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: stable/11/sys/dev/txp/if_txp.c 347962 2019-05-18 20:43:13Z brooks $"); 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> 62257176Sglebius#include <net/if_var.h> 6380219Swpaul#include <net/if_arp.h> 6480219Swpaul#include <net/ethernet.h> 6580219Swpaul#include <net/if_dl.h> 66189714Syongari#include <net/if_media.h> 6780219Swpaul#include <net/if_types.h> 6883115Sbrooks#include <net/if_vlan_var.h> 6980219Swpaul 7080219Swpaul#include <netinet/in.h> 7180219Swpaul#include <netinet/in_systm.h> 7280219Swpaul#include <netinet/ip.h> 7380219Swpaul 74189714Syongari#include <dev/mii/mii.h> 7580219Swpaul 7680219Swpaul#include <dev/pci/pcireg.h> 7780219Swpaul#include <dev/pci/pcivar.h> 7880219Swpaul 79189714Syongari#include <machine/bus.h> 80189714Syongari#include <machine/in_cksum.h> 8180219Swpaul 8280219Swpaul#include <dev/txp/if_txpreg.h> 8380219Swpaul#include <dev/txp/3c990img.h> 8480219Swpaul 85189714SyongariMODULE_DEPEND(txp, pci, 1, 1, 1); 86189714SyongariMODULE_DEPEND(txp, ether, 1, 1, 1); 8780219Swpaul 8880219Swpaul/* 89189714Syongari * XXX Known Typhoon firmware issues. 90189714Syongari * 91189714Syongari * 1. It seems that firmware has Tx TCP/UDP checksum offloading bug. 92189714Syongari * The firmware hangs when it's told to compute TCP/UDP checksum. 93189714Syongari * I'm not sure whether the firmware requires special alignment to 94189714Syongari * do checksum offloading but datasheet says nothing about that. 95189714Syongari * 2. Datasheet says nothing for maximum number of fragmented 96189714Syongari * descriptors supported. Experimentation shows up to 16 fragment 97189714Syongari * descriptors are supported in the firmware. For TSO case, upper 98189714Syongari * stack can send 64KB sized IP datagram plus link header size( 99189714Syongari * ethernet header + VLAN tag) frame but controller can handle up 100189714Syongari * to 64KB frame given that PAGE_SIZE is 4KB(i.e. 16 * PAGE_SIZE). 101189714Syongari * Because frames that need TSO operation of hardware can be 102189714Syongari * larger than 64KB I disabled TSO capability. TSO operation for 103189714Syongari * less than or equal to 16 fragment descriptors works without 104189714Syongari * problems, though. 105189714Syongari * 3. VLAN hardware tag stripping is always enabled in the firmware 106189714Syongari * even if it's explicitly told to not strip the tag. It's 107189714Syongari * possible to add the tag back in Rx handler if VLAN hardware 108189714Syongari * tag is not active but I didn't try that as it would be 109189714Syongari * layering violation. 110189714Syongari * 4. TXP_CMD_RECV_BUFFER_CONTROL does not work as expected in 111189714Syongari * datasheet such that driver should handle the alignment 112189714Syongari * restriction by copying received frame to align the frame on 113189714Syongari * 32bit boundary on strict-alignment architectures. This adds a 114189714Syongari * lot of CPU burden and it effectively reduce Rx performance on 115268351Smarcel * strict-alignment architectures(e.g. sparc64, arm and mips). 116189714Syongari * 117189714Syongari * Unfortunately it seems that 3Com have no longer interests in 118189714Syongari * releasing fixed firmware so we may have to live with these bugs. 119189714Syongari */ 120189714Syongari 121189714Syongari#define TXP_CSUM_FEATURES (CSUM_IP) 122189714Syongari 123189714Syongari/* 12480219Swpaul * Various supported device vendors/types and their names. 12580219Swpaul */ 12680219Swpaulstatic struct txp_type txp_devs[] = { 12780219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_95, 12880219Swpaul "3Com 3cR990-TX-95 Etherlink with 3XP Processor" }, 12980219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_TX_97, 13080219Swpaul "3Com 3cR990-TX-97 Etherlink with 3XP Processor" }, 13180219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_TXM, 13280219Swpaul "3Com 3cR990B-TXM Etherlink with 3XP Processor" }, 13380219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_95, 13480219Swpaul "3Com 3cR990-SRV-95 Etherlink Server with 3XP Processor" }, 13580219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990_SRV_97, 13680219Swpaul "3Com 3cR990-SRV-97 Etherlink Server with 3XP Processor" }, 13780219Swpaul { TXP_VENDORID_3COM, TXP_DEVICEID_3CR990B_SRV, 13880219Swpaul "3Com 3cR990B-SRV Etherlink Server with 3XP Processor" }, 13980219Swpaul { 0, 0, NULL } 14080219Swpaul}; 14180219Swpaul 142149678Sjhbstatic int txp_probe(device_t); 143149678Sjhbstatic int txp_attach(device_t); 144149678Sjhbstatic int txp_detach(device_t); 145189714Syongaristatic int txp_shutdown(device_t); 146189714Syongaristatic int txp_suspend(device_t); 147189714Syongaristatic int txp_resume(device_t); 148189714Syongaristatic int txp_intr(void *); 149189714Syongaristatic void txp_int_task(void *, int); 150149678Sjhbstatic void txp_tick(void *); 151149678Sjhbstatic int txp_ioctl(struct ifnet *, u_long, caddr_t); 152272067Sglebiusstatic uint64_t txp_get_counter(struct ifnet *, ift_counter); 153149678Sjhbstatic void txp_start(struct ifnet *); 154151772Sjhbstatic void txp_start_locked(struct ifnet *); 155189714Syongaristatic int txp_encap(struct txp_softc *, struct txp_tx_ring *, struct mbuf **); 156149678Sjhbstatic void txp_stop(struct txp_softc *); 157149678Sjhbstatic void txp_init(void *); 158151772Sjhbstatic void txp_init_locked(struct txp_softc *); 159189714Syongaristatic void txp_watchdog(struct txp_softc *); 16080219Swpaul 161189714Syongaristatic int txp_reset(struct txp_softc *); 162189714Syongaristatic int txp_boot(struct txp_softc *, uint32_t); 163189714Syongaristatic int txp_sleep(struct txp_softc *, int); 164189714Syongaristatic int txp_wait(struct txp_softc *, uint32_t); 16592739Salfredstatic int txp_download_fw(struct txp_softc *); 16692739Salfredstatic int txp_download_fw_wait(struct txp_softc *); 167149678Sjhbstatic int txp_download_fw_section(struct txp_softc *, 16892739Salfred struct txp_fw_section_header *, int); 16992739Salfredstatic int txp_alloc_rings(struct txp_softc *); 170189714Syongaristatic void txp_init_rings(struct txp_softc *); 171189714Syongaristatic int txp_dma_alloc(struct txp_softc *, char *, bus_dma_tag_t *, 172189714Syongari bus_size_t, bus_size_t, bus_dmamap_t *, void **, bus_size_t, bus_addr_t *); 173267580Sjhbstatic void txp_dma_free(struct txp_softc *, bus_dma_tag_t *, bus_dmamap_t, 174267580Sjhb void **, bus_addr_t *); 175189714Syongaristatic void txp_free_rings(struct txp_softc *); 17692739Salfredstatic int txp_rxring_fill(struct txp_softc *); 17792739Salfredstatic void txp_rxring_empty(struct txp_softc *); 17892739Salfredstatic void txp_set_filter(struct txp_softc *); 17980219Swpaul 18092739Salfredstatic int txp_cmd_desc_numfree(struct txp_softc *); 181189689Syongaristatic int txp_command(struct txp_softc *, uint16_t, uint16_t, uint32_t, 182189689Syongari uint32_t, uint16_t *, uint32_t *, uint32_t *, int); 183189714Syongaristatic int txp_ext_command(struct txp_softc *, uint16_t, uint16_t, 184189689Syongari uint32_t, uint32_t, struct txp_ext_desc *, uint8_t, 18592739Salfred struct txp_rsp_desc **, int); 186189714Syongaristatic int txp_response(struct txp_softc *, uint16_t, uint16_t, 18792739Salfred struct txp_rsp_desc **); 188149678Sjhbstatic void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *, 18992739Salfred struct txp_rsp_desc *); 190189714Syongaristatic int txp_set_capabilities(struct txp_softc *); 19180219Swpaul 19292739Salfredstatic void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *); 19392739Salfredstatic int txp_ifmedia_upd(struct ifnet *); 19480219Swpaul#ifdef TXP_DEBUG 19592739Salfredstatic void txp_show_descriptor(void *); 19680219Swpaul#endif 19792739Salfredstatic void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *); 19892739Salfredstatic void txp_rxbuf_reclaim(struct txp_softc *); 199189714Syongari#ifndef __NO_STRICT_ALIGNMENT 200189714Syongaristatic __inline void txp_fixup_rx(struct mbuf *); 20180219Swpaul#endif 202189714Syongaristatic int txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *, int); 203189714Syongaristatic void txp_stats_save(struct txp_softc *); 204189714Syongaristatic void txp_stats_update(struct txp_softc *, struct txp_rsp_desc *); 205189714Syongaristatic void txp_sysctl_node(struct txp_softc *); 206189714Syongaristatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); 207189714Syongaristatic int sysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS); 20880219Swpaul 209189714Syongaristatic int prefer_iomap = 0; 210189714SyongariTUNABLE_INT("hw.txp.prefer_iomap", &prefer_iomap); 211189714Syongari 21280219Swpaulstatic device_method_t txp_methods[] = { 21380219Swpaul /* Device interface */ 21480219Swpaul DEVMETHOD(device_probe, txp_probe), 21580219Swpaul DEVMETHOD(device_attach, txp_attach), 21680219Swpaul DEVMETHOD(device_detach, txp_detach), 21780219Swpaul DEVMETHOD(device_shutdown, txp_shutdown), 218189714Syongari DEVMETHOD(device_suspend, txp_suspend), 219189714Syongari DEVMETHOD(device_resume, txp_resume), 220189714Syongari 221189714Syongari { NULL, NULL } 22280219Swpaul}; 22380219Swpaul 22480219Swpaulstatic driver_t txp_driver = { 22580219Swpaul "txp", 22680219Swpaul txp_methods, 22780219Swpaul sizeof(struct txp_softc) 22880219Swpaul}; 22980219Swpaul 23080219Swpaulstatic devclass_t txp_devclass; 23180219Swpaul 232113506SmdoddDRIVER_MODULE(txp, pci, txp_driver, txp_devclass, 0, 0); 23380219Swpaul 23480219Swpaulstatic int 235189685Syongaritxp_probe(device_t dev) 23680219Swpaul{ 23780219Swpaul struct txp_type *t; 23880219Swpaul 23980219Swpaul t = txp_devs; 24080219Swpaul 241189688Syongari while (t->txp_name != NULL) { 24280219Swpaul if ((pci_get_vendor(dev) == t->txp_vid) && 24380219Swpaul (pci_get_device(dev) == t->txp_did)) { 24480219Swpaul device_set_desc(dev, t->txp_name); 245189688Syongari return (BUS_PROBE_DEFAULT); 24680219Swpaul } 24780219Swpaul t++; 24880219Swpaul } 24980219Swpaul 250189688Syongari return (ENXIO); 25180219Swpaul} 25280219Swpaul 25380219Swpaulstatic int 254189685Syongaritxp_attach(device_t dev) 25580219Swpaul{ 25680219Swpaul struct txp_softc *sc; 25780219Swpaul struct ifnet *ifp; 258189714Syongari struct txp_rsp_desc *rsp; 259189689Syongari uint16_t p1; 260189714Syongari uint32_t p2, reg; 261189714Syongari int error = 0, pmc, rid; 262189714Syongari uint8_t eaddr[ETHER_ADDR_LEN], *ver; 26380219Swpaul 26480219Swpaul sc = device_get_softc(dev); 26580219Swpaul sc->sc_dev = dev; 26680219Swpaul 26793818Sjhb mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 268151772Sjhb MTX_DEF); 269151772Sjhb callout_init_mtx(&sc->sc_tick, &sc->sc_mtx, 0); 270189714Syongari TASK_INIT(&sc->sc_int_task, 0, txp_int_task, sc); 271189714Syongari TAILQ_INIT(&sc->sc_busy_list); 272189714Syongari TAILQ_INIT(&sc->sc_free_list); 273151772Sjhb 274189714Syongari ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts); 275189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL); 276189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL); 277189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); 278189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL); 279189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL); 280189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); 281189714Syongari ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); 282189714Syongari 28380219Swpaul pci_enable_busmaster(dev); 284189714Syongari /* Prefer memory space register mapping over IO space. */ 285189714Syongari if (prefer_iomap == 0) { 286189714Syongari sc->sc_res_id = PCIR_BAR(1); 287189714Syongari sc->sc_res_type = SYS_RES_MEMORY; 288189714Syongari } else { 289189714Syongari sc->sc_res_id = PCIR_BAR(0); 290189714Syongari sc->sc_res_type = SYS_RES_IOPORT; 291189714Syongari } 292189714Syongari sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type, 293189714Syongari &sc->sc_res_id, RF_ACTIVE); 294189714Syongari if (sc->sc_res == NULL && prefer_iomap == 0) { 295189714Syongari sc->sc_res_id = PCIR_BAR(0); 296189714Syongari sc->sc_res_type = SYS_RES_IOPORT; 297189714Syongari sc->sc_res = bus_alloc_resource_any(dev, sc->sc_res_type, 298189714Syongari &sc->sc_res_id, RF_ACTIVE); 299189714Syongari } 30080219Swpaul if (sc->sc_res == NULL) { 30180219Swpaul device_printf(dev, "couldn't map ports/memory\n"); 302189714Syongari ifmedia_removeall(&sc->sc_ifmedia); 303189714Syongari mtx_destroy(&sc->sc_mtx); 304189714Syongari return (ENXIO); 30580219Swpaul } 30680219Swpaul 307189714Syongari /* Enable MWI. */ 308189714Syongari reg = pci_read_config(dev, PCIR_COMMAND, 2); 309189714Syongari reg |= PCIM_CMD_MWRICEN; 310189714Syongari pci_write_config(dev, PCIR_COMMAND, reg, 2); 311189714Syongari /* Check cache line size. */ 312189714Syongari reg = pci_read_config(dev, PCIR_CACHELNSZ, 1); 313189714Syongari reg <<= 4; 314189714Syongari if (reg == 0 || (reg % 16) != 0) 315189714Syongari device_printf(sc->sc_dev, 316189714Syongari "invalid cache line size : %u\n", reg); 31780219Swpaul 31880219Swpaul /* Allocate interrupt */ 31980219Swpaul rid = 0; 320127135Snjl sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 32180219Swpaul RF_SHAREABLE | RF_ACTIVE); 32280219Swpaul 32380219Swpaul if (sc->sc_irq == NULL) { 32480219Swpaul device_printf(dev, "couldn't map interrupt\n"); 32580219Swpaul error = ENXIO; 32680219Swpaul goto fail; 32780219Swpaul } 32880219Swpaul 329189714Syongari if ((error = txp_alloc_rings(sc)) != 0) 33080219Swpaul goto fail; 331189714Syongari txp_init_rings(sc); 332189714Syongari txp_sysctl_node(sc); 333189714Syongari /* Reset controller and make it reload sleep image. */ 334189714Syongari if (txp_reset(sc) != 0) { 335170596Syongari error = ENXIO; 336170596Syongari goto fail; 337170596Syongari } 33880219Swpaul 339189714Syongari /* Let controller boot from sleep image. */ 340189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) { 341189714Syongari device_printf(sc->sc_dev, "could not boot sleep image\n"); 342170596Syongari error = ENXIO; 343170596Syongari goto fail; 344170596Syongari } 34580219Swpaul 346189714Syongari /* Get station address. */ 34780219Swpaul if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0, 348189714Syongari &p1, &p2, NULL, TXP_CMD_WAIT)) { 349149678Sjhb error = ENXIO; 35080219Swpaul goto fail; 35180219Swpaul } 35280219Swpaul 353189714Syongari p1 = le16toh(p1); 354189689Syongari eaddr[0] = ((uint8_t *)&p1)[1]; 355189689Syongari eaddr[1] = ((uint8_t *)&p1)[0]; 356189714Syongari p2 = le32toh(p2); 357189689Syongari eaddr[2] = ((uint8_t *)&p2)[3]; 358189689Syongari eaddr[3] = ((uint8_t *)&p2)[2]; 359189689Syongari eaddr[4] = ((uint8_t *)&p2)[1]; 360189689Syongari eaddr[5] = ((uint8_t *)&p2)[0]; 36180219Swpaul 362189714Syongari ifp = sc->sc_ifp = if_alloc(IFT_ETHER); 363189714Syongari if (ifp == NULL) { 364189714Syongari device_printf(dev, "can not allocate ifnet structure\n"); 365189714Syongari error = ENOSPC; 366189714Syongari goto fail; 367189714Syongari } 36880219Swpaul 369189714Syongari /* 370189714Syongari * Show sleep image version information which may help to 371189714Syongari * diagnose sleep image specific issues. 372189714Syongari */ 373189714Syongari rsp = NULL; 374327589Spfg if (txp_ext_command(sc, TXP_CMD_VERSIONS_READ, 0, 0, 0, NULL, 0, 375189714Syongari &rsp, TXP_CMD_WAIT)) { 376189714Syongari device_printf(dev, "can not read sleep image version\n"); 377189714Syongari error = ENXIO; 378189714Syongari goto fail; 379189714Syongari } 380189714Syongari if (rsp->rsp_numdesc == 0) { 381189714Syongari p2 = le32toh(rsp->rsp_par2) & 0xFFFF; 382189714Syongari device_printf(dev, "Typhoon 1.0 sleep image (2000/%02u/%02u)\n", 383189714Syongari p2 >> 8, p2 & 0xFF); 384189714Syongari } else if (rsp->rsp_numdesc == 2) { 385189714Syongari p2 = le32toh(rsp->rsp_par2); 386189714Syongari ver = (uint8_t *)(rsp + 1); 387189714Syongari /* 388189714Syongari * Even if datasheet says the command returns a NULL 389189714Syongari * terminated version string, explicitly terminate 390189714Syongari * the string. Given that several bugs of firmware 391189714Syongari * I can't trust this simple one. 392189714Syongari */ 393189714Syongari ver[25] = '\0'; 394189714Syongari device_printf(dev, 395189714Syongari "Typhoon 1.1+ sleep image %02u.%03u.%03u %s\n", 396189714Syongari p2 >> 24, (p2 >> 12) & 0xFFF, p2 & 0xFFF, ver); 397189714Syongari } else { 398189714Syongari p2 = le32toh(rsp->rsp_par2); 399189714Syongari device_printf(dev, 400189714Syongari "Unknown Typhoon sleep image version: %u:0x%08x\n", 401189714Syongari rsp->rsp_numdesc, p2); 402189714Syongari } 403189714Syongari if (rsp != NULL) 404189714Syongari free(rsp, M_DEVBUF); 40580219Swpaul 40680219Swpaul sc->sc_xcvr = TXP_XCVR_AUTO; 40780219Swpaul txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0, 408189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 409189714Syongari ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO); 41080219Swpaul 41180219Swpaul ifp->if_softc = sc; 412121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 413151772Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 41480219Swpaul ifp->if_ioctl = txp_ioctl; 41580219Swpaul ifp->if_start = txp_start; 41680219Swpaul ifp->if_init = txp_init; 417272067Sglebius ifp->if_get_counter = txp_get_counter; 418189714Syongari ifp->if_snd.ifq_drv_maxlen = TX_ENTRIES - 1; 419189714Syongari IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); 420189714Syongari IFQ_SET_READY(&ifp->if_snd); 42180219Swpaul /* 422189714Syongari * It's possible to read firmware's offload capability but 423189714Syongari * we have not downloaded the firmware yet so announce 424189714Syongari * working capability here. We're not interested in IPSec 425189714Syongari * capability and due to the lots of firmware bug we can't 426189714Syongari * advertise the whole capability anyway. 42780219Swpaul */ 428189714Syongari ifp->if_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM; 429219902Sjhb if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0) 430189714Syongari ifp->if_capabilities |= IFCAP_WOL_MAGIC; 431189714Syongari /* Enable all capabilities. */ 432189714Syongari ifp->if_capenable = ifp->if_capabilities; 433189714Syongari 434147256Sbrooks ether_ifattach(ifp, eaddr); 435149678Sjhb 436189714Syongari /* VLAN capability setup. */ 437189714Syongari ifp->if_capabilities |= IFCAP_VLAN_MTU; 438189714Syongari ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; 439189714Syongari ifp->if_capenable = ifp->if_capabilities; 440189714Syongari /* Tell the upper layer(s) we support long frames. */ 441270856Sglebius ifp->if_hdrlen = sizeof(struct ether_vlan_header); 442189714Syongari 443189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 444189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 445189714Syongari 446189714Syongari /* Create local taskq. */ 447189714Syongari sc->sc_tq = taskqueue_create_fast("txp_taskq", M_WAITOK, 448189714Syongari taskqueue_thread_enqueue, &sc->sc_tq); 449189714Syongari if (sc->sc_tq == NULL) { 450189714Syongari device_printf(dev, "could not create taskqueue.\n"); 451189714Syongari ether_ifdetach(ifp); 452189714Syongari error = ENXIO; 453189714Syongari goto fail; 454189714Syongari } 455189714Syongari taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", 456189714Syongari device_get_nameunit(sc->sc_dev)); 457189714Syongari 458189714Syongari /* Put controller into sleep. */ 459189714Syongari if (txp_sleep(sc, 0) != 0) { 460189714Syongari ether_ifdetach(ifp); 461189714Syongari error = ENXIO; 462189714Syongari goto fail; 463189714Syongari } 464189714Syongari 465151772Sjhb error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 466189714Syongari txp_intr, NULL, sc, &sc->sc_intrhand); 467149678Sjhb 468189714Syongari if (error != 0) { 469149678Sjhb ether_ifdetach(ifp); 470189714Syongari device_printf(dev, "couldn't set up interrupt handler.\n"); 471149678Sjhb goto fail; 472149678Sjhb } 473149678Sjhb 474347962Sbrooks gone_by_fcp101_dev(dev); 475347962Sbrooks 476189688Syongari return (0); 47780219Swpaul 47880219Swpaulfail: 479189714Syongari if (error != 0) 480189714Syongari txp_detach(dev); 481189688Syongari return (error); 48280219Swpaul} 48380219Swpaul 48480219Swpaulstatic int 485189685Syongaritxp_detach(device_t dev) 48680219Swpaul{ 48780219Swpaul struct txp_softc *sc; 48880219Swpaul struct ifnet *ifp; 48980219Swpaul 49080219Swpaul sc = device_get_softc(dev); 491189714Syongari 492147256Sbrooks ifp = sc->sc_ifp; 493189714Syongari if (device_is_attached(dev)) { 494189714Syongari TXP_LOCK(sc); 495189714Syongari sc->sc_flags |= TXP_FLAG_DETACH; 496189714Syongari txp_stop(sc); 497189714Syongari TXP_UNLOCK(sc); 498189714Syongari callout_drain(&sc->sc_tick); 499189714Syongari taskqueue_drain(sc->sc_tq, &sc->sc_int_task); 500189714Syongari ether_ifdetach(ifp); 501189714Syongari } 502189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 50380219Swpaul 50480219Swpaul ifmedia_removeall(&sc->sc_ifmedia); 50580219Swpaul if (sc->sc_intrhand != NULL) 50680219Swpaul bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand); 50780219Swpaul if (sc->sc_irq != NULL) 50880219Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq); 50980219Swpaul if (sc->sc_res != NULL) 510189714Syongari bus_release_resource(dev, sc->sc_res_type, sc->sc_res_id, 511189714Syongari sc->sc_res); 512189714Syongari if (sc->sc_ifp != NULL) { 513150306Simp if_free(sc->sc_ifp); 514189714Syongari sc->sc_ifp = NULL; 515189714Syongari } 516189714Syongari txp_free_rings(sc); 517189714Syongari mtx_destroy(&sc->sc_mtx); 51880219Swpaul 51980219Swpaul return (0); 52080219Swpaul} 52180219Swpaul 52280219Swpaulstatic int 523189714Syongaritxp_reset(struct txp_softc *sc) 52480219Swpaul{ 525189689Syongari uint32_t r; 52680219Swpaul int i; 52780219Swpaul 528189714Syongari /* Disable interrupts. */ 529189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 530189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 531189714Syongari /* Ack all pending interrupts. */ 532189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 533189714Syongari 53492643Sjeff r = 0; 53580219Swpaul WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL); 53680219Swpaul DELAY(1000); 53780219Swpaul WRITE_REG(sc, TXP_SRR, 0); 53880219Swpaul 539189714Syongari /* Should wait max 6 seconds. */ 54080219Swpaul for (i = 0; i < 6000; i++) { 54180219Swpaul r = READ_REG(sc, TXP_A2H_0); 54280219Swpaul if (r == STAT_WAITING_FOR_HOST_REQUEST) 54380219Swpaul break; 54480219Swpaul DELAY(1000); 54580219Swpaul } 54680219Swpaul 547189714Syongari if (r != STAT_WAITING_FOR_HOST_REQUEST) 54880219Swpaul device_printf(sc->sc_dev, "reset hung\n"); 549189714Syongari 550189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 551189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 552189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 553189714Syongari 554189714Syongari /* 555189714Syongari * Give more time to complete loading sleep image before 556189714Syongari * trying to boot from sleep image. 557189714Syongari */ 558189714Syongari DELAY(5000); 559189714Syongari 560189714Syongari return (0); 561189714Syongari} 562189714Syongari 563189714Syongaristatic int 564189714Syongaritxp_boot(struct txp_softc *sc, uint32_t state) 565189714Syongari{ 566189714Syongari 567189714Syongari /* See if it's waiting for boot, and try to boot it. */ 568189714Syongari if (txp_wait(sc, state) != 0) { 569189714Syongari device_printf(sc->sc_dev, "not waiting for boot\n"); 570189714Syongari return (ENXIO); 57180219Swpaul } 57280219Swpaul 573189714Syongari WRITE_REG(sc, TXP_H2A_2, TXP_ADDR_HI(sc->sc_ldata.txp_boot_paddr)); 574189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 575189714Syongari WRITE_REG(sc, TXP_H2A_1, TXP_ADDR_LO(sc->sc_ldata.txp_boot_paddr)); 576189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 577189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD); 578189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 579189714Syongari 580189714Syongari /* See if it booted. */ 581189714Syongari if (txp_wait(sc, STAT_RUNNING) != 0) { 582189714Syongari device_printf(sc->sc_dev, "firmware not running\n"); 583189714Syongari return (ENXIO); 584189714Syongari } 585189714Syongari 586189714Syongari /* Clear TX and CMD ring write registers. */ 587189714Syongari WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL); 588189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 589189714Syongari WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL); 590189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 591189714Syongari WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL); 592189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 593189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL); 594189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 595189714Syongari 59680219Swpaul return (0); 59780219Swpaul} 59880219Swpaul 59980219Swpaulstatic int 600189685Syongaritxp_download_fw(struct txp_softc *sc) 60180219Swpaul{ 60280219Swpaul struct txp_fw_file_header *fileheader; 60380219Swpaul struct txp_fw_section_header *secthead; 604189714Syongari int sect; 605189714Syongari uint32_t error, ier, imr; 60680219Swpaul 607189714Syongari TXP_LOCK_ASSERT(sc); 608189714Syongari 609189022Syongari error = 0; 61080219Swpaul ier = READ_REG(sc, TXP_IER); 61180219Swpaul WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0); 61280219Swpaul 61380219Swpaul imr = READ_REG(sc, TXP_IMR); 61480219Swpaul WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0); 61580219Swpaul 616189714Syongari if (txp_wait(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) { 61780219Swpaul device_printf(sc->sc_dev, "not waiting for host request\n"); 618189714Syongari error = ETIMEDOUT; 619189022Syongari goto fail; 62080219Swpaul } 62180219Swpaul 622189714Syongari /* Ack the status. */ 62380219Swpaul WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); 62480219Swpaul 62580219Swpaul fileheader = (struct txp_fw_file_header *)tc990image; 62680219Swpaul if (bcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) { 627189714Syongari device_printf(sc->sc_dev, "firmware invalid magic\n"); 628189022Syongari goto fail; 62980219Swpaul } 63080219Swpaul 631189714Syongari /* Tell boot firmware to get ready for image. */ 632189714Syongari WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr)); 633189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 634189714Syongari WRITE_REG(sc, TXP_H2A_2, le32toh(fileheader->hmac[0])); 635189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 636189714Syongari WRITE_REG(sc, TXP_H2A_3, le32toh(fileheader->hmac[1])); 637189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 638189714Syongari WRITE_REG(sc, TXP_H2A_4, le32toh(fileheader->hmac[2])); 639189714Syongari TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE); 640189714Syongari WRITE_REG(sc, TXP_H2A_5, le32toh(fileheader->hmac[3])); 641189714Syongari TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE); 642189714Syongari WRITE_REG(sc, TXP_H2A_6, le32toh(fileheader->hmac[4])); 643189714Syongari TXP_BARRIER(sc, TXP_H2A_6, 4, BUS_SPACE_BARRIER_WRITE); 64480219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE); 645189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 64680219Swpaul 64780219Swpaul if (txp_download_fw_wait(sc)) { 648189714Syongari device_printf(sc->sc_dev, "firmware wait failed, initial\n"); 649189714Syongari error = ETIMEDOUT; 650189022Syongari goto fail; 65180219Swpaul } 65280219Swpaul 653189689Syongari secthead = (struct txp_fw_section_header *)(((uint8_t *)tc990image) + 65480219Swpaul sizeof(struct txp_fw_file_header)); 65580219Swpaul 656189714Syongari for (sect = 0; sect < le32toh(fileheader->nsections); sect++) { 657189714Syongari if ((error = txp_download_fw_section(sc, secthead, sect)) != 0) 658189022Syongari goto fail; 65980219Swpaul secthead = (struct txp_fw_section_header *) 660189714Syongari (((uint8_t *)secthead) + le32toh(secthead->nbytes) + 66180219Swpaul sizeof(*secthead)); 66280219Swpaul } 66380219Swpaul 66480219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE); 665189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 66680219Swpaul 667189714Syongari if (txp_wait(sc, STAT_WAITING_FOR_BOOT) != 0) { 66880219Swpaul device_printf(sc->sc_dev, "not waiting for boot\n"); 669189714Syongari error = ETIMEDOUT; 670189022Syongari goto fail; 67180219Swpaul } 67280219Swpaul 673189022Syongarifail: 67480219Swpaul WRITE_REG(sc, TXP_IER, ier); 67580219Swpaul WRITE_REG(sc, TXP_IMR, imr); 67680219Swpaul 677189022Syongari return (error); 67880219Swpaul} 67980219Swpaul 68080219Swpaulstatic int 681189685Syongaritxp_download_fw_wait(struct txp_softc *sc) 68280219Swpaul{ 683189714Syongari uint32_t i; 68480219Swpaul 685189714Syongari TXP_LOCK_ASSERT(sc); 686189714Syongari 687189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 688189714Syongari if ((READ_REG(sc, TXP_ISR) & TXP_INT_A2H_0) != 0) 68980219Swpaul break; 69080219Swpaul DELAY(50); 69180219Swpaul } 69280219Swpaul 693189714Syongari if (i == TXP_TIMEOUT) { 694189714Syongari device_printf(sc->sc_dev, "firmware wait failed comm0\n"); 695189714Syongari return (ETIMEDOUT); 69680219Swpaul } 69780219Swpaul 69880219Swpaul WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0); 69980219Swpaul 700189714Syongari if (READ_REG(sc, TXP_A2H_0) != STAT_WAITING_FOR_SEGMENT) { 701189714Syongari device_printf(sc->sc_dev, "firmware not waiting for segment\n"); 702189714Syongari return (ETIMEDOUT); 70380219Swpaul } 70480219Swpaul return (0); 70580219Swpaul} 70680219Swpaul 70780219Swpaulstatic int 708189685Syongaritxp_download_fw_section(struct txp_softc *sc, 709189685Syongari struct txp_fw_section_header *sect, int sectnum) 71080219Swpaul{ 711189714Syongari bus_dma_tag_t sec_tag; 712189714Syongari bus_dmamap_t sec_map; 713189714Syongari bus_addr_t sec_paddr; 714189714Syongari uint8_t *sec_buf; 71580219Swpaul int rseg, err = 0; 71680219Swpaul struct mbuf m; 717189689Syongari uint16_t csum; 71880219Swpaul 719189714Syongari TXP_LOCK_ASSERT(sc); 720189714Syongari 721189714Syongari /* Skip zero length sections. */ 722189714Syongari if (le32toh(sect->nbytes) == 0) 72380219Swpaul return (0); 72480219Swpaul 725189714Syongari /* Make sure we aren't past the end of the image. */ 726189689Syongari rseg = ((uint8_t *)sect) - ((uint8_t *)tc990image); 72780219Swpaul if (rseg >= sizeof(tc990image)) { 728189714Syongari device_printf(sc->sc_dev, 729189714Syongari "firmware invalid section address, section %d\n", sectnum); 730189714Syongari return (EIO); 73180219Swpaul } 73280219Swpaul 733189714Syongari /* Make sure this section doesn't go past the end. */ 734189714Syongari rseg += le32toh(sect->nbytes); 73580219Swpaul if (rseg >= sizeof(tc990image)) { 736189714Syongari device_printf(sc->sc_dev, "firmware truncated section %d\n", 73780219Swpaul sectnum); 738189714Syongari return (EIO); 73980219Swpaul } 74080219Swpaul 741189714Syongari sec_tag = NULL; 742189714Syongari sec_map = NULL; 743189714Syongari sec_buf = NULL; 744189714Syongari /* XXX */ 745189714Syongari TXP_UNLOCK(sc); 746189714Syongari err = txp_dma_alloc(sc, "firmware sections", &sec_tag, sizeof(uint32_t), 747189714Syongari 0, &sec_map, (void **)&sec_buf, le32toh(sect->nbytes), &sec_paddr); 748189714Syongari TXP_LOCK(sc); 749189714Syongari if (err != 0) 750189714Syongari goto bail; 751189714Syongari bcopy(((uint8_t *)sect) + sizeof(*sect), sec_buf, 752189714Syongari le32toh(sect->nbytes)); 75380219Swpaul 75480219Swpaul /* 75580219Swpaul * dummy up mbuf and verify section checksum 75680219Swpaul */ 75780219Swpaul m.m_type = MT_DATA; 75880219Swpaul m.m_next = m.m_nextpkt = NULL; 759189714Syongari m.m_len = le32toh(sect->nbytes); 760189714Syongari m.m_data = sec_buf; 76180219Swpaul m.m_flags = 0; 762189714Syongari csum = in_cksum(&m, le32toh(sect->nbytes)); 76380219Swpaul if (csum != sect->cksum) { 764189714Syongari device_printf(sc->sc_dev, 765189714Syongari "firmware section %d, bad cksum (expected 0x%x got 0x%x)\n", 766189714Syongari sectnum, le16toh(sect->cksum), csum); 767189714Syongari err = EIO; 76880219Swpaul goto bail; 76980219Swpaul } 77080219Swpaul 771189714Syongari bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_PREWRITE); 772189714Syongari 773189714Syongari WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes)); 774189714Syongari TXP_BARRIER(sc, TXP_H2A_1, 4, BUS_SPACE_BARRIER_WRITE); 775189714Syongari WRITE_REG(sc, TXP_H2A_2, le16toh(sect->cksum)); 776189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 777189714Syongari WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr)); 778189714Syongari TXP_BARRIER(sc, TXP_H2A_3, 4, BUS_SPACE_BARRIER_WRITE); 779189714Syongari WRITE_REG(sc, TXP_H2A_4, TXP_ADDR_HI(sec_paddr)); 780189714Syongari TXP_BARRIER(sc, TXP_H2A_4, 4, BUS_SPACE_BARRIER_WRITE); 781189714Syongari WRITE_REG(sc, TXP_H2A_5, TXP_ADDR_LO(sec_paddr)); 782189714Syongari TXP_BARRIER(sc, TXP_H2A_5, 4, BUS_SPACE_BARRIER_WRITE); 78380219Swpaul WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE); 784189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 78580219Swpaul 78680219Swpaul if (txp_download_fw_wait(sc)) { 787189714Syongari device_printf(sc->sc_dev, 788189714Syongari "firmware wait failed, section %d\n", sectnum); 789189714Syongari err = ETIMEDOUT; 79080219Swpaul } 79180219Swpaul 792189714Syongari bus_dmamap_sync(sec_tag, sec_map, BUS_DMASYNC_POSTWRITE); 79380219Swpaulbail: 794267580Sjhb txp_dma_free(sc, &sec_tag, sec_map, (void **)&sec_buf, &sec_paddr); 79580219Swpaul return (err); 79680219Swpaul} 79780219Swpaul 798189714Syongaristatic int 799189685Syongaritxp_intr(void *vsc) 80080219Swpaul{ 801189714Syongari struct txp_softc *sc; 802189714Syongari uint32_t status; 803189714Syongari 804189714Syongari sc = vsc; 805189714Syongari status = READ_REG(sc, TXP_ISR); 806189714Syongari if ((status & TXP_INT_LATCH) == 0) 807189714Syongari return (FILTER_STRAY); 808189714Syongari WRITE_REG(sc, TXP_ISR, status); 809189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 810189714Syongari taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task); 811189714Syongari 812189714Syongari return (FILTER_HANDLED); 813189714Syongari} 814189714Syongari 815189714Syongaristatic void 816189714Syongaritxp_int_task(void *arg, int pending) 817189714Syongari{ 818189714Syongari struct txp_softc *sc; 819189714Syongari struct ifnet *ifp; 820189714Syongari struct txp_hostvar *hv; 821189689Syongari uint32_t isr; 822189714Syongari int more; 82380219Swpaul 824189714Syongari sc = (struct txp_softc *)arg; 825189714Syongari 826151772Sjhb TXP_LOCK(sc); 827189714Syongari ifp = sc->sc_ifp; 828189714Syongari hv = sc->sc_hostvar; 82980219Swpaul isr = READ_REG(sc, TXP_ISR); 830189714Syongari if ((isr & TXP_INT_LATCH) != 0) 83180219Swpaul WRITE_REG(sc, TXP_ISR, isr); 83280219Swpaul 833189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 834189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 835189714Syongari sc->sc_cdata.txp_hostvar_map, 836189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 837189714Syongari more = 0; 83880219Swpaul if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff)) 839189714Syongari more += txp_rx_reclaim(sc, &sc->sc_rxhir, 840189714Syongari sc->sc_process_limit); 84180219Swpaul if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff)) 842189714Syongari more += txp_rx_reclaim(sc, &sc->sc_rxlor, 843189714Syongari sc->sc_process_limit); 844189714Syongari /* 845189714Syongari * XXX 846189714Syongari * It seems controller is not smart enough to handle 847189714Syongari * FIFO overflow conditions under heavy network load. 848189714Syongari * No matter how often new Rx buffers are passed to 849189714Syongari * controller the situation didn't change. Maybe 850189714Syongari * flow-control would be the only way to mitigate the 851189714Syongari * issue but firmware does not have commands that 852189714Syongari * control the threshold of emitting pause frames. 853189714Syongari */ 85480219Swpaul if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx) 85580219Swpaul txp_rxbuf_reclaim(sc); 85680219Swpaul if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons != 857189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off))))) 85880219Swpaul txp_tx_reclaim(sc, &sc->sc_txhir); 85980219Swpaul if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons != 860189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off))))) 86180219Swpaul txp_tx_reclaim(sc, &sc->sc_txlor); 862189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 863189714Syongari sc->sc_cdata.txp_hostvar_map, 864189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 865189714Syongari if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 866189714Syongari txp_start_locked(sc->sc_ifp); 867189714Syongari if (more != 0 || READ_REG(sc, TXP_ISR & TXP_INT_LATCH) != 0) { 868189714Syongari taskqueue_enqueue(sc->sc_tq, &sc->sc_int_task); 869189714Syongari TXP_UNLOCK(sc); 870189714Syongari return; 871189714Syongari } 87280219Swpaul } 87380219Swpaul 874189714Syongari /* Re-enable interrupts. */ 875189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE); 876151772Sjhb TXP_UNLOCK(sc); 87780219Swpaul} 87880219Swpaul 879189714Syongari#ifndef __NO_STRICT_ALIGNMENT 880189714Syongaristatic __inline void 881189714Syongaritxp_fixup_rx(struct mbuf *m) 88280219Swpaul{ 883189714Syongari int i; 884189714Syongari uint16_t *src, *dst; 885189714Syongari 886189714Syongari src = mtod(m, uint16_t *); 887189714Syongari dst = src - (TXP_RXBUF_ALIGN - ETHER_ALIGN) / sizeof *src; 888189714Syongari 889189714Syongari for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 890189714Syongari *dst++ = *src++; 891189714Syongari 892189714Syongari m->m_data -= TXP_RXBUF_ALIGN - ETHER_ALIGN; 893189714Syongari} 894189714Syongari#endif 895189714Syongari 896189714Syongaristatic int 897189714Syongaritxp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r, int count) 898189714Syongari{ 899189714Syongari struct ifnet *ifp; 90080219Swpaul struct txp_rx_desc *rxd; 90180219Swpaul struct mbuf *m; 902189714Syongari struct txp_rx_swdesc *sd; 903189714Syongari uint32_t roff, woff, rx_stat, prog; 90480219Swpaul 905151772Sjhb TXP_LOCK_ASSERT(sc); 90680219Swpaul 907189714Syongari ifp = sc->sc_ifp; 90880219Swpaul 909189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD | 910189714Syongari BUS_DMASYNC_POSTWRITE); 91180219Swpaul 912189714Syongari roff = le32toh(*r->r_roff); 913189714Syongari woff = le32toh(*r->r_woff); 914189714Syongari rxd = r->r_desc + roff / sizeof(struct txp_rx_desc); 915189714Syongari for (prog = 0; roff != woff; prog++, count--) { 916189714Syongari if (count <= 0) 917189714Syongari break; 918189714Syongari bcopy((u_long *)&rxd->rx_vaddrlo, &sd, sizeof(sd)); 919189714Syongari KASSERT(sd != NULL, ("%s: Rx desc ring corrupted", __func__)); 920189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 921189714Syongari BUS_DMASYNC_POSTREAD); 922189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map); 92380219Swpaul m = sd->sd_mbuf; 924189714Syongari KASSERT(m != NULL, ("%s: Rx buffer ring corrupted", __func__)); 92580219Swpaul sd->sd_mbuf = NULL; 926189714Syongari TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next); 927189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 928189714Syongari if ((rxd->rx_flags & RX_FLAGS_ERROR) != 0) { 929189714Syongari if (bootverbose) 930189714Syongari device_printf(sc->sc_dev, "Rx error %u\n", 931189714Syongari le32toh(rxd->rx_stat) & RX_ERROR_MASK); 932151772Sjhb m_freem(m); 933189714Syongari goto next; 93480219Swpaul } 935189714Syongari 936189714Syongari m->m_pkthdr.len = m->m_len = le16toh(rxd->rx_len); 937189714Syongari m->m_pkthdr.rcvif = ifp; 938189714Syongari#ifndef __NO_STRICT_ALIGNMENT 939189714Syongari txp_fixup_rx(m); 94080219Swpaul#endif 941189714Syongari rx_stat = le32toh(rxd->rx_stat); 942189714Syongari if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { 943189714Syongari if ((rx_stat & RX_STAT_IPCKSUMBAD) != 0) 944189714Syongari m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; 945189714Syongari else if ((rx_stat & RX_STAT_IPCKSUMGOOD) != 0) 946189714Syongari m->m_pkthdr.csum_flags |= 947189714Syongari CSUM_IP_CHECKED|CSUM_IP_VALID; 94880219Swpaul 949189714Syongari if ((rx_stat & RX_STAT_TCPCKSUMGOOD) != 0 || 950189714Syongari (rx_stat & RX_STAT_UDPCKSUMGOOD) != 0) { 951189714Syongari m->m_pkthdr.csum_flags |= 952189714Syongari CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 953189714Syongari m->m_pkthdr.csum_data = 0xffff; 954189714Syongari } 95580219Swpaul } 95680219Swpaul 957189714Syongari /* 958189714Syongari * XXX 959189714Syongari * Typhoon has a firmware bug that VLAN tag is always 960189714Syongari * stripped out even if it is told to not remove the tag. 961189714Syongari * Therefore don't check if_capenable here. 962189714Syongari */ 963189714Syongari if (/* (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && */ 964189714Syongari (rx_stat & RX_STAT_VLAN) != 0) { 965189714Syongari m->m_pkthdr.ether_vtag = 966189714Syongari bswap16((le32toh(rxd->rx_vlan) >> 16)); 967162375Sandre m->m_flags |= M_VLANTAG; 96880219Swpaul } 96983115Sbrooks 970151772Sjhb TXP_UNLOCK(sc); 971106937Ssam (*ifp->if_input)(ifp, m); 972151772Sjhb TXP_LOCK(sc); 97380219Swpaul 97480219Swpaulnext: 97580219Swpaul roff += sizeof(struct txp_rx_desc); 97680219Swpaul if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) { 97780219Swpaul roff = 0; 97880219Swpaul rxd = r->r_desc; 97980219Swpaul } else 98080219Swpaul rxd++; 981189714Syongari prog++; 98280219Swpaul } 98380219Swpaul 984189714Syongari if (prog == 0) 985189714Syongari return (0); 986189714Syongari 987189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD | 988189714Syongari BUS_DMASYNC_PREWRITE); 989189714Syongari *r->r_roff = le32toh(roff); 990189714Syongari 991189714Syongari return (count > 0 ? 0 : EAGAIN); 99280219Swpaul} 99380219Swpaul 99480219Swpaulstatic void 995189685Syongaritxp_rxbuf_reclaim(struct txp_softc *sc) 99680219Swpaul{ 997189714Syongari struct txp_hostvar *hv; 99880219Swpaul struct txp_rxbuf_desc *rbd; 999189714Syongari struct txp_rx_swdesc *sd; 1000189714Syongari bus_dma_segment_t segs[1]; 1001189714Syongari int nsegs, prod, prog; 1002189714Syongari uint32_t cons; 100380219Swpaul 1004151772Sjhb TXP_LOCK_ASSERT(sc); 1005189714Syongari 1006189714Syongari hv = sc->sc_hostvar; 1007189714Syongari cons = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_read_idx)); 1008189714Syongari prod = sc->sc_rxbufprod; 1009189714Syongari TXP_DESC_INC(prod, RXBUF_ENTRIES); 1010189714Syongari if (prod == cons) 101180219Swpaul return; 101280219Swpaul 1013189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1014189714Syongari sc->sc_cdata.txp_rxbufs_map, 1015189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 101680219Swpaul 1017189714Syongari for (prog = 0; prod != cons; prog++) { 1018189714Syongari sd = TAILQ_FIRST(&sc->sc_free_list); 1019189714Syongari if (sd == NULL) 102080219Swpaul break; 1021189714Syongari rbd = sc->sc_rxbufs + prod; 1022189714Syongari bcopy((u_long *)&rbd->rb_vaddrlo, &sd, sizeof(sd)); 1023243857Sglebius sd->sd_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 102480219Swpaul if (sd->sd_mbuf == NULL) 1025189714Syongari break; 102680219Swpaul sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; 1027189714Syongari#ifndef __NO_STRICT_ALIGNMENT 1028189714Syongari m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN); 1029189714Syongari#endif 1030189714Syongari if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag, 1031189714Syongari sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0) != 0) { 1032189714Syongari m_freem(sd->sd_mbuf); 1033189714Syongari sd->sd_mbuf = NULL; 1034189714Syongari break; 1035189714Syongari } 1036189714Syongari KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__, 1037189714Syongari nsegs)); 1038189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1039189714Syongari TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next); 1040189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1041189714Syongari BUS_DMASYNC_PREREAD); 1042189714Syongari rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr)); 1043189714Syongari rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr)); 1044189714Syongari TXP_DESC_INC(prod, RXBUF_ENTRIES); 104580219Swpaul } 104680219Swpaul 1047189714Syongari if (prog == 0) 1048189714Syongari return; 1049189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1050189714Syongari sc->sc_cdata.txp_rxbufs_map, 1051189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1052189714Syongari prod = (prod + RXBUF_ENTRIES - 1) % RXBUF_ENTRIES; 1053189714Syongari sc->sc_rxbufprod = prod; 1054189714Syongari hv->hv_rx_buf_write_idx = htole32(TXP_IDX2OFFSET(prod)); 105580219Swpaul} 105680219Swpaul 105780219Swpaul/* 105880219Swpaul * Reclaim mbufs and entries from a transmit ring. 105980219Swpaul */ 106080219Swpaulstatic void 1061189685Syongaritxp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r) 106280219Swpaul{ 1063189714Syongari struct ifnet *ifp; 1064189714Syongari uint32_t idx; 1065189714Syongari uint32_t cons, cnt; 1066189714Syongari struct txp_tx_desc *txd; 1067189714Syongari struct txp_swdesc *sd; 106880219Swpaul 1069151772Sjhb TXP_LOCK_ASSERT(sc); 107080219Swpaul 1071189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_POSTREAD | 1072189714Syongari BUS_DMASYNC_POSTWRITE); 1073189714Syongari ifp = sc->sc_ifp; 1074189714Syongari idx = TXP_OFFSET2IDX(le32toh(*(r->r_off))); 1075189714Syongari cons = r->r_cons; 1076189714Syongari cnt = r->r_cnt; 1077189714Syongari txd = r->r_desc + cons; 1078189714Syongari sd = sc->sc_txd + cons; 1079189714Syongari 1080189714Syongari for (cnt = r->r_cnt; cons != idx && cnt > 0; cnt--) { 1081189714Syongari if ((txd->tx_flags & TX_FLAGS_TYPE_M) == TX_FLAGS_TYPE_DATA) { 1082189714Syongari if (sd->sd_mbuf != NULL) { 1083189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, 1084189714Syongari sd->sd_map, BUS_DMASYNC_POSTWRITE); 1085189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, 1086189714Syongari sd->sd_map); 1087189714Syongari m_freem(sd->sd_mbuf); 1088189714Syongari sd->sd_mbuf = NULL; 108980219Swpaul txd->tx_addrlo = 0; 109080219Swpaul txd->tx_addrhi = 0; 1091189714Syongari txd->tx_flags = 0; 109280219Swpaul } 109380219Swpaul } 1094148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 109580219Swpaul 109680219Swpaul if (++cons == TX_ENTRIES) { 109780219Swpaul txd = r->r_desc; 109880219Swpaul cons = 0; 109980219Swpaul sd = sc->sc_txd; 110080219Swpaul } else { 110180219Swpaul txd++; 110280219Swpaul sd++; 110380219Swpaul } 110480219Swpaul } 110580219Swpaul 1106189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, BUS_DMASYNC_PREREAD | 1107189714Syongari BUS_DMASYNC_PREWRITE); 110880219Swpaul r->r_cons = cons; 110980219Swpaul r->r_cnt = cnt; 111080219Swpaul if (cnt == 0) 1111189714Syongari sc->sc_watchdog_timer = 0; 111280219Swpaul} 111380219Swpaul 111480219Swpaulstatic int 1115189685Syongaritxp_shutdown(device_t dev) 111680219Swpaul{ 1117189714Syongari 1118189714Syongari return (txp_suspend(dev)); 1119189714Syongari} 1120189714Syongari 1121189714Syongaristatic int 1122189714Syongaritxp_suspend(device_t dev) 1123189714Syongari{ 112480219Swpaul struct txp_softc *sc; 1125189714Syongari struct ifnet *ifp; 1126189714Syongari uint8_t *eaddr; 1127189714Syongari uint16_t p1; 1128189714Syongari uint32_t p2; 1129189714Syongari int pmc; 1130189714Syongari uint16_t pmstat; 113180219Swpaul 113280219Swpaul sc = device_get_softc(dev); 113380219Swpaul 1134151772Sjhb TXP_LOCK(sc); 1135189714Syongari ifp = sc->sc_ifp; 1136189714Syongari txp_stop(sc); 1137189714Syongari txp_init_rings(sc); 1138189714Syongari /* Reset controller and make it reload sleep image. */ 1139189714Syongari txp_reset(sc); 1140189714Syongari /* Let controller boot from sleep image. */ 1141189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) 1142189714Syongari device_printf(sc->sc_dev, "couldn't boot sleep image\n"); 1143151772Sjhb 1144189714Syongari /* Set station address. */ 1145189714Syongari eaddr = IF_LLADDR(sc->sc_ifp); 1146189714Syongari p1 = 0; 1147189714Syongari ((uint8_t *)&p1)[1] = eaddr[0]; 1148189714Syongari ((uint8_t *)&p1)[0] = eaddr[1]; 1149189714Syongari p1 = le16toh(p1); 1150189714Syongari ((uint8_t *)&p2)[3] = eaddr[2]; 1151189714Syongari ((uint8_t *)&p2)[2] = eaddr[3]; 1152189714Syongari ((uint8_t *)&p2)[1] = eaddr[4]; 1153189714Syongari ((uint8_t *)&p2)[0] = eaddr[5]; 1154189714Syongari p2 = le32toh(p2); 1155189714Syongari txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, NULL, NULL, 1156189714Syongari NULL, TXP_CMD_WAIT); 1157189714Syongari txp_set_filter(sc); 1158189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 1159189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 1160189714Syongari txp_sleep(sc, sc->sc_ifp->if_capenable); 1161219902Sjhb if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) { 1162189714Syongari /* Request PME. */ 1163189714Syongari pmstat = pci_read_config(sc->sc_dev, 1164189714Syongari pmc + PCIR_POWER_STATUS, 2); 1165189714Syongari pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); 1166189714Syongari if ((ifp->if_capenable & IFCAP_WOL) != 0) 1167189714Syongari pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; 1168189714Syongari pci_write_config(sc->sc_dev, 1169189714Syongari pmc + PCIR_POWER_STATUS, pmstat, 2); 1170189714Syongari } 1171189714Syongari TXP_UNLOCK(sc); 117280219Swpaul 1173189714Syongari return (0); 1174189714Syongari} 1175189714Syongari 1176189714Syongaristatic int 1177189714Syongaritxp_resume(device_t dev) 1178189714Syongari{ 1179189714Syongari struct txp_softc *sc; 1180189714Syongari int pmc; 1181189714Syongari uint16_t pmstat; 1182189714Syongari 1183189714Syongari sc = device_get_softc(dev); 1184189714Syongari 1185189714Syongari TXP_LOCK(sc); 1186219902Sjhb if (pci_find_cap(sc->sc_dev, PCIY_PMG, &pmc) == 0) { 1187189714Syongari /* Disable PME and clear PME status. */ 1188189714Syongari pmstat = pci_read_config(sc->sc_dev, 1189189714Syongari pmc + PCIR_POWER_STATUS, 2); 1190189714Syongari if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { 1191189714Syongari pmstat &= ~PCIM_PSTAT_PMEENABLE; 1192189714Syongari pci_write_config(sc->sc_dev, 1193189714Syongari pmc + PCIR_POWER_STATUS, pmstat, 2); 1194189714Syongari } 1195189714Syongari } 1196189714Syongari if ((sc->sc_ifp->if_flags & IFF_UP) != 0) 1197189714Syongari txp_init_locked(sc); 1198151772Sjhb TXP_UNLOCK(sc); 119980219Swpaul 1200189688Syongari return (0); 120180219Swpaul} 120280219Swpaul 1203189714Syongaristruct txp_dmamap_arg { 1204189714Syongari bus_addr_t txp_busaddr; 1205189714Syongari}; 1206189714Syongari 1207189714Syongaristatic void 1208189714Syongaritxp_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1209189714Syongari{ 1210189714Syongari struct txp_dmamap_arg *ctx; 1211189714Syongari 1212189714Syongari if (error != 0) 1213189714Syongari return; 1214189714Syongari 1215189714Syongari KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1216189714Syongari 1217189714Syongari ctx = (struct txp_dmamap_arg *)arg; 1218189714Syongari ctx->txp_busaddr = segs[0].ds_addr; 1219189714Syongari} 1220189714Syongari 122180219Swpaulstatic int 1222189714Syongaritxp_dma_alloc(struct txp_softc *sc, char *type, bus_dma_tag_t *tag, 1223189714Syongari bus_size_t alignment, bus_size_t boundary, bus_dmamap_t *map, void **buf, 1224189714Syongari bus_size_t size, bus_addr_t *paddr) 1225189714Syongari{ 1226189714Syongari struct txp_dmamap_arg ctx; 1227189714Syongari int error; 1228189714Syongari 1229189714Syongari /* Create DMA block tag. */ 1230189714Syongari error = bus_dma_tag_create( 1231189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1232189714Syongari alignment, boundary, /* algnmnt, boundary */ 1233189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1234189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1235189714Syongari NULL, NULL, /* filter, filterarg */ 1236189714Syongari size, /* maxsize */ 1237189714Syongari 1, /* nsegments */ 1238189714Syongari size, /* maxsegsize */ 1239189714Syongari 0, /* flags */ 1240189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1241189714Syongari tag); 1242189714Syongari if (error != 0) { 1243189714Syongari device_printf(sc->sc_dev, 1244189714Syongari "could not create DMA tag for %s.\n", type); 1245189714Syongari return (error); 1246189714Syongari } 1247189714Syongari 1248189714Syongari *paddr = 0; 1249189714Syongari /* Allocate DMA'able memory and load the DMA map. */ 1250189714Syongari error = bus_dmamem_alloc(*tag, buf, BUS_DMA_WAITOK | BUS_DMA_ZERO | 1251189714Syongari BUS_DMA_COHERENT, map); 1252189714Syongari if (error != 0) { 1253189714Syongari device_printf(sc->sc_dev, 1254189714Syongari "could not allocate DMA'able memory for %s.\n", type); 1255189714Syongari return (error); 1256189714Syongari } 1257189714Syongari 1258189714Syongari ctx.txp_busaddr = 0; 1259189714Syongari error = bus_dmamap_load(*tag, *map, *(uint8_t **)buf, 1260189714Syongari size, txp_dmamap_cb, &ctx, BUS_DMA_NOWAIT); 1261189714Syongari if (error != 0 || ctx.txp_busaddr == 0) { 1262189714Syongari device_printf(sc->sc_dev, 1263189714Syongari "could not load DMA'able memory for %s.\n", type); 1264189714Syongari return (error); 1265189714Syongari } 1266189714Syongari *paddr = ctx.txp_busaddr; 1267189714Syongari 1268189714Syongari return (0); 1269189714Syongari} 1270189714Syongari 1271189714Syongaristatic void 1272267580Sjhbtxp_dma_free(struct txp_softc *sc, bus_dma_tag_t *tag, bus_dmamap_t map, 1273267580Sjhb void **buf, bus_addr_t *paddr) 1274189714Syongari{ 1275189714Syongari 1276189714Syongari if (*tag != NULL) { 1277267580Sjhb if (*paddr != 0) 1278267580Sjhb bus_dmamap_unload(*tag, map); 1279267580Sjhb if (buf != NULL) 1280267580Sjhb bus_dmamem_free(*tag, *(uint8_t **)buf, map); 1281189714Syongari *(uint8_t **)buf = NULL; 1282267580Sjhb *paddr = 0; 1283189714Syongari bus_dma_tag_destroy(*tag); 1284189714Syongari *tag = NULL; 1285189714Syongari } 1286189714Syongari} 1287189714Syongari 1288189714Syongaristatic int 1289189685Syongaritxp_alloc_rings(struct txp_softc *sc) 129080219Swpaul{ 129180219Swpaul struct txp_boot_record *boot; 129280219Swpaul struct txp_ldata *ld; 1293189714Syongari struct txp_swdesc *txd; 1294189714Syongari struct txp_rxbuf_desc *rbd; 1295189714Syongari struct txp_rx_swdesc *sd; 1296189714Syongari int error, i; 129780219Swpaul 1298189714Syongari ld = &sc->sc_ldata; 1299189714Syongari boot = ld->txp_boot; 130080219Swpaul 130180219Swpaul /* boot record */ 130280219Swpaul sc->sc_boot = boot; 130380219Swpaul 1304189714Syongari /* 1305189714Syongari * Create parent ring/DMA block tag. 1306189714Syongari * Datasheet says that all ring addresses and descriptors 1307189714Syongari * support 64bits addressing. However the controller is 1308189714Syongari * known to have no support DAC so limit DMA address space 1309189714Syongari * to 32bits. 1310189714Syongari */ 1311189714Syongari error = bus_dma_tag_create( 1312189714Syongari bus_get_dma_tag(sc->sc_dev), /* parent */ 1313189714Syongari 1, 0, /* algnmnt, boundary */ 1314189714Syongari BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1315189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1316189714Syongari NULL, NULL, /* filter, filterarg */ 1317189714Syongari BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 1318189714Syongari 0, /* nsegments */ 1319189714Syongari BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1320189714Syongari 0, /* flags */ 1321189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1322189714Syongari &sc->sc_cdata.txp_parent_tag); 1323189714Syongari if (error != 0) { 1324189714Syongari device_printf(sc->sc_dev, "could not create parent DMA tag.\n"); 1325189714Syongari return (error); 1326189714Syongari } 132780219Swpaul 1328189714Syongari /* Boot record. */ 1329189714Syongari error = txp_dma_alloc(sc, "boot record", 1330189714Syongari &sc->sc_cdata.txp_boot_tag, sizeof(uint32_t), 0, 1331189714Syongari &sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot, 1332189714Syongari sizeof(struct txp_boot_record), 1333189714Syongari &sc->sc_ldata.txp_boot_paddr); 1334189714Syongari if (error != 0) 1335189714Syongari return (error); 1336189714Syongari boot = sc->sc_ldata.txp_boot; 1337189714Syongari sc->sc_boot = boot; 1338189714Syongari 1339189714Syongari /* Host variables. */ 1340189714Syongari error = txp_dma_alloc(sc, "host variables", 1341189714Syongari &sc->sc_cdata.txp_hostvar_tag, sizeof(uint32_t), 0, 1342189714Syongari &sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar, 1343189714Syongari sizeof(struct txp_hostvar), 1344189714Syongari &sc->sc_ldata.txp_hostvar_paddr); 1345189714Syongari if (error != 0) 1346189714Syongari return (error); 1347189714Syongari boot->br_hostvar_lo = 1348189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_hostvar_paddr)); 1349189714Syongari boot->br_hostvar_hi = 1350189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_hostvar_paddr)); 1351189714Syongari sc->sc_hostvar = sc->sc_ldata.txp_hostvar; 1352189714Syongari 1353189714Syongari /* Hi priority tx ring. */ 1354189714Syongari error = txp_dma_alloc(sc, "hi priority tx ring", 1355189714Syongari &sc->sc_cdata.txp_txhiring_tag, sizeof(struct txp_tx_desc), 0, 1356189714Syongari &sc->sc_cdata.txp_txhiring_map, (void **)&sc->sc_ldata.txp_txhiring, 1357189714Syongari sizeof(struct txp_tx_desc) * TX_ENTRIES, 1358189714Syongari &sc->sc_ldata.txp_txhiring_paddr); 1359189714Syongari if (error != 0) 1360189714Syongari return (error); 1361189714Syongari boot->br_txhipri_lo = 1362189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txhiring_paddr)); 1363189714Syongari boot->br_txhipri_hi = 1364189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txhiring_paddr)); 1365189714Syongari boot->br_txhipri_siz = 1366189714Syongari htole32(TX_ENTRIES * sizeof(struct txp_tx_desc)); 1367189714Syongari sc->sc_txhir.r_tag = sc->sc_cdata.txp_txhiring_tag; 1368189714Syongari sc->sc_txhir.r_map = sc->sc_cdata.txp_txhiring_map; 136980219Swpaul sc->sc_txhir.r_reg = TXP_H2A_1; 1370189714Syongari sc->sc_txhir.r_desc = sc->sc_ldata.txp_txhiring; 137180219Swpaul sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0; 137280219Swpaul sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx; 137380219Swpaul 1374189714Syongari /* Low priority tx ring. */ 1375189714Syongari error = txp_dma_alloc(sc, "low priority tx ring", 1376189714Syongari &sc->sc_cdata.txp_txloring_tag, sizeof(struct txp_tx_desc), 0, 1377189714Syongari &sc->sc_cdata.txp_txloring_map, (void **)&sc->sc_ldata.txp_txloring, 1378189714Syongari sizeof(struct txp_tx_desc) * TX_ENTRIES, 1379189714Syongari &sc->sc_ldata.txp_txloring_paddr); 1380189714Syongari if (error != 0) 1381189714Syongari return (error); 1382189714Syongari boot->br_txlopri_lo = 1383189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_txloring_paddr)); 1384189714Syongari boot->br_txlopri_hi = 1385189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_txloring_paddr)); 1386189714Syongari boot->br_txlopri_siz = 1387189714Syongari htole32(TX_ENTRIES * sizeof(struct txp_tx_desc)); 1388189714Syongari sc->sc_txlor.r_tag = sc->sc_cdata.txp_txloring_tag; 1389189714Syongari sc->sc_txlor.r_map = sc->sc_cdata.txp_txloring_map; 139080219Swpaul sc->sc_txlor.r_reg = TXP_H2A_3; 1391189714Syongari sc->sc_txlor.r_desc = sc->sc_ldata.txp_txloring; 139280219Swpaul sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0; 139380219Swpaul sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx; 139480219Swpaul 1395189714Syongari /* High priority rx ring. */ 1396189714Syongari error = txp_dma_alloc(sc, "hi priority rx ring", 1397196721Syongari &sc->sc_cdata.txp_rxhiring_tag, 1398196721Syongari roundup(sizeof(struct txp_rx_desc), 16), 0, 1399189714Syongari &sc->sc_cdata.txp_rxhiring_map, (void **)&sc->sc_ldata.txp_rxhiring, 1400189714Syongari sizeof(struct txp_rx_desc) * RX_ENTRIES, 1401189714Syongari &sc->sc_ldata.txp_rxhiring_paddr); 1402189714Syongari if (error != 0) 1403189714Syongari return (error); 1404189714Syongari boot->br_rxhipri_lo = 1405189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxhiring_paddr)); 1406189714Syongari boot->br_rxhipri_hi = 1407189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxhiring_paddr)); 1408189714Syongari boot->br_rxhipri_siz = 1409189714Syongari htole32(RX_ENTRIES * sizeof(struct txp_rx_desc)); 1410189714Syongari sc->sc_rxhir.r_tag = sc->sc_cdata.txp_rxhiring_tag; 1411189714Syongari sc->sc_rxhir.r_map = sc->sc_cdata.txp_rxhiring_map; 1412189714Syongari sc->sc_rxhir.r_desc = sc->sc_ldata.txp_rxhiring; 141380219Swpaul sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx; 141480219Swpaul sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx; 141580219Swpaul 1416189714Syongari /* Low priority rx ring. */ 1417189714Syongari error = txp_dma_alloc(sc, "low priority rx ring", 1418196721Syongari &sc->sc_cdata.txp_rxloring_tag, 1419196721Syongari roundup(sizeof(struct txp_rx_desc), 16), 0, 1420189714Syongari &sc->sc_cdata.txp_rxloring_map, (void **)&sc->sc_ldata.txp_rxloring, 1421189714Syongari sizeof(struct txp_rx_desc) * RX_ENTRIES, 1422189714Syongari &sc->sc_ldata.txp_rxloring_paddr); 1423189714Syongari if (error != 0) 1424189714Syongari return (error); 1425189714Syongari boot->br_rxlopri_lo = 1426189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxloring_paddr)); 1427189714Syongari boot->br_rxlopri_hi = 1428189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxloring_paddr)); 1429189714Syongari boot->br_rxlopri_siz = 1430189714Syongari htole32(RX_ENTRIES * sizeof(struct txp_rx_desc)); 1431189714Syongari sc->sc_rxlor.r_tag = sc->sc_cdata.txp_rxloring_tag; 1432189714Syongari sc->sc_rxlor.r_map = sc->sc_cdata.txp_rxloring_map; 1433189714Syongari sc->sc_rxlor.r_desc = sc->sc_ldata.txp_rxloring; 143480219Swpaul sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx; 143580219Swpaul sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx; 143680219Swpaul 1437189714Syongari /* Command ring. */ 1438189714Syongari error = txp_dma_alloc(sc, "command ring", 1439189714Syongari &sc->sc_cdata.txp_cmdring_tag, sizeof(struct txp_cmd_desc), 0, 1440189714Syongari &sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring, 1441189714Syongari sizeof(struct txp_cmd_desc) * CMD_ENTRIES, 1442189714Syongari &sc->sc_ldata.txp_cmdring_paddr); 1443189714Syongari if (error != 0) 1444189714Syongari return (error); 1445189714Syongari boot->br_cmd_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_cmdring_paddr)); 1446189714Syongari boot->br_cmd_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_cmdring_paddr)); 1447189714Syongari boot->br_cmd_siz = htole32(CMD_ENTRIES * sizeof(struct txp_cmd_desc)); 1448189714Syongari sc->sc_cmdring.base = sc->sc_ldata.txp_cmdring; 144980219Swpaul sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc); 145080219Swpaul sc->sc_cmdring.lastwrite = 0; 145180219Swpaul 1452189714Syongari /* Response ring. */ 1453189714Syongari error = txp_dma_alloc(sc, "response ring", 1454189714Syongari &sc->sc_cdata.txp_rspring_tag, sizeof(struct txp_rsp_desc), 0, 1455189714Syongari &sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring, 1456189714Syongari sizeof(struct txp_rsp_desc) * RSP_ENTRIES, 1457189714Syongari &sc->sc_ldata.txp_rspring_paddr); 1458189714Syongari if (error != 0) 1459189714Syongari return (error); 1460189714Syongari boot->br_resp_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rspring_paddr)); 1461189714Syongari boot->br_resp_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rspring_paddr)); 1462189714Syongari boot->br_resp_siz = htole32(RSP_ENTRIES * sizeof(struct txp_rsp_desc)); 1463189714Syongari sc->sc_rspring.base = sc->sc_ldata.txp_rspring; 146480219Swpaul sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc); 146580219Swpaul sc->sc_rspring.lastwrite = 0; 146680219Swpaul 1467189714Syongari /* Receive buffer ring. */ 1468189714Syongari error = txp_dma_alloc(sc, "receive buffer ring", 1469189714Syongari &sc->sc_cdata.txp_rxbufs_tag, sizeof(struct txp_rxbuf_desc), 0, 1470189714Syongari &sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs, 1471189714Syongari sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES, 1472189714Syongari &sc->sc_ldata.txp_rxbufs_paddr); 1473189714Syongari if (error != 0) 1474189714Syongari return (error); 1475189714Syongari boot->br_rxbuf_lo = 1476189714Syongari htole32(TXP_ADDR_LO(sc->sc_ldata.txp_rxbufs_paddr)); 1477189714Syongari boot->br_rxbuf_hi = 1478189714Syongari htole32(TXP_ADDR_HI(sc->sc_ldata.txp_rxbufs_paddr)); 1479189714Syongari boot->br_rxbuf_siz = 1480189714Syongari htole32(RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc)); 1481189714Syongari sc->sc_rxbufs = sc->sc_ldata.txp_rxbufs; 148280219Swpaul 1483189714Syongari /* Zero ring. */ 1484189714Syongari error = txp_dma_alloc(sc, "zero buffer", 1485189714Syongari &sc->sc_cdata.txp_zero_tag, sizeof(uint32_t), 0, 1486189714Syongari &sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero, 1487189714Syongari sizeof(uint32_t), &sc->sc_ldata.txp_zero_paddr); 1488189714Syongari if (error != 0) 1489189714Syongari return (error); 1490189714Syongari boot->br_zero_lo = htole32(TXP_ADDR_LO(sc->sc_ldata.txp_zero_paddr)); 1491189714Syongari boot->br_zero_hi = htole32(TXP_ADDR_HI(sc->sc_ldata.txp_zero_paddr)); 1492189714Syongari 1493189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_boot_tag, sc->sc_cdata.txp_boot_map, 1494189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1495189714Syongari 1496189714Syongari /* Create Tx buffers. */ 1497189714Syongari error = bus_dma_tag_create( 1498189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1499189714Syongari 1, 0, /* algnmnt, boundary */ 1500189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1501189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1502189714Syongari NULL, NULL, /* filter, filterarg */ 1503189714Syongari MCLBYTES * TXP_MAXTXSEGS, /* maxsize */ 1504189714Syongari TXP_MAXTXSEGS, /* nsegments */ 1505189714Syongari MCLBYTES, /* maxsegsize */ 1506189714Syongari 0, /* flags */ 1507189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1508189714Syongari &sc->sc_cdata.txp_tx_tag); 1509189714Syongari if (error != 0) { 1510189714Syongari device_printf(sc->sc_dev, "could not create Tx DMA tag.\n"); 1511189714Syongari goto fail; 1512189714Syongari } 1513189714Syongari 1514189714Syongari /* Create tag for Rx buffers. */ 1515189714Syongari error = bus_dma_tag_create( 1516189714Syongari sc->sc_cdata.txp_parent_tag, /* parent */ 1517189714Syongari TXP_RXBUF_ALIGN, 0, /* algnmnt, boundary */ 1518189714Syongari BUS_SPACE_MAXADDR, /* lowaddr */ 1519189714Syongari BUS_SPACE_MAXADDR, /* highaddr */ 1520189714Syongari NULL, NULL, /* filter, filterarg */ 1521189714Syongari MCLBYTES, /* maxsize */ 1522189714Syongari 1, /* nsegments */ 1523189714Syongari MCLBYTES, /* maxsegsize */ 1524189714Syongari 0, /* flags */ 1525189714Syongari NULL, NULL, /* lockfunc, lockarg */ 1526189714Syongari &sc->sc_cdata.txp_rx_tag); 1527189714Syongari if (error != 0) { 1528189714Syongari device_printf(sc->sc_dev, "could not create Rx DMA tag.\n"); 1529189714Syongari goto fail; 1530189714Syongari } 1531189714Syongari 1532189714Syongari /* Create DMA maps for Tx buffers. */ 1533189714Syongari for (i = 0; i < TX_ENTRIES; i++) { 1534189714Syongari txd = &sc->sc_txd[i]; 1535189714Syongari txd->sd_mbuf = NULL; 1536189714Syongari txd->sd_map = NULL; 1537189714Syongari error = bus_dmamap_create(sc->sc_cdata.txp_tx_tag, 0, 1538189714Syongari &txd->sd_map); 1539189714Syongari if (error != 0) { 1540189714Syongari device_printf(sc->sc_dev, 1541189714Syongari "could not create Tx dmamap.\n"); 1542189714Syongari goto fail; 1543189714Syongari } 1544189714Syongari } 1545189714Syongari 1546189714Syongari /* Create DMA maps for Rx buffers. */ 154780219Swpaul for (i = 0; i < RXBUF_ENTRIES; i++) { 1548189714Syongari sd = malloc(sizeof(struct txp_rx_swdesc), M_DEVBUF, 1549189714Syongari M_NOWAIT | M_ZERO); 1550189714Syongari if (sd == NULL) { 1551189714Syongari error = ENOMEM; 1552189714Syongari goto fail; 1553189714Syongari } 1554189714Syongari /* 1555189714Syongari * The virtual address part of descriptor is not used 1556189714Syongari * by hardware so use that to save an ring entry. We 1557189714Syongari * need bcopy here otherwise the address wouldn't be 1558189714Syongari * valid on big-endian architectures. 1559189714Syongari */ 1560189714Syongari rbd = sc->sc_rxbufs + i; 1561189714Syongari bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd)); 156280457Swpaul sd->sd_mbuf = NULL; 1563189714Syongari sd->sd_map = NULL; 1564189714Syongari error = bus_dmamap_create(sc->sc_cdata.txp_rx_tag, 0, 1565189714Syongari &sd->sd_map); 1566189714Syongari if (error != 0) { 1567189714Syongari device_printf(sc->sc_dev, 1568189714Syongari "could not create Rx dmamap.\n"); 1569189714Syongari goto fail; 1570189714Syongari } 1571189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 157280219Swpaul } 1573189714Syongari 1574189714Syongarifail: 1575189714Syongari return (error); 1576189714Syongari} 1577189714Syongari 1578189714Syongaristatic void 1579189714Syongaritxp_init_rings(struct txp_softc *sc) 1580189714Syongari{ 1581189714Syongari 1582189714Syongari bzero(sc->sc_ldata.txp_hostvar, sizeof(struct txp_hostvar)); 1583189714Syongari bzero(sc->sc_ldata.txp_zero, sizeof(uint32_t)); 1584189714Syongari sc->sc_txhir.r_cons = 0; 1585189714Syongari sc->sc_txhir.r_prod = 0; 1586189714Syongari sc->sc_txhir.r_cnt = 0; 1587189714Syongari sc->sc_txlor.r_cons = 0; 1588189714Syongari sc->sc_txlor.r_prod = 0; 1589189714Syongari sc->sc_txlor.r_cnt = 0; 1590189714Syongari sc->sc_cmdring.lastwrite = 0; 1591189714Syongari sc->sc_rspring.lastwrite = 0; 159280219Swpaul sc->sc_rxbufprod = 0; 1593189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1594189714Syongari sc->sc_cdata.txp_hostvar_map, 1595189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1596189714Syongari} 159780219Swpaul 1598189714Syongaristatic int 1599189714Syongaritxp_wait(struct txp_softc *sc, uint32_t state) 1600189714Syongari{ 1601189714Syongari uint32_t reg; 1602189714Syongari int i; 160380219Swpaul 1604189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 1605189714Syongari reg = READ_REG(sc, TXP_A2H_0); 1606189714Syongari if (reg == state) 160780219Swpaul break; 160880219Swpaul DELAY(50); 160980219Swpaul } 161080219Swpaul 1611189714Syongari return (i == TXP_TIMEOUT ? ETIMEDOUT : 0); 1612189714Syongari} 161380219Swpaul 1614189714Syongaristatic void 1615189714Syongaritxp_free_rings(struct txp_softc *sc) 1616189714Syongari{ 1617189714Syongari struct txp_swdesc *txd; 1618189714Syongari struct txp_rx_swdesc *sd; 1619189714Syongari int i; 162080219Swpaul 1621189714Syongari /* Tx buffers. */ 1622189714Syongari if (sc->sc_cdata.txp_tx_tag != NULL) { 1623189714Syongari for (i = 0; i < TX_ENTRIES; i++) { 1624189714Syongari txd = &sc->sc_txd[i]; 1625189714Syongari if (txd->sd_map != NULL) { 1626189714Syongari bus_dmamap_destroy(sc->sc_cdata.txp_tx_tag, 1627189714Syongari txd->sd_map); 1628189714Syongari txd->sd_map = NULL; 1629189714Syongari } 1630189714Syongari } 1631189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_tx_tag); 1632189714Syongari sc->sc_cdata.txp_tx_tag = NULL; 163380219Swpaul } 1634189714Syongari /* Rx buffers. */ 1635189714Syongari if (sc->sc_cdata.txp_rx_tag != NULL) { 1636189714Syongari if (sc->sc_rxbufs != NULL) { 1637189714Syongari KASSERT(TAILQ_FIRST(&sc->sc_busy_list) == NULL, 1638189714Syongari ("%s : still have busy Rx buffers", __func__)); 1639189714Syongari while ((sd = TAILQ_FIRST(&sc->sc_free_list)) != NULL) { 1640189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1641189714Syongari if (sd->sd_map != NULL) { 1642189714Syongari bus_dmamap_destroy( 1643189714Syongari sc->sc_cdata.txp_rx_tag, 1644189714Syongari sd->sd_map); 1645189714Syongari sd->sd_map = NULL; 1646189714Syongari } 1647189714Syongari free(sd, M_DEVBUF); 1648189714Syongari } 1649189714Syongari } 1650189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_rx_tag); 1651189714Syongari sc->sc_cdata.txp_rx_tag = NULL; 165280219Swpaul } 165380219Swpaul 1654189714Syongari /* Hi priority Tx ring. */ 1655189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_txhiring_tag, 1656267580Sjhb sc->sc_cdata.txp_txhiring_map, 1657267580Sjhb (void **)&sc->sc_ldata.txp_txhiring, 1658267580Sjhb &sc->sc_ldata.txp_txhiring_paddr); 1659189714Syongari /* Low priority Tx ring. */ 1660189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_txloring_tag, 1661267580Sjhb sc->sc_cdata.txp_txloring_map, 1662267580Sjhb (void **)&sc->sc_ldata.txp_txloring, 1663267580Sjhb &sc->sc_ldata.txp_txloring_paddr); 1664189714Syongari /* Hi priority Rx ring. */ 1665189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxhiring_tag, 1666267580Sjhb sc->sc_cdata.txp_rxhiring_map, 1667267580Sjhb (void **)&sc->sc_ldata.txp_rxhiring, 1668267580Sjhb &sc->sc_ldata.txp_rxhiring_paddr); 1669189714Syongari /* Low priority Rx ring. */ 1670189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxloring_tag, 1671267580Sjhb sc->sc_cdata.txp_rxloring_map, 1672267580Sjhb (void **)&sc->sc_ldata.txp_rxloring, 1673267580Sjhb &sc->sc_ldata.txp_rxloring_paddr); 1674189714Syongari /* Receive buffer ring. */ 1675189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rxbufs_tag, 1676267580Sjhb sc->sc_cdata.txp_rxbufs_map, (void **)&sc->sc_ldata.txp_rxbufs, 1677267580Sjhb &sc->sc_ldata.txp_rxbufs_paddr); 1678189714Syongari /* Command ring. */ 1679189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_cmdring_tag, 1680267580Sjhb sc->sc_cdata.txp_cmdring_map, (void **)&sc->sc_ldata.txp_cmdring, 1681267580Sjhb &sc->sc_ldata.txp_cmdring_paddr); 1682189714Syongari /* Response ring. */ 1683189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_rspring_tag, 1684267580Sjhb sc->sc_cdata.txp_rspring_map, (void **)&sc->sc_ldata.txp_rspring, 1685267580Sjhb &sc->sc_ldata.txp_rspring_paddr); 1686189714Syongari /* Zero ring. */ 1687189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_zero_tag, 1688267580Sjhb sc->sc_cdata.txp_zero_map, (void **)&sc->sc_ldata.txp_zero, 1689267580Sjhb &sc->sc_ldata.txp_zero_paddr); 1690189714Syongari /* Host variables. */ 1691189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_hostvar_tag, 1692267580Sjhb sc->sc_cdata.txp_hostvar_map, (void **)&sc->sc_ldata.txp_hostvar, 1693267580Sjhb &sc->sc_ldata.txp_hostvar_paddr); 1694189714Syongari /* Boot record. */ 1695189714Syongari txp_dma_free(sc, &sc->sc_cdata.txp_boot_tag, 1696267580Sjhb sc->sc_cdata.txp_boot_map, (void **)&sc->sc_ldata.txp_boot, 1697267580Sjhb &sc->sc_ldata.txp_boot_paddr); 169880219Swpaul 1699189714Syongari if (sc->sc_cdata.txp_parent_tag != NULL) { 1700189714Syongari bus_dma_tag_destroy(sc->sc_cdata.txp_parent_tag); 1701189714Syongari sc->sc_cdata.txp_parent_tag = NULL; 1702189714Syongari } 1703189714Syongari 170480219Swpaul} 170580219Swpaul 170680219Swpaulstatic int 1707189685Syongaritxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 170880219Swpaul{ 170980219Swpaul struct txp_softc *sc = ifp->if_softc; 171080219Swpaul struct ifreq *ifr = (struct ifreq *)data; 1711189714Syongari int capenable, error = 0, mask; 171280219Swpaul 1713189714Syongari switch(command) { 171480219Swpaul case SIOCSIFFLAGS: 1715151772Sjhb TXP_LOCK(sc); 1716189714Syongari if ((ifp->if_flags & IFF_UP) != 0) { 1717189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 1718189714Syongari if (((ifp->if_flags ^ sc->sc_if_flags) 1719189714Syongari & (IFF_PROMISC | IFF_ALLMULTI)) != 0) 1720189714Syongari txp_set_filter(sc); 1721189714Syongari } else { 1722189714Syongari if ((sc->sc_flags & TXP_FLAG_DETACH) == 0) 1723189714Syongari txp_init_locked(sc); 1724189714Syongari } 172580219Swpaul } else { 1726189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 172780219Swpaul txp_stop(sc); 172880219Swpaul } 1729189714Syongari sc->sc_if_flags = ifp->if_flags; 1730151772Sjhb TXP_UNLOCK(sc); 173180219Swpaul break; 173280219Swpaul case SIOCADDMULTI: 173380219Swpaul case SIOCDELMULTI: 173480219Swpaul /* 173580219Swpaul * Multicast list has changed; set the hardware 173680219Swpaul * filter accordingly. 173780219Swpaul */ 1738151772Sjhb TXP_LOCK(sc); 1739189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1740189714Syongari txp_set_filter(sc); 1741151772Sjhb TXP_UNLOCK(sc); 174280219Swpaul break; 1743189714Syongari case SIOCSIFCAP: 1744189714Syongari TXP_LOCK(sc); 1745189714Syongari capenable = ifp->if_capenable; 1746189714Syongari mask = ifr->ifr_reqcap ^ ifp->if_capenable; 1747189714Syongari if ((mask & IFCAP_TXCSUM) != 0 && 1748189714Syongari (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { 1749189714Syongari ifp->if_capenable ^= IFCAP_TXCSUM; 1750189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) 1751189714Syongari ifp->if_hwassist |= TXP_CSUM_FEATURES; 1752189714Syongari else 1753189714Syongari ifp->if_hwassist &= ~TXP_CSUM_FEATURES; 1754189714Syongari } 1755189714Syongari if ((mask & IFCAP_RXCSUM) != 0 && 1756189714Syongari (ifp->if_capabilities & IFCAP_RXCSUM) != 0) 1757189714Syongari ifp->if_capenable ^= IFCAP_RXCSUM; 1758189714Syongari if ((mask & IFCAP_WOL_MAGIC) != 0 && 1759189714Syongari (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) 1760189714Syongari ifp->if_capenable ^= IFCAP_WOL_MAGIC; 1761189714Syongari if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && 1762189714Syongari (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) 1763189714Syongari ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; 1764189714Syongari if ((mask & IFCAP_VLAN_HWCSUM) != 0 && 1765189714Syongari (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) 1766189714Syongari ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; 1767189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) == 0) 1768189714Syongari ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; 1769189714Syongari if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) 1770189714Syongari ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; 1771189714Syongari if (capenable != ifp->if_capenable) 1772189714Syongari txp_set_capabilities(sc); 1773189714Syongari TXP_UNLOCK(sc); 1774189714Syongari VLAN_CAPABILITIES(ifp); 1775189714Syongari break; 177680219Swpaul case SIOCGIFMEDIA: 177780219Swpaul case SIOCSIFMEDIA: 177880219Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command); 177980219Swpaul break; 178080219Swpaul default: 1781106937Ssam error = ether_ioctl(ifp, command, data); 178280219Swpaul break; 178380219Swpaul } 178480219Swpaul 1785189688Syongari return (error); 178680219Swpaul} 178780219Swpaul 178880219Swpaulstatic int 1789189685Syongaritxp_rxring_fill(struct txp_softc *sc) 179080219Swpaul{ 1791189714Syongari struct txp_rxbuf_desc *rbd; 1792189714Syongari struct txp_rx_swdesc *sd; 1793189714Syongari bus_dma_segment_t segs[1]; 1794189714Syongari int error, i, nsegs; 179580219Swpaul 1796151772Sjhb TXP_LOCK_ASSERT(sc); 179780219Swpaul 1798189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1799189714Syongari sc->sc_cdata.txp_rxbufs_map, 1800189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1801189714Syongari 180280219Swpaul for (i = 0; i < RXBUF_ENTRIES; i++) { 1803189714Syongari sd = TAILQ_FIRST(&sc->sc_free_list); 1804189714Syongari if (sd == NULL) 1805189714Syongari return (ENOMEM); 1806189714Syongari rbd = sc->sc_rxbufs + i; 1807189714Syongari bcopy(&sd, (u_long *)&rbd->rb_vaddrlo, sizeof(sd)); 1808189714Syongari KASSERT(sd->sd_mbuf == NULL, 1809189714Syongari ("%s : Rx buffer ring corrupted", __func__)); 1810243857Sglebius sd->sd_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 181180219Swpaul if (sd->sd_mbuf == NULL) 1812189714Syongari return (ENOMEM); 181380219Swpaul sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES; 1814189714Syongari#ifndef __NO_STRICT_ALIGNMENT 1815189714Syongari m_adj(sd->sd_mbuf, TXP_RXBUF_ALIGN); 1816189714Syongari#endif 1817189714Syongari if ((error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_rx_tag, 1818189714Syongari sd->sd_map, sd->sd_mbuf, segs, &nsegs, 0)) != 0) { 1819189714Syongari m_freem(sd->sd_mbuf); 1820189714Syongari sd->sd_mbuf = NULL; 1821189714Syongari return (error); 1822189714Syongari } 1823189714Syongari KASSERT(nsegs == 1, ("%s : %d segments returned!", __func__, 1824189714Syongari nsegs)); 1825189714Syongari TAILQ_REMOVE(&sc->sc_free_list, sd, sd_next); 1826189714Syongari TAILQ_INSERT_TAIL(&sc->sc_busy_list, sd, sd_next); 1827189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1828189714Syongari BUS_DMASYNC_PREREAD); 1829189714Syongari rbd->rb_paddrlo = htole32(TXP_ADDR_LO(segs[0].ds_addr)); 1830189714Syongari rbd->rb_paddrhi = htole32(TXP_ADDR_HI(segs[0].ds_addr)); 183180219Swpaul } 183280219Swpaul 1833189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rxbufs_tag, 1834189714Syongari sc->sc_cdata.txp_rxbufs_map, 1835189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1836189714Syongari sc->sc_rxbufprod = RXBUF_ENTRIES - 1; 1837189714Syongari sc->sc_hostvar->hv_rx_buf_write_idx = 1838189714Syongari htole32(TXP_IDX2OFFSET(RXBUF_ENTRIES - 1)); 183980219Swpaul 1840189688Syongari return (0); 184180219Swpaul} 184280219Swpaul 184380219Swpaulstatic void 1844189685Syongaritxp_rxring_empty(struct txp_softc *sc) 184580219Swpaul{ 1846189714Syongari struct txp_rx_swdesc *sd; 1847189714Syongari int cnt; 184880219Swpaul 1849151772Sjhb TXP_LOCK_ASSERT(sc); 1850189714Syongari 185180219Swpaul if (sc->sc_rxbufs == NULL) 185280219Swpaul return; 1853189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1854189714Syongari sc->sc_cdata.txp_hostvar_map, 1855189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 185680219Swpaul 1857189714Syongari /* Release allocated Rx buffers. */ 1858189714Syongari cnt = 0; 1859189714Syongari while ((sd = TAILQ_FIRST(&sc->sc_busy_list)) != NULL) { 1860189714Syongari TAILQ_REMOVE(&sc->sc_busy_list, sd, sd_next); 1861189714Syongari KASSERT(sd->sd_mbuf != NULL, 1862189714Syongari ("%s : Rx buffer ring corrupted", __func__)); 1863189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rx_tag, sd->sd_map, 1864189714Syongari BUS_DMASYNC_POSTREAD); 1865189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_rx_tag, sd->sd_map); 1866189714Syongari m_freem(sd->sd_mbuf); 1867189714Syongari sd->sd_mbuf = NULL; 1868189714Syongari TAILQ_INSERT_TAIL(&sc->sc_free_list, sd, sd_next); 1869189714Syongari cnt++; 187080219Swpaul } 187180219Swpaul} 187280219Swpaul 187380219Swpaulstatic void 1874189685Syongaritxp_init(void *xsc) 187580219Swpaul{ 187680219Swpaul struct txp_softc *sc; 1877151772Sjhb 1878151772Sjhb sc = xsc; 1879151772Sjhb TXP_LOCK(sc); 1880151772Sjhb txp_init_locked(sc); 1881151772Sjhb TXP_UNLOCK(sc); 1882151772Sjhb} 1883151772Sjhb 1884151772Sjhbstatic void 1885189685Syongaritxp_init_locked(struct txp_softc *sc) 1886151772Sjhb{ 188780219Swpaul struct ifnet *ifp; 1888189714Syongari uint8_t *eaddr; 1889189689Syongari uint16_t p1; 1890189689Syongari uint32_t p2; 1891189714Syongari int error; 189280219Swpaul 1893151772Sjhb TXP_LOCK_ASSERT(sc); 1894147256Sbrooks ifp = sc->sc_ifp; 189580219Swpaul 1896189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 189780219Swpaul return; 189880219Swpaul 1899189714Syongari /* Initialize ring structure. */ 1900189714Syongari txp_init_rings(sc); 1901189714Syongari /* Wakeup controller. */ 1902189714Syongari WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_WAKEUP); 1903189714Syongari TXP_BARRIER(sc, TXP_H2A_0, 4, BUS_SPACE_BARRIER_WRITE); 1904189714Syongari /* 1905189714Syongari * It seems that earlier NV image can go back to online from 1906189714Syongari * wakeup command but newer ones require controller reset. 1907189714Syongari * So jut reset controller again. 1908189714Syongari */ 1909189714Syongari if (txp_reset(sc) != 0) 1910189714Syongari goto init_fail; 1911189714Syongari /* Download firmware. */ 1912189714Syongari error = txp_download_fw(sc); 1913189714Syongari if (error != 0) { 1914189714Syongari device_printf(sc->sc_dev, "could not download firmware.\n"); 1915189714Syongari goto init_fail; 1916189714Syongari } 1917189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1918189714Syongari sc->sc_cdata.txp_hostvar_map, 1919189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1920189714Syongari if ((error = txp_rxring_fill(sc)) != 0) { 1921189714Syongari device_printf(sc->sc_dev, "no memory for Rx buffers.\n"); 1922189714Syongari goto init_fail; 1923189714Syongari } 1924189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 1925189714Syongari sc->sc_cdata.txp_hostvar_map, 1926189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1927189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_BOOT) != 0) { 1928189714Syongari device_printf(sc->sc_dev, "could not boot firmware.\n"); 1929189714Syongari goto init_fail; 1930189714Syongari } 193180219Swpaul 1932189714Syongari /* 1933189714Syongari * Quite contrary to Typhoon T2 software functional specification, 1934189714Syongari * it seems that TXP_CMD_RECV_BUFFER_CONTROL command is not 1935189714Syongari * implemented in the firmware. This means driver should have to 1936189714Syongari * handle misaligned frames on alignment architectures. AFAIK this 1937189714Syongari * is the only controller manufactured by 3Com that has this stupid 1938189714Syongari * bug. 3Com should fix this. 1939189714Syongari */ 1940189714Syongari if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0, 1941189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1942189714Syongari goto init_fail; 1943189714Syongari /* Undocumented command(interrupt coalescing disable?) - From Linux. */ 1944189714Syongari if (txp_command(sc, TXP_CMD_FILTER_DEFINE, 0, 0, 0, NULL, NULL, NULL, 1945189714Syongari TXP_CMD_NOWAIT) != 0) 1946189714Syongari goto init_fail; 194780219Swpaul 194880219Swpaul /* Set station address. */ 1949189714Syongari eaddr = IF_LLADDR(sc->sc_ifp); 1950189714Syongari p1 = 0; 1951189714Syongari ((uint8_t *)&p1)[1] = eaddr[0]; 1952189714Syongari ((uint8_t *)&p1)[0] = eaddr[1]; 1953189714Syongari p1 = le16toh(p1); 1954189714Syongari ((uint8_t *)&p2)[3] = eaddr[2]; 1955189714Syongari ((uint8_t *)&p2)[2] = eaddr[3]; 1956189714Syongari ((uint8_t *)&p2)[1] = eaddr[4]; 1957189714Syongari ((uint8_t *)&p2)[0] = eaddr[5]; 1958189714Syongari p2 = le32toh(p2); 1959189714Syongari if (txp_command(sc, TXP_CMD_STATION_ADDRESS_WRITE, p1, p2, 0, 1960189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1961189714Syongari goto init_fail; 196280219Swpaul 196380219Swpaul txp_set_filter(sc); 1964189714Syongari txp_set_capabilities(sc); 196580219Swpaul 1966189714Syongari if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0, 1967189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT)) 1968189714Syongari goto init_fail; 1969189714Syongari if (txp_command(sc, TXP_CMD_XCVR_SELECT, sc->sc_xcvr, 0, 0, 1970189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT) != 0) 1971189714Syongari goto init_fail; 1972189714Syongari if (txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1973189714Syongari TXP_CMD_NOWAIT) != 0) 1974189714Syongari goto init_fail; 1975189714Syongari if (txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1976189714Syongari TXP_CMD_NOWAIT) != 0) 1977189714Syongari goto init_fail; 197880219Swpaul 1979189714Syongari /* Ack all pending interrupts and enable interrupts. */ 1980189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 1981189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTRS); 1982189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_NONE); 198380219Swpaul 1984148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 1985148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 198680219Swpaul 1987151772Sjhb callout_reset(&sc->sc_tick, hz, txp_tick, sc); 1988189714Syongari return; 1989189714Syongari 1990189714Syongariinit_fail: 1991189714Syongari txp_rxring_empty(sc); 1992189714Syongari txp_init_rings(sc); 1993189714Syongari txp_reset(sc); 1994189714Syongari WRITE_REG(sc, TXP_IMR, TXP_INTR_ALL); 199580219Swpaul} 199680219Swpaul 199780219Swpaulstatic void 1998189685Syongaritxp_tick(void *vsc) 199980219Swpaul{ 2000189714Syongari struct txp_softc *sc; 2001189714Syongari struct ifnet *ifp; 2002189714Syongari struct txp_rsp_desc *rsp; 200380219Swpaul struct txp_ext_desc *ext; 2004189714Syongari int link; 200580219Swpaul 2006189714Syongari sc = vsc; 2007151772Sjhb TXP_LOCK_ASSERT(sc); 2008189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2009189714Syongari sc->sc_cdata.txp_hostvar_map, 2010189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 201180219Swpaul txp_rxbuf_reclaim(sc); 2012189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2013189714Syongari sc->sc_cdata.txp_hostvar_map, 2014189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 201580219Swpaul 2016189714Syongari ifp = sc->sc_ifp; 2017189714Syongari rsp = NULL; 2018189714Syongari 2019189714Syongari link = sc->sc_flags & TXP_FLAG_LINK; 2020189714Syongari if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0, 2021189714Syongari &rsp, TXP_CMD_WAIT)) 202280219Swpaul goto out; 202380219Swpaul if (rsp->rsp_numdesc != 6) 202480219Swpaul goto out; 2025189714Syongari txp_stats_update(sc, rsp); 2026189714Syongari if (link == 0 && (sc->sc_flags & TXP_FLAG_LINK) != 0) { 2027189714Syongari ext = (struct txp_ext_desc *)(rsp + 1); 2028189714Syongari /* Update baudrate with resolved speed. */ 2029189714Syongari if ((ext[5].ext_2 & 0x02) != 0) 2030189714Syongari ifp->if_baudrate = IF_Mbps(100); 2031189714Syongari else 2032189714Syongari ifp->if_baudrate = IF_Mbps(10); 2033189714Syongari } 203480219Swpaul 203580219Swpaulout: 203680219Swpaul if (rsp != NULL) 203780219Swpaul free(rsp, M_DEVBUF); 2038189714Syongari txp_watchdog(sc); 2039151772Sjhb callout_reset(&sc->sc_tick, hz, txp_tick, sc); 204080219Swpaul} 204180219Swpaul 204280219Swpaulstatic void 2043189685Syongaritxp_start(struct ifnet *ifp) 204480219Swpaul{ 2045151772Sjhb struct txp_softc *sc; 2046151772Sjhb 2047151772Sjhb sc = ifp->if_softc; 2048151772Sjhb TXP_LOCK(sc); 2049151772Sjhb txp_start_locked(ifp); 2050151772Sjhb TXP_UNLOCK(sc); 2051151772Sjhb} 2052151772Sjhb 2053151772Sjhbstatic void 2054189685Syongaritxp_start_locked(struct ifnet *ifp) 2055151772Sjhb{ 2056189714Syongari struct txp_softc *sc; 2057189714Syongari struct mbuf *m_head; 2058189714Syongari int enq; 205980219Swpaul 2060189714Syongari sc = ifp->if_softc; 2061151772Sjhb TXP_LOCK_ASSERT(sc); 2062189714Syongari 2063148887Srwatson if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 2064189714Syongari IFF_DRV_RUNNING || (sc->sc_flags & TXP_FLAG_LINK) == 0) 206580219Swpaul return; 206680219Swpaul 2067189714Syongari for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { 2068189714Syongari IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 2069189714Syongari if (m_head == NULL) 207080219Swpaul break; 2071189714Syongari /* 2072189714Syongari * Pack the data into the transmit ring. If we 2073189714Syongari * don't have room, set the OACTIVE flag and wait 2074189714Syongari * for the NIC to drain the ring. 2075189714Syongari * ATM only Hi-ring is used. 2076189714Syongari */ 2077189714Syongari if (txp_encap(sc, &sc->sc_txhir, &m_head)) { 2078189714Syongari if (m_head == NULL) 2079189714Syongari break; 2080189714Syongari IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 2081189714Syongari ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2082189714Syongari break; 2083189714Syongari } 208480219Swpaul 2085189714Syongari /* 2086189714Syongari * If there's a BPF listener, bounce a copy of this frame 2087189714Syongari * to him. 2088189714Syongari */ 2089189714Syongari ETHER_BPF_MTAP(ifp, m_head); 209080219Swpaul 2091189714Syongari /* Send queued frame. */ 2092189714Syongari WRITE_REG(sc, sc->sc_txhir.r_reg, 2093189714Syongari TXP_IDX2OFFSET(sc->sc_txhir.r_prod)); 2094189714Syongari } 209580219Swpaul 2096189714Syongari if (enq > 0) { 2097189714Syongari /* Set a timeout in case the chip goes out to lunch. */ 2098189714Syongari sc->sc_watchdog_timer = TXP_TX_TIMEOUT; 2099189714Syongari } 2100189714Syongari} 210180219Swpaul 2102189714Syongaristatic int 2103189714Syongaritxp_encap(struct txp_softc *sc, struct txp_tx_ring *r, struct mbuf **m_head) 2104189714Syongari{ 2105189714Syongari struct txp_tx_desc *first_txd; 2106189714Syongari struct txp_frag_desc *fxd; 2107189714Syongari struct txp_swdesc *sd; 2108189714Syongari struct mbuf *m; 2109189714Syongari bus_dma_segment_t txsegs[TXP_MAXTXSEGS]; 2110189714Syongari int error, i, nsegs; 211180219Swpaul 2112189714Syongari TXP_LOCK_ASSERT(sc); 211380219Swpaul 2114189714Syongari M_ASSERTPKTHDR((*m_head)); 211580219Swpaul 2116189714Syongari m = *m_head; 2117189714Syongari first_txd = r->r_desc + r->r_prod; 2118189714Syongari sd = sc->sc_txd + r->r_prod; 211980219Swpaul 2120189714Syongari error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, sd->sd_map, 2121189714Syongari *m_head, txsegs, &nsegs, 0); 2122189714Syongari if (error == EFBIG) { 2123243857Sglebius m = m_collapse(*m_head, M_NOWAIT, TXP_MAXTXSEGS); 2124189714Syongari if (m == NULL) { 2125189714Syongari m_freem(*m_head); 2126189714Syongari *m_head = NULL; 2127189714Syongari return (ENOMEM); 212880219Swpaul } 2129189714Syongari *m_head = m; 2130189714Syongari error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.txp_tx_tag, 2131189714Syongari sd->sd_map, *m_head, txsegs, &nsegs, 0); 2132189714Syongari if (error != 0) { 2133189714Syongari m_freem(*m_head); 2134189714Syongari *m_head = NULL; 2135189714Syongari return (error); 2136189714Syongari } 2137189714Syongari } else if (error != 0) 2138189714Syongari return (error); 2139189714Syongari if (nsegs == 0) { 2140189714Syongari m_freem(*m_head); 2141189714Syongari *m_head = NULL; 2142189714Syongari return (EIO); 2143189714Syongari } 214483115Sbrooks 2145189714Syongari /* Check descriptor overrun. */ 2146189714Syongari if (r->r_cnt + nsegs >= TX_ENTRIES - TXP_TXD_RESERVED) { 2147189714Syongari bus_dmamap_unload(sc->sc_cdata.txp_tx_tag, sd->sd_map); 2148189714Syongari return (ENOBUFS); 2149189714Syongari } 2150189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_tx_tag, sd->sd_map, 2151189714Syongari BUS_DMASYNC_PREWRITE); 2152189714Syongari sd->sd_mbuf = m; 215380219Swpaul 2154189714Syongari first_txd->tx_flags = TX_FLAGS_TYPE_DATA; 2155189714Syongari first_txd->tx_numdesc = 0; 2156189714Syongari first_txd->tx_addrlo = 0; 2157189714Syongari first_txd->tx_addrhi = 0; 2158189714Syongari first_txd->tx_totlen = 0; 2159189714Syongari first_txd->tx_pflags = 0; 2160189714Syongari r->r_cnt++; 2161189714Syongari TXP_DESC_INC(r->r_prod, TX_ENTRIES); 2162189714Syongari 2163189714Syongari /* Configure Tx IP/TCP/UDP checksum offload. */ 2164189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) 2165189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_IPCKSUM); 2166189714Syongari#ifdef notyet 2167189714Syongari /* XXX firmware bug. */ 2168189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0) 2169189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_TCPCKSUM); 2170189714Syongari if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0) 2171189714Syongari first_txd->tx_pflags |= htole32(TX_PFLAGS_UDPCKSUM); 217280219Swpaul#endif 217380219Swpaul 2174189714Syongari /* Configure VLAN hardware tag insertion. */ 2175189714Syongari if ((m->m_flags & M_VLANTAG) != 0) 2176189714Syongari first_txd->tx_pflags |= 2177189714Syongari htole32(TX_PFLAGS_VLAN | TX_PFLAGS_PRIO | 2178189714Syongari (bswap16(m->m_pkthdr.ether_vtag) << TX_PFLAGS_VLANTAG_S)); 217980219Swpaul 2180189714Syongari for (i = 0; i < nsegs; i++) { 2181189714Syongari fxd = (struct txp_frag_desc *)(r->r_desc + r->r_prod); 2182189714Syongari fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG | TX_FLAGS_VALID; 2183189714Syongari fxd->frag_rsvd1 = 0; 2184189714Syongari fxd->frag_len = htole16(txsegs[i].ds_len); 2185189714Syongari fxd->frag_addrhi = htole32(TXP_ADDR_HI(txsegs[i].ds_addr)); 2186189714Syongari fxd->frag_addrlo = htole32(TXP_ADDR_LO(txsegs[i].ds_addr)); 2187189714Syongari fxd->frag_rsvd2 = 0; 2188189714Syongari first_txd->tx_numdesc++; 2189189714Syongari r->r_cnt++; 2190189714Syongari TXP_DESC_INC(r->r_prod, TX_ENTRIES); 2191189714Syongari } 219280219Swpaul 2193189714Syongari /* Lastly set valid flag. */ 2194189714Syongari first_txd->tx_flags |= TX_FLAGS_VALID; 219580219Swpaul 2196189714Syongari /* Sync descriptors. */ 2197189714Syongari bus_dmamap_sync(r->r_tag, r->r_map, 2198189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 219980219Swpaul 2200189714Syongari return (0); 220180219Swpaul} 220280219Swpaul 220380219Swpaul/* 220480219Swpaul * Handle simple commands sent to the typhoon 220580219Swpaul */ 220680219Swpaulstatic int 2207189689Syongaritxp_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2, 2208189689Syongari uint32_t in3, uint16_t *out1, uint32_t *out2, uint32_t *out3, int wait) 220980219Swpaul{ 2210189714Syongari struct txp_rsp_desc *rsp; 221180219Swpaul 2212189714Syongari rsp = NULL; 2213189714Syongari if (txp_ext_command(sc, id, in1, in2, in3, NULL, 0, &rsp, wait) != 0) { 2214189714Syongari device_printf(sc->sc_dev, "command 0x%02x failed\n", id); 221580219Swpaul return (-1); 2216189714Syongari } 221780219Swpaul 2218189714Syongari if (wait == TXP_CMD_NOWAIT) 221980219Swpaul return (0); 222080219Swpaul 2221189714Syongari KASSERT(rsp != NULL, ("rsp is NULL!\n")); 222280219Swpaul if (out1 != NULL) 2223189714Syongari *out1 = le16toh(rsp->rsp_par1); 222480219Swpaul if (out2 != NULL) 2225189714Syongari *out2 = le32toh(rsp->rsp_par2); 222680219Swpaul if (out3 != NULL) 2227189714Syongari *out3 = le32toh(rsp->rsp_par3); 222880219Swpaul free(rsp, M_DEVBUF); 222980219Swpaul return (0); 223080219Swpaul} 223180219Swpaul 223280219Swpaulstatic int 2233189714Syongaritxp_ext_command(struct txp_softc *sc, uint16_t id, uint16_t in1, uint32_t in2, 2234189689Syongari uint32_t in3, struct txp_ext_desc *in_extp, uint8_t in_extn, 2235189004Srdivacky struct txp_rsp_desc **rspp, int wait) 223680219Swpaul{ 2237189714Syongari struct txp_hostvar *hv; 223880219Swpaul struct txp_cmd_desc *cmd; 223980219Swpaul struct txp_ext_desc *ext; 2240189689Syongari uint32_t idx, i; 2241189689Syongari uint16_t seq; 2242189714Syongari int error; 224380219Swpaul 2244189714Syongari error = 0; 2245189714Syongari hv = sc->sc_hostvar; 224680219Swpaul if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) { 2247189714Syongari device_printf(sc->sc_dev, 2248189714Syongari "%s : out of free cmd descriptors for command 0x%02x\n", 2249189714Syongari __func__, id); 2250189714Syongari return (ENOBUFS); 225180219Swpaul } 225280219Swpaul 2253189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag, 2254189714Syongari sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_POSTWRITE); 225580219Swpaul idx = sc->sc_cmdring.lastwrite; 2256189689Syongari cmd = (struct txp_cmd_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx); 225780219Swpaul bzero(cmd, sizeof(*cmd)); 225880219Swpaul 225980219Swpaul cmd->cmd_numdesc = in_extn; 2260189714Syongari seq = sc->sc_seq++; 2261189714Syongari cmd->cmd_seq = htole16(seq); 2262189714Syongari cmd->cmd_id = htole16(id); 2263189714Syongari cmd->cmd_par1 = htole16(in1); 2264189714Syongari cmd->cmd_par2 = htole32(in2); 2265189714Syongari cmd->cmd_par3 = htole32(in3); 226680219Swpaul cmd->cmd_flags = CMD_FLAGS_TYPE_CMD | 2267189714Syongari (wait == TXP_CMD_WAIT ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID; 226880219Swpaul 226980219Swpaul idx += sizeof(struct txp_cmd_desc); 227080219Swpaul if (idx == sc->sc_cmdring.size) 227180219Swpaul idx = 0; 227280219Swpaul 227380219Swpaul for (i = 0; i < in_extn; i++) { 2274189689Syongari ext = (struct txp_ext_desc *)(((uint8_t *)sc->sc_cmdring.base) + idx); 227580219Swpaul bcopy(in_extp, ext, sizeof(struct txp_ext_desc)); 227680219Swpaul in_extp++; 227780219Swpaul idx += sizeof(struct txp_cmd_desc); 227880219Swpaul if (idx == sc->sc_cmdring.size) 227980219Swpaul idx = 0; 228080219Swpaul } 228180219Swpaul 228280219Swpaul sc->sc_cmdring.lastwrite = idx; 2283189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_cmdring_tag, 2284189714Syongari sc->sc_cdata.txp_cmdring_map, BUS_DMASYNC_PREWRITE); 2285189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2286189714Syongari sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_PREREAD | 2287189714Syongari BUS_DMASYNC_PREWRITE); 228880219Swpaul WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite); 2289189714Syongari TXP_BARRIER(sc, TXP_H2A_2, 4, BUS_SPACE_BARRIER_WRITE); 229080219Swpaul 2291189714Syongari if (wait == TXP_CMD_NOWAIT) 229280219Swpaul return (0); 229380219Swpaul 2294189714Syongari for (i = 0; i < TXP_TIMEOUT; i++) { 2295189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2296189714Syongari sc->sc_cdata.txp_hostvar_map, BUS_DMASYNC_POSTREAD | 2297189714Syongari BUS_DMASYNC_POSTWRITE); 2298189714Syongari if (le32toh(hv->hv_resp_read_idx) != 2299189714Syongari le32toh(hv->hv_resp_write_idx)) { 2300189714Syongari error = txp_response(sc, id, seq, rspp); 2301189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2302189714Syongari sc->sc_cdata.txp_hostvar_map, 2303189714Syongari BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 2304189714Syongari if (error != 0) 2305189714Syongari return (error); 2306189714Syongari if (*rspp != NULL) 230780219Swpaul break; 230880219Swpaul } 230980219Swpaul DELAY(50); 231080219Swpaul } 2311189714Syongari if (i == TXP_TIMEOUT) { 2312189714Syongari device_printf(sc->sc_dev, "command 0x%02x timedout\n", id); 2313189714Syongari error = ETIMEDOUT; 231480219Swpaul } 231580219Swpaul 2316189714Syongari return (error); 231780219Swpaul} 231880219Swpaul 231980219Swpaulstatic int 2320189714Syongaritxp_response(struct txp_softc *sc, uint16_t id, uint16_t seq, 2321189004Srdivacky struct txp_rsp_desc **rspp) 232280219Swpaul{ 2323189714Syongari struct txp_hostvar *hv; 232480219Swpaul struct txp_rsp_desc *rsp; 2325189714Syongari uint32_t ridx; 232680219Swpaul 2327189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_rspring_tag, 2328189714Syongari sc->sc_cdata.txp_rspring_map, BUS_DMASYNC_POSTREAD); 2329189714Syongari hv = sc->sc_hostvar; 2330189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 2331189714Syongari while (ridx != le32toh(hv->hv_resp_write_idx)) { 2332189689Syongari rsp = (struct txp_rsp_desc *)(((uint8_t *)sc->sc_rspring.base) + ridx); 233380219Swpaul 2334189714Syongari if (id == le16toh(rsp->rsp_id) && 2335189714Syongari le16toh(rsp->rsp_seq) == seq) { 233680219Swpaul *rspp = (struct txp_rsp_desc *)malloc( 233780219Swpaul sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1), 233880219Swpaul M_DEVBUF, M_NOWAIT); 2339189714Syongari if (*rspp == NULL) { 2340189714Syongari device_printf(sc->sc_dev,"%s : command 0x%02x " 2341189714Syongari "memory allocation failure\n", 2342189714Syongari __func__, id); 2343189714Syongari return (ENOMEM); 2344189714Syongari } 234580219Swpaul txp_rsp_fixup(sc, rsp, *rspp); 234680219Swpaul return (0); 234780219Swpaul } 234880219Swpaul 2349189714Syongari if ((rsp->rsp_flags & RSP_FLAGS_ERROR) != 0) { 2350189714Syongari device_printf(sc->sc_dev, 2351189714Syongari "%s : command 0x%02x response error!\n", __func__, 2352189714Syongari le16toh(rsp->rsp_id)); 235380219Swpaul txp_rsp_fixup(sc, rsp, NULL); 2354189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 235580219Swpaul continue; 235680219Swpaul } 235780219Swpaul 2358189714Syongari /* 2359189714Syongari * The following unsolicited responses are handled during 2360189714Syongari * processing of TXP_CMD_READ_STATISTICS which requires 2361189714Syongari * response. Driver abuses the command to detect media 2362189714Syongari * status change. 2363189714Syongari * TXP_CMD_FILTER_DEFINE is not an unsolicited response 2364189714Syongari * but we don't process response ring in interrupt handler 2365189714Syongari * so we have to ignore this command here, otherwise 2366189714Syongari * unknown command message would be printed. 2367189714Syongari */ 2368189714Syongari switch (le16toh(rsp->rsp_id)) { 236980219Swpaul case TXP_CMD_CYCLE_STATISTICS: 2370189714Syongari case TXP_CMD_FILTER_DEFINE: 2371189714Syongari break; 237280219Swpaul case TXP_CMD_MEDIA_STATUS_READ: 2373189714Syongari if ((le16toh(rsp->rsp_par1) & 0x0800) == 0) { 2374189714Syongari sc->sc_flags |= TXP_FLAG_LINK; 2375189714Syongari if_link_state_change(sc->sc_ifp, 2376189714Syongari LINK_STATE_UP); 2377189714Syongari } else { 2378189714Syongari sc->sc_flags &= ~TXP_FLAG_LINK; 2379189714Syongari if_link_state_change(sc->sc_ifp, 2380189714Syongari LINK_STATE_DOWN); 2381189714Syongari } 238280219Swpaul break; 238380219Swpaul case TXP_CMD_HELLO_RESPONSE: 2384189714Syongari /* 2385189714Syongari * Driver should repsond to hello message but 2386189714Syongari * TXP_CMD_READ_STATISTICS is issued for every 2387189714Syongari * hz, therefore there is no need to send an 2388189714Syongari * explicit command here. 2389189714Syongari */ 2390189714Syongari device_printf(sc->sc_dev, "%s : hello\n", __func__); 239180219Swpaul break; 239280219Swpaul default: 2393189714Syongari device_printf(sc->sc_dev, 2394189714Syongari "%s : unknown command 0x%02x\n", __func__, 2395189714Syongari le16toh(rsp->rsp_id)); 239680219Swpaul } 239780219Swpaul txp_rsp_fixup(sc, rsp, NULL); 2398189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 239980219Swpaul } 240080219Swpaul 240180219Swpaul return (0); 240280219Swpaul} 240380219Swpaul 240480219Swpaulstatic void 2405189685Syongaritxp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp, 2406189685Syongari struct txp_rsp_desc *dst) 240780219Swpaul{ 2408189714Syongari struct txp_rsp_desc *src; 2409189714Syongari struct txp_hostvar *hv; 2410189689Syongari uint32_t i, ridx; 241180219Swpaul 2412189714Syongari src = rsp; 2413189714Syongari hv = sc->sc_hostvar; 2414189714Syongari ridx = le32toh(hv->hv_resp_read_idx); 241580219Swpaul 241680219Swpaul for (i = 0; i < rsp->rsp_numdesc + 1; i++) { 241780219Swpaul if (dst != NULL) 241880219Swpaul bcopy(src, dst++, sizeof(struct txp_rsp_desc)); 241980219Swpaul ridx += sizeof(struct txp_rsp_desc); 242080219Swpaul if (ridx == sc->sc_rspring.size) { 242180219Swpaul src = sc->sc_rspring.base; 242280219Swpaul ridx = 0; 242380219Swpaul } else 242480219Swpaul src++; 2425189714Syongari sc->sc_rspring.lastwrite = ridx; 242680219Swpaul } 2427189686Syongari 2428189714Syongari hv->hv_resp_read_idx = htole32(ridx); 242980219Swpaul} 243080219Swpaul 243180219Swpaulstatic int 2432189685Syongaritxp_cmd_desc_numfree(struct txp_softc *sc) 243380219Swpaul{ 2434189714Syongari struct txp_hostvar *hv; 2435189714Syongari struct txp_boot_record *br; 2436189689Syongari uint32_t widx, ridx, nfree; 243780219Swpaul 2438189714Syongari bus_dmamap_sync(sc->sc_cdata.txp_hostvar_tag, 2439189714Syongari sc->sc_cdata.txp_hostvar_map, 2440189714Syongari BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2441189714Syongari hv = sc->sc_hostvar; 2442189714Syongari br = sc->sc_boot; 244380219Swpaul widx = sc->sc_cmdring.lastwrite; 2444189714Syongari ridx = le32toh(hv->hv_cmd_read_idx); 244580219Swpaul 244680219Swpaul if (widx == ridx) { 244780219Swpaul /* Ring is completely free */ 2448189714Syongari nfree = le32toh(br->br_cmd_siz) - sizeof(struct txp_cmd_desc); 244980219Swpaul } else { 245080219Swpaul if (widx > ridx) 2451189714Syongari nfree = le32toh(br->br_cmd_siz) - 245280219Swpaul (widx - ridx + sizeof(struct txp_cmd_desc)); 245380219Swpaul else 245480219Swpaul nfree = ridx - widx - sizeof(struct txp_cmd_desc); 245580219Swpaul } 245680219Swpaul 245780219Swpaul return (nfree / sizeof(struct txp_cmd_desc)); 245880219Swpaul} 245980219Swpaul 2460189714Syongaristatic int 2461189714Syongaritxp_sleep(struct txp_softc *sc, int capenable) 2462189714Syongari{ 2463189714Syongari uint16_t events; 2464189714Syongari int error; 2465189714Syongari 2466189714Syongari events = 0; 2467189714Syongari if ((capenable & IFCAP_WOL_MAGIC) != 0) 2468189714Syongari events |= 0x01; 2469189714Syongari error = txp_command(sc, TXP_CMD_ENABLE_WAKEUP_EVENTS, events, 0, 0, 2470189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 2471189714Syongari if (error == 0) { 2472189714Syongari /* Goto sleep. */ 2473189714Syongari error = txp_command(sc, TXP_CMD_GOTO_SLEEP, 0, 0, 0, NULL, 2474189714Syongari NULL, NULL, TXP_CMD_NOWAIT); 2475189714Syongari if (error == 0) { 2476189714Syongari error = txp_wait(sc, STAT_SLEEPING); 2477189714Syongari if (error != 0) 2478189714Syongari device_printf(sc->sc_dev, 2479189714Syongari "unable to enter into sleep\n"); 2480189714Syongari } 2481189714Syongari } 2482189714Syongari 2483189714Syongari return (error); 2484189714Syongari} 2485189714Syongari 248680219Swpaulstatic void 2487189685Syongaritxp_stop(struct txp_softc *sc) 248880219Swpaul{ 248980219Swpaul struct ifnet *ifp; 249080219Swpaul 2491151772Sjhb TXP_LOCK_ASSERT(sc); 2492147256Sbrooks ifp = sc->sc_ifp; 249380219Swpaul 2494189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2495189714Syongari return; 2496189714Syongari 2497189714Syongari WRITE_REG(sc, TXP_IER, TXP_INTR_NONE); 2498189714Syongari WRITE_REG(sc, TXP_ISR, TXP_INTR_ALL); 2499189714Syongari 2500148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2501189714Syongari sc->sc_flags &= ~TXP_FLAG_LINK; 250280219Swpaul 2503151772Sjhb callout_stop(&sc->sc_tick); 250480219Swpaul 2505189714Syongari txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 2506189714Syongari TXP_CMD_NOWAIT); 2507189714Syongari txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 2508189714Syongari TXP_CMD_NOWAIT); 2509189714Syongari /* Save statistics for later use. */ 2510189714Syongari txp_stats_save(sc); 2511189714Syongari /* Halt controller. */ 2512189714Syongari txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 2513189714Syongari TXP_CMD_NOWAIT); 251480219Swpaul 2515189714Syongari if (txp_wait(sc, STAT_HALTED) != 0) 2516189714Syongari device_printf(sc->sc_dev, "controller halt timedout!\n"); 2517189714Syongari /* Reclaim Tx/Rx buffers. */ 2518189714Syongari if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons != 2519189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off))))) 2520189714Syongari txp_tx_reclaim(sc, &sc->sc_txhir); 2521189714Syongari if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons != 2522189714Syongari TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off))))) 2523189714Syongari txp_tx_reclaim(sc, &sc->sc_txlor); 252480219Swpaul txp_rxring_empty(sc); 2525189714Syongari 2526189714Syongari txp_init_rings(sc); 2527189714Syongari /* Reset controller and make it reload sleep image. */ 2528189714Syongari txp_reset(sc); 2529189714Syongari /* Let controller boot from sleep image. */ 2530189714Syongari if (txp_boot(sc, STAT_WAITING_FOR_HOST_REQUEST) != 0) 2531189714Syongari device_printf(sc->sc_dev, "could not boot sleep image\n"); 2532189714Syongari txp_sleep(sc, 0); 253380219Swpaul} 253480219Swpaul 253580219Swpaulstatic void 2536189714Syongaritxp_watchdog(struct txp_softc *sc) 253780219Swpaul{ 2538189714Syongari struct ifnet *ifp; 2539189687Syongari 2540189714Syongari TXP_LOCK_ASSERT(sc); 2541189714Syongari 2542189714Syongari if (sc->sc_watchdog_timer == 0 || --sc->sc_watchdog_timer) 2543189714Syongari return; 2544189714Syongari 2545189714Syongari ifp = sc->sc_ifp; 2546189714Syongari if_printf(ifp, "watchdog timeout -- resetting\n"); 2547272067Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2548189714Syongari txp_stop(sc); 2549189714Syongari txp_init_locked(sc); 255080219Swpaul} 255180219Swpaul 255280219Swpaulstatic int 2553189685Syongaritxp_ifmedia_upd(struct ifnet *ifp) 255480219Swpaul{ 255580219Swpaul struct txp_softc *sc = ifp->if_softc; 255680219Swpaul struct ifmedia *ifm = &sc->sc_ifmedia; 2557189689Syongari uint16_t new_xcvr; 255880219Swpaul 2559151772Sjhb TXP_LOCK(sc); 2560151772Sjhb if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) { 2561151772Sjhb TXP_UNLOCK(sc); 256280219Swpaul return (EINVAL); 2563151772Sjhb } 256480219Swpaul 256580219Swpaul if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) { 256680219Swpaul if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) 256780219Swpaul new_xcvr = TXP_XCVR_10_FDX; 256880219Swpaul else 256980219Swpaul new_xcvr = TXP_XCVR_10_HDX; 257080219Swpaul } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) { 257180219Swpaul if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) 257280219Swpaul new_xcvr = TXP_XCVR_100_FDX; 257380219Swpaul else 257480219Swpaul new_xcvr = TXP_XCVR_100_HDX; 257580219Swpaul } else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { 257680219Swpaul new_xcvr = TXP_XCVR_AUTO; 2577151772Sjhb } else { 2578151772Sjhb TXP_UNLOCK(sc); 257980219Swpaul return (EINVAL); 2580151772Sjhb } 258180219Swpaul 258280219Swpaul /* nothing to do */ 2583151772Sjhb if (sc->sc_xcvr == new_xcvr) { 2584151772Sjhb TXP_UNLOCK(sc); 258580219Swpaul return (0); 2586151772Sjhb } 258780219Swpaul 258880219Swpaul txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0, 2589189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 259080219Swpaul sc->sc_xcvr = new_xcvr; 2591151772Sjhb TXP_UNLOCK(sc); 259280219Swpaul 259380219Swpaul return (0); 259480219Swpaul} 259580219Swpaul 259680219Swpaulstatic void 2597189685Syongaritxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 259880219Swpaul{ 259980219Swpaul struct txp_softc *sc = ifp->if_softc; 260080219Swpaul struct ifmedia *ifm = &sc->sc_ifmedia; 2601189689Syongari uint16_t bmsr, bmcr, anar, anlpar; 260280219Swpaul 260380219Swpaul ifmr->ifm_status = IFM_AVALID; 260480219Swpaul ifmr->ifm_active = IFM_ETHER; 260580219Swpaul 2606151772Sjhb TXP_LOCK(sc); 2607189714Syongari /* Check whether firmware is running. */ 2608189714Syongari if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2609189714Syongari goto bail; 261080219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, 2611189714Syongari &bmsr, NULL, NULL, TXP_CMD_WAIT)) 261280219Swpaul goto bail; 261380219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0, 2614189714Syongari &bmsr, NULL, NULL, TXP_CMD_WAIT)) 261580219Swpaul goto bail; 261680219Swpaul 261780219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0, 2618189714Syongari &bmcr, NULL, NULL, TXP_CMD_WAIT)) 261980219Swpaul goto bail; 262080219Swpaul 262180219Swpaul if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0, 2622189714Syongari &anlpar, NULL, NULL, TXP_CMD_WAIT)) 262380219Swpaul goto bail; 2624173666Syongari 2625173666Syongari if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANAR, 0, 2626189714Syongari &anar, NULL, NULL, TXP_CMD_WAIT)) 2627173666Syongari goto bail; 2628151772Sjhb TXP_UNLOCK(sc); 262980219Swpaul 263080219Swpaul if (bmsr & BMSR_LINK) 263180219Swpaul ifmr->ifm_status |= IFM_ACTIVE; 263280219Swpaul 263380219Swpaul if (bmcr & BMCR_ISO) { 263480219Swpaul ifmr->ifm_active |= IFM_NONE; 263580219Swpaul ifmr->ifm_status = 0; 263680219Swpaul return; 263780219Swpaul } 263880219Swpaul 263980219Swpaul if (bmcr & BMCR_LOOP) 264080219Swpaul ifmr->ifm_active |= IFM_LOOP; 264180219Swpaul 264280219Swpaul if (bmcr & BMCR_AUTOEN) { 264380219Swpaul if ((bmsr & BMSR_ACOMP) == 0) { 264480219Swpaul ifmr->ifm_active |= IFM_NONE; 264580219Swpaul return; 264680219Swpaul } 264780219Swpaul 2648173666Syongari anlpar &= anar; 2649173665Syongari if (anlpar & ANLPAR_TX_FD) 2650173665Syongari ifmr->ifm_active |= IFM_100_TX|IFM_FDX; 2651173665Syongari else if (anlpar & ANLPAR_T4) 265280219Swpaul ifmr->ifm_active |= IFM_100_T4; 265380219Swpaul else if (anlpar & ANLPAR_TX) 265480219Swpaul ifmr->ifm_active |= IFM_100_TX; 265580219Swpaul else if (anlpar & ANLPAR_10_FD) 265680219Swpaul ifmr->ifm_active |= IFM_10_T|IFM_FDX; 265780219Swpaul else if (anlpar & ANLPAR_10) 265880219Swpaul ifmr->ifm_active |= IFM_10_T; 265980219Swpaul else 266080219Swpaul ifmr->ifm_active |= IFM_NONE; 266180219Swpaul } else 266280219Swpaul ifmr->ifm_active = ifm->ifm_cur->ifm_media; 266380219Swpaul return; 266480219Swpaul 266580219Swpaulbail: 2666151772Sjhb TXP_UNLOCK(sc); 266780219Swpaul ifmr->ifm_active |= IFM_NONE; 266880219Swpaul ifmr->ifm_status &= ~IFM_AVALID; 266980219Swpaul} 267080219Swpaul 267180219Swpaul#ifdef TXP_DEBUG 267280219Swpaulstatic void 2673189685Syongaritxp_show_descriptor(void *d) 267480219Swpaul{ 267580219Swpaul struct txp_cmd_desc *cmd = d; 267680219Swpaul struct txp_rsp_desc *rsp = d; 267780219Swpaul struct txp_tx_desc *txd = d; 267880219Swpaul struct txp_frag_desc *frgd = d; 267980219Swpaul 268080219Swpaul switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) { 268180219Swpaul case CMD_FLAGS_TYPE_CMD: 268280219Swpaul /* command descriptor */ 268380219Swpaul printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 2684189714Syongari cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id), 2685189714Syongari le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1), 2686189714Syongari le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3)); 268780219Swpaul break; 268880219Swpaul case CMD_FLAGS_TYPE_RESP: 268980219Swpaul /* response descriptor */ 269080219Swpaul printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 2691189714Syongari rsp->rsp_flags, rsp->rsp_numdesc, le16toh(rsp->rsp_id), 2692189714Syongari le16toh(rsp->rsp_seq), le16toh(rsp->rsp_par1), 2693189714Syongari le32toh(rsp->rsp_par2), le32toh(rsp->rsp_par3)); 269480219Swpaul break; 269580219Swpaul case CMD_FLAGS_TYPE_DATA: 269680219Swpaul /* data header (assuming tx for now) */ 269780219Swpaul printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]", 2698189714Syongari txd->tx_flags, txd->tx_numdesc, le16toh(txd->tx_totlen), 2699189714Syongari le32toh(txd->tx_addrlo), le32toh(txd->tx_addrhi), 2700189714Syongari le32toh(txd->tx_pflags)); 270180219Swpaul break; 270280219Swpaul case CMD_FLAGS_TYPE_FRAG: 270380219Swpaul /* fragment descriptor */ 270480219Swpaul printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]", 2705189714Syongari frgd->frag_flags, frgd->frag_rsvd1, le16toh(frgd->frag_len), 2706189714Syongari le32toh(frgd->frag_addrlo), le32toh(frgd->frag_addrhi), 2707189714Syongari le32toh(frgd->frag_rsvd2)); 270880219Swpaul break; 270980219Swpaul default: 271080219Swpaul printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n", 271180219Swpaul cmd->cmd_flags & CMD_FLAGS_TYPE_M, 2712189714Syongari cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id), 2713189714Syongari le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1), 2714189714Syongari le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3)); 271580219Swpaul break; 271680219Swpaul } 271780219Swpaul} 271880219Swpaul#endif 271980219Swpaul 272080219Swpaulstatic void 2721189685Syongaritxp_set_filter(struct txp_softc *sc) 272280219Swpaul{ 2723189690Syongari struct ifnet *ifp; 2724189690Syongari uint32_t crc, mchash[2]; 2725189689Syongari uint16_t filter; 272680219Swpaul struct ifmultiaddr *ifma; 2727189690Syongari int mcnt; 272880219Swpaul 2729189690Syongari TXP_LOCK_ASSERT(sc); 273080219Swpaul 2731189690Syongari ifp = sc->sc_ifp; 273280219Swpaul filter = TXP_RXFILT_DIRECT; 2733189690Syongari if ((ifp->if_flags & IFF_BROADCAST) != 0) 273480219Swpaul filter |= TXP_RXFILT_BROADCAST; 2735189690Syongari if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 2736189690Syongari if ((ifp->if_flags & IFF_ALLMULTI) != 0) 2737189690Syongari filter |= TXP_RXFILT_ALLMULTI; 2738189690Syongari if ((ifp->if_flags & IFF_PROMISC) != 0) 2739189690Syongari filter = TXP_RXFILT_PROMISC; 2740189690Syongari goto setit; 2741189690Syongari } 274280219Swpaul 2743189690Syongari mchash[0] = mchash[1] = 0; 2744189690Syongari mcnt = 0; 2745195049Srwatson if_maddr_rlock(ifp); 2746189690Syongari TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2747189690Syongari if (ifma->ifma_addr->sa_family != AF_LINK) 2748189690Syongari continue; 2749189690Syongari crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) 2750189690Syongari ifma->ifma_addr), ETHER_ADDR_LEN); 2751189690Syongari crc &= 0x3f; 2752189690Syongari mchash[crc >> 5] |= 1 << (crc & 0x1f); 2753189690Syongari mcnt++; 2754189690Syongari } 2755195049Srwatson if_maddr_runlock(ifp); 275680219Swpaul 2757189690Syongari if (mcnt > 0) { 2758189690Syongari filter |= TXP_RXFILT_HASHMULTI; 2759189690Syongari txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE, 2, mchash[0], 2760189714Syongari mchash[1], NULL, NULL, NULL, TXP_CMD_NOWAIT); 276180219Swpaul } 276280219Swpaul 276380219Swpaulsetit: 276480219Swpaul txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0, 2765189714Syongari NULL, NULL, NULL, TXP_CMD_NOWAIT); 276680219Swpaul} 276780219Swpaul 2768189714Syongaristatic int 2769189714Syongaritxp_set_capabilities(struct txp_softc *sc) 2770189714Syongari{ 2771189714Syongari struct ifnet *ifp; 2772189714Syongari uint32_t rxcap, txcap; 2773189714Syongari 2774189714Syongari TXP_LOCK_ASSERT(sc); 2775189714Syongari 2776189714Syongari rxcap = txcap = 0; 2777189714Syongari ifp = sc->sc_ifp; 2778189714Syongari if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) { 2779189714Syongari if ((ifp->if_hwassist & CSUM_IP) != 0) 2780189714Syongari txcap |= OFFLOAD_IPCKSUM; 2781189714Syongari if ((ifp->if_hwassist & CSUM_TCP) != 0) 2782189714Syongari txcap |= OFFLOAD_TCPCKSUM; 2783189714Syongari if ((ifp->if_hwassist & CSUM_UDP) != 0) 2784189714Syongari txcap |= OFFLOAD_UDPCKSUM; 2785189714Syongari rxcap = txcap; 2786189714Syongari } 2787189714Syongari if ((ifp->if_capenable & IFCAP_RXCSUM) == 0) 2788189714Syongari rxcap &= ~(OFFLOAD_IPCKSUM | OFFLOAD_TCPCKSUM | 2789189714Syongari OFFLOAD_UDPCKSUM); 2790189714Syongari if ((ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { 2791189714Syongari rxcap |= OFFLOAD_VLAN; 2792189714Syongari txcap |= OFFLOAD_VLAN; 2793189714Syongari } 2794189714Syongari 2795189714Syongari /* Tell firmware new offload configuration. */ 2796189714Syongari return (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0, txcap, rxcap, NULL, 2797189714Syongari NULL, NULL, TXP_CMD_NOWAIT)); 2798189714Syongari} 2799189714Syongari 280080219Swpaulstatic void 2801189714Syongaritxp_stats_save(struct txp_softc *sc) 280280219Swpaul{ 2803189714Syongari struct txp_rsp_desc *rsp; 280480219Swpaul 2805189714Syongari TXP_LOCK_ASSERT(sc); 2806189714Syongari 2807189714Syongari rsp = NULL; 2808189714Syongari if (txp_ext_command(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0, 2809189714Syongari &rsp, TXP_CMD_WAIT)) 281080219Swpaul goto out; 2811189714Syongari if (rsp->rsp_numdesc != 6) 2812189714Syongari goto out; 2813189714Syongari txp_stats_update(sc, rsp); 2814189714Syongariout: 2815189714Syongari if (rsp != NULL) 2816189714Syongari free(rsp, M_DEVBUF); 2817189714Syongari bcopy(&sc->sc_stats, &sc->sc_ostats, sizeof(struct txp_hw_stats)); 2818189714Syongari} 281980219Swpaul 2820189714Syongaristatic void 2821189714Syongaritxp_stats_update(struct txp_softc *sc, struct txp_rsp_desc *rsp) 2822189714Syongari{ 2823189714Syongari struct txp_hw_stats *ostats, *stats; 2824189714Syongari struct txp_ext_desc *ext; 2825189714Syongari 2826189714Syongari TXP_LOCK_ASSERT(sc); 2827189714Syongari 282880219Swpaul ext = (struct txp_ext_desc *)(rsp + 1); 2829189714Syongari ostats = &sc->sc_ostats; 2830189714Syongari stats = &sc->sc_stats; 2831189714Syongari stats->tx_frames = ostats->tx_frames + le32toh(rsp->rsp_par2); 2832189714Syongari stats->tx_bytes = ostats->tx_bytes + (uint64_t)le32toh(rsp->rsp_par3) + 2833189714Syongari ((uint64_t)le32toh(ext[0].ext_1) << 32); 2834189714Syongari stats->tx_deferred = ostats->tx_deferred + le32toh(ext[0].ext_2); 2835189714Syongari stats->tx_late_colls = ostats->tx_late_colls + le32toh(ext[0].ext_3); 2836189714Syongari stats->tx_colls = ostats->tx_colls + le32toh(ext[0].ext_4); 2837189714Syongari stats->tx_carrier_lost = ostats->tx_carrier_lost + 2838189714Syongari le32toh(ext[1].ext_1); 2839189714Syongari stats->tx_multi_colls = ostats->tx_multi_colls + 2840189714Syongari le32toh(ext[1].ext_2); 2841189714Syongari stats->tx_excess_colls = ostats->tx_excess_colls + 2842189714Syongari le32toh(ext[1].ext_3); 2843189714Syongari stats->tx_fifo_underruns = ostats->tx_fifo_underruns + 2844189714Syongari le32toh(ext[1].ext_4); 2845189714Syongari stats->tx_mcast_oflows = ostats->tx_mcast_oflows + 2846189714Syongari le32toh(ext[2].ext_1); 2847189714Syongari stats->tx_filtered = ostats->tx_filtered + le32toh(ext[2].ext_2); 2848189714Syongari stats->rx_frames = ostats->rx_frames + le32toh(ext[2].ext_3); 2849189714Syongari stats->rx_bytes = ostats->rx_bytes + (uint64_t)le32toh(ext[2].ext_4) + 2850189714Syongari ((uint64_t)le32toh(ext[3].ext_1) << 32); 2851189714Syongari stats->rx_fifo_oflows = ostats->rx_fifo_oflows + le32toh(ext[3].ext_2); 2852189714Syongari stats->rx_badssd = ostats->rx_badssd + le32toh(ext[3].ext_3); 2853189714Syongari stats->rx_crcerrs = ostats->rx_crcerrs + le32toh(ext[3].ext_4); 2854189714Syongari stats->rx_lenerrs = ostats->rx_lenerrs + le32toh(ext[4].ext_1); 2855189714Syongari stats->rx_bcast_frames = ostats->rx_bcast_frames + 2856189714Syongari le32toh(ext[4].ext_2); 2857189714Syongari stats->rx_mcast_frames = ostats->rx_mcast_frames + 2858189714Syongari le32toh(ext[4].ext_3); 2859189714Syongari stats->rx_oflows = ostats->rx_oflows + le32toh(ext[4].ext_4); 2860189714Syongari stats->rx_filtered = ostats->rx_filtered + le32toh(ext[5].ext_1); 2861272067Sglebius} 286280219Swpaul 2863272067Sglebiusstatic uint64_t 2864272067Sglebiustxp_get_counter(struct ifnet *ifp, ift_counter cnt) 2865272067Sglebius{ 2866272067Sglebius struct txp_softc *sc; 2867272067Sglebius struct txp_hw_stats *stats; 2868272067Sglebius 2869272067Sglebius sc = if_getsoftc(ifp); 2870272067Sglebius stats = &sc->sc_stats; 2871272067Sglebius 2872272067Sglebius switch (cnt) { 2873272067Sglebius case IFCOUNTER_IERRORS: 2874272067Sglebius return (stats->rx_fifo_oflows + stats->rx_badssd + 2875272067Sglebius stats->rx_crcerrs + stats->rx_lenerrs + stats->rx_oflows); 2876272067Sglebius case IFCOUNTER_OERRORS: 2877272067Sglebius return (stats->tx_deferred + stats->tx_carrier_lost + 2878272067Sglebius stats->tx_fifo_underruns + stats->tx_mcast_oflows); 2879272067Sglebius case IFCOUNTER_COLLISIONS: 2880272067Sglebius return (stats->tx_late_colls + stats->tx_multi_colls + 2881272067Sglebius stats->tx_excess_colls); 2882272067Sglebius case IFCOUNTER_OPACKETS: 2883272067Sglebius return (stats->tx_frames); 2884272067Sglebius case IFCOUNTER_IPACKETS: 2885272067Sglebius return (stats->rx_frames); 2886272067Sglebius default: 2887272067Sglebius return (if_get_counter_default(ifp, cnt)); 2888272067Sglebius } 2889189714Syongari} 289080219Swpaul 2891189714Syongari#define TXP_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 2892189714Syongari SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 289380219Swpaul 2894217323Smdf#if __FreeBSD_version >= 900030 2895189714Syongari#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2896217323Smdf SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 2897217323Smdf#elif __FreeBSD_version > 800000 2898217323Smdf#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2899189714Syongari SYSCTL_ADD_QUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 2900189714Syongari#else 2901189714Syongari#define TXP_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 2902189714Syongari SYSCTL_ADD_ULONG(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 290380219Swpaul#endif 290480219Swpaul 2905189714Syongaristatic void 2906189714Syongaritxp_sysctl_node(struct txp_softc *sc) 2907189714Syongari{ 2908189714Syongari struct sysctl_ctx_list *ctx; 2909189714Syongari struct sysctl_oid_list *child, *parent; 2910189714Syongari struct sysctl_oid *tree; 2911189714Syongari struct txp_hw_stats *stats; 2912189714Syongari int error; 291380219Swpaul 2914189714Syongari stats = &sc->sc_stats; 2915189714Syongari ctx = device_get_sysctl_ctx(sc->sc_dev); 2916189714Syongari child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); 2917189714Syongari SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "process_limit", 2918189714Syongari CTLTYPE_INT | CTLFLAG_RW, &sc->sc_process_limit, 0, 2919189714Syongari sysctl_hw_txp_proc_limit, "I", 2920189714Syongari "max number of Rx events to process"); 2921189714Syongari /* Pull in device tunables. */ 2922189714Syongari sc->sc_process_limit = TXP_PROC_DEFAULT; 2923189714Syongari error = resource_int_value(device_get_name(sc->sc_dev), 2924189714Syongari device_get_unit(sc->sc_dev), "process_limit", 2925189714Syongari &sc->sc_process_limit); 2926189714Syongari if (error == 0) { 2927189714Syongari if (sc->sc_process_limit < TXP_PROC_MIN || 2928189714Syongari sc->sc_process_limit > TXP_PROC_MAX) { 2929189714Syongari device_printf(sc->sc_dev, 2930189714Syongari "process_limit value out of range; " 2931189714Syongari "using default: %d\n", TXP_PROC_DEFAULT); 2932189714Syongari sc->sc_process_limit = TXP_PROC_DEFAULT; 2933189714Syongari } 293480219Swpaul } 2935189714Syongari tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, 2936189714Syongari NULL, "TXP statistics"); 2937189714Syongari parent = SYSCTL_CHILDREN(tree); 293880219Swpaul 2939189714Syongari /* Tx statistics. */ 2940189714Syongari tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, 2941189714Syongari NULL, "Tx MAC statistics"); 2942189714Syongari child = SYSCTL_CHILDREN(tree); 294380219Swpaul 2944189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "frames", 2945189714Syongari &stats->tx_frames, "Frames"); 2946189714Syongari TXP_SYSCTL_STAT_ADD64(ctx, child, "octets", 2947189714Syongari &stats->tx_bytes, "Octets"); 2948189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "deferred", 2949189714Syongari &stats->tx_deferred, "Deferred frames"); 2950189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 2951189714Syongari &stats->tx_late_colls, "Late collisions"); 2952189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "colls", 2953189714Syongari &stats->tx_colls, "Collisions"); 2954189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "carrier_lost", 2955189714Syongari &stats->tx_carrier_lost, "Carrier lost"); 2956189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "multi_colls", 2957189714Syongari &stats->tx_multi_colls, "Multiple collisions"); 2958189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "excess_colls", 2959189714Syongari &stats->tx_excess_colls, "Excessive collisions"); 2960189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_underruns", 2961189714Syongari &stats->tx_fifo_underruns, "FIFO underruns"); 2962189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_oflows", 2963189714Syongari &stats->tx_mcast_oflows, "Multicast overflows"); 2964189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered", 2965189714Syongari &stats->tx_filtered, "Filtered frames"); 296680219Swpaul 2967189714Syongari /* Rx statistics. */ 2968189714Syongari tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, 2969189714Syongari NULL, "Rx MAC statistics"); 2970189714Syongari child = SYSCTL_CHILDREN(tree); 2971189714Syongari 2972189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "frames", 2973189714Syongari &stats->rx_frames, "Frames"); 2974189714Syongari TXP_SYSCTL_STAT_ADD64(ctx, child, "octets", 2975189714Syongari &stats->rx_bytes, "Octets"); 2976189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "fifo_oflows", 2977189714Syongari &stats->rx_fifo_oflows, "FIFO overflows"); 2978189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "badssd", 2979189714Syongari &stats->rx_badssd, "Bad SSD"); 2980189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "crcerrs", 2981189714Syongari &stats->rx_crcerrs, "CRC errors"); 2982189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "lenerrs", 2983189714Syongari &stats->rx_lenerrs, "Length errors"); 2984189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "bcast_frames", 2985189714Syongari &stats->rx_bcast_frames, "Broadcast frames"); 2986189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "mcast_frames", 2987189714Syongari &stats->rx_mcast_frames, "Multicast frames"); 2988189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "oflows", 2989189714Syongari &stats->rx_oflows, "Overflows"); 2990189714Syongari TXP_SYSCTL_STAT_ADD32(ctx, child, "filtered", 2991189714Syongari &stats->rx_filtered, "Filtered frames"); 299280219Swpaul} 2993189714Syongari 2994189714Syongari#undef TXP_SYSCTL_STAT_ADD32 2995189714Syongari#undef TXP_SYSCTL_STAT_ADD64 2996189714Syongari 2997189714Syongaristatic int 2998189714Syongarisysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) 2999189714Syongari{ 3000189714Syongari int error, value; 3001189714Syongari 3002189714Syongari if (arg1 == NULL) 3003189714Syongari return (EINVAL); 3004189714Syongari value = *(int *)arg1; 3005189714Syongari error = sysctl_handle_int(oidp, &value, 0, req); 3006189714Syongari if (error || req->newptr == NULL) 3007189714Syongari return (error); 3008189714Syongari if (value < low || value > high) 3009189714Syongari return (EINVAL); 3010189714Syongari *(int *)arg1 = value; 3011189714Syongari 3012189714Syongari return (0); 3013189714Syongari} 3014189714Syongari 3015189714Syongaristatic int 3016189714Syongarisysctl_hw_txp_proc_limit(SYSCTL_HANDLER_ARGS) 3017189714Syongari{ 3018189714Syongari return (sysctl_int_range(oidp, arg1, arg2, req, 3019189714Syongari TXP_PROC_MIN, TXP_PROC_MAX)); 3020189714Syongari} 3021