if_pcn.c revision 67089
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 67089 2000-10-13 18:35:49Z wpaul $ 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/clock.h> /* for DELAY */ 7766131Swpaul#include <machine/bus_pio.h> 7866131Swpaul#include <machine/bus_memio.h> 7966131Swpaul#include <machine/bus.h> 8066131Swpaul#include <machine/resource.h> 8166131Swpaul#include <sys/bus.h> 8266131Swpaul#include <sys/rman.h> 8366131Swpaul 8466131Swpaul#include <dev/mii/mii.h> 8566131Swpaul#include <dev/mii/miivar.h> 8666131Swpaul 8766131Swpaul#include <pci/pcireg.h> 8866131Swpaul#include <pci/pcivar.h> 8966131Swpaul 9066131Swpaul#define PCN_USEIOSPACE 9166131Swpaul 9266131Swpaul#include <pci/if_pcnreg.h> 9366131Swpaul 9466131SwpaulMODULE_DEPEND(pcn, miibus, 1, 1, 1); 9566131Swpaul 9666131Swpaul/* "controller miibus0" required. See GENERIC if you get errors here. */ 9766131Swpaul#include "miibus_if.h" 9866131Swpaul 9966131Swpaul#ifndef lint 10066131Swpaulstatic const char rcsid[] = 10166131Swpaul "$FreeBSD: head/sys/pci/if_pcn.c 67089 2000-10-13 18:35:49Z wpaul $"; 10266131Swpaul#endif 10366131Swpaul 10466131Swpaul/* 10566131Swpaul * Various supported device vendors/types and their names. 10666131Swpaul */ 10766131Swpaulstatic struct pcn_type pcn_devs[] = { 10866131Swpaul { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, 10966131Swpaul { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, 11066131Swpaul { 0, 0, NULL } 11166131Swpaul}; 11266131Swpaul 11366131Swpaulstatic u_int32_t pcn_csr_read __P((struct pcn_softc *, int)); 11466131Swpaulstatic void pcn_csr_write __P((struct pcn_softc *, int, int)); 11566131Swpaulstatic u_int32_t pcn_bcr_read __P((struct pcn_softc *, int)); 11666131Swpaulstatic void pcn_bcr_write __P((struct pcn_softc *, int, int)); 11766131Swpaul 11866131Swpaulstatic int pcn_probe __P((device_t)); 11966131Swpaulstatic int pcn_attach __P((device_t)); 12066131Swpaulstatic int pcn_detach __P((device_t)); 12166131Swpaul 12266131Swpaulstatic int pcn_newbuf __P((struct pcn_softc *, int, struct mbuf *)); 12366131Swpaulstatic int pcn_encap __P((struct pcn_softc *, 12466131Swpaul struct mbuf *, u_int32_t *)); 12566131Swpaulstatic void pcn_rxeof __P((struct pcn_softc *)); 12666131Swpaulstatic void pcn_txeof __P((struct pcn_softc *)); 12766131Swpaulstatic void pcn_intr __P((void *)); 12866131Swpaulstatic void pcn_tick __P((void *)); 12966131Swpaulstatic void pcn_start __P((struct ifnet *)); 13066131Swpaulstatic int pcn_ioctl __P((struct ifnet *, u_long, caddr_t)); 13166131Swpaulstatic void pcn_init __P((void *)); 13266131Swpaulstatic void pcn_stop __P((struct pcn_softc *)); 13366131Swpaulstatic void pcn_watchdog __P((struct ifnet *)); 13466131Swpaulstatic void pcn_shutdown __P((device_t)); 13566131Swpaulstatic int pcn_ifmedia_upd __P((struct ifnet *)); 13666131Swpaulstatic void pcn_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 13766131Swpaul 13866131Swpaulstatic int pcn_miibus_readreg __P((device_t, int, int)); 13966131Swpaulstatic int pcn_miibus_writereg __P((device_t, int, int, int)); 14066131Swpaulstatic void pcn_miibus_statchg __P((device_t)); 14166131Swpaul 14266131Swpaulstatic void pcn_setmulti __P((struct pcn_softc *)); 14366131Swpaulstatic u_int32_t pcn_crc __P((caddr_t)); 14466131Swpaulstatic void pcn_reset __P((struct pcn_softc *)); 14566131Swpaulstatic int pcn_list_rx_init __P((struct pcn_softc *)); 14666131Swpaulstatic int pcn_list_tx_init __P((struct pcn_softc *)); 14766131Swpaul 14866131Swpaul#ifdef PCN_USEIOSPACE 14966131Swpaul#define PCN_RES SYS_RES_IOPORT 15066131Swpaul#define PCN_RID PCN_PCI_LOIO 15166131Swpaul#else 15266131Swpaul#define PCN_RES SYS_RES_MEMORY 15366131Swpaul#define PCN_RID PCN_PCI_LOMEM 15466131Swpaul#endif 15566131Swpaul 15666131Swpaulstatic device_method_t pcn_methods[] = { 15766131Swpaul /* Device interface */ 15866131Swpaul DEVMETHOD(device_probe, pcn_probe), 15966131Swpaul DEVMETHOD(device_attach, pcn_attach), 16066131Swpaul DEVMETHOD(device_detach, pcn_detach), 16166131Swpaul DEVMETHOD(device_shutdown, pcn_shutdown), 16266131Swpaul 16366131Swpaul /* bus interface */ 16466131Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 16566131Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 16666131Swpaul 16766131Swpaul /* MII interface */ 16866131Swpaul DEVMETHOD(miibus_readreg, pcn_miibus_readreg), 16966131Swpaul DEVMETHOD(miibus_writereg, pcn_miibus_writereg), 17066131Swpaul DEVMETHOD(miibus_statchg, pcn_miibus_statchg), 17166131Swpaul 17266131Swpaul { 0, 0 } 17366131Swpaul}; 17466131Swpaul 17566131Swpaulstatic driver_t pcn_driver = { 17666131Swpaul "pcn", 17766131Swpaul pcn_methods, 17866131Swpaul sizeof(struct pcn_softc) 17966131Swpaul}; 18066131Swpaul 18166131Swpaulstatic devclass_t pcn_devclass; 18266131Swpaul 18366131SwpaulDRIVER_MODULE(if_pcn, pci, pcn_driver, pcn_devclass, 0, 0); 18466131SwpaulDRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); 18566131Swpaul 18666131Swpaul#define PCN_CSR_SETBIT(sc, reg, x) \ 18766131Swpaul pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) 18866131Swpaul 18966131Swpaul#define PCN_CSR_CLRBIT(sc, reg, x) \ 19066131Swpaul pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) 19166131Swpaul 19266131Swpaul#define PCN_BCR_SETBIT(sc, reg, x) \ 19366131Swpaul pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) 19466131Swpaul 19566131Swpaul#define PCN_BCR_CLRBIT(sc, reg, x) \ 19666131Swpaul pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) 19766131Swpaul 19866131Swpaulstatic u_int32_t pcn_csr_read(sc, reg) 19966131Swpaul struct pcn_softc *sc; 20066131Swpaul int reg; 20166131Swpaul{ 20266131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 20366131Swpaul return(CSR_READ_4(sc, PCN_IO32_RDP)); 20466131Swpaul} 20566131Swpaul 20666131Swpaulstatic void pcn_csr_write(sc, reg, val) 20766131Swpaul struct pcn_softc *sc; 20866131Swpaul int reg; 20966131Swpaul{ 21066131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 21166131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, val); 21266131Swpaul return; 21366131Swpaul} 21466131Swpaul 21566131Swpaulstatic u_int32_t pcn_bcr_read(sc, reg) 21666131Swpaul struct pcn_softc *sc; 21766131Swpaul int reg; 21866131Swpaul{ 21966131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 22066131Swpaul return(CSR_READ_4(sc, PCN_IO32_BDP)); 22166131Swpaul} 22266131Swpaul 22366131Swpaulstatic void pcn_bcr_write(sc, reg, val) 22466131Swpaul struct pcn_softc *sc; 22566131Swpaul int reg; 22666131Swpaul{ 22766131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, reg); 22866131Swpaul CSR_WRITE_4(sc, PCN_IO32_BDP, val); 22966131Swpaul return; 23066131Swpaul} 23166131Swpaul 23266131Swpaulstatic int pcn_miibus_readreg(dev, phy, reg) 23366131Swpaul device_t dev; 23466131Swpaul int phy, reg; 23566131Swpaul{ 23666131Swpaul struct pcn_softc *sc; 23766131Swpaul int val; 23866131Swpaul 23966131Swpaul sc = device_get_softc(dev); 24066131Swpaul 24166208Swpaul if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr) 24266131Swpaul return(0); 24366131Swpaul 24466131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 24566131Swpaul val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; 24666131Swpaul if (val == 0xFFFF) 24766131Swpaul return(0); 24866208Swpaul 24966208Swpaul sc->pcn_phyaddr = phy; 25066208Swpaul 25166131Swpaul return(val); 25266131Swpaul} 25366131Swpaul 25466131Swpaulstatic int pcn_miibus_writereg(dev, phy, reg, data) 25566131Swpaul device_t dev; 25666131Swpaul int phy, reg, data; 25766131Swpaul{ 25866131Swpaul struct pcn_softc *sc; 25966131Swpaul 26066131Swpaul sc = device_get_softc(dev); 26166131Swpaul 26266131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); 26366131Swpaul pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); 26466131Swpaul 26566131Swpaul return(0); 26666131Swpaul} 26766131Swpaul 26866131Swpaulstatic void pcn_miibus_statchg(dev) 26966131Swpaul device_t dev; 27066131Swpaul{ 27166131Swpaul struct pcn_softc *sc; 27266131Swpaul struct mii_data *mii; 27366131Swpaul 27466131Swpaul sc = device_get_softc(dev); 27566131Swpaul mii = device_get_softc(sc->pcn_miibus); 27666131Swpaul 27766131Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 27866131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 27966131Swpaul } else { 28066131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); 28166131Swpaul } 28266131Swpaul 28366131Swpaul return; 28466131Swpaul} 28566131Swpaul 28666131Swpaul#define DC_POLY 0xEDB88320 28766131Swpaul 28866131Swpaulstatic u_int32_t pcn_crc(addr) 28966131Swpaul caddr_t addr; 29066131Swpaul{ 29166131Swpaul u_int32_t idx, bit, data, crc; 29266131Swpaul 29366131Swpaul /* Compute CRC for the address value. */ 29466131Swpaul crc = 0xFFFFFFFF; /* initial value */ 29566131Swpaul 29666131Swpaul for (idx = 0; idx < 6; idx++) { 29766131Swpaul for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) 29866131Swpaul crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); 29966131Swpaul } 30066131Swpaul 30166131Swpaul return ((crc >> 26) & 0x3F); 30266131Swpaul} 30366131Swpaul 30466131Swpaulstatic void pcn_setmulti(sc) 30566131Swpaul struct pcn_softc *sc; 30666131Swpaul{ 30766131Swpaul struct ifnet *ifp; 30866131Swpaul struct ifmultiaddr *ifma; 30966131Swpaul u_int32_t h, i; 31066131Swpaul u_int16_t hashes[4] = { 0, 0, 0, 0 }; 31166131Swpaul 31266131Swpaul ifp = &sc->arpcom.ac_if; 31366131Swpaul 31466131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 31566131Swpaul 31666131Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 31766131Swpaul for (i = 0; i < 4; i++) 31866131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); 31966131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 32066131Swpaul return; 32166131Swpaul } 32266131Swpaul 32366131Swpaul /* first, zot all the existing hash bits */ 32466131Swpaul for (i = 0; i < 4; i++) 32566131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); 32666131Swpaul 32766131Swpaul /* now program new ones */ 32866131Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 32966131Swpaul ifma = ifma->ifma_link.le_next) { 33066131Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 33166131Swpaul continue; 33266131Swpaul h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 33366131Swpaul hashes[h >> 4] |= 1 << (h & 0xF); 33466131Swpaul } 33566131Swpaul 33666131Swpaul for (i = 0; i < 4; i++) 33766131Swpaul pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); 33866131Swpaul 33966131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); 34066131Swpaul 34166131Swpaul return; 34266131Swpaul} 34366131Swpaul 34466131Swpaulstatic void pcn_reset(sc) 34566131Swpaul struct pcn_softc *sc; 34666131Swpaul{ 34766131Swpaul /* 34866131Swpaul * Issue a reset by reading from the RESET register. 34966131Swpaul * Note that we don't know if the chip is operating in 35066131Swpaul * 16-bit or 32-bit mode at this point, so we attempt 35166131Swpaul * to reset the chip both ways. If one fails, the other 35266131Swpaul * will succeed. 35366131Swpaul */ 35466131Swpaul CSR_READ_2(sc, PCN_IO16_RESET); 35566131Swpaul CSR_READ_4(sc, PCN_IO32_RESET); 35666131Swpaul 35766131Swpaul /* Wait a little while for the chip to get its brains in order. */ 35866131Swpaul DELAY(1000); 35966131Swpaul 36066131Swpaul /* Select 32-bit (DWIO) mode */ 36166131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, 0); 36266131Swpaul 36366131Swpaul /* Select software style 3. */ 36466131Swpaul pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); 36566131Swpaul 36666131Swpaul return; 36766131Swpaul} 36866131Swpaul 36966131Swpaul/* 37066131Swpaul * Probe for an AMD chip. Check the PCI vendor and device 37166131Swpaul * IDs against our list and return a device name if we find a match. 37266131Swpaul */ 37366131Swpaulstatic int pcn_probe(dev) 37466131Swpaul device_t dev; 37566131Swpaul{ 37666131Swpaul struct pcn_type *t; 37766131Swpaul struct pcn_softc *sc; 37866131Swpaul int rid; 37966131Swpaul u_int32_t chip_id; 38066131Swpaul 38166131Swpaul t = pcn_devs; 38266131Swpaul sc = device_get_softc(dev); 38366131Swpaul 38466131Swpaul while(t->pcn_name != NULL) { 38566131Swpaul if ((pci_get_vendor(dev) == t->pcn_vid) && 38666131Swpaul (pci_get_device(dev) == t->pcn_did)) { 38766131Swpaul /* 38866131Swpaul * Temporarily map the I/O space 38966131Swpaul * so we can read the chip ID register. 39066131Swpaul */ 39166131Swpaul rid = PCN_RID; 39266131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 39366131Swpaul 0, ~0, 1, RF_ACTIVE); 39466131Swpaul if (sc->pcn_res == NULL) { 39566131Swpaul device_printf(dev, 39666131Swpaul "couldn't map ports/memory\n"); 39766131Swpaul return(ENXIO); 39866131Swpaul } 39966131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 40066131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 40167089Swpaul mtx_init(&sc->pcn_mtx, 40267089Swpaul device_get_nameunit(dev), MTX_DEF); 40367087Swpaul PCN_LOCK(sc); 40466131Swpaul pcn_reset(sc); 40566131Swpaul chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); 40666131Swpaul chip_id <<= 16; 40766131Swpaul chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); 40866131Swpaul bus_release_resource(dev, PCN_RES, 40966131Swpaul PCN_RID, sc->pcn_res); 41067087Swpaul PCN_UNLOCK(sc); 41167087Swpaul mtx_destroy(&sc->pcn_mtx); 41266131Swpaul chip_id >>= 12; 41366131Swpaul sc->pcn_type = chip_id & PART_MASK; 41466131Swpaul switch(sc->pcn_type) { 41566131Swpaul case Am79C971: 41666131Swpaul case Am79C972: 41766131Swpaul case Am79C973: 41866690Swpaul case Am79C975: 41966592Swpaul case Am79C976: 42066131Swpaul case Am79C978: 42166131Swpaul break; 42266131Swpaul default: 42366131Swpaul return(ENXIO); 42466131Swpaul break; 42566131Swpaul } 42666131Swpaul device_set_desc(dev, t->pcn_name); 42766131Swpaul return(0); 42866131Swpaul } 42966131Swpaul t++; 43066131Swpaul } 43166131Swpaul 43266131Swpaul return(ENXIO); 43366131Swpaul} 43466131Swpaul 43566131Swpaul/* 43666131Swpaul * Attach the interface. Allocate softc structures, do ifmedia 43766131Swpaul * setup and ethernet/BPF attach. 43866131Swpaul */ 43966131Swpaulstatic int pcn_attach(dev) 44066131Swpaul device_t dev; 44166131Swpaul{ 44266131Swpaul u_int32_t eaddr[2]; 44366131Swpaul u_int32_t command; 44466131Swpaul struct pcn_softc *sc; 44566131Swpaul struct ifnet *ifp; 44666131Swpaul int unit, error = 0, rid; 44766131Swpaul 44866131Swpaul sc = device_get_softc(dev); 44966131Swpaul unit = device_get_unit(dev); 45066131Swpaul 45166131Swpaul /* 45266131Swpaul * Handle power management nonsense. 45366131Swpaul */ 45466131Swpaul 45566131Swpaul command = pci_read_config(dev, PCN_PCI_CAPID, 4) & 0x000000FF; 45666131Swpaul if (command == 0x01) { 45766131Swpaul 45866131Swpaul command = pci_read_config(dev, PCN_PCI_PWRMGMTCTRL, 4); 45966131Swpaul if (command & PCN_PSTATE_MASK) { 46066131Swpaul u_int32_t iobase, membase, irq; 46166131Swpaul 46266131Swpaul /* Save important PCI config data. */ 46366131Swpaul iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); 46466131Swpaul membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); 46566131Swpaul irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); 46666131Swpaul 46766131Swpaul /* Reset the power state. */ 46866131Swpaul printf("pcn%d: chip is in D%d power mode " 46966131Swpaul "-- setting to D0\n", unit, command & PCN_PSTATE_MASK); 47066131Swpaul command &= 0xFFFFFFFC; 47166131Swpaul pci_write_config(dev, PCN_PCI_PWRMGMTCTRL, command, 4); 47266131Swpaul 47366131Swpaul /* Restore PCI config data. */ 47466131Swpaul pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); 47566131Swpaul pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); 47666131Swpaul pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); 47766131Swpaul } 47866131Swpaul } 47966131Swpaul 48066131Swpaul /* 48166131Swpaul * Map control/status registers. 48266131Swpaul */ 48366131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 48466131Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 48566131Swpaul pci_write_config(dev, PCIR_COMMAND, command, 4); 48666131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 48766131Swpaul 48866131Swpaul#ifdef PCN_USEIOSPACE 48966131Swpaul if (!(command & PCIM_CMD_PORTEN)) { 49066131Swpaul printf("pcn%d: failed to enable I/O ports!\n", unit); 49166131Swpaul error = ENXIO;; 49266131Swpaul goto fail; 49366131Swpaul } 49466131Swpaul#else 49566131Swpaul if (!(command & PCIM_CMD_MEMEN)) { 49666131Swpaul printf("pcn%d: failed to enable memory mapping!\n", unit); 49766131Swpaul error = ENXIO;; 49866131Swpaul goto fail; 49966131Swpaul } 50066131Swpaul#endif 50166131Swpaul 50266131Swpaul rid = PCN_RID; 50366131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 50466131Swpaul 0, ~0, 1, RF_ACTIVE); 50566131Swpaul 50666131Swpaul if (sc->pcn_res == NULL) { 50766131Swpaul printf("pcn%d: couldn't map ports/memory\n", unit); 50866131Swpaul error = ENXIO; 50966131Swpaul goto fail; 51066131Swpaul } 51166131Swpaul 51266131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 51366131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 51466131Swpaul 51566131Swpaul /* Allocate interrupt */ 51666131Swpaul rid = 0; 51766131Swpaul sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 51866131Swpaul RF_SHAREABLE | RF_ACTIVE); 51966131Swpaul 52066131Swpaul if (sc->pcn_irq == NULL) { 52166131Swpaul printf("pcn%d: couldn't map interrupt\n", unit); 52266131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 52366131Swpaul error = ENXIO; 52466131Swpaul goto fail; 52566131Swpaul } 52666131Swpaul 52766131Swpaul error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, 52866131Swpaul pcn_intr, sc, &sc->pcn_intrhand); 52966131Swpaul 53066131Swpaul if (error) { 53166131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res); 53266131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 53366131Swpaul printf("pcn%d: couldn't set up irq\n", unit); 53466131Swpaul goto fail; 53566131Swpaul } 53666131Swpaul 53767087Swpaul /* Initialize our mutex. */ 53867089Swpaul mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_DEF); 53967087Swpaul PCN_LOCK(sc); 54067087Swpaul 54166131Swpaul /* Reset the adapter. */ 54266131Swpaul pcn_reset(sc); 54366131Swpaul 54466131Swpaul /* 54566131Swpaul * Get station address from the EEPROM. 54666131Swpaul */ 54766131Swpaul eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); 54866131Swpaul eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); 54966131Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 55066131Swpaul 55166131Swpaul /* 55266131Swpaul * An AMD chip was detected. Inform the world. 55366131Swpaul */ 55466131Swpaul printf("pcn%d: Ethernet address: %6D\n", unit, 55566131Swpaul sc->arpcom.ac_enaddr, ":"); 55666131Swpaul 55766131Swpaul sc->pcn_unit = unit; 55866131Swpaul callout_handle_init(&sc->pcn_stat_ch); 55966131Swpaul 56066131Swpaul sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, 56166131Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 56266131Swpaul 56366131Swpaul if (sc->pcn_ldata == NULL) { 56466131Swpaul printf("pcn%d: no memory for list buffers!\n", unit); 56566131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 56666131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 56766131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 56866131Swpaul error = ENXIO; 56966131Swpaul goto fail; 57066131Swpaul } 57166131Swpaul bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); 57266131Swpaul 57366131Swpaul ifp = &sc->arpcom.ac_if; 57466131Swpaul ifp->if_softc = sc; 57566131Swpaul ifp->if_unit = unit; 57666131Swpaul ifp->if_name = "pcn"; 57766131Swpaul ifp->if_mtu = ETHERMTU; 57866131Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 57966131Swpaul ifp->if_ioctl = pcn_ioctl; 58066131Swpaul ifp->if_output = ether_output; 58166131Swpaul ifp->if_start = pcn_start; 58266131Swpaul ifp->if_watchdog = pcn_watchdog; 58366131Swpaul ifp->if_init = pcn_init; 58466131Swpaul ifp->if_baudrate = 10000000; 58566131Swpaul ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; 58666131Swpaul 58766131Swpaul /* 58866131Swpaul * Do MII setup. 58966131Swpaul */ 59066131Swpaul if (mii_phy_probe(dev, &sc->pcn_miibus, 59166131Swpaul pcn_ifmedia_upd, pcn_ifmedia_sts)) { 59266131Swpaul printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); 59366131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 59466131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 59566131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 59666131Swpaul error = ENXIO; 59766131Swpaul goto fail; 59866131Swpaul } 59966131Swpaul 60066131Swpaul /* 60166131Swpaul * Call MI attach routine. 60266131Swpaul */ 60366131Swpaul ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 60466131Swpaul callout_handle_init(&sc->pcn_stat_ch); 60567087Swpaul PCN_UNLOCK(sc); 60667087Swpaul return(0); 60766131Swpaul 60866131Swpaulfail: 60967087Swpaul PCN_UNLOCK(sc); 61067087Swpaul mtx_destroy(&sc->pcn_mtx); 61167087Swpaul 61266131Swpaul return(error); 61366131Swpaul} 61466131Swpaul 61566131Swpaulstatic int pcn_detach(dev) 61666131Swpaul device_t dev; 61766131Swpaul{ 61866131Swpaul struct pcn_softc *sc; 61966131Swpaul struct ifnet *ifp; 62066131Swpaul 62166131Swpaul sc = device_get_softc(dev); 62266131Swpaul ifp = &sc->arpcom.ac_if; 62366131Swpaul 62467087Swpaul PCN_LOCK(sc); 62567087Swpaul 62666131Swpaul pcn_reset(sc); 62766131Swpaul pcn_stop(sc); 62866131Swpaul ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 62966131Swpaul 63066131Swpaul if (sc->pcn_miibus != NULL) { 63166131Swpaul bus_generic_detach(dev); 63266131Swpaul device_delete_child(dev, sc->pcn_miibus); 63366131Swpaul } 63466131Swpaul 63566131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 63666131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 63766131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 63866131Swpaul 63966131Swpaul contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); 64067087Swpaul PCN_UNLOCK(sc); 64166131Swpaul 64267087Swpaul mtx_destroy(&sc->pcn_mtx); 64366131Swpaul 64466131Swpaul return(0); 64566131Swpaul} 64666131Swpaul 64766131Swpaul/* 64866131Swpaul * Initialize the transmit descriptors. 64966131Swpaul */ 65066131Swpaulstatic int pcn_list_tx_init(sc) 65166131Swpaul struct pcn_softc *sc; 65266131Swpaul{ 65366131Swpaul struct pcn_list_data *ld; 65466131Swpaul struct pcn_ring_data *cd; 65566131Swpaul int i; 65666131Swpaul 65766131Swpaul cd = &sc->pcn_cdata; 65866131Swpaul ld = sc->pcn_ldata; 65966131Swpaul 66066131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 66166131Swpaul cd->pcn_tx_chain[i] = NULL; 66266131Swpaul ld->pcn_tx_list[i].pcn_tbaddr = 0; 66366131Swpaul ld->pcn_tx_list[i].pcn_txctl = 0; 66466131Swpaul ld->pcn_tx_list[i].pcn_txstat = 0; 66566131Swpaul } 66666131Swpaul 66766131Swpaul cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; 66866131Swpaul 66966131Swpaul return(0); 67066131Swpaul} 67166131Swpaul 67266131Swpaul 67366131Swpaul/* 67466131Swpaul * Initialize the RX descriptors and allocate mbufs for them. 67566131Swpaul */ 67666131Swpaulstatic int pcn_list_rx_init(sc) 67766131Swpaul struct pcn_softc *sc; 67866131Swpaul{ 67966131Swpaul struct pcn_list_data *ld; 68066131Swpaul struct pcn_ring_data *cd; 68166131Swpaul int i; 68266131Swpaul 68366131Swpaul ld = sc->pcn_ldata; 68466131Swpaul cd = &sc->pcn_cdata; 68566131Swpaul 68666131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 68766131Swpaul if (pcn_newbuf(sc, i, NULL) == ENOBUFS) 68866131Swpaul return(ENOBUFS); 68966131Swpaul } 69066131Swpaul 69166131Swpaul cd->pcn_rx_prod = 0; 69266131Swpaul 69366131Swpaul return(0); 69466131Swpaul} 69566131Swpaul 69666131Swpaul/* 69766131Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 69866131Swpaul */ 69966131Swpaulstatic int pcn_newbuf(sc, idx, m) 70066131Swpaul struct pcn_softc *sc; 70166131Swpaul int idx; 70266131Swpaul struct mbuf *m; 70366131Swpaul{ 70466131Swpaul struct mbuf *m_new = NULL; 70566131Swpaul struct pcn_rx_desc *c; 70666131Swpaul 70766131Swpaul c = &sc->pcn_ldata->pcn_rx_list[idx]; 70866131Swpaul 70966131Swpaul if (m == NULL) { 71066131Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 71166131Swpaul if (m_new == NULL) { 71266131Swpaul printf("pcn%d: no memory for rx list " 71366131Swpaul "-- packet dropped!\n", sc->pcn_unit); 71466131Swpaul return(ENOBUFS); 71566131Swpaul } 71666131Swpaul 71766131Swpaul MCLGET(m_new, M_DONTWAIT); 71866131Swpaul if (!(m_new->m_flags & M_EXT)) { 71966131Swpaul printf("pcn%d: no memory for rx list " 72066131Swpaul "-- packet dropped!\n", sc->pcn_unit); 72166131Swpaul m_freem(m_new); 72266131Swpaul return(ENOBUFS); 72366131Swpaul } 72466131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 72566131Swpaul } else { 72666131Swpaul m_new = m; 72766131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 72866131Swpaul m_new->m_data = m_new->m_ext.ext_buf; 72966131Swpaul } 73066131Swpaul 73166131Swpaul m_adj(m_new, ETHER_ALIGN); 73266131Swpaul 73366131Swpaul sc->pcn_cdata.pcn_rx_chain[idx] = m_new; 73466131Swpaul c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); 73566131Swpaul c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; 73666131Swpaul c->pcn_bufsz |= PCN_RXLEN_MBO; 73766131Swpaul c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; 73866131Swpaul 73966131Swpaul return(0); 74066131Swpaul} 74166131Swpaul 74266131Swpaul/* 74366131Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 74466131Swpaul * the higher level protocols. 74566131Swpaul */ 74666131Swpaulstatic void pcn_rxeof(sc) 74766131Swpaul struct pcn_softc *sc; 74866131Swpaul{ 74966131Swpaul struct ether_header *eh; 75066131Swpaul struct mbuf *m; 75166131Swpaul struct ifnet *ifp; 75266131Swpaul struct pcn_rx_desc *cur_rx; 75366131Swpaul int i; 75466131Swpaul 75566131Swpaul ifp = &sc->arpcom.ac_if; 75666131Swpaul i = sc->pcn_cdata.pcn_rx_prod; 75766131Swpaul 75866131Swpaul while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { 75966131Swpaul cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; 76066131Swpaul m = sc->pcn_cdata.pcn_rx_chain[i]; 76166131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 76266131Swpaul 76366131Swpaul /* 76466131Swpaul * If an error occurs, update stats, clear the 76566131Swpaul * status word and leave the mbuf cluster in place: 76666131Swpaul * it should simply get re-used next time this descriptor 76766131Swpaul * comes up in the ring. 76866131Swpaul */ 76966131Swpaul if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { 77066131Swpaul ifp->if_ierrors++; 77166131Swpaul pcn_newbuf(sc, i, m); 77266131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 77366131Swpaul continue; 77466131Swpaul } 77566131Swpaul 77666592Swpaul if (pcn_newbuf(sc, i, NULL)) { 77766592Swpaul /* Ran out of mbufs; recycle this one. */ 77866592Swpaul pcn_newbuf(sc, i, m); 77966592Swpaul ifp->if_ierrors++; 78066592Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 78166592Swpaul continue; 78266592Swpaul } 78366592Swpaul 78466131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 78566131Swpaul 78666131Swpaul /* No errors; receive the packet. */ 78766131Swpaul ifp->if_ipackets++; 78866131Swpaul eh = mtod(m, struct ether_header *); 78966131Swpaul m->m_len = m->m_pkthdr.len = 79066131Swpaul cur_rx->pcn_rxlen - ETHER_CRC_LEN; 79166131Swpaul m->m_pkthdr.rcvif = ifp; 79266131Swpaul 79366131Swpaul /* Remove header from mbuf and pass it on. */ 79466131Swpaul m_adj(m, sizeof(struct ether_header)); 79566131Swpaul ether_input(ifp, eh, m); 79666131Swpaul } 79766131Swpaul 79866131Swpaul sc->pcn_cdata.pcn_rx_prod = i; 79966131Swpaul 80066131Swpaul return; 80166131Swpaul} 80266131Swpaul 80366131Swpaul/* 80466131Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 80566131Swpaul * the list buffers. 80666131Swpaul */ 80766131Swpaul 80866131Swpaulstatic void pcn_txeof(sc) 80966131Swpaul struct pcn_softc *sc; 81066131Swpaul{ 81166131Swpaul struct pcn_tx_desc *cur_tx = NULL; 81266131Swpaul struct ifnet *ifp; 81366131Swpaul u_int32_t idx; 81466131Swpaul 81566131Swpaul ifp = &sc->arpcom.ac_if; 81666131Swpaul 81766131Swpaul /* Clear the timeout timer. */ 81866131Swpaul ifp->if_timer = 0; 81966131Swpaul 82066131Swpaul /* 82166131Swpaul * Go through our tx list and free mbufs for those 82266131Swpaul * frames that have been transmitted. 82366131Swpaul */ 82466131Swpaul idx = sc->pcn_cdata.pcn_tx_cons; 82566131Swpaul while (idx != sc->pcn_cdata.pcn_tx_prod) { 82666131Swpaul cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; 82766131Swpaul 82866131Swpaul if (!PCN_OWN_TXDESC(cur_tx)) 82966131Swpaul break; 83066131Swpaul 83166131Swpaul if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { 83266131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 83366131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 83466131Swpaul continue; 83566131Swpaul } 83666131Swpaul 83766131Swpaul if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { 83866131Swpaul ifp->if_oerrors++; 83966131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) 84066131Swpaul ifp->if_collisions++; 84166131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) 84266131Swpaul ifp->if_collisions++; 84366131Swpaul } 84466131Swpaul 84566131Swpaul ifp->if_collisions += 84666131Swpaul cur_tx->pcn_txstat & PCN_TXSTAT_TRC; 84766131Swpaul 84866131Swpaul ifp->if_opackets++; 84966131Swpaul if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { 85066131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); 85166131Swpaul sc->pcn_cdata.pcn_tx_chain[idx] = NULL; 85266131Swpaul } 85366131Swpaul 85466131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 85566131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 85666131Swpaul ifp->if_timer = 0; 85766131Swpaul } 85866131Swpaul 85966131Swpaul sc->pcn_cdata.pcn_tx_cons = idx; 86066131Swpaul 86166131Swpaul if (cur_tx != NULL) 86266131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 86366131Swpaul 86466131Swpaul return; 86566131Swpaul} 86666131Swpaul 86766131Swpaulstatic void pcn_tick(xsc) 86866131Swpaul void *xsc; 86966131Swpaul{ 87066131Swpaul struct pcn_softc *sc; 87166131Swpaul struct mii_data *mii; 87266131Swpaul struct ifnet *ifp; 87366131Swpaul 87466131Swpaul sc = xsc; 87566131Swpaul ifp = &sc->arpcom.ac_if; 87667087Swpaul PCN_LOCK(sc); 87766131Swpaul 87866131Swpaul mii = device_get_softc(sc->pcn_miibus); 87966131Swpaul mii_tick(mii); 88066131Swpaul 88166131Swpaul if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) 88266131Swpaul sc->pcn_link = 0; 88366131Swpaul 88466131Swpaul if (!sc->pcn_link) { 88566131Swpaul mii_pollstat(mii); 88666131Swpaul if (mii->mii_media_status & IFM_ACTIVE && 88766131Swpaul IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) 88866131Swpaul sc->pcn_link++; 88966131Swpaul if (ifp->if_snd.ifq_head != NULL) 89066131Swpaul pcn_start(ifp); 89166131Swpaul } 89266131Swpaul 89366131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 89466131Swpaul 89567087Swpaul PCN_UNLOCK(sc); 89666131Swpaul 89766131Swpaul return; 89866131Swpaul} 89966131Swpaul 90066131Swpaulstatic void pcn_intr(arg) 90166131Swpaul void *arg; 90266131Swpaul{ 90366131Swpaul struct pcn_softc *sc; 90466131Swpaul struct ifnet *ifp; 90566131Swpaul u_int32_t status; 90666131Swpaul 90766131Swpaul sc = arg; 90866131Swpaul ifp = &sc->arpcom.ac_if; 90966131Swpaul 91066131Swpaul /* Supress unwanted interrupts */ 91166131Swpaul if (!(ifp->if_flags & IFF_UP)) { 91266131Swpaul pcn_stop(sc); 91366131Swpaul return; 91466131Swpaul } 91566131Swpaul 91666131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); 91766131Swpaul 91866131Swpaul while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { 91966131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, status); 92066131Swpaul 92166131Swpaul if (status & PCN_CSR_RINT) 92266131Swpaul pcn_rxeof(sc); 92366131Swpaul 92466131Swpaul if (status & PCN_CSR_TINT) 92566131Swpaul pcn_txeof(sc); 92666131Swpaul 92766131Swpaul if (status & PCN_CSR_ERR) { 92866131Swpaul pcn_init(sc); 92966131Swpaul break; 93066131Swpaul } 93166131Swpaul } 93266131Swpaul 93366131Swpaul if (ifp->if_snd.ifq_head != NULL) 93466131Swpaul pcn_start(ifp); 93566131Swpaul 93666131Swpaul return; 93766131Swpaul} 93866131Swpaul 93966131Swpaul/* 94066131Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 94166131Swpaul * pointers to the fragment pointers. 94266131Swpaul */ 94366131Swpaulstatic int pcn_encap(sc, m_head, txidx) 94466131Swpaul struct pcn_softc *sc; 94566131Swpaul struct mbuf *m_head; 94666131Swpaul u_int32_t *txidx; 94766131Swpaul{ 94866131Swpaul struct pcn_tx_desc *f = NULL; 94966131Swpaul struct mbuf *m; 95066131Swpaul int frag, cur, cnt = 0; 95166131Swpaul 95266131Swpaul /* 95366131Swpaul * Start packing the mbufs in this chain into 95466131Swpaul * the fragment pointers. Stop when we run out 95566131Swpaul * of fragments or hit the end of the mbuf chain. 95666131Swpaul */ 95766131Swpaul m = m_head; 95866131Swpaul cur = frag = *txidx; 95966131Swpaul 96066131Swpaul for (m = m_head; m != NULL; m = m->m_next) { 96166131Swpaul if (m->m_len != 0) { 96266131Swpaul if ((PCN_TX_LIST_CNT - 96366131Swpaul (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) 96466131Swpaul return(ENOBUFS); 96566131Swpaul f = &sc->pcn_ldata->pcn_tx_list[frag]; 96666131Swpaul f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; 96766131Swpaul f->pcn_txctl |= PCN_TXCTL_MBO; 96866131Swpaul f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); 96966131Swpaul if (cnt == 0) 97066131Swpaul f->pcn_txctl |= PCN_TXCTL_STP; 97166131Swpaul else 97266131Swpaul f->pcn_txctl |= PCN_TXCTL_OWN; 97366131Swpaul cur = frag; 97466131Swpaul PCN_INC(frag, PCN_TX_LIST_CNT); 97566131Swpaul cnt++; 97666131Swpaul } 97766131Swpaul } 97866131Swpaul 97966131Swpaul if (m != NULL) 98066131Swpaul return(ENOBUFS); 98166131Swpaul 98266131Swpaul sc->pcn_cdata.pcn_tx_chain[cur] = m_head; 98366131Swpaul sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= 98466131Swpaul PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; 98566131Swpaul sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; 98666131Swpaul sc->pcn_cdata.pcn_tx_cnt += cnt; 98766131Swpaul *txidx = frag; 98866131Swpaul 98966131Swpaul return(0); 99066131Swpaul} 99166131Swpaul 99266131Swpaul/* 99366131Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 99466131Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 99566131Swpaul * copy of the pointers since the transmit list fragment pointers are 99666131Swpaul * physical addresses. 99766131Swpaul */ 99866131Swpaulstatic void pcn_start(ifp) 99966131Swpaul struct ifnet *ifp; 100066131Swpaul{ 100166131Swpaul struct pcn_softc *sc; 100266131Swpaul struct mbuf *m_head = NULL; 100366131Swpaul u_int32_t idx; 100466131Swpaul 100566131Swpaul sc = ifp->if_softc; 100666131Swpaul 100767087Swpaul PCN_LOCK(sc); 100867087Swpaul 100967087Swpaul if (!sc->pcn_link) { 101067087Swpaul PCN_UNLOCK(sc); 101166131Swpaul return; 101267087Swpaul } 101366131Swpaul 101466131Swpaul idx = sc->pcn_cdata.pcn_tx_prod; 101566131Swpaul 101667087Swpaul if (ifp->if_flags & IFF_OACTIVE) { 101767087Swpaul PCN_UNLOCK(sc); 101866131Swpaul return; 101967087Swpaul } 102066131Swpaul 102166131Swpaul while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { 102266131Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 102366131Swpaul if (m_head == NULL) 102466131Swpaul break; 102566131Swpaul 102666131Swpaul if (pcn_encap(sc, m_head, &idx)) { 102766131Swpaul IF_PREPEND(&ifp->if_snd, m_head); 102866131Swpaul ifp->if_flags |= IFF_OACTIVE; 102966131Swpaul break; 103066131Swpaul } 103166131Swpaul 103266131Swpaul /* 103366131Swpaul * If there's a BPF listener, bounce a copy of this frame 103466131Swpaul * to him. 103566131Swpaul */ 103666131Swpaul if (ifp->if_bpf) 103766131Swpaul bpf_mtap(ifp, m_head); 103866131Swpaul 103966131Swpaul } 104066131Swpaul 104166131Swpaul /* Transmit */ 104266131Swpaul sc->pcn_cdata.pcn_tx_prod = idx; 104366131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); 104466131Swpaul 104566131Swpaul /* 104666131Swpaul * Set a timeout in case the chip goes out to lunch. 104766131Swpaul */ 104866131Swpaul ifp->if_timer = 5; 104966131Swpaul 105067087Swpaul PCN_UNLOCK(sc); 105167087Swpaul 105266131Swpaul return; 105366131Swpaul} 105466131Swpaul 105566131Swpaulstatic void pcn_init(xsc) 105666131Swpaul void *xsc; 105766131Swpaul{ 105866131Swpaul struct pcn_softc *sc = xsc; 105966131Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 106066131Swpaul struct mii_data *mii = NULL; 106166131Swpaul 106267087Swpaul PCN_LOCK(sc); 106366131Swpaul 106466131Swpaul /* 106566131Swpaul * Cancel pending I/O and free all RX/TX buffers. 106666131Swpaul */ 106766131Swpaul pcn_stop(sc); 106866131Swpaul pcn_reset(sc); 106966131Swpaul 107066131Swpaul mii = device_get_softc(sc->pcn_miibus); 107166131Swpaul 107266131Swpaul /* Set MAC address */ 107366131Swpaul pcn_csr_write(sc, PCN_CSR_PAR0, 107466131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 107566131Swpaul pcn_csr_write(sc, PCN_CSR_PAR1, 107666131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 107766131Swpaul pcn_csr_write(sc, PCN_CSR_PAR2, 107866131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 107966131Swpaul 108066131Swpaul /* Init circular RX list. */ 108166131Swpaul if (pcn_list_rx_init(sc) == ENOBUFS) { 108266131Swpaul printf("pcn%d: initialization failed: no " 108366131Swpaul "memory for rx buffers\n", sc->pcn_unit); 108466131Swpaul pcn_stop(sc); 108567087Swpaul PCN_UNLOCK(sc); 108666131Swpaul return; 108766131Swpaul } 108866131Swpaul 108966131Swpaul /* 109066131Swpaul * Init tx descriptors. 109166131Swpaul */ 109266131Swpaul pcn_list_tx_init(sc); 109366131Swpaul 109466131Swpaul /* Set up the mode register. */ 109566131Swpaul pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); 109666131Swpaul 109766131Swpaul /* If we want promiscuous mode, set the allframes bit. */ 109866131Swpaul if (ifp->if_flags & IFF_PROMISC) { 109966131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 110066131Swpaul } else { 110166131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 110266131Swpaul } 110366131Swpaul 110466131Swpaul /* Set the capture broadcast bit to capture broadcast frames. */ 110566131Swpaul if (ifp->if_flags & IFF_BROADCAST) { 110666131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 110766131Swpaul } else { 110866131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 110966131Swpaul } 111066131Swpaul 111166131Swpaul /* 111266131Swpaul * Load the multicast filter. 111366131Swpaul */ 111466131Swpaul pcn_setmulti(sc); 111566131Swpaul 111666131Swpaul /* 111766131Swpaul * Load the addresses of the RX and TX lists. 111866131Swpaul */ 111966131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR0, 112066131Swpaul vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); 112166131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR1, 112266131Swpaul (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); 112366131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR0, 112466131Swpaul vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); 112566131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR1, 112666131Swpaul (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); 112766131Swpaul 112866131Swpaul /* Set the RX and TX ring sizes. */ 112966131Swpaul pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); 113066131Swpaul pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); 113166131Swpaul 113266131Swpaul /* We're not using the initialization block. */ 113366131Swpaul pcn_csr_write(sc, PCN_CSR_IAB1, 0); 113466131Swpaul 113566131Swpaul /* Enable fast suspend mode. */ 113666131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); 113766131Swpaul 113866131Swpaul /* 113966131Swpaul * Enable burst read and write. Also set the no underflow 114066131Swpaul * bit. This will avoid transmit underruns in certain 114166210Swpaul * conditions while still providing decent performance. 114266131Swpaul */ 114366131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| 114466131Swpaul PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); 114566131Swpaul 114666131Swpaul /* Enable graceful recovery from underflow. */ 114766131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); 114866131Swpaul 114966131Swpaul /* Enable auto-padding of short TX frames. */ 115066131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); 115166131Swpaul 115266131Swpaul /* Disable MII autoneg (we handle this ourselves). */ 115366131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); 115466131Swpaul 115566131Swpaul if (sc->pcn_type == Am79C978) 115666131Swpaul pcn_bcr_write(sc, PCN_BCR_PHYSEL, 115766131Swpaul PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); 115866131Swpaul 115966131Swpaul /* Enable interrupts and start the controller running. */ 116066131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); 116166131Swpaul 116266131Swpaul mii_mediachg(mii); 116366131Swpaul 116466131Swpaul ifp->if_flags |= IFF_RUNNING; 116566131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 116666131Swpaul 116766131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 116867087Swpaul PCN_UNLOCK(sc); 116966131Swpaul 117066131Swpaul return; 117166131Swpaul} 117266131Swpaul 117366131Swpaul/* 117466131Swpaul * Set media options. 117566131Swpaul */ 117666131Swpaulstatic int pcn_ifmedia_upd(ifp) 117766131Swpaul struct ifnet *ifp; 117866131Swpaul{ 117966131Swpaul struct pcn_softc *sc; 118066131Swpaul struct mii_data *mii; 118166131Swpaul 118266131Swpaul sc = ifp->if_softc; 118366131Swpaul mii = device_get_softc(sc->pcn_miibus); 118466131Swpaul 118566131Swpaul sc->pcn_link = 0; 118666131Swpaul if (mii->mii_instance) { 118766131Swpaul struct mii_softc *miisc; 118866131Swpaul for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 118966131Swpaul miisc = LIST_NEXT(miisc, mii_list)) 119066131Swpaul mii_phy_reset(miisc); 119166131Swpaul } 119266131Swpaul mii_mediachg(mii); 119366131Swpaul 119466131Swpaul return(0); 119566131Swpaul} 119666131Swpaul 119766131Swpaul/* 119866131Swpaul * Report current media status. 119966131Swpaul */ 120066131Swpaulstatic void pcn_ifmedia_sts(ifp, ifmr) 120166131Swpaul struct ifnet *ifp; 120266131Swpaul struct ifmediareq *ifmr; 120366131Swpaul{ 120466131Swpaul struct pcn_softc *sc; 120566131Swpaul struct mii_data *mii; 120666131Swpaul 120766131Swpaul sc = ifp->if_softc; 120866131Swpaul 120966131Swpaul mii = device_get_softc(sc->pcn_miibus); 121066131Swpaul mii_pollstat(mii); 121166131Swpaul ifmr->ifm_active = mii->mii_media_active; 121266131Swpaul ifmr->ifm_status = mii->mii_media_status; 121366131Swpaul 121466131Swpaul return; 121566131Swpaul} 121666131Swpaul 121766131Swpaulstatic int pcn_ioctl(ifp, command, data) 121866131Swpaul struct ifnet *ifp; 121966131Swpaul u_long command; 122066131Swpaul caddr_t data; 122166131Swpaul{ 122266131Swpaul struct pcn_softc *sc = ifp->if_softc; 122366131Swpaul struct ifreq *ifr = (struct ifreq *) data; 122466131Swpaul struct mii_data *mii = NULL; 122567087Swpaul int error = 0; 122666131Swpaul 122767087Swpaul PCN_LOCK(sc); 122866131Swpaul 122966131Swpaul switch(command) { 123066131Swpaul case SIOCSIFADDR: 123166131Swpaul case SIOCGIFADDR: 123266131Swpaul case SIOCSIFMTU: 123366131Swpaul error = ether_ioctl(ifp, command, data); 123466131Swpaul break; 123566131Swpaul case SIOCSIFFLAGS: 123666131Swpaul if (ifp->if_flags & IFF_UP) { 123766131Swpaul if (ifp->if_flags & IFF_RUNNING && 123866131Swpaul ifp->if_flags & IFF_PROMISC && 123966131Swpaul !(sc->pcn_if_flags & IFF_PROMISC)) { 124066131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 124166131Swpaul PCN_EXTCTL1_SPND); 124266131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, 124366131Swpaul PCN_MODE_PROMISC); 124466131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 124566131Swpaul PCN_EXTCTL1_SPND); 124666771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 124766771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 124866131Swpaul } else if (ifp->if_flags & IFF_RUNNING && 124966131Swpaul !(ifp->if_flags & IFF_PROMISC) && 125066131Swpaul sc->pcn_if_flags & IFF_PROMISC) { 125166131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 125266131Swpaul PCN_EXTCTL1_SPND); 125366131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, 125466131Swpaul PCN_MODE_PROMISC); 125566131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 125666131Swpaul PCN_EXTCTL1_SPND); 125766771Swpaul pcn_csr_write(sc, PCN_CSR_CSR, 125866771Swpaul PCN_CSR_INTEN|PCN_CSR_START); 125966131Swpaul } else if (!(ifp->if_flags & IFF_RUNNING)) 126066131Swpaul pcn_init(sc); 126166131Swpaul } else { 126266131Swpaul if (ifp->if_flags & IFF_RUNNING) 126366131Swpaul pcn_stop(sc); 126466131Swpaul } 126566131Swpaul sc->pcn_if_flags = ifp->if_flags; 126666131Swpaul error = 0; 126766131Swpaul break; 126866131Swpaul case SIOCADDMULTI: 126966131Swpaul case SIOCDELMULTI: 127066131Swpaul pcn_setmulti(sc); 127166131Swpaul error = 0; 127266131Swpaul break; 127366131Swpaul case SIOCGIFMEDIA: 127466131Swpaul case SIOCSIFMEDIA: 127566131Swpaul mii = device_get_softc(sc->pcn_miibus); 127666131Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 127766131Swpaul break; 127866131Swpaul default: 127966131Swpaul error = EINVAL; 128066131Swpaul break; 128166131Swpaul } 128266131Swpaul 128367087Swpaul PCN_UNLOCK(sc); 128466131Swpaul 128566131Swpaul return(error); 128666131Swpaul} 128766131Swpaul 128866131Swpaulstatic void pcn_watchdog(ifp) 128966131Swpaul struct ifnet *ifp; 129066131Swpaul{ 129166131Swpaul struct pcn_softc *sc; 129266131Swpaul 129366131Swpaul sc = ifp->if_softc; 129466131Swpaul 129567087Swpaul PCN_LOCK(sc); 129667087Swpaul 129766131Swpaul ifp->if_oerrors++; 129866131Swpaul printf("pcn%d: watchdog timeout\n", sc->pcn_unit); 129966131Swpaul 130066131Swpaul pcn_stop(sc); 130166131Swpaul pcn_reset(sc); 130266131Swpaul pcn_init(sc); 130366131Swpaul 130466131Swpaul if (ifp->if_snd.ifq_head != NULL) 130566131Swpaul pcn_start(ifp); 130666131Swpaul 130767087Swpaul PCN_UNLOCK(sc); 130867087Swpaul 130966131Swpaul return; 131066131Swpaul} 131166131Swpaul 131266131Swpaul/* 131366131Swpaul * Stop the adapter and free any mbufs allocated to the 131466131Swpaul * RX and TX lists. 131566131Swpaul */ 131666131Swpaulstatic void pcn_stop(sc) 131766131Swpaul struct pcn_softc *sc; 131866131Swpaul{ 131966131Swpaul register int i; 132066131Swpaul struct ifnet *ifp; 132166131Swpaul 132266131Swpaul ifp = &sc->arpcom.ac_if; 132367087Swpaul PCN_LOCK(sc); 132466131Swpaul ifp->if_timer = 0; 132566131Swpaul 132666131Swpaul untimeout(pcn_tick, sc, sc->pcn_stat_ch); 132766131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); 132866131Swpaul sc->pcn_link = 0; 132966131Swpaul 133066131Swpaul /* 133166131Swpaul * Free data in the RX lists. 133266131Swpaul */ 133366131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 133466131Swpaul if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { 133566131Swpaul m_freem(sc->pcn_cdata.pcn_rx_chain[i]); 133666131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 133766131Swpaul } 133866131Swpaul } 133966131Swpaul bzero((char *)&sc->pcn_ldata->pcn_rx_list, 134066131Swpaul sizeof(sc->pcn_ldata->pcn_rx_list)); 134166131Swpaul 134266131Swpaul /* 134366131Swpaul * Free the TX list buffers. 134466131Swpaul */ 134566131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 134666131Swpaul if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { 134766131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[i]); 134866131Swpaul sc->pcn_cdata.pcn_tx_chain[i] = NULL; 134966131Swpaul } 135066131Swpaul } 135166131Swpaul 135266131Swpaul bzero((char *)&sc->pcn_ldata->pcn_tx_list, 135366131Swpaul sizeof(sc->pcn_ldata->pcn_tx_list)); 135466131Swpaul 135566131Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 135667087Swpaul PCN_UNLOCK(sc); 135766131Swpaul 135866131Swpaul return; 135966131Swpaul} 136066131Swpaul 136166131Swpaul/* 136266131Swpaul * Stop all chip I/O so that the kernel's probe routines don't 136366131Swpaul * get confused by errant DMAs when rebooting. 136466131Swpaul */ 136566131Swpaulstatic void pcn_shutdown(dev) 136666131Swpaul device_t dev; 136766131Swpaul{ 136866131Swpaul struct pcn_softc *sc; 136966131Swpaul 137066131Swpaul sc = device_get_softc(dev); 137166131Swpaul 137267087Swpaul PCN_LOCK(sc); 137366131Swpaul pcn_reset(sc); 137466131Swpaul pcn_stop(sc); 137567087Swpaul PCN_UNLOCK(sc); 137666131Swpaul 137766131Swpaul return; 137866131Swpaul} 1379