if_pcn.c revision 66690
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 66690 2000-10-05 19:40:19Z 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 66690 2000-10-05 19:40:19Z 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); 40166131Swpaul pcn_reset(sc); 40266131Swpaul chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); 40366131Swpaul chip_id <<= 16; 40466131Swpaul chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); 40566131Swpaul bus_release_resource(dev, PCN_RES, 40666131Swpaul PCN_RID, sc->pcn_res); 40766131Swpaul chip_id >>= 12; 40866131Swpaul sc->pcn_type = chip_id & PART_MASK; 40966131Swpaul switch(sc->pcn_type) { 41066131Swpaul case Am79C971: 41166131Swpaul case Am79C972: 41266131Swpaul case Am79C973: 41366690Swpaul case Am79C975: 41466592Swpaul case Am79C976: 41566131Swpaul case Am79C978: 41666131Swpaul break; 41766131Swpaul default: 41866131Swpaul return(ENXIO); 41966131Swpaul break; 42066131Swpaul } 42166131Swpaul device_set_desc(dev, t->pcn_name); 42266131Swpaul return(0); 42366131Swpaul } 42466131Swpaul t++; 42566131Swpaul } 42666131Swpaul 42766131Swpaul return(ENXIO); 42866131Swpaul} 42966131Swpaul 43066131Swpaul/* 43166131Swpaul * Attach the interface. Allocate softc structures, do ifmedia 43266131Swpaul * setup and ethernet/BPF attach. 43366131Swpaul */ 43466131Swpaulstatic int pcn_attach(dev) 43566131Swpaul device_t dev; 43666131Swpaul{ 43766131Swpaul int s; 43866131Swpaul u_int32_t eaddr[2]; 43966131Swpaul u_int32_t command; 44066131Swpaul struct pcn_softc *sc; 44166131Swpaul struct ifnet *ifp; 44266131Swpaul int unit, error = 0, rid; 44366131Swpaul 44466131Swpaul s = splimp(); 44566131Swpaul 44666131Swpaul sc = device_get_softc(dev); 44766131Swpaul unit = device_get_unit(dev); 44866131Swpaul 44966131Swpaul /* 45066131Swpaul * Handle power management nonsense. 45166131Swpaul */ 45266131Swpaul 45366131Swpaul command = pci_read_config(dev, PCN_PCI_CAPID, 4) & 0x000000FF; 45466131Swpaul if (command == 0x01) { 45566131Swpaul 45666131Swpaul command = pci_read_config(dev, PCN_PCI_PWRMGMTCTRL, 4); 45766131Swpaul if (command & PCN_PSTATE_MASK) { 45866131Swpaul u_int32_t iobase, membase, irq; 45966131Swpaul 46066131Swpaul /* Save important PCI config data. */ 46166131Swpaul iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); 46266131Swpaul membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); 46366131Swpaul irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); 46466131Swpaul 46566131Swpaul /* Reset the power state. */ 46666131Swpaul printf("pcn%d: chip is in D%d power mode " 46766131Swpaul "-- setting to D0\n", unit, command & PCN_PSTATE_MASK); 46866131Swpaul command &= 0xFFFFFFFC; 46966131Swpaul pci_write_config(dev, PCN_PCI_PWRMGMTCTRL, command, 4); 47066131Swpaul 47166131Swpaul /* Restore PCI config data. */ 47266131Swpaul pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); 47366131Swpaul pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); 47466131Swpaul pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); 47566131Swpaul } 47666131Swpaul } 47766131Swpaul 47866131Swpaul /* 47966131Swpaul * Map control/status registers. 48066131Swpaul */ 48166131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 48266131Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 48366131Swpaul pci_write_config(dev, PCIR_COMMAND, command, 4); 48466131Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 48566131Swpaul 48666131Swpaul#ifdef PCN_USEIOSPACE 48766131Swpaul if (!(command & PCIM_CMD_PORTEN)) { 48866131Swpaul printf("pcn%d: failed to enable I/O ports!\n", unit); 48966131Swpaul error = ENXIO;; 49066131Swpaul goto fail; 49166131Swpaul } 49266131Swpaul#else 49366131Swpaul if (!(command & PCIM_CMD_MEMEN)) { 49466131Swpaul printf("pcn%d: failed to enable memory mapping!\n", unit); 49566131Swpaul error = ENXIO;; 49666131Swpaul goto fail; 49766131Swpaul } 49866131Swpaul#endif 49966131Swpaul 50066131Swpaul rid = PCN_RID; 50166131Swpaul sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, 50266131Swpaul 0, ~0, 1, RF_ACTIVE); 50366131Swpaul 50466131Swpaul if (sc->pcn_res == NULL) { 50566131Swpaul printf("pcn%d: couldn't map ports/memory\n", unit); 50666131Swpaul error = ENXIO; 50766131Swpaul goto fail; 50866131Swpaul } 50966131Swpaul 51066131Swpaul sc->pcn_btag = rman_get_bustag(sc->pcn_res); 51166131Swpaul sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); 51266131Swpaul 51366131Swpaul /* Allocate interrupt */ 51466131Swpaul rid = 0; 51566131Swpaul sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 51666131Swpaul RF_SHAREABLE | RF_ACTIVE); 51766131Swpaul 51866131Swpaul if (sc->pcn_irq == NULL) { 51966131Swpaul printf("pcn%d: couldn't map interrupt\n", unit); 52066131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 52166131Swpaul error = ENXIO; 52266131Swpaul goto fail; 52366131Swpaul } 52466131Swpaul 52566131Swpaul error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, 52666131Swpaul pcn_intr, sc, &sc->pcn_intrhand); 52766131Swpaul 52866131Swpaul if (error) { 52966131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res); 53066131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 53166131Swpaul printf("pcn%d: couldn't set up irq\n", unit); 53266131Swpaul goto fail; 53366131Swpaul } 53466131Swpaul 53566131Swpaul /* Reset the adapter. */ 53666131Swpaul pcn_reset(sc); 53766131Swpaul 53866131Swpaul /* 53966131Swpaul * Get station address from the EEPROM. 54066131Swpaul */ 54166131Swpaul eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); 54266131Swpaul eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); 54366131Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 54466131Swpaul 54566131Swpaul /* 54666131Swpaul * An AMD chip was detected. Inform the world. 54766131Swpaul */ 54866131Swpaul printf("pcn%d: Ethernet address: %6D\n", unit, 54966131Swpaul sc->arpcom.ac_enaddr, ":"); 55066131Swpaul 55166131Swpaul sc->pcn_unit = unit; 55266131Swpaul callout_handle_init(&sc->pcn_stat_ch); 55366131Swpaul 55466131Swpaul sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, 55566131Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 55666131Swpaul 55766131Swpaul if (sc->pcn_ldata == NULL) { 55866131Swpaul printf("pcn%d: no memory for list buffers!\n", unit); 55966131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 56066131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 56166131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 56266131Swpaul error = ENXIO; 56366131Swpaul goto fail; 56466131Swpaul } 56566131Swpaul bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); 56666131Swpaul 56766131Swpaul ifp = &sc->arpcom.ac_if; 56866131Swpaul ifp->if_softc = sc; 56966131Swpaul ifp->if_unit = unit; 57066131Swpaul ifp->if_name = "pcn"; 57166131Swpaul ifp->if_mtu = ETHERMTU; 57266131Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 57366131Swpaul ifp->if_ioctl = pcn_ioctl; 57466131Swpaul ifp->if_output = ether_output; 57566131Swpaul ifp->if_start = pcn_start; 57666131Swpaul ifp->if_watchdog = pcn_watchdog; 57766131Swpaul ifp->if_init = pcn_init; 57866131Swpaul ifp->if_baudrate = 10000000; 57966131Swpaul ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; 58066131Swpaul 58166131Swpaul /* 58266131Swpaul * Do MII setup. 58366131Swpaul */ 58466131Swpaul if (mii_phy_probe(dev, &sc->pcn_miibus, 58566131Swpaul pcn_ifmedia_upd, pcn_ifmedia_sts)) { 58666131Swpaul printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); 58766131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 58866131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 58966131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 59066131Swpaul error = ENXIO; 59166131Swpaul goto fail; 59266131Swpaul } 59366131Swpaul 59466131Swpaul /* 59566131Swpaul * Call MI attach routine. 59666131Swpaul */ 59766131Swpaul ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 59866131Swpaul callout_handle_init(&sc->pcn_stat_ch); 59966131Swpaul 60066131Swpaulfail: 60166131Swpaul splx(s); 60266131Swpaul return(error); 60366131Swpaul} 60466131Swpaul 60566131Swpaulstatic int pcn_detach(dev) 60666131Swpaul device_t dev; 60766131Swpaul{ 60866131Swpaul struct pcn_softc *sc; 60966131Swpaul struct ifnet *ifp; 61066131Swpaul int s; 61166131Swpaul 61266131Swpaul s = splimp(); 61366131Swpaul 61466131Swpaul sc = device_get_softc(dev); 61566131Swpaul ifp = &sc->arpcom.ac_if; 61666131Swpaul 61766131Swpaul pcn_reset(sc); 61866131Swpaul pcn_stop(sc); 61966131Swpaul ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 62066131Swpaul 62166131Swpaul if (sc->pcn_miibus != NULL) { 62266131Swpaul bus_generic_detach(dev); 62366131Swpaul device_delete_child(dev, sc->pcn_miibus); 62466131Swpaul } 62566131Swpaul 62666131Swpaul bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); 62766131Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); 62866131Swpaul bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); 62966131Swpaul 63066131Swpaul contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); 63166131Swpaul 63266131Swpaul splx(s); 63366131Swpaul 63466131Swpaul return(0); 63566131Swpaul} 63666131Swpaul 63766131Swpaul/* 63866131Swpaul * Initialize the transmit descriptors. 63966131Swpaul */ 64066131Swpaulstatic int pcn_list_tx_init(sc) 64166131Swpaul struct pcn_softc *sc; 64266131Swpaul{ 64366131Swpaul struct pcn_list_data *ld; 64466131Swpaul struct pcn_ring_data *cd; 64566131Swpaul int i; 64666131Swpaul 64766131Swpaul cd = &sc->pcn_cdata; 64866131Swpaul ld = sc->pcn_ldata; 64966131Swpaul 65066131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 65166131Swpaul cd->pcn_tx_chain[i] = NULL; 65266131Swpaul ld->pcn_tx_list[i].pcn_tbaddr = 0; 65366131Swpaul ld->pcn_tx_list[i].pcn_txctl = 0; 65466131Swpaul ld->pcn_tx_list[i].pcn_txstat = 0; 65566131Swpaul } 65666131Swpaul 65766131Swpaul cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; 65866131Swpaul 65966131Swpaul return(0); 66066131Swpaul} 66166131Swpaul 66266131Swpaul 66366131Swpaul/* 66466131Swpaul * Initialize the RX descriptors and allocate mbufs for them. 66566131Swpaul */ 66666131Swpaulstatic int pcn_list_rx_init(sc) 66766131Swpaul struct pcn_softc *sc; 66866131Swpaul{ 66966131Swpaul struct pcn_list_data *ld; 67066131Swpaul struct pcn_ring_data *cd; 67166131Swpaul int i; 67266131Swpaul 67366131Swpaul ld = sc->pcn_ldata; 67466131Swpaul cd = &sc->pcn_cdata; 67566131Swpaul 67666131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 67766131Swpaul if (pcn_newbuf(sc, i, NULL) == ENOBUFS) 67866131Swpaul return(ENOBUFS); 67966131Swpaul } 68066131Swpaul 68166131Swpaul cd->pcn_rx_prod = 0; 68266131Swpaul 68366131Swpaul return(0); 68466131Swpaul} 68566131Swpaul 68666131Swpaul/* 68766131Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 68866131Swpaul */ 68966131Swpaulstatic int pcn_newbuf(sc, idx, m) 69066131Swpaul struct pcn_softc *sc; 69166131Swpaul int idx; 69266131Swpaul struct mbuf *m; 69366131Swpaul{ 69466131Swpaul struct mbuf *m_new = NULL; 69566131Swpaul struct pcn_rx_desc *c; 69666131Swpaul 69766131Swpaul c = &sc->pcn_ldata->pcn_rx_list[idx]; 69866131Swpaul 69966131Swpaul if (m == NULL) { 70066131Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 70166131Swpaul if (m_new == NULL) { 70266131Swpaul printf("pcn%d: no memory for rx list " 70366131Swpaul "-- packet dropped!\n", sc->pcn_unit); 70466131Swpaul return(ENOBUFS); 70566131Swpaul } 70666131Swpaul 70766131Swpaul MCLGET(m_new, M_DONTWAIT); 70866131Swpaul if (!(m_new->m_flags & M_EXT)) { 70966131Swpaul printf("pcn%d: no memory for rx list " 71066131Swpaul "-- packet dropped!\n", sc->pcn_unit); 71166131Swpaul m_freem(m_new); 71266131Swpaul return(ENOBUFS); 71366131Swpaul } 71466131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 71566131Swpaul } else { 71666131Swpaul m_new = m; 71766131Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 71866131Swpaul m_new->m_data = m_new->m_ext.ext_buf; 71966131Swpaul } 72066131Swpaul 72166131Swpaul m_adj(m_new, ETHER_ALIGN); 72266131Swpaul 72366131Swpaul sc->pcn_cdata.pcn_rx_chain[idx] = m_new; 72466131Swpaul c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); 72566131Swpaul c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; 72666131Swpaul c->pcn_bufsz |= PCN_RXLEN_MBO; 72766131Swpaul c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; 72866131Swpaul 72966131Swpaul return(0); 73066131Swpaul} 73166131Swpaul 73266131Swpaul/* 73366131Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 73466131Swpaul * the higher level protocols. 73566131Swpaul */ 73666131Swpaulstatic void pcn_rxeof(sc) 73766131Swpaul struct pcn_softc *sc; 73866131Swpaul{ 73966131Swpaul struct ether_header *eh; 74066131Swpaul struct mbuf *m; 74166131Swpaul struct ifnet *ifp; 74266131Swpaul struct pcn_rx_desc *cur_rx; 74366131Swpaul int i; 74466131Swpaul 74566131Swpaul ifp = &sc->arpcom.ac_if; 74666131Swpaul i = sc->pcn_cdata.pcn_rx_prod; 74766131Swpaul 74866131Swpaul while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { 74966131Swpaul cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; 75066131Swpaul m = sc->pcn_cdata.pcn_rx_chain[i]; 75166131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 75266131Swpaul 75366131Swpaul /* 75466131Swpaul * If an error occurs, update stats, clear the 75566131Swpaul * status word and leave the mbuf cluster in place: 75666131Swpaul * it should simply get re-used next time this descriptor 75766131Swpaul * comes up in the ring. 75866131Swpaul */ 75966131Swpaul if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { 76066131Swpaul ifp->if_ierrors++; 76166131Swpaul pcn_newbuf(sc, i, m); 76266131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 76366131Swpaul continue; 76466131Swpaul } 76566131Swpaul 76666592Swpaul if (pcn_newbuf(sc, i, NULL)) { 76766592Swpaul /* Ran out of mbufs; recycle this one. */ 76866592Swpaul pcn_newbuf(sc, i, m); 76966592Swpaul ifp->if_ierrors++; 77066592Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 77166592Swpaul continue; 77266592Swpaul } 77366592Swpaul 77466131Swpaul PCN_INC(i, PCN_RX_LIST_CNT); 77566131Swpaul 77666131Swpaul /* No errors; receive the packet. */ 77766131Swpaul ifp->if_ipackets++; 77866131Swpaul eh = mtod(m, struct ether_header *); 77966131Swpaul m->m_len = m->m_pkthdr.len = 78066131Swpaul cur_rx->pcn_rxlen - ETHER_CRC_LEN; 78166131Swpaul m->m_pkthdr.rcvif = ifp; 78266131Swpaul 78366131Swpaul /* Remove header from mbuf and pass it on. */ 78466131Swpaul m_adj(m, sizeof(struct ether_header)); 78566131Swpaul ether_input(ifp, eh, m); 78666131Swpaul } 78766131Swpaul 78866131Swpaul sc->pcn_cdata.pcn_rx_prod = i; 78966131Swpaul 79066131Swpaul return; 79166131Swpaul} 79266131Swpaul 79366131Swpaul/* 79466131Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 79566131Swpaul * the list buffers. 79666131Swpaul */ 79766131Swpaul 79866131Swpaulstatic void pcn_txeof(sc) 79966131Swpaul struct pcn_softc *sc; 80066131Swpaul{ 80166131Swpaul struct pcn_tx_desc *cur_tx = NULL; 80266131Swpaul struct ifnet *ifp; 80366131Swpaul u_int32_t idx; 80466131Swpaul 80566131Swpaul ifp = &sc->arpcom.ac_if; 80666131Swpaul 80766131Swpaul /* Clear the timeout timer. */ 80866131Swpaul ifp->if_timer = 0; 80966131Swpaul 81066131Swpaul /* 81166131Swpaul * Go through our tx list and free mbufs for those 81266131Swpaul * frames that have been transmitted. 81366131Swpaul */ 81466131Swpaul idx = sc->pcn_cdata.pcn_tx_cons; 81566131Swpaul while (idx != sc->pcn_cdata.pcn_tx_prod) { 81666131Swpaul cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; 81766131Swpaul 81866131Swpaul if (!PCN_OWN_TXDESC(cur_tx)) 81966131Swpaul break; 82066131Swpaul 82166131Swpaul if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { 82266131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 82366131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 82466131Swpaul continue; 82566131Swpaul } 82666131Swpaul 82766131Swpaul if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { 82866131Swpaul ifp->if_oerrors++; 82966131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) 83066131Swpaul ifp->if_collisions++; 83166131Swpaul if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) 83266131Swpaul ifp->if_collisions++; 83366131Swpaul } 83466131Swpaul 83566131Swpaul ifp->if_collisions += 83666131Swpaul cur_tx->pcn_txstat & PCN_TXSTAT_TRC; 83766131Swpaul 83866131Swpaul ifp->if_opackets++; 83966131Swpaul if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { 84066131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); 84166131Swpaul sc->pcn_cdata.pcn_tx_chain[idx] = NULL; 84266131Swpaul } 84366131Swpaul 84466131Swpaul sc->pcn_cdata.pcn_tx_cnt--; 84566131Swpaul PCN_INC(idx, PCN_TX_LIST_CNT); 84666131Swpaul ifp->if_timer = 0; 84766131Swpaul } 84866131Swpaul 84966131Swpaul sc->pcn_cdata.pcn_tx_cons = idx; 85066131Swpaul 85166131Swpaul if (cur_tx != NULL) 85266131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 85366131Swpaul 85466131Swpaul return; 85566131Swpaul} 85666131Swpaul 85766131Swpaulstatic void pcn_tick(xsc) 85866131Swpaul void *xsc; 85966131Swpaul{ 86066131Swpaul struct pcn_softc *sc; 86166131Swpaul struct mii_data *mii; 86266131Swpaul struct ifnet *ifp; 86366131Swpaul int s; 86466131Swpaul 86566131Swpaul s = splimp(); 86666131Swpaul 86766131Swpaul sc = xsc; 86866131Swpaul ifp = &sc->arpcom.ac_if; 86966131Swpaul 87066131Swpaul mii = device_get_softc(sc->pcn_miibus); 87166131Swpaul mii_tick(mii); 87266131Swpaul 87366131Swpaul if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) 87466131Swpaul sc->pcn_link = 0; 87566131Swpaul 87666131Swpaul if (!sc->pcn_link) { 87766131Swpaul mii_pollstat(mii); 87866131Swpaul if (mii->mii_media_status & IFM_ACTIVE && 87966131Swpaul IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) 88066131Swpaul sc->pcn_link++; 88166131Swpaul if (ifp->if_snd.ifq_head != NULL) 88266131Swpaul pcn_start(ifp); 88366131Swpaul } 88466131Swpaul 88566131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 88666131Swpaul 88766131Swpaul splx(s); 88866131Swpaul 88966131Swpaul return; 89066131Swpaul} 89166131Swpaul 89266131Swpaulstatic void pcn_intr(arg) 89366131Swpaul void *arg; 89466131Swpaul{ 89566131Swpaul struct pcn_softc *sc; 89666131Swpaul struct ifnet *ifp; 89766131Swpaul u_int32_t status; 89866131Swpaul 89966131Swpaul sc = arg; 90066131Swpaul ifp = &sc->arpcom.ac_if; 90166131Swpaul 90266131Swpaul /* Supress unwanted interrupts */ 90366131Swpaul if (!(ifp->if_flags & IFF_UP)) { 90466131Swpaul pcn_stop(sc); 90566131Swpaul return; 90666131Swpaul } 90766131Swpaul 90866131Swpaul CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); 90966131Swpaul 91066131Swpaul while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { 91166131Swpaul CSR_WRITE_4(sc, PCN_IO32_RDP, status); 91266131Swpaul 91366131Swpaul if (status & PCN_CSR_RINT) 91466131Swpaul pcn_rxeof(sc); 91566131Swpaul 91666131Swpaul if (status & PCN_CSR_TINT) 91766131Swpaul pcn_txeof(sc); 91866131Swpaul 91966131Swpaul if (status & PCN_CSR_ERR) { 92066131Swpaul pcn_init(sc); 92166131Swpaul break; 92266131Swpaul } 92366131Swpaul } 92466131Swpaul 92566131Swpaul if (ifp->if_snd.ifq_head != NULL) 92666131Swpaul pcn_start(ifp); 92766131Swpaul 92866131Swpaul return; 92966131Swpaul} 93066131Swpaul 93166131Swpaul/* 93266131Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 93366131Swpaul * pointers to the fragment pointers. 93466131Swpaul */ 93566131Swpaulstatic int pcn_encap(sc, m_head, txidx) 93666131Swpaul struct pcn_softc *sc; 93766131Swpaul struct mbuf *m_head; 93866131Swpaul u_int32_t *txidx; 93966131Swpaul{ 94066131Swpaul struct pcn_tx_desc *f = NULL; 94166131Swpaul struct mbuf *m; 94266131Swpaul int frag, cur, cnt = 0; 94366131Swpaul 94466131Swpaul /* 94566131Swpaul * Start packing the mbufs in this chain into 94666131Swpaul * the fragment pointers. Stop when we run out 94766131Swpaul * of fragments or hit the end of the mbuf chain. 94866131Swpaul */ 94966131Swpaul m = m_head; 95066131Swpaul cur = frag = *txidx; 95166131Swpaul 95266131Swpaul for (m = m_head; m != NULL; m = m->m_next) { 95366131Swpaul if (m->m_len != 0) { 95466131Swpaul if ((PCN_TX_LIST_CNT - 95566131Swpaul (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) 95666131Swpaul return(ENOBUFS); 95766131Swpaul f = &sc->pcn_ldata->pcn_tx_list[frag]; 95866131Swpaul f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; 95966131Swpaul f->pcn_txctl |= PCN_TXCTL_MBO; 96066131Swpaul f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); 96166131Swpaul if (cnt == 0) 96266131Swpaul f->pcn_txctl |= PCN_TXCTL_STP; 96366131Swpaul else 96466131Swpaul f->pcn_txctl |= PCN_TXCTL_OWN; 96566131Swpaul cur = frag; 96666131Swpaul PCN_INC(frag, PCN_TX_LIST_CNT); 96766131Swpaul cnt++; 96866131Swpaul } 96966131Swpaul } 97066131Swpaul 97166131Swpaul if (m != NULL) 97266131Swpaul return(ENOBUFS); 97366131Swpaul 97466131Swpaul sc->pcn_cdata.pcn_tx_chain[cur] = m_head; 97566131Swpaul sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= 97666131Swpaul PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; 97766131Swpaul sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; 97866131Swpaul sc->pcn_cdata.pcn_tx_cnt += cnt; 97966131Swpaul *txidx = frag; 98066131Swpaul 98166131Swpaul return(0); 98266131Swpaul} 98366131Swpaul 98466131Swpaul/* 98566131Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 98666131Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 98766131Swpaul * copy of the pointers since the transmit list fragment pointers are 98866131Swpaul * physical addresses. 98966131Swpaul */ 99066131Swpaulstatic void pcn_start(ifp) 99166131Swpaul struct ifnet *ifp; 99266131Swpaul{ 99366131Swpaul struct pcn_softc *sc; 99466131Swpaul struct mbuf *m_head = NULL; 99566131Swpaul u_int32_t idx; 99666131Swpaul 99766131Swpaul sc = ifp->if_softc; 99866131Swpaul 99966131Swpaul if (!sc->pcn_link) 100066131Swpaul return; 100166131Swpaul 100266131Swpaul idx = sc->pcn_cdata.pcn_tx_prod; 100366131Swpaul 100466131Swpaul if (ifp->if_flags & IFF_OACTIVE) 100566131Swpaul return; 100666131Swpaul 100766131Swpaul while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { 100866131Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 100966131Swpaul if (m_head == NULL) 101066131Swpaul break; 101166131Swpaul 101266131Swpaul if (pcn_encap(sc, m_head, &idx)) { 101366131Swpaul IF_PREPEND(&ifp->if_snd, m_head); 101466131Swpaul ifp->if_flags |= IFF_OACTIVE; 101566131Swpaul break; 101666131Swpaul } 101766131Swpaul 101866131Swpaul /* 101966131Swpaul * If there's a BPF listener, bounce a copy of this frame 102066131Swpaul * to him. 102166131Swpaul */ 102266131Swpaul if (ifp->if_bpf) 102366131Swpaul bpf_mtap(ifp, m_head); 102466131Swpaul 102566131Swpaul } 102666131Swpaul 102766131Swpaul /* Transmit */ 102866131Swpaul sc->pcn_cdata.pcn_tx_prod = idx; 102966131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); 103066131Swpaul 103166131Swpaul /* 103266131Swpaul * Set a timeout in case the chip goes out to lunch. 103366131Swpaul */ 103466131Swpaul ifp->if_timer = 5; 103566131Swpaul 103666131Swpaul return; 103766131Swpaul} 103866131Swpaul 103966131Swpaulstatic void pcn_init(xsc) 104066131Swpaul void *xsc; 104166131Swpaul{ 104266131Swpaul struct pcn_softc *sc = xsc; 104366131Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 104466131Swpaul struct mii_data *mii = NULL; 104566131Swpaul int s; 104666131Swpaul 104766131Swpaul s = splimp(); 104866131Swpaul 104966131Swpaul /* 105066131Swpaul * Cancel pending I/O and free all RX/TX buffers. 105166131Swpaul */ 105266131Swpaul pcn_stop(sc); 105366131Swpaul pcn_reset(sc); 105466131Swpaul 105566131Swpaul mii = device_get_softc(sc->pcn_miibus); 105666131Swpaul 105766131Swpaul /* Set MAC address */ 105866131Swpaul pcn_csr_write(sc, PCN_CSR_PAR0, 105966131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 106066131Swpaul pcn_csr_write(sc, PCN_CSR_PAR1, 106166131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 106266131Swpaul pcn_csr_write(sc, PCN_CSR_PAR2, 106366131Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 106466131Swpaul 106566131Swpaul /* Init circular RX list. */ 106666131Swpaul if (pcn_list_rx_init(sc) == ENOBUFS) { 106766131Swpaul printf("pcn%d: initialization failed: no " 106866131Swpaul "memory for rx buffers\n", sc->pcn_unit); 106966131Swpaul pcn_stop(sc); 107066131Swpaul (void)splx(s); 107166131Swpaul return; 107266131Swpaul } 107366131Swpaul 107466131Swpaul /* 107566131Swpaul * Init tx descriptors. 107666131Swpaul */ 107766131Swpaul pcn_list_tx_init(sc); 107866131Swpaul 107966131Swpaul /* Set up the mode register. */ 108066131Swpaul pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); 108166131Swpaul 108266131Swpaul /* If we want promiscuous mode, set the allframes bit. */ 108366131Swpaul if (ifp->if_flags & IFF_PROMISC) { 108466131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 108566131Swpaul } else { 108666131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); 108766131Swpaul } 108866131Swpaul 108966131Swpaul /* Set the capture broadcast bit to capture broadcast frames. */ 109066131Swpaul if (ifp->if_flags & IFF_BROADCAST) { 109166131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 109266131Swpaul } else { 109366131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); 109466131Swpaul } 109566131Swpaul 109666131Swpaul /* 109766131Swpaul * Load the multicast filter. 109866131Swpaul */ 109966131Swpaul pcn_setmulti(sc); 110066131Swpaul 110166131Swpaul /* 110266131Swpaul * Load the addresses of the RX and TX lists. 110366131Swpaul */ 110466131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR0, 110566131Swpaul vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); 110666131Swpaul pcn_csr_write(sc, PCN_CSR_RXADDR1, 110766131Swpaul (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); 110866131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR0, 110966131Swpaul vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); 111066131Swpaul pcn_csr_write(sc, PCN_CSR_TXADDR1, 111166131Swpaul (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); 111266131Swpaul 111366131Swpaul /* Set the RX and TX ring sizes. */ 111466131Swpaul pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); 111566131Swpaul pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); 111666131Swpaul 111766131Swpaul /* We're not using the initialization block. */ 111866131Swpaul pcn_csr_write(sc, PCN_CSR_IAB1, 0); 111966131Swpaul 112066131Swpaul /* Enable fast suspend mode. */ 112166131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); 112266131Swpaul 112366131Swpaul /* 112466131Swpaul * Enable burst read and write. Also set the no underflow 112566131Swpaul * bit. This will avoid transmit underruns in certain 112666210Swpaul * conditions while still providing decent performance. 112766131Swpaul */ 112866131Swpaul PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| 112966131Swpaul PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); 113066131Swpaul 113166131Swpaul /* Enable graceful recovery from underflow. */ 113266131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); 113366131Swpaul 113466131Swpaul /* Enable auto-padding of short TX frames. */ 113566131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); 113666131Swpaul 113766131Swpaul /* Disable MII autoneg (we handle this ourselves). */ 113866131Swpaul PCN_BCR_CLRBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); 113966131Swpaul 114066131Swpaul if (sc->pcn_type == Am79C978) 114166131Swpaul pcn_bcr_write(sc, PCN_BCR_PHYSEL, 114266131Swpaul PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); 114366131Swpaul 114466131Swpaul /* Enable interrupts and start the controller running. */ 114566131Swpaul pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); 114666131Swpaul 114766131Swpaul mii_mediachg(mii); 114866131Swpaul 114966131Swpaul ifp->if_flags |= IFF_RUNNING; 115066131Swpaul ifp->if_flags &= ~IFF_OACTIVE; 115166131Swpaul 115266131Swpaul (void)splx(s); 115366131Swpaul sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); 115466131Swpaul 115566131Swpaul return; 115666131Swpaul} 115766131Swpaul 115866131Swpaul/* 115966131Swpaul * Set media options. 116066131Swpaul */ 116166131Swpaulstatic int pcn_ifmedia_upd(ifp) 116266131Swpaul struct ifnet *ifp; 116366131Swpaul{ 116466131Swpaul struct pcn_softc *sc; 116566131Swpaul struct mii_data *mii; 116666131Swpaul 116766131Swpaul sc = ifp->if_softc; 116866131Swpaul mii = device_get_softc(sc->pcn_miibus); 116966131Swpaul 117066131Swpaul sc->pcn_link = 0; 117166131Swpaul if (mii->mii_instance) { 117266131Swpaul struct mii_softc *miisc; 117366131Swpaul for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 117466131Swpaul miisc = LIST_NEXT(miisc, mii_list)) 117566131Swpaul mii_phy_reset(miisc); 117666131Swpaul } 117766131Swpaul mii_mediachg(mii); 117866131Swpaul 117966131Swpaul return(0); 118066131Swpaul} 118166131Swpaul 118266131Swpaul/* 118366131Swpaul * Report current media status. 118466131Swpaul */ 118566131Swpaulstatic void pcn_ifmedia_sts(ifp, ifmr) 118666131Swpaul struct ifnet *ifp; 118766131Swpaul struct ifmediareq *ifmr; 118866131Swpaul{ 118966131Swpaul struct pcn_softc *sc; 119066131Swpaul struct mii_data *mii; 119166131Swpaul 119266131Swpaul sc = ifp->if_softc; 119366131Swpaul 119466131Swpaul mii = device_get_softc(sc->pcn_miibus); 119566131Swpaul mii_pollstat(mii); 119666131Swpaul ifmr->ifm_active = mii->mii_media_active; 119766131Swpaul ifmr->ifm_status = mii->mii_media_status; 119866131Swpaul 119966131Swpaul return; 120066131Swpaul} 120166131Swpaul 120266131Swpaulstatic int pcn_ioctl(ifp, command, data) 120366131Swpaul struct ifnet *ifp; 120466131Swpaul u_long command; 120566131Swpaul caddr_t data; 120666131Swpaul{ 120766131Swpaul struct pcn_softc *sc = ifp->if_softc; 120866131Swpaul struct ifreq *ifr = (struct ifreq *) data; 120966131Swpaul struct mii_data *mii = NULL; 121066131Swpaul int s, error = 0; 121166131Swpaul 121266131Swpaul s = splimp(); 121366131Swpaul 121466131Swpaul switch(command) { 121566131Swpaul case SIOCSIFADDR: 121666131Swpaul case SIOCGIFADDR: 121766131Swpaul case SIOCSIFMTU: 121866131Swpaul error = ether_ioctl(ifp, command, data); 121966131Swpaul break; 122066131Swpaul case SIOCSIFFLAGS: 122166131Swpaul if (ifp->if_flags & IFF_UP) { 122266131Swpaul if (ifp->if_flags & IFF_RUNNING && 122366131Swpaul ifp->if_flags & IFF_PROMISC && 122466131Swpaul !(sc->pcn_if_flags & IFF_PROMISC)) { 122566131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 122666131Swpaul PCN_EXTCTL1_SPND); 122766131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_MODE, 122866131Swpaul PCN_MODE_PROMISC); 122966131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 123066131Swpaul PCN_EXTCTL1_SPND); 123166131Swpaul } else if (ifp->if_flags & IFF_RUNNING && 123266131Swpaul !(ifp->if_flags & IFF_PROMISC) && 123366131Swpaul sc->pcn_if_flags & IFF_PROMISC) { 123466131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, 123566131Swpaul PCN_EXTCTL1_SPND); 123666131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, 123766131Swpaul PCN_MODE_PROMISC); 123866131Swpaul PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, 123966131Swpaul PCN_EXTCTL1_SPND); 124066131Swpaul } else if (!(ifp->if_flags & IFF_RUNNING)) 124166131Swpaul pcn_init(sc); 124266131Swpaul } else { 124366131Swpaul if (ifp->if_flags & IFF_RUNNING) 124466131Swpaul pcn_stop(sc); 124566131Swpaul } 124666131Swpaul sc->pcn_if_flags = ifp->if_flags; 124766131Swpaul error = 0; 124866131Swpaul break; 124966131Swpaul case SIOCADDMULTI: 125066131Swpaul case SIOCDELMULTI: 125166131Swpaul pcn_setmulti(sc); 125266131Swpaul error = 0; 125366131Swpaul break; 125466131Swpaul case SIOCGIFMEDIA: 125566131Swpaul case SIOCSIFMEDIA: 125666131Swpaul mii = device_get_softc(sc->pcn_miibus); 125766131Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 125866131Swpaul break; 125966131Swpaul default: 126066131Swpaul error = EINVAL; 126166131Swpaul break; 126266131Swpaul } 126366131Swpaul 126466131Swpaul (void)splx(s); 126566131Swpaul 126666131Swpaul return(error); 126766131Swpaul} 126866131Swpaul 126966131Swpaulstatic void pcn_watchdog(ifp) 127066131Swpaul struct ifnet *ifp; 127166131Swpaul{ 127266131Swpaul struct pcn_softc *sc; 127366131Swpaul 127466131Swpaul sc = ifp->if_softc; 127566131Swpaul 127666131Swpaul ifp->if_oerrors++; 127766131Swpaul printf("pcn%d: watchdog timeout\n", sc->pcn_unit); 127866131Swpaul 127966131Swpaul pcn_stop(sc); 128066131Swpaul pcn_reset(sc); 128166131Swpaul pcn_init(sc); 128266131Swpaul 128366131Swpaul if (ifp->if_snd.ifq_head != NULL) 128466131Swpaul pcn_start(ifp); 128566131Swpaul 128666131Swpaul return; 128766131Swpaul} 128866131Swpaul 128966131Swpaul/* 129066131Swpaul * Stop the adapter and free any mbufs allocated to the 129166131Swpaul * RX and TX lists. 129266131Swpaul */ 129366131Swpaulstatic void pcn_stop(sc) 129466131Swpaul struct pcn_softc *sc; 129566131Swpaul{ 129666131Swpaul register int i; 129766131Swpaul struct ifnet *ifp; 129866131Swpaul 129966131Swpaul ifp = &sc->arpcom.ac_if; 130066131Swpaul ifp->if_timer = 0; 130166131Swpaul 130266131Swpaul untimeout(pcn_tick, sc, sc->pcn_stat_ch); 130366131Swpaul PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); 130466131Swpaul sc->pcn_link = 0; 130566131Swpaul 130666131Swpaul /* 130766131Swpaul * Free data in the RX lists. 130866131Swpaul */ 130966131Swpaul for (i = 0; i < PCN_RX_LIST_CNT; i++) { 131066131Swpaul if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { 131166131Swpaul m_freem(sc->pcn_cdata.pcn_rx_chain[i]); 131266131Swpaul sc->pcn_cdata.pcn_rx_chain[i] = NULL; 131366131Swpaul } 131466131Swpaul } 131566131Swpaul bzero((char *)&sc->pcn_ldata->pcn_rx_list, 131666131Swpaul sizeof(sc->pcn_ldata->pcn_rx_list)); 131766131Swpaul 131866131Swpaul /* 131966131Swpaul * Free the TX list buffers. 132066131Swpaul */ 132166131Swpaul for (i = 0; i < PCN_TX_LIST_CNT; i++) { 132266131Swpaul if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { 132366131Swpaul m_freem(sc->pcn_cdata.pcn_tx_chain[i]); 132466131Swpaul sc->pcn_cdata.pcn_tx_chain[i] = NULL; 132566131Swpaul } 132666131Swpaul } 132766131Swpaul 132866131Swpaul bzero((char *)&sc->pcn_ldata->pcn_tx_list, 132966131Swpaul sizeof(sc->pcn_ldata->pcn_tx_list)); 133066131Swpaul 133166131Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 133266131Swpaul 133366131Swpaul return; 133466131Swpaul} 133566131Swpaul 133666131Swpaul/* 133766131Swpaul * Stop all chip I/O so that the kernel's probe routines don't 133866131Swpaul * get confused by errant DMAs when rebooting. 133966131Swpaul */ 134066131Swpaulstatic void pcn_shutdown(dev) 134166131Swpaul device_t dev; 134266131Swpaul{ 134366131Swpaul struct pcn_softc *sc; 134466131Swpaul 134566131Swpaul sc = device_get_softc(dev); 134666131Swpaul 134766131Swpaul pcn_reset(sc); 134866131Swpaul pcn_stop(sc); 134966131Swpaul 135066131Swpaul return; 135166131Swpaul} 1352