if_ate.c revision 175120
1219019Sgabor/*- 2219019Sgabor * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3219019Sgabor * 4219019Sgabor * Redistribution and use in source and binary forms, with or without 5219019Sgabor * modification, are permitted provided that the following conditions 6219019Sgabor * are met: 7219019Sgabor * 1. Redistributions of source code must retain the above copyright 8219019Sgabor * notice, this list of conditions and the following disclaimer. 9219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 10219019Sgabor * notice, this list of conditions and the following disclaimer in the 11219019Sgabor * documentation and/or other materials provided with the distribution. 12219019Sgabor * 13219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14219019Sgabor * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15219019Sgabor * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16219019Sgabor * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17219019Sgabor * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18219019Sgabor * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19219019Sgabor * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20219019Sgabor * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21219019Sgabor * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22219019Sgabor * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23219019Sgabor */ 24219019Sgabor 25219019Sgabor/* TODO: (in no order) 26219019Sgabor * 27219019Sgabor * 8) Need to sync busdma goo in atestop 28219019Sgabor * 9) atestop should maybe free the mbufs? 29219019Sgabor * 30219019Sgabor * 1) detach 31219019Sgabor * 2) Free dma setup 32219019Sgabor * 3) Turn on the clock in pmc? Turn off? 33219019Sgabor */ 34219019Sgabor 35219019Sgabor#include <sys/cdefs.h> 36219019Sgabor__FBSDID("$FreeBSD: head/sys/arm/at91/if_ate.c 175120 2008-01-07 00:36:09Z cognet $"); 37219019Sgabor 38219019Sgabor#include <sys/param.h> 39219019Sgabor#include <sys/systm.h> 40219019Sgabor#include <sys/bus.h> 41219019Sgabor#include <sys/kernel.h> 42219019Sgabor#include <sys/mbuf.h> 43219019Sgabor#include <sys/malloc.h> 44219019Sgabor#include <sys/module.h> 45219019Sgabor#include <sys/rman.h> 46219019Sgabor#include <sys/socket.h> 47219019Sgabor#include <sys/sockio.h> 48219019Sgabor#include <sys/sysctl.h> 49219019Sgabor#include <machine/bus.h> 50219019Sgabor 51219019Sgabor#include <net/ethernet.h> 52219019Sgabor#include <net/if.h> 53219019Sgabor#include <net/if_arp.h> 54219019Sgabor#include <net/if_dl.h> 55219019Sgabor#include <net/if_media.h> 56219019Sgabor#include <net/if_mib.h> 57219019Sgabor#include <net/if_types.h> 58219019Sgabor 59219019Sgabor#ifdef INET 60219019Sgabor#include <netinet/in.h> 61219019Sgabor#include <netinet/in_systm.h> 62219019Sgabor#include <netinet/in_var.h> 63219019Sgabor#include <netinet/ip.h> 64219019Sgabor#endif 65219019Sgabor 66219019Sgabor#include <net/bpf.h> 67219019Sgabor#include <net/bpfdesc.h> 68219019Sgabor 69219019Sgabor#include <dev/mii/mii.h> 70219019Sgabor#include <dev/mii/miivar.h> 71219019Sgabor#include <arm/at91/if_atereg.h> 72219019Sgabor 73219019Sgabor#include "miibus_if.h" 74219019Sgabor 75219019Sgabor#define ATE_MAX_TX_BUFFERS 2 /* We have ping-pong tx buffers */ 76219019Sgabor#define ATE_MAX_RX_BUFFERS 64 77219019Sgabor 78219019Sgaborstruct ate_softc 79219019Sgabor{ 80219019Sgabor struct ifnet *ifp; /* ifnet pointer */ 81219019Sgabor struct mtx sc_mtx; /* basically a perimeter lock */ 82219019Sgabor device_t dev; /* Myself */ 83219019Sgabor device_t miibus; /* My child miibus */ 84219019Sgabor void *intrhand; /* Interrupt handle */ 85219019Sgabor struct resource *irq_res; /* IRQ resource */ 86219019Sgabor struct resource *mem_res; /* Memory resource */ 87219019Sgabor struct callout tick_ch; /* Tick callout */ 88219019Sgabor bus_dma_tag_t mtag; /* bus dma tag for mbufs */ 89219019Sgabor bus_dmamap_t tx_map[ATE_MAX_TX_BUFFERS]; 90219019Sgabor struct mbuf *sent_mbuf[ATE_MAX_TX_BUFFERS]; /* Sent mbufs */ 91219019Sgabor bus_dma_tag_t rxtag; 92219019Sgabor bus_dmamap_t rx_map[ATE_MAX_RX_BUFFERS]; 93219019Sgabor void *rx_buf[ATE_MAX_RX_BUFFERS]; /* RX buffer space */ 94219019Sgabor int rx_buf_ptr; 95219019Sgabor bus_dma_tag_t rx_desc_tag; 96219019Sgabor bus_dmamap_t rx_desc_map; 97219019Sgabor int txcur; /* current tx map pointer */ 98219019Sgabor bus_addr_t rx_desc_phys; 99219019Sgabor eth_rx_desc_t *rx_descs; 100219019Sgabor int use_rmii; 101219019Sgabor struct ifmib_iso_8802_3 mibdata; /* stuff for network mgmt */ 102219019Sgabor}; 103219019Sgabor 104219019Sgaborstatic inline uint32_t 105219019SgaborRD4(struct ate_softc *sc, bus_size_t off) 106219019Sgabor{ 107219019Sgabor return bus_read_4(sc->mem_res, off); 108219019Sgabor} 109219019Sgabor 110219019Sgaborstatic inline void 111219019SgaborWR4(struct ate_softc *sc, bus_size_t off, uint32_t val) 112219019Sgabor{ 113219019Sgabor bus_write_4(sc->mem_res, off, val); 114219019Sgabor} 115219019Sgabor 116219019Sgabor#define ATE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 117219019Sgabor#define ATE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 118219019Sgabor#define ATE_LOCK_INIT(_sc) \ 119219019Sgabor mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 120219019Sgabor MTX_NETWORK_LOCK, MTX_DEF) 121219019Sgabor#define ATE_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 122219019Sgabor#define ATE_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 123219019Sgabor#define ATE_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 124219019Sgabor 125219019Sgaborstatic devclass_t ate_devclass; 126219019Sgabor 127219019Sgabor/* ifnet entry points */ 128219019Sgabor 129219019Sgaborstatic void ateinit_locked(void *); 130219019Sgaborstatic void atestart_locked(struct ifnet *); 131219019Sgabor 132219019Sgaborstatic void ateinit(void *); 133219019Sgaborstatic void atestart(struct ifnet *); 134219019Sgaborstatic void atestop(struct ate_softc *); 135219019Sgaborstatic int ateioctl(struct ifnet * ifp, u_long, caddr_t); 136219019Sgabor 137219019Sgabor/* bus entry points */ 138219019Sgabor 139219019Sgaborstatic int ate_probe(device_t dev); 140219019Sgaborstatic int ate_attach(device_t dev); 141219019Sgaborstatic int ate_detach(device_t dev); 142219019Sgaborstatic void ate_intr(void *); 143219019Sgabor 144219019Sgabor/* helper routines */ 145219019Sgaborstatic int ate_activate(device_t dev); 146219019Sgaborstatic void ate_deactivate(device_t dev); 147219019Sgaborstatic int ate_ifmedia_upd(struct ifnet *ifp); 148219019Sgaborstatic void ate_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); 149219019Sgaborstatic int ate_get_mac(struct ate_softc *sc, u_char *eaddr); 150219019Sgaborstatic void ate_set_mac(struct ate_softc *sc, u_char *eaddr); 151219019Sgabor 152219019Sgabor/* 153219019Sgabor * The AT91 family of products has the ethernet called EMAC. However, 154219019Sgabor * it isn't self identifying. It is anticipated that the parent bus 155219019Sgabor * code will take care to only add ate devices where they really are. As 156219019Sgabor * such, we do nothing here to identify the device and just set its name. 157219019Sgabor */ 158219019Sgaborstatic int 159219019Sgaborate_probe(device_t dev) 160219019Sgabor{ 161219019Sgabor device_set_desc(dev, "EMAC"); 162219019Sgabor return (0); 163219019Sgabor} 164219019Sgabor 165219019Sgaborstatic int 166219019Sgaborate_attach(device_t dev) 167219019Sgabor{ 168219019Sgabor struct ate_softc *sc = device_get_softc(dev); 169219019Sgabor struct ifnet *ifp = NULL; 170219019Sgabor struct sysctl_ctx_list *sctx; 171219019Sgabor struct sysctl_oid *soid; 172219019Sgabor int err; 173219019Sgabor u_char eaddr[6]; 174219019Sgabor 175219019Sgabor sc->dev = dev; 176219019Sgabor err = ate_activate(dev); 177219019Sgabor if (err) 178219019Sgabor goto out; 179219019Sgabor 180219019Sgabor sc->use_rmii = (RD4(sc, ETH_CFG) & ETH_CFG_RMII) == ETH_CFG_RMII; 181219019Sgabor 182219019Sgabor /*Sysctls*/ 183219019Sgabor sctx = device_get_sysctl_ctx(dev); 184219019Sgabor soid = device_get_sysctl_tree(dev); 185219019Sgabor SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "rmii", 186219019Sgabor CTLFLAG_RD, &sc->use_rmii, 0, "rmii in use"); 187219019Sgabor 188219019Sgabor /* calling atestop before ifp is set is OK */ 189219019Sgabor atestop(sc); 190219019Sgabor ATE_LOCK_INIT(sc); 191219019Sgabor callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); 192219019Sgabor 193219019Sgabor if ((err = ate_get_mac(sc, eaddr)) != 0) { 194219019Sgabor device_printf(dev, "No MAC address set\n"); 195219019Sgabor goto out; 196219019Sgabor } 197219019Sgabor ate_set_mac(sc, eaddr); 198219019Sgabor 199219019Sgabor sc->ifp = ifp = if_alloc(IFT_ETHER); 200219019Sgabor if (mii_phy_probe(dev, &sc->miibus, ate_ifmedia_upd, ate_ifmedia_sts)) { 201219019Sgabor device_printf(dev, "Cannot find my PHY.\n"); 202219019Sgabor err = ENXIO; 203219019Sgabor goto out; 204219019Sgabor } 205219019Sgabor 206219019Sgabor ifp->if_softc = sc; 207219019Sgabor if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 208219019Sgabor ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 209219019Sgabor ifp->if_capabilities |= IFCAP_VLAN_MTU; 210219019Sgabor ifp->if_capenable |= IFCAP_VLAN_MTU; /* the hw bits already set */ 211219019Sgabor ifp->if_start = atestart; 212219019Sgabor ifp->if_ioctl = ateioctl; 213219019Sgabor ifp->if_init = ateinit; 214219019Sgabor ifp->if_baudrate = 10000000; 215219019Sgabor IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 216219019Sgabor ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 217219019Sgabor IFQ_SET_READY(&ifp->if_snd); 218219019Sgabor ifp->if_timer = 0; 219219019Sgabor ifp->if_linkmib = &sc->mibdata; 220219019Sgabor ifp->if_linkmiblen = sizeof(sc->mibdata); 221219019Sgabor sc->mibdata.dot3Compliance = DOT3COMPLIANCE_COLLS; 222219019Sgabor 223219019Sgabor ether_ifattach(ifp, eaddr); 224219019Sgabor 225219019Sgabor /* 226219019Sgabor * Activate the interrupt 227219019Sgabor */ 228219019Sgabor err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, 229219019Sgabor NULL, ate_intr, sc, &sc->intrhand); 230219019Sgabor if (err) { 231219019Sgabor ether_ifdetach(ifp); 232219019Sgabor ATE_LOCK_DESTROY(sc); 233219019Sgabor } 234219019Sgaborout:; 235219019Sgabor if (err) 236219019Sgabor ate_deactivate(dev); 237219019Sgabor if (err && ifp) 238219019Sgabor if_free(ifp); 239219019Sgabor return (err); 240219019Sgabor} 241219019Sgabor 242219019Sgaborstatic int 243219019Sgaborate_detach(device_t dev) 244219019Sgabor{ 245219019Sgabor return EBUSY; /* XXX TODO(1) */ 246219019Sgabor} 247219019Sgabor 248219019Sgaborstatic void 249219019Sgaborate_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 250219019Sgabor{ 251219019Sgabor struct ate_softc *sc; 252219019Sgabor 253219019Sgabor if (error != 0) 254219019Sgabor return; 255219019Sgabor sc = (struct ate_softc *)arg; 256219019Sgabor sc->rx_desc_phys = segs[0].ds_addr; 257219019Sgabor} 258219019Sgabor 259219019Sgaborstatic void 260219019Sgaborate_load_rx_buf(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 261219019Sgabor{ 262219019Sgabor struct ate_softc *sc; 263219019Sgabor int i; 264219019Sgabor 265219019Sgabor if (error != 0) 266219019Sgabor return; 267219019Sgabor sc = (struct ate_softc *)arg; 268219019Sgabor i = sc->rx_buf_ptr; 269219019Sgabor 270219019Sgabor /* 271219019Sgabor * For the last buffer, set the wrap bit so the controller 272219019Sgabor * restarts from the first descriptor. 273219019Sgabor */ 274219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_PREWRITE); 275219019Sgabor if (i == ATE_MAX_RX_BUFFERS - 1) 276219019Sgabor sc->rx_descs[i].addr = segs[0].ds_addr | ETH_WRAP_BIT; 277219019Sgabor else 278219019Sgabor sc->rx_descs[i].addr = segs[0].ds_addr; 279219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_POSTWRITE); 280219019Sgabor sc->rx_descs[i].status = 0; 281219019Sgabor /* Flush the memory in the mbuf */ 282219019Sgabor bus_dmamap_sync(sc->rxtag, sc->rx_map[i], BUS_DMASYNC_PREREAD); 283219019Sgabor} 284219019Sgabor 285219019Sgabor/* 286219019Sgabor * Compute the multicast filter for this device using the standard 287219019Sgabor * algorithm. I wonder why this isn't in ether somewhere as a lot 288219019Sgabor * of different MAC chips use this method (or the reverse the bits) 289219019Sgabor * method. 290219019Sgabor */ 291219019Sgaborstatic void 292219019Sgaborate_setmcast(struct ate_softc *sc) 293219019Sgabor{ 294219019Sgabor uint32_t index; 295219019Sgabor uint32_t mcaf[2]; 296219019Sgabor u_char *af = (u_char *) mcaf; 297219019Sgabor struct ifmultiaddr *ifma; 298219019Sgabor 299219019Sgabor mcaf[0] = 0; 300219019Sgabor mcaf[1] = 0; 301219019Sgabor 302219019Sgabor IF_ADDR_LOCK(sc->ifp); 303219019Sgabor TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 304219019Sgabor if (ifma->ifma_addr->sa_family != AF_LINK) 305219019Sgabor continue; 306219019Sgabor index = ether_crc32_be(LLADDR((struct sockaddr_dl *) 307219019Sgabor ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; 308219019Sgabor af[index >> 3] |= 1 << (index & 7); 309219019Sgabor } 310219019Sgabor IF_ADDR_UNLOCK(sc->ifp); 311219019Sgabor 312219019Sgabor /* 313219019Sgabor * Write the hash to the hash register. This card can also 314219019Sgabor * accept unicast packets as well as multicast packets using this 315219019Sgabor * register for easier bridging operations, but we don't take 316219019Sgabor * advantage of that. Locks here are to avoid LOR with the 317219019Sgabor * IF_ADDR_LOCK, but might not be strictly necessary. 318219019Sgabor */ 319219019Sgabor WR4(sc, ETH_HSL, mcaf[0]); 320219019Sgabor WR4(sc, ETH_HSH, mcaf[1]); 321219019Sgabor} 322219019Sgabor 323219019Sgaborstatic int 324219019Sgaborate_activate(device_t dev) 325219019Sgabor{ 326219019Sgabor struct ate_softc *sc; 327219019Sgabor int rid, err, i; 328219019Sgabor 329219019Sgabor sc = device_get_softc(dev); 330219019Sgabor rid = 0; 331219019Sgabor sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 332219019Sgabor RF_ACTIVE); 333219019Sgabor if (sc->mem_res == NULL) 334219019Sgabor goto errout; 335219019Sgabor rid = 0; 336219019Sgabor sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 337219019Sgabor RF_ACTIVE); 338219019Sgabor if (sc->irq_res == NULL) 339219019Sgabor goto errout; 340219019Sgabor 341219019Sgabor /* 342219019Sgabor * Allocate DMA tags and maps 343219019Sgabor */ 344219019Sgabor err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, 345219019Sgabor BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, 346219019Sgabor busdma_lock_mutex, &sc->sc_mtx, &sc->mtag); 347219019Sgabor if (err != 0) 348219019Sgabor goto errout; 349219019Sgabor for (i = 0; i < ATE_MAX_TX_BUFFERS; i++) { 350219019Sgabor err = bus_dmamap_create(sc->mtag, 0, &sc->tx_map[i]); 351219019Sgabor if (err != 0) 352219019Sgabor goto errout; 353219019Sgabor } 354219019Sgabor /* 355219019Sgabor * Allocate our Rx buffers. This chip has a rx structure that's filled 356219019Sgabor * in 357219019Sgabor */ 358219019Sgabor 359219019Sgabor /* 360219019Sgabor * Allocate DMA tags and maps for RX. 361219019Sgabor */ 362219019Sgabor err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, 363219019Sgabor BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, 364219019Sgabor busdma_lock_mutex, &sc->sc_mtx, &sc->rxtag); 365219019Sgabor if (err != 0) 366219019Sgabor goto errout; 367219019Sgabor 368219019Sgabor /* Dma TAG and MAP for the rx descriptors. */ 369219019Sgabor err = bus_dma_tag_create(NULL, sizeof(eth_rx_desc_t), 0, 370219019Sgabor BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 371219019Sgabor ATE_MAX_RX_BUFFERS * sizeof(eth_rx_desc_t), 1, 372219019Sgabor ATE_MAX_RX_BUFFERS * sizeof(eth_rx_desc_t), 0, busdma_lock_mutex, 373219019Sgabor &sc->sc_mtx, &sc->rx_desc_tag); 374219019Sgabor if (err != 0) 375219019Sgabor goto errout; 376219019Sgabor if (bus_dmamem_alloc(sc->rx_desc_tag, (void **)&sc->rx_descs, 377219019Sgabor BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->rx_desc_map) != 0) 378219019Sgabor goto errout; 379219019Sgabor if (bus_dmamap_load(sc->rx_desc_tag, sc->rx_desc_map, 380219019Sgabor sc->rx_descs, ATE_MAX_RX_BUFFERS * sizeof(eth_rx_desc_t), 381219019Sgabor ate_getaddr, sc, 0) != 0) 382219019Sgabor goto errout; 383219019Sgabor /* XXX TODO(5) Put this in ateinit_locked? */ 384219019Sgabor for (i = 0; i < ATE_MAX_RX_BUFFERS; i++) { 385219019Sgabor sc->rx_buf_ptr = i; 386219019Sgabor if (bus_dmamem_alloc(sc->rxtag, (void **)&sc->rx_buf[i], 387219019Sgabor BUS_DMA_NOWAIT, &sc->rx_map[i]) != 0) 388219019Sgabor goto errout; 389219019Sgabor if (bus_dmamap_load(sc->rxtag, sc->rx_map[i], sc->rx_buf[i], 390219019Sgabor MCLBYTES, ate_load_rx_buf, sc, 0) != 0) 391219019Sgabor goto errout; 392219019Sgabor } 393219019Sgabor sc->rx_buf_ptr = 0; 394219019Sgabor /* Flush the memory for the EMAC rx descriptor */ 395219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_PREWRITE); 396219019Sgabor /* Write the descriptor queue address. */ 397219019Sgabor WR4(sc, ETH_RBQP, sc->rx_desc_phys); 398219019Sgabor return (0); 399219019Sgaborerrout: 400219019Sgabor ate_deactivate(dev); 401219019Sgabor return (ENOMEM); 402219019Sgabor} 403219019Sgabor 404219019Sgaborstatic void 405219019Sgaborate_deactivate(device_t dev) 406219019Sgabor{ 407219019Sgabor struct ate_softc *sc; 408219019Sgabor 409219019Sgabor sc = device_get_softc(dev); 410219019Sgabor /* XXX TODO(2) teardown busdma junk, below from fxp -- customize */ 411219019Sgabor#if 0 412219019Sgabor if (sc->fxp_mtag) { 413219019Sgabor for (i = 0; i < FXP_NRFABUFS; i++) { 414219019Sgabor rxp = &sc->fxp_desc.rx_list[i]; 415219019Sgabor if (rxp->rx_mbuf != NULL) { 416219019Sgabor bus_dmamap_sync(sc->fxp_mtag, rxp->rx_map, 417219019Sgabor BUS_DMASYNC_POSTREAD); 418219019Sgabor bus_dmamap_unload(sc->fxp_mtag, rxp->rx_map); 419219019Sgabor m_freem(rxp->rx_mbuf); 420219019Sgabor } 421219019Sgabor bus_dmamap_destroy(sc->fxp_mtag, rxp->rx_map); 422219019Sgabor } 423219019Sgabor bus_dmamap_destroy(sc->fxp_mtag, sc->spare_map); 424219019Sgabor for (i = 0; i < FXP_NTXCB; i++) { 425219019Sgabor txp = &sc->fxp_desc.tx_list[i]; 426219019Sgabor if (txp->tx_mbuf != NULL) { 427219019Sgabor bus_dmamap_sync(sc->fxp_mtag, txp->tx_map, 428219019Sgabor BUS_DMASYNC_POSTWRITE); 429219019Sgabor bus_dmamap_unload(sc->fxp_mtag, txp->tx_map); 430219019Sgabor m_freem(txp->tx_mbuf); 431219019Sgabor } 432219019Sgabor bus_dmamap_destroy(sc->fxp_mtag, txp->tx_map); 433219019Sgabor } 434219019Sgabor bus_dma_tag_destroy(sc->fxp_mtag); 435219019Sgabor } 436219019Sgabor if (sc->fxp_stag) 437219019Sgabor bus_dma_tag_destroy(sc->fxp_stag); 438219019Sgabor if (sc->cbl_tag) 439219019Sgabor bus_dma_tag_destroy(sc->cbl_tag); 440219019Sgabor if (sc->mcs_tag) 441219019Sgabor bus_dma_tag_destroy(sc->mcs_tag); 442219019Sgabor#endif 443219019Sgabor if (sc->intrhand) 444219019Sgabor bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 445219019Sgabor sc->intrhand = 0; 446219019Sgabor bus_generic_detach(sc->dev); 447219019Sgabor if (sc->miibus) 448219019Sgabor device_delete_child(sc->dev, sc->miibus); 449219019Sgabor if (sc->mem_res) 450219019Sgabor bus_release_resource(dev, SYS_RES_IOPORT, 451219019Sgabor rman_get_rid(sc->mem_res), sc->mem_res); 452219019Sgabor sc->mem_res = 0; 453219019Sgabor if (sc->irq_res) 454219019Sgabor bus_release_resource(dev, SYS_RES_IRQ, 455219019Sgabor rman_get_rid(sc->irq_res), sc->irq_res); 456219019Sgabor sc->irq_res = 0; 457219019Sgabor return; 458219019Sgabor} 459219019Sgabor 460219019Sgabor/* 461219019Sgabor * Change media according to request. 462219019Sgabor */ 463219019Sgaborstatic int 464219019Sgaborate_ifmedia_upd(struct ifnet *ifp) 465219019Sgabor{ 466219019Sgabor struct ate_softc *sc = ifp->if_softc; 467219019Sgabor struct mii_data *mii; 468219019Sgabor 469219019Sgabor mii = device_get_softc(sc->miibus); 470219019Sgabor ATE_LOCK(sc); 471219019Sgabor mii_mediachg(mii); 472219019Sgabor ATE_UNLOCK(sc); 473219019Sgabor return (0); 474219019Sgabor} 475219019Sgabor 476219019Sgabor/* 477219019Sgabor * Notify the world which media we're using. 478219019Sgabor */ 479219019Sgaborstatic void 480219019Sgaborate_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 481219019Sgabor{ 482219019Sgabor struct ate_softc *sc = ifp->if_softc; 483219019Sgabor struct mii_data *mii; 484219019Sgabor 485219019Sgabor mii = device_get_softc(sc->miibus); 486219019Sgabor ATE_LOCK(sc); 487219019Sgabor mii_pollstat(mii); 488219019Sgabor ifmr->ifm_active = mii->mii_media_active; 489219019Sgabor ifmr->ifm_status = mii->mii_media_status; 490219019Sgabor ATE_UNLOCK(sc); 491219019Sgabor} 492219019Sgabor 493219019Sgaborstatic void 494219019Sgaborate_stat_update(struct ate_softc *sc, int active) 495219019Sgabor{ 496219019Sgabor /* 497219019Sgabor * The speed and full/half-duplex state needs to be reflected 498219019Sgabor * in the ETH_CFG register. 499219019Sgabor */ 500219019Sgabor if (IFM_SUBTYPE(active) == IFM_10_T) 501219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) & ~ETH_CFG_SPD); 502219019Sgabor else 503219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_SPD); 504219019Sgabor if (active & IFM_FDX) 505219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_FD); 506219019Sgabor else 507219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) & ~ETH_CFG_FD); 508219019Sgabor} 509219019Sgabor 510219019Sgaborstatic void 511219019Sgaborate_tick(void *xsc) 512219019Sgabor{ 513219019Sgabor struct ate_softc *sc = xsc; 514219019Sgabor struct ifnet *ifp = sc->ifp; 515219019Sgabor struct mii_data *mii; 516219019Sgabor int active; 517219019Sgabor uint32_t c; 518219019Sgabor 519219019Sgabor /* 520219019Sgabor * The KB920x boot loader tests ETH_SR & ETH_SR_LINK and will ask 521219019Sgabor * the MII if there's a link if this bit is clear. Not sure if we 522219019Sgabor * should do the same thing here or not. 523219019Sgabor */ 524219019Sgabor ATE_ASSERT_LOCKED(sc); 525219019Sgabor if (sc->miibus != NULL) { 526219019Sgabor mii = device_get_softc(sc->miibus); 527219019Sgabor active = mii->mii_media_active; 528219019Sgabor mii_tick(mii); 529219019Sgabor if (mii->mii_media_status & IFM_ACTIVE && 530219019Sgabor active != mii->mii_media_active) 531219019Sgabor ate_stat_update(sc, mii->mii_media_active); 532219019Sgabor } 533219019Sgabor 534219019Sgabor /* 535219019Sgabor * Update the stats as best we can. When we're done, clear 536219019Sgabor * the status counters and start over. We're supposed to read these 537219019Sgabor * registers often enough that they won't overflow. Hopefully 538219019Sgabor * once a second is often enough. Some don't map well to 539219019Sgabor * the dot3Stats mib, so for those we just count them as general 540219019Sgabor * errors. Stats for iframes, ibutes, oframes and obytes are 541219019Sgabor * collected elsewhere. These registers zero on a read to prevent 542219019Sgabor * races. For all the collision stats, also update the collision 543219019Sgabor * stats for the interface. 544219019Sgabor */ 545219019Sgabor sc->mibdata.dot3StatsAlignmentErrors += RD4(sc, ETH_ALE); 546219019Sgabor sc->mibdata.dot3StatsFCSErrors += RD4(sc, ETH_SEQE); 547219019Sgabor c = RD4(sc, ETH_SCOL); 548219019Sgabor ifp->if_collisions += c; 549219019Sgabor sc->mibdata.dot3StatsSingleCollisionFrames += c; 550219019Sgabor c = RD4(sc, ETH_MCOL); 551219019Sgabor sc->mibdata.dot3StatsMultipleCollisionFrames += c; 552219019Sgabor ifp->if_collisions += c; 553219019Sgabor sc->mibdata.dot3StatsSQETestErrors += RD4(sc, ETH_SQEE); 554219019Sgabor sc->mibdata.dot3StatsDeferredTransmissions += RD4(sc, ETH_DTE); 555219019Sgabor c = RD4(sc, ETH_LCOL); 556219019Sgabor sc->mibdata.dot3StatsLateCollisions += c; 557219019Sgabor ifp->if_collisions += c; 558219019Sgabor c = RD4(sc, ETH_ECOL); 559219019Sgabor sc->mibdata.dot3StatsExcessiveCollisions += c; 560219019Sgabor ifp->if_collisions += c; 561219019Sgabor sc->mibdata.dot3StatsCarrierSenseErrors += RD4(sc, ETH_CSE); 562219019Sgabor sc->mibdata.dot3StatsFrameTooLongs += RD4(sc, ETH_ELR); 563219019Sgabor sc->mibdata.dot3StatsInternalMacReceiveErrors += RD4(sc, ETH_DRFC); 564219019Sgabor /* 565219019Sgabor * not sure where to lump these, so count them against the errors 566219019Sgabor * for the interface. 567219019Sgabor */ 568219019Sgabor sc->ifp->if_oerrors += RD4(sc, ETH_TUE); 569219019Sgabor sc->ifp->if_ierrors += RD4(sc, ETH_CDE) + RD4(sc, ETH_RJB) + 570219019Sgabor RD4(sc, ETH_USF); 571219019Sgabor 572219019Sgabor /* 573219019Sgabor * Schedule another timeout one second from now. 574219019Sgabor */ 575219019Sgabor callout_reset(&sc->tick_ch, hz, ate_tick, sc); 576219019Sgabor} 577219019Sgabor 578219019Sgaborstatic void 579219019Sgaborate_set_mac(struct ate_softc *sc, u_char *eaddr) 580219019Sgabor{ 581219019Sgabor WR4(sc, ETH_SA1L, (eaddr[3] << 24) | (eaddr[2] << 16) | 582219019Sgabor (eaddr[1] << 8) | eaddr[0]); 583219019Sgabor WR4(sc, ETH_SA1H, (eaddr[5] << 8) | (eaddr[4])); 584219019Sgabor} 585219019Sgabor 586219019Sgaborstatic int 587219019Sgaborate_get_mac(struct ate_softc *sc, u_char *eaddr) 588219019Sgabor{ 589219019Sgabor uint32_t low, high; 590219019Sgabor 591219019Sgabor /* 592219019Sgabor * The boot loader setup the MAC with an address, if one is set in 593219019Sgabor * the loader. The TSC loader will also set the MAC address in a 594219019Sgabor * similar way. Grab the MAC address from the SA1[HL] registers. 595219019Sgabor */ 596219019Sgabor low = RD4(sc, ETH_SA1L); 597219019Sgabor high = RD4(sc, ETH_SA1H); 598219019Sgabor if ((low | (high & 0xffff)) == 0) 599219019Sgabor return (ENXIO); 600219019Sgabor eaddr[0] = low & 0xff; 601219019Sgabor eaddr[1] = (low >> 8) & 0xff; 602219019Sgabor eaddr[2] = (low >> 16) & 0xff; 603219019Sgabor eaddr[3] = (low >> 24) & 0xff; 604219019Sgabor eaddr[4] = high & 0xff; 605219019Sgabor eaddr[5] = (high >> 8) & 0xff; 606219019Sgabor return (0); 607219019Sgabor} 608219019Sgabor 609219019Sgaborstatic void 610219019Sgaborate_intr(void *xsc) 611219019Sgabor{ 612219019Sgabor struct ate_softc *sc = xsc; 613219019Sgabor struct ifnet *ifp = sc->ifp; 614219019Sgabor int status; 615219019Sgabor int i; 616219019Sgabor void *bp; 617219019Sgabor struct mbuf *mb; 618219019Sgabor uint32_t rx_stat; 619219019Sgabor 620219019Sgabor status = RD4(sc, ETH_ISR); 621219019Sgabor if (status == 0) 622219019Sgabor return; 623219019Sgabor if (status & ETH_ISR_RCOM) { 624219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, 625219019Sgabor BUS_DMASYNC_POSTREAD); 626219019Sgabor while (sc->rx_descs[sc->rx_buf_ptr].addr & ETH_CPU_OWNER) { 627219019Sgabor i = sc->rx_buf_ptr; 628219019Sgabor sc->rx_buf_ptr = (i + 1) % ATE_MAX_RX_BUFFERS; 629219019Sgabor bp = sc->rx_buf[i]; 630219019Sgabor rx_stat = sc->rx_descs[i].status; 631219019Sgabor if ((rx_stat & ETH_LEN_MASK) == 0) { 632219019Sgabor printf("ignoring bogus 0 len packet\n"); 633219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, 634219019Sgabor BUS_DMASYNC_PREWRITE); 635219019Sgabor sc->rx_descs[i].addr &= ~ETH_CPU_OWNER; 636219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, 637219019Sgabor BUS_DMASYNC_POSTWRITE); 638219019Sgabor continue; 639219019Sgabor } 640219019Sgabor /* Flush memory for mbuf so we don't get stale bytes */ 641219019Sgabor bus_dmamap_sync(sc->rxtag, sc->rx_map[i], 642219019Sgabor BUS_DMASYNC_POSTREAD); 643219019Sgabor WR4(sc, ETH_RSR, RD4(sc, ETH_RSR)); 644219019Sgabor 645219019Sgabor /* 646219019Sgabor * The length returned by the device includes the 647219019Sgabor * ethernet CRC calculation for the packet, but 648219019Sgabor * ifnet drivers are supposed to discard it. 649219019Sgabor */ 650219019Sgabor mb = m_devget(sc->rx_buf[i], 651219019Sgabor (rx_stat & ETH_LEN_MASK) - ETHER_CRC_LEN, 652219019Sgabor ETHER_ALIGN, ifp, NULL); 653219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, 654219019Sgabor BUS_DMASYNC_PREWRITE); 655219019Sgabor sc->rx_descs[i].addr &= ~ETH_CPU_OWNER; 656219019Sgabor bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, 657219019Sgabor BUS_DMASYNC_POSTWRITE); 658219019Sgabor bus_dmamap_sync(sc->rxtag, sc->rx_map[i], 659219019Sgabor BUS_DMASYNC_PREREAD); 660219019Sgabor if (mb != NULL) { 661219019Sgabor ifp->if_ipackets++; 662219019Sgabor (*ifp->if_input)(ifp, mb); 663219019Sgabor } 664219019Sgabor 665219019Sgabor } 666219019Sgabor } 667219019Sgabor if (status & ETH_ISR_TCOM) { 668219019Sgabor ATE_LOCK(sc); 669219019Sgabor /* XXX TSR register should be cleared */ 670219019Sgabor if (sc->sent_mbuf[0]) { 671219019Sgabor bus_dmamap_sync(sc->rxtag, sc->tx_map[0], 672219019Sgabor BUS_DMASYNC_POSTWRITE); 673219019Sgabor m_freem(sc->sent_mbuf[0]); 674219019Sgabor ifp->if_opackets++; 675219019Sgabor sc->sent_mbuf[0] = NULL; 676219019Sgabor } 677219019Sgabor if (sc->sent_mbuf[1]) { 678219019Sgabor if (RD4(sc, ETH_TSR) & ETH_TSR_IDLE) { 679219019Sgabor bus_dmamap_sync(sc->rxtag, sc->tx_map[1], 680219019Sgabor BUS_DMASYNC_POSTWRITE); 681219019Sgabor m_freem(sc->sent_mbuf[1]); 682219019Sgabor ifp->if_opackets++; 683219019Sgabor sc->txcur = 0; 684219019Sgabor sc->sent_mbuf[0] = sc->sent_mbuf[1] = NULL; 685219019Sgabor } else { 686219019Sgabor sc->sent_mbuf[0] = sc->sent_mbuf[1]; 687219019Sgabor sc->sent_mbuf[1] = NULL; 688219019Sgabor sc->txcur = 1; 689219019Sgabor } 690219019Sgabor } else { 691219019Sgabor sc->sent_mbuf[0] = NULL; 692219019Sgabor sc->txcur = 0; 693219019Sgabor } 694219019Sgabor /* 695219019Sgabor * We're no longer busy, so clear the busy flag and call the 696219019Sgabor * start routine to xmit more packets. 697219019Sgabor */ 698219019Sgabor sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 699219019Sgabor atestart_locked(sc->ifp); 700219019Sgabor ATE_UNLOCK(sc); 701219019Sgabor } 702219019Sgabor if (status & ETH_ISR_RBNA) { 703219019Sgabor printf("RBNA workaround\n"); 704219019Sgabor /* Workaround Errata #11 */ 705219019Sgabor WR4(sc, ETH_CTL, RD4(sc, ETH_CTL) &~ ETH_CTL_RE); 706219019Sgabor WR4(sc, ETH_CTL, RD4(sc, ETH_CTL) | ETH_CTL_RE); 707219019Sgabor } 708219019Sgabor} 709219019Sgabor 710219019Sgabor/* 711219019Sgabor * Reset and initialize the chip 712219019Sgabor */ 713219019Sgaborstatic void 714219019Sgaborateinit_locked(void *xsc) 715219019Sgabor{ 716219019Sgabor struct ate_softc *sc = xsc; 717219019Sgabor struct ifnet *ifp = sc->ifp; 718219019Sgabor struct mii_data *mii; 719219019Sgabor 720219019Sgabor ATE_ASSERT_LOCKED(sc); 721219019Sgabor 722219019Sgabor /* 723219019Sgabor * XXX TODO(3) 724219019Sgabor * we need to turn on the EMAC clock in the pmc. With the 725219019Sgabor * default boot loader, this is already turned on. However, we 726219019Sgabor * need to think about how best to turn it on/off as the interface 727219019Sgabor * is brought up/down, as well as dealing with the mii bus... 728219019Sgabor * 729219019Sgabor * We also need to multiplex the pins correctly. 730219019Sgabor */ 731219019Sgabor 732219019Sgabor /* 733219019Sgabor * There are two different ways that the mii bus is connected 734219019Sgabor * to this chip. Select the right one based on a compile-time 735219019Sgabor * option. 736219019Sgabor */ 737219019Sgabor if (sc->use_rmii) 738219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_RMII); 739219019Sgabor else 740219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) & ~ETH_CFG_RMII); 741219019Sgabor 742219019Sgabor /* 743219019Sgabor * Turn on the multicast hash, and write 0's to it. 744219019Sgabor */ 745219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_MTI); 746219019Sgabor WR4(sc, ETH_HSH, 0); 747219019Sgabor WR4(sc, ETH_HSL, 0); 748219019Sgabor 749219019Sgabor WR4(sc, ETH_CTL, RD4(sc, ETH_CTL) | ETH_CTL_TE | ETH_CTL_RE); 750219019Sgabor WR4(sc, ETH_IER, ETH_ISR_RCOM | ETH_ISR_TCOM | ETH_ISR_RBNA); 751219019Sgabor 752219019Sgabor /* 753219019Sgabor * Boot loader fills in MAC address. If that's not the case, then 754219019Sgabor * we should set SA1L and SA1H here to the appropriate value. Note: 755219019Sgabor * the byte order is big endian, not little endian, so we have some 756219019Sgabor * swapping to do. Again, if we need it (which I don't think we do). 757219019Sgabor */ 758219019Sgabor ate_setmcast(sc); 759219019Sgabor 760219019Sgabor /* enable big packets */ 761219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_BIG); 762219019Sgabor 763219019Sgabor /* 764219019Sgabor * Set 'running' flag, and clear output active flag 765219019Sgabor * and attempt to start the output 766219019Sgabor */ 767219019Sgabor ifp->if_drv_flags |= IFF_DRV_RUNNING; 768219019Sgabor ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 769219019Sgabor 770219019Sgabor mii = device_get_softc(sc->miibus); 771219019Sgabor mii_pollstat(mii); 772219019Sgabor ate_stat_update(sc, mii->mii_media_active); 773219019Sgabor atestart_locked(ifp); 774219019Sgabor 775219019Sgabor callout_reset(&sc->tick_ch, hz, ate_tick, sc); 776219019Sgabor} 777219019Sgabor 778219019Sgabor/* 779219019Sgabor * dequeu packets and transmit 780219019Sgabor */ 781219019Sgaborstatic void 782219019Sgaboratestart_locked(struct ifnet *ifp) 783219019Sgabor{ 784219019Sgabor struct ate_softc *sc = ifp->if_softc; 785219019Sgabor struct mbuf *m, *mdefrag; 786219019Sgabor bus_dma_segment_t segs[1]; 787219019Sgabor int nseg, e; 788219019Sgabor 789219019Sgabor ATE_ASSERT_LOCKED(sc); 790219019Sgabor if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 791219019Sgabor return; 792219019Sgabor 793219019Sgabor while (sc->txcur < ATE_MAX_TX_BUFFERS) { 794219019Sgabor /* 795219019Sgabor * check to see if there's room to put another packet into the 796219019Sgabor * xmit queue. The EMAC chip has a ping-pong buffer for xmit 797219019Sgabor * packets. We use OACTIVE to indicate "we can stuff more into 798219019Sgabor * our buffers (clear) or not (set)." 799219019Sgabor */ 800219019Sgabor if (!(RD4(sc, ETH_TSR) & ETH_TSR_BNQ)) { 801219019Sgabor ifp->if_drv_flags |= IFF_DRV_OACTIVE; 802219019Sgabor return; 803219019Sgabor } 804219019Sgabor IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 805219019Sgabor if (m == 0) { 806219019Sgabor ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 807219019Sgabor return; 808219019Sgabor } 809219019Sgabor e = bus_dmamap_load_mbuf_sg(sc->mtag, sc->tx_map[sc->txcur], m, 810219019Sgabor segs, &nseg, 0); 811219019Sgabor if (e == EFBIG) { 812219019Sgabor mdefrag = m_defrag(m, M_DONTWAIT); 813219019Sgabor if (mdefrag == NULL) { 814219019Sgabor IFQ_DRV_PREPEND(&ifp->if_snd, m); 815219019Sgabor return; 816219019Sgabor } 817219019Sgabor m = mdefrag; 818219019Sgabor e = bus_dmamap_load_mbuf_sg(sc->mtag, 819219019Sgabor sc->tx_map[sc->txcur], m, segs, &nseg, 0); 820219019Sgabor } 821219019Sgabor if (e != 0) { 822219019Sgabor m_freem(m); 823219019Sgabor continue; 824219019Sgabor } 825219019Sgabor bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txcur], 826219019Sgabor BUS_DMASYNC_PREWRITE); 827219019Sgabor 828219019Sgabor /* 829219019Sgabor * tell the hardware to xmit the packet. 830219019Sgabor */ 831219019Sgabor WR4(sc, ETH_TAR, segs[0].ds_addr); 832219019Sgabor WR4(sc, ETH_TCR, segs[0].ds_len); 833219019Sgabor 834219019Sgabor /* 835219019Sgabor * Tap off here if there is a bpf listener. 836219019Sgabor */ 837219019Sgabor BPF_MTAP(ifp, m); 838219019Sgabor 839219019Sgabor sc->sent_mbuf[sc->txcur] = m; 840219019Sgabor sc->txcur++; 841219019Sgabor } 842219019Sgabor} 843219019Sgabor 844219019Sgaborstatic void 845219019Sgaborateinit(void *xsc) 846219019Sgabor{ 847219019Sgabor struct ate_softc *sc = xsc; 848219019Sgabor ATE_LOCK(sc); 849219019Sgabor ateinit_locked(sc); 850219019Sgabor ATE_UNLOCK(sc); 851219019Sgabor} 852219019Sgabor 853219019Sgaborstatic void 854219019Sgaboratestart(struct ifnet *ifp) 855219019Sgabor{ 856219019Sgabor struct ate_softc *sc = ifp->if_softc; 857219019Sgabor ATE_LOCK(sc); 858219019Sgabor atestart_locked(ifp); 859219019Sgabor ATE_UNLOCK(sc); 860219019Sgabor} 861219019Sgabor 862219019Sgabor/* 863219019Sgabor * Turn off interrupts, and stop the nic. Can be called with sc->ifp NULL 864219019Sgabor * so be careful. 865219019Sgabor */ 866219019Sgaborstatic void 867219019Sgaboratestop(struct ate_softc *sc) 868219019Sgabor{ 869219019Sgabor struct ifnet *ifp = sc->ifp; 870219019Sgabor 871219019Sgabor if (ifp) { 872219019Sgabor ifp->if_timer = 0; 873219019Sgabor ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 874219019Sgabor } 875219019Sgabor 876219019Sgabor callout_stop(&sc->tick_ch); 877219019Sgabor 878219019Sgabor /* 879219019Sgabor * Enable some parts of the MAC that are needed always (like the 880219019Sgabor * MII bus. This turns off the RE and TE bits, which will remain 881219019Sgabor * off until ateinit() is called to turn them on. With RE and TE 882219019Sgabor * turned off, there's no DMA to worry about after this write. 883219019Sgabor */ 884219019Sgabor WR4(sc, ETH_CTL, ETH_CTL_MPE); 885219019Sgabor 886219019Sgabor /* 887219019Sgabor * Turn off all the configured options and revert to defaults. 888219019Sgabor */ 889219019Sgabor WR4(sc, ETH_CFG, ETH_CFG_CLK_32); 890219019Sgabor 891219019Sgabor /* 892219019Sgabor * Turn off all the interrupts, and ack any pending ones by reading 893219019Sgabor * the ISR. 894219019Sgabor */ 895219019Sgabor WR4(sc, ETH_IDR, 0xffffffff); 896219019Sgabor RD4(sc, ETH_ISR); 897219019Sgabor 898219019Sgabor /* 899219019Sgabor * Clear out the Transmit and Receiver Status registers of any 900219019Sgabor * errors they may be reporting 901219019Sgabor */ 902219019Sgabor WR4(sc, ETH_TSR, 0xffffffff); 903219019Sgabor WR4(sc, ETH_RSR, 0xffffffff); 904219019Sgabor 905219019Sgabor /* 906219019Sgabor * XXX TODO(8) 907219019Sgabor * need to worry about the busdma resources? Yes, I think we need 908219019Sgabor * to sync and unload them. We may also need to release the mbufs 909219019Sgabor * that are assocaited with RX and TX operations. 910219019Sgabor */ 911219019Sgabor 912219019Sgabor /* 913219019Sgabor * XXX we should power down the EMAC if it isn't in use, after 914219019Sgabor * putting it into loopback mode. This saves about 400uA according 915219019Sgabor * to the datasheet. 916219019Sgabor */ 917219019Sgabor} 918219019Sgabor 919219019Sgaborstatic int 920219019Sgaborateioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 921219019Sgabor{ 922219019Sgabor struct ate_softc *sc = ifp->if_softc; 923219019Sgabor struct mii_data *mii; 924219019Sgabor struct ifreq *ifr = (struct ifreq *)data; 925219019Sgabor int mask, error = 0; 926219019Sgabor 927219019Sgabor switch (cmd) { 928219019Sgabor case SIOCSIFFLAGS: 929219019Sgabor ATE_LOCK(sc); 930219019Sgabor if ((ifp->if_flags & IFF_UP) == 0 && 931219019Sgabor ifp->if_drv_flags & IFF_DRV_RUNNING) { 932219019Sgabor ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 933219019Sgabor atestop(sc); 934219019Sgabor } else { 935219019Sgabor /* reinitialize card on any parameter change */ 936219019Sgabor ateinit_locked(sc); 937219019Sgabor } 938219019Sgabor ATE_UNLOCK(sc); 939219019Sgabor break; 940219019Sgabor 941219019Sgabor case SIOCADDMULTI: 942219019Sgabor case SIOCDELMULTI: 943219019Sgabor /* update multicast filter list. */ 944219019Sgabor ATE_LOCK(sc); 945219019Sgabor ate_setmcast(sc); 946219019Sgabor ATE_UNLOCK(sc); 947219019Sgabor error = 0; 948219019Sgabor break; 949219019Sgabor 950219019Sgabor case SIOCSIFMEDIA: 951219019Sgabor case SIOCGIFMEDIA: 952219019Sgabor mii = device_get_softc(sc->miibus); 953219019Sgabor error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 954219019Sgabor break; 955219019Sgabor case SIOCSIFCAP: 956219019Sgabor mask = ifp->if_capenable ^ ifr->ifr_reqcap; 957219019Sgabor if (mask & IFCAP_VLAN_MTU) { 958219019Sgabor ATE_LOCK(sc); 959219019Sgabor if (ifr->ifr_reqcap & IFCAP_VLAN_MTU) { 960219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_BIG); 961219019Sgabor ifp->if_capenable |= IFCAP_VLAN_MTU; 962219019Sgabor } else { 963219019Sgabor WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) & ~ETH_CFG_BIG); 964219019Sgabor ifp->if_capenable &= ~IFCAP_VLAN_MTU; 965219019Sgabor } 966219019Sgabor ATE_UNLOCK(sc); 967219019Sgabor } 968219019Sgabor default: 969219019Sgabor error = ether_ioctl(ifp, cmd, data); 970219019Sgabor break; 971219019Sgabor } 972219019Sgabor return (error); 973219019Sgabor} 974219019Sgabor 975219019Sgaborstatic void 976219019Sgaborate_child_detached(device_t dev, device_t child) 977219019Sgabor{ 978219019Sgabor struct ate_softc *sc; 979219019Sgabor 980219019Sgabor sc = device_get_softc(dev); 981219019Sgabor if (child == sc->miibus) 982219019Sgabor sc->miibus = NULL; 983219019Sgabor} 984219019Sgabor 985219019Sgabor/* 986219019Sgabor * MII bus support routines. 987219019Sgabor */ 988219019Sgaborstatic int 989219019Sgaborate_miibus_readreg(device_t dev, int phy, int reg) 990219019Sgabor{ 991219019Sgabor struct ate_softc *sc; 992219019Sgabor int val; 993219019Sgabor 994219019Sgabor /* 995219019Sgabor * XXX if we implement agressive power savings, then we need 996219019Sgabor * XXX to make sure that the clock to the emac is on here 997219019Sgabor */ 998219019Sgabor 999219019Sgabor sc = device_get_softc(dev); 1000219019Sgabor DELAY(1); /* Hangs w/o this delay really 30.5us atm */ 1001219019Sgabor WR4(sc, ETH_MAN, ETH_MAN_REG_RD(phy, reg)); 1002219019Sgabor while ((RD4(sc, ETH_SR) & ETH_SR_IDLE) == 0) 1003219019Sgabor continue; 1004219019Sgabor val = RD4(sc, ETH_MAN) & ETH_MAN_VALUE_MASK; 1005219019Sgabor 1006219019Sgabor return (val); 1007219019Sgabor} 1008219019Sgabor 1009219019Sgaborstatic void 1010219019Sgaborate_miibus_writereg(device_t dev, int phy, int reg, int data) 1011219019Sgabor{ 1012219019Sgabor struct ate_softc *sc; 1013219019Sgabor 1014219019Sgabor /* 1015219019Sgabor * XXX if we implement agressive power savings, then we need 1016219019Sgabor * XXX to make sure that the clock to the emac is on here 1017219019Sgabor */ 1018219019Sgabor 1019219019Sgabor sc = device_get_softc(dev); 1020219019Sgabor WR4(sc, ETH_MAN, ETH_MAN_REG_WR(phy, reg, data)); 1021219019Sgabor while ((RD4(sc, ETH_SR) & ETH_SR_IDLE) == 0) 1022219019Sgabor continue; 1023219019Sgabor return; 1024219019Sgabor} 1025219019Sgabor 1026219019Sgaborstatic device_method_t ate_methods[] = { 1027219019Sgabor /* Device interface */ 1028219019Sgabor DEVMETHOD(device_probe, ate_probe), 1029219019Sgabor DEVMETHOD(device_attach, ate_attach), 1030219019Sgabor DEVMETHOD(device_detach, ate_detach), 1031219019Sgabor 1032219019Sgabor /* Bus interface */ 1033219019Sgabor DEVMETHOD(bus_child_detached, ate_child_detached), 1034219019Sgabor 1035219019Sgabor /* MII interface */ 1036219019Sgabor DEVMETHOD(miibus_readreg, ate_miibus_readreg), 1037219019Sgabor DEVMETHOD(miibus_writereg, ate_miibus_writereg), 1038219019Sgabor 1039219019Sgabor { 0, 0 } 1040219019Sgabor}; 1041219019Sgabor 1042219019Sgaborstatic driver_t ate_driver = { 1043219019Sgabor "ate", 1044219019Sgabor ate_methods, 1045219019Sgabor sizeof(struct ate_softc), 1046219019Sgabor}; 1047219019Sgabor 1048219019SgaborDRIVER_MODULE(ate, atmelarm, ate_driver, ate_devclass, 0, 0); 1049219019SgaborDRIVER_MODULE(miibus, ate, miibus_driver, miibus_devclass, 0, 0); 1050219019SgaborMODULE_DEPEND(ate, miibus, 1, 1, 1); 1051219019SgaborMODULE_DEPEND(ate, ether, 1, 1, 1); 1052219019Sgabor