diff options
Diffstat (limited to 'sys/pci')
-rw-r--r-- | sys/pci/if_pcn.c | 1340 | ||||
-rw-r--r-- | sys/pci/if_pcnreg.h | 525 |
2 files changed, 1865 insertions, 0 deletions
diff --git a/sys/pci/if_pcn.c b/sys/pci/if_pcn.c new file mode 100644 index 0000000..2b5b60c --- /dev/null +++ b/sys/pci/if_pcn.c @@ -0,0 +1,1340 @@ +/* + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@osd.bsdi.com>. 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$ + */ + +/* + * AMD Am79c972 fast ethernet PCI NIC driver. Datatheets are available + * from http://www.amd.com. + * + * Written by Bill Paul <wpaul@osd.bsdi.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 <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/clock.h> /* for DELAY */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define PCN_USEIOSPACE + +#include <pci/if_pcnreg.h> + +MODULE_DEPEND(pcn, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static 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 u_int32_t pcn_csr_read __P((struct pcn_softc *, int)); +static void pcn_csr_write __P((struct pcn_softc *, int, int)); +static u_int32_t pcn_bcr_read __P((struct pcn_softc *, int)); +static void pcn_bcr_write __P((struct pcn_softc *, int, int)); + +static int pcn_probe __P((device_t)); +static int pcn_attach __P((device_t)); +static int pcn_detach __P((device_t)); + +static int pcn_newbuf __P((struct pcn_softc *, int, struct mbuf *)); +static int pcn_encap __P((struct pcn_softc *, + struct mbuf *, u_int32_t *)); +static void pcn_rxeof __P((struct pcn_softc *)); +static void pcn_txeof __P((struct pcn_softc *)); +static void pcn_intr __P((void *)); +static void pcn_tick __P((void *)); +static void pcn_start __P((struct ifnet *)); +static int pcn_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void pcn_init __P((void *)); +static void pcn_stop __P((struct pcn_softc *)); +static void pcn_watchdog __P((struct ifnet *)); +static void pcn_shutdown __P((device_t)); +static int pcn_ifmedia_upd __P((struct ifnet *)); +static void pcn_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); + +static int pcn_miibus_readreg __P((device_t, int, int)); +static int pcn_miibus_writereg __P((device_t, int, int, int)); +static void pcn_miibus_statchg __P((device_t)); + +static void pcn_setmulti __P((struct pcn_softc *)); +static u_int32_t pcn_crc __P((caddr_t)); +static void pcn_reset __P((struct pcn_softc *)); +static int pcn_list_rx_init __P((struct pcn_softc *)); +static int pcn_list_tx_init __P((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(if_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 void pcn_csr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; +{ + 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 void pcn_bcr_write(sc, reg, val) + struct pcn_softc *sc; + int reg; +{ + 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); + + if (phy >= 30) + 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); + 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; +} + +#define DC_POLY 0xEDB88320 + +static u_int32_t pcn_crc(addr) + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); + } + + return ((crc >> 26) & 0x3F); +} + +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->arpcom.ac_if; + + 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 */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashes[h >> 4] |= 1 << (h & 0xF); + } + + 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; +} + +/* + * 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; +{ + struct pcn_type *t; + struct pcn_softc *sc; + int rid; + u_int32_t chip_id; + + t = pcn_devs; + sc = device_get_softc(dev); + + while(t->pcn_name != NULL) { + if ((pci_get_vendor(dev) == t->pcn_vid) && + (pci_get_device(dev) == t->pcn_did)) { + /* + * Temporarily map the I/O space + * so we can read the chip ID register. + */ + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, + 0, ~0, 1, 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); + pcn_reset(sc); + chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); + chip_id <<= 16; + chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); + bus_release_resource(dev, PCN_RES, + PCN_RID, sc->pcn_res); + chip_id >>= 12; + sc->pcn_type = chip_id & PART_MASK; + switch(sc->pcn_type) { + case Am79C971: + case Am79C972: + case Am79C973: + case Am79C978: + break; + default: + return(ENXIO); + break; + } + device_set_desc(dev, t->pcn_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int pcn_attach(dev) + device_t dev; +{ + int s; + u_int32_t eaddr[2]; + u_int32_t command; + struct pcn_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + s = splimp(); + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + /* + * Handle power management nonsense. + */ + + command = pci_read_config(dev, PCN_PCI_CAPID, 4) & 0x000000FF; + if (command == 0x01) { + + command = pci_read_config(dev, PCN_PCI_PWRMGMTCTRL, 4); + if (command & PCN_PSTATE_MASK) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); + membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); + irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); + + /* Reset the power state. */ + printf("pcn%d: chip is in D%d power mode " + "-- setting to D0\n", unit, command & PCN_PSTATE_MASK); + command &= 0xFFFFFFFC; + pci_write_config(dev, PCN_PCI_PWRMGMTCTRL, command, 4); + + /* Restore PCI config data. */ + pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); + pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); + pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); + } + } + + /* + * Map control/status registers. + */ + command = pci_read_config(dev, PCIR_COMMAND, 4); + command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, command, 4); + command = pci_read_config(dev, PCIR_COMMAND, 4); + +#ifdef PCN_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("pcn%d: failed to enable I/O ports!\n", unit); + error = ENXIO;; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("pcn%d: failed to enable memory mapping!\n", unit); + error = ENXIO;; + goto fail; + } +#endif + + rid = PCN_RID; + sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->pcn_res == NULL) { + printf("pcn%d: couldn't map ports/memory\n", unit); + 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(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->pcn_irq == NULL) { + printf("pcn%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, + pcn_intr, sc, &sc->pcn_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + printf("pcn%d: couldn't set up irq\n", unit); + 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); + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + /* + * An AMD chip was detected. Inform the world. + */ + printf("pcn%d: Ethernet address: %6D\n", unit, + sc->arpcom.ac_enaddr, ":"); + + sc->pcn_unit = unit; + callout_handle_init(&sc->pcn_stat_ch); + + sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->pcn_ldata == NULL) { + printf("pcn%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "pcn"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = pcn_ioctl; + ifp->if_output = ether_output; + ifp->if_start = pcn_start; + ifp->if_watchdog = pcn_watchdog; + ifp->if_init = pcn_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + bootverbose = 1; + if (mii_phy_probe(dev, &sc->pcn_miibus, + pcn_ifmedia_upd, pcn_ifmedia_sts)) { + printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + callout_handle_init(&sc->pcn_stat_ch); + +fail: + splx(s); + return(error); +} + +static int pcn_detach(dev) + device_t dev; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + int s; + + s = splimp(); + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + pcn_reset(sc); + pcn_stop(sc); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + + if (sc->pcn_miibus != NULL) { + bus_generic_detach(dev); + device_delete_child(dev, sc->pcn_miibus); + } + + bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); + bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); + + contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); + + splx(s); + + 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_list_data *ld; + struct pcn_ring_data *cd; + int i; + + ld = sc->pcn_ldata; + 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) { + printf("pcn%d: no memory for rx list " + "-- packet dropped!\n", sc->pcn_unit); + return(ENOBUFS); + } + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("pcn%d: no memory for rx list " + "-- packet dropped!\n", sc->pcn_unit); + 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 ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct pcn_rx_desc *cur_rx; + int i; + + ifp = &sc->arpcom.ac_if; + 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; + } + + pcn_newbuf(sc, i, NULL); + PCN_INC(i, PCN_RX_LIST_CNT); + + /* No errors; receive the packet. */ + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + m->m_len = m->m_pkthdr.len = + cur_rx->pcn_rxlen - ETHER_CRC_LEN; + m->m_pkthdr.rcvif = ifp; + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + 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->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * 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); + ifp->if_timer = 0; + } + + sc->pcn_cdata.pcn_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void pcn_tick(xsc) + void *xsc; +{ + struct pcn_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + int s; + + s = splimp(); + + sc = xsc; + ifp = &sc->arpcom.ac_if; + + mii = device_get_softc(sc->pcn_miibus); + mii_tick(mii); + + if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) + sc->pcn_link = 0; + + if (!sc->pcn_link) { + mii_pollstat(mii); + if (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(ifp); + } + + sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); + + splx(s); + + return; +} + +static void pcn_intr(arg) + void *arg; +{ + struct pcn_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + /* Supress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + pcn_stop(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(sc); + break; + } + } + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + 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) { + 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; + struct mbuf *m_head = NULL; + u_int32_t idx; + + sc = ifp->if_softc; + + if (!sc->pcn_link) + return; + + idx = sc->pcn_cdata.pcn_tx_prod; + + if (ifp->if_flags & IFF_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_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + 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_init(xsc) + void *xsc; +{ + struct pcn_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii = NULL; + int s; + + s = splimp(); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + pcn_stop(sc); + pcn_reset(sc); + + mii = device_get_softc(sc->pcn_miibus); + + /* Set MAC address */ + pcn_csr_write(sc, PCN_CSR_PAR0, + ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); + pcn_csr_write(sc, PCN_CSR_PAR1, + ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); + pcn_csr_write(sc, PCN_CSR_PAR2, + ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); + + /* Init circular RX list. */ + if (pcn_list_rx_init(sc) == ENOBUFS) { + printf("pcn%d: initialization failed: no " + "memory for rx buffers\n", sc->pcn_unit); + pcn_stop(sc); + (void)splx(s); + return; + } + + /* + * Init tx descriptors. + */ + pcn_list_tx_init(sc); + + /* Set up the mode register. */ + pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); + + /* 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); + } + + /* + * 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 performances. + */ + 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_CLRBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); + + if (sc->pcn_type == Am79C978) + 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_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + (void)splx(s); + sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); + + return; +} + +/* + * Set media options. + */ +static int pcn_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->pcn_miibus); + + sc->pcn_link = 0; + if (mii->mii_instance) { + struct mii_softc *miisc; + for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; + miisc = LIST_NEXT(miisc, mii_list)) + mii_phy_reset(miisc); + } + mii_mediachg(mii); + + 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); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + 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 s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->pcn_if_flags & IFF_PROMISC)) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + PCN_CSR_SETBIT(sc, PCN_CSR_MODE, + PCN_MODE_PROMISC); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->pcn_if_flags & IFF_PROMISC) { + PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, + PCN_MODE_PROMISC); + PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, + PCN_EXTCTL1_SPND); + } else if (!(ifp->if_flags & IFF_RUNNING)) + pcn_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + pcn_stop(sc); + } + sc->pcn_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + pcn_setmulti(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 = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +static void pcn_watchdog(ifp) + struct ifnet *ifp; +{ + struct pcn_softc *sc; + + sc = ifp->if_softc; + + ifp->if_oerrors++; + printf("pcn%d: watchdog timeout\n", sc->pcn_unit); + + pcn_stop(sc); + pcn_reset(sc); + pcn_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + pcn_start(ifp); + + 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; + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(pcn_tick, sc, sc->pcn_stat_ch); + 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_flags &= ~(IFF_RUNNING | IFF_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_reset(sc); + pcn_stop(sc); + + return; +} diff --git a/sys/pci/if_pcnreg.h b/sys/pci/if_pcnreg.h new file mode 100644 index 0000000..7074035 --- /dev/null +++ b/sys/pci/if_pcnreg.h @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2000 Berkeley Software Design, Inc. + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul <wpaul@ee.columbia.edu>. 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 + +#define PCN_PORT_GPSI 0x0100 +#define PCN_PORT_MII 0x0180 + +/* + * Chip ID values. + */ +/* Chip types */ +#define LANCE 1 /* Am7990 */ +#define C_LANCE 2 /* Am79C90 */ +#define PCnet_ISA 3 /* Am79C960 */ +#define PCnet_ISAplus 4 /* Am79C961 */ +#define PCnet_ISA_II 5 /* Am79C961A */ +#define PCnet_32 6 /* Am79C965 */ +#define PCnet_PCI 7 /* Am79C970 */ +#define PCnet_PCI_II 8 /* Am79C970A */ +#define PCnet_FAST 9 /* Am79C971 */ +#define PCnet_FASTplus 10 /* Am79C972 */ +#define PCnet_Home 11 /* Am79C978 */ + +/* CSR88-89: Chip ID masks */ +#define AMD_MASK 0x003 +#define PART_MASK 0xffff +#define Am79C960 0x0003 +#define Am79C961 0x2260 +#define Am79C961A 0x2261 +#define Am79C965 0x2430 +#define Am79C970 0x0242 +#define Am79C970A 0x2621 +#define Am79C971 0x2623 +#define Am79C972 0x2624 +#define Am79C973 0x2625 +#define Am79C978 0x2626 + +/* + * Advanced feature control (CSR122) + */ +#define PCN_AFC_RXALIGN 0x0001 + +/* + * BCR (bus control) registers + */ +#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_PCISUNSYSID 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 + +/* + * Full duplex control (BCR9) + */ +#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */ +#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_PHYADD 0x03E0 + +/* + * 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; + char *pcn_name; +}; + +struct pcn_softc { + struct arpcom arpcom; /* interface info */ + 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_unit; + u_int8_t pcn_link; + int pcn_if_flags; + int pcn_type; + struct pcn_list_data *pcn_ldata; + struct pcn_ring_data pcn_cdata; + struct callout_handle pcn_stat_ch; +}; + +/* + * 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 0x0F +#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 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif |