if_pcn.c revision 72084
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 72084 2001-02-06 10:12:15Z phk $ 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 72084 2001-02-06 10:12:15Z phk $"; 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 11266131Swpaulstatic u_int32_t pcn_csr_read __P((struct pcn_softc *, int)); 11368837Swpaulstatic u_int16_t pcn_csr_read16 __P((struct pcn_softc *, int)); 11469067Swpaulstatic u_int16_t pcn_bcr_read16 __P((struct pcn_softc *, int)); 11566131Swpaulstatic void pcn_csr_write __P((struct pcn_softc *, int, int)); 11666131Swpaulstatic u_int32_t pcn_bcr_read __P((struct pcn_softc *, int)); 11766131Swpaulstatic void pcn_bcr_write __P((struct pcn_softc *, int, int)); 11866131Swpaul 11966131Swpaulstatic int pcn_probe __P((device_t)); 12066131Swpaulstatic int pcn_attach __P((device_t)); 12166131Swpaulstatic int pcn_detach __P((device_t)); 12266131Swpaul 12366131Swpaulstatic int pcn_newbuf __P((struct pcn_softc *, int, struct mbuf *)); 12466131Swpaulstatic int pcn_encap __P((struct pcn_softc *, 12566131Swpaul struct mbuf *, u_int32_t *)); 12666131Swpaulstatic void pcn_rxeof __P((struct pcn_softc *)); 12766131Swpaulstatic void pcn_txeof __P((struct pcn_softc *)); 12866131Swpaulstatic void pcn_intr __P((void *)); 12966131Swpaulstatic void pcn_tick __P((void *)); 13066131Swpaulstatic void pcn_start __P((struct ifnet *)); 13166131Swpaulstatic int pcn_ioctl __P((struct ifnet *, u_long, caddr_t)); 13266131Swpaulstatic void pcn_init __P((void *)); 13366131Swpaulstatic void pcn_stop __P((struct pcn_softc *)); 13466131Swpaulstatic void pcn_watchdog __P((struct ifnet *)); 13566131Swpaulstatic void pcn_shutdown __P((device_t)); 13666131Swpaulstatic int pcn_ifmedia_upd __P((struct ifnet *)); 13766131Swpaulstatic void pcn_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 13866131Swpaul 13966131Swpaulstatic int pcn_miibus_readreg __P((device_t, int, int)); 14066131Swpaulstatic int pcn_miibus_writereg __P((device_t, int, int, int)); 14166131Swpaulstatic void pcn_miibus_statchg __P((device_t)); 14266131Swpaul 14368270Swpaulstatic void pcn_setfilt __P((struct ifnet *)); 14466131Swpaulstatic void pcn_setmulti __P((struct pcn_softc *)); 14566131Swpaulstatic u_int32_t pcn_crc __P((caddr_t)); 14666131Swpaulstatic void pcn_reset __P((struct pcn_softc *)); 14766131Swpaulstatic int pcn_list_rx_init __P((struct pcn_softc *)); 14866131Swpaulstatic int pcn_list_tx_init __P((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 20066131Swpaulstatic u_int32_t pcn_csr_read(sc, reg) 20166131Swpaul struct pcn_softc *sc; 20266131Swpaul int reg; 20366131Swpaul{ 20466131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 20566131Swpaul return(CSR_READ_4(sc, PCN_IO32_RDP)); 20666131Swpaul} 20766131Swpaul 20868837Swpaulstatic u_int16_t pcn_csr_read16(sc, reg) 20968837Swpaul struct pcn_softc *sc; 21068837Swpaul int reg; 21168837Swpaul{ 21268837Swpaul CSR_WRITE_2(sc, PCN_IO16_RAP, reg); 21368837Swpaul return(CSR_READ_2(sc, PCN_IO16_RDP)); 21468837Swpaul} 21568837Swpaul 21666131Swpaulstatic void pcn_csr_write(sc, reg, val) 21766131Swpaul struct pcn_softc *sc; 21866131Swpaul int reg; 21966131Swpaul{ 22066131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 22166131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, val); 22266131Swpaul return; 22366131Swpaul} 22466131Swpaul 22566131Swpaulstatic u_int32_t pcn_bcr_read(sc, reg) 22666131Swpaul struct pcn_softc *sc; 22766131Swpaul int reg; 22866131Swpaul{ 22966131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 23066131Swpaul return(CSR_READ_4(sc, PCN_IO32_BDP)); 23166131Swpaul} 23266131Swpaul 23369067Swpaulstatic u_int16_t pcn_bcr_read16(sc, reg) 23469067Swpaul struct pcn_softc *sc; 23569067Swpaul int reg; 23669067Swpaul{ 23769067Swpaul CSR_WRITE_2(sc, PCN_IO16_RAP, reg); 23869067Swpaul return(CSR_READ_2(sc, PCN_IO16_BDP)); 23969067Swpaul} 24069067Swpaul 24166131Swpaulstatic void pcn_bcr_write(sc, reg, val) 24266131Swpaul struct pcn_softc *sc; 24366131Swpaul int reg; 24466131Swpaul{ 24566131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 24666131Swpaul CSR_WRITE_4(sc, PCN_IO32_BDP, val); 24766131Swpaul return; 24866131Swpaul} 24966131Swpaul 25066131Swpaulstatic int pcn_miibus_readreg(dev, phy, reg) 25166131Swpaul device_t dev; 25266131Swpaul int phy, reg; 25366131Swpaul{ 25466131Swpaul struct pcn_softc *sc; 25566131Swpaul int val; 25666131Swpaul 25766131Swpaul sc = device_get_softc(dev); 25866131Swpaul 25966208Swpaul if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr) 26066131Swpaul return(0); 26166131Swpaul 26266131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 26366131Swpaul val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; 26466131Swpaul if (val == 0xFFFF) 26566131Swpaul return(0); 26666208Swpaul 26766208Swpaul sc->pcn_phyaddr = phy; 26866208Swpaul 26966131Swpaul return(val); 27066131Swpaul} 27166131Swpaul 27266131Swpaulstatic int pcn_miibus_writereg(dev, phy, reg, data) 27366131Swpaul device_t dev; 27466131Swpaul int phy, reg, data; 27566131Swpaul{ 27666131Swpaul struct pcn_softc *sc; 27766131Swpaul 27866131Swpaul sc = device_get_softc(dev); 27966131Swpaul 28066131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 28166131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); 28266131Swpaul 28366131Swpaul return(0); 28466131Swpaul} 28566131Swpaul 28666131Swpaulstatic void pcn_miibus_statchg(dev) 28766131Swpaul device_t dev; 28866131Swpaul{ 28966131Swpaul struct pcn_softc *sc; 29066131Swpaul struct mii_data *mii; 29166131Swpaul 29266131Swpaul sc = device_get_softc(dev); 29366131Swpaul mii = device_get_softc(sc->pcn_miibus); 29466131Swpaul 29566131Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 29666131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 29766131Swpaul } else { 29866131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 29966131Swpaul } 30066131Swpaul 30166131Swpaul return; 30266131Swpaul} 30366131Swpaul 30466131Swpaul#define DC_POLY 0xEDB88320 30566131Swpaul 30666131Swpaulstatic u_int32_t pcn_crc(addr) 30766131Swpaul caddr_t addr; 30866131Swpaul{ 30966131Swpaul u_int32_t idx, bit, data, crc; 31066131Swpaul 31166131Swpaul /* Compute CRC for the address value. */ 31266131Swpaul crc = 0xFFFFFFFF; /* initial value */ 31366131Swpaul 31466131Swpaul for (idx = 0; idx < 6; idx++) { 31566131Swpaul for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) 31666131Swpaul crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); 31766131Swpaul } 31866131Swpaul 31966131Swpaul return ((crc >> 26) & 0x3F); 32066131Swpaul} 32166131Swpaul 32266131Swpaulstatic void pcn_setmulti(sc) 32366131Swpaul struct pcn_softc *sc; 32466131Swpaul{ 32566131Swpaul struct ifnet *ifp; 32666131Swpaul struct ifmultiaddr *ifma; 32766131Swpaul u_int32_t h, i; 32866131Swpaul u_int16_t hashes[4] = { 0, 0, 0, 0 }; 32966131Swpaul 33066131Swpaul ifp = &sc->arpcom.ac_if; 33166131Swpaul 33266131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 33366131Swpaul 33466131Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 33566131Swpaul for (i = 0; i < 4; i++) 33666131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); 33766131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 33866131Swpaul return; 33966131Swpaul } 34066131Swpaul 34166131Swpaul /* first, zot all the existing hash bits */ 34266131Swpaul for (i = 0; i < 4; i++) 34366131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); 34466131Swpaul 34566131Swpaul /* now program new ones */ 34672084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 34766131Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 34866131Swpaul continue; 34966131Swpaul h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 35066131Swpaul hashes[h >> 4] |= 1 << (h & 0xF); 35166131Swpaul } 35266131Swpaul 35366131Swpaul for (i = 0; i < 4; i++) 35466131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); 35566131Swpaul 35666131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 35766131Swpaul 35866131Swpaul return; 35966131Swpaul} 36066131Swpaul 36166131Swpaulstatic void pcn_reset(sc) 36266131Swpaul struct pcn_softc *sc; 36366131Swpaul{ 36466131Swpaul /* 36566131Swpaul * Issue a reset by reading from the RESET register. 36666131Swpaul * Note that we don't know if the chip is operating in 36766131Swpaul * 16-bit or 32-bit mode at this point, so we attempt 36866131Swpaul * to reset the chip both ways. If one fails, the other 36966131Swpaul * will succeed. 37066131Swpaul */ 37166131Swpaul CSR_READ_2(sc, PCN_IO16_RESET); 37266131Swpaul CSR_READ_4(sc, PCN_IO32_RESET); 37366131Swpaul 37466131Swpaul /* Wait a little while for the chip to get its brains in order. */ 37566131Swpaul DELAY(1000); 37666131Swpaul 37766131Swpaul /* Select 32-bit (DWIO) mode */ 37866131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, 0); 37966131Swpaul 38066131Swpaul /* Select software style 3. */ 38166131Swpaul pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); 38266131Swpaul 38366131Swpaul return; 38466131Swpaul} 38566131Swpaul 38666131Swpaul/* 38766131Swpaul * Probe for an AMD chip. Check the PCI vendor and device 38866131Swpaul * IDs against our list and return a device name if we find a match. 38966131Swpaul */ 39066131Swpaulstatic int pcn_probe(dev) 39166131Swpaul device_t dev; 39266131Swpaul{ 39366131Swpaul struct pcn_type *t; 39466131Swpaul struct pcn_softc *sc; 39566131Swpaul int rid; 39666131Swpaul u_int32_t chip_id; 39766131Swpaul 39866131Swpaul t = pcn_devs; 39966131Swpaul sc = device_get_softc(dev); 40066131Swpaul 40166131Swpaul while(t->pcn_name != NULL) { 40266131Swpaul if ((pci_get_vendor(dev) == t->pcn_vid) && 40366131Swpaul (pci_get_device(dev) == t->pcn_did)) { 40466131Swpaul /* 40566131Swpaul * Temporarily map the I/O space 40666131Swpaul * so we can read the chip ID register. 40766131Swpaul */ 40866131Swpaul rid = PCN_RID; 40966131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 41066131Swpaul 0, ~0, 1, RF_ACTIVE); 41166131Swpaul if (sc->pcn_res == NULL) { 41266131Swpaul device_printf(dev, 41366131Swpaul "couldn't map ports/memory\n"); 41466131Swpaul return(ENXIO); 41566131Swpaul } 41666131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 41766131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 41867089Swpaul mtx_init(&sc->pcn_mtx, 41967089Swpaul device_get_nameunit(dev), MTX_DEF); 42067087Swpaul PCN_LOCK(sc); 42168837Swpaul /* 42268837Swpaul * Note: we can *NOT* put the chip into 42368837Swpaul * 32-bit mode yet. The lnc driver will only 42468837Swpaul * work in 16-bit mode, and once the chip 42568837Swpaul * goes into 32-bit mode, the only way to 42668837Swpaul * get it out again is with a hardware reset. 42768837Swpaul * So if pcn_probe() is called before the 42868837Swpaul * lnc driver's probe routine, the chip will 42968837Swpaul * be locked into 32-bit operation and the lnc 43068837Swpaul * driver will be unable to attach to it. 43169067Swpaul * Note II: if the chip happens to already 43269067Swpaul * be in 32-bit mode, we still need to check 43369067Swpaul * the chip ID, but first we have to detect 43469067Swpaul * 32-bit mode using only 16-bit operations. 43569067Swpaul * The safest way to do this is to read the 43669067Swpaul * PCI subsystem ID from BCR23/24 and compare 43769067Swpaul * that with the value read from PCI config 43869067Swpaul * space. 43968837Swpaul */ 44069067Swpaul chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); 44166131Swpaul chip_id <<= 16; 44269067Swpaul chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); 44369067Swpaul if (chip_id == pci_read_config(dev, 44469067Swpaul PCIR_SUBVEND_0, 4)) { 44569067Swpaul /* We're in 16-bit mode. */ 44669067Swpaul chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); 44769067Swpaul chip_id <<= 16; 44869067Swpaul chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); 44969067Swpaul } else { 45069067Swpaul /* We're in 32-bit mode. */ 45169067Swpaul chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); 45269067Swpaul chip_id <<= 16; 45369067Swpaul chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); 45469067Swpaul } 45566131Swpaul bus_release_resource(dev, PCN_RES, 45666131Swpaul PCN_RID, sc->pcn_res); 45767087Swpaul PCN_UNLOCK(sc); 45867087Swpaul mtx_destroy(&sc->pcn_mtx); 45966131Swpaul chip_id >>= 12; 46066131Swpaul sc->pcn_type = chip_id & PART_MASK; 46166131Swpaul switch(sc->pcn_type) { 46266131Swpaul case Am79C971: 46366131Swpaul case Am79C972: 46466131Swpaul case Am79C973: 46566690Swpaul case Am79C975: 46666592Swpaul case Am79C976: 46766131Swpaul case Am79C978: 46866131Swpaul break; 46966131Swpaul default: 47066131Swpaul return(ENXIO); 47166131Swpaul break; 47266131Swpaul } 47366131Swpaul device_set_desc(dev, t->pcn_name); 47466131Swpaul return(0); 47566131Swpaul } 47666131Swpaul t++; 47766131Swpaul } 47866131Swpaul 47966131Swpaul return(ENXIO); 48066131Swpaul} 48166131Swpaul 48266131Swpaul/* 48366131Swpaul * Attach the interface. Allocate softc structures, do ifmedia 48466131Swpaul * setup and ethernet/BPF attach. 48566131Swpaul */ 48666131Swpaulstatic int pcn_attach(dev) 48766131Swpaul device_t dev; 48866131Swpaul{ 48966131Swpaul u_int32_t eaddr[2]; 49066131Swpaul u_int32_t command; 49166131Swpaul struct pcn_softc *sc; 49266131Swpaul struct ifnet *ifp; 49366131Swpaul int unit, error = 0, rid; 49466131Swpaul 49566131Swpaul sc = device_get_softc(dev); 49666131Swpaul unit = device_get_unit(dev); 49766131Swpaul 49869583Swpaul /* Initialize our mutex. */ 49971228Sbmilekic mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE); 50069583Swpaul PCN_LOCK(sc); 50169583Swpaul 50266131Swpaul /* 50366131Swpaul * Handle power management nonsense. 50466131Swpaul */ 50566131Swpaul 50666131Swpaul command = pci_read_config(dev, PCN_PCI_CAPID, 4) & 0x000000FF; 50766131Swpaul if (command == 0x01) { 50866131Swpaul 50966131Swpaul command = pci_read_config(dev, PCN_PCI_PWRMGMTCTRL, 4); 51066131Swpaul if (command & PCN_PSTATE_MASK) { 51166131Swpaul u_int32_t iobase, membase, irq; 51266131Swpaul 51366131Swpaul /* Save important PCI config data. */ 51466131Swpaul iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); 51566131Swpaul membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); 51666131Swpaul irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); 51766131Swpaul 51866131Swpaul /* Reset the power state. */ 51966131Swpaul printf("pcn%d: chip is in D%d power mode " 52066131Swpaul "-- setting to D0\n", unit, command & PCN_PSTATE_MASK); 52166131Swpaul command &= 0xFFFFFFFC; 52266131Swpaul pci_write_config(dev, PCN_PCI_PWRMGMTCTRL, command, 4); 52366131Swpaul 52466131Swpaul /* Restore PCI config data. */ 52566131Swpaul pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); 52666131Swpaul pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); 52766131Swpaul pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); 52866131Swpaul } 52966131Swpaul } 53066131Swpaul 53166131Swpaul /* 53266131Swpaul * Map control/status registers. 53366131Swpaul */ 53466131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 53566131Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 53666131Swpaul pci_write_config(dev, PCIR_COMMAND, command, 4); 53766131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 53866131Swpaul 53966131Swpaul#ifdef PCN_USEIOSPACE 54066131Swpaul if (!(command & PCIM_CMD_PORTEN)) { 54166131Swpaul printf("pcn%d: failed to enable I/O ports!\n", unit); 54266131Swpaul error = ENXIO;; 54366131Swpaul goto fail; 54466131Swpaul } 54566131Swpaul#else 54666131Swpaul if (!(command & PCIM_CMD_MEMEN)) { 54766131Swpaul printf("pcn%d: failed to enable memory mapping!\n", unit); 54866131Swpaul error = ENXIO;; 54966131Swpaul goto fail; 55066131Swpaul } 55166131Swpaul#endif 55266131Swpaul 55366131Swpaul rid = PCN_RID; 55466131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 55566131Swpaul 0, ~0, 1, RF_ACTIVE); 55666131Swpaul 55766131Swpaul if (sc->pcn_res == NULL) { 55866131Swpaul printf("pcn%d: couldn't map ports/memory\n", unit); 55966131Swpaul error = ENXIO; 56066131Swpaul goto fail; 56166131Swpaul } 56266131Swpaul 56366131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 56466131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 56566131Swpaul 56666131Swpaul /* Allocate interrupt */ 56766131Swpaul rid = 0; 56866131Swpaul sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 56966131Swpaul RF_SHAREABLE | RF_ACTIVE); 57066131Swpaul 57166131Swpaul if (sc->pcn_irq == NULL) { 57266131Swpaul printf("pcn%d: couldn't map interrupt\n", unit); 57366131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 57466131Swpaul error = ENXIO; 57566131Swpaul goto fail; 57666131Swpaul } 57766131Swpaul 57866131Swpaul error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, 57966131Swpaul pcn_intr, sc, &sc->pcn_intrhand); 58066131Swpaul 58166131Swpaul if (error) { 58266131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res); 58366131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 58466131Swpaul printf("pcn%d: couldn't set up irq\n", unit); 58566131Swpaul goto fail; 58666131Swpaul } 58766131Swpaul 58866131Swpaul /* Reset the adapter. */ 58966131Swpaul pcn_reset(sc); 59066131Swpaul 59166131Swpaul /* 59266131Swpaul * Get station address from the EEPROM. 59366131Swpaul */ 59466131Swpaul eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); 59566131Swpaul eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); 59666131Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 59766131Swpaul 59866131Swpaul /* 59966131Swpaul * An AMD chip was detected. Inform the world. 60066131Swpaul */ 60166131Swpaul printf("pcn%d: Ethernet address: %6D\n", unit, 60266131Swpaul sc->arpcom.ac_enaddr, ":"); 60366131Swpaul 60466131Swpaul sc->pcn_unit = unit; 60566131Swpaul callout_handle_init(&sc->pcn_stat_ch); 60666131Swpaul 60766131Swpaul sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, 60866131Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 60966131Swpaul 61066131Swpaul if (sc->pcn_ldata == NULL) { 61166131Swpaul printf("pcn%d: no memory for list buffers!\n", unit); 61266131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 61366131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 61466131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 61566131Swpaul error = ENXIO; 61666131Swpaul goto fail; 61766131Swpaul } 61866131Swpaul bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); 61966131Swpaul 62066131Swpaul ifp = &sc->arpcom.ac_if; 62166131Swpaul ifp->if_softc = sc; 62266131Swpaul ifp->if_unit = unit; 62366131Swpaul ifp->if_name = "pcn"; 62466131Swpaul ifp->if_mtu = ETHERMTU; 62566131Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 62666131Swpaul ifp->if_ioctl = pcn_ioctl; 62766131Swpaul ifp->if_output = ether_output; 62866131Swpaul ifp->if_start = pcn_start; 62966131Swpaul ifp->if_watchdog = pcn_watchdog; 63066131Swpaul ifp->if_init = pcn_init; 63166131Swpaul ifp->if_baudrate = 10000000; 63266131Swpaul ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; 63366131Swpaul 63466131Swpaul /* 63566131Swpaul * Do MII setup. 63666131Swpaul */ 63766131Swpaul if (mii_phy_probe(dev, &sc->pcn_miibus, 63866131Swpaul pcn_ifmedia_upd, pcn_ifmedia_sts)) { 63966131Swpaul printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); 64066131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 64166131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 64266131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 64366131Swpaul error = ENXIO; 64466131Swpaul goto fail; 64566131Swpaul } 64666131Swpaul 64766131Swpaul /* 64866131Swpaul * Call MI attach routine. 64966131Swpaul */ 65066131Swpaul ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 65166131Swpaul callout_handle_init(&sc->pcn_stat_ch); 65267087Swpaul PCN_UNLOCK(sc); 65367087Swpaul return(0); 65466131Swpaul 65566131Swpaulfail: 65667087Swpaul PCN_UNLOCK(sc); 65767087Swpaul mtx_destroy(&sc->pcn_mtx); 65867087Swpaul 65966131Swpaul return(error); 66066131Swpaul} 66166131Swpaul 66266131Swpaulstatic int pcn_detach(dev) 66366131Swpaul device_t dev; 66466131Swpaul{ 66566131Swpaul struct pcn_softc *sc; 66666131Swpaul struct ifnet *ifp; 66766131Swpaul 66866131Swpaul sc = device_get_softc(dev); 66966131Swpaul ifp = &sc->arpcom.ac_if; 67066131Swpaul 67167087Swpaul PCN_LOCK(sc); 67267087Swpaul 67366131Swpaul pcn_reset(sc); 67466131Swpaul pcn_stop(sc); 67566131Swpaul ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 67666131Swpaul 67766131Swpaul if (sc->pcn_miibus != NULL) { 67866131Swpaul bus_generic_detach(dev); 67966131Swpaul device_delete_child(dev, sc->pcn_miibus); 68066131Swpaul } 68166131Swpaul 68266131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 68366131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 68466131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 68566131Swpaul 68666131Swpaul contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); 68767087Swpaul PCN_UNLOCK(sc); 68866131Swpaul 68967087Swpaul mtx_destroy(&sc->pcn_mtx); 69066131Swpaul 69166131Swpaul return(0); 69266131Swpaul} 69366131Swpaul 69466131Swpaul/* 69566131Swpaul * Initialize the transmit descriptors. 69666131Swpaul */ 69766131Swpaulstatic int pcn_list_tx_init(sc) 69866131Swpaul struct pcn_softc *sc; 69966131Swpaul{ 70066131Swpaul struct pcn_list_data *ld; 70166131Swpaul struct pcn_ring_data *cd; 70266131Swpaul int i; 70366131Swpaul 70466131Swpaul cd = &sc->pcn_cdata; 70566131Swpaul ld = sc->pcn_ldata; 70666131Swpaul 70766131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 70866131Swpaul cd->pcn_tx_chain[i] = NULL; 70966131Swpaul ld->pcn_tx_list[i].pcn_tbaddr = 0; 71066131Swpaul ld->pcn_tx_list[i].pcn_txctl = 0; 71166131Swpaul ld->pcn_tx_list[i].pcn_txstat = 0; 71266131Swpaul } 71366131Swpaul 71466131Swpaul cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; 71566131Swpaul 71666131Swpaul return(0); 71766131Swpaul} 71866131Swpaul 71966131Swpaul 72066131Swpaul/* 72166131Swpaul * Initialize the RX descriptors and allocate mbufs for them. 72266131Swpaul */ 72366131Swpaulstatic int pcn_list_rx_init(sc) 72466131Swpaul struct pcn_softc *sc; 72566131Swpaul{ 72666131Swpaul struct pcn_list_data *ld; 72766131Swpaul struct pcn_ring_data *cd; 72866131Swpaul int i; 72966131Swpaul 73066131Swpaul ld = sc->pcn_ldata; 73166131Swpaul cd = &sc->pcn_cdata; 73266131Swpaul 73366131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 73466131Swpaul if (pcn_newbuf(sc, i, NULL) == ENOBUFS) 73566131Swpaul return(ENOBUFS); 73666131Swpaul } 73766131Swpaul 73866131Swpaul cd->pcn_rx_prod = 0; 73966131Swpaul 74066131Swpaul return(0); 74166131Swpaul} 74266131Swpaul 74366131Swpaul/* 74466131Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 74566131Swpaul */ 74666131Swpaulstatic int pcn_newbuf(sc, idx, m) 74766131Swpaul struct pcn_softc *sc; 74866131Swpaul int idx; 74966131Swpaul struct mbuf *m; 75066131Swpaul{ 75166131Swpaul struct mbuf *m_new = NULL; 75266131Swpaul struct pcn_rx_desc *c; 75366131Swpaul 75466131Swpaul c = &sc->pcn_ldata->pcn_rx_list[idx]; 75566131Swpaul 75666131Swpaul if (m == NULL) { 75766131Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 75866131Swpaul if (m_new == NULL) { 75966131Swpaul printf("pcn%d: no memory for rx list " 76066131Swpaul "-- packet dropped!\n", sc->pcn_unit); 76166131Swpaul return(ENOBUFS); 76266131Swpaul } 76366131Swpaul 76466131Swpaul MCLGET(m_new, M_DONTWAIT); 76566131Swpaul if (!(m_new->m_flags & M_EXT)) { 76666131Swpaul printf("pcn%d: no memory for rx list " 76766131Swpaul "-- packet dropped!\n", sc->pcn_unit); 76866131Swpaul m_freem(m_new); 76966131Swpaul return(ENOBUFS); 77066131Swpaul } 77166131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 77266131Swpaul } else { 77366131Swpaul m_new = m; 77466131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 77566131Swpaul m_new->m_data = m_new->m_ext.ext_buf; 77666131Swpaul } 77766131Swpaul 77866131Swpaul m_adj(m_new, ETHER_ALIGN); 77966131Swpaul 78066131Swpaul sc->pcn_cdata.pcn_rx_chain[idx] = m_new; 78166131Swpaul c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); 78266131Swpaul c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; 78366131Swpaul c->pcn_bufsz |= PCN_RXLEN_MBO; 78466131Swpaul c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; 78566131Swpaul 78666131Swpaul return(0); 78766131Swpaul} 78866131Swpaul 78966131Swpaul/* 79066131Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 79166131Swpaul * the higher level protocols. 79266131Swpaul */ 79366131Swpaulstatic void pcn_rxeof(sc) 79466131Swpaul struct pcn_softc *sc; 79566131Swpaul{ 79666131Swpaul struct ether_header *eh; 79766131Swpaul struct mbuf *m; 79866131Swpaul struct ifnet *ifp; 79966131Swpaul struct pcn_rx_desc *cur_rx; 80066131Swpaul int i; 80166131Swpaul 80266131Swpaul ifp = &sc->arpcom.ac_if; 80366131Swpaul i = sc->pcn_cdata.pcn_rx_prod; 80466131Swpaul 80566131Swpaul while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { 80666131Swpaul cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; 80766131Swpaul m = sc->pcn_cdata.pcn_rx_chain[i]; 80866131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 80966131Swpaul 81066131Swpaul /* 81166131Swpaul * If an error occurs, update stats, clear the 81266131Swpaul * status word and leave the mbuf cluster in place: 81366131Swpaul * it should simply get re-used next time this descriptor 81466131Swpaul * comes up in the ring. 81566131Swpaul */ 81666131Swpaul if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { 81766131Swpaul ifp->if_ierrors++; 81866131Swpaul pcn_newbuf(sc, i, m); 81966131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 82066131Swpaul continue; 82166131Swpaul } 82266131Swpaul 82366592Swpaul if (pcn_newbuf(sc, i, NULL)) { 82466592Swpaul /* Ran out of mbufs; recycle this one. */ 82566592Swpaul pcn_newbuf(sc, i, m); 82666592Swpaul ifp->if_ierrors++; 82766592Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 82866592Swpaul continue; 82966592Swpaul } 83066592Swpaul 83166131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 83266131Swpaul 83366131Swpaul /* No errors; receive the packet. */ 83466131Swpaul ifp->if_ipackets++; 83566131Swpaul eh = mtod(m, struct ether_header *); 83666131Swpaul m->m_len = m->m_pkthdr.len = 83766131Swpaul cur_rx->pcn_rxlen - ETHER_CRC_LEN; 83866131Swpaul m->m_pkthdr.rcvif = ifp; 83966131Swpaul 84066131Swpaul /* Remove header from mbuf and pass it on. */ 84166131Swpaul m_adj(m, sizeof(struct ether_header)); 84266131Swpaul ether_input(ifp, eh, m); 84366131Swpaul } 84466131Swpaul 84566131Swpaul sc->pcn_cdata.pcn_rx_prod = i; 84666131Swpaul 84766131Swpaul return; 84866131Swpaul} 84966131Swpaul 85066131Swpaul/* 85166131Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 85266131Swpaul * the list buffers. 85366131Swpaul */ 85466131Swpaul 85566131Swpaulstatic void pcn_txeof(sc) 85666131Swpaul struct pcn_softc *sc; 85766131Swpaul{ 85866131Swpaul struct pcn_tx_desc *cur_tx = NULL; 85966131Swpaul struct ifnet *ifp; 86066131Swpaul u_int32_t idx; 86166131Swpaul 86266131Swpaul ifp = &sc->arpcom.ac_if; 86366131Swpaul 86466131Swpaul /* Clear the timeout timer. */ 86566131Swpaul ifp->if_timer = 0; 86666131Swpaul 86766131Swpaul /* 86866131Swpaul * Go through our tx list and free mbufs for those 86966131Swpaul * frames that have been transmitted. 87066131Swpaul */ 87166131Swpaul idx = sc->pcn_cdata.pcn_tx_cons; 87266131Swpaul while (idx != sc->pcn_cdata.pcn_tx_prod) { 87366131Swpaul cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; 87466131Swpaul 87566131Swpaul if (!PCN_OWN_TXDESC(cur_tx)) 87666131Swpaul break; 87766131Swpaul 87866131Swpaul if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { 87966131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 88066131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 88166131Swpaul continue; 88266131Swpaul } 88366131Swpaul 88466131Swpaul if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { 88566131Swpaul ifp->if_oerrors++; 88666131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) 88766131Swpaul ifp->if_collisions++; 88866131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) 88966131Swpaul ifp->if_collisions++; 89066131Swpaul } 89166131Swpaul 89266131Swpaul ifp->if_collisions += 89366131Swpaul cur_tx->pcn_txstat & PCN_TXSTAT_TRC; 89466131Swpaul 89566131Swpaul ifp->if_opackets++; 89666131Swpaul if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { 89766131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); 89866131Swpaul sc->pcn_cdata.pcn_tx_chain[idx] = NULL; 89966131Swpaul } 90066131Swpaul 90166131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 90266131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 90366131Swpaul ifp->if_timer = 0; 90466131Swpaul } 90566131Swpaul 90666131Swpaul sc->pcn_cdata.pcn_tx_cons = idx; 90766131Swpaul 90866131Swpaul if (cur_tx != NULL) 90966131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 91066131Swpaul 91166131Swpaul return; 91266131Swpaul} 91366131Swpaul 91466131Swpaulstatic void pcn_tick(xsc) 91566131Swpaul void *xsc; 91666131Swpaul{ 91766131Swpaul struct pcn_softc *sc; 91866131Swpaul struct mii_data *mii; 91966131Swpaul struct ifnet *ifp; 92066131Swpaul 92166131Swpaul sc = xsc; 92266131Swpaul ifp = &sc->arpcom.ac_if; 92367087Swpaul PCN_LOCK(sc); 92466131Swpaul 92566131Swpaul mii = device_get_softc(sc->pcn_miibus); 92666131Swpaul mii_tick(mii); 92766131Swpaul 92866131Swpaul if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) 92966131Swpaul sc->pcn_link = 0; 93066131Swpaul 93166131Swpaul if (!sc->pcn_link) { 93266131Swpaul mii_pollstat(mii); 93366131Swpaul if (mii->mii_media_status & IFM_ACTIVE && 93466131Swpaul IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) 93566131Swpaul sc->pcn_link++; 93666131Swpaul if (ifp->if_snd.ifq_head != NULL) 93766131Swpaul pcn_start(ifp); 93866131Swpaul } 93966131Swpaul 94066131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 94166131Swpaul 94267087Swpaul PCN_UNLOCK(sc); 94366131Swpaul 94466131Swpaul return; 94566131Swpaul} 94666131Swpaul 94766131Swpaulstatic void pcn_intr(arg) 94866131Swpaul void *arg; 94966131Swpaul{ 95066131Swpaul struct pcn_softc *sc; 95166131Swpaul struct ifnet *ifp; 95266131Swpaul u_int32_t status; 95366131Swpaul 95466131Swpaul sc = arg; 95566131Swpaul ifp = &sc->arpcom.ac_if; 95666131Swpaul 95766131Swpaul /* Supress unwanted interrupts */ 95866131Swpaul if (!(ifp->if_flags & IFF_UP)) { 95966131Swpaul pcn_stop(sc); 96066131Swpaul return; 96166131Swpaul } 96266131Swpaul 96366131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); 96466131Swpaul 96566131Swpaul while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { 96666131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, status); 96766131Swpaul 96866131Swpaul if (status & PCN_CSR_RINT) 96966131Swpaul pcn_rxeof(sc); 97066131Swpaul 97166131Swpaul if (status & PCN_CSR_TINT) 97266131Swpaul pcn_txeof(sc); 97366131Swpaul 97466131Swpaul if (status & PCN_CSR_ERR) { 97566131Swpaul pcn_init(sc); 97666131Swpaul break; 97766131Swpaul } 97866131Swpaul } 97966131Swpaul 98066131Swpaul if (ifp->if_snd.ifq_head != NULL) 98166131Swpaul pcn_start(ifp); 98266131Swpaul 98366131Swpaul return; 98466131Swpaul} 98566131Swpaul 98666131Swpaul/* 98766131Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 98866131Swpaul * pointers to the fragment pointers. 98966131Swpaul */ 99066131Swpaulstatic int pcn_encap(sc, m_head, txidx) 99166131Swpaul struct pcn_softc *sc; 99266131Swpaul struct mbuf *m_head; 99366131Swpaul u_int32_t *txidx; 99466131Swpaul{ 99566131Swpaul struct pcn_tx_desc *f = NULL; 99666131Swpaul struct mbuf *m; 99766131Swpaul int frag, cur, cnt = 0; 99866131Swpaul 99966131Swpaul /* 100066131Swpaul * Start packing the mbufs in this chain into 100166131Swpaul * the fragment pointers. Stop when we run out 100266131Swpaul * of fragments or hit the end of the mbuf chain. 100366131Swpaul */ 100466131Swpaul m = m_head; 100566131Swpaul cur = frag = *txidx; 100666131Swpaul 100766131Swpaul for (m = m_head; m != NULL; m = m->m_next) { 100866131Swpaul if (m->m_len != 0) { 100966131Swpaul if ((PCN_TX_LIST_CNT - 101066131Swpaul (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) 101166131Swpaul return(ENOBUFS); 101266131Swpaul f = &sc->pcn_ldata->pcn_tx_list[frag]; 101366131Swpaul f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; 101466131Swpaul f->pcn_txctl |= PCN_TXCTL_MBO; 101566131Swpaul f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); 101666131Swpaul if (cnt == 0) 101766131Swpaul f->pcn_txctl |= PCN_TXCTL_STP; 101866131Swpaul else 101966131Swpaul f->pcn_txctl |= PCN_TXCTL_OWN; 102066131Swpaul cur = frag; 102166131Swpaul PCN_INC(frag, PCN_TX_LIST_CNT); 102266131Swpaul cnt++; 102366131Swpaul } 102466131Swpaul } 102566131Swpaul 102666131Swpaul if (m != NULL) 102766131Swpaul return(ENOBUFS); 102866131Swpaul 102966131Swpaul sc->pcn_cdata.pcn_tx_chain[cur] = m_head; 103066131Swpaul sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= 103166131Swpaul PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; 103266131Swpaul sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; 103366131Swpaul sc->pcn_cdata.pcn_tx_cnt += cnt; 103466131Swpaul *txidx = frag; 103566131Swpaul 103666131Swpaul return(0); 103766131Swpaul} 103866131Swpaul 103966131Swpaul/* 104066131Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 104166131Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 104266131Swpaul * copy of the pointers since the transmit list fragment pointers are 104366131Swpaul * physical addresses. 104466131Swpaul */ 104566131Swpaulstatic void pcn_start(ifp) 104666131Swpaul struct ifnet *ifp; 104766131Swpaul{ 104866131Swpaul struct pcn_softc *sc; 104966131Swpaul struct mbuf *m_head = NULL; 105066131Swpaul u_int32_t idx; 105166131Swpaul 105266131Swpaul sc = ifp->if_softc; 105366131Swpaul 105467087Swpaul PCN_LOCK(sc); 105567087Swpaul 105667087Swpaul if (!sc->pcn_link) { 105767087Swpaul PCN_UNLOCK(sc); 105866131Swpaul return; 105967087Swpaul } 106066131Swpaul 106166131Swpaul idx = sc->pcn_cdata.pcn_tx_prod; 106266131Swpaul 106367087Swpaul if (ifp->if_flags & IFF_OACTIVE) { 106467087Swpaul PCN_UNLOCK(sc); 106566131Swpaul return; 106667087Swpaul } 106766131Swpaul 106866131Swpaul while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { 106966131Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 107066131Swpaul if (m_head == NULL) 107166131Swpaul break; 107266131Swpaul 107366131Swpaul if (pcn_encap(sc, m_head, &idx)) { 107466131Swpaul IF_PREPEND(&ifp->if_snd, m_head); 107566131Swpaul ifp->if_flags |= IFF_OACTIVE; 107666131Swpaul break; 107766131Swpaul } 107866131Swpaul 107966131Swpaul /* 108066131Swpaul * If there's a BPF listener, bounce a copy of this frame 108166131Swpaul * to him. 108266131Swpaul */ 108366131Swpaul if (ifp->if_bpf) 108466131Swpaul bpf_mtap(ifp, m_head); 108566131Swpaul 108666131Swpaul } 108766131Swpaul 108866131Swpaul /* Transmit */ 108966131Swpaul sc->pcn_cdata.pcn_tx_prod = idx; 109066131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); 109166131Swpaul 109266131Swpaul /* 109366131Swpaul * Set a timeout in case the chip goes out to lunch. 109466131Swpaul */ 109566131Swpaul ifp->if_timer = 5; 109666131Swpaul 109767087Swpaul PCN_UNLOCK(sc); 109867087Swpaul 109966131Swpaul return; 110066131Swpaul} 110166131Swpaul 110268270Swpaulstatic void pcn_setfilt(ifp) 110368270Swpaul struct ifnet *ifp; 110468270Swpaul{ 110568270Swpaul struct pcn_softc *sc; 110668270Swpaul 110768270Swpaul sc = ifp->if_softc; 110868270Swpaul 110968270Swpaul /* If we want promiscuous mode, set the allframes bit. */ 111068270Swpaul if (ifp->if_flags & IFF_PROMISC) { 111168270Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 111268270Swpaul } else { 111368270Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 111468270Swpaul } 111568270Swpaul 111668270Swpaul /* Set the capture broadcast bit to capture broadcast frames. */ 111768270Swpaul if (ifp->if_flags & IFF_BROADCAST) { 111868270Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 111968270Swpaul } else { 112068270Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 112168270Swpaul } 112268270Swpaul 112368270Swpaul return; 112468270Swpaul} 112568270Swpaul 112666131Swpaulstatic void pcn_init(xsc) 112766131Swpaul void *xsc; 112866131Swpaul{ 112966131Swpaul struct pcn_softc *sc = xsc; 113066131Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 113166131Swpaul struct mii_data *mii = NULL; 113266131Swpaul 113367087Swpaul PCN_LOCK(sc); 113466131Swpaul 113566131Swpaul /* 113666131Swpaul * Cancel pending I/O and free all RX/TX buffers. 113766131Swpaul */ 113866131Swpaul pcn_stop(sc); 113966131Swpaul pcn_reset(sc); 114066131Swpaul 114166131Swpaul mii = device_get_softc(sc->pcn_miibus); 114266131Swpaul 114366131Swpaul /* Set MAC address */ 114466131Swpaul pcn_csr_write(sc, PCN_CSR_PAR0, 114566131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 114666131Swpaul pcn_csr_write(sc, PCN_CSR_PAR1, 114766131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 114866131Swpaul pcn_csr_write(sc, PCN_CSR_PAR2, 114966131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 115066131Swpaul 115166131Swpaul /* Init circular RX list. */ 115266131Swpaul if (pcn_list_rx_init(sc) == ENOBUFS) { 115366131Swpaul printf("pcn%d: initialization failed: no " 115466131Swpaul "memory for rx buffers\n", sc->pcn_unit); 115566131Swpaul pcn_stop(sc); 115667087Swpaul PCN_UNLOCK(sc); 115766131Swpaul return; 115866131Swpaul } 115966131Swpaul 116066131Swpaul /* 116166131Swpaul * Init tx descriptors. 116266131Swpaul */ 116366131Swpaul pcn_list_tx_init(sc); 116466131Swpaul 116566131Swpaul /* Set up the mode register. */ 116666131Swpaul pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); 116766131Swpaul 116868270Swpaul /* Set up RX filter. */ 116968270Swpaul pcn_setfilt(ifp); 117066131Swpaul 117166131Swpaul /* 117266131Swpaul * Load the multicast filter. 117366131Swpaul */ 117466131Swpaul pcn_setmulti(sc); 117566131Swpaul 117666131Swpaul /* 117766131Swpaul * Load the addresses of the RX and TX lists. 117866131Swpaul */ 117966131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR0, 118066131Swpaul vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); 118166131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR1, 118266131Swpaul (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); 118366131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR0, 118466131Swpaul vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); 118566131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR1, 118666131Swpaul (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); 118766131Swpaul 118866131Swpaul /* Set the RX and TX ring sizes. */ 118966131Swpaul pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); 119066131Swpaul pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); 119166131Swpaul 119266131Swpaul /* We're not using the initialization block. */ 119366131Swpaul pcn_csr_write(sc, PCN_CSR_IAB1, 0); 119466131Swpaul 119566131Swpaul /* Enable fast suspend mode. */ 119666131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); 119766131Swpaul 119866131Swpaul /* 119966131Swpaul * Enable burst read and write. Also set the no underflow 120066131Swpaul * bit. This will avoid transmit underruns in certain 120166210Swpaul * conditions while still providing decent performance. 120266131Swpaul */ 120366131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| 120466131Swpaul PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); 120566131Swpaul 120666131Swpaul /* Enable graceful recovery from underflow. */ 120766131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); 120866131Swpaul 120966131Swpaul /* Enable auto-padding of short TX frames. */ 121066131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); 121166131Swpaul 121266131Swpaul /* Disable MII autoneg (we handle this ourselves). */ 121366131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); 121466131Swpaul 121566131Swpaul if (sc->pcn_type == Am79C978) 121666131Swpaul pcn_bcr_write(sc, PCN_BCR_PHYSEL, 121766131Swpaul PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); 121866131Swpaul 121966131Swpaul /* Enable interrupts and start the controller running. */ 122066131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); 122166131Swpaul 122266131Swpaul mii_mediachg(mii); 122366131Swpaul 122466131Swpaul ifp->if_flags |= IFF_RUNNING; 122566131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 122666131Swpaul 122766131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 122867087Swpaul PCN_UNLOCK(sc); 122966131Swpaul 123066131Swpaul return; 123166131Swpaul} 123266131Swpaul 123366131Swpaul/* 123466131Swpaul * Set media options. 123566131Swpaul */ 123666131Swpaulstatic int pcn_ifmedia_upd(ifp) 123766131Swpaul struct ifnet *ifp; 123866131Swpaul{ 123966131Swpaul struct pcn_softc *sc; 124066131Swpaul struct mii_data *mii; 124166131Swpaul 124266131Swpaul sc = ifp->if_softc; 124366131Swpaul mii = device_get_softc(sc->pcn_miibus); 124466131Swpaul 124566131Swpaul sc->pcn_link = 0; 124666131Swpaul if (mii->mii_instance) { 124766131Swpaul struct mii_softc *miisc; 124872012Sphk LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 124966131Swpaul mii_phy_reset(miisc); 125066131Swpaul } 125166131Swpaul mii_mediachg(mii); 125266131Swpaul 125366131Swpaul return(0); 125466131Swpaul} 125566131Swpaul 125666131Swpaul/* 125766131Swpaul * Report current media status. 125866131Swpaul */ 125966131Swpaulstatic void pcn_ifmedia_sts(ifp, ifmr) 126066131Swpaul struct ifnet *ifp; 126166131Swpaul struct ifmediareq *ifmr; 126266131Swpaul{ 126366131Swpaul struct pcn_softc *sc; 126466131Swpaul struct mii_data *mii; 126566131Swpaul 126666131Swpaul sc = ifp->if_softc; 126766131Swpaul 126866131Swpaul mii = device_get_softc(sc->pcn_miibus); 126966131Swpaul mii_pollstat(mii); 127066131Swpaul ifmr->ifm_active = mii->mii_media_active; 127166131Swpaul ifmr->ifm_status = mii->mii_media_status; 127266131Swpaul 127366131Swpaul return; 127466131Swpaul} 127566131Swpaul 127666131Swpaulstatic int pcn_ioctl(ifp, command, data) 127766131Swpaul struct ifnet *ifp; 127866131Swpaul u_long command; 127966131Swpaul caddr_t data; 128066131Swpaul{ 128166131Swpaul struct pcn_softc *sc = ifp->if_softc; 128266131Swpaul struct ifreq *ifr = (struct ifreq *) data; 128366131Swpaul struct mii_data *mii = NULL; 128467087Swpaul int error = 0; 128566131Swpaul 128667087Swpaul PCN_LOCK(sc); 128766131Swpaul 128866131Swpaul switch(command) { 128966131Swpaul case SIOCSIFADDR: 129066131Swpaul case SIOCGIFADDR: 129166131Swpaul case SIOCSIFMTU: 129266131Swpaul error = ether_ioctl(ifp, command, data); 129366131Swpaul break; 129466131Swpaul case SIOCSIFFLAGS: 129566131Swpaul if (ifp->if_flags & IFF_UP) { 129666131Swpaul if (ifp->if_flags & IFF_RUNNING && 129766131Swpaul ifp->if_flags & IFF_PROMISC && 129866131Swpaul !(sc->pcn_if_flags & IFF_PROMISC)) { 129966131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 130066131Swpaul PCN_EXTCTL1_SPND); 130168270Swpaul pcn_setfilt(ifp); 130266131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 130366131Swpaul PCN_EXTCTL1_SPND); 130466771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 130566771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 130666131Swpaul } else if (ifp->if_flags & IFF_RUNNING && 130766131Swpaul !(ifp->if_flags & IFF_PROMISC) && 130866131Swpaul sc->pcn_if_flags & IFF_PROMISC) { 130966131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 131066131Swpaul PCN_EXTCTL1_SPND); 131168270Swpaul pcn_setfilt(ifp); 131266131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 131366131Swpaul PCN_EXTCTL1_SPND); 131466771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 131566771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 131666131Swpaul } else if (!(ifp->if_flags & IFF_RUNNING)) 131766131Swpaul pcn_init(sc); 131866131Swpaul } else { 131966131Swpaul if (ifp->if_flags & IFF_RUNNING) 132066131Swpaul pcn_stop(sc); 132166131Swpaul } 132266131Swpaul sc->pcn_if_flags = ifp->if_flags; 132366131Swpaul error = 0; 132466131Swpaul break; 132566131Swpaul case SIOCADDMULTI: 132666131Swpaul case SIOCDELMULTI: 132766131Swpaul pcn_setmulti(sc); 132866131Swpaul error = 0; 132966131Swpaul break; 133066131Swpaul case SIOCGIFMEDIA: 133166131Swpaul case SIOCSIFMEDIA: 133266131Swpaul mii = device_get_softc(sc->pcn_miibus); 133366131Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 133466131Swpaul break; 133566131Swpaul default: 133666131Swpaul error = EINVAL; 133766131Swpaul break; 133866131Swpaul } 133966131Swpaul 134067087Swpaul PCN_UNLOCK(sc); 134166131Swpaul 134266131Swpaul return(error); 134366131Swpaul} 134466131Swpaul 134566131Swpaulstatic void pcn_watchdog(ifp) 134666131Swpaul struct ifnet *ifp; 134766131Swpaul{ 134866131Swpaul struct pcn_softc *sc; 134966131Swpaul 135066131Swpaul sc = ifp->if_softc; 135166131Swpaul 135267087Swpaul PCN_LOCK(sc); 135367087Swpaul 135466131Swpaul ifp->if_oerrors++; 135566131Swpaul printf("pcn%d: watchdog timeout\n", sc->pcn_unit); 135666131Swpaul 135766131Swpaul pcn_stop(sc); 135866131Swpaul pcn_reset(sc); 135966131Swpaul pcn_init(sc); 136066131Swpaul 136166131Swpaul if (ifp->if_snd.ifq_head != NULL) 136266131Swpaul pcn_start(ifp); 136366131Swpaul 136467087Swpaul PCN_UNLOCK(sc); 136567087Swpaul 136666131Swpaul return; 136766131Swpaul} 136866131Swpaul 136966131Swpaul/* 137066131Swpaul * Stop the adapter and free any mbufs allocated to the 137166131Swpaul * RX and TX lists. 137266131Swpaul */ 137366131Swpaulstatic void pcn_stop(sc) 137466131Swpaul struct pcn_softc *sc; 137566131Swpaul{ 137666131Swpaul register int i; 137766131Swpaul struct ifnet *ifp; 137866131Swpaul 137966131Swpaul ifp = &sc->arpcom.ac_if; 138067087Swpaul PCN_LOCK(sc); 138166131Swpaul ifp->if_timer = 0; 138266131Swpaul 138366131Swpaul untimeout(pcn_tick, sc, sc->pcn_stat_ch); 138466131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); 138566131Swpaul sc->pcn_link = 0; 138666131Swpaul 138766131Swpaul /* 138866131Swpaul * Free data in the RX lists. 138966131Swpaul */ 139066131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 139166131Swpaul if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { 139266131Swpaul m_freem(sc->pcn_cdata.pcn_rx_chain[i]); 139366131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 139466131Swpaul } 139566131Swpaul } 139666131Swpaul bzero((char *)&sc->pcn_ldata->pcn_rx_list, 139766131Swpaul sizeof(sc->pcn_ldata->pcn_rx_list)); 139866131Swpaul 139966131Swpaul /* 140066131Swpaul * Free the TX list buffers. 140166131Swpaul */ 140266131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 140366131Swpaul if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { 140466131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[i]); 140566131Swpaul sc->pcn_cdata.pcn_tx_chain[i] = NULL; 140666131Swpaul } 140766131Swpaul } 140866131Swpaul 140966131Swpaul bzero((char *)&sc->pcn_ldata->pcn_tx_list, 141066131Swpaul sizeof(sc->pcn_ldata->pcn_tx_list)); 141166131Swpaul 141266131Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 141367087Swpaul PCN_UNLOCK(sc); 141466131Swpaul 141566131Swpaul return; 141666131Swpaul} 141766131Swpaul 141866131Swpaul/* 141966131Swpaul * Stop all chip I/O so that the kernel's probe routines don't 142066131Swpaul * get confused by errant DMAs when rebooting. 142166131Swpaul */ 142266131Swpaulstatic void pcn_shutdown(dev) 142366131Swpaul device_t dev; 142466131Swpaul{ 142566131Swpaul struct pcn_softc *sc; 142666131Swpaul 142766131Swpaul sc = device_get_softc(dev); 142866131Swpaul 142967087Swpaul PCN_LOCK(sc); 143066131Swpaul pcn_reset(sc); 143166131Swpaul pcn_stop(sc); 143267087Swpaul PCN_UNLOCK(sc); 143366131Swpaul 143466131Swpaul return; 143566131Swpaul} 1436