From 8c7c350b188c9298546fe6bc9eee0182fbf5e7e5 Mon Sep 17 00:00:00 2001 From: imp Date: Thu, 14 Aug 2008 20:34:46 +0000 Subject: Move pcn driver from sys/pci to sys/dev/pcn. --- sys/conf/files | 2 +- sys/dev/pcn/if_pcn.c | 1527 ++++++++++++++++++++++++++++++++++++++++++++++ sys/dev/pcn/if_pcnreg.h | 530 ++++++++++++++++ sys/modules/pcn/Makefile | 2 +- sys/pci/if_pcn.c | 1527 ---------------------------------------------- sys/pci/if_pcnreg.h | 530 ---------------- 6 files changed, 2059 insertions(+), 2059 deletions(-) create mode 100644 sys/dev/pcn/if_pcn.c create mode 100644 sys/dev/pcn/if_pcnreg.h delete mode 100644 sys/pci/if_pcn.c delete mode 100644 sys/pci/if_pcnreg.h (limited to 'sys') diff --git a/sys/conf/files b/sys/conf/files index f7b7f4f..1d59a09 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -977,6 +977,7 @@ dev/pci/pci_pci.c optional pci dev/pci/pci_user.c optional pci dev/pci/pcib_if.m standard dev/pci/vga_pci.c optional pci +dev/pcn/if_pcn.c optional pcn pci dev/pdq/if_fea.c optional fea eisa dev/pdq/if_fpa.c optional fpa pci dev/pdq/pdq.c optional nowerror fea eisa | fpa pci @@ -2085,7 +2086,6 @@ pci/alpm.c optional alpm pci pci/amdpm.c optional amdpm pci | nfpm pci pci/amdsmb.c optional amdsmb pci pci/if_mn.c optional mn pci -pci/if_pcn.c optional pcn pci pci/if_rl.c optional rl pci pci/if_wb.c optional wb pci pci/intpm.c optional intpm pci diff --git a/sys/dev/pcn/if_pcn.c b/sys/dev/pcn/if_pcn.c new file mode 100644 index 0000000..eccbda0 --- /dev/null +++ b/sys/dev/pcn/if_pcn.c @@ -0,0 +1,1527 @@ +/*- + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * AMD Am79c972 fast ethernet PCI NIC driver. Datasheets are available + * from http://www.amd.com. + * + * The AMD PCnet/PCI controllers are more advanced and functional + * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain + * backwards compatibility with the LANCE and thus can be made + * to work with older LANCE drivers. This is in fact how the + * PCnet/PCI chips were supported in FreeBSD originally. The trouble + * is that the PCnet/PCI devices offer several performance enhancements + * which can't be exploited in LANCE compatibility mode. Chief among + * these enhancements is the ability to perform PCI DMA operations + * using 32-bit addressing (which eliminates the need for ISA + * bounce-buffering), and special receive buffer alignment (which + * allows the receive handler to pass packets to the upper protocol + * layers without copying on both the x86 and alpha platforms). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include /* for vtophys */ +#include /* for vtophys */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define PCN_USEIOSPACE + +#include + +MODULE_DEPEND(pcn, pci, 1, 1, 1); +MODULE_DEPEND(pcn, ether, 1, 1, 1); +MODULE_DEPEND(pcn, miibus, 1, 1, 1); + +/* "device miibus" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +/* + * Various supported device vendors/types and their names. + */ +static const struct pcn_type pcn_devs[] = { + { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, + { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, + { 0, 0, NULL } +}; + +static const struct pcn_chipid { + u_int32_t id; + const char *name; +} pcn_chipid[] = { + { Am79C971, "Am79C971" }, + { Am79C972, "Am79C972" }, + { Am79C973, "Am79C973" }, + { Am79C978, "Am79C978" }, + { Am79C975, "Am79C975" }, + { Am79C976, "Am79C976" }, + { 0, NULL }, +}; + +static const char *pcn_chipid_name(u_int32_t); +static u_int32_t pcn_chip_id(device_t); +static const struct pcn_type *pcn_match(u_int16_t, u_int16_t); + +static u_int32_t pcn_csr_read(struct pcn_softc *, int); +static u_int16_t pcn_csr_read16(struct pcn_softc *, int); +static u_int16_t pcn_bcr_read16(struct pcn_softc *, int); +static void pcn_csr_write(struct pcn_softc *, int, int); +static u_int32_t pcn_bcr_read(struct pcn_softc *, int); +static void pcn_bcr_write(struct pcn_softc *, int, int); + +static int pcn_probe(device_t); +static int pcn_attach(device_t); +static int pcn_detach(device_t); + +static int pcn_newbuf(struct pcn_softc *, int, struct mbuf *); +static int pcn_encap(struct pcn_softc *, struct mbuf *, u_int32_t *); +static void pcn_rxeof(struct pcn_softc *); +static void pcn_txeof(struct pcn_softc *); +static void pcn_intr(void *); +static void pcn_tick(void *); +static void pcn_start(struct ifnet *); +static void pcn_start_locked(struct ifnet *); +static int pcn_ioctl(struct ifnet *, u_long, caddr_t); +static void pcn_init(void *); +static void pcn_init_locked(struct pcn_softc *); +static void pcn_stop(struct pcn_softc *); +static void pcn_watchdog(struct ifnet *); +static void pcn_shutdown(device_t); +static int pcn_ifmedia_upd(struct ifnet *); +static void pcn_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static int pcn_miibus_readreg(device_t, int, int); +static int pcn_miibus_writereg(device_t, int, int, int); +static void pcn_miibus_statchg(device_t); + +static void pcn_setfilt(struct ifnet *); +static void pcn_setmulti(struct pcn_softc *); +static void pcn_reset(struct pcn_softc *); +static int pcn_list_rx_init(struct pcn_softc *); +static int pcn_list_tx_init(struct pcn_softc *); + +#ifdef PCN_USEIOSPACE +#define PCN_RES SYS_RES_IOPORT +#define PCN_RID PCN_PCI_LOIO +#else +#define PCN_RES SYS_RES_MEMORY +#define PCN_RID PCN_PCI_LOMEM +#endif + +static device_method_t pcn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pcn_probe), + DEVMETHOD(device_attach, pcn_attach), + DEVMETHOD(device_detach, pcn_detach), + DEVMETHOD(device_shutdown, pcn_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, pcn_miibus_readreg), + DEVMETHOD(miibus_writereg, pcn_miibus_writereg), + DEVMETHOD(miibus_statchg, pcn_miibus_statchg), + + { 0, 0 } +}; + +static driver_t pcn_driver = { + "pcn", + pcn_methods, + sizeof(struct pcn_softc) +}; + +static devclass_t pcn_devclass; + +DRIVER_MODULE(pcn, pci, pcn_driver, pcn_devclass, 0, 0); +DRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); + +#define PCN_CSR_SETBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) + +#define PCN_CSR_CLRBIT(sc, reg, x) \ + pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) + +#define PCN_BCR_SETBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) + +#define PCN_BCR_CLRBIT(sc, reg, x) \ + pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) + +static u_int32_t +pcn_csr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_RDP)); +} + +static u_int16_t +pcn_csr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_RDP)); +} + +static void +pcn_csr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; + int val; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_RDP, val); + return; +} + +static u_int32_t +pcn_bcr_read(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + return(CSR_READ_4(sc, PCN_IO32_BDP)); +} + +static u_int16_t +pcn_bcr_read16(sc, reg) + struct pcn_softc *sc; + int reg; +{ + CSR_WRITE_2(sc, PCN_IO16_RAP, reg); + return(CSR_READ_2(sc, PCN_IO16_BDP)); +} + +static void +pcn_bcr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; + int val; +{ + CSR_WRITE_4(sc, PCN_IO32_RAP, reg); + CSR_WRITE_4(sc, PCN_IO32_BDP, val); + return; +} + +static int +pcn_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct pcn_softc *sc; + int val; + + sc = device_get_softc(dev); + + /* + * At least Am79C971 with DP83840A wedge when isolating the + * external PHY so we can't allow multiple external PHYs. + * There are cards that use Am79C971 with both the internal + * and an external PHY though. + * For internal PHYs it doesn't really matter whether we can + * isolate the remaining internal and the external ones in + * the PHY drivers as the internal PHYs have to be enabled + * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc. + * With Am79C97{3,5,8} we don't support switching beetween + * the internal and external PHYs, yet, so we can't allow + * multiple PHYs with these either. + * Am79C97{2,6} actually only support external PHYs (not + * connectable internal ones respond at the usual addresses, + * which don't hurt if we let them show up on the bus) and + * isolating them works. + */ + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr != -1 && + phy != sc->pcn_extphyaddr) + return(0); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; + if (val == 0xFFFF) + return(0); + + if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || + sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || + sc->pcn_type == Am79C978) && sc->pcn_extphyaddr == -1) + sc->pcn_extphyaddr = phy; + + return(val); +} + +static int +pcn_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); + pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); + + return(0); +} + +static void +pcn_miibus_statchg(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(sc->pcn_miibus); + + if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { + PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } else { + PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); + } + + return; +} + +static void +pcn_setmulti(sc) + struct pcn_softc *sc; +{ + struct ifnet *ifp; + struct ifmultiaddr *ifma; + u_int32_t h, i; + u_int16_t hashes[4] = { 0, 0, 0, 0 }; + + ifp = sc->pcn_ifp; + + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + return; + } + + /* first, zot all the existing hash bits */ + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); + + /* now program new ones */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_le(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + hashes[h >> 4] |= 1 << (h & 0xF); + } + IF_ADDR_UNLOCK(ifp); + + for (i = 0; i < 4; i++) + pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); + + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); + + return; +} + +static void +pcn_reset(sc) + struct pcn_softc *sc; +{ + /* + * Issue a reset by reading from the RESET register. + * Note that we don't know if the chip is operating in + * 16-bit or 32-bit mode at this point, so we attempt + * to reset the chip both ways. If one fails, the other + * will succeed. + */ + CSR_READ_2(sc, PCN_IO16_RESET); + CSR_READ_4(sc, PCN_IO32_RESET); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + /* Select 32-bit (DWIO) mode */ + CSR_WRITE_4(sc, PCN_IO32_RDP, 0); + + /* Select software style 3. */ + pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); + + return; +} + +static const char * +pcn_chipid_name(u_int32_t id) +{ + const struct pcn_chipid *p; + + p = pcn_chipid; + while (p->name) { + if (id == p->id) + return (p->name); + p++; + } + return ("Unknown"); +} + +static u_int32_t +pcn_chip_id(device_t dev) +{ + struct pcn_softc *sc; + u_int32_t chip_id; + + sc = device_get_softc(dev); + /* + * Note: we can *NOT* put the chip into + * 32-bit mode yet. The le(4) driver will only + * work in 16-bit mode, and once the chip + * goes into 32-bit mode, the only way to + * get it out again is with a hardware reset. + * So if pcn_probe() is called before the + * le(4) driver's probe routine, the chip will + * be locked into 32-bit operation and the + * le(4) driver will be unable to attach to it. + * Note II: if the chip happens to already + * be in 32-bit mode, we still need to check + * the chip ID, but first we have to detect + * 32-bit mode using only 16-bit operations. + * The safest way to do this is to read the + * PCI subsystem ID from BCR23/24 and compare + * that with the value read from PCI config + * space. + */ + chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); + chip_id <<= 16; + chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); + /* + * Note III: the test for 0x10001000 is a hack to + * pacify VMware, who's pseudo-PCnet interface is + * broken. Reading the subsystem register from PCI + * config space yields 0x00000000 while reading the + * same value from I/O space yields 0x10001000. It's + * not supposed to be that way. + */ + if (chip_id == pci_read_config(dev, + PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { + /* We're in 16-bit mode. */ + chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); + } else { + /* We're in 32-bit mode. */ + chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); + } + + return (chip_id); +} + +static const struct pcn_type * +pcn_match(u_int16_t vid, u_int16_t did) +{ + const struct pcn_type *t; + + t = pcn_devs; + while (t->pcn_name != NULL) { + if ((vid == t->pcn_vid) && (did == t->pcn_did)) + return (t); + t++; + } + return (NULL); +} + +/* + * Probe for an AMD chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +pcn_probe(dev) + device_t dev; +{ + const struct pcn_type *t; + struct pcn_softc *sc; + int rid; + u_int32_t chip_id; + + t = pcn_match(pci_get_vendor(dev), pci_get_device(dev)); + if (t == NULL) + return (ENXIO); + sc = device_get_softc(dev); + + /* + * Temporarily map the I/O space so we can read the chip ID register. + */ + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); + if (sc->pcn_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + return(ENXIO); + } + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + + chip_id = pcn_chip_id(dev); + + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + switch((chip_id >> 12) & PART_MASK) { + case Am79C971: + case Am79C972: + case Am79C973: + case Am79C975: + case Am79C976: + case Am79C978: + break; + default: + return(ENXIO); + } + device_set_desc(dev, t->pcn_name); + return(BUS_PROBE_DEFAULT); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +pcn_attach(dev) + device_t dev; +{ + u_int32_t eaddr[2]; + struct pcn_softc *sc; + struct mii_data *mii; + struct mii_softc *miisc; + struct ifnet *ifp; + int error = 0, rid; + + sc = device_get_softc(dev); + + /* Initialize our mutex. */ + mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + + /* Retrieve the chip ID */ + sc->pcn_type = (pcn_chip_id(dev) >> 12) & PART_MASK; + device_printf(dev, "Chip ID %04x (%s)\n", + sc->pcn_type, pcn_chipid_name(sc->pcn_type)); + + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); + + if (sc->pcn_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + + sc->pcn_btag = rman_get_bustag(sc->pcn_res); + sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); + + /* Allocate interrupt */ + rid = 0; + sc->pcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->pcn_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Reset the adapter. */ + pcn_reset(sc); + + /* + * Get station address from the EEPROM. + */ + eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); + eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); + + callout_init_mtx(&sc->pcn_stat_callout, &sc->pcn_mtx, 0); + + sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->pcn_ldata == NULL) { + device_printf(dev, "no memory for list buffers!\n"); + error = ENXIO; + goto fail; + } + bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); + + ifp = sc->pcn_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + error = ENOSPC; + goto fail; + } + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = pcn_ioctl; + ifp->if_start = pcn_start; + ifp->if_watchdog = pcn_watchdog; + ifp->if_init = pcn_init; + ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + sc->pcn_extphyaddr = -1; + if (mii_phy_probe(dev, &sc->pcn_miibus, + pcn_ifmedia_upd, pcn_ifmedia_sts)) { + device_printf(dev, "MII without any PHY!\n"); + error = ENXIO; + goto fail; + } + /* + * Record the media instances of internal PHYs, which map the + * built-in interfaces to the MII, so we can set the active + * PHY/port based on the currently selected media. + */ + sc->pcn_inst_10bt = -1; + mii = device_get_softc(sc->pcn_miibus); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + switch (miisc->mii_phy) { + case PCN_PHYAD_10BT: + sc->pcn_inst_10bt = miisc->mii_inst; + break; + /* + * XXX deal with the Am79C97{3,5} internal 100baseT + * and the Am79C978 internal HomePNA PHYs. + */ + } + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, (u_int8_t *) eaddr); + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, pcn_intr, sc, &sc->pcn_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + pcn_detach(dev); + + return(error); +} + +/* + * Shutdown hardware and free up resources. This can be called any + * time after the mutex has been initialized. It is called in both + * the error case in attach and the normal detach case so it needs + * to be careful about only freeing resources that have actually been + * allocated. + */ +static int +pcn_detach(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = sc->pcn_ifp; + + KASSERT(mtx_initialized(&sc->pcn_mtx), ("pcn mutex not initialized")); + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + PCN_LOCK(sc); + pcn_reset(sc); + pcn_stop(sc); + PCN_UNLOCK(sc); + callout_drain(&sc->pcn_stat_callout); + ether_ifdetach(ifp); + } + if (sc->pcn_miibus) + device_delete_child(dev, sc->pcn_miibus); + bus_generic_detach(dev); + + if (sc->pcn_intrhand) + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + if (sc->pcn_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + if (sc->pcn_res) + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + if (ifp) + if_free(ifp); + + if (sc->pcn_ldata) { + contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), + M_DEVBUF); + } + + mtx_destroy(&sc->pcn_mtx); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int +pcn_list_tx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_list_data *ld; + struct pcn_ring_data *cd; + int i; + + cd = &sc->pcn_cdata; + ld = sc->pcn_ldata; + + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + cd->pcn_tx_chain[i] = NULL; + ld->pcn_tx_list[i].pcn_tbaddr = 0; + ld->pcn_tx_list[i].pcn_txctl = 0; + ld->pcn_tx_list[i].pcn_txstat = 0; + } + + cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; + + return(0); +} + + +/* + * Initialize the RX descriptors and allocate mbufs for them. + */ +static int +pcn_list_rx_init(sc) + struct pcn_softc *sc; +{ + struct pcn_ring_data *cd; + int i; + + cd = &sc->pcn_cdata; + + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (pcn_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + } + + cd->pcn_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +pcn_newbuf(sc, idx, m) + struct pcn_softc *sc; + int idx; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct pcn_rx_desc *c; + + c = &sc->pcn_ldata->pcn_rx_list[idx]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return(ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, ETHER_ALIGN); + + sc->pcn_cdata.pcn_rx_chain[idx] = m_new; + c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); + c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; + c->pcn_bufsz |= PCN_RXLEN_MBO; + c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; + + return(0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +pcn_rxeof(sc) + struct pcn_softc *sc; +{ + struct mbuf *m; + struct ifnet *ifp; + struct pcn_rx_desc *cur_rx; + int i; + + PCN_LOCK_ASSERT(sc); + + ifp = sc->pcn_ifp; + i = sc->pcn_cdata.pcn_rx_prod; + + while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { + cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; + m = sc->pcn_cdata.pcn_rx_chain[i]; + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { + ifp->if_ierrors++; + pcn_newbuf(sc, i, m); + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + if (pcn_newbuf(sc, i, NULL)) { + /* Ran out of mbufs; recycle this one. */ + pcn_newbuf(sc, i, m); + ifp->if_ierrors++; + PCN_INC(i, PCN_RX_LIST_CNT); + continue; + } + + PCN_INC(i, PCN_RX_LIST_CNT); + + /* No errors; receive the packet. */ + ifp->if_ipackets++; + m->m_len = m->m_pkthdr.len = + cur_rx->pcn_rxlen - ETHER_CRC_LEN; + m->m_pkthdr.rcvif = ifp; + + PCN_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + PCN_LOCK(sc); + } + + sc->pcn_cdata.pcn_rx_prod = i; + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void +pcn_txeof(sc) + struct pcn_softc *sc; +{ + struct pcn_tx_desc *cur_tx = NULL; + struct ifnet *ifp; + u_int32_t idx; + + ifp = sc->pcn_ifp; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->pcn_cdata.pcn_tx_cons; + while (idx != sc->pcn_cdata.pcn_tx_prod) { + cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; + + if (!PCN_OWN_TXDESC(cur_tx)) + break; + + if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + continue; + } + + if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { + ifp->if_oerrors++; + if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) + ifp->if_collisions++; + if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) + ifp->if_collisions++; + } + + ifp->if_collisions += + cur_tx->pcn_txstat & PCN_TXSTAT_TRC; + + ifp->if_opackets++; + if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); + sc->pcn_cdata.pcn_tx_chain[idx] = NULL; + } + + sc->pcn_cdata.pcn_tx_cnt--; + PCN_INC(idx, PCN_TX_LIST_CNT); + } + + if (idx != sc->pcn_cdata.pcn_tx_cons) { + /* Some buffers have been freed. */ + sc->pcn_cdata.pcn_tx_cons = idx; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + } + ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5; + + return; +} + +static void +pcn_tick(xsc) + void *xsc; +{ + struct pcn_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + + sc = xsc; + ifp = sc->pcn_ifp; + PCN_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->pcn_miibus); + mii_tick(mii); + + /* link just died */ + if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) + sc->pcn_link = 0; + + /* link just came up, restart */ + if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->pcn_link++; + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + } + + callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); + + return; +} + +static void +pcn_intr(arg) + void *arg; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = sc->pcn_ifp; + + PCN_LOCK(sc); + + /* Suppress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + pcn_stop(sc); + PCN_UNLOCK(sc); + return; + } + + CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); + + while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { + CSR_WRITE_4(sc, PCN_IO32_RDP, status); + + if (status & PCN_CSR_RINT) + pcn_rxeof(sc); + + if (status & PCN_CSR_TINT) + pcn_txeof(sc); + + if (status & PCN_CSR_ERR) { + pcn_init_locked(sc); + break; + } + } + + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + + PCN_UNLOCK(sc); + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +pcn_encap(sc, m_head, txidx) + struct pcn_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct pcn_tx_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len == 0) + continue; + + if ((PCN_TX_LIST_CNT - (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc->pcn_ldata->pcn_tx_list[frag]; + f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; + f->pcn_txctl |= PCN_TXCTL_MBO; + f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); + if (cnt == 0) + f->pcn_txctl |= PCN_TXCTL_STP; + else + f->pcn_txctl |= PCN_TXCTL_OWN; + cur = frag; + PCN_INC(frag, PCN_TX_LIST_CNT); + cnt++; + } + + if (m != NULL) + return(ENOBUFS); + + sc->pcn_cdata.pcn_tx_chain[cur] = m_head; + sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= + PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; + sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; + sc->pcn_cdata.pcn_tx_cnt += cnt; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ +static void +pcn_start(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + PCN_LOCK(sc); + pcn_start_locked(ifp); + PCN_UNLOCK(sc); +} + +static void +pcn_start_locked(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc = ifp->if_softc; + + PCN_LOCK_ASSERT(sc); + + if (!sc->pcn_link) + return; + + idx = sc->pcn_cdata.pcn_tx_prod; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) + return; + + while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (pcn_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, m_head); + + } + + /* Transmit */ + sc->pcn_cdata.pcn_tx_prod = idx; + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + +static void +pcn_setfilt(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } else { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); + } + + /* Set the capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) { + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } else { + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); + } + + return; +} + +static void +pcn_init(xsc) + void *xsc; +{ + struct pcn_softc *sc = xsc; + + PCN_LOCK(sc); + pcn_init_locked(sc); + PCN_UNLOCK(sc); +} + +static void +pcn_init_locked(sc) + struct pcn_softc *sc; +{ + struct ifnet *ifp = sc->pcn_ifp; + struct mii_data *mii = NULL; + struct ifmedia_entry *ife; + + PCN_LOCK_ASSERT(sc); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + pcn_stop(sc); + pcn_reset(sc); + + mii = device_get_softc(sc->pcn_miibus); + ife = mii->mii_media.ifm_cur; + + /* Set MAC address */ + pcn_csr_write(sc, PCN_CSR_PAR0, + ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[0]); + pcn_csr_write(sc, PCN_CSR_PAR1, + ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[1]); + pcn_csr_write(sc, PCN_CSR_PAR2, + ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[2]); + + /* Init circular RX list. */ + if (pcn_list_rx_init(sc) == ENOBUFS) { + if_printf(ifp, "initialization failed: no " + "memory for rx buffers\n"); + pcn_stop(sc); + return; + } + + /* + * Init tx descriptors. + */ + pcn_list_tx_init(sc); + + /* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */ + PCN_BCR_CLRBIT(sc, PCN_BCR_MISCCFG, PCN_MISC_ASEL); + + /* + * Set up the port based on the currently selected media. + * For Am79C978 we've to unconditionally set PCN_PORT_MII and + * set the PHY in PCN_BCR_PHYSEL instead. + */ + if (sc->pcn_type != Am79C978 && + IFM_INST(ife->ifm_media) == sc->pcn_inst_10bt) + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_10BASET); + else + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); + + /* Set up RX filter. */ + pcn_setfilt(ifp); + + /* + * Load the multicast filter. + */ + pcn_setmulti(sc); + + /* + * Load the addresses of the RX and TX lists. + */ + pcn_csr_write(sc, PCN_CSR_RXADDR0, + vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_RXADDR1, + (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR0, + vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); + pcn_csr_write(sc, PCN_CSR_TXADDR1, + (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); + + /* Set the RX and TX ring sizes. */ + pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); + pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); + + /* We're not using the initialization block. */ + pcn_csr_write(sc, PCN_CSR_IAB1, 0); + + /* Enable fast suspend mode. */ + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); + + /* + * Enable burst read and write. Also set the no underflow + * bit. This will avoid transmit underruns in certain + * conditions while still providing decent performance. + */ + PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| + PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); + + /* Enable graceful recovery from underflow. */ + PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); + + /* Enable auto-padding of short TX frames. */ + PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); + + /* Disable MII autoneg (we handle this ourselves). */ + PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); + + if (sc->pcn_type == Am79C978) + /* XXX support other PHYs? */ + pcn_bcr_write(sc, PCN_BCR_PHYSEL, + PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); + + /* Enable interrupts and start the controller running. */ + pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); + + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); + + return; +} + +/* + * Set media options. + */ +static int +pcn_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + /* + * At least Am79C971 with DP83840A can wedge when switching + * from the internal 10baseT PHY to the external PHY without + * issuing pcn_reset(). For setting the port in PCN_CSR_MODE + * the PCnet chip has to be powered down or stopped anyway + * and although documented otherwise it doesn't take effect + * until the next initialization. + */ + sc->pcn_link = 0; + pcn_stop(sc); + pcn_reset(sc); + pcn_init_locked(sc); + if (ifp->if_snd.ifq_head != NULL) + pcn_start_locked(ifp); + + PCN_UNLOCK(sc); + + return(0); +} + +/* + * Report current media status. + */ +static void +pcn_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + + mii = device_get_softc(sc->pcn_miibus); + PCN_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + PCN_UNLOCK(sc); + + return; +} + +static int +pcn_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct pcn_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii = NULL; + int error = 0; + + switch(command) { + case SIOCSIFFLAGS: + PCN_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->pcn_if_flags & IFF_PROMISC)) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->pcn_if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_setfilt(ifp); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + pcn_csr_write(sc, PCN_CSR_CSR, + PCN_CSR_INTEN|PCN_CSR_START); + } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + pcn_init_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + pcn_stop(sc); + } + sc->pcn_if_flags = ifp->if_flags; + PCN_UNLOCK(sc); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + PCN_LOCK(sc); + pcn_setmulti(sc); + PCN_UNLOCK(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->pcn_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return(error); +} + +static void +pcn_watchdog(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + PCN_LOCK(sc); + + ifp->if_oerrors++; + if_printf(ifp, "watchdog timeout\n"); + + pcn_stop(sc); + pcn_reset(sc); + pcn_init_locked(sc); + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + PCN_UNLOCK(sc); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +pcn_stop(sc) + struct pcn_softc *sc; +{ + register int i; + struct ifnet *ifp; + + PCN_LOCK_ASSERT(sc); + ifp = sc->pcn_ifp; + ifp->if_timer = 0; + + callout_stop(&sc->pcn_stat_callout); + + /* Turn off interrupts */ + PCN_CSR_CLRBIT(sc, PCN_CSR_CSR, PCN_CSR_INTEN); + /* Stop adapter */ + PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); + sc->pcn_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < PCN_RX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_rx_chain[i]); + sc->pcn_cdata.pcn_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->pcn_ldata->pcn_rx_list, + sizeof(sc->pcn_ldata->pcn_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < PCN_TX_LIST_CNT; i++) { + if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { + m_freem(sc->pcn_cdata.pcn_tx_chain[i]); + sc->pcn_cdata.pcn_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->pcn_ldata->pcn_tx_list, + sizeof(sc->pcn_ldata->pcn_tx_list)); + + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void +pcn_shutdown(dev) + device_t dev; +{ + struct pcn_softc *sc; + + sc = device_get_softc(dev); + + PCN_LOCK(sc); + pcn_reset(sc); + pcn_stop(sc); + PCN_UNLOCK(sc); + + return; +} diff --git a/sys/dev/pcn/if_pcnreg.h b/sys/dev/pcn/if_pcnreg.h new file mode 100644 index 0000000..406d438 --- /dev/null +++ b/sys/dev/pcn/if_pcnreg.h @@ -0,0 +1,530 @@ +/*- + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * I/O map in 16-bit mode. To switch to 32-bit mode, + * you need to perform a 32-bit write to the RDP register + * (writing a 0 is recommended). + */ +#define PCN_IO16_APROM00 0x00 +#define PCN_IO16_APROM01 0x02 +#define PCN_IO16_APROM02 0x04 +#define PCN_IO16_APROM03 0x06 +#define PCN_IO16_APROM04 0x08 +#define PCN_IO16_APROM05 0x0A +#define PCN_IO16_APROM06 0x0C +#define PCN_IO16_APROM07 0x0E +#define PCN_IO16_RDP 0x10 +#define PCN_IO16_RAP 0x12 +#define PCN_IO16_RESET 0x14 +#define PCN_IO16_BDP 0x16 + +/* + * I/O map in 32-bit mode. + */ +#define PCN_IO32_APROM00 0x00 +#define PCN_IO32_APROM01 0x04 +#define PCN_IO32_APROM02 0x08 +#define PCN_IO32_APROM03 0x0C +#define PCN_IO32_RDP 0x10 +#define PCN_IO32_RAP 0x14 +#define PCN_IO32_RESET 0x18 +#define PCN_IO32_BDP 0x1C + +/* + * CSR registers + */ +#define PCN_CSR_CSR 0x00 +#define PCN_CSR_IAB0 0x01 +#define PCN_CSR_IAB1 0x02 +#define PCN_CSR_IMR 0x03 +#define PCN_CSR_TFEAT 0x04 +#define PCN_CSR_EXTCTL1 0x05 +#define PCN_CSR_DTBLLEN 0x06 +#define PCN_CSR_EXTCTL2 0x07 +#define PCN_CSR_MAR0 0x08 +#define PCN_CSR_MAR1 0x09 +#define PCN_CSR_MAR2 0x0A +#define PCN_CSR_MAR3 0x0B +#define PCN_CSR_PAR0 0x0C +#define PCN_CSR_PAR1 0x0D +#define PCN_CSR_PAR2 0x0E +#define PCN_CSR_MODE 0x0F +#define PCN_CSR_RXADDR0 0x18 +#define PCN_CSR_RXADDR1 0x19 +#define PCN_CSR_TXADDR0 0x1E +#define PCN_CSR_TXADDR1 0x1F +#define PCN_CSR_TXPOLL 0x2F +#define PCN_CSR_RXPOLL 0x31 +#define PCN_CSR_RXRINGLEN 0x4C +#define PCN_CSR_TXRINGLEN 0x4E +#define PCN_CSR_DMACTL 0x50 +#define PCN_CSR_BUSTIMER 0x52 +#define PCN_CSR_MEMERRTIMEO 0x64 +#define PCN_CSR_ONNOWMISC 0x74 +#define PCN_CSR_ADVFEAT 0x7A +#define PCN_CSR_MACCFG 0x7D +#define PCN_CSR_CHIPID0 0x58 +#define PCN_CSR_CHIPID1 0x59 + +/* + * Control and status register (CSR0) + */ +#define PCN_CSR_INIT 0x0001 +#define PCN_CSR_START 0x0002 +#define PCN_CSR_STOP 0x0004 +#define PCN_CSR_TX 0x0008 +#define PCN_CSR_TXON 0x0010 +#define PCN_CSR_RXON 0x0020 +#define PCN_CSR_INTEN 0x0040 +#define PCN_CSR_INTR 0x0080 +#define PCN_CSR_IDONE 0x0100 +#define PCN_CSR_TINT 0x0200 +#define PCN_CSR_RINT 0x0400 +#define PCN_CSR_MERR 0x0800 +#define PCN_CSR_MISS 0x1000 +#define PCN_CSR_CERR 0x2000 +#define PCN_CSR_ERR 0x8000 + +/* + * Interrupt masks and deferral control (CSR3) + */ +#define PCN_IMR_BSWAP 0x0004 +#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */ +#define PCN_IMR_DXMT2PD 0x0010 +#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */ +#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */ +#define PCN_IMR_IDONE 0x0100 +#define PCN_IMR_TINT 0x0200 +#define PCN_IMR_RINT 0x0400 +#define PCN_IMR_MERR 0x0800 +#define PCN_IMR_MISS 0x1000 + +/* + * Test and features control (CSR4) + */ +#define PCN_TFEAT_TXSTRTMASK 0x0004 +#define PCN_TFEAT_TXSTRT 0x0008 +#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */ +#define PCN_TFEAT_RXCCOFLOW 0x0020 +#define PCN_TFEAT_UINT 0x0040 +#define PCN_TFEAT_UINTREQ 0x0080 +#define PCN_TFEAT_MISSOFLOWM 0x0100 +#define PCN_TFEAT_MISSOFLOW 0x0200 +#define PCN_TFEAT_STRIP_FCS 0x0400 +#define PCN_TFEAT_PAD_TX 0x0800 +#define PCN_TFEAT_TXDPOLL 0x1000 +#define PCN_TFEAT_DMAPLUS 0x4000 + +/* + * Extended control and interrupt 1 (CSR5) + */ +#define PCN_EXTCTL1_SPND 0x0001 /* suspend */ +#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */ +#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */ +#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */ +#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */ +#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */ +#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */ +#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */ +#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */ +#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */ +#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */ +#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */ + +/* + * RX/TX descriptor len (CSR6) + */ +#define PCN_DTBLLEN_RLEN 0x0F00 +#define PCN_DTBLLEN_TLEN 0xF000 + +/* + * Extended control and interrupt 2 (CSR7) + */ +#define PCN_EXTCTL2_MIIPDTINTE 0x0001 +#define PCN_EXTCTL2_MIIPDTINT 0x0002 +#define PCN_EXTCTL2_MCCIINTE 0x0004 +#define PCN_EXTCTL2_MCCIINT 0x0008 +#define PCN_EXTCTL2_MCCINTE 0x0010 +#define PCN_EXTCTL2_MCCINT 0x0020 +#define PCN_EXTCTL2_MAPINTE 0x0040 +#define PCN_EXTCTL2_MAPINT 0x0080 +#define PCN_EXTCTL2_MREINTE 0x0100 +#define PCN_EXTCTL2_MREINT 0x0200 +#define PCN_EXTCTL2_STINTE 0x0400 +#define PCN_EXTCTL2_STINT 0x0800 +#define PCN_EXTCTL2_RXDPOLL 0x1000 +#define PCN_EXTCTL2_RDMD 0x2000 +#define PCN_EXTCTL2_RXFRTG 0x4000 +#define PCN_EXTCTL2_FASTSPNDE 0x8000 + + +/* + * Mode (CSR15) + */ +#define PCN_MODE_RXD 0x0001 /* RX disable */ +#define PCN_MODE_TXD 0x0002 /* TX disable */ +#define PCN_MODE_LOOP 0x0004 /* loopback enable */ +#define PCN_MODE_TXCRCD 0x0008 +#define PCN_MODE_FORCECOLL 0x0010 +#define PCN_MODE_RETRYD 0x0020 +#define PCN_MODE_INTLOOP 0x0040 +#define PCN_MODE_PORTSEL 0x0180 +#define PCN_MODE_RXVPAD 0x2000 +#define PCN_MODE_RXNOBROAD 0x4000 +#define PCN_MODE_PROMISC 0x8000 + +/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */ +#define PCN_PORT_AUI 0x0000 +#define PCN_PORT_10BASET 0x0080 +#define PCN_PORT_GPSI 0x0100 +#define PCN_PORT_MII 0x0180 + +/* + * Chip ID values. + */ +/* CSR88-89: Chip ID masks */ +#define AMD_MASK 0x003 +#define PART_MASK 0xffff +#define Am79C971 0x2623 +#define Am79C972 0x2624 +#define Am79C973 0x2625 +#define Am79C978 0x2626 +#define Am79C975 0x2627 +#define Am79C976 0x2628 + +/* + * Advanced feature control (CSR122) + */ +#define PCN_AFC_RXALIGN 0x0001 + +/* + * BCR (bus control) registers + */ +#define PCN_BCR_MMRA 0x00 /* Master Mode Read Active */ +#define PCN_BCR_MMW 0x01 /* Master Mode Write Active */ +#define PCN_BCR_MISCCFG 0x02 +#define PCN_BCR_LED0 0x04 +#define PCN_BCR_LED1 0x05 +#define PCN_BCR_LED2 0x06 +#define PCN_BCR_LED3 0x07 +#define PCN_BCR_DUPLEX 0x09 +#define PCN_BCR_BUSCTL 0x12 +#define PCN_BCR_EECTL 0x13 +#define PCN_BCR_SSTYLE 0x14 +#define PCN_BCR_PCILAT 0x16 +#define PCN_BCR_PCISUBVENID 0x17 +#define PCN_BCR_PCISUBSYSID 0x18 +#define PCN_BCR_SRAMSIZE 0x19 +#define PCN_BCR_SRAMBOUND 0x1A +#define PCN_BCR_SRAMCTL 0x1B +#define PCN_BCR_MIICTL 0x20 +#define PCN_BCR_MIIADDR 0x21 +#define PCN_BCR_MIIDATA 0x22 +#define PCN_BCR_PCIVENID 0x23 +#define PCN_BCR_PCIPCAP 0x24 +#define PCN_BCR_DATA0 0x25 +#define PCN_BCR_DATA1 0x26 +#define PCN_BCR_DATA2 0x27 +#define PCN_BCR_DATA3 0x28 +#define PCN_BCR_DATA4 0x29 +#define PCN_BCR_DATA5 0x2A +#define PCN_BCR_DATA6 0x2B +#define PCN_BCR_DATA7 0x2C +#define PCN_BCR_ONNOWPAT0 0x2D +#define PCN_BCR_ONNOWPAT1 0x2E +#define PCN_BCR_ONNOWPAT2 0x2F +#define PCN_BCR_PHYSEL 0x31 + +/* + * Miscellaneous Configuration (BCR2) + */ +#define PCN_MISC_TMAULOOP 1<<14 /* T-MAU Loopback packet enable. */ +#define PCN_MISC_LEDPE 1<<12 /* LED Program Enable */ +#define PCN_MISC_APROMWE 1<<8 /* Address PROM Write Enable */ +#define PCN_MISC_INTLEVEL 1<<7 /* Interrupt level */ +#define PCN_MISC_EADISEL 1<<3 /* EADI Select */ +#define PCN_MISC_AWAKE 1<<2 /* Power saving mode select */ +#define PCN_MISC_ASEL 1<<1 /* Auto Select */ +#define PCN_MISC_XMAUSEL 1<<0 /* Reserved. */ + +/* + * Full duplex control (BCR9) + */ +#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ +#define PCN_DUPLEX_AUI 0x0002 /* AUI full-duplex */ +#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */ + +/* + * Burst and bus control register (BCR18) + */ +#define PCN_BUSCTL_BWRITE 0x0020 +#define PCN_BUSCTL_BREAD 0x0040 +#define PCN_BUSCTL_DWIO 0x0080 +#define PCN_BUSCTL_EXTREQ 0x0100 +#define PCN_BUSCTL_MEMCMD 0x0200 +#define PCN_BUSCTL_NOUFLOW 0x0800 +#define PCN_BUSCTL_ROMTMG 0xF000 + +/* + * EEPROM control (BCR19) + */ +#define PCN_EECTL_EDATA 0x0001 +#define PCN_EECTL_ECLK 0x0002 +#define PCN_EECTL_EECS 0x0004 +#define PCN_EECTL_EEN 0x0100 +#define PCN_EECTL_EEDET 0x2000 +#define PCN_EECTL_PREAD 0x4000 +#define PCN_EECTL_PVALID 0x8000 + +/* + * Software style (BCR20) + */ +#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */ +#define PCN_SSTYLE_SSIZE32 0x0100 +#define PCN_SSTYLE_SWSTYLE 0x00FF + +#define PCN_SWSTYLE_LANCE 0x0000 +#define PCN_SWSTYLE_PCNETPCI 0x0102 +#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103 + +/* + * MII control and status (BCR32) + */ +#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */ +#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */ +#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */ +#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */ +#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */ +#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */ +#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */ +#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */ +#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */ +#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */ +#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */ + +/* + * MII address register (BCR33) + */ +#define PCN_MIIADDR_REGAD 0x001F +#define PCN_MIIADDR_PHYAD 0x03E0 + +/* addresses of internal PHYs */ +#define PCN_PHYAD_100BTX 30 +#define PCN_PHYAD_10BT 31 + +/* + * MII data register (BCR34) + */ +#define PCN_MIIDATA_MIIMD 0xFFFF + +/* + * PHY selection (BCR49) (HomePNA NIC only) + */ +#define PCN_PHYSEL_PHYSEL 0x0003 +#define PCN_PHYSEL_DEFAULT 0x0300 +#define PCN_PHYSEL_PCNET 0x8000 + +#define PCN_PHY_10BT 0x0000 +#define PCN_PHY_HOMEPNA 0x0001 +#define PCN_PHY_EXTERNAL 0x0002 + +struct pcn_rx_desc { + u_int16_t pcn_rxlen; + u_int16_t pcn_rsvd0; + u_int16_t pcn_bufsz; + u_int16_t pcn_rxstat; + u_int32_t pcn_rbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */ +#define PCN_RXSTAT_ENP 0x0100 /* end of packet */ +#define PCN_RXSTAT_STP 0x0200 /* start of packet */ +#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */ +#define PCN_RXSTAT_CRC 0x0800 /* CRC error */ +#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */ +#define PCN_RXSTAT_FRAM 0x2000 /* framing error */ +#define PCN_RXSTAT_ERR 0x4000 /* error summary */ +#define PCN_RXSTAT_OWN 0x8000 + +#define PCN_RXLEN_MBO 0xF000 +#define PCN_RXLEN_BUFSZ 0x0FFF + +#define PCN_OWN_RXDESC(x) (((x)->pcn_rxstat & PCN_RXSTAT_OWN) == 0) + +struct pcn_tx_desc { + u_int32_t pcn_txstat; + u_int32_t pcn_txctl; + u_int32_t pcn_tbaddr; + u_int32_t pcn_uspace; +}; + +#define PCN_TXSTAT_TRC 0x0000000F /* transmit retries */ +#define PCN_TXSTAT_RTRY 0x04000000 /* retry */ +#define PCN_TXSTAT_LCAR 0x08000000 /* lost carrier */ +#define PCN_TXSTAT_LCOL 0x10000000 /* late collision */ +#define PCN_TXSTAT_EXDEF 0x20000000 /* excessive deferrals */ +#define PCN_TXSTAT_UFLOW 0x40000000 /* transmit underrun */ +#define PCN_TXSTAT_BUFF 0x80000000 /* buffer error */ + +#define PCN_TXCTL_OWN 0x80000000 +#define PCN_TXCTL_ERR 0x40000000 /* error summary */ +#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */ +#define PCN_TXCTL_MORE_LTINT 0x10000000 +#define PCN_TXCTL_ONE 0x08000000 +#define PCN_TXCTL_DEF 0x04000000 +#define PCN_TXCTL_STP 0x02000000 +#define PCN_TXCTL_ENP 0x01000000 +#define PCN_TXCTL_BPE 0x00800000 +#define PCN_TXCTL_MBO 0x0000F000 +#define PCN_TXCTL_BUFSZ 0x00000FFF + +#define PCN_OWN_TXDESC(x) (((x)->pcn_txctl & PCN_TXCTL_OWN) == 0) + +#define PCN_RX_LIST_CNT 64 +#define PCN_TX_LIST_CNT 256 + +struct pcn_list_data { + struct pcn_rx_desc pcn_rx_list[PCN_RX_LIST_CNT]; + struct pcn_tx_desc pcn_tx_list[PCN_TX_LIST_CNT]; +}; + +struct pcn_ring_data { + struct mbuf *pcn_rx_chain[PCN_RX_LIST_CNT]; + struct mbuf *pcn_tx_chain[PCN_TX_LIST_CNT]; + int pcn_rx_prod; + int pcn_tx_prod; + int pcn_tx_cons; + int pcn_tx_cnt; +}; + +/* + * AMD PCI vendor ID. + */ +#define PCN_VENDORID 0x1022 + +/* + * AMD PCnet/PCI device IDs + */ +#define PCN_DEVICEID_PCNET 0x2000 +#define PCN_DEVICEID_HOME 0x2001 + +struct pcn_type { + u_int16_t pcn_vid; + u_int16_t pcn_did; + const char *pcn_name; +}; + +struct pcn_softc { + struct ifnet *pcn_ifp; + bus_space_handle_t pcn_bhandle; + bus_space_tag_t pcn_btag; + struct resource *pcn_res; + struct resource *pcn_irq; + void *pcn_intrhand; + device_t pcn_miibus; + u_int8_t pcn_link; + int8_t pcn_extphyaddr; + int8_t pcn_inst_10bt; + int pcn_if_flags; + int pcn_type; + struct pcn_list_data *pcn_ldata; + struct pcn_ring_data pcn_cdata; + struct callout pcn_stat_callout; + struct mtx pcn_mtx; +}; + +#define PCN_LOCK(_sc) mtx_lock(&(_sc)->pcn_mtx) +#define PCN_UNLOCK(_sc) mtx_unlock(&(_sc)->pcn_mtx) +#define PCN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->pcn_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->pcn_btag, sc->pcn_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->pcn_btag, sc->pcn_bhandle, reg) + +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->pcn_btag, sc->pcn_bhandle, reg, val) + +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->pcn_btag, sc->pcn_bhandle, reg) + +#define PCN_TIMEOUT 1000 +#define ETHER_ALIGN 2 +#define PCN_RXLEN 1536 +#define PCN_MIN_FRAMELEN 60 +#define PCN_INC(x, y) (x) = (x + 1) % y +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define PCN_PCI_VENDOR_ID 0x00 +#define PCN_PCI_DEVICE_ID 0x02 +#define PCN_PCI_COMMAND 0x04 +#define PCN_PCI_STATUS 0x06 +#define PCN_PCI_REVID 0x08 +#define PCN_PCI_CLASSCODE 0x09 +#define PCN_PCI_CACHELEN 0x0C +#define PCN_PCI_LATENCY_TIMER 0x0D +#define PCN_PCI_HEADER_TYPE 0x0E +#define PCN_PCI_LOIO 0x10 +#define PCN_PCI_LOMEM 0x14 +#define PCN_PCI_BIOSROM 0x30 +#define PCN_PCI_INTLINE 0x3C +#define PCN_PCI_INTPIN 0x3D +#define PCN_PCI_MINGNT 0x3E +#define PCN_PCI_MINLAT 0x3F +#define PCN_PCI_RESETOPT 0x48 +#define PCN_PCI_EEPROM_DATA 0x4C + +/* power management registers */ +#define PCN_PCI_CAPID 0x50 /* 8 bits */ +#define PCN_PCI_NEXTPTR 0x51 /* 8 bits */ +#define PCN_PCI_PWRMGMTCAP 0x52 /* 16 bits */ +#define PCN_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ + +#define PCN_PSTATE_MASK 0x0003 +#define PCN_PSTATE_D0 0x0000 +#define PCN_PSTATE_D1 0x0001 +#define PCN_PSTATE_D2 0x0002 +#define PCN_PSTATE_D3 0x0003 +#define PCN_PME_EN 0x0010 +#define PCN_PME_STATUS 0x8000 diff --git a/sys/modules/pcn/Makefile b/sys/modules/pcn/Makefile index c09af2d..9955c6c 100644 --- a/sys/modules/pcn/Makefile +++ b/sys/modules/pcn/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../pci +.PATH: ${.CURDIR}/../../dev/pcn KMOD= if_pcn SRCS= if_pcn.c device_if.h bus_if.h pci_if.h diff --git a/sys/pci/if_pcn.c b/sys/pci/if_pcn.c deleted file mode 100644 index 8298502..0000000 --- a/sys/pci/if_pcn.c +++ /dev/null @@ -1,1527 +0,0 @@ -/*- - * Copyright (c) 2000 Berkeley Software Design, Inc. - * Copyright (c) 1997, 1998, 1999, 2000 - * Bill Paul . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -/* - * AMD Am79c972 fast ethernet PCI NIC driver. Datasheets are available - * from http://www.amd.com. - * - * The AMD PCnet/PCI controllers are more advanced and functional - * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain - * backwards compatibility with the LANCE and thus can be made - * to work with older LANCE drivers. This is in fact how the - * PCnet/PCI chips were supported in FreeBSD originally. The trouble - * is that the PCnet/PCI devices offer several performance enhancements - * which can't be exploited in LANCE compatibility mode. Chief among - * these enhancements is the ability to perform PCI DMA operations - * using 32-bit addressing (which eliminates the need for ISA - * bounce-buffering), and special receive buffer alignment (which - * allows the receive handler to pass packets to the upper protocol - * layers without copying on both the x86 and alpha platforms). - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include /* for vtophys */ -#include /* for vtophys */ -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define PCN_USEIOSPACE - -#include - -MODULE_DEPEND(pcn, pci, 1, 1, 1); -MODULE_DEPEND(pcn, ether, 1, 1, 1); -MODULE_DEPEND(pcn, miibus, 1, 1, 1); - -/* "device miibus" required. See GENERIC if you get errors here. */ -#include "miibus_if.h" - -/* - * Various supported device vendors/types and their names. - */ -static const struct pcn_type pcn_devs[] = { - { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, - { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, - { 0, 0, NULL } -}; - -static const struct pcn_chipid { - u_int32_t id; - const char *name; -} pcn_chipid[] = { - { Am79C971, "Am79C971" }, - { Am79C972, "Am79C972" }, - { Am79C973, "Am79C973" }, - { Am79C978, "Am79C978" }, - { Am79C975, "Am79C975" }, - { Am79C976, "Am79C976" }, - { 0, NULL }, -}; - -static const char *pcn_chipid_name(u_int32_t); -static u_int32_t pcn_chip_id(device_t); -static const struct pcn_type *pcn_match(u_int16_t, u_int16_t); - -static u_int32_t pcn_csr_read(struct pcn_softc *, int); -static u_int16_t pcn_csr_read16(struct pcn_softc *, int); -static u_int16_t pcn_bcr_read16(struct pcn_softc *, int); -static void pcn_csr_write(struct pcn_softc *, int, int); -static u_int32_t pcn_bcr_read(struct pcn_softc *, int); -static void pcn_bcr_write(struct pcn_softc *, int, int); - -static int pcn_probe(device_t); -static int pcn_attach(device_t); -static int pcn_detach(device_t); - -static int pcn_newbuf(struct pcn_softc *, int, struct mbuf *); -static int pcn_encap(struct pcn_softc *, struct mbuf *, u_int32_t *); -static void pcn_rxeof(struct pcn_softc *); -static void pcn_txeof(struct pcn_softc *); -static void pcn_intr(void *); -static void pcn_tick(void *); -static void pcn_start(struct ifnet *); -static void pcn_start_locked(struct ifnet *); -static int pcn_ioctl(struct ifnet *, u_long, caddr_t); -static void pcn_init(void *); -static void pcn_init_locked(struct pcn_softc *); -static void pcn_stop(struct pcn_softc *); -static void pcn_watchdog(struct ifnet *); -static void pcn_shutdown(device_t); -static int pcn_ifmedia_upd(struct ifnet *); -static void pcn_ifmedia_sts(struct ifnet *, struct ifmediareq *); - -static int pcn_miibus_readreg(device_t, int, int); -static int pcn_miibus_writereg(device_t, int, int, int); -static void pcn_miibus_statchg(device_t); - -static void pcn_setfilt(struct ifnet *); -static void pcn_setmulti(struct pcn_softc *); -static void pcn_reset(struct pcn_softc *); -static int pcn_list_rx_init(struct pcn_softc *); -static int pcn_list_tx_init(struct pcn_softc *); - -#ifdef PCN_USEIOSPACE -#define PCN_RES SYS_RES_IOPORT -#define PCN_RID PCN_PCI_LOIO -#else -#define PCN_RES SYS_RES_MEMORY -#define PCN_RID PCN_PCI_LOMEM -#endif - -static device_method_t pcn_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, pcn_probe), - DEVMETHOD(device_attach, pcn_attach), - DEVMETHOD(device_detach, pcn_detach), - DEVMETHOD(device_shutdown, pcn_shutdown), - - /* bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), - - /* MII interface */ - DEVMETHOD(miibus_readreg, pcn_miibus_readreg), - DEVMETHOD(miibus_writereg, pcn_miibus_writereg), - DEVMETHOD(miibus_statchg, pcn_miibus_statchg), - - { 0, 0 } -}; - -static driver_t pcn_driver = { - "pcn", - pcn_methods, - sizeof(struct pcn_softc) -}; - -static devclass_t pcn_devclass; - -DRIVER_MODULE(pcn, pci, pcn_driver, pcn_devclass, 0, 0); -DRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); - -#define PCN_CSR_SETBIT(sc, reg, x) \ - pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) - -#define PCN_CSR_CLRBIT(sc, reg, x) \ - pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) - -#define PCN_BCR_SETBIT(sc, reg, x) \ - pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) - -#define PCN_BCR_CLRBIT(sc, reg, x) \ - pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) - -static u_int32_t -pcn_csr_read(sc, reg) - struct pcn_softc *sc; - int reg; -{ - CSR_WRITE_4(sc, PCN_IO32_RAP, reg); - return(CSR_READ_4(sc, PCN_IO32_RDP)); -} - -static u_int16_t -pcn_csr_read16(sc, reg) - struct pcn_softc *sc; - int reg; -{ - CSR_WRITE_2(sc, PCN_IO16_RAP, reg); - return(CSR_READ_2(sc, PCN_IO16_RDP)); -} - -static void -pcn_csr_write(sc, reg, val) - struct pcn_softc *sc; - int reg; - int val; -{ - CSR_WRITE_4(sc, PCN_IO32_RAP, reg); - CSR_WRITE_4(sc, PCN_IO32_RDP, val); - return; -} - -static u_int32_t -pcn_bcr_read(sc, reg) - struct pcn_softc *sc; - int reg; -{ - CSR_WRITE_4(sc, PCN_IO32_RAP, reg); - return(CSR_READ_4(sc, PCN_IO32_BDP)); -} - -static u_int16_t -pcn_bcr_read16(sc, reg) - struct pcn_softc *sc; - int reg; -{ - CSR_WRITE_2(sc, PCN_IO16_RAP, reg); - return(CSR_READ_2(sc, PCN_IO16_BDP)); -} - -static void -pcn_bcr_write(sc, reg, val) - struct pcn_softc *sc; - int reg; - int val; -{ - CSR_WRITE_4(sc, PCN_IO32_RAP, reg); - CSR_WRITE_4(sc, PCN_IO32_BDP, val); - return; -} - -static int -pcn_miibus_readreg(dev, phy, reg) - device_t dev; - int phy, reg; -{ - struct pcn_softc *sc; - int val; - - sc = device_get_softc(dev); - - /* - * At least Am79C971 with DP83840A wedge when isolating the - * external PHY so we can't allow multiple external PHYs. - * There are cards that use Am79C971 with both the internal - * and an external PHY though. - * For internal PHYs it doesn't really matter whether we can - * isolate the remaining internal and the external ones in - * the PHY drivers as the internal PHYs have to be enabled - * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc. - * With Am79C97{3,5,8} we don't support switching beetween - * the internal and external PHYs, yet, so we can't allow - * multiple PHYs with these either. - * Am79C97{2,6} actually only support external PHYs (not - * connectable internal ones respond at the usual addresses, - * which don't hurt if we let them show up on the bus) and - * isolating them works. - */ - if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || - sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || - sc->pcn_type == Am79C978) && sc->pcn_extphyaddr != -1 && - phy != sc->pcn_extphyaddr) - return(0); - - pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); - val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; - if (val == 0xFFFF) - return(0); - - if (((sc->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) || - sc->pcn_type == Am79C973 || sc->pcn_type == Am79C975 || - sc->pcn_type == Am79C978) && sc->pcn_extphyaddr == -1) - sc->pcn_extphyaddr = phy; - - return(val); -} - -static int -pcn_miibus_writereg(dev, phy, reg, data) - device_t dev; - int phy, reg, data; -{ - struct pcn_softc *sc; - - sc = device_get_softc(dev); - - pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); - pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); - - return(0); -} - -static void -pcn_miibus_statchg(dev) - device_t dev; -{ - struct pcn_softc *sc; - struct mii_data *mii; - - sc = device_get_softc(dev); - mii = device_get_softc(sc->pcn_miibus); - - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { - PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); - } else { - PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); - } - - return; -} - -static void -pcn_setmulti(sc) - struct pcn_softc *sc; -{ - struct ifnet *ifp; - struct ifmultiaddr *ifma; - u_int32_t h, i; - u_int16_t hashes[4] = { 0, 0, 0, 0 }; - - ifp = sc->pcn_ifp; - - PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - for (i = 0; i < 4; i++) - pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); - PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); - return; - } - - /* first, zot all the existing hash bits */ - for (i = 0; i < 4; i++) - pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); - - /* now program new ones */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ether_crc32_le(LLADDR((struct sockaddr_dl *) - ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; - hashes[h >> 4] |= 1 << (h & 0xF); - } - IF_ADDR_UNLOCK(ifp); - - for (i = 0; i < 4; i++) - pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); - - PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); - - return; -} - -static void -pcn_reset(sc) - struct pcn_softc *sc; -{ - /* - * Issue a reset by reading from the RESET register. - * Note that we don't know if the chip is operating in - * 16-bit or 32-bit mode at this point, so we attempt - * to reset the chip both ways. If one fails, the other - * will succeed. - */ - CSR_READ_2(sc, PCN_IO16_RESET); - CSR_READ_4(sc, PCN_IO32_RESET); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - - /* Select 32-bit (DWIO) mode */ - CSR_WRITE_4(sc, PCN_IO32_RDP, 0); - - /* Select software style 3. */ - pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); - - return; -} - -static const char * -pcn_chipid_name(u_int32_t id) -{ - const struct pcn_chipid *p; - - p = pcn_chipid; - while (p->name) { - if (id == p->id) - return (p->name); - p++; - } - return ("Unknown"); -} - -static u_int32_t -pcn_chip_id(device_t dev) -{ - struct pcn_softc *sc; - u_int32_t chip_id; - - sc = device_get_softc(dev); - /* - * Note: we can *NOT* put the chip into - * 32-bit mode yet. The le(4) driver will only - * work in 16-bit mode, and once the chip - * goes into 32-bit mode, the only way to - * get it out again is with a hardware reset. - * So if pcn_probe() is called before the - * le(4) driver's probe routine, the chip will - * be locked into 32-bit operation and the - * le(4) driver will be unable to attach to it. - * Note II: if the chip happens to already - * be in 32-bit mode, we still need to check - * the chip ID, but first we have to detect - * 32-bit mode using only 16-bit operations. - * The safest way to do this is to read the - * PCI subsystem ID from BCR23/24 and compare - * that with the value read from PCI config - * space. - */ - chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); - chip_id <<= 16; - chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); - /* - * Note III: the test for 0x10001000 is a hack to - * pacify VMware, who's pseudo-PCnet interface is - * broken. Reading the subsystem register from PCI - * config space yields 0x00000000 while reading the - * same value from I/O space yields 0x10001000. It's - * not supposed to be that way. - */ - if (chip_id == pci_read_config(dev, - PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { - /* We're in 16-bit mode. */ - chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); - chip_id <<= 16; - chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); - } else { - /* We're in 32-bit mode. */ - chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); - chip_id <<= 16; - chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); - } - - return (chip_id); -} - -static const struct pcn_type * -pcn_match(u_int16_t vid, u_int16_t did) -{ - const struct pcn_type *t; - - t = pcn_devs; - while (t->pcn_name != NULL) { - if ((vid == t->pcn_vid) && (did == t->pcn_did)) - return (t); - t++; - } - return (NULL); -} - -/* - * Probe for an AMD chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ -static int -pcn_probe(dev) - device_t dev; -{ - const struct pcn_type *t; - struct pcn_softc *sc; - int rid; - u_int32_t chip_id; - - t = pcn_match(pci_get_vendor(dev), pci_get_device(dev)); - if (t == NULL) - return (ENXIO); - sc = device_get_softc(dev); - - /* - * Temporarily map the I/O space so we can read the chip ID register. - */ - rid = PCN_RID; - sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); - if (sc->pcn_res == NULL) { - device_printf(dev, "couldn't map ports/memory\n"); - return(ENXIO); - } - sc->pcn_btag = rman_get_bustag(sc->pcn_res); - sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); - - chip_id = pcn_chip_id(dev); - - bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); - - switch((chip_id >> 12) & PART_MASK) { - case Am79C971: - case Am79C972: - case Am79C973: - case Am79C975: - case Am79C976: - case Am79C978: - break; - default: - return(ENXIO); - } - device_set_desc(dev, t->pcn_name); - return(BUS_PROBE_DEFAULT); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int -pcn_attach(dev) - device_t dev; -{ - u_int32_t eaddr[2]; - struct pcn_softc *sc; - struct mii_data *mii; - struct mii_softc *miisc; - struct ifnet *ifp; - int error = 0, rid; - - sc = device_get_softc(dev); - - /* Initialize our mutex. */ - mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, - MTX_DEF); - /* - * Map control/status registers. - */ - pci_enable_busmaster(dev); - - /* Retrieve the chip ID */ - sc->pcn_type = (pcn_chip_id(dev) >> 12) & PART_MASK; - device_printf(dev, "Chip ID %04x (%s)\n", - sc->pcn_type, pcn_chipid_name(sc->pcn_type)); - - rid = PCN_RID; - sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); - - if (sc->pcn_res == NULL) { - device_printf(dev, "couldn't map ports/memory\n"); - error = ENXIO; - goto fail; - } - - sc->pcn_btag = rman_get_bustag(sc->pcn_res); - sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); - - /* Allocate interrupt */ - rid = 0; - sc->pcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->pcn_irq == NULL) { - device_printf(dev, "couldn't map interrupt\n"); - error = ENXIO; - goto fail; - } - - /* Reset the adapter. */ - pcn_reset(sc); - - /* - * Get station address from the EEPROM. - */ - eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); - eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); - - callout_init_mtx(&sc->pcn_stat_callout, &sc->pcn_mtx, 0); - - sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, - M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); - - if (sc->pcn_ldata == NULL) { - device_printf(dev, "no memory for list buffers!\n"); - error = ENXIO; - goto fail; - } - bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); - - ifp = sc->pcn_ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(dev, "can not if_alloc()\n"); - error = ENOSPC; - goto fail; - } - ifp->if_softc = sc; - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = pcn_ioctl; - ifp->if_start = pcn_start; - ifp->if_watchdog = pcn_watchdog; - ifp->if_init = pcn_init; - ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; - - /* - * Do MII setup. - */ - sc->pcn_extphyaddr = -1; - if (mii_phy_probe(dev, &sc->pcn_miibus, - pcn_ifmedia_upd, pcn_ifmedia_sts)) { - device_printf(dev, "MII without any PHY!\n"); - error = ENXIO; - goto fail; - } - /* - * Record the media instances of internal PHYs, which map the - * built-in interfaces to the MII, so we can set the active - * PHY/port based on the currently selected media. - */ - sc->pcn_inst_10bt = -1; - mii = device_get_softc(sc->pcn_miibus); - LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { - switch (miisc->mii_phy) { - case PCN_PHYAD_10BT: - sc->pcn_inst_10bt = miisc->mii_inst; - break; - /* - * XXX deal with the Am79C97{3,5} internal 100baseT - * and the Am79C978 internal HomePNA PHYs. - */ - } - } - - /* - * Call MI attach routine. - */ - ether_ifattach(ifp, (u_int8_t *) eaddr); - - /* Hook interrupt last to avoid having to lock softc */ - error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET | INTR_MPSAFE, - NULL, pcn_intr, sc, &sc->pcn_intrhand); - - if (error) { - device_printf(dev, "couldn't set up irq\n"); - ether_ifdetach(ifp); - goto fail; - } - -fail: - if (error) - pcn_detach(dev); - - return(error); -} - -/* - * Shutdown hardware and free up resources. This can be called any - * time after the mutex has been initialized. It is called in both - * the error case in attach and the normal detach case so it needs - * to be careful about only freeing resources that have actually been - * allocated. - */ -static int -pcn_detach(dev) - device_t dev; -{ - struct pcn_softc *sc; - struct ifnet *ifp; - - sc = device_get_softc(dev); - ifp = sc->pcn_ifp; - - KASSERT(mtx_initialized(&sc->pcn_mtx), ("pcn mutex not initialized")); - - /* These should only be active if attach succeeded */ - if (device_is_attached(dev)) { - PCN_LOCK(sc); - pcn_reset(sc); - pcn_stop(sc); - PCN_UNLOCK(sc); - callout_drain(&sc->pcn_stat_callout); - ether_ifdetach(ifp); - } - if (sc->pcn_miibus) - device_delete_child(dev, sc->pcn_miibus); - bus_generic_detach(dev); - - if (sc->pcn_intrhand) - bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); - if (sc->pcn_irq) - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); - if (sc->pcn_res) - bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); - - if (ifp) - if_free(ifp); - - if (sc->pcn_ldata) { - contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), - M_DEVBUF); - } - - mtx_destroy(&sc->pcn_mtx); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int -pcn_list_tx_init(sc) - struct pcn_softc *sc; -{ - struct pcn_list_data *ld; - struct pcn_ring_data *cd; - int i; - - cd = &sc->pcn_cdata; - ld = sc->pcn_ldata; - - for (i = 0; i < PCN_TX_LIST_CNT; i++) { - cd->pcn_tx_chain[i] = NULL; - ld->pcn_tx_list[i].pcn_tbaddr = 0; - ld->pcn_tx_list[i].pcn_txctl = 0; - ld->pcn_tx_list[i].pcn_txstat = 0; - } - - cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. - */ -static int -pcn_list_rx_init(sc) - struct pcn_softc *sc; -{ - struct pcn_ring_data *cd; - int i; - - cd = &sc->pcn_cdata; - - for (i = 0; i < PCN_RX_LIST_CNT; i++) { - if (pcn_newbuf(sc, i, NULL) == ENOBUFS) - return(ENOBUFS); - } - - cd->pcn_rx_prod = 0; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - */ -static int -pcn_newbuf(sc, idx, m) - struct pcn_softc *sc; - int idx; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - struct pcn_rx_desc *c; - - c = &sc->pcn_ldata->pcn_rx_list[idx]; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) - return(ENOBUFS); - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - m_adj(m_new, ETHER_ALIGN); - - sc->pcn_cdata.pcn_rx_chain[idx] = m_new; - c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); - c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; - c->pcn_bufsz |= PCN_RXLEN_MBO; - c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; - - return(0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void -pcn_rxeof(sc) - struct pcn_softc *sc; -{ - struct mbuf *m; - struct ifnet *ifp; - struct pcn_rx_desc *cur_rx; - int i; - - PCN_LOCK_ASSERT(sc); - - ifp = sc->pcn_ifp; - i = sc->pcn_cdata.pcn_rx_prod; - - while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { - cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; - m = sc->pcn_cdata.pcn_rx_chain[i]; - sc->pcn_cdata.pcn_rx_chain[i] = NULL; - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { - ifp->if_ierrors++; - pcn_newbuf(sc, i, m); - PCN_INC(i, PCN_RX_LIST_CNT); - continue; - } - - if (pcn_newbuf(sc, i, NULL)) { - /* Ran out of mbufs; recycle this one. */ - pcn_newbuf(sc, i, m); - ifp->if_ierrors++; - PCN_INC(i, PCN_RX_LIST_CNT); - continue; - } - - PCN_INC(i, PCN_RX_LIST_CNT); - - /* No errors; receive the packet. */ - ifp->if_ipackets++; - m->m_len = m->m_pkthdr.len = - cur_rx->pcn_rxlen - ETHER_CRC_LEN; - m->m_pkthdr.rcvif = ifp; - - PCN_UNLOCK(sc); - (*ifp->if_input)(ifp, m); - PCN_LOCK(sc); - } - - sc->pcn_cdata.pcn_rx_prod = i; - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void -pcn_txeof(sc) - struct pcn_softc *sc; -{ - struct pcn_tx_desc *cur_tx = NULL; - struct ifnet *ifp; - u_int32_t idx; - - ifp = sc->pcn_ifp; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - idx = sc->pcn_cdata.pcn_tx_cons; - while (idx != sc->pcn_cdata.pcn_tx_prod) { - cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; - - if (!PCN_OWN_TXDESC(cur_tx)) - break; - - if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { - sc->pcn_cdata.pcn_tx_cnt--; - PCN_INC(idx, PCN_TX_LIST_CNT); - continue; - } - - if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { - ifp->if_oerrors++; - if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) - ifp->if_collisions++; - if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) - ifp->if_collisions++; - } - - ifp->if_collisions += - cur_tx->pcn_txstat & PCN_TXSTAT_TRC; - - ifp->if_opackets++; - if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { - m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); - sc->pcn_cdata.pcn_tx_chain[idx] = NULL; - } - - sc->pcn_cdata.pcn_tx_cnt--; - PCN_INC(idx, PCN_TX_LIST_CNT); - } - - if (idx != sc->pcn_cdata.pcn_tx_cons) { - /* Some buffers have been freed. */ - sc->pcn_cdata.pcn_tx_cons = idx; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - } - ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5; - - return; -} - -static void -pcn_tick(xsc) - void *xsc; -{ - struct pcn_softc *sc; - struct mii_data *mii; - struct ifnet *ifp; - - sc = xsc; - ifp = sc->pcn_ifp; - PCN_LOCK_ASSERT(sc); - - mii = device_get_softc(sc->pcn_miibus); - mii_tick(mii); - - /* link just died */ - if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) - sc->pcn_link = 0; - - /* link just came up, restart */ - if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->pcn_link++; - if (ifp->if_snd.ifq_head != NULL) - pcn_start_locked(ifp); - } - - callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); - - return; -} - -static void -pcn_intr(arg) - void *arg; -{ - struct pcn_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - sc = arg; - ifp = sc->pcn_ifp; - - PCN_LOCK(sc); - - /* Suppress unwanted interrupts */ - if (!(ifp->if_flags & IFF_UP)) { - pcn_stop(sc); - PCN_UNLOCK(sc); - return; - } - - CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); - - while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { - CSR_WRITE_4(sc, PCN_IO32_RDP, status); - - if (status & PCN_CSR_RINT) - pcn_rxeof(sc); - - if (status & PCN_CSR_TINT) - pcn_txeof(sc); - - if (status & PCN_CSR_ERR) { - pcn_init_locked(sc); - break; - } - } - - if (ifp->if_snd.ifq_head != NULL) - pcn_start_locked(ifp); - - PCN_UNLOCK(sc); - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int -pcn_encap(sc, m_head, txidx) - struct pcn_softc *sc; - struct mbuf *m_head; - u_int32_t *txidx; -{ - struct pcn_tx_desc *f = NULL; - struct mbuf *m; - int frag, cur, cnt = 0; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - cur = frag = *txidx; - - for (m = m_head; m != NULL; m = m->m_next) { - if (m->m_len == 0) - continue; - - if ((PCN_TX_LIST_CNT - (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) - return(ENOBUFS); - f = &sc->pcn_ldata->pcn_tx_list[frag]; - f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; - f->pcn_txctl |= PCN_TXCTL_MBO; - f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); - if (cnt == 0) - f->pcn_txctl |= PCN_TXCTL_STP; - else - f->pcn_txctl |= PCN_TXCTL_OWN; - cur = frag; - PCN_INC(frag, PCN_TX_LIST_CNT); - cnt++; - } - - if (m != NULL) - return(ENOBUFS); - - sc->pcn_cdata.pcn_tx_chain[cur] = m_head; - sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= - PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; - sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; - sc->pcn_cdata.pcn_tx_cnt += cnt; - *txidx = frag; - - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ -static void -pcn_start(ifp) - struct ifnet *ifp; -{ - struct pcn_softc *sc; - - sc = ifp->if_softc; - PCN_LOCK(sc); - pcn_start_locked(ifp); - PCN_UNLOCK(sc); -} - -static void -pcn_start_locked(ifp) - struct ifnet *ifp; -{ - struct pcn_softc *sc; - struct mbuf *m_head = NULL; - u_int32_t idx; - - sc = ifp->if_softc; - - PCN_LOCK_ASSERT(sc); - - if (!sc->pcn_link) - return; - - idx = sc->pcn_cdata.pcn_tx_prod; - - if (ifp->if_drv_flags & IFF_DRV_OACTIVE) - return; - - while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - if (pcn_encap(sc, m_head, &idx)) { - IF_PREPEND(&ifp->if_snd, m_head); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - BPF_MTAP(ifp, m_head); - - } - - /* Transmit */ - sc->pcn_cdata.pcn_tx_prod = idx; - pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void -pcn_setfilt(ifp) - struct ifnet *ifp; -{ - struct pcn_softc *sc; - - sc = ifp->if_softc; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) { - PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); - } else { - PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); - } - - /* Set the capture broadcast bit to capture broadcast frames. */ - if (ifp->if_flags & IFF_BROADCAST) { - PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); - } else { - PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); - } - - return; -} - -static void -pcn_init(xsc) - void *xsc; -{ - struct pcn_softc *sc = xsc; - - PCN_LOCK(sc); - pcn_init_locked(sc); - PCN_UNLOCK(sc); -} - -static void -pcn_init_locked(sc) - struct pcn_softc *sc; -{ - struct ifnet *ifp = sc->pcn_ifp; - struct mii_data *mii = NULL; - struct ifmedia_entry *ife; - - PCN_LOCK_ASSERT(sc); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - pcn_stop(sc); - pcn_reset(sc); - - mii = device_get_softc(sc->pcn_miibus); - ife = mii->mii_media.ifm_cur; - - /* Set MAC address */ - pcn_csr_write(sc, PCN_CSR_PAR0, - ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[0]); - pcn_csr_write(sc, PCN_CSR_PAR1, - ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[1]); - pcn_csr_write(sc, PCN_CSR_PAR2, - ((u_int16_t *)IF_LLADDR(sc->pcn_ifp))[2]); - - /* Init circular RX list. */ - if (pcn_list_rx_init(sc) == ENOBUFS) { - if_printf(ifp, "initialization failed: no " - "memory for rx buffers\n"); - pcn_stop(sc); - return; - } - - /* - * Init tx descriptors. - */ - pcn_list_tx_init(sc); - - /* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */ - PCN_BCR_CLRBIT(sc, PCN_BCR_MISCCFG, PCN_MISC_ASEL); - - /* - * Set up the port based on the currently selected media. - * For Am79C978 we've to unconditionally set PCN_PORT_MII and - * set the PHY in PCN_BCR_PHYSEL instead. - */ - if (sc->pcn_type != Am79C978 && - IFM_INST(ife->ifm_media) == sc->pcn_inst_10bt) - pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_10BASET); - else - pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); - - /* Set up RX filter. */ - pcn_setfilt(ifp); - - /* - * Load the multicast filter. - */ - pcn_setmulti(sc); - - /* - * Load the addresses of the RX and TX lists. - */ - pcn_csr_write(sc, PCN_CSR_RXADDR0, - vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); - pcn_csr_write(sc, PCN_CSR_RXADDR1, - (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); - pcn_csr_write(sc, PCN_CSR_TXADDR0, - vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); - pcn_csr_write(sc, PCN_CSR_TXADDR1, - (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); - - /* Set the RX and TX ring sizes. */ - pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); - pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); - - /* We're not using the initialization block. */ - pcn_csr_write(sc, PCN_CSR_IAB1, 0); - - /* Enable fast suspend mode. */ - PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); - - /* - * Enable burst read and write. Also set the no underflow - * bit. This will avoid transmit underruns in certain - * conditions while still providing decent performance. - */ - PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| - PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); - - /* Enable graceful recovery from underflow. */ - PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); - - /* Enable auto-padding of short TX frames. */ - PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); - - /* Disable MII autoneg (we handle this ourselves). */ - PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); - - if (sc->pcn_type == Am79C978) - /* XXX support other PHYs? */ - pcn_bcr_write(sc, PCN_BCR_PHYSEL, - PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); - - /* Enable interrupts and start the controller running. */ - pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); - - mii_mediachg(mii); - - ifp->if_drv_flags |= IFF_DRV_RUNNING; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - - callout_reset(&sc->pcn_stat_callout, hz, pcn_tick, sc); - - return; -} - -/* - * Set media options. - */ -static int -pcn_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct pcn_softc *sc; - - sc = ifp->if_softc; - - PCN_LOCK(sc); - - /* - * At least Am79C971 with DP83840A can wedge when switching - * from the internal 10baseT PHY to the external PHY without - * issuing pcn_reset(). For setting the port in PCN_CSR_MODE - * the PCnet chip has to be powered down or stopped anyway - * and although documented otherwise it doesn't take effect - * until the next initialization. - */ - sc->pcn_link = 0; - pcn_stop(sc); - pcn_reset(sc); - pcn_init_locked(sc); - if (ifp->if_snd.ifq_head != NULL) - pcn_start_locked(ifp); - - PCN_UNLOCK(sc); - - return(0); -} - -/* - * Report current media status. - */ -static void -pcn_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct pcn_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; - - mii = device_get_softc(sc->pcn_miibus); - PCN_LOCK(sc); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - PCN_UNLOCK(sc); - - return; -} - -static int -pcn_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct pcn_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *) data; - struct mii_data *mii = NULL; - int error = 0; - - switch(command) { - case SIOCSIFFLAGS: - PCN_LOCK(sc); - if (ifp->if_flags & IFF_UP) { - if (ifp->if_drv_flags & IFF_DRV_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->pcn_if_flags & IFF_PROMISC)) { - PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, - PCN_EXTCTL1_SPND); - pcn_setfilt(ifp); - PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, - PCN_EXTCTL1_SPND); - pcn_csr_write(sc, PCN_CSR_CSR, - PCN_CSR_INTEN|PCN_CSR_START); - } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->pcn_if_flags & IFF_PROMISC) { - PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, - PCN_EXTCTL1_SPND); - pcn_setfilt(ifp); - PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, - PCN_EXTCTL1_SPND); - pcn_csr_write(sc, PCN_CSR_CSR, - PCN_CSR_INTEN|PCN_CSR_START); - } else if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) - pcn_init_locked(sc); - } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - pcn_stop(sc); - } - sc->pcn_if_flags = ifp->if_flags; - PCN_UNLOCK(sc); - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - PCN_LOCK(sc); - pcn_setmulti(sc); - PCN_UNLOCK(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - mii = device_get_softc(sc->pcn_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - break; - default: - error = ether_ioctl(ifp, command, data); - break; - } - - return(error); -} - -static void -pcn_watchdog(ifp) - struct ifnet *ifp; -{ - struct pcn_softc *sc; - - sc = ifp->if_softc; - - PCN_LOCK(sc); - - ifp->if_oerrors++; - if_printf(ifp, "watchdog timeout\n"); - - pcn_stop(sc); - pcn_reset(sc); - pcn_init_locked(sc); - - if (ifp->if_snd.ifq_head != NULL) - pcn_start(ifp); - - PCN_UNLOCK(sc); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void -pcn_stop(sc) - struct pcn_softc *sc; -{ - register int i; - struct ifnet *ifp; - - PCN_LOCK_ASSERT(sc); - ifp = sc->pcn_ifp; - ifp->if_timer = 0; - - callout_stop(&sc->pcn_stat_callout); - - /* Turn off interrupts */ - PCN_CSR_CLRBIT(sc, PCN_CSR_CSR, PCN_CSR_INTEN); - /* Stop adapter */ - PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); - sc->pcn_link = 0; - - /* - * Free data in the RX lists. - */ - for (i = 0; i < PCN_RX_LIST_CNT; i++) { - if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { - m_freem(sc->pcn_cdata.pcn_rx_chain[i]); - sc->pcn_cdata.pcn_rx_chain[i] = NULL; - } - } - bzero((char *)&sc->pcn_ldata->pcn_rx_list, - sizeof(sc->pcn_ldata->pcn_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < PCN_TX_LIST_CNT; i++) { - if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { - m_freem(sc->pcn_cdata.pcn_tx_chain[i]); - sc->pcn_cdata.pcn_tx_chain[i] = NULL; - } - } - - bzero((char *)&sc->pcn_ldata->pcn_tx_list, - sizeof(sc->pcn_ldata->pcn_tx_list)); - - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void -pcn_shutdown(dev) - device_t dev; -{ - struct pcn_softc *sc; - - sc = device_get_softc(dev); - - PCN_LOCK(sc); - pcn_reset(sc); - pcn_stop(sc); - PCN_UNLOCK(sc); - - return; -} diff --git a/sys/pci/if_pcnreg.h b/sys/pci/if_pcnreg.h deleted file mode 100644 index 406d438..0000000 --- a/sys/pci/if_pcnreg.h +++ /dev/null @@ -1,530 +0,0 @@ -/*- - * Copyright (c) 2000 Berkeley Software Design, Inc. - * Copyright (c) 1997, 1998, 1999, 2000 - * Bill Paul . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* - * I/O map in 16-bit mode. To switch to 32-bit mode, - * you need to perform a 32-bit write to the RDP register - * (writing a 0 is recommended). - */ -#define PCN_IO16_APROM00 0x00 -#define PCN_IO16_APROM01 0x02 -#define PCN_IO16_APROM02 0x04 -#define PCN_IO16_APROM03 0x06 -#define PCN_IO16_APROM04 0x08 -#define PCN_IO16_APROM05 0x0A -#define PCN_IO16_APROM06 0x0C -#define PCN_IO16_APROM07 0x0E -#define PCN_IO16_RDP 0x10 -#define PCN_IO16_RAP 0x12 -#define PCN_IO16_RESET 0x14 -#define PCN_IO16_BDP 0x16 - -/* - * I/O map in 32-bit mode. - */ -#define PCN_IO32_APROM00 0x00 -#define PCN_IO32_APROM01 0x04 -#define PCN_IO32_APROM02 0x08 -#define PCN_IO32_APROM03 0x0C -#define PCN_IO32_RDP 0x10 -#define PCN_IO32_RAP 0x14 -#define PCN_IO32_RESET 0x18 -#define PCN_IO32_BDP 0x1C - -/* - * CSR registers - */ -#define PCN_CSR_CSR 0x00 -#define PCN_CSR_IAB0 0x01 -#define PCN_CSR_IAB1 0x02 -#define PCN_CSR_IMR 0x03 -#define PCN_CSR_TFEAT 0x04 -#define PCN_CSR_EXTCTL1 0x05 -#define PCN_CSR_DTBLLEN 0x06 -#define PCN_CSR_EXTCTL2 0x07 -#define PCN_CSR_MAR0 0x08 -#define PCN_CSR_MAR1 0x09 -#define PCN_CSR_MAR2 0x0A -#define PCN_CSR_MAR3 0x0B -#define PCN_CSR_PAR0 0x0C -#define PCN_CSR_PAR1 0x0D -#define PCN_CSR_PAR2 0x0E -#define PCN_CSR_MODE 0x0F -#define PCN_CSR_RXADDR0 0x18 -#define PCN_CSR_RXADDR1 0x19 -#define PCN_CSR_TXADDR0 0x1E -#define PCN_CSR_TXADDR1 0x1F -#define PCN_CSR_TXPOLL 0x2F -#define PCN_CSR_RXPOLL 0x31 -#define PCN_CSR_RXRINGLEN 0x4C -#define PCN_CSR_TXRINGLEN 0x4E -#define PCN_CSR_DMACTL 0x50 -#define PCN_CSR_BUSTIMER 0x52 -#define PCN_CSR_MEMERRTIMEO 0x64 -#define PCN_CSR_ONNOWMISC 0x74 -#define PCN_CSR_ADVFEAT 0x7A -#define PCN_CSR_MACCFG 0x7D -#define PCN_CSR_CHIPID0 0x58 -#define PCN_CSR_CHIPID1 0x59 - -/* - * Control and status register (CSR0) - */ -#define PCN_CSR_INIT 0x0001 -#define PCN_CSR_START 0x0002 -#define PCN_CSR_STOP 0x0004 -#define PCN_CSR_TX 0x0008 -#define PCN_CSR_TXON 0x0010 -#define PCN_CSR_RXON 0x0020 -#define PCN_CSR_INTEN 0x0040 -#define PCN_CSR_INTR 0x0080 -#define PCN_CSR_IDONE 0x0100 -#define PCN_CSR_TINT 0x0200 -#define PCN_CSR_RINT 0x0400 -#define PCN_CSR_MERR 0x0800 -#define PCN_CSR_MISS 0x1000 -#define PCN_CSR_CERR 0x2000 -#define PCN_CSR_ERR 0x8000 - -/* - * Interrupt masks and deferral control (CSR3) - */ -#define PCN_IMR_BSWAP 0x0004 -#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */ -#define PCN_IMR_DXMT2PD 0x0010 -#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */ -#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */ -#define PCN_IMR_IDONE 0x0100 -#define PCN_IMR_TINT 0x0200 -#define PCN_IMR_RINT 0x0400 -#define PCN_IMR_MERR 0x0800 -#define PCN_IMR_MISS 0x1000 - -/* - * Test and features control (CSR4) - */ -#define PCN_TFEAT_TXSTRTMASK 0x0004 -#define PCN_TFEAT_TXSTRT 0x0008 -#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */ -#define PCN_TFEAT_RXCCOFLOW 0x0020 -#define PCN_TFEAT_UINT 0x0040 -#define PCN_TFEAT_UINTREQ 0x0080 -#define PCN_TFEAT_MISSOFLOWM 0x0100 -#define PCN_TFEAT_MISSOFLOW 0x0200 -#define PCN_TFEAT_STRIP_FCS 0x0400 -#define PCN_TFEAT_PAD_TX 0x0800 -#define PCN_TFEAT_TXDPOLL 0x1000 -#define PCN_TFEAT_DMAPLUS 0x4000 - -/* - * Extended control and interrupt 1 (CSR5) - */ -#define PCN_EXTCTL1_SPND 0x0001 /* suspend */ -#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */ -#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */ -#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */ -#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */ -#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */ -#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */ -#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */ -#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */ -#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */ -#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */ -#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */ - -/* - * RX/TX descriptor len (CSR6) - */ -#define PCN_DTBLLEN_RLEN 0x0F00 -#define PCN_DTBLLEN_TLEN 0xF000 - -/* - * Extended control and interrupt 2 (CSR7) - */ -#define PCN_EXTCTL2_MIIPDTINTE 0x0001 -#define PCN_EXTCTL2_MIIPDTINT 0x0002 -#define PCN_EXTCTL2_MCCIINTE 0x0004 -#define PCN_EXTCTL2_MCCIINT 0x0008 -#define PCN_EXTCTL2_MCCINTE 0x0010 -#define PCN_EXTCTL2_MCCINT 0x0020 -#define PCN_EXTCTL2_MAPINTE 0x0040 -#define PCN_EXTCTL2_MAPINT 0x0080 -#define PCN_EXTCTL2_MREINTE 0x0100 -#define PCN_EXTCTL2_MREINT 0x0200 -#define PCN_EXTCTL2_STINTE 0x0400 -#define PCN_EXTCTL2_STINT 0x0800 -#define PCN_EXTCTL2_RXDPOLL 0x1000 -#define PCN_EXTCTL2_RDMD 0x2000 -#define PCN_EXTCTL2_RXFRTG 0x4000 -#define PCN_EXTCTL2_FASTSPNDE 0x8000 - - -/* - * Mode (CSR15) - */ -#define PCN_MODE_RXD 0x0001 /* RX disable */ -#define PCN_MODE_TXD 0x0002 /* TX disable */ -#define PCN_MODE_LOOP 0x0004 /* loopback enable */ -#define PCN_MODE_TXCRCD 0x0008 -#define PCN_MODE_FORCECOLL 0x0010 -#define PCN_MODE_RETRYD 0x0020 -#define PCN_MODE_INTLOOP 0x0040 -#define PCN_MODE_PORTSEL 0x0180 -#define PCN_MODE_RXVPAD 0x2000 -#define PCN_MODE_RXNOBROAD 0x4000 -#define PCN_MODE_PROMISC 0x8000 - -/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */ -#define PCN_PORT_AUI 0x0000 -#define PCN_PORT_10BASET 0x0080 -#define PCN_PORT_GPSI 0x0100 -#define PCN_PORT_MII 0x0180 - -/* - * Chip ID values. - */ -/* CSR88-89: Chip ID masks */ -#define AMD_MASK 0x003 -#define PART_MASK 0xffff -#define Am79C971 0x2623 -#define Am79C972 0x2624 -#define Am79C973 0x2625 -#define Am79C978 0x2626 -#define Am79C975 0x2627 -#define Am79C976 0x2628 - -/* - * Advanced feature control (CSR122) - */ -#define PCN_AFC_RXALIGN 0x0001 - -/* - * BCR (bus control) registers - */ -#define PCN_BCR_MMRA 0x00 /* Master Mode Read Active */ -#define PCN_BCR_MMW 0x01 /* Master Mode Write Active */ -#define PCN_BCR_MISCCFG 0x02 -#define PCN_BCR_LED0 0x04 -#define PCN_BCR_LED1 0x05 -#define PCN_BCR_LED2 0x06 -#define PCN_BCR_LED3 0x07 -#define PCN_BCR_DUPLEX 0x09 -#define PCN_BCR_BUSCTL 0x12 -#define PCN_BCR_EECTL 0x13 -#define PCN_BCR_SSTYLE 0x14 -#define PCN_BCR_PCILAT 0x16 -#define PCN_BCR_PCISUBVENID 0x17 -#define PCN_BCR_PCISUBSYSID 0x18 -#define PCN_BCR_SRAMSIZE 0x19 -#define PCN_BCR_SRAMBOUND 0x1A -#define PCN_BCR_SRAMCTL 0x1B -#define PCN_BCR_MIICTL 0x20 -#define PCN_BCR_MIIADDR 0x21 -#define PCN_BCR_MIIDATA 0x22 -#define PCN_BCR_PCIVENID 0x23 -#define PCN_BCR_PCIPCAP 0x24 -#define PCN_BCR_DATA0 0x25 -#define PCN_BCR_DATA1 0x26 -#define PCN_BCR_DATA2 0x27 -#define PCN_BCR_DATA3 0x28 -#define PCN_BCR_DATA4 0x29 -#define PCN_BCR_DATA5 0x2A -#define PCN_BCR_DATA6 0x2B -#define PCN_BCR_DATA7 0x2C -#define PCN_BCR_ONNOWPAT0 0x2D -#define PCN_BCR_ONNOWPAT1 0x2E -#define PCN_BCR_ONNOWPAT2 0x2F -#define PCN_BCR_PHYSEL 0x31 - -/* - * Miscellaneous Configuration (BCR2) - */ -#define PCN_MISC_TMAULOOP 1<<14 /* T-MAU Loopback packet enable. */ -#define PCN_MISC_LEDPE 1<<12 /* LED Program Enable */ -#define PCN_MISC_APROMWE 1<<8 /* Address PROM Write Enable */ -#define PCN_MISC_INTLEVEL 1<<7 /* Interrupt level */ -#define PCN_MISC_EADISEL 1<<3 /* EADI Select */ -#define PCN_MISC_AWAKE 1<<2 /* Power saving mode select */ -#define PCN_MISC_ASEL 1<<1 /* Auto Select */ -#define PCN_MISC_XMAUSEL 1<<0 /* Reserved. */ - -/* - * Full duplex control (BCR9) - */ -#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ -#define PCN_DUPLEX_AUI 0x0002 /* AUI full-duplex */ -#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */ - -/* - * Burst and bus control register (BCR18) - */ -#define PCN_BUSCTL_BWRITE 0x0020 -#define PCN_BUSCTL_BREAD 0x0040 -#define PCN_BUSCTL_DWIO 0x0080 -#define PCN_BUSCTL_EXTREQ 0x0100 -#define PCN_BUSCTL_MEMCMD 0x0200 -#define PCN_BUSCTL_NOUFLOW 0x0800 -#define PCN_BUSCTL_ROMTMG 0xF000 - -/* - * EEPROM control (BCR19) - */ -#define PCN_EECTL_EDATA 0x0001 -#define PCN_EECTL_ECLK 0x0002 -#define PCN_EECTL_EECS 0x0004 -#define PCN_EECTL_EEN 0x0100 -#define PCN_EECTL_EEDET 0x2000 -#define PCN_EECTL_PREAD 0x4000 -#define PCN_EECTL_PVALID 0x8000 - -/* - * Software style (BCR20) - */ -#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */ -#define PCN_SSTYLE_SSIZE32 0x0100 -#define PCN_SSTYLE_SWSTYLE 0x00FF - -#define PCN_SWSTYLE_LANCE 0x0000 -#define PCN_SWSTYLE_PCNETPCI 0x0102 -#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103 - -/* - * MII control and status (BCR32) - */ -#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */ -#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */ -#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */ -#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */ -#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */ -#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */ -#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */ -#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */ -#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */ -#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */ -#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */ - -/* - * MII address register (BCR33) - */ -#define PCN_MIIADDR_REGAD 0x001F -#define PCN_MIIADDR_PHYAD 0x03E0 - -/* addresses of internal PHYs */ -#define PCN_PHYAD_100BTX 30 -#define PCN_PHYAD_10BT 31 - -/* - * MII data register (BCR34) - */ -#define PCN_MIIDATA_MIIMD 0xFFFF - -/* - * PHY selection (BCR49) (HomePNA NIC only) - */ -#define PCN_PHYSEL_PHYSEL 0x0003 -#define PCN_PHYSEL_DEFAULT 0x0300 -#define PCN_PHYSEL_PCNET 0x8000 - -#define PCN_PHY_10BT 0x0000 -#define PCN_PHY_HOMEPNA 0x0001 -#define PCN_PHY_EXTERNAL 0x0002 - -struct pcn_rx_desc { - u_int16_t pcn_rxlen; - u_int16_t pcn_rsvd0; - u_int16_t pcn_bufsz; - u_int16_t pcn_rxstat; - u_int32_t pcn_rbaddr; - u_int32_t pcn_uspace; -}; - -#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */ -#define PCN_RXSTAT_ENP 0x0100 /* end of packet */ -#define PCN_RXSTAT_STP 0x0200 /* start of packet */ -#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */ -#define PCN_RXSTAT_CRC 0x0800 /* CRC error */ -#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */ -#define PCN_RXSTAT_FRAM 0x2000 /* framing error */ -#define PCN_RXSTAT_ERR 0x4000 /* error summary */ -#define PCN_RXSTAT_OWN 0x8000 - -#define PCN_RXLEN_MBO 0xF000 -#define PCN_RXLEN_BUFSZ 0x0FFF - -#define PCN_OWN_RXDESC(x) (((x)->pcn_rxstat & PCN_RXSTAT_OWN) == 0) - -struct pcn_tx_desc { - u_int32_t pcn_txstat; - u_int32_t pcn_txctl; - u_int32_t pcn_tbaddr; - u_int32_t pcn_uspace; -}; - -#define PCN_TXSTAT_TRC 0x0000000F /* transmit retries */ -#define PCN_TXSTAT_RTRY 0x04000000 /* retry */ -#define PCN_TXSTAT_LCAR 0x08000000 /* lost carrier */ -#define PCN_TXSTAT_LCOL 0x10000000 /* late collision */ -#define PCN_TXSTAT_EXDEF 0x20000000 /* excessive deferrals */ -#define PCN_TXSTAT_UFLOW 0x40000000 /* transmit underrun */ -#define PCN_TXSTAT_BUFF 0x80000000 /* buffer error */ - -#define PCN_TXCTL_OWN 0x80000000 -#define PCN_TXCTL_ERR 0x40000000 /* error summary */ -#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */ -#define PCN_TXCTL_MORE_LTINT 0x10000000 -#define PCN_TXCTL_ONE 0x08000000 -#define PCN_TXCTL_DEF 0x04000000 -#define PCN_TXCTL_STP 0x02000000 -#define PCN_TXCTL_ENP 0x01000000 -#define PCN_TXCTL_BPE 0x00800000 -#define PCN_TXCTL_MBO 0x0000F000 -#define PCN_TXCTL_BUFSZ 0x00000FFF - -#define PCN_OWN_TXDESC(x) (((x)->pcn_txctl & PCN_TXCTL_OWN) == 0) - -#define PCN_RX_LIST_CNT 64 -#define PCN_TX_LIST_CNT 256 - -struct pcn_list_data { - struct pcn_rx_desc pcn_rx_list[PCN_RX_LIST_CNT]; - struct pcn_tx_desc pcn_tx_list[PCN_TX_LIST_CNT]; -}; - -struct pcn_ring_data { - struct mbuf *pcn_rx_chain[PCN_RX_LIST_CNT]; - struct mbuf *pcn_tx_chain[PCN_TX_LIST_CNT]; - int pcn_rx_prod; - int pcn_tx_prod; - int pcn_tx_cons; - int pcn_tx_cnt; -}; - -/* - * AMD PCI vendor ID. - */ -#define PCN_VENDORID 0x1022 - -/* - * AMD PCnet/PCI device IDs - */ -#define PCN_DEVICEID_PCNET 0x2000 -#define PCN_DEVICEID_HOME 0x2001 - -struct pcn_type { - u_int16_t pcn_vid; - u_int16_t pcn_did; - const char *pcn_name; -}; - -struct pcn_softc { - struct ifnet *pcn_ifp; - bus_space_handle_t pcn_bhandle; - bus_space_tag_t pcn_btag; - struct resource *pcn_res; - struct resource *pcn_irq; - void *pcn_intrhand; - device_t pcn_miibus; - u_int8_t pcn_link; - int8_t pcn_extphyaddr; - int8_t pcn_inst_10bt; - int pcn_if_flags; - int pcn_type; - struct pcn_list_data *pcn_ldata; - struct pcn_ring_data pcn_cdata; - struct callout pcn_stat_callout; - struct mtx pcn_mtx; -}; - -#define PCN_LOCK(_sc) mtx_lock(&(_sc)->pcn_mtx) -#define PCN_UNLOCK(_sc) mtx_unlock(&(_sc)->pcn_mtx) -#define PCN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->pcn_mtx, MA_OWNED) - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->pcn_btag, sc->pcn_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->pcn_btag, sc->pcn_bhandle, reg) - -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->pcn_btag, sc->pcn_bhandle, reg, val) - -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->pcn_btag, sc->pcn_bhandle, reg) - -#define PCN_TIMEOUT 1000 -#define ETHER_ALIGN 2 -#define PCN_RXLEN 1536 -#define PCN_MIN_FRAMELEN 60 -#define PCN_INC(x, y) (x) = (x + 1) % y -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define PCN_PCI_VENDOR_ID 0x00 -#define PCN_PCI_DEVICE_ID 0x02 -#define PCN_PCI_COMMAND 0x04 -#define PCN_PCI_STATUS 0x06 -#define PCN_PCI_REVID 0x08 -#define PCN_PCI_CLASSCODE 0x09 -#define PCN_PCI_CACHELEN 0x0C -#define PCN_PCI_LATENCY_TIMER 0x0D -#define PCN_PCI_HEADER_TYPE 0x0E -#define PCN_PCI_LOIO 0x10 -#define PCN_PCI_LOMEM 0x14 -#define PCN_PCI_BIOSROM 0x30 -#define PCN_PCI_INTLINE 0x3C -#define PCN_PCI_INTPIN 0x3D -#define PCN_PCI_MINGNT 0x3E -#define PCN_PCI_MINLAT 0x3F -#define PCN_PCI_RESETOPT 0x48 -#define PCN_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define PCN_PCI_CAPID 0x50 /* 8 bits */ -#define PCN_PCI_NEXTPTR 0x51 /* 8 bits */ -#define PCN_PCI_PWRMGMTCAP 0x52 /* 16 bits */ -#define PCN_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ - -#define PCN_PSTATE_MASK 0x0003 -#define PCN_PSTATE_D0 0x0000 -#define PCN_PSTATE_D1 0x0001 -#define PCN_PSTATE_D2 0x0002 -#define PCN_PSTATE_D3 0x0003 -#define PCN_PME_EN 0x0010 -#define PCN_PME_STATUS 0x8000 -- cgit v1.1