diff options
-rw-r--r-- | sys/arch/alpha/conf/GENERIC | 5 | ||||
-rw-r--r-- | sys/arch/alpha/conf/RAMDISKBIG | 5 | ||||
-rw-r--r-- | sys/arch/amd64/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/arch/amd64/conf/RAMDISK | 4 | ||||
-rw-r--r-- | sys/arch/amd64/conf/RAMDISK_CD | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/RAMDISK | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/RAMDISKB | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/RAMDISKC | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/RAMDISK_CD | 4 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/ic/smc83c170.c | 1608 | ||||
-rw-r--r-- | sys/dev/ic/smc83c170reg.h | 487 | ||||
-rw-r--r-- | sys/dev/ic/smc83c170var.h | 191 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 9 | ||||
-rw-r--r-- | sys/dev/pci/if_epic_pci.c | 277 |
16 files changed, 2592 insertions, 28 deletions
diff --git a/sys/arch/alpha/conf/GENERIC b/sys/arch/alpha/conf/GENERIC index 7f1e849e336..a106e93bf70 100644 --- a/sys/arch/alpha/conf/GENERIC +++ b/sys/arch/alpha/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.146 2005/05/01 17:19:33 david Exp $ +# $OpenBSD: GENERIC,v 1.147 2005/05/10 01:16:31 brad Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -220,8 +220,7 @@ skc* at pci? # SysKonnect GEnesis 984x sk* at skc? # each port of above an* at pci? # Cisco/Aironet 802.11DS -#BROKEN: The following driver has 64-bit issues -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet vga* at pci? # PCI VGA graphics tga* at pci? # DEC ZLXp-E[123] graphics diff --git a/sys/arch/alpha/conf/RAMDISKBIG b/sys/arch/alpha/conf/RAMDISKBIG index 452c985183f..355affc2046 100644 --- a/sys/arch/alpha/conf/RAMDISKBIG +++ b/sys/arch/alpha/conf/RAMDISKBIG @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISKBIG,v 1.52 2005/05/01 07:54:41 david Exp $ +# $OpenBSD: RAMDISKBIG,v 1.53 2005/05/10 01:16:31 brad Exp $ # $NetBSD: GENERIC,v 1.31 1996/12/03 17:25:29 cgd Exp $ # # Generic Alpha kernel. Enough to get booted, etc., but not much more. @@ -201,8 +201,7 @@ skc* at pci? # SysKonnect GEnesis 984x sk* at skc? # each port of above an* at pci? # Cisco/Aironet 802.11DS -#BROKEN: The following driver has 64-bit issues -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet vga* at pci? # PCI VGA graphics tga* at pci? # DEC ZLXp-E[123] graphics diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 29b50f76550..55f3119b1a7 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.63 2005/05/01 07:54:42 david Exp $ +# $OpenBSD: GENERIC,v 1.64 2005/05/10 01:16:31 brad Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -261,7 +261,7 @@ xl* at pci? # 3C9xx ethernet rl* at pci? # RealTek 81[23]9 ethernet #rl* at cardbus? # RealTek 81[23]9 ethernet #mtd* at pci? # Myson MTD800/803/891 -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet #tl* at pci? # Compaq Thunderlan ethernet vr* at pci? # VIA Rhine ethernet #wb* at pci? # Winbond W89C840F ethernet diff --git a/sys/arch/amd64/conf/RAMDISK b/sys/arch/amd64/conf/RAMDISK index def55722ad5..433d3f174ce 100644 --- a/sys/arch/amd64/conf/RAMDISK +++ b/sys/arch/amd64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.19 2005/01/06 21:32:56 deraadt Exp $ +# $OpenBSD: RAMDISK,v 1.20 2005/05/10 01:16:31 brad Exp $ machine amd64 # architecture, used by config; REQUIRED @@ -200,7 +200,7 @@ fxp* at pci? # EtherExpress 10/100B ethernet #xl* at cardbus? # 3C575 ethernet rl* at pci? # RealTek 81[23]9 ethernet re* at pci? # Realtek 8169/8169S/8110S -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet #tl* at pci? # Compaq Thunderlan ethernet vr* at pci? # VIA Rhine ethernet dc* at pci? # 21143, "tulip" clone ethernet diff --git a/sys/arch/amd64/conf/RAMDISK_CD b/sys/arch/amd64/conf/RAMDISK_CD index baa2642850b..2a934581a84 100644 --- a/sys/arch/amd64/conf/RAMDISK_CD +++ b/sys/arch/amd64/conf/RAMDISK_CD @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK_CD,v 1.29 2005/03/30 07:42:13 deraadt Exp $ +# $OpenBSD: RAMDISK_CD,v 1.30 2005/05/10 01:16:31 brad Exp $ machine amd64 # architecture, used by config; REQUIRED @@ -216,7 +216,7 @@ fxp* at pci? # EtherExpress 10/100B ethernet #xl* at pci? # 3C9xx ethernet #xl* at cardbus? # 3C575 ethernet rl* at pci? # RealTek 81[23]9 ethernet -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet #tl* at pci? # Compaq Thunderlan ethernet vr* at pci? # VIA Rhine ethernet dc* at pci? # 21143, "tulip" clone ethernet diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index b1002422948..eef8d87d908 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.411 2005/05/02 17:26:00 grange Exp $ +# $OpenBSD: GENERIC,v 1.412 2005/05/10 01:16:31 brad Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -396,7 +396,7 @@ xl* at cardbus? # 3C575/3C656 ethernet rl* at pci? # RealTek 81[23]9 ethernet rl* at cardbus? # RealTek 81[23]9 ethernet mtd* at pci? # Myson MTD800/803/891 -tx* at pci? # SMC 83C170 EPIC ethernet +epic* at pci? # SMC EPIC/100 ethernet tl* at pci? # Compaq Thunderlan ethernet vr* at pci? # VIA Rhine ethernet wb* at pci? # Winbond W89C840F ethernet diff --git a/sys/arch/i386/conf/RAMDISK b/sys/arch/i386/conf/RAMDISK index 2e1b06cb9b1..eae4c6fbabf 100644 --- a/sys/arch/i386/conf/RAMDISK +++ b/sys/arch/i386/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.137 2005/03/18 19:24:27 deraadt Exp $ +# $OpenBSD: RAMDISK,v 1.138 2005/05/10 01:16:31 brad Exp $ machine i386 # architecture, used by config; REQUIRED @@ -224,7 +224,7 @@ ep* at pcmcia? # PCMCIA based 3c5xx cards #fpa* at pci? # DEC DEFPA FDDI cards xl* at pci? # 3C9xx ethernet cards rl* at pci? # RealTek 81[23]9 ethernet -tx* at pci? # SMC 83C170 EPIC ethernet cards +epic* at pci? # SMC EPIC/100 ethernet tl* at pci? # Compaq Thunderlan ethernet cards vr* at pci? # VIA Rhine ethernet #wb* at pci? # Winbond W89C840F ethernet diff --git a/sys/arch/i386/conf/RAMDISKB b/sys/arch/i386/conf/RAMDISKB index dc7f47eac47..55de72f29b8 100644 --- a/sys/arch/i386/conf/RAMDISKB +++ b/sys/arch/i386/conf/RAMDISKB @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISKB,v 1.80 2005/03/18 22:39:59 deraadt Exp $ +# $OpenBSD: RAMDISKB,v 1.81 2005/05/10 01:16:31 brad Exp $ machine i386 # architecture, used by config; REQUIRED @@ -247,7 +247,7 @@ ep* at pci? # 3C59x ethernet cards #fpa* at pci? # DEC DEFPA FDDI cards xl* at pci? # 3C9xx ethernet cards #rl* at pci? # RealTek 81[23]9 ethernet -#tx* at pci? # SMC 83C170 EPIC ethernet cards +#epic* at pci? # SMC EPIC/100 ethernet #tl* at pci? # Compaq Thunderlan ethernet cards vr* at pci? # VIA Rhine ethernet #wb* at pci? # Winbond W89C840F ethernet diff --git a/sys/arch/i386/conf/RAMDISKC b/sys/arch/i386/conf/RAMDISKC index 830e42aff08..e59c54c322b 100644 --- a/sys/arch/i386/conf/RAMDISKC +++ b/sys/arch/i386/conf/RAMDISKC @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISKC,v 1.56 2005/05/01 07:54:42 david Exp $ +# $OpenBSD: RAMDISKC,v 1.57 2005/05/10 01:16:31 brad Exp $ machine i386 # architecture, used by config; REQUIRED @@ -263,7 +263,7 @@ xl* at pci? # 3C9xx ethernet xl* at cardbus? # 3C575 ethernet rl* at pci? # RealTek 81[23]9 ethernet rl* at cardbus? # RealTek 81[23]9 ethernet -#tx* at pci? # SMC 83C170 EPIC ethernet +#epic* at pci? # SMC EPIC/100 ethernet #tl* at pci? # Compaq Thunderlan ethernet #vr* at pci? # VIA Rhine ethernet #wb* at pci? # Winbond W89C840F ethernet diff --git a/sys/arch/i386/conf/RAMDISK_CD b/sys/arch/i386/conf/RAMDISK_CD index 2e51d055305..b9cec934d89 100644 --- a/sys/arch/i386/conf/RAMDISK_CD +++ b/sys/arch/i386/conf/RAMDISK_CD @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK_CD,v 1.94 2005/04/14 20:21:07 brad Exp $ +# $OpenBSD: RAMDISK_CD,v 1.95 2005/05/10 01:16:31 brad Exp $ machine i386 # architecture, used by config; REQUIRED @@ -308,7 +308,7 @@ fpa* at pci? # DEC DEFPA FDDI xl* at pci? # 3C9xx ethernet xl* at cardbus? # 3C575 ethernet rl* at pci? # RealTek 81[23]9 ethernet -tx* at pci? # SMC 83C170 EPIC ethernet +epic* at pci? # SMC EPIC/100 ethernet tl* at pci? # Compaq Thunderlan ethernet vr* at pci? # VIA Rhine ethernet dc* at pci? # 21143, "tulip" clone ethernet diff --git a/sys/conf/files b/sys/conf/files index 4fc7ebff899..1f91d849951 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.335 2005/05/01 21:36:57 brad Exp $ +# $OpenBSD: files,v 1.336 2005/05/10 01:16:31 brad Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -258,6 +258,10 @@ file dev/ic/dc.c dc device sm: ether, ifnet, ifmedia file dev/ic/smc91cxx.c sm +# SMC 83C170 EPIC/100 Fast Ethernet Controller +device epic: ether, ifnet, ifmedia, mii +file dev/ic/smc83c170.c epic + # Novell NE2000-compatible Ethernet cards, based on the # National Semiconductor DS8390. device ne: ether, ifnet, dp8390nic, ifmedia, mii diff --git a/sys/dev/ic/smc83c170.c b/sys/dev/ic/smc83c170.c new file mode 100644 index 00000000000..3fcfdc5cd2a --- /dev/null +++ b/sys/dev/ic/smc83c170.c @@ -0,0 +1,1608 @@ +/* $OpenBSD: smc83c170.c,v 1.1 2005/05/10 01:16:32 brad Exp $ */ +/* $NetBSD: smc83c170.c,v 1.59 2005/02/27 00:27:02 perry Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * Device driver for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100). + */ + +#if 0 +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: smc83c170.c,v 1.59 2005/02/27 00:27:02 perry Exp $"); +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +/* #include <uvm/uvm_extern.h> */ + +#include <net/if.h> +#include <net/if_dl.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/mii/miivar.h> +#include <dev/mii/lxtphyreg.h> + +#include <dev/ic/smc83c170reg.h> +#include <dev/ic/smc83c170var.h> + +void epic_start(struct ifnet *); +void epic_watchdog(struct ifnet *); +int epic_ioctl(struct ifnet *, u_long, caddr_t); +int epic_init(struct ifnet *); +void epic_stop(struct ifnet *, int); + +void epic_shutdown(void *); + +void epic_reset(struct epic_softc *); +void epic_rxdrain(struct epic_softc *); +int epic_add_rxbuf(struct epic_softc *, int); +void epic_read_eeprom(struct epic_softc *, int, int, u_int16_t *); +void epic_set_mchash(struct epic_softc *); +void epic_fixup_clock_source(struct epic_softc *); +int epic_mii_read(struct device *, int, int); +void epic_mii_write(struct device *, int, int, int); +int epic_mii_wait(struct epic_softc *, u_int32_t); +void epic_tick(void *); + +void epic_statchg(struct device *); +int epic_mediachange(struct ifnet *); +void epic_mediastatus(struct ifnet *, struct ifmediareq *); + +struct cfdriver epic_cd = { + 0, "epic", DV_IFNET +}; + +#define INTMASK (INTSTAT_FATAL_INT | INTSTAT_TXU | \ + INTSTAT_TXC | INTSTAT_RXE | INTSTAT_RQE | INTSTAT_RCC) + +int epic_copy_small = 0; + +#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +/* + * Attach an EPIC interface to the system. + */ +void +epic_attach(sc, intrstr) + struct epic_softc *sc; + const char *intrstr; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int rseg, error, miiflags; + u_int i; + bus_dma_segment_t seg; + u_int8_t enaddr[ETHER_ADDR_LEN], devname[12 + 1]; + u_int16_t myea[ETHER_ADDR_LEN / 2], mydevname[6]; + char *nullbuf; + + timeout_set(&sc->sc_mii_timeout, epic_tick, sc); + + /* + * Allocate the control data structures, and create and load the + * DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct epic_control_data) + ETHER_PAD_LEN, PAGE_SIZE, 0, + &seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { + printf( + "%s: unable to allocate control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_0; + } + + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + sizeof(struct epic_control_data) + ETHER_PAD_LEN, + (caddr_t *)&sc->sc_control_data, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_1; + } + nullbuf = + (char *)sc->sc_control_data + sizeof(struct epic_control_data); + memset(nullbuf, 0, ETHER_PAD_LEN); + + if ((error = bus_dmamap_create(sc->sc_dmat, + sizeof(struct epic_control_data), 1, + sizeof(struct epic_control_data), 0, BUS_DMA_NOWAIT, + &sc->sc_cddmamap)) != 0) { + printf("%s: unable to create control data DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail_2; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap, + sc->sc_control_data, sizeof(struct epic_control_data), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf( + "%s: unable to load control data DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_3; + } + + /* + * Create the transmit buffer DMA maps. + */ + for (i = 0; i < EPIC_NTXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + EPIC_NFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT, + &EPIC_DSTX(sc, i)->ds_dmamap)) != 0) { + printf("%s: unable to create tx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail_4; + } + } + + /* + * Create the receive buffer DMA maps. + */ + for (i = 0; i < EPIC_NRXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, + MCLBYTES, 0, BUS_DMA_NOWAIT, + &EPIC_DSRX(sc, i)->ds_dmamap)) != 0) { + printf("%s: unable to create rx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail_5; + } + EPIC_DSRX(sc, i)->ds_mbuf = NULL; + } + + /* + * create and map the pad buffer + */ + if ((error = bus_dmamap_create(sc->sc_dmat, ETHER_PAD_LEN, 1, + ETHER_PAD_LEN, 0, BUS_DMA_NOWAIT,&sc->sc_nulldmamap)) != 0) { + printf("%s: unable to create pad buffer DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail_5; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_nulldmamap, + nullbuf, ETHER_PAD_LEN, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load pad buffer DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail_6; + } + bus_dmamap_sync(sc->sc_dmat, sc->sc_nulldmamap, 0, ETHER_PAD_LEN, + BUS_DMASYNC_PREWRITE); + + /* + * Bring the chip out of low-power mode and reset it to a known state. + */ + bus_space_write_4(st, sh, EPIC_GENCTL, 0); + epic_reset(sc); + + /* + * Read the Ethernet address from the EEPROM. + */ + epic_read_eeprom(sc, 0, (sizeof(myea) / sizeof(myea[0])), myea); + for (i = 0; i < sizeof(myea)/ sizeof(myea[0]); i++) { + enaddr[i * 2] = myea[i] & 0xff; + enaddr[i * 2 + 1] = myea[i] >> 8; + } + + /* + * ...and the device name. + */ + epic_read_eeprom(sc, 0x2c, (sizeof(mydevname) / sizeof(mydevname[0])), + mydevname); + for (i = 0; i < sizeof(mydevname) / sizeof(mydevname[0]); i++) { + devname[i * 2] = mydevname[i] & 0xff; + devname[i * 2 + 1] = mydevname[i] >> 8; + } + + devname[sizeof(mydevname)] = '\0'; + for (i = sizeof(mydevname) - 1; i >= 0; i--) { + if (devname[i] == ' ') + devname[i] = '\0'; + else + break; + } + + printf(": %s, address %s\n", intrstr, ether_sprintf(enaddr)); + + miiflags = 0; + if (sc->sc_hwflags & EPIC_HAS_MII_FIBER) + miiflags |= MIIF_HAVEFIBER; + + /* + * Initialize our media structures and probe the MII. + */ + sc->sc_mii.mii_ifp = ifp; + sc->sc_mii.mii_readreg = epic_mii_read; + sc->sc_mii.mii_writereg = epic_mii_write; + sc->sc_mii.mii_statchg = epic_statchg; + ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, epic_mediachange, + epic_mediastatus); + mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, miiflags); + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); + } else + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); + + if (sc->sc_hwflags & EPIC_HAS_BNC) { + /* use the next free media instance */ + sc->sc_serinst = sc->sc_mii.mii_instance++; + ifmedia_add(&sc->sc_mii.mii_media, + IFM_MAKEWORD(IFM_ETHER, IFM_10_2, 0, + sc->sc_serinst), + 0, NULL); + } else + sc->sc_serinst = -1; + + bcopy(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = epic_ioctl; + ifp->if_start = epic_start; + ifp->if_watchdog = epic_watchdog; + IFQ_SET_MAXLEN(&ifp->if_snd, EPIC_NTXDESC - 1); + IFQ_SET_READY(&ifp->if_snd); + + ifp->if_capabilities = IFCAP_VLAN_MTU; + + /* + * Attach the interface. + */ + if_attach(ifp); + ether_ifattach(ifp); + + /* + * Make sure the interface is shutdown during reboot. + */ + sc->sc_sdhook = shutdownhook_establish(epic_shutdown, sc); + if (sc->sc_sdhook == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + sc->sc_dev.dv_xname); + return; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_6: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_nulldmamap); + fail_5: + for (i = 0; i < EPIC_NRXDESC; i++) { + if (EPIC_DSRX(sc, i)->ds_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmat, + EPIC_DSRX(sc, i)->ds_dmamap); + } + fail_4: + for (i = 0; i < EPIC_NTXDESC; i++) { + if (EPIC_DSTX(sc, i)->ds_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmat, + EPIC_DSTX(sc, i)->ds_dmamap); + } + bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap); + fail_3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap); + fail_2: + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_control_data, + sizeof(struct epic_control_data)); + fail_1: + bus_dmamem_free(sc->sc_dmat, &seg, rseg); + fail_0: + return; +} + +/* + * Shutdown hook. Make sure the interface is stopped at reboot. + */ +void +epic_shutdown(arg) + void *arg; +{ + struct epic_softc *sc = arg; + + epic_stop(&sc->sc_arpcom.ac_if, 1); +} + +/* + * Start packet transmission on the interface. + * [ifnet interface function] + */ +void +epic_start(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + struct mbuf *m0, *m; + struct epic_txdesc *txd; + struct epic_descsoft *ds; + struct epic_fraglist *fr; + bus_dmamap_t dmamap; + int error, firsttx, nexttx, opending, seg; + u_int len; + + /* + * Remember the previous txpending and the first transmit + * descriptor we use. + */ + opending = sc->sc_txpending; + firsttx = EPIC_NEXTTX(sc->sc_txlast); + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->sc_txpending < EPIC_NTXDESC) { + /* + * Grab a packet off the queue. + */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + m = NULL; + + /* + * Get the last and next available transmit descriptor. + */ + nexttx = EPIC_NEXTTX(sc->sc_txlast); + txd = EPIC_CDTX(sc, nexttx); + fr = EPIC_CDFL(sc, nexttx); + ds = EPIC_DSTX(sc, nexttx); + dmamap = ds->ds_dmamap; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the alloted number of frags, or we were + * short on resources. In this case, we'll copy and try + * again. + */ + if ((error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_WRITE|BUS_DMA_NOWAIT)) != 0 || + (m0->m_pkthdr.len < ETHER_PAD_LEN && + dmamap-> dm_nsegs == EPIC_NFRAGS)) { + if (error == 0) + bus_dmamap_unload(sc->sc_dmat, dmamap); + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + sc->sc_dev.dv_xname); + break; + } + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: unable to allocate Tx " + "cluster\n", sc->sc_dev.dv_xname); + m_freem(m); + break; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len; + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, + m, BUS_DMA_WRITE|BUS_DMA_NOWAIT); + if (error) { + printf("%s: unable to load Tx buffer, " + "error = %d\n", sc->sc_dev.dv_xname, error); + break; + } + } + IFQ_DEQUEUE(&ifp->if_snd, m0); + if (m != NULL) { + m_freem(m0); + m0 = m; + } + + /* Initialize the fraglist. */ + for (seg = 0; seg < dmamap->dm_nsegs; seg++) { + fr->ef_frags[seg].ef_addr = + dmamap->dm_segs[seg].ds_addr; + fr->ef_frags[seg].ef_length = + dmamap->dm_segs[seg].ds_len; + } + len = m0->m_pkthdr.len; + if (len < ETHER_PAD_LEN) { + fr->ef_frags[seg].ef_addr = sc->sc_nulldma; + fr->ef_frags[seg].ef_length = ETHER_PAD_LEN - len; + len = ETHER_PAD_LEN; + seg++; + } + fr->ef_nfrags = seg; + + EPIC_CDFLSYNC(sc, nexttx, BUS_DMASYNC_PREWRITE); + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Store a pointer to the packet so we can free it later. + */ + ds->ds_mbuf = m0; + + /* + * Fill in the transmit descriptor. + */ + txd->et_control = ET_TXCTL_LASTDESC | ET_TXCTL_FRAGLIST; + + /* + * If this is the first descriptor we're enqueueing, + * don't give it to the EPIC yet. That could cause + * a race condition. We'll do it below. + */ + if (nexttx == firsttx) + txd->et_txstatus = TXSTAT_TXLENGTH(len); + else + txd->et_txstatus = + TXSTAT_TXLENGTH(len) | ET_TXSTAT_OWNER; + + EPIC_CDTXSYNC(sc, nexttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Advance the tx pointer. */ + sc->sc_txpending++; + sc->sc_txlast = nexttx; + +#if NBPFILTER > 0 + /* + * Pass the packet to any BPF listeners. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + } + + if (sc->sc_txpending == EPIC_NTXDESC) { + /* No more slots left; notify upper layer. */ + ifp->if_flags |= IFF_OACTIVE; + } + + if (sc->sc_txpending != opending) { + /* + * We enqueued packets. If the transmitter was idle, + * reset the txdirty pointer. + */ + if (opending == 0) + sc->sc_txdirty = firsttx; + + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued. + */ + EPIC_CDTX(sc, sc->sc_txlast)->et_control |= ET_TXCTL_IAF; + EPIC_CDTXSYNC(sc, sc->sc_txlast, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* + * The entire packet chain is set up. Give the + * first descriptor to the EPIC now. + */ + EPIC_CDTX(sc, firsttx)->et_txstatus |= ET_TXSTAT_OWNER; + EPIC_CDTXSYNC(sc, firsttx, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Start the transmitter. */ + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_COMMAND, + COMMAND_TXQUEUED); + + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +/* + * Watchdog timer handler. + * [ifnet interface function] + */ +void +epic_watchdog(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + ifp->if_oerrors++; + + (void) epic_init(ifp); +} + +/* + * Handle control requests from the operator. + * [ifnet interface function] + */ +int +epic_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct epic_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error; + + s = splnet(); + + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + splx(s); + return (error); + } + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + epic_init(ifp); + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif + default: + epic_init(ifp); + break; + } + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) { + error = EINVAL; + } else if (ifp->if_mtu != ifr->ifr_mtu) { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked up and not running, then start it. + * If it is marked down and running, stop it. + * XXX If it's up then re-initialize it. This is so flags + * such as IFF_PROMISC are handled. + */ + if (ifp->if_flags & IFF_UP) + epic_init(ifp); + else if (ifp->if_flags & IFF_RUNNING) + epic_stop(ifp, 1); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom) : + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. + */ + if (ifp->if_flags & IFF_RUNNING) + mii_pollstat(&sc->sc_mii); + epic_set_mchash(sc); + error = 0; + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +/* + * Interrupt handler. + */ +int +epic_intr(arg) + void *arg; +{ + struct epic_softc *sc = arg; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct epic_rxdesc *rxd; + struct epic_txdesc *txd; + struct epic_descsoft *ds; + struct mbuf *m; + u_int32_t intstat, rxstatus, txstatus; + int i, claimed = 0; + u_int len; + + top: + /* + * Get the interrupt status from the EPIC. + */ + intstat = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_INTSTAT); + if ((intstat & INTSTAT_INT_ACTV) == 0) + return (claimed); + + claimed = 1; + + /* + * Acknowledge the interrupt. + */ + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_INTSTAT, + intstat & INTMASK); + + /* + * Check for receive interrupts. + */ + if (intstat & (INTSTAT_RCC | INTSTAT_RXE | INTSTAT_RQE)) { + for (i = sc->sc_rxptr;; i = EPIC_NEXTRX(i)) { + rxd = EPIC_CDRX(sc, i); + ds = EPIC_DSRX(sc, i); + + EPIC_CDRXSYNC(sc, i, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + rxstatus = rxd->er_rxstatus; + if (rxstatus & ER_RXSTAT_OWNER) { + /* + * We have processed all of the + * receive buffers. + */ + break; + } + + /* + * Make sure the packet arrived intact. If an error + * occurred, update stats and reset the descriptor. + * The buffer will be reused the next time the + * descriptor comes up in the ring. + */ + if ((rxstatus & ER_RXSTAT_PKTINTACT) == 0) { + if (rxstatus & ER_RXSTAT_CRCERROR) + printf("%s: CRC error\n", + sc->sc_dev.dv_xname); + if (rxstatus & ER_RXSTAT_ALIGNERROR) + printf("%s: alignment error\n", + sc->sc_dev.dv_xname); + ifp->if_ierrors++; + EPIC_INIT_RXDESC(sc, i); + continue; + } + + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + /* + * The EPIC includes the CRC with every packet; + * trim it. + */ + len = RXSTAT_RXLENGTH(rxstatus) - ETHER_CRC_LEN; + + if (len < sizeof(struct ether_header)) { + /* + * Runt packet; drop it now. + */ + ifp->if_ierrors++; + EPIC_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + continue; + } + + /* + * If the packet is small enough to fit in a + * single header mbuf, allocate one and copy + * the data into it. This greatly reduces + * memory consumption when we receive lots + * of small packets. + * + * Otherwise, we add a new buffer to the receive + * chain. If this fails, we drop the packet and + * recycle the old buffer. + */ + if (epic_copy_small != 0 && len <= MHLEN) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto dropit; + memcpy(mtod(m, caddr_t), + mtod(ds->ds_mbuf, caddr_t), len); + EPIC_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + } else { + m = ds->ds_mbuf; + if (epic_add_rxbuf(sc, i) != 0) { + dropit: + ifp->if_ierrors++; + EPIC_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->sc_dmat, + ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + continue; + } + } + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + +#if NBPFILTER > 0 + /* + * Pass this up to any BPF listeners, but only + * pass it up the stack if its for us. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + /* Pass it on. */ + ether_input_mbuf(ifp, m); + ifp->if_ipackets++; + } + + /* Update the receive pointer. */ + sc->sc_rxptr = i; + + /* + * Check for receive queue underflow. + */ + if (intstat & INTSTAT_RQE) { + printf("%s: receiver queue empty\n", + sc->sc_dev.dv_xname); + /* + * Ring is already built; just restart the + * receiver. + */ + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_PRCDAR, + EPIC_CDRXADDR(sc, sc->sc_rxptr)); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_COMMAND, + COMMAND_RXQUEUED | COMMAND_START_RX); + } + } + + /* + * Check for transmission complete interrupts. + */ + if (intstat & (INTSTAT_TXC | INTSTAT_TXU)) { + ifp->if_flags &= ~IFF_OACTIVE; + for (i = sc->sc_txdirty; sc->sc_txpending != 0; + i = EPIC_NEXTTX(i), sc->sc_txpending--) { + txd = EPIC_CDTX(sc, i); + ds = EPIC_DSTX(sc, i); + + EPIC_CDTXSYNC(sc, i, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + txstatus = txd->et_txstatus; + if (txstatus & ET_TXSTAT_OWNER) + break; + + EPIC_CDFLSYNC(sc, i, BUS_DMASYNC_POSTWRITE); + + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, + 0, ds->ds_dmamap->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + + /* + * Check for errors and collisions. + */ + if ((txstatus & ET_TXSTAT_PACKETTX) == 0) + ifp->if_oerrors++; + else + ifp->if_opackets++; + ifp->if_collisions += + TXSTAT_COLLISIONS(txstatus); + if (txstatus & ET_TXSTAT_CARSENSELOST) + printf("%s: lost carrier\n", + sc->sc_dev.dv_xname); + } + + /* Update the dirty transmit buffer pointer. */ + sc->sc_txdirty = i; + + /* + * Cancel the watchdog timer if there are no pending + * transmissions. + */ + if (sc->sc_txpending == 0) + ifp->if_timer = 0; + + /* + * Kick the transmitter after a DMA underrun. + */ + if (intstat & INTSTAT_TXU) { + printf("%s: transmit underrun\n", sc->sc_dev.dv_xname); + bus_space_write_4(sc->sc_st, sc->sc_sh, + EPIC_COMMAND, COMMAND_TXUGO); + if (sc->sc_txpending) + bus_space_write_4(sc->sc_st, sc->sc_sh, + EPIC_COMMAND, COMMAND_TXQUEUED); + } + + /* + * Try to get more packets going. + */ + epic_start(ifp); + } + + /* + * Check for fatal interrupts. + */ + if (intstat & INTSTAT_FATAL_INT) { + if (intstat & INTSTAT_PTA) + printf("%s: PCI target abort error\n", + sc->sc_dev.dv_xname); + else if (intstat & INTSTAT_PMA) + printf("%s: PCI master abort error\n", + sc->sc_dev.dv_xname); + else if (intstat & INTSTAT_APE) + printf("%s: PCI address parity error\n", + sc->sc_dev.dv_xname); + else if (intstat & INTSTAT_DPE) + printf("%s: PCI data parity error\n", + sc->sc_dev.dv_xname); + else + printf("%s: unknown fatal error\n", + sc->sc_dev.dv_xname); + (void) epic_init(ifp); + } + + /* + * Check for more interrupts. + */ + goto top; +} + +/* + * One second timer, used to tick the MII. + */ +void +epic_tick(arg) + void *arg; +{ + struct epic_softc *sc = arg; + int s; + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + timeout_add(&sc->sc_mii_timeout, hz); +} + +/* + * Fixup the clock source on the EPIC. + */ +void +epic_fixup_clock_source(sc) + struct epic_softc *sc; +{ + int i; + + /* + * According to SMC Application Note 7-15, the EPIC's clock + * source is incorrect following a reset. This manifests itself + * as failure to recognize when host software has written to + * a register on the EPIC. The appnote recommends issuing at + * least 16 consecutive writes to the CLOCK TEST bit to correctly + * configure the clock source. + */ + for (i = 0; i < 16; i++) + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_TEST, + TEST_CLOCKTEST); +} + +/* + * Perform a soft reset on the EPIC. + */ +void +epic_reset(sc) + struct epic_softc *sc; +{ + + epic_fixup_clock_source(sc); + + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_GENCTL, 0); + delay(100); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_GENCTL, GENCTL_SOFTRESET); + delay(100); + + epic_fixup_clock_source(sc); +} + +/* + * Initialize the interface. Must be called at splnet(). + */ +int +epic_init(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct epic_txdesc *txd; + struct epic_descsoft *ds; + u_int32_t genctl, reg0; + int i, error = 0; + + /* + * Cancel any pending I/O. + */ + epic_stop(ifp, 0); + + /* + * Reset the EPIC to a known state. + */ + epic_reset(sc); + + /* + * Magical mystery initialization. + */ + bus_space_write_4(st, sh, EPIC_TXTEST, 0); + + /* + * Initialize the EPIC genctl register: + * + * - 64 byte receive FIFO threshold + * - automatic advance to next receive frame + */ + genctl = GENCTL_RX_FIFO_THRESH0 | GENCTL_ONECOPY; +#if BYTE_ORDER == BIG_ENDIAN + genctl |= GENCTL_BIG_ENDIAN; +#endif + bus_space_write_4(st, sh, EPIC_GENCTL, genctl); + + /* + * Reset the MII bus and PHY. + */ + reg0 = bus_space_read_4(st, sh, EPIC_NVCTL); + bus_space_write_4(st, sh, EPIC_NVCTL, reg0 | NVCTL_GPIO1 | NVCTL_GPOE1); + bus_space_write_4(st, sh, EPIC_MIICFG, MIICFG_ENASER); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl | GENCTL_RESET_PHY); + delay(100); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl); + delay(1000); + bus_space_write_4(st, sh, EPIC_NVCTL, reg0); + + /* + * Initialize Ethernet address. + */ + reg0 = sc->sc_arpcom.ac_enaddr[1] << 8 | sc->sc_arpcom.ac_enaddr[0]; + bus_space_write_4(st, sh, EPIC_LAN0, reg0); + reg0 = sc->sc_arpcom.ac_enaddr[3] << 8 | sc->sc_arpcom.ac_enaddr[2]; + bus_space_write_4(st, sh, EPIC_LAN1, reg0); + reg0 = sc->sc_arpcom.ac_enaddr[5] << 8 | sc->sc_arpcom.ac_enaddr[4]; + bus_space_write_4(st, sh, EPIC_LAN2, reg0); + + /* + * Initialize receive control. Remember the external buffer + * size setting. + */ + reg0 = bus_space_read_4(st, sh, EPIC_RXCON) & + (RXCON_EXTBUFSIZESEL1 | RXCON_EXTBUFSIZESEL0); + reg0 |= (RXCON_RXMULTICAST | RXCON_RXBROADCAST); + if (ifp->if_flags & IFF_PROMISC) + reg0 |= RXCON_PROMISCMODE; + bus_space_write_4(st, sh, EPIC_RXCON, reg0); + + /* Set the current media. */ + epic_mediachange(ifp); + + /* Set up the multicast hash table. */ + epic_set_mchash(sc); + + /* + * Initialize the transmit descriptor ring. txlast is initialized + * to the end of the list so that it will wrap around to the first + * descriptor when the first packet is transmitted. + */ + for (i = 0; i < EPIC_NTXDESC; i++) { + txd = EPIC_CDTX(sc, i); + memset(txd, 0, sizeof(struct epic_txdesc)); + txd->et_bufaddr = EPIC_CDFLADDR(sc, i); + txd->et_nextdesc = EPIC_CDTXADDR(sc, EPIC_NEXTTX(i)); + EPIC_CDTXSYNC(sc, i, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + } + sc->sc_txpending = 0; + sc->sc_txdirty = 0; + sc->sc_txlast = EPIC_NTXDESC - 1; + + /* + * Initialize the receive descriptor ring. + */ + for (i = 0; i < EPIC_NRXDESC; i++) { + ds = EPIC_DSRX(sc, i); + if (ds->ds_mbuf == NULL) { + if ((error = epic_add_rxbuf(sc, i)) != 0) { + printf("%s: unable to allocate or map rx " + "buffer %d error = %d\n", + sc->sc_dev.dv_xname, i, error); + /* + * XXX Should attempt to run with fewer receive + * XXX buffers instead of just failing. + */ + epic_rxdrain(sc); + goto out; + } + } else + EPIC_INIT_RXDESC(sc, i); + } + sc->sc_rxptr = 0; + + /* + * Initialize the interrupt mask and enable interrupts. + */ + bus_space_write_4(st, sh, EPIC_INTMASK, INTMASK); + bus_space_write_4(st, sh, EPIC_GENCTL, genctl | GENCTL_INTENA); + + /* + * Give the transmit and receive rings to the EPIC. + */ + bus_space_write_4(st, sh, EPIC_PTCDAR, + EPIC_CDTXADDR(sc, EPIC_NEXTTX(sc->sc_txlast))); + bus_space_write_4(st, sh, EPIC_PRCDAR, + EPIC_CDRXADDR(sc, sc->sc_rxptr)); + + /* + * Set the EPIC in motion. + */ + bus_space_write_4(st, sh, EPIC_COMMAND, + COMMAND_RXQUEUED | COMMAND_START_RX); + + /* + * ...all done! + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Start the one second clock. + */ + timeout_add(&sc->sc_mii_timeout, hz); + + /* + * Attempt to start output on the interface. + */ + epic_start(ifp); + + out: + if (error) + printf("%s: interface not running\n", sc->sc_dev.dv_xname); + return (error); +} + +/* + * Drain the receive queue. + */ +void +epic_rxdrain(sc) + struct epic_softc *sc; +{ + struct epic_descsoft *ds; + int i; + + for (i = 0; i < EPIC_NRXDESC; i++) { + ds = EPIC_DSRX(sc, i); + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + } + } +} + +/* + * Stop transmission on the interface. + */ +void +epic_stop(ifp, disable) + struct ifnet *ifp; + int disable; +{ + struct epic_softc *sc = ifp->if_softc; + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + struct epic_descsoft *ds; + u_int32_t reg; + int i; + + /* + * Stop the one second clock. + */ + timeout_del(&sc->sc_mii_timeout); + + /* Down the MII. */ + mii_down(&sc->sc_mii); + + /* Paranoia... */ + epic_fixup_clock_source(sc); + + /* + * Disable interrupts. + */ + reg = bus_space_read_4(st, sh, EPIC_GENCTL); + bus_space_write_4(st, sh, EPIC_GENCTL, reg & ~GENCTL_INTENA); + bus_space_write_4(st, sh, EPIC_INTMASK, 0); + + /* + * Stop the DMA engine and take the receiver off-line. + */ + bus_space_write_4(st, sh, EPIC_COMMAND, COMMAND_STOP_RDMA | + COMMAND_STOP_TDMA | COMMAND_STOP_RX); + + /* + * Release any queued transmit buffers. + */ + for (i = 0; i < EPIC_NTXDESC; i++) { + ds = EPIC_DSTX(sc, i); + if (ds->ds_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + m_freem(ds->ds_mbuf); + ds->ds_mbuf = NULL; + } + } + + if (disable) + epic_rxdrain(sc); + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; +} + +/* + * Read the EPIC Serial EEPROM. + */ +void +epic_read_eeprom(sc, word, wordcnt, data) + struct epic_softc *sc; + int word, wordcnt; + u_int16_t *data; +{ + bus_space_tag_t st = sc->sc_st; + bus_space_handle_t sh = sc->sc_sh; + u_int16_t reg; + int i, x; + +#define EEPROM_WAIT_READY(st, sh) \ + while ((bus_space_read_4((st), (sh), EPIC_EECTL) & EECTL_EERDY) == 0) \ + /* nothing */ + + /* + * Enable the EEPROM. + */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE); + EEPROM_WAIT_READY(st, sh); + + for (i = 0; i < wordcnt; i++) { + /* Send CHIP SELECT for one clock tick. */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE|EECTL_EECS); + EEPROM_WAIT_READY(st, sh); + + /* Shift in the READ opcode. */ + for (x = 3; x > 0; x--) { + reg = EECTL_ENABLE|EECTL_EECS; + if (EPIC_EEPROM_OPC_READ & (1 << (x - 1))) + reg |= EECTL_EEDI; + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Shift in address. */ + for (x = 6; x > 0; x--) { + reg = EECTL_ENABLE|EECTL_EECS; + if ((word + i) & (1 << (x - 1))) + reg |= EECTL_EEDI; + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Shift out data. */ + reg = EECTL_ENABLE|EECTL_EECS; + data[i] = 0; + for (x = 16; x > 0; x--) { + bus_space_write_4(st, sh, EPIC_EECTL, reg|EECTL_EESK); + EEPROM_WAIT_READY(st, sh); + if (bus_space_read_4(st, sh, EPIC_EECTL) & EECTL_EEDO) + data[i] |= (1 << (x - 1)); + bus_space_write_4(st, sh, EPIC_EECTL, reg); + EEPROM_WAIT_READY(st, sh); + } + + /* Clear CHIP SELECT. */ + bus_space_write_4(st, sh, EPIC_EECTL, EECTL_ENABLE); + EEPROM_WAIT_READY(st, sh); + } + + /* + * Disable the EEPROM. + */ + bus_space_write_4(st, sh, EPIC_EECTL, 0); + +#undef EEPROM_WAIT_READY +} + +/* + * Add a receive buffer to the indicated descriptor. + */ +int +epic_add_rxbuf(sc, idx) + struct epic_softc *sc; + int idx; +{ + struct epic_descsoft *ds = EPIC_DSRX(sc, idx); + struct mbuf *m; + int error; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return (ENOBUFS); + } + + if (ds->ds_mbuf != NULL) + bus_dmamap_unload(sc->sc_dmat, ds->ds_dmamap); + + ds->ds_mbuf = m; + + error = bus_dmamap_load(sc->sc_dmat, ds->ds_dmamap, + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, + BUS_DMA_READ|BUS_DMA_NOWAIT); + if (error) { + printf("%s: can't load rx DMA map %d, error = %d\n", + sc->sc_dev.dv_xname, idx, error); + panic("epic_add_rxbuf"); /* XXX */ + } + + bus_dmamap_sync(sc->sc_dmat, ds->ds_dmamap, 0, + ds->ds_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); + + EPIC_INIT_RXDESC(sc, idx); + + return (0); +} + +/* + * Set the EPIC multicast hash table. + * + * NOTE: We rely on a recently-updated mii_media_active here! + */ +void +epic_set_mchash(sc) + struct epic_softc *sc; +{ + struct arpcom *ac = &sc->sc_arpcom; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + u_int32_t hash, mchash[4]; + + /* + * Set up the multicast address filter by passing all multicast + * addresses through a CRC generator, and then using the low-order + * 6 bits as an index into the 64 bit multicast hash table (only + * the lower 16 bits of each 32 bit multicast hash register are + * valid). The high order bits select the register, while the + * rest of the bits select the bit within the register. + */ + + if (ifp->if_flags & IFF_PROMISC) + goto allmulti; + + if (IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_10_T) { + /* XXX hardware bug in 10Mbps mode. */ + goto allmulti; + } + + mchash[0] = mchash[1] = mchash[2] = mchash[3] = 0; + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) + goto allmulti; + + hash = ether_crc32_be(enm->enm_addrlo, ETHER_ADDR_LEN); + hash >>= 26; + + /* Set the corresponding bit in the hash table. */ + mchash[hash >> 4] |= 1 << (hash & 0xf); + + ETHER_NEXT_MULTI(step, enm); + } + + ifp->if_flags &= ~IFF_ALLMULTI; + goto sethash; + + allmulti: + ifp->if_flags |= IFF_ALLMULTI; + mchash[0] = mchash[1] = mchash[2] = mchash[3] = 0xffff; + + sethash: + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC0, mchash[0]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC1, mchash[1]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC2, mchash[2]); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MC3, mchash[3]); +} + +/* + * Wait for the MII to become ready. + */ +int +epic_mii_wait(sc, rw) + struct epic_softc *sc; + u_int32_t rw; +{ + int i; + + for (i = 0; i < 50; i++) { + if ((bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_MMCTL) & rw) + == 0) + break; + delay(2); + } + if (i == 50) { + printf("%s: MII timed out\n", sc->sc_dev.dv_xname); + return (1); + } + + return (0); +} + +/* + * Read from the MII. + */ +int +epic_mii_read(self, phy, reg) + struct device *self; + int phy, reg; +{ + struct epic_softc *sc = (struct epic_softc *)self; + + if (epic_mii_wait(sc, MMCTL_WRITE)) + return (0); + + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MMCTL, + MMCTL_ARG(phy, reg, MMCTL_READ)); + + if (epic_mii_wait(sc, MMCTL_READ)) + return (0); + + return (bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_MMDATA) & + MMDATA_MASK); +} + +/* + * Write to the MII. + */ +void +epic_mii_write(self, phy, reg, val) + struct device *self; + int phy, reg, val; +{ + struct epic_softc *sc = (struct epic_softc *)self; + + if (epic_mii_wait(sc, MMCTL_WRITE)) + return; + + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MMDATA, val); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MMCTL, + MMCTL_ARG(phy, reg, MMCTL_WRITE)); +} + +/* + * Callback from PHY when media changes. + */ +void +epic_statchg(self) + struct device *self; +{ + struct epic_softc *sc = (struct epic_softc *)self; + u_int32_t txcon, miicfg; + + /* + * Update loopback bits in TXCON to reflect duplex mode. + */ + txcon = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_TXCON); + if (sc->sc_mii.mii_media_active & IFM_FDX) + txcon |= (TXCON_LOOPBACK_D1|TXCON_LOOPBACK_D2); + else + txcon &= ~(TXCON_LOOPBACK_D1|TXCON_LOOPBACK_D2); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_TXCON, txcon); + + /* On some cards we need manualy set fullduplex led */ + if (sc->sc_hwflags & EPIC_DUPLEXLED_ON_694) { + miicfg = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG); + if (IFM_OPTIONS(sc->sc_mii.mii_media_active) & IFM_FDX) + miicfg |= MIICFG_ENABLE; + else + miicfg &= ~MIICFG_ENABLE; + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG, miicfg); + } + + /* + * There is a multicast filter bug in 10Mbps mode. Kick the + * multicast filter in case the speed changed. + */ + epic_set_mchash(sc); +} + +/* + * Callback from ifmedia to request current media status. + */ +void +epic_mediastatus(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct epic_softc *sc = ifp->if_softc; + + mii_pollstat(&sc->sc_mii); + ifmr->ifm_status = sc->sc_mii.mii_media_status; + ifmr->ifm_active = sc->sc_mii.mii_media_active; +} + +/* + * Callback from ifmedia to request new media setting. + */ +int +epic_mediachange(ifp) + struct ifnet *ifp; +{ + struct epic_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + struct ifmedia *ifm = &mii->mii_media; + int media = ifm->ifm_cur->ifm_media; + u_int32_t miicfg; + struct mii_softc *miisc; + int cfg; + + if (!(ifp->if_flags & IFF_UP)) + return (0); + + if (IFM_INST(media) != sc->sc_serinst) { + /* If we're not selecting serial interface, select MII mode */ +#ifdef EPICMEDIADEBUG + printf("%s: parallel mode\n", ifp->if_xname); +#endif + miicfg = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG); + miicfg &= ~MIICFG_SERMODEENA; + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG, miicfg); + } + + mii_mediachg(mii); + + if (IFM_INST(media) == sc->sc_serinst) { + /* select serial interface */ +#ifdef EPICMEDIADEBUG + printf("%s: serial mode\n", ifp->if_xname); +#endif + miicfg = bus_space_read_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG); + miicfg |= (MIICFG_SERMODEENA | MIICFG_ENABLE); + bus_space_write_4(sc->sc_st, sc->sc_sh, EPIC_MIICFG, miicfg); + + /* There is no driver to fill this */ + mii->mii_media_active = media; + mii->mii_media_status = 0; + + epic_statchg(&sc->sc_dev); + return (0); + } + + /* Lookup selected PHY */ + for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; + miisc = LIST_NEXT(miisc, mii_list)) { + if (IFM_INST(media) == miisc->mii_inst) + break; + } + if (!miisc) { + printf("epic_mediachange: can't happen\n"); /* ??? panic */ + return (0); + } +#ifdef EPICMEDIADEBUG + printf("%s: using phy %s\n", ifp->if_xname, + miisc->mii_dev.dv_xname); +#endif + + if (miisc->mii_flags & MIIF_HAVEFIBER) { + /* XXX XXX assume it's a Level1 - should check */ + + /* We have to powerup fiber transceivers */ + cfg = PHY_READ(miisc, MII_LXTPHY_CONFIG); + if (IFM_SUBTYPE(media) == IFM_100_FX) { +#ifdef EPICMEDIADEBUG + printf("%s: power up fiber\n", ifp->if_xname); +#endif + cfg |= (CONFIG_LEDC1 | CONFIG_LEDC0); + } else { +#ifdef EPICMEDIADEBUG + printf("%s: power down fiber\n", ifp->if_xname); +#endif + cfg &= ~(CONFIG_LEDC1 | CONFIG_LEDC0); + } + PHY_WRITE(miisc, MII_LXTPHY_CONFIG, cfg); + } + + return (0); +} diff --git a/sys/dev/ic/smc83c170reg.h b/sys/dev/ic/smc83c170reg.h new file mode 100644 index 00000000000..5c9c98779fb --- /dev/null +++ b/sys/dev/ic/smc83c170reg.h @@ -0,0 +1,487 @@ +/* $OpenBSD: smc83c170reg.h,v 1.1 2005/05/10 01:16:32 brad Exp $ */ +/* $NetBSD: smc83c170reg.h,v 1.9 2003/11/08 16:08:13 tsutsui Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_IC_SMC83C170REG_H_ +#define _DEV_IC_SMC83C170REG_H_ + +/* + * Register description for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100). + */ + +/* + * EPIC transmit descriptor. Must be 4-byte aligned. + */ +struct epic_txdesc { + u_int32_t et_txstatus; /* transmit status; see below */ + u_int32_t et_bufaddr; /* buffer address */ + u_int32_t et_control; /* control word; see below */ + u_int32_t et_nextdesc; /* next descriptor pointer */ +}; + +/* et_txstatus */ +#define TXSTAT_TXLENGTH_SHIFT 16 /* TX length in higher 16bits */ +#define TXSTAT_TXLENGTH(x) ((x) << TXSTAT_TXLENGTH_SHIFT) + +#define ET_TXSTAT_OWNER 0x8000 /* NIC owns descriptor */ +#define ET_TXSTAT_COLLMASK 0x1f00 /* collisions */ +#define ET_TXSTAT_DEFERRING 0x0080 /* deferring due to jabber */ +#define ET_TXSTAT_OOWCOLL 0x0040 /* out of window collision */ +#define ET_TXSTAT_CDHB 0x0020 /* collision detect heartbeat */ +#define ET_TXSTAT_UNDERRUN 0x0010 /* DMA underrun */ +#define ET_TXSTAT_CARSENSELOST 0x0008 /* carrier lost */ +#define ET_TXSTAT_TXWITHCOLL 0x0004 /* encountered collisions during tx */ +#define ET_TXSTAT_NONDEFERRED 0x0002 /* transmitted without deferring */ +#define ET_TXSTAT_PACKETTX 0x0001 /* packet transmitted successfully */ + +#define TXSTAT_COLLISIONS(x) (((x) & ET_TXSTAT_COLLMASK) >> 8) + +/* et_control */ +#define TXCTL_BUFLENGTH_MASK 0x0000ffff /* buf length in lower 16bits */ +#define TXCTL_BUFLENGTH(x) ((x) & TXCTL_BUFLENGTH_MASK) + +#define ET_TXCTL_LASTDESC 0x00100000 /* last descriptor in frame */ +#define ET_TXCTL_NOCRC 0x00080000 /* disable CRC generation */ +#define ET_TXCTL_IAF 0x00040000 /* interrupt after frame */ +#define ET_TXCTL_LFFORM 0x00020000 /* alternate fraglist format */ +#define ET_TXCTL_FRAGLIST 0x00010000 /* descriptor points to fraglist */ + +/* + * EPIC receive descriptor. Must be 4-byte aligned. + */ +struct epic_rxdesc { + u_int32_t er_rxstatus; /* receive status; see below */ + u_int32_t er_bufaddr; /* buffer address */ + u_int32_t er_control; /* control word; see below */ + u_int32_t er_nextdesc; /* next descriptor pointer */ +}; + +/* er_rxstatus */ +#define RXSTAT_RXLENGTH_SHIFT 16 /* TX length in higher 16bits */ +#define RXSTAT_RXLENGTH(x) ((x) >> RXSTAT_RXLENGTH_SHIFT) + +#define ER_RXSTAT_OWNER 0x8000 /* NIC owns descriptor */ +#define ER_RXSTAT_HDRCOPIED 0x4000 /* rx status posted after hdr copy */ +#define ER_RXSTAT_FRAGLISTERR 0x2000 /* ran out of frags to copy frame */ +#define ER_RXSTAT_NETSTATVALID 0x1000 /* length and status are valid */ +#define ER_RXSTAT_RCVRDIS 0x0040 /* receiver disabled */ +#define ER_RXSTAT_BCAST 0x0020 /* broadcast address recognized */ +#define ER_RXSTAT_MCAST 0x0010 /* multicast address recognized */ +#define ER_RXSTAT_MISSEDPKT 0x0008 /* missed packet */ +#define ER_RXSTAT_CRCERROR 0x0004 /* EPIC or MII asserted CRC error */ +#define ER_RXSTAT_ALIGNERROR 0x0002 /* frame not byte-aligned */ +#define ER_RXSTAT_PKTINTACT 0x0001 /* packet received without error */ + +/* er_control */ +#define RXCTL_BUFLENGTH_MASK 0x0000ffff /* buf length in lower 16bits */ +#define RXCTL_BUFLENGTH(x) ((x) & RXCTL_BUFLENGTH_MASK) + +#define ER_RXCTL_HEADER 0x00040000 /* descriptor is for hdr copy */ +#define ER_RXCTL_LFFORM 0x00020000 /* alternate fraglist format */ +#define ER_RXCTL_FRAGLIST 0x00010000 /* descriptor points to fraglist */ + +/* + * This is not really part of the register description, but we need + * to define the number of transmit fragments *somewhere*. + */ +#define EPIC_NFRAGS 16 /* maximum number of frags in list */ + +/* + * EPIC fraglist descriptor. + */ +struct epic_fraglist { + u_int32_t ef_nfrags; /* number of frags in list */ + struct { + u_int32_t ef_addr; /* address of frag */ + u_int32_t ef_length; /* length of frag */ + } ef_frags[EPIC_NFRAGS]; +}; + +/* + * EPIC control registers. + */ + +#define EPIC_COMMAND 0x00 /* COMMAND */ +#define COMMAND_TXUGO 0x00000080 /* start tx after underrun */ +#define COMMAND_STOP_RDMA 0x00000040 /* stop rx dma */ +#define COMMAND_STOP_TDMA 0x00000020 /* stop tx dma */ +#define COMMAND_NEXTFRAME 0x00000010 /* move onto next rx frame */ +#define COMMAND_RXQUEUED 0x00000008 /* queue a rx descriptor */ +#define COMMAND_TXQUEUED 0x00000004 /* queue a tx descriptor */ +#define COMMAND_START_RX 0x00000002 /* start receiver */ +#define COMMAND_STOP_RX 0x00000001 /* stop receiver */ + +#define EPIC_INTSTAT 0x04 /* INTERRUPT STATUS */ +#define INTSTAT_PTA 0x08000000 /* PCI target abort */ +#define INTSTAT_PMA 0x04000000 /* PCI master abort */ +#define INTSTAT_APE 0x02000000 /* PCI address parity error */ +#define INTSTAT_DPE 0x01000000 /* PCI data parity error */ +#define INTSTAT_RSV 0x00800000 /* rx status valid */ +#define INTSTAT_RCTS 0x00400000 /* rx copy threshold status */ +#define INTSTAT_RBE 0x00200000 /* rx buffers empty */ +#define INTSTAT_TCIP 0x00100000 /* tx copy in progress */ +#define INTSTAT_RCIP 0x00080000 /* rx copy in progress */ +#define INTSTAT_TXIDLE 0x00040000 /* transmit idle */ +#define INTSTAT_RXIDLE 0x00020000 /* receive idle */ +#define INTSTAT_INT_ACTV 0x00010000 /* interrupt active */ +#define INTSTAT_GP2_INT 0x00008000 /* gpio2 low (PHY event) */ +#define INTSTAT_FATAL_INT 0x00001000 /* fatal error occurred */ +#define INTSTAT_RCT 0x00000800 /* rx copy threshold crossed */ +#define INTSTAT_PREI 0x00000400 /* preemptive interrupt */ +#define INTSTAT_CNT 0x00000200 /* counter overflow */ +#define INTSTAT_TXU 0x00000100 /* transmit underrun */ +#define INTSTAT_TQE 0x00000080 /* transmit queue empty */ +#define INTSTAT_TCC 0x00000040 /* transmit chain complete */ +#define INTSTAT_TXC 0x00000020 /* transmit complete */ +#define INTSTAT_RXE 0x00000010 /* receive error */ +#define INTSTAT_OVW 0x00000008 /* rx buffer overflow */ +#define INTSTAT_RQE 0x00000004 /* receive queue empty */ +#define INTSTAT_HCC 0x00000002 /* header copy complete */ +#define INTSTAT_RCC 0x00000001 /* receive copy complete */ + +#define EPIC_INTMASK 0x08 /* INTERRUPT MASK */ + /* Bits 0-15 enable the corresponding interrupt in INTSTAT. */ + +#define EPIC_GENCTL 0x0c /* GENERAL CONTROL */ +#define GENCTL_RESET_PHY 0x00004000 /* reset PHY */ +#define GENCTL_SOFT1 0x00002000 /* software use */ +#define GENCTL_SOFT0 0x00001000 /* software use */ +#define GENCTL_MEM_READ_CTL1 0x00000800 /* PCI memory control */ +#define GENCTL_MEM_READ_CTL0 0x00000400 /* (see below) */ +#define GENCTL_RX_FIFO_THRESH1 0x00000200 /* rx fifo thresh */ +#define GENCTL_RX_FIFO_THRESH0 0x00000100 /* (see below) */ +#define GENCTL_BIG_ENDIAN 0x00000020 /* big endian mode */ +#define GENCTL_ONECOPY 0x00000010 /* auto-NEXTFRAME */ +#define GENCTL_POWERDOWN 0x00000008 /* powersave sleep mode */ +#define GENCTL_SOFTINT 0x00000004 /* software-generated intr */ +#define GENCTL_INTENA 0x00000002 /* interrupt enable */ +#define GENCTL_SOFTRESET 0x00000001 /* initialize EPIC */ + +/* + * Explanation of MEMORY READ CONTROL: + * + * These bits control which PCI command the transmit DMA will use when + * bursting data over the PCI bus. When CTL1 is set, the transmit DMA + * will use the PCI "memory read line" command. When CTL0 is set, the + * transmit DMA will use the PCI "memory read multiple" command. When + * neither bit is set, the transmit DMA will use the "memory read" command. + * Use of "memory read line" or "memory read multiple" may enhance + * performance on some systems. + */ + +/* + * Explanation of RECEIVE FIFO THRESHOLD: + * + * Controls the level at which the PCI burst state machine begins to + * empty the receive FIFO. Default is "1/2 full" (0,1). + * + * 0,0 1/4 full 32 bytes + * 0,1 1/2 full 64 bytes + * 1,0 3/4 full 96 bytes + * 1,1 full 128 bytes + */ + +#define EPIC_NVCTL 0x10 /* NON-VOLATILE CONTROL */ +#define NVCTL_IPG_DLY_MASK 0x00000780 /* interpacket delay gap */ +#define NVCTL_CB_MODE 0x00000040 /* CardBus mode */ +#define NVCTL_GPIO2 0x00000020 /* general purpose i/o */ +#define NVCTL_GPIO1 0x00000010 /* ... */ +#define NVCTL_GPOE2 0x00000008 /* general purpose output ena */ +#define NVCTL_GPOE1 0x00000004 /* ... */ +#define NVCTL_CLKRUNSUPP 0x00000002 /* clock run supported */ +#define NVCTL_ENAMEMMAP 0x00000001 /* enable memory map */ + +#define NVCTL_IPG_DLY(x) (((x) & NVCTL_IPG_DLY_MASK) >> 7) + +#define EPIC_EECTL 0x14 /* EEPROM CONTROL */ +#define EECTL_EEPROMSIZE 0x00000040 /* eeprom size; see below */ +#define EECTL_EERDY 0x00000020 /* eeprom ready */ +#define EECTL_EEDO 0x00000010 /* eeprom data out (from) */ +#define EECTL_EEDI 0x00000008 /* eeprom data in (to) */ +#define EECTL_EESK 0x00000004 /* eeprom clock */ +#define EECTL_EECS 0x00000002 /* eeprom chip select */ +#define EECTL_ENABLE 0x00000001 /* eeprom enable */ + +/* + * Explanation of EEPROM SIZE: + * + * Indicates the size of the serial EEPROM: + * + * 1 16x16 or 64x16 + * 0 128x16 or 256x16 + */ + +/* + * Serial EEPROM opcodes, including start bit: + */ +#define EPIC_EEPROM_OPC_WRITE 0x05 +#define EPIC_EEPROM_OPC_READ 0x06 + +#define EPIC_PBLCNT 0x18 /* PBLCNT */ +#define PBLCNT_MASK 0x0000003f /* programmable burst length */ + +#define EPIC_TEST 0x1c /* TEST */ +#define TEST_CLOCKTEST 0x00000008 + +#define EPIC_CRCCNT 0x20 /* CRC ERROR COUNTER */ +#define CRCCNT_MASK 0x0000000f /* crc errs since last read */ + +#define EPIC_ALICNT 0x24 /* FRAME ALIGNMENT ERROR COUNTER */ +#define ALICNT_MASK 0x0000000f /* align errs since last read */ + +#define EPIC_MPCNT 0x28 /* MISSED PACKET COUNTER */ +#define MPCNT_MASK 0x0000000f /* miss. pkts since last read */ + +#define EPIC_RXFIFO 0x2c + +#define EPIC_MMCTL 0x30 /* MII MANAGEMENT INTERFACE CONTROL */ +#define MMCTL_PHY_ADDR_MASK 0x00003e00 /* phy address field */ +#define MMCTL_PHY_REG_ADDR_MASK 0x000001f0 /* phy register address field */ +#define MMCTL_RESPONDER 0x00000008 /* phy responder */ +#define MMCTL_WRITE 0x00000002 /* write to phy */ +#define MMCTL_READ 0x00000001 /* read from phy */ + +#define MMCTL_ARG(phy, reg, cmd) (((phy) << 9) | ((reg) << 4) | (cmd)) + +#define EPIC_MMDATA 0x34 /* MII MANAGEMENT INTERFACE DATA */ +#define MMDATA_MASK 0x0000ffff /* MII frame data */ + +#define EPIC_MIICFG 0x38 /* MII CONFIGURATION */ +#define MIICFG_ALTDIR 0x00000080 /* alternate direction */ +#define MIICFG_ALTDATA 0x00000040 /* alternate data */ +#define MIICFG_ALTCLOCK 0x00000020 /* alternate clock source */ +#define MIICFG_ENASER 0x00000010 /* enable serial manag intf */ +#define MIICFG_PHYPRESENT 0x00000008 /* phy present on MII */ +#define MIICFG_LINKSTATUS 0x00000004 /* 694 link status */ +#define MIICFG_ENABLE 0x00000002 /* enable 694 */ +#define MIICFG_SERMODEENA 0x00000001 /* serial mode enable */ + +#define EPIC_IPG 0x3c /* INTERPACKET GAP */ +#define IPG_INTERFRAME_MASK 0x00007f00 /* interframe gap time */ +#define IPG_INTERPKT_MASK 0x000000ff /* interpacket gap time */ + +#define EPIC_LAN0 0x40 /* LAN ADDRESS */ + +#define EPIC_LAN1 0x44 + +#define EPIC_LAN2 0x48 + +#define LANn_MASK 0x0000ffff + +/* + * Explanation of LAN ADDRESS registers: + * + * LAN address is described as: + * + * 0000 [n1][n0][n3][n2] | 0000 [n5][n4][n7][n6] | 0000 [n9][n8][n11][n10] + * + * n == one nibble, mapped as follows: + * + * LAN0 [15-12] n3 + * LAN0 [11-8] n2 + * LAN0 [7-4] n1 + * LAN0 [3-0] n0 + * LAN1 [15-12] n7 + * LAN1 [11-8] n6 + * LAN1 [7-4] n5 + * LAN1 [3-0] n4 + * LAN2 [15-12] n11 + * LAN2 [11-8] n10 + * LAN2 [7-4] n9 + * LAN2 [3-0] n8 + * + * The LAN address is automatically recalled from the EEPROM after a + * hard reseet. + */ + +#define EPIC_IDCHK 0x4c /* BOARD ID/CHECKSUM */ +#define IDCHK_ID_MASK 0x0000ff00 /* board ID */ +#define IDCHK_CKSUM_MASK 0x000000ff /* checksum (should be 0xff) */ + +#define EPIC_MC0 0x50 /* MULTICAST ADDRESS HASH TABLE */ + +#define EPIC_MC1 0x54 + +#define EPIC_MC2 0x58 + +#define EPIC_MC3 0x5c + +/* + * Explanation of MULTICAST ADDRESS HASH TABLE registers: + * + * Bits in the hash table are encoded as follows: + * + * MC0 [15-0] + * MC1 [31-16] + * MC2 [47-32] + * MC3 [53-48] + */ + +#define EPIC_RXCON 0x60 /* RECEIVE CONTROL */ +#define RXCON_EXTBUFSIZESEL1 0x00000200 /* ext buf size; see below */ +#define RXCON_EXTBUFSIZESEL0 0x00000100 /* ... */ +#define RXCON_EARLYRXENABLE 0x00000080 /* early receive enable */ +#define RXCON_MONITORMODE 0x00000040 /* monitor mode */ +#define RXCON_PROMISCMODE 0x00000020 /* promiscuous mode */ +#define RXCON_RXINVADDR 0x00000010 /* rx inv individual addr */ +#define RXCON_RXMULTICAST 0x00000008 /* receive multicast */ +#define RXCON_RXBROADCAST 0x00000004 /* receive broadcast */ +#define RXCON_RXRUNT 0x00000002 /* receive runt frames */ +#define RXCON_SAVEERRPKTS 0x00000001 /* save errored packets */ + +/* + * Explanation of EXTERNAL BUFFER SIZE SELECT: + * + * 0,0 external buffer access is disabled + * 0,1 16k + * 1,0 32k + * 1,1 128k + */ + +#define EPIC_RXSTAT 0x64 /* RECEIVE STATUS */ + +#define EPIC_RXCNT 0x68 + +#define EPIC_RXTEST 0x6c + +#define EPIC_TXCON 0x70 /* TRANSMIT CONTROL */ +#define TXCON_SLOTTIME_MASK 0x000000f8 /* slot time */ +#define TXCON_LOOPBACK_D2 0x00000004 /* loopback mode bit 2 */ +#define TXCON_LOOPBACK_D1 0x00000002 /* loopback mode bit 1 */ +#define TXCON_EARLYTX_ENABLE 0x00000001 /* early transmit enable */ + +/* + * Explanation of LOOPBACK MODE BIT: + * + * 0,0 normal operation + * 0,1 internal loopback (before PHY) + * 1,0 external loopback (after PHY) + * 1,1 full duplex - decouples transmit and receive blocks + */ + +#define EPIC_TXSTAT 0x74 /* TRANSMIT STATUS */ + +#define EPIC_TDPAR 0x78 + +#define EPIC_TXTEST 0x7c + +#define EPIC_PRFDAR 0x80 + +#define EPIC_PRCDAR 0x84 /* PCI RECEIVE CURRENT DESCRIPTOR ADDR */ + +#define EPIC_PRHDAR 0x88 + +#define EPIC_PRFLAR 0x8c + +#define EPIC_PRDLGTH 0x90 + +#define EPIC_PRFCNT 0x94 + +#define EPIC_PRLCAR 0x98 + +#define EPIC_PRLPAR 0x9c + +#define EPIC_PREFAR 0xa0 + +#define EPIC_PRSTAT 0xa4 /* PCI RECEIVE DMA STATUS */ + +#define EPIC_PRBUF 0xa8 + +#define EPIC_RDNCAR 0xac + +#define EPIC_PRCPTHR 0xb0 /* PCI RECEIVE COPY THRESHOLD */ + +#define EPIC_ROMDATA 0xb4 + +#define EPIC_PREEMPR 0xbc + +#define EPIC_PTFDAR 0xc0 + +#define EPIC_PTCDAR 0xc4 /* PCI TRANSMIT CURRENT DESCRIPTOR ADDR */ + +#define EPIC_PTHDAR 0xc8 + +#define EPIC_PTFLAR 0xcc + +#define EPIC_PTDLGTH 0xd0 + +#define EPIC_PTFCNT 0xd4 + +#define EPIC_PTLCAR 0xd8 + +#define EPIC_ETXTHR 0xdc /* EARLY TRANSMIT THRESHOLD */ + +#define EPIC_PTETXC 0xe0 + +#define EPIC_PTSTAT 0xe4 + +#define EPIC_PTBUF 0xe8 + +#define EPIC_PTFDAR2 0xec + +#define EPIC_FEVTR 0xf0 /* FEVTR (CardBus) */ + +#define EPIC_FEVTRMSKR 0xf4 /* FEVTRMSKR (CardBus) */ + +#define EPIC_FPRSTSTR 0xf8 /* FPRSTR (CardBus) */ + +#define EPIC_FFRCEVTR 0xfc /* PPRCEVTR (CardBus) */ + +/* + * EEPROM format: + * + * Word Bits Description + * ---- ---- ----------- + * 0 7-0 LAN Address Byte 0 + * 0 15-8 LAN Address Byte 1 + * 1 7-0 LAN Address Byte 2 + * 1 15-8 LAN Address Byte 3 + * 2 7-0 LAN Address Byte 4 + * 2 15-8 LAN Address Byte 5 + * 3 7-0 Board ID + * 3 15-8 Checksum + * 4 5-0 Non-Volatile Control Register Contents + * 5 7-0 PCI Minimum Grant Desired Setting + * 5 15-8 PCI Maximum Latency Desired Setting + * 6 15-0 Subsystem Vendor ID + * 7 14-0 Subsystem ID + */ + +#endif /* _DEV_IC_SMC83C170REG_H_ */ diff --git a/sys/dev/ic/smc83c170var.h b/sys/dev/ic/smc83c170var.h new file mode 100644 index 00000000000..33398da9bc5 --- /dev/null +++ b/sys/dev/ic/smc83c170var.h @@ -0,0 +1,191 @@ +/* $OpenBSD: smc83c170var.h,v 1.1 2005/05/10 01:16:32 brad Exp $ */ +/* $NetBSD: smc83c170var.h,v 1.9 2005/02/04 02:10:37 perry Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_IC_SMC83C170VAR_H_ +#define _DEV_IC_SMC83C170VAR_H_ + +#include <sys/timeout.h> + +/* + * Misc. definitions for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100) driver. + */ + +/* + * Transmit descriptor list size. + */ +#define EPIC_NTXDESC 128 +#define EPIC_NTXDESC_MASK (EPIC_NTXDESC - 1) +#define EPIC_NEXTTX(x) ((x + 1) & EPIC_NTXDESC_MASK) + +/* + * Receive descriptor list size. + */ +#define EPIC_NRXDESC 64 +#define EPIC_NRXDESC_MASK (EPIC_NRXDESC - 1) +#define EPIC_NEXTRX(x) ((x + 1) & EPIC_NRXDESC_MASK) + +/* + * Control structures are DMA'd to the EPIC chip. We allocate them in + * a single clump that maps to a single DMA segment to make several things + * easier. + */ +struct epic_control_data { + /* + * The transmit descriptors. + */ + struct epic_txdesc ecd_txdescs[EPIC_NTXDESC]; + + /* + * The receive descriptors. + */ + struct epic_rxdesc ecd_rxdescs[EPIC_NRXDESC]; + + /* + * The transmit fraglists. + */ + struct epic_fraglist ecd_txfrags[EPIC_NTXDESC]; +}; + +#define EPIC_CDOFF(x) offsetof(struct epic_control_data, x) +#define EPIC_CDTXOFF(x) EPIC_CDOFF(ecd_txdescs[(x)]) +#define EPIC_CDRXOFF(x) EPIC_CDOFF(ecd_rxdescs[(x)]) +#define EPIC_CDFLOFF(x) EPIC_CDOFF(ecd_txfrags[(x)]) + +/* + * Software state for transmit and receive desciptors. + */ +struct epic_descsoft { + struct mbuf *ds_mbuf; /* head of mbuf chain */ + bus_dmamap_t ds_dmamap; /* our DMA map */ +}; + +/* + * Software state per device. + */ +struct epic_softc { + struct device sc_dev; /* generic device information */ + bus_space_tag_t sc_st; /* bus space tag */ + bus_space_handle_t sc_sh; /* bus space handle */ + bus_dma_tag_t sc_dmat; /* bus DMA tag */ + struct arpcom sc_arpcom; /* ethernet common data */ + void *sc_sdhook; /* shutdown hook */ + + int sc_hwflags; /* info about board */ +#define EPIC_HAS_BNC 0x01 /* BNC on serial interface */ +#define EPIC_HAS_MII_FIBER 0x02 /* fiber on MII lxtphy */ +#define EPIC_DUPLEXLED_ON_694 0x04 /* duplex LED by software */ + + struct mii_data sc_mii; /* MII/media information */ + struct timeout sc_mii_timeout; /* MII timeout */ + + bus_dmamap_t sc_cddmamap; /* control data DMA map */ +#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr + bus_dmamap_t sc_nulldmamap; /* DMA map for the pad buffer */ +#define sc_nulldma sc_nulldmamap->dm_segs[0].ds_addr + + /* + * Software state for transmit and receive descriptors. + */ + struct epic_descsoft sc_txsoft[EPIC_NTXDESC]; + struct epic_descsoft sc_rxsoft[EPIC_NRXDESC]; + + /* + * Control data structures. + */ + struct epic_control_data *sc_control_data; + + int sc_txpending; /* number of TX requests pending */ + int sc_txdirty; /* first dirty TX descriptor */ + int sc_txlast; /* last used TX descriptor */ + + int sc_rxptr; /* next ready RX descriptor */ + + u_int sc_serinst; /* ifmedia instance for serial mode */ +}; + +#define EPIC_CDTXADDR(sc, x) ((sc)->sc_cddma + EPIC_CDTXOFF((x))) +#define EPIC_CDRXADDR(sc, x) ((sc)->sc_cddma + EPIC_CDRXOFF((x))) +#define EPIC_CDFLADDR(sc, x) ((sc)->sc_cddma + EPIC_CDFLOFF((x))) + +#define EPIC_CDTX(sc, x) (&(sc)->sc_control_data->ecd_txdescs[(x)]) +#define EPIC_CDRX(sc, x) (&(sc)->sc_control_data->ecd_rxdescs[(x)]) +#define EPIC_CDFL(sc, x) (&(sc)->sc_control_data->ecd_txfrags[(x)]) + +#define EPIC_DSTX(sc, x) (&(sc)->sc_txsoft[(x)]) +#define EPIC_DSRX(sc, x) (&(sc)->sc_rxsoft[(x)]) + +#define EPIC_CDTXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + EPIC_CDTXOFF((x)), sizeof(struct epic_txdesc), (ops)) + +#define EPIC_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + EPIC_CDRXOFF((x)), sizeof(struct epic_rxdesc), (ops)) + +#define EPIC_CDFLSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + EPIC_CDFLOFF((x)), sizeof(struct epic_fraglist), (ops)) + +#define EPIC_INIT_RXDESC(sc, x) \ +do { \ + struct epic_descsoft *__ds = EPIC_DSRX((sc), (x)); \ + struct epic_rxdesc *__rxd = EPIC_CDRX((sc), (x)); \ + struct mbuf *__m = __ds->ds_mbuf; \ + \ + /* \ + * Note we scoot the packet forward 2 bytes in the buffer \ + * so that the payload after the Ethernet header is aligned \ + * to a 4 byte boundary. \ + */ \ + __m->m_data = __m->m_ext.ext_buf + 2; \ + __rxd->er_bufaddr = __ds->ds_dmamap->dm_segs[0].ds_addr + 2; \ + __rxd->er_control = RXCTL_BUFLENGTH(__m->m_ext.ext_size - 2); \ + __rxd->er_rxstatus = ER_RXSTAT_OWNER; \ + __rxd->er_nextdesc = EPIC_CDRXADDR((sc), EPIC_NEXTRX((x))); \ + EPIC_CDRXSYNC((sc), (x), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); \ +} while (/* CONSTCOND */ 0) + +#ifdef _KERNEL +void epic_attach(struct epic_softc *, const char *); +int epic_intr(void *); +#endif /* _KERNEL */ + +#endif /* _DEV_IC_SMC83C170VAR_H_ */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 5f5fece1a16..13bbd56b393 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.175 2005/05/02 17:26:00 grange Exp $ +# $OpenBSD: files.pci,v 1.176 2005/05/10 01:16:32 brad Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -303,10 +303,9 @@ file dev/pci/if_em_hw.c em attach dc at pci with dc_pci file dev/pci/if_dc_pci.c dc_pci -# SMC EPIC, 83c170 -device tx: ether, ifnet, mii, ifmedia -attach tx at pci -file dev/pci/if_tx.c tx +# SMC EPIC/100 Fast Ethernet on PCI +attach epic at pci with epic_pci +file dev/pci/if_epic_pci.c epic_pci # Alteon Tigon I & II device ti: ether, ifnet, ifmedia, firmload diff --git a/sys/dev/pci/if_epic_pci.c b/sys/dev/pci/if_epic_pci.c new file mode 100644 index 00000000000..1aad3554170 --- /dev/null +++ b/sys/dev/pci/if_epic_pci.c @@ -0,0 +1,277 @@ +/* $OpenBSD: if_epic_pci.c,v 1.1 2005/05/10 01:16:32 brad Exp $ */ +/* $NetBSD: if_epic_pci.c,v 1.28 2005/02/27 00:27:32 perry Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * PCI bus front-end for the Standard Microsystems Corp. 83C170 + * Ethernet PCI Integrated Controller (EPIC/100) driver. + */ + +#if 0 +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: if_epic_pci.c,v 1.28 2005/02/27 00:27:32 perry Exp $"); +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <net/if_media.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/mii/miivar.h> + +#include <dev/ic/smc83c170reg.h> +#include <dev/ic/smc83c170var.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +/* + * PCI configuration space registers used by the EPIC. + */ +#define EPIC_PCI_IOBA 0x10 /* i/o mapped base */ +#define EPIC_PCI_MMBA 0x14 /* memory mapped base */ + +struct epic_pci_softc { + struct epic_softc sc_epic; /* real EPIC softc */ + + /* PCI-specific goo. */ + void *sc_ih; /* interrupt handle */ +}; + +static int epic_pci_match(struct device *, void *, void *); +static void epic_pci_attach(struct device *, struct device *, void *); + +struct cfattach epic_pci_ca = { + sizeof(struct epic_pci_softc), epic_pci_match, epic_pci_attach +}; + +static const struct epic_pci_product { + u_int32_t epp_prodid; /* PCI product ID */ + const char *epp_name; /* device name */ +} epic_pci_products[] = { + { PCI_PRODUCT_SMC_83C170, "SMC 83c170 Fast Ethernet" }, + { PCI_PRODUCT_SMC_83C175, "SMC 83c175 Fast Ethernet" }, + { 0, NULL }, +}; + +static const struct epic_pci_product * +epic_pci_lookup(const struct pci_attach_args *pa) +{ + const struct epic_pci_product *epp; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_SMC) + return (NULL); + + for (epp = epic_pci_products; epp->epp_name != NULL; epp++) + if (PCI_PRODUCT(pa->pa_id) == epp->epp_prodid) + return (epp); + + return (NULL); +} + +static const struct epic_pci_subsys_info { + pcireg_t subsysid; + int flags; +} epic_pci_subsys_info[] = { + { PCI_ID_CODE(PCI_VENDOR_SMC, 0xa015), /* SMC9432BTX */ + EPIC_HAS_BNC }, + { PCI_ID_CODE(PCI_VENDOR_SMC, 0xa024), /* SMC9432BTX1 */ + EPIC_HAS_BNC }, + { PCI_ID_CODE(PCI_VENDOR_SMC, 0xa016), /* SMC9432FTX */ + EPIC_HAS_MII_FIBER | EPIC_DUPLEXLED_ON_694 }, + { 0xffffffff, + 0 } +}; + +static const struct epic_pci_subsys_info * +epic_pci_subsys_lookup(const struct pci_attach_args *pa) +{ + pci_chipset_tag_t pc = pa->pa_pc; + pcireg_t reg; + const struct epic_pci_subsys_info *esp; + + reg = pci_conf_read(pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + + for (esp = epic_pci_subsys_info; esp->subsysid != 0xffffffff; esp++) + if (esp->subsysid == reg) + return (esp); + + return (NULL); +} + +static int +epic_pci_match(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (epic_pci_lookup(pa) != NULL) + return (1); + + return (0); +} + +static void +epic_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct epic_pci_softc *psc = (struct epic_pci_softc *)self; + struct epic_softc *sc = &psc->sc_epic; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + const struct epic_pci_product *epp; + const struct epic_pci_subsys_info *esp; + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + pcireg_t reg; + int pmreg, ioh_valid, memh_valid; + + epp = epic_pci_lookup(pa); + if (epp == NULL) { + printf("\n"); + panic(": epic_pci_attach: impossible"); + } + + if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT, &pmreg, 0)) { + reg = pci_conf_read(pc, pa->pa_tag, pmreg + PCI_PMCSR); + switch (reg & PCI_PMCSR_STATE_MASK) { + case PCI_PMCSR_STATE_D1: + case PCI_PMCSR_STATE_D2: + printf(": waking up from power state D%d\n", + sc->sc_dev.dv_xname, reg & PCI_PMCSR_STATE_MASK); + pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR, + (reg & ~PCI_PMCSR_STATE_MASK) | + PCI_PMCSR_STATE_D0); + break; + case PCI_PMCSR_STATE_D3: + /* + * IO and MEM are disabled. We can't enable + * the card because the BARs might be invalid. + */ + printf( + ": unable to wake up from power state D3, " + "reboot required.\n", sc->sc_dev.dv_xname); + pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR, + (reg & ~PCI_PMCSR_STATE_MASK) | + PCI_PMCSR_STATE_D0); + return; + } + } + + /* + * Map the device. + */ + ioh_valid = (pci_mapreg_map(pa, EPIC_PCI_IOBA, + PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL, 0) == 0); + memh_valid = (pci_mapreg_map(pa, EPIC_PCI_MMBA, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &memt, &memh, NULL, NULL, 0) == 0); + + if (memh_valid) { + sc->sc_st = memt; + sc->sc_sh = memh; + } else if (ioh_valid) { + sc->sc_st = iot; + sc->sc_sh = ioh; + } else { + printf(": unable to map device registers\n", + sc->sc_dev.dv_xname); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + /* Make sure bus mastering is enabled. */ + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) | + PCI_COMMAND_MASTER_ENABLE); + + /* + * Map and establish our interrupt. + */ + if (pci_intr_map(pa, &ih)) { + printf(": unable to map interrupt\n", + sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + psc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, epic_intr, sc, + self->dv_xname); + if (psc->sc_ih == NULL) { + printf("%s: unable to establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + esp = epic_pci_subsys_lookup(pa); + if (esp) + sc->sc_hwflags = esp->flags; + + /* + * Finish off the attach. + */ + epic_attach(sc, intrstr); +} |