if_pcn.c revision 110472
166131Swpaul/* 266131Swpaul * Copyright (c) 2000 Berkeley Software Design, Inc. 366131Swpaul * Copyright (c) 1997, 1998, 1999, 2000 466131Swpaul * Bill Paul <wpaul@osd.bsdi.com>. All rights reserved. 566131Swpaul * 666131Swpaul * Redistribution and use in source and binary forms, with or without 766131Swpaul * modification, are permitted provided that the following conditions 866131Swpaul * are met: 966131Swpaul * 1. Redistributions of source code must retain the above copyright 1066131Swpaul * notice, this list of conditions and the following disclaimer. 1166131Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1266131Swpaul * notice, this list of conditions and the following disclaimer in the 1366131Swpaul * documentation and/or other materials provided with the distribution. 1466131Swpaul * 3. All advertising materials mentioning features or use of this software 1566131Swpaul * must display the following acknowledgement: 1666131Swpaul * This product includes software developed by Bill Paul. 1766131Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1866131Swpaul * may be used to endorse or promote products derived from this software 1966131Swpaul * without specific prior written permission. 2066131Swpaul * 2166131Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2266131Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2366131Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2466131Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2566131Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2666131Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2766131Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2866131Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2966131Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3066131Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3166131Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3266131Swpaul * 3366131Swpaul * $FreeBSD: head/sys/pci/if_pcn.c 110472 2003-02-06 21:21:39Z mdodd $ 3466131Swpaul */ 3566131Swpaul 3666131Swpaul/* 3766131Swpaul * AMD Am79c972 fast ethernet PCI NIC driver. Datatheets are available 3866131Swpaul * from http://www.amd.com. 3966131Swpaul * 4066131Swpaul * Written by Bill Paul <wpaul@osd.bsdi.com> 4166131Swpaul */ 4266131Swpaul 4366131Swpaul/* 4466131Swpaul * The AMD PCnet/PCI controllers are more advanced and functional 4566131Swpaul * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain 4666131Swpaul * backwards compatibility with the LANCE and thus can be made 4766131Swpaul * to work with older LANCE drivers. This is in fact how the 4866131Swpaul * PCnet/PCI chips were supported in FreeBSD originally. The trouble 4966131Swpaul * is that the PCnet/PCI devices offer several performance enhancements 5066131Swpaul * which can't be exploited in LANCE compatibility mode. Chief among 5166131Swpaul * these enhancements is the ability to perform PCI DMA operations 5266131Swpaul * using 32-bit addressing (which eliminates the need for ISA 5366131Swpaul * bounce-buffering), and special receive buffer alignment (which 5466131Swpaul * allows the receive handler to pass packets to the upper protocol 5566131Swpaul * layers without copying on both the x86 and alpha platforms). 5666131Swpaul */ 5766131Swpaul 5866131Swpaul#include <sys/param.h> 5966131Swpaul#include <sys/systm.h> 6066131Swpaul#include <sys/sockio.h> 6166131Swpaul#include <sys/mbuf.h> 6266131Swpaul#include <sys/malloc.h> 6366131Swpaul#include <sys/kernel.h> 6466131Swpaul#include <sys/socket.h> 6566131Swpaul 6666131Swpaul#include <net/if.h> 6766131Swpaul#include <net/if_arp.h> 6866131Swpaul#include <net/ethernet.h> 6966131Swpaul#include <net/if_dl.h> 7066131Swpaul#include <net/if_media.h> 7166131Swpaul 7266131Swpaul#include <net/bpf.h> 7366131Swpaul 7466131Swpaul#include <vm/vm.h> /* for vtophys */ 7566131Swpaul#include <vm/pmap.h> /* for vtophys */ 7666131Swpaul#include <machine/bus_pio.h> 7766131Swpaul#include <machine/bus_memio.h> 7866131Swpaul#include <machine/bus.h> 7966131Swpaul#include <machine/resource.h> 8066131Swpaul#include <sys/bus.h> 8166131Swpaul#include <sys/rman.h> 8266131Swpaul 8366131Swpaul#include <dev/mii/mii.h> 8466131Swpaul#include <dev/mii/miivar.h> 8566131Swpaul 8666131Swpaul#include <pci/pcireg.h> 8766131Swpaul#include <pci/pcivar.h> 8866131Swpaul 8966131Swpaul#define PCN_USEIOSPACE 9066131Swpaul 9166131Swpaul#include <pci/if_pcnreg.h> 9266131Swpaul 9366131SwpaulMODULE_DEPEND(pcn, miibus, 1, 1, 1); 9466131Swpaul 9566131Swpaul/* "controller miibus0" required. See GENERIC if you get errors here. */ 9666131Swpaul#include "miibus_if.h" 9766131Swpaul 9866131Swpaul#ifndef lint 9966131Swpaulstatic const char rcsid[] = 10066131Swpaul "$FreeBSD: head/sys/pci/if_pcn.c 110472 2003-02-06 21:21:39Z mdodd $"; 10166131Swpaul#endif 10266131Swpaul 10366131Swpaul/* 10466131Swpaul * Various supported device vendors/types and their names. 10566131Swpaul */ 10666131Swpaulstatic struct pcn_type pcn_devs[] = { 10766131Swpaul { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, 10866131Swpaul { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, 10966131Swpaul { 0, 0, NULL } 11066131Swpaul}; 11166131Swpaul 11292739Salfredstatic u_int32_t pcn_csr_read (struct pcn_softc *, int); 11392739Salfredstatic u_int16_t pcn_csr_read16 (struct pcn_softc *, int); 11492739Salfredstatic u_int16_t pcn_bcr_read16 (struct pcn_softc *, int); 11592739Salfredstatic void pcn_csr_write (struct pcn_softc *, int, int); 11692739Salfredstatic u_int32_t pcn_bcr_read (struct pcn_softc *, int); 11792739Salfredstatic void pcn_bcr_write (struct pcn_softc *, int, int); 11866131Swpaul 11992739Salfredstatic int pcn_probe (device_t); 12092739Salfredstatic int pcn_attach (device_t); 12192739Salfredstatic int pcn_detach (device_t); 12266131Swpaul 12392739Salfredstatic int pcn_newbuf (struct pcn_softc *, int, struct mbuf *); 12492739Salfredstatic int pcn_encap (struct pcn_softc *, 12592739Salfred struct mbuf *, u_int32_t *); 12692739Salfredstatic void pcn_rxeof (struct pcn_softc *); 12792739Salfredstatic void pcn_txeof (struct pcn_softc *); 12892739Salfredstatic void pcn_intr (void *); 12992739Salfredstatic void pcn_tick (void *); 13092739Salfredstatic void pcn_start (struct ifnet *); 13192739Salfredstatic int pcn_ioctl (struct ifnet *, u_long, caddr_t); 13292739Salfredstatic void pcn_init (void *); 13392739Salfredstatic void pcn_stop (struct pcn_softc *); 13492739Salfredstatic void pcn_watchdog (struct ifnet *); 13592739Salfredstatic void pcn_shutdown (device_t); 13692739Salfredstatic int pcn_ifmedia_upd (struct ifnet *); 13792739Salfredstatic void pcn_ifmedia_sts (struct ifnet *, struct ifmediareq *); 13866131Swpaul 13992739Salfredstatic int pcn_miibus_readreg (device_t, int, int); 14092739Salfredstatic int pcn_miibus_writereg (device_t, int, int, int); 14192739Salfredstatic void pcn_miibus_statchg (device_t); 14266131Swpaul 14392739Salfredstatic void pcn_setfilt (struct ifnet *); 14492739Salfredstatic void pcn_setmulti (struct pcn_softc *); 14592739Salfredstatic u_int32_t pcn_crc (caddr_t); 14692739Salfredstatic void pcn_reset (struct pcn_softc *); 14792739Salfredstatic int pcn_list_rx_init (struct pcn_softc *); 14892739Salfredstatic int pcn_list_tx_init (struct pcn_softc *); 14966131Swpaul 15066131Swpaul#ifdef PCN_USEIOSPACE 15166131Swpaul#define PCN_RES SYS_RES_IOPORT 15266131Swpaul#define PCN_RID PCN_PCI_LOIO 15366131Swpaul#else 15466131Swpaul#define PCN_RES SYS_RES_MEMORY 15566131Swpaul#define PCN_RID PCN_PCI_LOMEM 15666131Swpaul#endif 15766131Swpaul 15866131Swpaulstatic device_method_t pcn_methods[] = { 15966131Swpaul /* Device interface */ 16066131Swpaul DEVMETHOD(device_probe, pcn_probe), 16166131Swpaul DEVMETHOD(device_attach, pcn_attach), 16266131Swpaul DEVMETHOD(device_detach, pcn_detach), 16366131Swpaul DEVMETHOD(device_shutdown, pcn_shutdown), 16466131Swpaul 16566131Swpaul /* bus interface */ 16666131Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 16766131Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 16866131Swpaul 16966131Swpaul /* MII interface */ 17066131Swpaul DEVMETHOD(miibus_readreg, pcn_miibus_readreg), 17166131Swpaul DEVMETHOD(miibus_writereg, pcn_miibus_writereg), 17266131Swpaul DEVMETHOD(miibus_statchg, pcn_miibus_statchg), 17366131Swpaul 17466131Swpaul { 0, 0 } 17566131Swpaul}; 17666131Swpaul 17766131Swpaulstatic driver_t pcn_driver = { 17866131Swpaul "pcn", 17966131Swpaul pcn_methods, 18066131Swpaul sizeof(struct pcn_softc) 18166131Swpaul}; 18266131Swpaul 18366131Swpaulstatic devclass_t pcn_devclass; 18466131Swpaul 18566131SwpaulDRIVER_MODULE(if_pcn, pci, pcn_driver, pcn_devclass, 0, 0); 18666131SwpaulDRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); 18766131Swpaul 18866131Swpaul#define PCN_CSR_SETBIT(sc, reg, x) \ 18966131Swpaul pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) 19066131Swpaul 19166131Swpaul#define PCN_CSR_CLRBIT(sc, reg, x) \ 19266131Swpaul pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) 19366131Swpaul 19466131Swpaul#define PCN_BCR_SETBIT(sc, reg, x) \ 19566131Swpaul pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) 19666131Swpaul 19766131Swpaul#define PCN_BCR_CLRBIT(sc, reg, x) \ 19866131Swpaul pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) 19966131Swpaul 200102335Salfredstatic u_int32_t 201102335Salfredpcn_csr_read(sc, reg) 20266131Swpaul struct pcn_softc *sc; 20366131Swpaul int reg; 20466131Swpaul{ 20566131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 20666131Swpaul return(CSR_READ_4(sc, PCN_IO32_RDP)); 20766131Swpaul} 20866131Swpaul 209102335Salfredstatic u_int16_t 210102335Salfredpcn_csr_read16(sc, reg) 21168837Swpaul struct pcn_softc *sc; 21268837Swpaul int reg; 21368837Swpaul{ 21468837Swpaul CSR_WRITE_2(sc, PCN_IO16_RAP, reg); 21568837Swpaul return(CSR_READ_2(sc, PCN_IO16_RDP)); 21668837Swpaul} 21768837Swpaul 218102335Salfredstatic void 219102335Salfredpcn_csr_write(sc, reg, val) 22066131Swpaul struct pcn_softc *sc; 22166131Swpaul int reg; 22266131Swpaul{ 22366131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 22466131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, val); 22566131Swpaul return; 22666131Swpaul} 22766131Swpaul 228102335Salfredstatic u_int32_t 229102335Salfredpcn_bcr_read(sc, reg) 23066131Swpaul struct pcn_softc *sc; 23166131Swpaul int reg; 23266131Swpaul{ 23366131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 23466131Swpaul return(CSR_READ_4(sc, PCN_IO32_BDP)); 23566131Swpaul} 23666131Swpaul 237102335Salfredstatic u_int16_t 238102335Salfredpcn_bcr_read16(sc, reg) 23969067Swpaul struct pcn_softc *sc; 24069067Swpaul int reg; 24169067Swpaul{ 24269067Swpaul CSR_WRITE_2(sc, PCN_IO16_RAP, reg); 24369067Swpaul return(CSR_READ_2(sc, PCN_IO16_BDP)); 24469067Swpaul} 24569067Swpaul 246102335Salfredstatic void 247102335Salfredpcn_bcr_write(sc, reg, val) 24866131Swpaul struct pcn_softc *sc; 24966131Swpaul int reg; 25066131Swpaul{ 25166131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 25266131Swpaul CSR_WRITE_4(sc, PCN_IO32_BDP, val); 25366131Swpaul return; 25466131Swpaul} 25566131Swpaul 256102335Salfredstatic int 257102335Salfredpcn_miibus_readreg(dev, phy, reg) 25866131Swpaul device_t dev; 25966131Swpaul int phy, reg; 26066131Swpaul{ 26166131Swpaul struct pcn_softc *sc; 26266131Swpaul int val; 26366131Swpaul 26466131Swpaul sc = device_get_softc(dev); 26566131Swpaul 26666208Swpaul if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr) 26766131Swpaul return(0); 26866131Swpaul 26966131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 27066131Swpaul val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; 27166131Swpaul if (val == 0xFFFF) 27266131Swpaul return(0); 27366208Swpaul 27466208Swpaul sc->pcn_phyaddr = phy; 27566208Swpaul 27666131Swpaul return(val); 27766131Swpaul} 27866131Swpaul 279102335Salfredstatic int 280102335Salfredpcn_miibus_writereg(dev, phy, reg, data) 28166131Swpaul device_t dev; 28266131Swpaul int phy, reg, data; 28366131Swpaul{ 28466131Swpaul struct pcn_softc *sc; 28566131Swpaul 28666131Swpaul sc = device_get_softc(dev); 28766131Swpaul 28866131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 28966131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); 29066131Swpaul 29166131Swpaul return(0); 29266131Swpaul} 29366131Swpaul 294102335Salfredstatic void 295102335Salfredpcn_miibus_statchg(dev) 29666131Swpaul device_t dev; 29766131Swpaul{ 29866131Swpaul struct pcn_softc *sc; 29966131Swpaul struct mii_data *mii; 30066131Swpaul 30166131Swpaul sc = device_get_softc(dev); 30266131Swpaul mii = device_get_softc(sc->pcn_miibus); 30366131Swpaul 30466131Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 30566131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 30666131Swpaul } else { 30766131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 30866131Swpaul } 30966131Swpaul 31066131Swpaul return; 31166131Swpaul} 31266131Swpaul 31366131Swpaul#define DC_POLY 0xEDB88320 31466131Swpaul 315102335Salfredstatic u_int32_t 316102335Salfredpcn_crc(addr) 31766131Swpaul caddr_t addr; 31866131Swpaul{ 31966131Swpaul u_int32_t idx, bit, data, crc; 32066131Swpaul 32166131Swpaul /* Compute CRC for the address value. */ 32266131Swpaul crc = 0xFFFFFFFF; /* initial value */ 32366131Swpaul 32466131Swpaul for (idx = 0; idx < 6; idx++) { 32566131Swpaul for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) 32666131Swpaul crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); 32766131Swpaul } 32866131Swpaul 32966131Swpaul return ((crc >> 26) & 0x3F); 33066131Swpaul} 33166131Swpaul 332102335Salfredstatic void 333102335Salfredpcn_setmulti(sc) 33466131Swpaul struct pcn_softc *sc; 33566131Swpaul{ 33666131Swpaul struct ifnet *ifp; 33766131Swpaul struct ifmultiaddr *ifma; 33866131Swpaul u_int32_t h, i; 33966131Swpaul u_int16_t hashes[4] = { 0, 0, 0, 0 }; 34066131Swpaul 34166131Swpaul ifp = &sc->arpcom.ac_if; 34266131Swpaul 34366131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 34466131Swpaul 34566131Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 34666131Swpaul for (i = 0; i < 4; i++) 34766131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); 34866131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 34966131Swpaul return; 35066131Swpaul } 35166131Swpaul 35266131Swpaul /* first, zot all the existing hash bits */ 35366131Swpaul for (i = 0; i < 4; i++) 35466131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); 35566131Swpaul 35666131Swpaul /* now program new ones */ 35772084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 35866131Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 35966131Swpaul continue; 36066131Swpaul h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 36166131Swpaul hashes[h >> 4] |= 1 << (h & 0xF); 36266131Swpaul } 36366131Swpaul 36466131Swpaul for (i = 0; i < 4; i++) 36566131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); 36666131Swpaul 36766131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 36866131Swpaul 36966131Swpaul return; 37066131Swpaul} 37166131Swpaul 372102335Salfredstatic void 373102335Salfredpcn_reset(sc) 37466131Swpaul struct pcn_softc *sc; 37566131Swpaul{ 37666131Swpaul /* 37766131Swpaul * Issue a reset by reading from the RESET register. 37866131Swpaul * Note that we don't know if the chip is operating in 37966131Swpaul * 16-bit or 32-bit mode at this point, so we attempt 38066131Swpaul * to reset the chip both ways. If one fails, the other 38166131Swpaul * will succeed. 38266131Swpaul */ 38366131Swpaul CSR_READ_2(sc, PCN_IO16_RESET); 38466131Swpaul CSR_READ_4(sc, PCN_IO32_RESET); 38566131Swpaul 38666131Swpaul /* Wait a little while for the chip to get its brains in order. */ 38766131Swpaul DELAY(1000); 38866131Swpaul 38966131Swpaul /* Select 32-bit (DWIO) mode */ 39066131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, 0); 39166131Swpaul 39266131Swpaul /* Select software style 3. */ 39366131Swpaul pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); 39466131Swpaul 39566131Swpaul return; 39666131Swpaul} 39766131Swpaul 39866131Swpaul/* 39966131Swpaul * Probe for an AMD chip. Check the PCI vendor and device 40066131Swpaul * IDs against our list and return a device name if we find a match. 40166131Swpaul */ 402102335Salfredstatic int 403102335Salfredpcn_probe(dev) 40466131Swpaul device_t dev; 40566131Swpaul{ 40666131Swpaul struct pcn_type *t; 40766131Swpaul struct pcn_softc *sc; 40866131Swpaul int rid; 40966131Swpaul u_int32_t chip_id; 41066131Swpaul 41166131Swpaul t = pcn_devs; 41266131Swpaul sc = device_get_softc(dev); 41366131Swpaul 41466131Swpaul while(t->pcn_name != NULL) { 41566131Swpaul if ((pci_get_vendor(dev) == t->pcn_vid) && 41666131Swpaul (pci_get_device(dev) == t->pcn_did)) { 41766131Swpaul /* 41866131Swpaul * Temporarily map the I/O space 41966131Swpaul * so we can read the chip ID register. 42066131Swpaul */ 42166131Swpaul rid = PCN_RID; 42266131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 42366131Swpaul 0, ~0, 1, RF_ACTIVE); 42466131Swpaul if (sc->pcn_res == NULL) { 42566131Swpaul device_printf(dev, 42666131Swpaul "couldn't map ports/memory\n"); 42766131Swpaul return(ENXIO); 42866131Swpaul } 42966131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 43066131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 43167089Swpaul mtx_init(&sc->pcn_mtx, 43293818Sjhb device_get_nameunit(dev), MTX_NETWORK_LOCK, 43393818Sjhb MTX_DEF); 43467087Swpaul PCN_LOCK(sc); 43568837Swpaul /* 43668837Swpaul * Note: we can *NOT* put the chip into 43768837Swpaul * 32-bit mode yet. The lnc driver will only 43868837Swpaul * work in 16-bit mode, and once the chip 43968837Swpaul * goes into 32-bit mode, the only way to 44068837Swpaul * get it out again is with a hardware reset. 44168837Swpaul * So if pcn_probe() is called before the 44268837Swpaul * lnc driver's probe routine, the chip will 44368837Swpaul * be locked into 32-bit operation and the lnc 44468837Swpaul * driver will be unable to attach to it. 44569067Swpaul * Note II: if the chip happens to already 44669067Swpaul * be in 32-bit mode, we still need to check 44769067Swpaul * the chip ID, but first we have to detect 44869067Swpaul * 32-bit mode using only 16-bit operations. 44969067Swpaul * The safest way to do this is to read the 45069067Swpaul * PCI subsystem ID from BCR23/24 and compare 45169067Swpaul * that with the value read from PCI config 45269067Swpaul * space. 45368837Swpaul */ 45469067Swpaul chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); 45566131Swpaul chip_id <<= 16; 45669067Swpaul chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); 45773111Swpaul /* 45873111Swpaul * Note III: the test for 0x10001000 is a hack to 45973111Swpaul * pacify VMware, who's pseudo-PCnet interface is 46073111Swpaul * broken. Reading the subsystem register from PCI 46173111Swpaul * config space yeilds 0x00000000 while reading the 46273111Swpaul * same value from I/O space yeilds 0x10001000. It's 46373111Swpaul * not supposed to be that way. 46473111Swpaul */ 46569067Swpaul if (chip_id == pci_read_config(dev, 46673111Swpaul PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { 46769067Swpaul /* We're in 16-bit mode. */ 46869067Swpaul chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); 46969067Swpaul chip_id <<= 16; 47069067Swpaul chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); 47169067Swpaul } else { 47269067Swpaul /* We're in 32-bit mode. */ 47369067Swpaul chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); 47469067Swpaul chip_id <<= 16; 47569067Swpaul chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); 47669067Swpaul } 47766131Swpaul bus_release_resource(dev, PCN_RES, 47866131Swpaul PCN_RID, sc->pcn_res); 47967087Swpaul PCN_UNLOCK(sc); 48067087Swpaul mtx_destroy(&sc->pcn_mtx); 48166131Swpaul chip_id >>= 12; 48266131Swpaul sc->pcn_type = chip_id & PART_MASK; 48366131Swpaul switch(sc->pcn_type) { 48466131Swpaul case Am79C971: 48566131Swpaul case Am79C972: 48666131Swpaul case Am79C973: 48766690Swpaul case Am79C975: 48866592Swpaul case Am79C976: 48966131Swpaul case Am79C978: 49066131Swpaul break; 49166131Swpaul default: 49266131Swpaul return(ENXIO); 49366131Swpaul break; 49466131Swpaul } 49566131Swpaul device_set_desc(dev, t->pcn_name); 49666131Swpaul return(0); 49766131Swpaul } 49866131Swpaul t++; 49966131Swpaul } 50066131Swpaul 50166131Swpaul return(ENXIO); 50266131Swpaul} 50366131Swpaul 50466131Swpaul/* 50566131Swpaul * Attach the interface. Allocate softc structures, do ifmedia 50666131Swpaul * setup and ethernet/BPF attach. 50766131Swpaul */ 508102335Salfredstatic int 509102335Salfredpcn_attach(dev) 51066131Swpaul device_t dev; 51166131Swpaul{ 51266131Swpaul u_int32_t eaddr[2]; 51366131Swpaul u_int32_t command; 51466131Swpaul struct pcn_softc *sc; 51566131Swpaul struct ifnet *ifp; 51666131Swpaul int unit, error = 0, rid; 51766131Swpaul 51866131Swpaul sc = device_get_softc(dev); 51966131Swpaul unit = device_get_unit(dev); 52066131Swpaul 52169583Swpaul /* Initialize our mutex. */ 52293818Sjhb mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 52393818Sjhb MTX_DEF | MTX_RECURSE); 52469583Swpaul PCN_LOCK(sc); 52569583Swpaul 52666131Swpaul /* 52766131Swpaul * Handle power management nonsense. 52866131Swpaul */ 52972813Swpaul if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 53072813Swpaul u_int32_t iobase, membase, irq; 53166131Swpaul 53272813Swpaul /* Save important PCI config data. */ 53372813Swpaul iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); 53472813Swpaul membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); 53572813Swpaul irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); 53666131Swpaul 53772813Swpaul /* Reset the power state. */ 53872813Swpaul printf("pcn%d: chip is in D%d power mode " 53972813Swpaul "-- setting to D0\n", unit, 54072813Swpaul pci_get_powerstate(dev)); 54172813Swpaul pci_set_powerstate(dev, PCI_POWERSTATE_D0); 54266131Swpaul 54372813Swpaul /* Restore PCI config data. */ 54472813Swpaul pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); 54572813Swpaul pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); 54672813Swpaul pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); 54766131Swpaul } 54866131Swpaul 54966131Swpaul /* 55066131Swpaul * Map control/status registers. 55166131Swpaul */ 55272813Swpaul pci_enable_busmaster(dev); 55379472Swpaul pci_enable_io(dev, SYS_RES_IOPORT); 55479472Swpaul pci_enable_io(dev, SYS_RES_MEMORY); 55566131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 55666131Swpaul 55766131Swpaul#ifdef PCN_USEIOSPACE 55866131Swpaul if (!(command & PCIM_CMD_PORTEN)) { 55966131Swpaul printf("pcn%d: failed to enable I/O ports!\n", unit); 56066131Swpaul error = ENXIO;; 56166131Swpaul goto fail; 56266131Swpaul } 56366131Swpaul#else 56466131Swpaul if (!(command & PCIM_CMD_MEMEN)) { 56566131Swpaul printf("pcn%d: failed to enable memory mapping!\n", unit); 56666131Swpaul error = ENXIO;; 56766131Swpaul goto fail; 56866131Swpaul } 56966131Swpaul#endif 57066131Swpaul 57166131Swpaul rid = PCN_RID; 57266131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 57366131Swpaul 0, ~0, 1, RF_ACTIVE); 57466131Swpaul 57566131Swpaul if (sc->pcn_res == NULL) { 57666131Swpaul printf("pcn%d: couldn't map ports/memory\n", unit); 57766131Swpaul error = ENXIO; 57866131Swpaul goto fail; 57966131Swpaul } 58066131Swpaul 58166131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 58266131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 58366131Swpaul 58466131Swpaul /* Allocate interrupt */ 58566131Swpaul rid = 0; 58666131Swpaul sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 58766131Swpaul RF_SHAREABLE | RF_ACTIVE); 58866131Swpaul 58966131Swpaul if (sc->pcn_irq == NULL) { 59066131Swpaul printf("pcn%d: couldn't map interrupt\n", unit); 59166131Swpaul error = ENXIO; 59266131Swpaul goto fail; 59366131Swpaul } 59466131Swpaul 59566131Swpaul error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, 59666131Swpaul pcn_intr, sc, &sc->pcn_intrhand); 59766131Swpaul 59866131Swpaul if (error) { 59966131Swpaul printf("pcn%d: couldn't set up irq\n", unit); 60066131Swpaul goto fail; 60166131Swpaul } 60266131Swpaul 60366131Swpaul /* Reset the adapter. */ 60466131Swpaul pcn_reset(sc); 60566131Swpaul 60666131Swpaul /* 60766131Swpaul * Get station address from the EEPROM. 60866131Swpaul */ 60966131Swpaul eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); 61066131Swpaul eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); 61166131Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 61266131Swpaul 61366131Swpaul /* 61466131Swpaul * An AMD chip was detected. Inform the world. 61566131Swpaul */ 61666131Swpaul printf("pcn%d: Ethernet address: %6D\n", unit, 61766131Swpaul sc->arpcom.ac_enaddr, ":"); 61866131Swpaul 61966131Swpaul sc->pcn_unit = unit; 62066131Swpaul callout_handle_init(&sc->pcn_stat_ch); 62166131Swpaul 62266131Swpaul sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, 62366131Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 62466131Swpaul 62566131Swpaul if (sc->pcn_ldata == NULL) { 62666131Swpaul printf("pcn%d: no memory for list buffers!\n", unit); 62766131Swpaul error = ENXIO; 62866131Swpaul goto fail; 62966131Swpaul } 63066131Swpaul bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); 63166131Swpaul 63266131Swpaul ifp = &sc->arpcom.ac_if; 63366131Swpaul ifp->if_softc = sc; 63466131Swpaul ifp->if_unit = unit; 63566131Swpaul ifp->if_name = "pcn"; 63666131Swpaul ifp->if_mtu = ETHERMTU; 63766131Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 63866131Swpaul ifp->if_ioctl = pcn_ioctl; 63966131Swpaul ifp->if_output = ether_output; 64066131Swpaul ifp->if_start = pcn_start; 64166131Swpaul ifp->if_watchdog = pcn_watchdog; 64266131Swpaul ifp->if_init = pcn_init; 64366131Swpaul ifp->if_baudrate = 10000000; 64466131Swpaul ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; 64566131Swpaul 64666131Swpaul /* 64766131Swpaul * Do MII setup. 64866131Swpaul */ 64966131Swpaul if (mii_phy_probe(dev, &sc->pcn_miibus, 65066131Swpaul pcn_ifmedia_upd, pcn_ifmedia_sts)) { 65166131Swpaul printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); 65266131Swpaul error = ENXIO; 65366131Swpaul goto fail; 65466131Swpaul } 65566131Swpaul 65666131Swpaul /* 65766131Swpaul * Call MI attach routine. 65866131Swpaul */ 659106936Ssam ether_ifattach(ifp, (u_int8_t *) eaddr); 66066131Swpaul callout_handle_init(&sc->pcn_stat_ch); 66167087Swpaul PCN_UNLOCK(sc); 66267087Swpaul return(0); 66366131Swpaul 66466131Swpaulfail: 66567087Swpaul PCN_UNLOCK(sc); 666110472Smdodd 667110472Smdodd if (sc->pcn_intrhand) 668110472Smdodd bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 669110472Smdodd if (sc->pcn_irq) 670110472Smdodd bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 671110472Smdodd if (sc->pcn_res) 672110472Smdodd bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 673110472Smdodd 67467087Swpaul mtx_destroy(&sc->pcn_mtx); 67567087Swpaul 67666131Swpaul return(error); 67766131Swpaul} 67866131Swpaul 679102335Salfredstatic int 680102335Salfredpcn_detach(dev) 68166131Swpaul device_t dev; 68266131Swpaul{ 68366131Swpaul struct pcn_softc *sc; 68466131Swpaul struct ifnet *ifp; 68566131Swpaul 68666131Swpaul sc = device_get_softc(dev); 68766131Swpaul ifp = &sc->arpcom.ac_if; 68866131Swpaul 68967087Swpaul PCN_LOCK(sc); 69067087Swpaul 69166131Swpaul pcn_reset(sc); 69266131Swpaul pcn_stop(sc); 693106936Ssam ether_ifdetach(ifp); 69466131Swpaul 69566131Swpaul if (sc->pcn_miibus != NULL) { 69666131Swpaul bus_generic_detach(dev); 69766131Swpaul device_delete_child(dev, sc->pcn_miibus); 69866131Swpaul } 69966131Swpaul 70066131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 70166131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 70266131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 70366131Swpaul 70466131Swpaul contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); 70567087Swpaul PCN_UNLOCK(sc); 70666131Swpaul 70767087Swpaul mtx_destroy(&sc->pcn_mtx); 70866131Swpaul 70966131Swpaul return(0); 71066131Swpaul} 71166131Swpaul 71266131Swpaul/* 71366131Swpaul * Initialize the transmit descriptors. 71466131Swpaul */ 715102335Salfredstatic int 716102335Salfredpcn_list_tx_init(sc) 71766131Swpaul struct pcn_softc *sc; 71866131Swpaul{ 71966131Swpaul struct pcn_list_data *ld; 72066131Swpaul struct pcn_ring_data *cd; 72166131Swpaul int i; 72266131Swpaul 72366131Swpaul cd = &sc->pcn_cdata; 72466131Swpaul ld = sc->pcn_ldata; 72566131Swpaul 72666131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 72766131Swpaul cd->pcn_tx_chain[i] = NULL; 72866131Swpaul ld->pcn_tx_list[i].pcn_tbaddr = 0; 72966131Swpaul ld->pcn_tx_list[i].pcn_txctl = 0; 73066131Swpaul ld->pcn_tx_list[i].pcn_txstat = 0; 73166131Swpaul } 73266131Swpaul 73366131Swpaul cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; 73466131Swpaul 73566131Swpaul return(0); 73666131Swpaul} 73766131Swpaul 73866131Swpaul 73966131Swpaul/* 74066131Swpaul * Initialize the RX descriptors and allocate mbufs for them. 74166131Swpaul */ 742102335Salfredstatic int 743102335Salfredpcn_list_rx_init(sc) 74466131Swpaul struct pcn_softc *sc; 74566131Swpaul{ 74666131Swpaul struct pcn_list_data *ld; 74766131Swpaul struct pcn_ring_data *cd; 74866131Swpaul int i; 74966131Swpaul 75066131Swpaul ld = sc->pcn_ldata; 75166131Swpaul cd = &sc->pcn_cdata; 75266131Swpaul 75366131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 75466131Swpaul if (pcn_newbuf(sc, i, NULL) == ENOBUFS) 75566131Swpaul return(ENOBUFS); 75666131Swpaul } 75766131Swpaul 75866131Swpaul cd->pcn_rx_prod = 0; 75966131Swpaul 76066131Swpaul return(0); 76166131Swpaul} 76266131Swpaul 76366131Swpaul/* 76466131Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 76566131Swpaul */ 766102335Salfredstatic int 767102335Salfredpcn_newbuf(sc, idx, m) 76866131Swpaul struct pcn_softc *sc; 76966131Swpaul int idx; 77066131Swpaul struct mbuf *m; 77166131Swpaul{ 77266131Swpaul struct mbuf *m_new = NULL; 77366131Swpaul struct pcn_rx_desc *c; 77466131Swpaul 77566131Swpaul c = &sc->pcn_ldata->pcn_rx_list[idx]; 77666131Swpaul 77766131Swpaul if (m == NULL) { 778109623Salfred MGETHDR(m_new, M_NOWAIT, MT_DATA); 77987846Sluigi if (m_new == NULL) 78066131Swpaul return(ENOBUFS); 78166131Swpaul 782109623Salfred MCLGET(m_new, M_NOWAIT); 78366131Swpaul if (!(m_new->m_flags & M_EXT)) { 78466131Swpaul m_freem(m_new); 78566131Swpaul return(ENOBUFS); 78666131Swpaul } 78766131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 78866131Swpaul } else { 78966131Swpaul m_new = m; 79066131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 79166131Swpaul m_new->m_data = m_new->m_ext.ext_buf; 79266131Swpaul } 79366131Swpaul 79466131Swpaul m_adj(m_new, ETHER_ALIGN); 79566131Swpaul 79666131Swpaul sc->pcn_cdata.pcn_rx_chain[idx] = m_new; 79766131Swpaul c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); 79866131Swpaul c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; 79966131Swpaul c->pcn_bufsz |= PCN_RXLEN_MBO; 80066131Swpaul c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; 80166131Swpaul 80266131Swpaul return(0); 80366131Swpaul} 80466131Swpaul 80566131Swpaul/* 80666131Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 80766131Swpaul * the higher level protocols. 80866131Swpaul */ 809102335Salfredstatic void 810102335Salfredpcn_rxeof(sc) 81166131Swpaul struct pcn_softc *sc; 81266131Swpaul{ 81366131Swpaul struct ether_header *eh; 81466131Swpaul struct mbuf *m; 81566131Swpaul struct ifnet *ifp; 81666131Swpaul struct pcn_rx_desc *cur_rx; 81766131Swpaul int i; 81866131Swpaul 81966131Swpaul ifp = &sc->arpcom.ac_if; 82066131Swpaul i = sc->pcn_cdata.pcn_rx_prod; 82166131Swpaul 82266131Swpaul while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { 82366131Swpaul cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; 82466131Swpaul m = sc->pcn_cdata.pcn_rx_chain[i]; 82566131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 82666131Swpaul 82766131Swpaul /* 82866131Swpaul * If an error occurs, update stats, clear the 82966131Swpaul * status word and leave the mbuf cluster in place: 83066131Swpaul * it should simply get re-used next time this descriptor 83166131Swpaul * comes up in the ring. 83266131Swpaul */ 83366131Swpaul if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { 83466131Swpaul ifp->if_ierrors++; 83566131Swpaul pcn_newbuf(sc, i, m); 83666131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 83766131Swpaul continue; 83866131Swpaul } 83966131Swpaul 84066592Swpaul if (pcn_newbuf(sc, i, NULL)) { 84166592Swpaul /* Ran out of mbufs; recycle this one. */ 84266592Swpaul pcn_newbuf(sc, i, m); 84366592Swpaul ifp->if_ierrors++; 84466592Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 84566592Swpaul continue; 84666592Swpaul } 84766592Swpaul 84866131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 84966131Swpaul 85066131Swpaul /* No errors; receive the packet. */ 85166131Swpaul ifp->if_ipackets++; 85266131Swpaul eh = mtod(m, struct ether_header *); 85366131Swpaul m->m_len = m->m_pkthdr.len = 85466131Swpaul cur_rx->pcn_rxlen - ETHER_CRC_LEN; 85566131Swpaul m->m_pkthdr.rcvif = ifp; 85666131Swpaul 857106936Ssam (*ifp->if_input)(ifp, m); 85866131Swpaul } 85966131Swpaul 86066131Swpaul sc->pcn_cdata.pcn_rx_prod = i; 86166131Swpaul 86266131Swpaul return; 86366131Swpaul} 86466131Swpaul 86566131Swpaul/* 86666131Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 86766131Swpaul * the list buffers. 86866131Swpaul */ 86966131Swpaul 870102335Salfredstatic void 871102335Salfredpcn_txeof(sc) 87266131Swpaul struct pcn_softc *sc; 87366131Swpaul{ 87466131Swpaul struct pcn_tx_desc *cur_tx = NULL; 87566131Swpaul struct ifnet *ifp; 87666131Swpaul u_int32_t idx; 87766131Swpaul 87866131Swpaul ifp = &sc->arpcom.ac_if; 87966131Swpaul 88066131Swpaul /* 88166131Swpaul * Go through our tx list and free mbufs for those 88266131Swpaul * frames that have been transmitted. 88366131Swpaul */ 88466131Swpaul idx = sc->pcn_cdata.pcn_tx_cons; 88566131Swpaul while (idx != sc->pcn_cdata.pcn_tx_prod) { 88666131Swpaul cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; 88766131Swpaul 88866131Swpaul if (!PCN_OWN_TXDESC(cur_tx)) 88966131Swpaul break; 89066131Swpaul 89166131Swpaul if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { 89266131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 89366131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 89466131Swpaul continue; 89566131Swpaul } 89666131Swpaul 89766131Swpaul if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { 89866131Swpaul ifp->if_oerrors++; 89966131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) 90066131Swpaul ifp->if_collisions++; 90166131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) 90266131Swpaul ifp->if_collisions++; 90366131Swpaul } 90466131Swpaul 90566131Swpaul ifp->if_collisions += 90666131Swpaul cur_tx->pcn_txstat & PCN_TXSTAT_TRC; 90766131Swpaul 90866131Swpaul ifp->if_opackets++; 90966131Swpaul if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { 91066131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); 91166131Swpaul sc->pcn_cdata.pcn_tx_chain[idx] = NULL; 91266131Swpaul } 91366131Swpaul 91466131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 91566131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 91666131Swpaul } 91766131Swpaul 91899165Sluigi if (idx != sc->pcn_cdata.pcn_tx_cons) { 91999165Sluigi /* Some buffers have been freed. */ 92099165Sluigi sc->pcn_cdata.pcn_tx_cons = idx; 92166131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 92299165Sluigi } 92399165Sluigi ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5; 92466131Swpaul 92566131Swpaul return; 92666131Swpaul} 92766131Swpaul 928102335Salfredstatic void 929102335Salfredpcn_tick(xsc) 93066131Swpaul void *xsc; 93166131Swpaul{ 93266131Swpaul struct pcn_softc *sc; 93366131Swpaul struct mii_data *mii; 93466131Swpaul struct ifnet *ifp; 93566131Swpaul 93666131Swpaul sc = xsc; 93766131Swpaul ifp = &sc->arpcom.ac_if; 93867087Swpaul PCN_LOCK(sc); 93966131Swpaul 94066131Swpaul mii = device_get_softc(sc->pcn_miibus); 94166131Swpaul mii_tick(mii); 94266131Swpaul 94384147Sjlemon /* link just died */ 94466131Swpaul if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) 94566131Swpaul sc->pcn_link = 0; 94666131Swpaul 94784147Sjlemon /* link just came up, restart */ 94884147Sjlemon if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && 94984147Sjlemon IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 95084147Sjlemon sc->pcn_link++; 95184147Sjlemon if (ifp->if_snd.ifq_head != NULL) 95284147Sjlemon pcn_start(ifp); 95366131Swpaul } 95466131Swpaul 95566131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 95666131Swpaul 95767087Swpaul PCN_UNLOCK(sc); 95866131Swpaul 95966131Swpaul return; 96066131Swpaul} 96166131Swpaul 962102335Salfredstatic void 963102335Salfredpcn_intr(arg) 96466131Swpaul void *arg; 96566131Swpaul{ 96666131Swpaul struct pcn_softc *sc; 96766131Swpaul struct ifnet *ifp; 96866131Swpaul u_int32_t status; 96966131Swpaul 97066131Swpaul sc = arg; 97166131Swpaul ifp = &sc->arpcom.ac_if; 97266131Swpaul 97366131Swpaul /* Supress unwanted interrupts */ 97466131Swpaul if (!(ifp->if_flags & IFF_UP)) { 97566131Swpaul pcn_stop(sc); 97666131Swpaul return; 97766131Swpaul } 97866131Swpaul 97966131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); 98066131Swpaul 98166131Swpaul while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { 98266131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, status); 98366131Swpaul 98466131Swpaul if (status & PCN_CSR_RINT) 98566131Swpaul pcn_rxeof(sc); 98666131Swpaul 98766131Swpaul if (status & PCN_CSR_TINT) 98866131Swpaul pcn_txeof(sc); 98966131Swpaul 99066131Swpaul if (status & PCN_CSR_ERR) { 99166131Swpaul pcn_init(sc); 99266131Swpaul break; 99366131Swpaul } 99466131Swpaul } 99566131Swpaul 99666131Swpaul if (ifp->if_snd.ifq_head != NULL) 99766131Swpaul pcn_start(ifp); 99866131Swpaul 99966131Swpaul return; 100066131Swpaul} 100166131Swpaul 100266131Swpaul/* 100366131Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 100466131Swpaul * pointers to the fragment pointers. 100566131Swpaul */ 1006102335Salfredstatic int 1007102335Salfredpcn_encap(sc, m_head, txidx) 100866131Swpaul struct pcn_softc *sc; 100966131Swpaul struct mbuf *m_head; 101066131Swpaul u_int32_t *txidx; 101166131Swpaul{ 101266131Swpaul struct pcn_tx_desc *f = NULL; 101366131Swpaul struct mbuf *m; 101466131Swpaul int frag, cur, cnt = 0; 101566131Swpaul 101666131Swpaul /* 101766131Swpaul * Start packing the mbufs in this chain into 101866131Swpaul * the fragment pointers. Stop when we run out 101966131Swpaul * of fragments or hit the end of the mbuf chain. 102066131Swpaul */ 102166131Swpaul m = m_head; 102266131Swpaul cur = frag = *txidx; 102366131Swpaul 102466131Swpaul for (m = m_head; m != NULL; m = m->m_next) { 102566131Swpaul if (m->m_len != 0) { 102666131Swpaul if ((PCN_TX_LIST_CNT - 102766131Swpaul (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) 102866131Swpaul return(ENOBUFS); 102966131Swpaul f = &sc->pcn_ldata->pcn_tx_list[frag]; 103066131Swpaul f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; 103166131Swpaul f->pcn_txctl |= PCN_TXCTL_MBO; 103266131Swpaul f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); 103366131Swpaul if (cnt == 0) 103466131Swpaul f->pcn_txctl |= PCN_TXCTL_STP; 103566131Swpaul else 103666131Swpaul f->pcn_txctl |= PCN_TXCTL_OWN; 103766131Swpaul cur = frag; 103866131Swpaul PCN_INC(frag, PCN_TX_LIST_CNT); 103966131Swpaul cnt++; 104066131Swpaul } 104166131Swpaul } 104266131Swpaul 104366131Swpaul if (m != NULL) 104466131Swpaul return(ENOBUFS); 104566131Swpaul 104666131Swpaul sc->pcn_cdata.pcn_tx_chain[cur] = m_head; 104766131Swpaul sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= 104866131Swpaul PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; 104966131Swpaul sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; 105066131Swpaul sc->pcn_cdata.pcn_tx_cnt += cnt; 105166131Swpaul *txidx = frag; 105266131Swpaul 105366131Swpaul return(0); 105466131Swpaul} 105566131Swpaul 105666131Swpaul/* 105766131Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 105866131Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 105966131Swpaul * copy of the pointers since the transmit list fragment pointers are 106066131Swpaul * physical addresses. 106166131Swpaul */ 1062102335Salfredstatic void 1063102335Salfredpcn_start(ifp) 106466131Swpaul struct ifnet *ifp; 106566131Swpaul{ 106666131Swpaul struct pcn_softc *sc; 106766131Swpaul struct mbuf *m_head = NULL; 106866131Swpaul u_int32_t idx; 106966131Swpaul 107066131Swpaul sc = ifp->if_softc; 107166131Swpaul 107267087Swpaul PCN_LOCK(sc); 107367087Swpaul 107467087Swpaul if (!sc->pcn_link) { 107567087Swpaul PCN_UNLOCK(sc); 107666131Swpaul return; 107767087Swpaul } 107866131Swpaul 107966131Swpaul idx = sc->pcn_cdata.pcn_tx_prod; 108066131Swpaul 108167087Swpaul if (ifp->if_flags & IFF_OACTIVE) { 108267087Swpaul PCN_UNLOCK(sc); 108366131Swpaul return; 108467087Swpaul } 108566131Swpaul 108666131Swpaul while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { 108766131Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 108866131Swpaul if (m_head == NULL) 108966131Swpaul break; 109066131Swpaul 109166131Swpaul if (pcn_encap(sc, m_head, &idx)) { 109266131Swpaul IF_PREPEND(&ifp->if_snd, m_head); 109366131Swpaul ifp->if_flags |= IFF_OACTIVE; 109466131Swpaul break; 109566131Swpaul } 109666131Swpaul 109766131Swpaul /* 109866131Swpaul * If there's a BPF listener, bounce a copy of this frame 109966131Swpaul * to him. 110066131Swpaul */ 1101106936Ssam BPF_MTAP(ifp, m_head); 110266131Swpaul 110366131Swpaul } 110466131Swpaul 110566131Swpaul /* Transmit */ 110666131Swpaul sc->pcn_cdata.pcn_tx_prod = idx; 110766131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); 110866131Swpaul 110966131Swpaul /* 111066131Swpaul * Set a timeout in case the chip goes out to lunch. 111166131Swpaul */ 111266131Swpaul ifp->if_timer = 5; 111366131Swpaul 111467087Swpaul PCN_UNLOCK(sc); 111567087Swpaul 111666131Swpaul return; 111766131Swpaul} 111866131Swpaul 1119102335Salfredstatic void 1120102335Salfredpcn_setfilt(ifp) 112168270Swpaul struct ifnet *ifp; 112268270Swpaul{ 112368270Swpaul struct pcn_softc *sc; 112468270Swpaul 112568270Swpaul sc = ifp->if_softc; 112668270Swpaul 112768270Swpaul /* If we want promiscuous mode, set the allframes bit. */ 112868270Swpaul if (ifp->if_flags & IFF_PROMISC) { 112968270Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 113068270Swpaul } else { 113168270Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 113268270Swpaul } 113368270Swpaul 113468270Swpaul /* Set the capture broadcast bit to capture broadcast frames. */ 113568270Swpaul if (ifp->if_flags & IFF_BROADCAST) { 113668270Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 113768270Swpaul } else { 113868270Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 113968270Swpaul } 114068270Swpaul 114168270Swpaul return; 114268270Swpaul} 114368270Swpaul 1144102335Salfredstatic void 1145102335Salfredpcn_init(xsc) 114666131Swpaul void *xsc; 114766131Swpaul{ 114866131Swpaul struct pcn_softc *sc = xsc; 114966131Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 115066131Swpaul struct mii_data *mii = NULL; 115166131Swpaul 115267087Swpaul PCN_LOCK(sc); 115366131Swpaul 115466131Swpaul /* 115566131Swpaul * Cancel pending I/O and free all RX/TX buffers. 115666131Swpaul */ 115766131Swpaul pcn_stop(sc); 115866131Swpaul pcn_reset(sc); 115966131Swpaul 116066131Swpaul mii = device_get_softc(sc->pcn_miibus); 116166131Swpaul 116266131Swpaul /* Set MAC address */ 116366131Swpaul pcn_csr_write(sc, PCN_CSR_PAR0, 116466131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 116566131Swpaul pcn_csr_write(sc, PCN_CSR_PAR1, 116666131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 116766131Swpaul pcn_csr_write(sc, PCN_CSR_PAR2, 116866131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 116966131Swpaul 117066131Swpaul /* Init circular RX list. */ 117166131Swpaul if (pcn_list_rx_init(sc) == ENOBUFS) { 117266131Swpaul printf("pcn%d: initialization failed: no " 117366131Swpaul "memory for rx buffers\n", sc->pcn_unit); 117466131Swpaul pcn_stop(sc); 117567087Swpaul PCN_UNLOCK(sc); 117666131Swpaul return; 117766131Swpaul } 117866131Swpaul 117966131Swpaul /* 118066131Swpaul * Init tx descriptors. 118166131Swpaul */ 118266131Swpaul pcn_list_tx_init(sc); 118366131Swpaul 118466131Swpaul /* Set up the mode register. */ 118566131Swpaul pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); 118666131Swpaul 118768270Swpaul /* Set up RX filter. */ 118868270Swpaul pcn_setfilt(ifp); 118966131Swpaul 119066131Swpaul /* 119166131Swpaul * Load the multicast filter. 119266131Swpaul */ 119366131Swpaul pcn_setmulti(sc); 119466131Swpaul 119566131Swpaul /* 119666131Swpaul * Load the addresses of the RX and TX lists. 119766131Swpaul */ 119866131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR0, 119966131Swpaul vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); 120066131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR1, 120166131Swpaul (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); 120266131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR0, 120366131Swpaul vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); 120466131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR1, 120566131Swpaul (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); 120666131Swpaul 120766131Swpaul /* Set the RX and TX ring sizes. */ 120866131Swpaul pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); 120966131Swpaul pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); 121066131Swpaul 121166131Swpaul /* We're not using the initialization block. */ 121266131Swpaul pcn_csr_write(sc, PCN_CSR_IAB1, 0); 121366131Swpaul 121466131Swpaul /* Enable fast suspend mode. */ 121566131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); 121666131Swpaul 121766131Swpaul /* 121866131Swpaul * Enable burst read and write. Also set the no underflow 121966131Swpaul * bit. This will avoid transmit underruns in certain 122066210Swpaul * conditions while still providing decent performance. 122166131Swpaul */ 122266131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| 122366131Swpaul PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); 122466131Swpaul 122566131Swpaul /* Enable graceful recovery from underflow. */ 122666131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); 122766131Swpaul 122866131Swpaul /* Enable auto-padding of short TX frames. */ 122966131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); 123066131Swpaul 123166131Swpaul /* Disable MII autoneg (we handle this ourselves). */ 123277786Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); 123366131Swpaul 123466131Swpaul if (sc->pcn_type == Am79C978) 123566131Swpaul pcn_bcr_write(sc, PCN_BCR_PHYSEL, 123666131Swpaul PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); 123766131Swpaul 123866131Swpaul /* Enable interrupts and start the controller running. */ 123966131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); 124066131Swpaul 124166131Swpaul mii_mediachg(mii); 124266131Swpaul 124366131Swpaul ifp->if_flags |= IFF_RUNNING; 124466131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 124566131Swpaul 124666131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 124767087Swpaul PCN_UNLOCK(sc); 124866131Swpaul 124966131Swpaul return; 125066131Swpaul} 125166131Swpaul 125266131Swpaul/* 125366131Swpaul * Set media options. 125466131Swpaul */ 1255102335Salfredstatic int 1256102335Salfredpcn_ifmedia_upd(ifp) 125766131Swpaul struct ifnet *ifp; 125866131Swpaul{ 125966131Swpaul struct pcn_softc *sc; 126066131Swpaul struct mii_data *mii; 126166131Swpaul 126266131Swpaul sc = ifp->if_softc; 126366131Swpaul mii = device_get_softc(sc->pcn_miibus); 126466131Swpaul 126566131Swpaul sc->pcn_link = 0; 126666131Swpaul if (mii->mii_instance) { 126766131Swpaul struct mii_softc *miisc; 126872012Sphk LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 126966131Swpaul mii_phy_reset(miisc); 127066131Swpaul } 127166131Swpaul mii_mediachg(mii); 127266131Swpaul 127366131Swpaul return(0); 127466131Swpaul} 127566131Swpaul 127666131Swpaul/* 127766131Swpaul * Report current media status. 127866131Swpaul */ 1279102335Salfredstatic void 1280102335Salfredpcn_ifmedia_sts(ifp, ifmr) 128166131Swpaul struct ifnet *ifp; 128266131Swpaul struct ifmediareq *ifmr; 128366131Swpaul{ 128466131Swpaul struct pcn_softc *sc; 128566131Swpaul struct mii_data *mii; 128666131Swpaul 128766131Swpaul sc = ifp->if_softc; 128866131Swpaul 128966131Swpaul mii = device_get_softc(sc->pcn_miibus); 129066131Swpaul mii_pollstat(mii); 129166131Swpaul ifmr->ifm_active = mii->mii_media_active; 129266131Swpaul ifmr->ifm_status = mii->mii_media_status; 129366131Swpaul 129466131Swpaul return; 129566131Swpaul} 129666131Swpaul 1297102335Salfredstatic int 1298102335Salfredpcn_ioctl(ifp, command, data) 129966131Swpaul struct ifnet *ifp; 130066131Swpaul u_long command; 130166131Swpaul caddr_t data; 130266131Swpaul{ 130366131Swpaul struct pcn_softc *sc = ifp->if_softc; 130466131Swpaul struct ifreq *ifr = (struct ifreq *) data; 130566131Swpaul struct mii_data *mii = NULL; 130667087Swpaul int error = 0; 130766131Swpaul 130867087Swpaul PCN_LOCK(sc); 130966131Swpaul 131066131Swpaul switch(command) { 131166131Swpaul case SIOCSIFFLAGS: 131266131Swpaul if (ifp->if_flags & IFF_UP) { 131366131Swpaul if (ifp->if_flags & IFF_RUNNING && 131466131Swpaul ifp->if_flags & IFF_PROMISC && 131566131Swpaul !(sc->pcn_if_flags & IFF_PROMISC)) { 131666131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 131766131Swpaul PCN_EXTCTL1_SPND); 131868270Swpaul pcn_setfilt(ifp); 131966131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 132066131Swpaul PCN_EXTCTL1_SPND); 132166771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 132266771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 132366131Swpaul } else if (ifp->if_flags & IFF_RUNNING && 132466131Swpaul !(ifp->if_flags & IFF_PROMISC) && 132566131Swpaul sc->pcn_if_flags & IFF_PROMISC) { 132666131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 132766131Swpaul PCN_EXTCTL1_SPND); 132868270Swpaul pcn_setfilt(ifp); 132966131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 133066131Swpaul PCN_EXTCTL1_SPND); 133166771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 133266771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 133366131Swpaul } else if (!(ifp->if_flags & IFF_RUNNING)) 133466131Swpaul pcn_init(sc); 133566131Swpaul } else { 133666131Swpaul if (ifp->if_flags & IFF_RUNNING) 133766131Swpaul pcn_stop(sc); 133866131Swpaul } 133966131Swpaul sc->pcn_if_flags = ifp->if_flags; 134066131Swpaul error = 0; 134166131Swpaul break; 134266131Swpaul case SIOCADDMULTI: 134366131Swpaul case SIOCDELMULTI: 134466131Swpaul pcn_setmulti(sc); 134566131Swpaul error = 0; 134666131Swpaul break; 134766131Swpaul case SIOCGIFMEDIA: 134866131Swpaul case SIOCSIFMEDIA: 134966131Swpaul mii = device_get_softc(sc->pcn_miibus); 135066131Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 135166131Swpaul break; 135266131Swpaul default: 1353106936Ssam error = ether_ioctl(ifp, command, data); 135466131Swpaul break; 135566131Swpaul } 135666131Swpaul 135767087Swpaul PCN_UNLOCK(sc); 135866131Swpaul 135966131Swpaul return(error); 136066131Swpaul} 136166131Swpaul 1362102335Salfredstatic void 1363102335Salfredpcn_watchdog(ifp) 136466131Swpaul struct ifnet *ifp; 136566131Swpaul{ 136666131Swpaul struct pcn_softc *sc; 136766131Swpaul 136866131Swpaul sc = ifp->if_softc; 136966131Swpaul 137067087Swpaul PCN_LOCK(sc); 137167087Swpaul 137266131Swpaul ifp->if_oerrors++; 137366131Swpaul printf("pcn%d: watchdog timeout\n", sc->pcn_unit); 137466131Swpaul 137566131Swpaul pcn_stop(sc); 137666131Swpaul pcn_reset(sc); 137766131Swpaul pcn_init(sc); 137866131Swpaul 137966131Swpaul if (ifp->if_snd.ifq_head != NULL) 138066131Swpaul pcn_start(ifp); 138166131Swpaul 138267087Swpaul PCN_UNLOCK(sc); 138367087Swpaul 138466131Swpaul return; 138566131Swpaul} 138666131Swpaul 138766131Swpaul/* 138866131Swpaul * Stop the adapter and free any mbufs allocated to the 138966131Swpaul * RX and TX lists. 139066131Swpaul */ 1391102335Salfredstatic void 1392102335Salfredpcn_stop(sc) 139366131Swpaul struct pcn_softc *sc; 139466131Swpaul{ 139566131Swpaul register int i; 139666131Swpaul struct ifnet *ifp; 139766131Swpaul 139866131Swpaul ifp = &sc->arpcom.ac_if; 139967087Swpaul PCN_LOCK(sc); 140066131Swpaul ifp->if_timer = 0; 140166131Swpaul 140266131Swpaul untimeout(pcn_tick, sc, sc->pcn_stat_ch); 140366131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); 140466131Swpaul sc->pcn_link = 0; 140566131Swpaul 140666131Swpaul /* 140766131Swpaul * Free data in the RX lists. 140866131Swpaul */ 140966131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 141066131Swpaul if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { 141166131Swpaul m_freem(sc->pcn_cdata.pcn_rx_chain[i]); 141266131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 141366131Swpaul } 141466131Swpaul } 141566131Swpaul bzero((char *)&sc->pcn_ldata->pcn_rx_list, 141666131Swpaul sizeof(sc->pcn_ldata->pcn_rx_list)); 141766131Swpaul 141866131Swpaul /* 141966131Swpaul * Free the TX list buffers. 142066131Swpaul */ 142166131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 142266131Swpaul if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { 142366131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[i]); 142466131Swpaul sc->pcn_cdata.pcn_tx_chain[i] = NULL; 142566131Swpaul } 142666131Swpaul } 142766131Swpaul 142866131Swpaul bzero((char *)&sc->pcn_ldata->pcn_tx_list, 142966131Swpaul sizeof(sc->pcn_ldata->pcn_tx_list)); 143066131Swpaul 143166131Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 143267087Swpaul PCN_UNLOCK(sc); 143366131Swpaul 143466131Swpaul return; 143566131Swpaul} 143666131Swpaul 143766131Swpaul/* 143866131Swpaul * Stop all chip I/O so that the kernel's probe routines don't 143966131Swpaul * get confused by errant DMAs when rebooting. 144066131Swpaul */ 1441102335Salfredstatic void 1442102335Salfredpcn_shutdown(dev) 144366131Swpaul device_t dev; 144466131Swpaul{ 144566131Swpaul struct pcn_softc *sc; 144666131Swpaul 144766131Swpaul sc = device_get_softc(dev); 144866131Swpaul 144967087Swpaul PCN_LOCK(sc); 145066131Swpaul pcn_reset(sc); 145166131Swpaul pcn_stop(sc); 145267087Swpaul PCN_UNLOCK(sc); 145366131Swpaul 145466131Swpaul return; 145566131Swpaul} 1456