diff options
-rw-r--r-- | sys/dev/my/if_my.c | 1882 | ||||
-rw-r--r-- | sys/dev/my/if_myreg.h | 585 |
2 files changed, 2467 insertions, 0 deletions
diff --git a/sys/dev/my/if_my.c b/sys/dev/my/if_my.c new file mode 100644 index 0000000..4c163a4 --- /dev/null +++ b/sys/dev/my/if_my.c @@ -0,0 +1,1882 @@ +/* + * Copyright (c) 2002 Myson Technology Inc. + * 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, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * Written by: yen_cw@myson.com.tw available at: http://www.myson.com.tw/ + * + * $FreeBSD$ + * + * Myson fast ethernet PCI NIC driver + */ +#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 <sys/queue.h> +#include <sys/types.h> +#include <sys/bus.h> +#include <sys/module.h> + +#define NBPFILTER 1 + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_media.h> +#include <net/if_dl.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_memio.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include "miibus_if.h" + +/* + * #define MY_USEIOSPACE + */ + +static int MY_USEIOSPACE = 1; + +#if (MY_USEIOSPACE) +#define MY_RES SYS_RES_IOPORT +#define MY_RID MY_PCI_LOIO +#else +#define MY_RES SYS_RES_MEMORY +#define MY_RID MY_PCI_LOMEM +#endif + + +#include <dev/my/if_myreg.h> + +#ifndef lint +static const char rcsid[] = +"$Id: if_my.c,v 1.50 2001/12/03 04:15:33 <yen_cw@myson.com.tw> wpaul Exp $"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +struct my_type *my_info_tmp; +static struct my_type my_devs[] = { + {MYSONVENDORID, MTD800ID, "Myson MTD80X Based Fast Ethernet Card"}, + {MYSONVENDORID, MTD803ID, "Myson MTD80X Based Fast Ethernet Card"}, + {MYSONVENDORID, MTD891ID, "Myson MTD89X Based Giga Ethernet Card"}, + {0, 0, NULL} +}; + +/* + * Various supported PHY vendors/types and their names. Note that this driver + * will work with pretty much any MII-compliant PHY, so failure to positively + * identify the chip is not a fatal error. + */ +static struct my_type my_phys[] = { + {MysonPHYID0, MysonPHYID0, "<MYSON MTD981>"}, + {SeeqPHYID0, SeeqPHYID0, "<SEEQ 80225>"}, + {AhdocPHYID0, AhdocPHYID0, "<AHDOC 101>"}, + {MarvellPHYID0, MarvellPHYID0, "<MARVELL 88E1000>"}, + {LevelOnePHYID0, LevelOnePHYID0, "<LevelOne LXT1000>"}, + {0, 0, "<MII-compliant physical interface>"} +}; + +static int my_probe(device_t); +static int my_attach(device_t); +static int my_detach(device_t); +static int my_newbuf(struct my_softc *, struct my_chain_onefrag *); +static int my_encap(struct my_softc *, struct my_chain *, struct mbuf *); +static void my_rxeof(struct my_softc *); +static void my_txeof(struct my_softc *); +static void my_txeoc(struct my_softc *); +static void my_intr(void *); +static void my_start(struct ifnet *); +static int my_ioctl(struct ifnet *, u_long, caddr_t); +static void my_init(void *); +static void my_stop(struct my_softc *); +static void my_watchdog(struct ifnet *); +static void my_shutdown(device_t); +static int my_ifmedia_upd(struct ifnet *); +static void my_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static u_int16_t my_phy_readreg(struct my_softc *, int); +static void my_phy_writereg(struct my_softc *, int, int); +static void my_autoneg_xmit(struct my_softc *); +static void my_autoneg_mii(struct my_softc *, int, int); +static void my_setmode_mii(struct my_softc *, int); +static void my_getmode_mii(struct my_softc *); +static void my_setcfg(struct my_softc *, int); +static u_int8_t my_calchash(caddr_t); +static void my_setmulti(struct my_softc *); +static void my_reset(struct my_softc *); +static int my_list_rx_init(struct my_softc *); +static int my_list_tx_init(struct my_softc *); +static long my_send_cmd_to_phy(struct my_softc *, int, int); + +#define MY_SETBIT(sc, reg, x) CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x) +#define MY_CLRBIT(sc, reg, x) CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~x) + +static device_method_t my_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, my_probe), + DEVMETHOD(device_attach, my_attach), + DEVMETHOD(device_detach, my_detach), + DEVMETHOD(device_shutdown, my_shutdown), + + {0, 0} +}; + +static driver_t my_driver = { + "my", + my_methods, + sizeof(struct my_softc) +}; + +static devclass_t my_devclass; + +DRIVER_MODULE(if_my, pci, my_driver, my_devclass, 0, 0); + +static long +my_send_cmd_to_phy(struct my_softc * sc, int opcode, int regad) +{ + long miir; + int i; + int mask, data; + + MY_LOCK(sc); + + /* enable MII output */ + miir = CSR_READ_4(sc, MY_MANAGEMENT); + miir &= 0xfffffff0; + + miir |= MY_MASK_MIIR_MII_WRITE + MY_MASK_MIIR_MII_MDO; + + /* send 32 1's preamble */ + for (i = 0; i < 32; i++) { + /* low MDC; MDO is already high (miir) */ + miir &= ~MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + + /* high MDC */ + miir |= MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + } + + /* calculate ST+OP+PHYAD+REGAD+TA */ + data = opcode | (sc->my_phy_addr << 7) | (regad << 2); + + /* sent out */ + mask = 0x8000; + while (mask) { + /* low MDC, prepare MDO */ + miir &= ~(MY_MASK_MIIR_MII_MDC + MY_MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MY_MASK_MIIR_MII_MDO; + + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + /* high MDC */ + miir |= MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + DELAY(30); + + /* next */ + mask >>= 1; + if (mask == 0x2 && opcode == MY_OP_READ) + miir &= ~MY_MASK_MIIR_MII_WRITE; + } + + MY_UNLOCK(sc); + return miir; +} + + +static u_int16_t +my_phy_readreg(struct my_softc * sc, int reg) +{ + long miir; + int mask, data; + + MY_LOCK(sc); + + if (sc->my_info->my_did == MTD803ID) + data = CSR_READ_2(sc, MY_PHYBASE + reg * 2); + else { + miir = my_send_cmd_to_phy(sc, MY_OP_READ, reg); + + /* read data */ + mask = 0x8000; + data = 0; + while (mask) { + /* low MDC */ + miir &= ~MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + + /* read MDI */ + miir = CSR_READ_4(sc, MY_MANAGEMENT); + if (miir & MY_MASK_MIIR_MII_MDI) + data |= mask; + + /* high MDC, and wait */ + miir |= MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + DELAY(30); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + } + + MY_UNLOCK(sc); + return (u_int16_t) data; +} + + +static void +my_phy_writereg(struct my_softc * sc, int reg, int data) +{ + long miir; + int mask; + + MY_LOCK(sc); + + if (sc->my_info->my_did == MTD803ID) + CSR_WRITE_2(sc, MY_PHYBASE + reg * 2, data); + else { + miir = my_send_cmd_to_phy(sc, MY_OP_WRITE, reg); + + /* write data */ + mask = 0x8000; + while (mask) { + /* low MDC, prepare MDO */ + miir &= ~(MY_MASK_MIIR_MII_MDC + MY_MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MY_MASK_MIIR_MII_MDO; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + DELAY(1); + + /* high MDC */ + miir |= MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + DELAY(1); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MY_MASK_MIIR_MII_MDC; + CSR_WRITE_4(sc, MY_MANAGEMENT, miir); + } + MY_UNLOCK(sc); + return; +} + +static u_int8_t +my_calchash(caddr_t addr) +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* + * return the filter bit position Note: I arrived at the following + * nonsense through experimentation. It's not the usual way to + * generate the bit position but it's the only thing I could come up + * with that works. + */ + return (~(crc >> 26) & 0x0000003F); +} + + +/* + * Program the 64-bit multicast hash filter. + */ +static void +my_setmulti(struct my_softc * sc) +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = {0, 0}; + struct ifmultiaddr *ifma; + u_int32_t rxfilt; + int mcnt = 0; + + MY_LOCK(sc); + + ifp = &sc->arpcom.ac_if; + + rxfilt = CSR_READ_4(sc, MY_TCRRCR); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= MY_AM; + CSR_WRITE_4(sc, MY_TCRRCR, rxfilt); + CSR_WRITE_4(sc, MY_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, MY_MAR1, 0xFFFFFFFF); + + MY_UNLOCK(sc); + + return; + } + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, MY_MAR0, 0); + CSR_WRITE_4(sc, MY_MAR1, 0); + + /* now program new ones */ + /* + * Add by Surfer 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 = + * my_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h + * < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); + * mcnt++; } + */ + + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = my_calchash(LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + + if (mcnt) + rxfilt |= MY_AM; + else + rxfilt &= ~MY_AM; + CSR_WRITE_4(sc, MY_MAR0, hashes[0]); + CSR_WRITE_4(sc, MY_MAR1, hashes[1]); + CSR_WRITE_4(sc, MY_TCRRCR, rxfilt); + MY_UNLOCK(sc); + return; +} + +/* + * Initiate an autonegotiation session. + */ +static void +my_autoneg_xmit(struct my_softc * sc) +{ + u_int16_t phy_sts = 0; + + MY_LOCK(sc); + + my_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); + DELAY(500); + while (my_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); + + phy_sts = my_phy_readreg(sc, PHY_BMCR); + phy_sts |= PHY_BMCR_AUTONEGENBL | PHY_BMCR_AUTONEGRSTR; + my_phy_writereg(sc, PHY_BMCR, phy_sts); + + MY_UNLOCK(sc); + return; +} + + +/* + * Invoke autonegotiation on a PHY. + */ +static void +my_autoneg_mii(struct my_softc * sc, int flag, int verbose) +{ + u_int16_t phy_sts = 0, media, advert, ability; + u_int16_t ability2 = 0; + struct ifnet *ifp; + struct ifmedia *ifm; + + MY_LOCK(sc); + + ifm = &sc->ifmedia; + ifp = &sc->arpcom.ac_if; + + ifm->ifm_media = IFM_ETHER | IFM_AUTO; + +#ifndef FORCE_AUTONEG_TFOUR + /* + * First, see if autoneg is supported. If not, there's no point in + * continuing. + */ + phy_sts = my_phy_readreg(sc, PHY_BMSR); + if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { + if (verbose) + printf("my%d: autonegotiation not supported\n", + sc->my_unit); + ifm->ifm_media = IFM_ETHER | IFM_10_T | IFM_HDX; + MY_UNLOCK(sc); + return; + } +#endif + switch (flag) { + case MY_FLAG_FORCEDELAY: + /* + * XXX Never use this option anywhere but in the probe + * routine: making the kernel stop dead in its tracks for + * three whole seconds after we've gone multi-user is really + * bad manners. + */ + my_autoneg_xmit(sc); + DELAY(5000000); + break; + case MY_FLAG_SCHEDDELAY: + /* + * Wait for the transmitter to go idle before starting an + * autoneg session, otherwise my_start() may clobber our + * timeout, and we don't want to allow transmission during an + * autoneg session since that can screw it up. + */ + if (sc->my_cdata.my_tx_head != NULL) { + sc->my_want_auto = 1; + MY_UNLOCK(sc); + return; + } + my_autoneg_xmit(sc); + ifp->if_timer = 5; + sc->my_autoneg = 1; + sc->my_want_auto = 0; + MY_UNLOCK(sc); + return; + case MY_FLAG_DELAYTIMEO: + ifp->if_timer = 0; + sc->my_autoneg = 0; + break; + default: + printf("my%d: invalid autoneg flag: %d\n", sc->my_unit, flag); + MY_UNLOCK(sc); + return; + } + + if (my_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { + if (verbose) + printf("my%d: autoneg complete, ", sc->my_unit); + phy_sts = my_phy_readreg(sc, PHY_BMSR); + } else { + if (verbose) + printf("my%d: autoneg not complete, ", sc->my_unit); + } + + media = my_phy_readreg(sc, PHY_BMCR); + + /* Link is good. Report modes and set duplex mode. */ + if (my_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { + if (verbose) + printf("my%d: link status good. ", sc->my_unit); + advert = my_phy_readreg(sc, PHY_ANAR); + ability = my_phy_readreg(sc, PHY_LPAR); + if ((sc->my_pinfo->my_vid == MarvellPHYID0) || + (sc->my_pinfo->my_vid == LevelOnePHYID0)) { + ability2 = my_phy_readreg(sc, PHY_1000SR); + if (ability2 & PHY_1000SR_1000BTXFULL) { + advert = 0; + ability = 0; + /* + * this version did not support 1000M, + * ifm->ifm_media = + * IFM_ETHER|IFM_1000_TX|IFM_FDX; + */ + ifm->ifm_media = + IFM_ETHER | IFM_100_TX | IFM_FDX; + media &= ~PHY_BMCR_SPEEDSEL; + media |= PHY_BMCR_1000; + media |= PHY_BMCR_DUPLEX; + printf("(full-duplex, 1000Mbps)\n"); + } else if (ability2 & PHY_1000SR_1000BTXHALF) { + advert = 0; + ability = 0; + /* + * this version did not support 1000M, + * ifm->ifm_media = IFM_ETHER|IFM_1000_TX; + */ + ifm->ifm_media = IFM_ETHER | IFM_100_TX; + media &= ~PHY_BMCR_SPEEDSEL; + media &= ~PHY_BMCR_DUPLEX; + media |= PHY_BMCR_1000; + printf("(half-duplex, 1000Mbps)\n"); + } + } + if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { + ifm->ifm_media = IFM_ETHER | IFM_100_T4; + media |= PHY_BMCR_SPEEDSEL; + media &= ~PHY_BMCR_DUPLEX; + printf("(100baseT4)\n"); + } else if (advert & PHY_ANAR_100BTXFULL && + ability & PHY_ANAR_100BTXFULL) { + ifm->ifm_media = IFM_ETHER | IFM_100_TX | IFM_FDX; + media |= PHY_BMCR_SPEEDSEL; + media |= PHY_BMCR_DUPLEX; + printf("(full-duplex, 100Mbps)\n"); + } else if (advert & PHY_ANAR_100BTXHALF && + ability & PHY_ANAR_100BTXHALF) { + ifm->ifm_media = IFM_ETHER | IFM_100_TX | IFM_HDX; + media |= PHY_BMCR_SPEEDSEL; + media &= ~PHY_BMCR_DUPLEX; + printf("(half-duplex, 100Mbps)\n"); + } else if (advert & PHY_ANAR_10BTFULL && + ability & PHY_ANAR_10BTFULL) { + ifm->ifm_media = IFM_ETHER | IFM_10_T | IFM_FDX; + media &= ~PHY_BMCR_SPEEDSEL; + media |= PHY_BMCR_DUPLEX; + printf("(full-duplex, 10Mbps)\n"); + } else if (advert) { + ifm->ifm_media = IFM_ETHER | IFM_10_T | IFM_HDX; + media &= ~PHY_BMCR_SPEEDSEL; + media &= ~PHY_BMCR_DUPLEX; + printf("(half-duplex, 10Mbps)\n"); + } + media &= ~PHY_BMCR_AUTONEGENBL; + + /* Set ASIC's duplex mode to match the PHY. */ + my_phy_writereg(sc, PHY_BMCR, media); + my_setcfg(sc, media); + } else { + if (verbose) + printf("my%d: no carrier\n", sc->my_unit); + } + + my_init(sc); + if (sc->my_tx_pend) { + sc->my_autoneg = 0; + sc->my_tx_pend = 0; + my_start(ifp); + } + MY_UNLOCK(sc); + return; +} + +/* + * To get PHY ability. + */ +static void +my_getmode_mii(struct my_softc * sc) +{ + u_int16_t bmsr; + struct ifnet *ifp; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + bmsr = my_phy_readreg(sc, PHY_BMSR); + if (bootverbose) + printf("my%d: PHY status word: %x\n", sc->my_unit, bmsr); + + /* fallback */ + sc->ifmedia.ifm_media = IFM_ETHER | IFM_10_T | IFM_HDX; + + if (bmsr & PHY_BMSR_10BTHALF) { + if (bootverbose) + printf("my%d: 10Mbps half-duplex mode supported\n", + sc->my_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, + 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T, 0, NULL); + } + if (bmsr & PHY_BMSR_10BTFULL) { + if (bootverbose) + printf("my%d: 10Mbps full-duplex mode supported\n", + sc->my_unit); + + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, + 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_10_T | IFM_FDX; + } + if (bmsr & PHY_BMSR_100BTXHALF) { + if (bootverbose) + printf("my%d: 100Mbps half-duplex mode supported\n", + sc->my_unit); + ifp->if_baudrate = 100000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, + 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_100_TX | IFM_HDX; + } + if (bmsr & PHY_BMSR_100BTXFULL) { + if (bootverbose) + printf("my%d: 100Mbps full-duplex mode supported\n", + sc->my_unit); + ifp->if_baudrate = 100000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, + 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_100_TX | IFM_FDX; + } + /* Some also support 100BaseT4. */ + if (bmsr & PHY_BMSR_100BT4) { + if (bootverbose) + printf("my%d: 100baseT4 mode supported\n", sc->my_unit); + ifp->if_baudrate = 100000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_T4, 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_100_T4; +#ifdef FORCE_AUTONEG_TFOUR + if (bootverbose) + printf("my%d: forcing on autoneg support for BT4\n", + sc->my_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_AUTO, 0 NULL): + sc->ifmedia.ifm_media = IFM_ETHER | IFM_AUTO; +#endif + } +#if 0 + /* this version did not support 1000M, */ + if (sc->my_pinfo->my_vid == MarvellPHYID0) { + if (bootverbose) + printf("my%d: 1000Mbps half-duplex mode supported\n", + sc->my_unit); + + ifp->if_baudrate = 1000000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_1000_TX, 0, NULL); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_1000_TX | IFM_HDX, + 0, NULL); + if (bootverbose) + printf("my%d: 1000Mbps full-duplex mode supported\n", + sc->my_unit); + ifp->if_baudrate = 1000000000; + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_1000_TX | IFM_FDX, + 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_1000_TX | IFM_FDX; + } +#endif + if (bmsr & PHY_BMSR_CANAUTONEG) { + if (bootverbose) + printf("my%d: autoneg supported\n", sc->my_unit); + ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); + sc->ifmedia.ifm_media = IFM_ETHER | IFM_AUTO; + } + MY_UNLOCK(sc); + return; +} + +/* + * Set speed and duplex mode. + */ +static void +my_setmode_mii(struct my_softc * sc, int media) +{ + u_int16_t bmcr; + struct ifnet *ifp; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + /* + * If an autoneg session is in progress, stop it. + */ + if (sc->my_autoneg) { + printf("my%d: canceling autoneg session\n", sc->my_unit); + ifp->if_timer = sc->my_autoneg = sc->my_want_auto = 0; + bmcr = my_phy_readreg(sc, PHY_BMCR); + bmcr &= ~PHY_BMCR_AUTONEGENBL; + my_phy_writereg(sc, PHY_BMCR, bmcr); + } + printf("my%d: selecting MII, ", sc->my_unit); + bmcr = my_phy_readreg(sc, PHY_BMCR); + bmcr &= ~(PHY_BMCR_AUTONEGENBL | PHY_BMCR_SPEEDSEL | PHY_BMCR_1000 | + PHY_BMCR_DUPLEX | PHY_BMCR_LOOPBK); + /* + * this version did not support 1000M, + */ +#if 0 + if (IFM_SUBTYPE(media) == + IFM_1000_TX) { + printf("1000Mbps/T4, half-duplex\n"); + bmcr &= ~PHY_BMCR_SPEEDSEL; + bmcr &= ~PHY_BMCR_DUPLEX; + bmcr |= PHY_BMCR_1000; + } +#endif + if (IFM_SUBTYPE(media) == IFM_100_T4) { + printf("100Mbps/T4, half-duplex\n"); + bmcr |= PHY_BMCR_SPEEDSEL; + bmcr &= ~PHY_BMCR_DUPLEX; + } + if (IFM_SUBTYPE(media) == IFM_100_TX) { + printf("100Mbps, "); + bmcr |= PHY_BMCR_SPEEDSEL; + } + if (IFM_SUBTYPE(media) == IFM_10_T) { + printf("10Mbps, "); + bmcr &= ~PHY_BMCR_SPEEDSEL; + } + if ((media & IFM_GMASK) == IFM_FDX) { + printf("full duplex\n"); + bmcr |= PHY_BMCR_DUPLEX; + } else { + printf("half duplex\n"); + bmcr &= ~PHY_BMCR_DUPLEX; + } + my_phy_writereg(sc, PHY_BMCR, bmcr); + my_setcfg(sc, bmcr); + MY_UNLOCK(sc); + return; +} + +/* + * The Myson manual states that in order to fiddle with the 'full-duplex' and + * '100Mbps' bits in the netconfig register, we first have to put the + * transmit and/or receive logic in the idle state. + */ +static void +my_setcfg(struct my_softc * sc, int bmcr) +{ + int i, restart = 0; + + MY_LOCK(sc); + if (CSR_READ_4(sc, MY_TCRRCR) & (MY_TE | MY_RE)) { + restart = 1; + MY_CLRBIT(sc, MY_TCRRCR, (MY_TE | MY_RE)); + for (i = 0; i < MY_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, MY_TCRRCR) & + (MY_TXRUN | MY_RXRUN))) + break; + } + if (i == MY_TIMEOUT) + printf("my%d: failed to force tx and rx to idle \n", + sc->my_unit); + } + MY_CLRBIT(sc, MY_TCRRCR, MY_PS1000); + MY_CLRBIT(sc, MY_TCRRCR, MY_PS10); + if (bmcr & PHY_BMCR_1000) + MY_SETBIT(sc, MY_TCRRCR, MY_PS1000); + else if (!(bmcr & PHY_BMCR_SPEEDSEL)) + MY_SETBIT(sc, MY_TCRRCR, MY_PS10); + if (bmcr & PHY_BMCR_DUPLEX) + MY_SETBIT(sc, MY_TCRRCR, MY_FD); + else + MY_CLRBIT(sc, MY_TCRRCR, MY_FD); + if (restart) + MY_SETBIT(sc, MY_TCRRCR, MY_TE | MY_RE); + MY_UNLOCK(sc); + return; +} + +static void +my_reset(struct my_softc * sc) +{ + register int i; + + MY_LOCK(sc); + MY_SETBIT(sc, MY_BCR, MY_SWR); + for (i = 0; i < MY_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, MY_BCR) & MY_SWR)) + break; + } + if (i == MY_TIMEOUT) + printf("m0x%d: reset never completed!\n", sc->my_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + MY_UNLOCK(sc); + return; +} + +/* + * Probe for a Myson chip. Check the PCI vendor and device IDs against our + * list and return a device name if we find a match. + */ +static int +my_probe(device_t dev) +{ + struct my_type *t; + + t = my_devs; + while (t->my_name != NULL) { + if ((pci_get_vendor(dev) == t->my_vid) && + (pci_get_device(dev) == t->my_did)) { + device_set_desc(dev, t->my_name); + my_info_tmp = t; + return (0); + } + t++; + } + return (ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia setup and + * ethernet/BPF attach. + */ +static int +my_attach(device_t dev) +{ + int s, i; + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command, iobase; + struct my_softc *sc; + struct ifnet *ifp; + int media = IFM_ETHER | IFM_100_TX | IFM_FDX; + unsigned int round; + caddr_t roundptr; + struct my_type *p; + u_int16_t phy_vid, phy_did, phy_sts = 0; + int rid, unit, error = 0; + + s = splimp(); + sc = device_get_softc(dev); + unit = device_get_unit(dev); + if (sc == NULL) { + printf("my%d: no memory for softc struct!\n", unit); + error = ENXIO; + goto fail; + + } + bzero(sc, sizeof(struct my_softc)); + mtx_init(&sc->my_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE); + MY_LOCK(sc); + + /* + * Map control/status registers. + */ + /* + * command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); command + * |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + * pci_write_config(dev, PCI_COMMAND_STATUS_REG, command & + * 0x000000ff, 4); command = pci_read_config(dev, + * PCI_COMMAND_STATUS_REG, 4); + */ + 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 & 0x000000ff, 4); + command = pci_read_config(dev, PCIR_COMMAND, 4); + + if (my_info_tmp->my_did == MTD800ID) { + iobase = pci_read_config(dev, MY_PCI_LOIO, 4); + if (iobase & 0x300) + MY_USEIOSPACE = 0; + } + if (MY_USEIOSPACE) { + if (!(command & PCIM_CMD_PORTEN)) { + printf("my%d: failed to enable I/O ports!\n", unit); + free(sc, M_DEVBUF); + error = ENXIO; + goto fail; + } +#if 0 + if (!pci_map_port(config_id, MY_PCI_LOIO, + (u_int16_t *)&(sc->my_bhandle))) { + printf ("my%d: couldn't map ports\n", + unit); error = ENXIO; goto fail; + } + + sc->my_btag = I386_BUS_SPACE_IO; +#endif + } else { + if (!(command & PCIM_CMD_MEMEN)) { + printf("my%d: failed to enable memory mapping!\n", + unit); + error = ENXIO; + goto fail; + } +#if 0 + if (!pci_map_mem(config_id, MY_PCI_LOMEM, &vbase, &pbase)) { + printf ("my%d: couldn't map memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->my_btag = I386_BUS_SPACE_MEM; sc->my_bhandle = vbase; +#endif + } + + rid = MY_RID; + sc->my_res = bus_alloc_resource(dev, MY_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->my_res == NULL) { + printf("my%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + sc->my_btag = rman_get_bustag(sc->my_res); + sc->my_bhandle = rman_get_bushandle(sc->my_res); + + rid = 0; + sc->my_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->my_irq == NULL) { + printf("my%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, MY_RES, MY_RID, sc->my_res); + error = ENXIO; + goto fail; + } + error = bus_setup_intr(dev, sc->my_irq, INTR_TYPE_NET, + my_intr, sc, &sc->my_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->my_irq); + bus_release_resource(dev, MY_RES, MY_RID, sc->my_res); + printf("my%d: couldn't set up irq\n", unit); + goto fail; + } + callout_handle_init(&sc->my_stat_ch); + + sc->my_info = my_info_tmp; + + /* Reset the adapter. */ + my_reset(sc); + + /* + * Get station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + eaddr[i] = CSR_READ_1(sc, MY_PAR0 + i); + + /* + * A Myson chip was detected. Inform the world. + */ + printf("my%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->my_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->my_ldata_ptr = malloc(sizeof(struct my_list_data) + 8, + M_DEVBUF, M_NOWAIT); + if (sc->my_ldata_ptr == NULL) { + free(sc, M_DEVBUF); + printf("my%d: no memory for list buffers!\n", unit); + error = ENXIO; + goto fail; + } + sc->my_ldata = (struct my_list_data *) sc->my_ldata_ptr; + round = (unsigned int)sc->my_ldata_ptr & 0xF; + roundptr = sc->my_ldata_ptr; + for (i = 0; i < 8; i++) { + if (round % 8) { + round++; + roundptr++; + } else + break; + } + sc->my_ldata = (struct my_list_data *) roundptr; + bzero(sc->my_ldata, sizeof(struct my_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "my"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = my_ioctl; + ifp->if_output = ether_output; + ifp->if_start = my_start; + ifp->if_watchdog = my_watchdog; + ifp->if_init = my_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + if (sc->my_info->my_did == MTD803ID) + sc->my_pinfo = my_phys; + else { + if (bootverbose) + printf("my%d: probing for a PHY\n", sc->my_unit); + for (i = MY_PHYADDR_MIN; i < MY_PHYADDR_MAX + 1; i++) { + if (bootverbose) + printf("my%d: checking address: %d\n", + sc->my_unit, i); + sc->my_phy_addr = i; + phy_sts = my_phy_readreg(sc, PHY_BMSR); + if ((phy_sts != 0) && (phy_sts != 0xffff)) + break; + else + phy_sts = 0; + } + if (phy_sts) { + phy_vid = my_phy_readreg(sc, PHY_VENID); + phy_did = my_phy_readreg(sc, PHY_DEVID); + if (bootverbose) { + printf("my%d: found PHY at address %d, ", + sc->my_unit, sc->my_phy_addr); + printf("vendor id: %x device id: %x\n", + phy_vid, phy_did); + } + p = my_phys; + while (p->my_vid) { + if (phy_vid == p->my_vid) { + sc->my_pinfo = p; + break; + } + p++; + } + if (sc->my_pinfo == NULL) + sc->my_pinfo = &my_phys[PHY_UNKNOWN]; + if (bootverbose) + printf("my%d: PHY type: %s\n", + sc->my_unit, sc->my_pinfo->my_name); + } else { + printf("my%d: MII without any phy!\n", sc->my_unit); + error = ENXIO; + goto fail; + } + } + + /* Do ifmedia setup. */ + ifmedia_init(&sc->ifmedia, 0, my_ifmedia_upd, my_ifmedia_sts); + my_getmode_mii(sc); + my_autoneg_mii(sc, MY_FLAG_FORCEDELAY, 1); + media = sc->ifmedia.ifm_media; + my_stop(sc); + ifmedia_set(&sc->ifmedia, media); + + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); + +#if 0 + at_shutdown(my_shutdown, sc, SHUTDOWN_POST_SYNC); + shutdownhook_establish(my_shutdown, sc); +#endif + + MY_UNLOCK(sc); + return (0); + +fail: + MY_UNLOCK(sc); + mtx_destroy(&sc->my_mtx); + splx(s); + return (error); +} + +static int +my_detach(device_t dev) +{ + struct my_softc *sc; + struct ifnet *ifp; + int s; + + s = splimp(); + sc = device_get_softc(dev); + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + my_stop(sc); + + /* + * bus_generic_detach(dev); device_delete_child(dev, sc->rl_miibus); + */ + bus_teardown_intr(dev, sc->my_irq, sc->my_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->my_irq); + bus_release_resource(dev, MY_RES, MY_RID, sc->my_res); + /* + * contigfree(sc->my_cdata.my_rx_buf, MY_RXBUFLEN + 32, M_DEVBUF); + */ + free(sc, M_DEVBUF); + MY_UNLOCK(sc); + splx(s); + mtx_destroy(&sc->my_mtx); + return (0); +} + + +/* + * Initialize the transmit descriptors. + */ +static int +my_list_tx_init(struct my_softc * sc) +{ + struct my_chain_data *cd; + struct my_list_data *ld; + int i; + + MY_LOCK(sc); + cd = &sc->my_cdata; + ld = sc->my_ldata; + for (i = 0; i < MY_TX_LIST_CNT; i++) { + cd->my_tx_chain[i].my_ptr = &ld->my_tx_list[i]; + if (i == (MY_TX_LIST_CNT - 1)) + cd->my_tx_chain[i].my_nextdesc = &cd->my_tx_chain[0]; + else + cd->my_tx_chain[i].my_nextdesc = + &cd->my_tx_chain[i + 1]; + } + cd->my_tx_free = &cd->my_tx_chain[0]; + cd->my_tx_tail = cd->my_tx_head = NULL; + MY_UNLOCK(sc); + return (0); +} + +/* + * Initialize the RX descriptors and allocate mbufs for them. Note that we + * arrange the descriptors in a closed ring, so that the last descriptor + * points back to the first. + */ +static int +my_list_rx_init(struct my_softc * sc) +{ + struct my_chain_data *cd; + struct my_list_data *ld; + int i; + + MY_LOCK(sc); + cd = &sc->my_cdata; + ld = sc->my_ldata; + for (i = 0; i < MY_RX_LIST_CNT; i++) { + cd->my_rx_chain[i].my_ptr = + (struct my_desc *) & ld->my_rx_list[i]; + if (my_newbuf(sc, &cd->my_rx_chain[i]) == ENOBUFS) + return (ENOBUFS); + if (i == (MY_RX_LIST_CNT - 1)) { + cd->my_rx_chain[i].my_nextdesc = &cd->my_rx_chain[0]; + ld->my_rx_list[i].my_next = vtophys(&ld->my_rx_list[0]); + } else { + cd->my_rx_chain[i].my_nextdesc = + &cd->my_rx_chain[i + 1]; + ld->my_rx_list[i].my_next = + vtophys(&ld->my_rx_list[i + 1]); + } + } + cd->my_rx_head = &cd->my_rx_chain[0]; + MY_UNLOCK(sc); + return (0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int +my_newbuf(struct my_softc * sc, struct my_chain_onefrag * c) +{ + struct mbuf *m_new = NULL; + + MY_LOCK(sc); + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("my%d: no memory for rx list -- packet dropped!\n", + sc->my_unit); + return (ENOBUFS); + } + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("my%d: no memory for rx list -- packet dropped!\n", + sc->my_unit); + m_freem(m_new); + return (ENOBUFS); + } + c->my_mbuf = m_new; + c->my_ptr->my_data = vtophys(mtod(m_new, caddr_t)); + c->my_ptr->my_ctl = (MCLBYTES - 1) << MY_RBSShift; + c->my_ptr->my_status = MY_OWNByNIC; + MY_UNLOCK(sc); + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to the higher + * level protocols. + */ +static void +my_rxeof(struct my_softc * sc) +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct my_chain_onefrag *cur_rx; + int total_len = 0; + u_int32_t rxstat; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + while (!((rxstat = sc->my_cdata.my_rx_head->my_ptr->my_status) + & MY_OWNByNIC)) { + cur_rx = sc->my_cdata.my_rx_head; + sc->my_cdata.my_rx_head = cur_rx->my_nextdesc; + + if (rxstat & MY_ES) { /* error summary: give up this rx pkt */ + ifp->if_ierrors++; + cur_rx->my_ptr->my_status = MY_OWNByNIC; + continue; + } + /* No errors; receive the packet. */ + total_len = (rxstat & MY_FLNGMASK) >> MY_FLNGShift; + total_len -= ETHER_CRC_LEN; + + if (total_len < MINCLSIZE) { + m = m_devget(mtod(cur_rx->my_mbuf, char *), + total_len, 0, ifp, NULL); + cur_rx->my_ptr->my_status = MY_OWNByNIC; + if (m == NULL) { + ifp->if_ierrors++; + continue; + } + } else { + m = cur_rx->my_mbuf; + /* + * Try to conjure up a new mbuf cluster. If that + * fails, it means we have an out of memory condition + * and should leave the buffer in place and continue. + * This will result in a lost packet, but there's + * little else we can do in this situation. + */ + if (my_newbuf(sc, cur_rx) == ENOBUFS) { + ifp->if_ierrors++; + cur_rx->my_ptr->my_status = MY_OWNByNIC; + continue; + } + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = total_len; + } + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); +#if NBPFILTER > 0 + /* + * Handle BPF listeners. Let the BPF user see the packet, but + * don't pass it up to the ether_input() layer unless it's a + * broadcast packet, multicast packet, matches our ethernet + * address or the interface is in promiscuous mode. + */ + if (ifp->if_bpf) { + bpf_mtap(ifp, m); + if (ifp->if_flags & IFF_PROMISC && + (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0)) { + m_freem(m); + continue; + } + } +#endif + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + MY_UNLOCK(sc); + return; +} + + +/* + * A frame was downloaded to the chip. It's safe for us to clean up the list + * buffers. + */ +static void +my_txeof(struct my_softc * sc) +{ + struct my_chain *cur_tx; + struct ifnet *ifp; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + /* Clear the timeout timer. */ + ifp->if_timer = 0; + if (sc->my_cdata.my_tx_head == NULL) + return; + /* + * Go through our tx list and free mbufs for those frames that have + * been transmitted. + */ + while (sc->my_cdata.my_tx_head->my_mbuf != NULL) { + u_int32_t txstat; + + cur_tx = sc->my_cdata.my_tx_head; + txstat = MY_TXSTATUS(cur_tx); + if ((txstat & MY_OWNByNIC) || txstat == MY_UNSENT) + break; + if (!(CSR_READ_4(sc, MY_TCRRCR) & MY_Enhanced)) { + if (txstat & MY_TXERR) { + ifp->if_oerrors++; + if (txstat & MY_EC) /* excessive collision */ + ifp->if_collisions++; + if (txstat & MY_LC) /* late collision */ + ifp->if_collisions++; + } + ifp->if_collisions += (txstat & MY_NCRMASK) >> + MY_NCRShift; + } + ifp->if_opackets++; + m_freem(cur_tx->my_mbuf); + cur_tx->my_mbuf = NULL; + if (sc->my_cdata.my_tx_head == sc->my_cdata.my_tx_tail) { + sc->my_cdata.my_tx_head = NULL; + sc->my_cdata.my_tx_tail = NULL; + break; + } + sc->my_cdata.my_tx_head = cur_tx->my_nextdesc; + } + if (CSR_READ_4(sc, MY_TCRRCR) & MY_Enhanced) { + ifp->if_collisions += (CSR_READ_4(sc, MY_TSR) & MY_NCRMask); + } + MY_UNLOCK(sc); + return; +} + +/* + * TX 'end of channel' interrupt handler. + */ +static void +my_txeoc(struct my_softc * sc) +{ + struct ifnet *ifp; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + if (sc->my_cdata.my_tx_head == NULL) { + ifp->if_flags &= ~IFF_OACTIVE; + sc->my_cdata.my_tx_tail = NULL; + if (sc->my_want_auto) + my_autoneg_mii(sc, MY_FLAG_SCHEDDELAY, 1); + } else { + if (MY_TXOWN(sc->my_cdata.my_tx_head) == MY_UNSENT) { + MY_TXOWN(sc->my_cdata.my_tx_head) = MY_OWNByNIC; + ifp->if_timer = 5; + CSR_WRITE_4(sc, MY_TXPDR, 0xFFFFFFFF); + } + } + MY_UNLOCK(sc); + return; +} + +static void +my_intr(void *arg) +{ + struct my_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + if (!(ifp->if_flags & IFF_UP)) { + MY_UNLOCK(sc); + return; + } + /* Disable interrupts. */ + CSR_WRITE_4(sc, MY_IMR, 0x00000000); + + for (;;) { + status = CSR_READ_4(sc, MY_ISR); + status &= MY_INTRS; + if (status) + CSR_WRITE_4(sc, MY_ISR, status); + else + break; + + if (status & MY_RI) /* receive interrupt */ + my_rxeof(sc); + + if ((status & MY_RBU) || (status & MY_RxErr)) { + /* rx buffer unavailable or rx error */ + ifp->if_ierrors++; +#ifdef foo + my_stop(sc); + my_reset(sc); + my_init(sc); +#endif + } + if (status & MY_TI) /* tx interrupt */ + my_txeof(sc); + if (status & MY_ETI) /* tx early interrupt */ + my_txeof(sc); + if (status & MY_TBU) /* tx buffer unavailable */ + my_txeoc(sc); + /* + * 90/1/18 delete if (status & MY_FBE) { my_reset(sc); + * my_init(sc); } + */ + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, MY_IMR, MY_INTRS); + if (ifp->if_snd.ifq_head != NULL) + my_start(ifp); + MY_UNLOCK(sc); + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +my_encap(struct my_softc * sc, struct my_chain * c, struct mbuf * m_head) +{ + struct my_desc *f = NULL; + int total_len; + struct mbuf *m, *m_new = NULL; + + MY_LOCK(sc); + /* calculate the total tx pkt length */ + total_len = 0; + for (m = m_head; m != NULL; m = m->m_next) + total_len += m->m_len; + /* + * 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; + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("my%d: no memory for tx list", sc->my_unit); + return (1); + } + if (m_head->m_pkthdr.len > MHLEN) { + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + printf("my%d: no memory for tx list", sc->my_unit); + return (1); + } + } + m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); + m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; + m_freem(m_head); + m_head = m_new; + f = &c->my_ptr->my_frag[0]; + f->my_status = 0; + f->my_data = vtophys(mtod(m_new, caddr_t)); + total_len = m_new->m_len; + f->my_ctl = MY_TXFD | MY_TXLD | MY_CRCEnable | MY_PADEnable; + f->my_ctl |= total_len << MY_PKTShift; /* pkt size */ + f->my_ctl |= total_len; /* buffer size */ + /* 89/12/29 add, for mtd891 */ + if (sc->my_info->my_did == MTD891ID) + f->my_ctl |= MY_ETIControl | MY_RetryTxLC; + c->my_mbuf = m_head; + c->my_lastdesc = 0; + MY_TXNEXT(c) = vtophys(&c->my_nextdesc->my_ptr->my_frag[0]); + MY_UNLOCK(sc); + 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 +my_start(struct ifnet * ifp) +{ + struct my_softc *sc; + struct mbuf *m_head = NULL; + struct my_chain *cur_tx = NULL, *start_tx; + + sc = ifp->if_softc; + MY_LOCK(sc); + if (sc->my_autoneg) { + sc->my_tx_pend = 1; + MY_UNLOCK(sc); + return; + } + /* + * Check for an available queue slot. If there are none, punt. + */ + if (sc->my_cdata.my_tx_free->my_mbuf != NULL) { + ifp->if_flags |= IFF_OACTIVE; + MY_UNLOCK(sc); + return; + } + start_tx = sc->my_cdata.my_tx_free; + while (sc->my_cdata.my_tx_free->my_mbuf == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pick a descriptor off the free list. */ + cur_tx = sc->my_cdata.my_tx_free; + sc->my_cdata.my_tx_free = cur_tx->my_nextdesc; + + /* Pack the data into the descriptor. */ + my_encap(sc, cur_tx, m_head); + + if (cur_tx != start_tx) + MY_TXOWN(cur_tx) = MY_OWNByNIC; +#if NBPFILTER > 0 + /* + * If there's a BPF listener, bounce a copy of this frame to + * him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, cur_tx->my_mbuf); +#endif + } + /* + * If there are no packets queued, bail. + */ + if (cur_tx == NULL) { + MY_UNLOCK(sc); + return; + } + /* + * Place the request for the upload interrupt in the last descriptor + * in the chain. This way, if we're chaining several packets at once, + * we'll only get an interupt once for the whole chain rather than + * once for each packet. + */ + MY_TXCTL(cur_tx) |= MY_TXIC; + cur_tx->my_ptr->my_frag[0].my_ctl |= MY_TXIC; + sc->my_cdata.my_tx_tail = cur_tx; + if (sc->my_cdata.my_tx_head == NULL) + sc->my_cdata.my_tx_head = start_tx; + MY_TXOWN(start_tx) = MY_OWNByNIC; + CSR_WRITE_4(sc, MY_TXPDR, 0xFFFFFFFF); /* tx polling demand */ + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + MY_UNLOCK(sc); + return; +} + +static void +my_init(void *xsc) +{ + struct my_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + u_int16_t phy_bmcr = 0; + + MY_LOCK(sc); + if (sc->my_autoneg) { + MY_UNLOCK(sc); + return; + } + s = splimp(); + if (sc->my_pinfo != NULL) + phy_bmcr = my_phy_readreg(sc, PHY_BMCR); + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + my_stop(sc); + my_reset(sc); + + /* + * Set cache alignment and burst length. + */ + /* + * 89/9/1 modify, CSR_WRITE_4(sc, MY_BCR, MY_RPBLE512); + * CSR_WRITE_4(sc, MY_TCRRCR, MY_TFTSF); + */ + CSR_WRITE_4(sc, MY_BCR, MY_PBL8); + CSR_WRITE_4(sc, MY_TCRRCR, MY_TFTSF | MY_RBLEN | MY_RPBLE512); + /* + * 89/12/29 add, for mtd891, + */ + if (sc->my_info->my_did == MTD891ID) { + MY_SETBIT(sc, MY_BCR, MY_PROG); + MY_SETBIT(sc, MY_TCRRCR, MY_Enhanced); + } + my_setcfg(sc, phy_bmcr); + /* Init circular RX list. */ + if (my_list_rx_init(sc) == ENOBUFS) { + printf("my%d: init failed: no memory for rx buffers\n", + sc->my_unit); + my_stop(sc); + (void)splx(s); + MY_UNLOCK(sc); + return; + } + /* Init TX descriptors. */ + my_list_tx_init(sc); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + MY_SETBIT(sc, MY_TCRRCR, MY_PROM); + else + MY_CLRBIT(sc, MY_TCRRCR, MY_PROM); + + /* + * Set capture broadcast bit to capture broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + MY_SETBIT(sc, MY_TCRRCR, MY_AB); + else + MY_CLRBIT(sc, MY_TCRRCR, MY_AB); + + /* + * Program the multicast filter, if necessary. + */ + my_setmulti(sc); + + /* + * Load the address of the RX list. + */ + MY_CLRBIT(sc, MY_TCRRCR, MY_RE); + CSR_WRITE_4(sc, MY_RXLBA, vtophys(&sc->my_ldata->my_rx_list[0])); + + /* + * Enable interrupts. + */ + CSR_WRITE_4(sc, MY_IMR, MY_INTRS); + CSR_WRITE_4(sc, MY_ISR, 0xFFFFFFFF); + + /* Enable receiver and transmitter. */ + MY_SETBIT(sc, MY_TCRRCR, MY_RE); + MY_CLRBIT(sc, MY_TCRRCR, MY_TE); + CSR_WRITE_4(sc, MY_TXLBA, vtophys(&sc->my_ldata->my_tx_list[0])); + MY_SETBIT(sc, MY_TCRRCR, MY_TE); + + /* Restore state of BMCR */ + if (sc->my_pinfo != NULL) + my_phy_writereg(sc, PHY_BMCR, phy_bmcr); + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + (void)splx(s); + MY_UNLOCK(sc); + return; +} + +/* + * Set media options. + */ + +static int +my_ifmedia_upd(struct ifnet * ifp) +{ + struct my_softc *sc; + struct ifmedia *ifm; + + sc = ifp->if_softc; + MY_LOCK(sc); + ifm = &sc->ifmedia; + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) { + MY_UNLOCK(sc); + return (EINVAL); + } + if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) + my_autoneg_mii(sc, MY_FLAG_SCHEDDELAY, 1); + else + my_setmode_mii(sc, ifm->ifm_media); + MY_UNLOCK(sc); + return (0); +} + +/* + * Report current media status. + */ + +static void +my_ifmedia_sts(struct ifnet * ifp, struct ifmediareq * ifmr) +{ + struct my_softc *sc; + u_int16_t advert = 0, ability = 0; + + sc = ifp->if_softc; + MY_LOCK(sc); + ifmr->ifm_active = IFM_ETHER; + if (!(my_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { + /* + * this version did not support 1000M, if (my_phy_readreg(sc, + * PHY_BMCR) & PHY_BMCR_1000) ifmr->ifm_active = + * IFM_ETHER|IFM_1000TX; + */ + if (my_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) + ifmr->ifm_active = IFM_ETHER | IFM_100_TX; + else + ifmr->ifm_active = IFM_ETHER | IFM_10_T; + if (my_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) + ifmr->ifm_active |= IFM_FDX; + else + ifmr->ifm_active |= IFM_HDX; + + MY_UNLOCK(sc); + return; + } + ability = my_phy_readreg(sc, PHY_LPAR); + advert = my_phy_readreg(sc, PHY_ANAR); + + /* + * this version did not support 1000M, + */ +#if 0 + if (sc->my_pinfo->my_vid = MarvellPHYID0) { + ability2 = my_phy_readreg(sc, PHY_1000SR); + if (ability2 & PHY_1000SR_1000BTXFULL) { + advert = 0; + ability = 0; + ifmr->ifm_active = IFM_ETHER|IFM_1000_TX|IFM_FDX; + } else if (ability & PHY_1000SR_1000BTXHALF) { + advert = 0; + ability = 0; + ifmr->ifm_active = IFM_ETHER|IFM_1000_TX|IFM_HDX; + } + } +#endif + if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) + ifmr->ifm_active = IFM_ETHER | IFM_100_T4; + else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) + ifmr->ifm_active = IFM_ETHER | IFM_100_TX | IFM_FDX; + else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) + ifmr->ifm_active = IFM_ETHER | IFM_100_TX | IFM_HDX; + else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) + ifmr->ifm_active = IFM_ETHER | IFM_10_T | IFM_FDX; + else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) + ifmr->ifm_active = IFM_ETHER | IFM_10_T | IFM_HDX; + MY_UNLOCK(sc); + return; +} + +static int +my_ioctl(struct ifnet * ifp, u_long command, caddr_t data) +{ + struct my_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + + s = splimp(); + MY_LOCK(sc); + switch (command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + my_init(sc); + else if (ifp->if_flags & IFF_RUNNING) + my_stop(sc); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + my_setmulti(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + break; + default: + error = EINVAL; + break; + } + MY_UNLOCK(sc); + (void)splx(s); + return (error); +} + +static void +my_watchdog(struct ifnet * ifp) +{ + struct my_softc *sc; + + sc = ifp->if_softc; + MY_LOCK(sc); + if (sc->my_autoneg) { + my_autoneg_mii(sc, MY_FLAG_DELAYTIMEO, 1); + MY_UNLOCK(sc); + return; + } + ifp->if_oerrors++; + printf("my%d: watchdog timeout\n", sc->my_unit); + if (!(my_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) + printf("my%d: no carrier - transceiver cable problem?\n", + sc->my_unit); + my_stop(sc); + my_reset(sc); + my_init(sc); + if (ifp->if_snd.ifq_head != NULL) + my_start(ifp); + MY_LOCK(sc); + return; +} + + +/* + * Stop the adapter and free any mbufs allocated to the RX and TX lists. + */ +static void +my_stop(struct my_softc * sc) +{ + register int i; + struct ifnet *ifp; + + MY_LOCK(sc); + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + MY_CLRBIT(sc, MY_TCRRCR, (MY_RE | MY_TE)); + CSR_WRITE_4(sc, MY_IMR, 0x00000000); + CSR_WRITE_4(sc, MY_TXLBA, 0x00000000); + CSR_WRITE_4(sc, MY_RXLBA, 0x00000000); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < MY_RX_LIST_CNT; i++) { + if (sc->my_cdata.my_rx_chain[i].my_mbuf != NULL) { + m_freem(sc->my_cdata.my_rx_chain[i].my_mbuf); + sc->my_cdata.my_rx_chain[i].my_mbuf = NULL; + } + } + bzero((char *)&sc->my_ldata->my_rx_list, + sizeof(sc->my_ldata->my_rx_list)); + /* + * Free the TX list buffers. + */ + for (i = 0; i < MY_TX_LIST_CNT; i++) { + if (sc->my_cdata.my_tx_chain[i].my_mbuf != NULL) { + m_freem(sc->my_cdata.my_tx_chain[i].my_mbuf); + sc->my_cdata.my_tx_chain[i].my_mbuf = NULL; + } + } + bzero((char *)&sc->my_ldata->my_tx_list, + sizeof(sc->my_ldata->my_tx_list)); + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + MY_UNLOCK(sc); + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't get confused + * by errant DMAs when rebooting. + */ +static void +my_shutdown(device_t dev) +{ + struct my_softc *sc; + + sc = device_get_softc(dev); + my_stop(sc); + return; +} diff --git a/sys/dev/my/if_myreg.h b/sys/dev/my/if_myreg.h new file mode 100644 index 0000000..ccc7338 --- /dev/null +++ b/sys/dev/my/if_myreg.h @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2002 Myson Technology Inc. + * 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, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * Written by: yen_cw@myson.com.tw available at: http://www.myson.com.tw/ + * + * $FreeBSD$ + * + * Myson MTD80x register definitions. + * + */ +#define MY_PAR0 0x0 /* physical address 0-3 */ +#define MY_PAR1 0x04 /* physical address 4-5 */ +#define MY_MAR0 0x08 /* multicast address 0-3 */ +#define MY_MAR1 0x0C /* multicast address 4-7 */ +#define MY_FAR0 0x10 /* flow-control address 0-3 */ +#define MY_FAR1 0x14 /* flow-control address 4-5 */ +#define MY_TCRRCR 0x18 /* receive & transmit configuration */ +#define MY_BCR 0x1C /* bus command */ +#define MY_TXPDR 0x20 /* transmit polling demand */ +#define MY_RXPDR 0x24 /* receive polling demand */ +#define MY_RXCWP 0x28 /* receive current word pointer */ +#define MY_TXLBA 0x2C /* transmit list base address */ +#define MY_RXLBA 0x30 /* receive list base address */ +#define MY_ISR 0x34 /* interrupt status */ +#define MY_IMR 0x38 /* interrupt mask */ +#define MY_FTH 0x3C /* flow control high/low threshold */ +#define MY_MANAGEMENT 0x40 /* bootrom/eeprom and mii management */ +#define MY_TALLY 0x44 /* tally counters for crc and mpa */ +#define MY_TSR 0x48 /* tally counter for transmit status */ +#define MY_PHYBASE 0x4c + +/* + * Receive Configuration Register + */ +#define MY_RXRUN 0x00008000 /* receive running status */ +#define MY_EIEN 0x00004000 /* early interrupt enable */ +#define MY_RFCEN 0x00002000 /* receive flow control packet enable */ +#define MY_NDFA 0x00001000 /* not defined flow control address */ +#define MY_RBLEN 0x00000800 /* receive burst length enable */ +#define MY_RPBLE1 0x00000000 /* 1 word */ +#define MY_RPBLE4 0x00000100 /* 4 words */ +#define MY_RPBLE8 0x00000200 /* 8 words */ +#define MY_RPBLE16 0x00000300 /* 16 words */ +#define MY_RPBLE32 0x00000400 /* 32 words */ +#define MY_RPBLE64 0x00000500 /* 64 words */ +#define MY_RPBLE128 0x00000600 /* 128 words */ +#define MY_RPBLE512 0x00000700 /* 512 words */ +#define MY_PROM 0x000000080 /* promiscuous mode */ +#define MY_AB 0x000000040 /* accept broadcast */ +#define MY_AM 0x000000020 /* accept mutlicast */ +#define MY_ARP 0x000000008 /* receive runt pkt */ +#define MY_ALP 0x000000004 /* receive long pkt */ +#define MY_SEP 0x000000002 /* receive error pkt */ +#define MY_RE 0x000000001 /* receive enable */ + +/* + * Transmit Configuration Register + */ +#define MY_TXRUN 0x04000000 /* transmit running status */ +#define MY_Enhanced 0x02000000 /* transmit enhanced mode */ +#define MY_TFCEN 0x01000000 /* tx flow control packet enable */ +#define MY_TFT64 0x00000000 /* 64 bytes */ +#define MY_TFT32 0x00200000 /* 32 bytes */ +#define MY_TFT128 0x00400000 /* 128 bytes */ +#define MY_TFT256 0x00600000 /* 256 bytes */ +#define MY_TFT512 0x00800000 /* 512 bytes */ +#define MY_TFT768 0x00A00000 /* 768 bytes */ +#define MY_TFT1024 0x00C00000 /* 1024 bytes */ +#define MY_TFTSF 0x00E00000 /* store and forward */ +#define MY_FD 0x00100000 /* full duplex mode */ +#define MY_PS10 0x00080000 /* port speed is 10M */ +#define MY_TE 0x00040000 /* transmit enable */ +#define MY_PS1000 0x00010000 /* port speed is 1000M */ +/* + * Bus Command Register + */ +#define MY_PROG 0x00000200 /* programming */ +#define MY_RLE 0x00000100 /* read line command enable */ +#define MY_RME 0x00000080 /* read multiple command enable */ +#define MY_WIE 0x00000040 /* write and invalidate cmd enable */ +#define MY_PBL1 0x00000000 /* 1 dword */ +#define MY_PBL4 0x00000008 /* 4 dwords */ +#define MY_PBL8 0x00000010 /* 8 dwords */ +#define MY_PBL16 0x00000018 /* 16 dwords */ +#define MY_PBL32 0x00000020 /* 32 dwords */ +#define MY_PBL64 0x00000028 /* 64 dwords */ +#define MY_PBL128 0x00000030 /* 128 dwords */ +#define MY_PBL512 0x00000038 /* 512 dwords */ +#define MY_ABR 0x00000004 /* arbitration rule */ +#define MY_BLS 0x00000002 /* big/little endian select */ +#define MY_SWR 0x00000001 /* software reset */ + +/* + * Transmit Poll Demand Register + */ +#define MY_TxPollDemand 0x1 + +/* + * Receive Poll Demand Register + */ +#define MY_RxPollDemand 0x01 + +/* + * Interrupt Status Register + */ +#define MY_RFCON 0x00020000 /* receive flow control xon packet */ +#define MY_RFCOFF 0x00010000 /* receive flow control xoff packet */ +#define MY_LSCStatus 0x00008000 /* link status change */ +#define MY_ANCStatus 0x00004000 /* autonegotiation completed */ +#define MY_FBE 0x00002000 /* fatal bus error */ +#define MY_FBEMask 0x00001800 +#define MY_ParityErr 0x00000000 /* parity error */ +#define MY_MasterErr 0x00000800 /* master error */ +#define MY_TargetErr 0x00001000 /* target abort */ +#define MY_TUNF 0x00000400 /* transmit underflow */ +#define MY_ROVF 0x00000200 /* receive overflow */ +#define MY_ETI 0x00000100 /* transmit early int */ +#define MY_ERI 0x00000080 /* receive early int */ +#define MY_CNTOVF 0x00000040 /* counter overflow */ +#define MY_RBU 0x00000020 /* receive buffer unavailable */ +#define MY_TBU 0x00000010 /* transmit buffer unavilable */ +#define MY_TI 0x00000008 /* transmit interrupt */ +#define MY_RI 0x00000004 /* receive interrupt */ +#define MY_RxErr 0x00000002 /* receive error */ + +/* + * Interrupt Mask Register + */ +#define MY_MRFCON 0x00020000 /* receive flow control xon packet */ +#define MY_MRFCOFF 0x00010000 /* receive flow control xoff packet */ +#define MY_MLSCStatus 0x00008000 /* link status change */ +#define MY_MANCStatus 0x00004000 /* autonegotiation completed */ +#define MY_MFBE 0x00002000 /* fatal bus error */ +#define MY_MFBEMask 0x00001800 +#define MY_MTUNF 0x00000400 /* transmit underflow */ +#define MY_MROVF 0x00000200 /* receive overflow */ +#define MY_METI 0x00000100 /* transmit early int */ +#define MY_MERI 0x00000080 /* receive early int */ +#define MY_MCNTOVF 0x00000040 /* counter overflow */ +#define MY_MRBU 0x00000020 /* receive buffer unavailable */ +#define MY_MTBU 0x00000010 /* transmit buffer unavilable */ +#define MY_MTI 0x00000008 /* transmit interrupt */ +#define MY_MRI 0x00000004 /* receive interrupt */ +#define MY_MRxErr 0x00000002 /* receive error */ + +/* 90/1/18 delete */ +/* #define MY_INTRS MY_FBE|MY_MRBU|MY_TBU|MY_MTI|MY_MRI|MY_METI */ +#define MY_INTRS MY_MRBU|MY_TBU|MY_MTI|MY_MRI|MY_METI + +/* + * Flow Control High/Low Threshold Register + */ +#define MY_FCHTShift 16 /* flow control high threshold */ +#define MY_FCLTShift 0 /* flow control low threshold */ + +/* + * BootROM/EEPROM/MII Management Register + */ +#define MY_MASK_MIIR_MII_READ 0x00000000 +#define MY_MASK_MIIR_MII_WRITE 0x00000008 +#define MY_MASK_MIIR_MII_MDO 0x00000004 +#define MY_MASK_MIIR_MII_MDI 0x00000002 +#define MY_MASK_MIIR_MII_MDC 0x00000001 + +/* + * Tally Counter for CRC and MPA + */ +#define MY_TCOVF 0x80000000 /* crc tally counter overflow */ +#define MY_CRCMask 0x7fff0000 /* crc number: bit 16-30 */ +#define MY_CRCShift 16 +#define MY_TMOVF 0x00008000 /* mpa tally counter overflow */ +#define MY_MPAMask 0x00007fff /* mpa number: bit 0-14 */ +#define MY_MPAShift 0 + +/* + * Tally Counters for transmit status + */ +#define MY_AbortMask 0xff000000 /* transmit abort number */ +#define MY_AbortShift 24 +#define MY_LColMask 0x00ff0000 /* transmit late collisions */ +#define MY_LColShift 16 +#define MY_NCRMask 0x0000ffff /* transmit retry number */ +#define MY_NCRShift 0 + +/* + * Myson TX/RX descriptor structure. + */ + +struct my_desc { + u_int32_t my_status; + u_int32_t my_ctl; + u_int32_t my_data; + u_int32_t my_next; +}; + +/* + * for tx/rx descriptors + */ +#define MY_OWNByNIC 0x80000000 +#define MY_OWNByDriver 0x0 + +/* + * receive descriptor 0 + */ +#define MY_RXOWN 0x80000000 /* own bit */ +#define MY_FLNGMASK 0x0fff0000 /* frame length */ +#define MY_FLNGShift 16 +#define MY_MARSTATUS 0x00004000 /* multicast address received */ +#define MY_BARSTATUS 0x00002000 /* broadcast address received */ +#define MY_PHYSTATUS 0x00001000 /* physical address received */ +#define MY_RXFSD 0x00000800 /* first descriptor */ +#define MY_RXLSD 0x00000400 /* last descriptor */ +#define MY_ES 0x00000080 /* error summary */ +#define MY_RUNT 0x00000040 /* runt packet received */ +#define MY_LONG 0x00000020 /* long packet received */ +#define MY_FAE 0x00000010 /* frame align error */ +#define MY_CRC 0x00000008 /* crc error */ +#define MY_RXER 0x00000004 /* receive error */ +#define MY_RDES0CHECK 0x000078fc /* only check MAR, BAR, PHY, ES, RUNT, + LONG, FAE, CRC and RXER bits */ + +/* + * receive descriptor 1 + */ +#define MY_RXIC 0x00800000 /* interrupt control */ +#define MY_RBSMASK 0x000007ff /* receive buffer size */ +#define MY_RBSShift 0 + +/* + * transmit descriptor 0 + */ +#define MY_TXERR 0x00008000 /* transmit error */ +#define MY_JABTO 0x00004000 /* jabber timeout */ +#define MY_CSL 0x00002000 /* carrier sense lost */ +#define MY_LC 0x00001000 /* late collision */ +#define MY_EC 0x00000800 /* excessive collision */ +#define MY_UDF 0x00000400 /* fifo underflow */ +#define MY_DFR 0x00000200 /* deferred */ +#define MY_HF 0x00000100 /* heartbeat fail */ +#define MY_NCRMASK 0x000000ff /* collision retry count */ +#define MY_NCRShift 0 + +/* + * tx descriptor 1 + */ +#define MY_TXIC 0x80000000 /* interrupt control */ +#define MY_ETIControl 0x40000000 /* early transmit interrupt */ +#define MY_TXLD 0x20000000 /* last descriptor */ +#define MY_TXFD 0x10000000 /* first descriptor */ +#define MY_CRCDisable 0x00000000 /* crc control */ +#define MY_CRCEnable 0x08000000 +#define MY_PADDisable 0x00000000 /* padding control */ +#define MY_PADEnable 0x04000000 +#define MY_RetryTxLC 0x02000000 /* retry late collision */ +#define MY_PKTShift 11 /* transmit pkt size */ +#define MY_TBSMASK 0x000007ff +#define MY_TBSShift 0 /* transmit buffer size */ + +#define MY_MAXFRAGS 1 +#define MY_RX_LIST_CNT 64 +#define MY_TX_LIST_CNT 64 +#define MY_MIN_FRAMELEN 60 + +/* + * A transmit 'super descriptor' is actually MY_MAXFRAGS regular + * descriptors clumped together. The idea here is to emulate the + * multi-fragment descriptor layout found in devices such as the + * Texas Instruments ThunderLAN and 3Com boomerang and cylone chips. + * The advantage to using this scheme is that it avoids buffer copies. + * The disadvantage is that there's a certain amount of overhead due + * to the fact that each 'fragment' is 16 bytes long. In my tests, + * this limits top speed to about 10.5MB/sec. It should be more like + * 11.5MB/sec. However, the upshot is that you can achieve better + * results on slower machines: a Pentium 200 can pump out packets at + * same speed as a PII 400. + */ +struct my_txdesc { + struct my_desc my_frag[MY_MAXFRAGS]; +}; + +#define MY_TXSTATUS(x) x->my_ptr->my_frag[x->my_lastdesc].my_status +#define MY_TXCTL(x) x->my_ptr->my_frag[x->my_lastdesc].my_ctl +#define MY_TXDATA(x) x->my_ptr->my_frag[x->my_lastdesc].my_data +#define MY_TXNEXT(x) x->my_ptr->my_frag[x->my_lastdesc].my_next + +#define MY_TXOWN(x) x->my_ptr->my_frag[0].my_status + +#define MY_UNSENT 0x1234 + +struct my_list_data { + struct my_desc my_rx_list[MY_RX_LIST_CNT]; + struct my_txdesc my_tx_list[MY_TX_LIST_CNT]; +}; + +struct my_chain { + struct my_txdesc *my_ptr; + struct mbuf *my_mbuf; + struct my_chain *my_nextdesc; + u_int8_t my_lastdesc; +}; + +struct my_chain_onefrag { + struct my_desc *my_ptr; + struct mbuf *my_mbuf; + struct my_chain_onefrag *my_nextdesc; + u_int8_t my_rlast; +}; + +struct my_chain_data { + struct my_chain_onefrag my_rx_chain[MY_RX_LIST_CNT]; + struct my_chain my_tx_chain[MY_TX_LIST_CNT]; + + struct my_chain_onefrag *my_rx_head; + + struct my_chain *my_tx_head; + struct my_chain *my_tx_tail; + struct my_chain *my_tx_free; +}; + +struct my_type { + u_int16_t my_vid; + u_int16_t my_did; + char *my_name; +}; + +#define MY_FLAG_FORCEDELAY 1 +#define MY_FLAG_SCHEDDELAY 2 +#define MY_FLAG_DELAYTIMEO 3 + +struct my_softc { + struct arpcom arpcom; /* interface info */ + struct ifmedia ifmedia; /* media info */ + bus_space_handle_t my_bhandle; + bus_space_tag_t my_btag; + struct my_type *my_info; /* adapter info */ + struct my_type *my_pinfo; /* phy info */ + struct resource *my_res; + struct resource *my_irq; + void *my_intrhand; + u_int8_t my_unit; /* interface number */ + u_int8_t my_phy_addr; /* PHY address */ + u_int8_t my_tx_pend; /* TX pending */ + u_int8_t my_want_auto; + u_int8_t my_autoneg; + u_int16_t my_txthresh; + u_int8_t my_stats_no_timeout; + caddr_t my_ldata_ptr; + struct my_list_data *my_ldata; + struct my_chain_data my_cdata; + device_t my_miibus; + struct callout_handle my_stat_ch; +/* Add by Surfer 2001/12/2 */ + struct mtx my_mtx; + +}; + +/* Add by Surfer 2001/12/2 */ +#define MY_LOCK(_sc) mtx_lock(&(_sc)->my_mtx) +#define MY_UNLOCK(_sc) mtx_unlock(&(_sc)->my_mtx) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->my_btag, sc->my_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->my_btag, sc->my_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->my_btag, sc->my_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->my_btag, sc->my_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->my_btag, sc->my_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->my_btag, sc->my_bhandle, reg) + +#define MY_TIMEOUT 1000 + +/* + * General constants that are fun to know. + * + * MYSON PCI vendor ID + */ +#define MYSONVENDORID 0x1516 + +/* + * MYSON device IDs. + */ +#define MTD800ID 0x0800 +#define MTD803ID 0x0803 +#define MTD891ID 0x0891 + +/* + * ST+OP+PHYAD+REGAD+TA + */ +#define MY_OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ +#define MY_OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ + +/* + * Constansts for Myson PHY + */ +#define MysonPHYID0 0x0300 + +/* + * Constansts for Seeq 80225 PHY + */ +#define SeeqPHYID0 0x0016 + +#define SEEQ_MIIRegister18 18 +#define SEEQ_SPD_DET_100 0x80 +#define SEEQ_DPLX_DET_FULL 0x40 + +/* + * Constansts for Ahdoc 101 PHY + */ +#define AhdocPHYID0 0x0022 + +#define AHDOC_DiagnosticReg 18 +#define AHDOC_DPLX_FULL 0x0800 +#define AHDOC_Speed_100 0x0400 + +/* + * Constansts for Marvell 88E1000/88E1000S PHY and LevelOne PHY + */ +#define MarvellPHYID0 0x0141 +#define LevelOnePHYID0 0x0013 + +#define Marvell_SpecificStatus 17 +#define Marvell_Speed1000 0x8000 +#define Marvell_Speed100 0x4000 +#define Marvell_FullDuplex 0x2000 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. Note: some are only available on + * the 3c905B, in particular those that related to power management. + */ +#define MY_PCI_VENDOR_ID 0x00 +#define MY_PCI_DEVICE_ID 0x02 +#define MY_PCI_COMMAND 0x04 +#define MY_PCI_STATUS 0x06 +#define MY_PCI_CLASSCODE 0x09 +#define MY_PCI_LATENCY_TIMER 0x0D +#define MY_PCI_HEADER_TYPE 0x0E +#define MY_PCI_LOIO 0x10 +#define MY_PCI_LOMEM 0x14 +#define MY_PCI_BIOSROM 0x30 +#define MY_PCI_INTLINE 0x3C +#define MY_PCI_INTPIN 0x3D +#define MY_PCI_MINGNT 0x3E +#define MY_PCI_MINLAT 0x0F +#define MY_PCI_RESETOPT 0x48 +#define MY_PCI_EEPROM_DATA 0x4C + +#define PHY_UNKNOWN 3 + +#define MY_PHYADDR_MIN 0x00 +#define MY_PHYADDR_MAX 0x1F + +#define PHY_BMCR 0x00 +#define PHY_BMSR 0x01 +#define PHY_VENID 0x02 +#define PHY_DEVID 0x03 +#define PHY_ANAR 0x04 +#define PHY_LPAR 0x05 +#define PHY_ANEXP 0x06 +#define PHY_NPTR 0x07 +#define PHY_LPNPR 0x08 +#define PHY_1000CR 0x09 +#define PHY_1000SR 0x0a + +#define PHY_ANAR_NEXTPAGE 0x8000 +#define PHY_ANAR_RSVD0 0x4000 +#define PHY_ANAR_TLRFLT 0x2000 +#define PHY_ANAR_RSVD1 0x1000 +#define PHY_ANAR_RSVD2 0x0800 +#define PHY_ANAR_RSVD3 0x0400 +#define PHY_ANAR_100BT4 0x0200L +#define PHY_ANAR_100BTXFULL 0x0100 +#define PHY_ANAR_100BTXHALF 0x0080 +#define PHY_ANAR_10BTFULL 0x0040 +#define PHY_ANAR_10BTHALF 0x0020 +#define PHY_ANAR_PROTO4 0x0010 +#define PHY_ANAR_PROTO3 0x0008 +#define PHY_ANAR_PROTO2 0x0004 +#define PHY_ANAR_PROTO1 0x0002 +#define PHY_ANAR_PROTO0 0x0001 + +#define PHY_1000SR_1000BTXFULL 0x0800 +#define PHY_1000SR_1000BTXHALF 0x0400 + +/* + * These are the register definitions for the PHY (physical layer + * interface chip). + */ +/* + * PHY BMCR Basic Mode Control Register + */ +#define PHY_BMCR_RESET 0x8000 +#define PHY_BMCR_LOOPBK 0x4000 +#define PHY_BMCR_SPEEDSEL 0x2000 +#define PHY_BMCR_AUTONEGENBL 0x1000 +#define PHY_BMCR_RSVD0 0x0800 /* write as zero */ +#define PHY_BMCR_ISOLATE 0x0400 +#define PHY_BMCR_AUTONEGRSTR 0x0200 +#define PHY_BMCR_DUPLEX 0x0100 +#define PHY_BMCR_COLLTEST 0x0080 +#define PHY_BMCR_1000 0x0040 /* only used for Marvell PHY */ +#define PHY_BMCR_RSVD2 0x0020 /* write as zero, don't care */ +#define PHY_BMCR_RSVD3 0x0010 /* write as zero, don't care */ +#define PHY_BMCR_RSVD4 0x0008 /* write as zero, don't care */ +#define PHY_BMCR_RSVD5 0x0004 /* write as zero, don't care */ +#define PHY_BMCR_RSVD6 0x0002 /* write as zero, don't care */ +#define PHY_BMCR_RSVD7 0x0001 /* write as zero, don't care */ + +/* + * RESET: 1 == software reset, 0 == normal operation + * Resets status and control registers to default values. + * Relatches all hardware config values. + * + * LOOPBK: 1 == loopback operation enabled, 0 == normal operation + * + * SPEEDSEL: 1 == 100Mb/s, 0 == 10Mb/s + * Link speed is selected byt his bit or if auto-negotiation if bit + * 12 (AUTONEGENBL) is set (in which case the value of this register + * is ignored). + * + * AUTONEGENBL: 1 == Autonegotiation enabled, 0 == Autonegotiation disabled + * Bits 8 and 13 are ignored when autoneg is set, otherwise bits 8 and 13 + * determine speed and mode. Should be cleared and then set if PHY configured + * for no autoneg on startup. + * + * ISOLATE: 1 == isolate PHY from MII, 0 == normal operation + * + * AUTONEGRSTR: 1 == restart autonegotiation, 0 = normal operation + * + * DUPLEX: 1 == full duplex mode, 0 == half duplex mode + * + * COLLTEST: 1 == collision test enabled, 0 == normal operation + */ + +/* + * PHY, BMSR Basic Mode Status Register + */ +#define PHY_BMSR_100BT4 0x8000 +#define PHY_BMSR_100BTXFULL 0x4000 +#define PHY_BMSR_100BTXHALF 0x2000 +#define PHY_BMSR_10BTFULL 0x1000 +#define PHY_BMSR_10BTHALF 0x0800 +#define PHY_BMSR_RSVD1 0x0400 /* write as zero, don't care */ +#define PHY_BMSR_RSVD2 0x0200 /* write as zero, don't care */ +#define PHY_BMSR_RSVD3 0x0100 /* write as zero, don't care */ +#define PHY_BMSR_RSVD4 0x0080 /* write as zero, don't care */ +#define PHY_BMSR_MFPRESUP 0x0040 +#define PHY_BMSR_AUTONEGCOMP 0x0020 +#define PHY_BMSR_REMFAULT 0x0010 +#define PHY_BMSR_CANAUTONEG 0x0008 +#define PHY_BMSR_LINKSTAT 0x0004 +#define PHY_BMSR_JABBER 0x0002 +#define PHY_BMSR_EXTENDED 0x0001 + + |