diff options
author | wpaul <wpaul@FreeBSD.org> | 1999-09-22 05:07:51 +0000 |
---|---|---|
committer | wpaul <wpaul@FreeBSD.org> | 1999-09-22 05:07:51 +0000 |
commit | ef2bc702accfd810fcb93cfb23789816dc7be195 (patch) | |
tree | eeafa344d341bc8d49836772b1468389baa366a6 /sys/pci | |
parent | d651b26a6430a146c8d1ac8d830354cc6fcf6349 (diff) | |
download | FreeBSD-src-ef2bc702accfd810fcb93cfb23789816dc7be195.zip FreeBSD-src-ef2bc702accfd810fcb93cfb23789816dc7be195.tar.gz |
Spruce up the ADMtek driver: conver to newbus, miibus and add support
for the AN985 "Centaur" chip, which is apparently the next genetation
of the "Comet." The AN985 is also a tulip clone and is similar to the
AL981 except that it uses a 99C66 EEPROM and a serial MII interface
(instead of direct access to the PHY registers).
Also updated various documentation to mention the AN985 and created
a loadable module.
I don't think there are any cards that use this chip on the market yet:
the datasheet I got from ADMtek has boxes with big X's in them where the
diagrams should be, and the sample boards I got have chips without any
artwork on them.
Diffstat (limited to 'sys/pci')
-rw-r--r-- | sys/pci/if_al.c | 1525 | ||||
-rw-r--r-- | sys/pci/if_alreg.h | 217 |
2 files changed, 680 insertions, 1062 deletions
diff --git a/sys/pci/if_al.c b/sys/pci/if_al.c index 5d07973..073cd66 100644 --- a/sys/pci/if_al.c +++ b/sys/pci/if_al.c @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,10 +33,10 @@ */ /* - * ADMtek AL981 Comet fast ethernet PCI NIC driver. Datasheets for - * the AL981 are available from http://www.admtek.com.tw. + * ADMtek AL981 Comet and AN985 Centaur fast ethernet PCI NIC driver. + * Datasheets for the AL981 are available from http://www.admtek.com.tw. * - * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Written by Bill Paul <wpaul@ee.columbia.edu> * Electrical Engineering Department * Columbia University, New York City */ @@ -47,13 +47,23 @@ * programming. Where the DEC chip has a special setup frame that * needs to be downloaded into the transmit DMA engine, the ADMtek chip * has physical address and multicast address registers. + * + * The AN985 is an update to the AL981 which is mostly the same, except + * for the following things: + * - The AN985 uses a 99C66 EEPROM which requires a slightly different + * bit sequence to initiate a read. + * - The AN985 uses a serial MII interface instead of providing direct + * access to the PHY registers (it uses an internal PHY though). + * Although the datasheet for the AN985 is not yet available, you can + * use an AL981 datasheet as a reference for most of the chip functions, + * except for the MII interface which matches the DEC 21x4x specification + * (bits 16, 17, 18 and 19 in the serial I/O register control the MII). */ #include "bpf.h" #include <sys/param.h> #include <sys/systm.h> -#include <sys/eventhandler.h> #include <sys/sockio.h> #include <sys/mbuf.h> #include <sys/malloc.h> @@ -76,6 +86,12 @@ #include <machine/bus_pio.h> #include <machine/bus_memio.h> #include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> #include <pci/pcireg.h> #include <pci/pcivar.h> @@ -85,10 +101,10 @@ #define AL_USEIOSPACE -/* #define AL_BACKGROUND_AUTONEG */ - #include <pci/if_alreg.h> +#include "miibus_if.h" + #ifndef lint static const char rcsid[] = "$FreeBSD$"; @@ -98,47 +114,31 @@ static const char rcsid[] = * Various supported device vendors/types and their names. */ static struct al_type al_devs[] = { - { AL_VENDORID, AL_DEVICEID_AL981, - "ADMtek AL981 10/100BaseTX" }, + { AL_VENDORID, AL_DEVICEID_AL981, "ADMtek AL981 10/100BaseTX" }, + { AL_VENDORID, AL_DEVICEID_AN985, "ADMtek AN985 10/100BaseTX" }, { 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 al_type al_phys[] = { - { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, - { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, - { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, - { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, - { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, - { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, - { 0, 0, "<MII-compliant physical interface>" } -}; - -static unsigned long al_count = 0; -static const char *al_probe __P((pcici_t, pcidi_t)); -static void al_attach __P((pcici_t, int)); +static int al_probe __P((device_t)); +static int al_attach __P((device_t)); +static int al_detach __P((device_t)); static int al_newbuf __P((struct al_softc *, - struct al_chain_onefrag *)); -static int al_encap __P((struct al_softc *, struct al_chain *, - struct mbuf *)); + struct al_desc *, + struct mbuf *)); +static int al_encap __P((struct al_softc *, + struct mbuf *, u_int32_t *)); static void al_rxeof __P((struct al_softc *)); -static void al_rxeoc __P((struct al_softc *)); static void al_txeof __P((struct al_softc *)); -static void al_txeoc __P((struct al_softc *)); +static void al_tick __P((void *)); static void al_intr __P((void *)); static void al_start __P((struct ifnet *)); static int al_ioctl __P((struct ifnet *, u_long, caddr_t)); static void al_init __P((void *)); static void al_stop __P((struct al_softc *)); static void al_watchdog __P((struct ifnet *)); -static void al_shutdown __P((void *, int)); +static void al_shutdown __P((device_t)); static int al_ifmedia_upd __P((struct ifnet *)); static void al_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); @@ -149,19 +149,61 @@ static void al_eeprom_getword __P((struct al_softc *, int, u_int16_t *)); static void al_read_eeprom __P((struct al_softc *, caddr_t, int, int, int)); -static u_int16_t al_phy_readreg __P((struct al_softc *, int)); -static void al_phy_writereg __P((struct al_softc *, int, int)); +static void al_mii_writebit __P((struct al_softc *, int)); +static int al_mii_readbit __P((struct al_softc *)); +static void al_mii_sync __P((struct al_softc *)); +static void al_mii_send __P((struct al_softc *, u_int32_t, int)); +static int al_mii_readreg __P((struct al_softc *, struct al_mii_frame *)); +static int al_mii_writereg __P((struct al_softc *, struct al_mii_frame *)); + +static int al_miibus_readreg __P((device_t, int, int)); +static int al_miibus_writereg __P((device_t, int, int, int)); +static void al_miibus_statchg __P((device_t)); -static void al_autoneg_xmit __P((struct al_softc *)); -static void al_autoneg_mii __P((struct al_softc *, int, int)); -static void al_setmode_mii __P((struct al_softc *, int)); -static void al_getmode_mii __P((struct al_softc *)); static u_int32_t al_calchash __P((caddr_t)); static void al_setmulti __P((struct al_softc *)); static void al_reset __P((struct al_softc *)); static int al_list_rx_init __P((struct al_softc *)); static int al_list_tx_init __P((struct al_softc *)); +#ifdef AL_USEIOSPACE +#define AL_RES SYS_RES_IOPORT +#define AL_RID AL_PCI_LOIO +#else +#define AL_RES SYS_RES_MEMORY +#define AL_RID AL_PCI_LOMEM +#endif + +static device_method_t al_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, al_probe), + DEVMETHOD(device_attach, al_attach), + DEVMETHOD(device_detach, al_detach), + DEVMETHOD(device_shutdown, al_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, al_miibus_readreg), + DEVMETHOD(miibus_writereg, al_miibus_writereg), + DEVMETHOD(miibus_statchg, al_miibus_statchg), + + { 0, 0 } +}; + +static driver_t al_driver = { + "al", + al_methods, + sizeof(struct al_softc), +}; + +static devclass_t al_devclass; + +DRIVER_MODULE(al, pci, al_driver, al_devclass, 0, 0); +DRIVER_MODULE(miibus, al, miibus_driver, miibus_devclass, 0, 0); + #define AL_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | x) @@ -226,7 +268,15 @@ static void al_eeprom_putbyte(sc, addr) { register int d, i; - d = addr | AL_EECMD_READ; + /* + * The AN985 has a 99C66 EEPROM on it instead of + * a 99C64. It uses a different bit sequence for + * specifying the "read" opcode. + */ + if (sc->al_did == AL_DEVICEID_AN985) + d = addr | (AL_EECMD_READ << 2); + else + d = addr | AL_EECMD_READ; /* * Feed in each bit and stobe the clock. @@ -268,7 +318,7 @@ static void al_eeprom_getword(sc, addr, dest) al_delay(sc); AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CS); al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CLK); + AL_CLRBIT(sc, AL_SIO, AL_SIO_EE_CLK); al_delay(sc); /* @@ -322,83 +372,313 @@ static void al_read_eeprom(sc, dest, off, cnt, swap) return; } -static u_int16_t al_phy_readreg(sc, reg) +/* + * Write a bit to the MII bus. + */ +static void al_mii_writebit(sc, bit) + struct al_softc *sc; + int bit; +{ + if (bit) + CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE|AL_SIO_MII_DATAOUT); + else + CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE); + + AL_SETBIT(sc, AL_SIO, AL_SIO_MII_CLK); + AL_CLRBIT(sc, AL_SIO, AL_SIO_MII_CLK); + + return; +} + +/* + * Read a bit from the MII bus. + */ +static int al_mii_readbit(sc) + struct al_softc *sc; +{ + CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_READ|AL_SIO_MII_DIR); + CSR_READ_4(sc, AL_SIO); + AL_SETBIT(sc, AL_SIO, AL_SIO_MII_CLK); + AL_CLRBIT(sc, AL_SIO, AL_SIO_MII_CLK); + if (CSR_READ_4(sc, AL_SIO) & AL_SIO_MII_DATAIN) + return(1); + + return(0); +} + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void al_mii_sync(sc) + struct al_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE); + + for (i = 0; i < 32; i++) + al_mii_writebit(sc, 1); + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void al_mii_send(sc, bits, cnt) + struct al_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) + al_mii_writebit(sc, bits & i); +} + +/* + * Read an PHY register through the MII. + */ +static int al_mii_readreg(sc, frame) struct al_softc *sc; - int reg; + struct al_mii_frame *frame; + { + int i, ack, s; + + s = splimp(); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = AL_MII_STARTDELIM; + frame->mii_opcode = AL_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Sync the PHYs. + */ + al_mii_sync(sc); + + /* + * Send command/address info. + */ + al_mii_send(sc, frame->mii_stdelim, 2); + al_mii_send(sc, frame->mii_opcode, 2); + al_mii_send(sc, frame->mii_phyaddr, 5); + al_mii_send(sc, frame->mii_regaddr, 5); + +#ifdef notdef + /* Idle bit */ + al_mii_writebit(sc, 1); + al_mii_writebit(sc, 0); +#endif + + /* Check for ack */ + ack = al_mii_readbit(sc); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + al_mii_readbit(sc); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + if (!ack) { + if (al_mii_readbit(sc)) + frame->mii_data |= i; + } + } + +fail: + + al_mii_writebit(sc, 0); + al_mii_writebit(sc, 0); + + splx(s); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int al_mii_writereg(sc, frame) + struct al_softc *sc; + struct al_mii_frame *frame; + +{ + int s; + + s = splimp(); + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = AL_MII_STARTDELIM; + frame->mii_opcode = AL_MII_WRITEOP; + frame->mii_turnaround = AL_MII_TURNAROUND; + + /* + * Sync the PHYs. + */ + al_mii_sync(sc); + + al_mii_send(sc, frame->mii_stdelim, 2); + al_mii_send(sc, frame->mii_opcode, 2); + al_mii_send(sc, frame->mii_phyaddr, 5); + al_mii_send(sc, frame->mii_regaddr, 5); + al_mii_send(sc, frame->mii_turnaround, 2); + al_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + al_mii_writebit(sc, 0); + al_mii_writebit(sc, 0); + + splx(s); + + return(0); +} + +static int al_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct al_mii_frame frame; u_int16_t rval = 0; u_int16_t phy_reg = 0; + struct al_softc *sc; + + sc = device_get_softc(dev); + + /* + * Note: both the AL981 and AN985 have internal PHYs, + * however the AL981 provides direct access to the PHY + * registers while the AN985 uses a serial MII interface. + * The AN985's MII interface is also buggy in that you + * can read from any MII address (0 to 31), but only address 1 + * behaves normally. To deal with both cases, we pretend + * that the PHY is at MII address 1. + */ + if (phy != 1) + return(0); + + if (sc->al_did == AL_DEVICEID_AN985) { + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + al_mii_readreg(sc, &frame); + + return(frame.mii_data); + } switch(reg) { - case PHY_BMCR: + case MII_BMCR: phy_reg = AL_BMCR; break; - case PHY_BMSR: + case MII_BMSR: phy_reg = AL_BMSR; break; - case PHY_VENID: + case MII_PHYIDR1: phy_reg = AL_VENID; break; - case PHY_DEVID: + case MII_PHYIDR2: phy_reg = AL_DEVID; break; - case PHY_ANAR: + case MII_ANAR: phy_reg = AL_ANAR; break; - case PHY_LPAR: + case MII_ANLPAR: phy_reg = AL_LPAR; break; - case PHY_ANEXP: + case MII_ANER: phy_reg = AL_ANER; break; default: printf("al%d: read: bad phy register %x\n", sc->al_unit, reg); + return(0); break; } rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; + if (rval == 0xFFFF) + return(0); + return(rval); } -static void al_phy_writereg(sc, reg, data) - struct al_softc *sc; - int reg; - int data; +static int al_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; { + struct al_mii_frame frame; + struct al_softc *sc; u_int16_t phy_reg = 0; + sc = device_get_softc(dev); + + if (phy != 1) + return(0); + + if (sc->al_did == AL_DEVICEID_AN985) { + bzero((char *)&frame, sizeof(frame)); + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + al_mii_writereg(sc, &frame); + return(0); + } + switch(reg) { - case PHY_BMCR: + case MII_BMCR: phy_reg = AL_BMCR; break; - case PHY_BMSR: + case MII_BMSR: phy_reg = AL_BMSR; break; - case PHY_VENID: + case MII_PHYIDR1: phy_reg = AL_VENID; break; - case PHY_DEVID: + case MII_PHYIDR2: phy_reg = AL_DEVID; break; - case PHY_ANAR: + case MII_ANAR: phy_reg = AL_ANAR; break; - case PHY_LPAR: + case MII_ANLPAR: phy_reg = AL_LPAR; break; - case PHY_ANEXP: + case MII_ANER: phy_reg = AL_ANER; break; default: printf("al%d: phy_write: bad phy register %x\n", sc->al_unit, reg); + return(0); break; } CSR_WRITE_4(sc, phy_reg, data); + return(0); +} + +static void al_miibus_statchg(dev) + device_t dev; +{ return; } @@ -473,311 +753,6 @@ static void al_setmulti(sc) return; } -/* - * Initiate an autonegotiation session. - */ -static void al_autoneg_xmit(sc) - struct al_softc *sc; -{ - u_int16_t phy_sts; - - al_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(al_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - - phy_sts = al_phy_readreg(sc, PHY_BMCR); - phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; - al_phy_writereg(sc, PHY_BMCR, phy_sts); - - return; -} - -/* - * Invoke autonegotiation on a PHY. - */ -static void al_autoneg_mii(sc, flag, verbose) - struct al_softc *sc; - int flag; - int verbose; -{ - u_int16_t phy_sts = 0, media, advert, ability; - struct ifnet *ifp; - struct ifmedia *ifm; - - ifm = &sc->ifmedia; - ifp = &sc->arpcom.ac_if; - - ifm->ifm_media = IFM_ETHER | IFM_AUTO; - - /* - * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' - * bit cleared in the status register, but has the 'autoneg enabled' - * bit set in the control register. This is a contradiction, and - * I'm not sure how to handle it. If you want to force an attempt - * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR - * and see what happens. - */ -#ifndef FORCE_AUTONEG_TFOUR - /* - * First, see if autoneg is supported. If not, there's - * no point in continuing. - */ - phy_sts = al_phy_readreg(sc, PHY_BMSR); - if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { - if (verbose) - printf("al%d: autonegotiation not supported\n", - sc->al_unit); - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - return; - } -#endif - - switch (flag) { - case AL_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. - */ - al_autoneg_xmit(sc); - DELAY(5000000); - break; - case AL_FLAG_SCHEDDELAY: - /* - * Wait for the transmitter to go idle before starting - * an autoneg session, otherwise al_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->al_cdata.al_tx_head != NULL) { - sc->al_want_auto = 1; - return; - } - al_autoneg_xmit(sc); - ifp->if_timer = 5; - sc->al_autoneg = 1; - sc->al_want_auto = 0; - return; - break; - case AL_FLAG_DELAYTIMEO: - ifp->if_timer = 0; - sc->al_autoneg = 0; - break; - default: - printf("al%d: invalid autoneg flag: %d\n", sc->al_unit, flag); - return; - } - - if (al_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { - if (verbose) - printf("al%d: autoneg complete, ", sc->al_unit); - phy_sts = al_phy_readreg(sc, PHY_BMSR); - } else { - if (verbose) - printf("al%d: autoneg not complete, ", sc->al_unit); - } - - media = al_phy_readreg(sc, PHY_BMCR); - - /* Link is good. Report modes and set duplex mode. */ - if (al_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { - if (verbose) - printf("link status good "); - advert = al_phy_readreg(sc, PHY_ANAR); - ability = al_phy_readreg(sc, PHY_LPAR); - - 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 & PHY_ANAR_10BTHALF && - ability & PHY_ANAR_10BTHALF) { - 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. */ - al_phy_writereg(sc, PHY_BMCR, media); - } else { - if (verbose) - printf("no carrier\n"); - } - - al_init(sc); - - if (sc->al_tx_pend) { - sc->al_autoneg = 0; - sc->al_tx_pend = 0; - al_start(ifp); - } - - return; -} - -static void al_getmode_mii(sc) - struct al_softc *sc; -{ - u_int16_t bmsr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - bmsr = al_phy_readreg(sc, PHY_BMSR); - if (bootverbose) - printf("al%d: PHY status word: %x\n", sc->al_unit, bmsr); - - /* fallback */ - sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - - if (bmsr & PHY_BMSR_10BTHALF) { - if (bootverbose) - printf("al%d: 10Mbps half-duplex mode supported\n", - sc->al_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("al%d: 10Mbps full-duplex mode supported\n", - sc->al_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("al%d: 100Mbps half-duplex mode supported\n", - sc->al_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("al%d: 100Mbps full-duplex mode supported\n", - sc->al_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("al%d: 100baseT4 mode supported\n", sc->al_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("al%d: forcing on autoneg support for BT4\n", - sc->al_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; -#endif - } - - if (bmsr & PHY_BMSR_CANAUTONEG) { - if (bootverbose) - printf("al%d: autoneg supported\n", sc->al_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; - } - - return; -} - -/* - * Set speed and duplex mode. - */ -static void al_setmode_mii(sc, media) - struct al_softc *sc; - int media; -{ - u_int16_t bmcr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* - * If an autoneg session is in progress, stop it. - */ - if (sc->al_autoneg) { - printf("al%d: canceling autoneg session\n", sc->al_unit); - ifp->if_timer = sc->al_autoneg = sc->al_want_auto = 0; - bmcr = al_phy_readreg(sc, PHY_BMCR); - bmcr &= ~PHY_BMCR_AUTONEGENBL; - al_phy_writereg(sc, PHY_BMCR, bmcr); - } - - printf("al%d: selecting MII, ", sc->al_unit); - - bmcr = al_phy_readreg(sc, PHY_BMCR); - - bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| - PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); - - 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; - } - - al_phy_writereg(sc, PHY_BMCR, bmcr); - - return; -} - static void al_reset(sc) struct al_softc *sc; { @@ -805,140 +780,134 @@ static void al_reset(sc) * Probe for an ADMtek chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ -static const char * -al_probe(config_id, device_id) - pcici_t config_id; - pcidi_t device_id; +static int al_probe(dev) + device_t dev; { struct al_type *t; t = al_devs; while(t->al_name != NULL) { - if ((device_id & 0xFFFF) == t->al_vid && - ((device_id >> 16) & 0xFFFF) == t->al_did) { - return(t->al_name); + if ((pci_get_vendor(dev) == t->al_vid) && + (pci_get_device(dev) == t->al_did)) { + device_set_desc(dev, t->al_name); + return(0); } t++; } - return(NULL); + return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ -static void -al_attach(config_id, unit) - pcici_t config_id; - int unit; +static int al_attach(dev) + device_t dev; { - int s, i; -#ifndef AL_USEIOSPACE - vm_offset_t pbase, vbase; -#endif + int s; u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct al_softc *sc; struct ifnet *ifp; - int media = IFM_ETHER|IFM_100_TX|IFM_FDX; - unsigned int round; - caddr_t roundptr; - struct al_type *p; - u_int16_t phy_vid, phy_did, phy_sts; + int unit, error = 0, rid; s = splimp(); - sc = malloc(sizeof(struct al_softc), M_DEVBUF, M_NOWAIT); - if (sc == NULL) { - printf("al%d: no memory for softc struct!\n", unit); - goto fail; - } + sc = device_get_softc(dev); + unit = device_get_unit(dev); bzero(sc, sizeof(struct al_softc)); + sc->al_did = pci_get_device(dev); /* * Handle power management nonsense. */ - command = pci_conf_read(config_id, AL_PCI_CAPID) & 0x000000FF; + command = pci_read_config(dev, AL_PCI_CAPID, 4) & 0x000000FF; if (command == 0x01) { - command = pci_conf_read(config_id, AL_PCI_PWRMGMTCTRL); + command = pci_read_config(dev, AL_PCI_PWRMGMTCTRL, 4); if (command & AL_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ - iobase = pci_conf_read(config_id, AL_PCI_LOIO); - membase = pci_conf_read(config_id, AL_PCI_LOMEM); - irq = pci_conf_read(config_id, AL_PCI_INTLINE); + iobase = pci_read_config(dev, AL_PCI_LOIO, 4); + membase = pci_read_config(dev, AL_PCI_LOMEM, 4); + irq = pci_read_config(dev, AL_PCI_INTLINE, 4); /* Reset the power state. */ printf("al%d: chip is in D%d power mode " "-- setting to D0\n", unit, command & AL_PSTATE_MASK); command &= 0xFFFFFFFC; - pci_conf_write(config_id, AL_PCI_PWRMGMTCTRL, command); + pci_write_config(dev, AL_PCI_PWRMGMTCTRL, command, 4); /* Restore PCI config data. */ - pci_conf_write(config_id, AL_PCI_LOIO, iobase); - pci_conf_write(config_id, AL_PCI_LOMEM, membase); - pci_conf_write(config_id, AL_PCI_INTLINE, irq); + pci_write_config(dev, AL_PCI_LOIO, iobase, 4); + pci_write_config(dev, AL_PCI_LOMEM, membase, 4); + pci_write_config(dev, AL_PCI_INTLINE, irq, 4); } } /* * Map control/status registers. */ - command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command); - command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); + pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); #ifdef AL_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("al%d: failed to enable I/O ports!\n", unit); - free(sc, M_DEVBUF); + error = ENXIO;; goto fail; } - - if (!pci_map_port(config_id, AL_PCI_LOIO, - (pci_port_t *)&(sc->al_bhandle))) { - printf ("al%d: couldn't map ports\n", unit); - goto fail; - } -#ifdef __i386__ - sc->al_btag = I386_BUS_SPACE_IO; -#endif -#ifdef __alpha__ - sc->al_btag = ALPHA_BUS_SPACE_IO; -#endif #else if (!(command & PCIM_CMD_MEMEN)) { printf("al%d: failed to enable memory mapping!\n", unit); + error = ENXIO;; goto fail; } +#endif + + rid = AL_RID; + sc->al_res = bus_alloc_resource(dev, AL_RES, &rid, + 0, ~0, 1, RF_ACTIVE); - if (!pci_map_mem(config_id, AL_PCI_LOMEM, &vbase, &pbase)) { - printf ("al%d: couldn't map memory\n", unit); + if (sc->al_res == NULL) { + printf("al%d: couldn't map ports/memory\n", unit); + error = ENXIO; goto fail; } -#ifdef __i386__ - sc->al_btag = I386_BUS_SPACE_MEM; -#endif -#ifdef __alpha__ - sc->al_btag = ALPHA_BUS_SPACE_MEM; -#endif - sc->al_bhandle = vbase; -#endif + + sc->al_btag = rman_get_bustag(sc->al_res); + sc->al_bhandle = rman_get_bushandle(sc->al_res); /* Allocate interrupt */ - if (!pci_map_int(config_id, al_intr, sc, &net_imask)) { + rid = 0; + sc->al_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->al_irq == NULL) { printf("al%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->al_irq, INTR_TYPE_NET, + al_intr, sc, &sc->al_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_res); + bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); + printf("al%d: couldn't set up irq\n", unit); goto fail; } - /* Save cache line size. */ - sc->al_cachesize = pci_conf_read(config_id, AL_PCI_CACHELEN) & 0xFF; + /* Save the cache line size. */ + sc->al_cachesize = pci_read_config(dev, AL_PCI_CACHELEN, 4) & 0xFF; /* Reset the adapter. */ al_reset(sc); @@ -954,27 +923,20 @@ al_attach(config_id, unit) printf("al%d: Ethernet address: %6D\n", unit, eaddr, ":"); sc->al_unit = unit; + callout_handle_init(&sc->al_stat_ch); bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - sc->al_ldata_ptr = malloc(sizeof(struct al_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->al_ldata_ptr == NULL) { - free(sc, M_DEVBUF); + sc->al_ldata = contigmalloc(sizeof(struct al_list_data), M_DEVBUF, + M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0); + + if (sc->al_ldata == NULL) { printf("al%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); + bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); + error = ENXIO; goto fail; } - - sc->al_ldata = (struct al_list_data *)sc->al_ldata_ptr; - round = (uintptr_t)sc->al_ldata_ptr & 0xF; - roundptr = sc->al_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } else - break; - } - sc->al_ldata = (struct al_list_data *)roundptr; bzero(sc->al_ldata, sizeof(struct al_list_data)); ifp = &sc->arpcom.ac_if; @@ -991,76 +953,19 @@ al_attach(config_id, unit) ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = AL_TX_LIST_CNT - 1; - if (bootverbose) - printf("al%d: probing for a PHY\n", sc->al_unit); - for (i = AL_PHYADDR_MIN; i < AL_PHYADDR_MAL + 1; i++) { - if (bootverbose) - printf("al%d: checking address: %d\n", - sc->al_unit, i); - sc->al_phy_addr = i; - al_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(al_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - if ((phy_sts = al_phy_readreg(sc, PHY_BMSR))) - break; - } - if (phy_sts) { - phy_vid = al_phy_readreg(sc, PHY_VENID); - phy_did = al_phy_readreg(sc, PHY_DEVID); - if (bootverbose) - printf("al%d: found PHY at address %d, ", - sc->al_unit, sc->al_phy_addr); - if (bootverbose) - printf("vendor id: %x device id: %x\n", - phy_vid, phy_did); - p = al_phys; - while(p->al_vid) { - if (phy_vid == p->al_vid && - (phy_did | 0x000F) == p->al_did) { - sc->al_pinfo = p; - break; - } - p++; - } - if (sc->al_pinfo == NULL) - sc->al_pinfo = &al_phys[PHY_UNKNOWN]; - if (bootverbose) - printf("al%d: PHY type: %s\n", - sc->al_unit, sc->al_pinfo->al_name); - } else { -#ifdef DIAGNOSTIC - printf("al%d: MII without any phy!\n", sc->al_unit); -#endif - } - /* - * Do ifmedia setup. + * Do MII setup. */ - ifmedia_init(&sc->ifmedia, 0, al_ifmedia_upd, al_ifmedia_sts); - - if (sc->al_pinfo != NULL) { - al_getmode_mii(sc); - al_autoneg_mii(sc, AL_FLAG_FORCEDELAY, 1); - } else { - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); + if (mii_phy_probe(dev, &sc->al_miibus, + al_ifmedia_upd, al_ifmedia_sts)) { + printf("al%d: MII without any PHY!\n", sc->al_unit); + bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); + bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); + error = ENXIO; + goto fail; } - media = sc->ifmedia.ifm_media; - al_stop(sc); - - ifmedia_set(&sc->ifmedia, media); - /* * Call MI attach routines. */ @@ -1070,12 +975,40 @@ al_attach(config_id, unit) #if NBPF > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif - EVENTHANDLER_REGISTER(shutdown_post_sync, al_shutdown, sc, - SHUTDOWN_PRI_DEFAULT); fail: splx(s); - return; + return(error); +} + +static int al_detach(dev) + device_t dev; +{ + struct al_softc *sc; + struct ifnet *ifp; + int s; + + s = splimp(); + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + al_reset(sc); + al_stop(sc); + if_detach(ifp); + + bus_generic_detach(dev); + device_delete_child(dev, sc->al_miibus); + + bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); + bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); + + contigfree(sc->al_ldata, sizeof(struct al_list_data), M_DEVBUF); + + splx(s); + + return(0); } /* @@ -1091,17 +1024,23 @@ static int al_list_tx_init(sc) cd = &sc->al_cdata; ld = sc->al_ldata; for (i = 0; i < AL_TX_LIST_CNT; i++) { - cd->al_tx_chain[i].al_ptr = &ld->al_tx_list[i]; - if (i == (AL_TX_LIST_CNT - 1)) - cd->al_tx_chain[i].al_nextdesc = - &cd->al_tx_chain[0]; - else - cd->al_tx_chain[i].al_nextdesc = - &cd->al_tx_chain[i + 1]; + if (i == (AL_TX_LIST_CNT - 1)) { + ld->al_tx_list[i].al_nextdesc = + &ld->al_tx_list[0]; + ld->al_tx_list[i].al_next = + vtophys(&ld->al_tx_list[0]); + } else { + ld->al_tx_list[i].al_nextdesc = + &ld->al_tx_list[i + 1]; + ld->al_tx_list[i].al_next = + vtophys(&ld->al_tx_list[i + 1]); + } + ld->al_tx_list[i].al_mbuf = NULL; + ld->al_tx_list[i].al_data = 0; + ld->al_tx_list[i].al_ctl = 0; } - cd->al_tx_free = &cd->al_tx_chain[0]; - cd->al_tx_tail = cd->al_tx_head = NULL; + cd->al_tx_prod = cd->al_tx_cons = cd->al_tx_cnt = 0; return(0); } @@ -1123,60 +1062,64 @@ static int al_list_rx_init(sc) ld = sc->al_ldata; for (i = 0; i < AL_RX_LIST_CNT; i++) { - cd->al_rx_chain[i].al_ptr = - (volatile struct al_desc *)&ld->al_rx_list[i]; - if (al_newbuf(sc, &cd->al_rx_chain[i]) == ENOBUFS) + if (al_newbuf(sc, &ld->al_rx_list[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (AL_RX_LIST_CNT - 1)) { - cd->al_rx_chain[i].al_nextdesc = - &cd->al_rx_chain[0]; + ld->al_rx_list[i].al_nextdesc = + &ld->al_rx_list[0]; ld->al_rx_list[i].al_next = - vtophys(&ld->al_rx_list[0]); + vtophys(&ld->al_rx_list[0]); } else { - cd->al_rx_chain[i].al_nextdesc = - &cd->al_rx_chain[i + 1]; + ld->al_rx_list[i].al_nextdesc = + &ld->al_rx_list[i + 1]; ld->al_rx_list[i].al_next = - vtophys(&ld->al_rx_list[i + 1]); + vtophys(&ld->al_rx_list[i + 1]); } } - cd->al_rx_head = &cd->al_rx_chain[0]; + cd->al_rx_prod = 0; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. - * Note: the length fields are only 11 bits wide, which means the - * largest size we can specify is 2047. This is important because - * MCLBYTES is 2048, so we have to subtract one otherwise we'll - * overflow the field and make a mess. */ -static int al_newbuf(sc, c) +static int al_newbuf(sc, c, m) struct al_softc *sc; - struct al_chain_onefrag *c; + struct al_desc *c; + struct mbuf *m; { struct mbuf *m_new = NULL; - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("al%d: no memory for rx list -- packet dropped!\n", - sc->al_unit); - return(ENOBUFS); - } + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("al%d: no memory for rx list " + "-- packet dropped!\n", sc->al_unit); + return(ENOBUFS); + } - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("al%d: no memory for rx list -- packet dropped!\n", - sc->al_unit); - m_freem(m_new); - return(ENOBUFS); + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("al%d: no memory for rx list " + "-- packet dropped!\n", sc->al_unit); + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; } + m_adj(m_new, sizeof(u_int64_t)); + c->al_mbuf = m_new; - c->al_ptr->al_status = AL_RXSTAT; - c->al_ptr->al_data = vtophys(mtod(m_new, caddr_t)); - c->al_ptr->al_ctl = MCLBYTES - 1; + c->al_data = vtophys(mtod(m_new, caddr_t)); + c->al_ctl = AL_RXCTL_RLINK | AL_RXLEN; + c->al_status = AL_RXSTAT_OWN; return(0); } @@ -1191,19 +1134,23 @@ static void al_rxeof(sc) struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; - struct al_chain_onefrag *cur_rx; - int total_len = 0; + struct al_desc *cur_rx; + int i, total_len = 0; u_int32_t rxstat; ifp = &sc->arpcom.ac_if; - while(!((rxstat = sc->al_cdata.al_rx_head->al_ptr->al_status) & - AL_RXSTAT_OWN)) { -#ifdef __alpha__ + i = sc->al_cdata.al_rx_prod; + + while(!(sc->al_ldata->al_rx_list[i].al_status & AL_RXSTAT_OWN)) { struct mbuf *m0 = NULL; -#endif - cur_rx = sc->al_cdata.al_rx_head; - sc->al_cdata.al_rx_head = cur_rx->al_nextdesc; + + cur_rx = &sc->al_ldata->al_rx_list[i]; + rxstat = cur_rx->al_status; + m = cur_rx->al_mbuf; + cur_rx->al_mbuf = NULL; + total_len = AL_RXBYTES(rxstat); + AL_INC(i, AL_RX_LIST_CNT); /* * If an error occurs, update stats, clear the @@ -1215,90 +1162,23 @@ static void al_rxeof(sc) ifp->if_ierrors++; if (rxstat & AL_RXSTAT_COLLSEEN) ifp->if_collisions++; - cur_rx->al_ptr->al_status = AL_RXSTAT; - cur_rx->al_ptr->al_ctl = (MCLBYTES - 1); - continue; + al_newbuf(sc, cur_rx, m); + al_init(sc); + return; } /* No errors; receive the packet. */ - m = cur_rx->al_mbuf; - total_len = AL_RXBYTES(cur_rx->al_ptr->al_status); - total_len -= ETHER_CRC_LEN; -#ifdef __alpha__ - /* - * 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 (al_newbuf(sc, cur_rx) == ENOBUFS) { - ifp->if_ierrors++; - cur_rx->al_ptr->al_status = AL_RXSTAT; - cur_rx->al_ptr->al_ctl = (MCLBYTES - 1); - continue; - } - - /* - * Sadly, the ADMtek chip doesn't decode the last few - * bits of the RX DMA buffer address, so we have to - * cheat in order to obtain proper payload alignment - * on the alpha. - */ - MGETHDR(m0, M_DONTWAIT, MT_DATA); + m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, + total_len + ETHER_ALIGN, 0, ifp, NULL); + al_newbuf(sc, cur_rx, m); if (m0 == NULL) { ifp->if_ierrors++; - cur_rx->al_ptr->al_status = AL_RXSTAT; - cur_rx->al_ptr->al_ctl = (MCLBYTES - 1); continue; } - - m0->m_data += 2; - if (total_len <= (MHLEN - 2)) { - bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), total_len); m_freem(m); - m = m0; - m->m_pkthdr.len = m->m_len = total_len; - } else { - bcopy(mtod(m, caddr_t), mtod(m0, caddr_t), (MHLEN - 2)); - m->m_len = total_len - (MHLEN - 2); - m->m_data += (MHLEN - 2); - m0->m_next = m; - m0->m_len = (MHLEN - 2); - m = m0; - m->m_pkthdr.len = total_len; - } - m->m_pkthdr.rcvif = ifp; -#else - if (total_len < MINCLSIZE) { - m = m_devget(mtod(cur_rx->al_mbuf, char *), - total_len, 0, ifp, NULL); - cur_rx->al_ptr->al_status = AL_RXSTAT; - cur_rx->al_ptr->al_ctl = (MCLBYTES - 1); - if (m == NULL) { - ifp->if_ierrors++; - continue; - } - } else { - m = cur_rx->al_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 (al_newbuf(sc, cur_rx) == ENOBUFS) { - ifp->if_ierrors++; - cur_rx->al_ptr->al_status = AL_RXSTAT; - cur_rx->al_ptr->al_ctl = (MCLBYTES - 1); - continue; - } - m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = total_len; - } -#endif + m_adj(m0, ETHER_ALIGN); + m = m0; ifp->if_ipackets++; eh = mtod(m, struct ether_header *); @@ -1312,9 +1192,9 @@ static void al_rxeof(sc) 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)) { + (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0)) { m_freem(m); continue; } @@ -1325,18 +1205,7 @@ static void al_rxeof(sc) ether_input(ifp, eh, m); } - return; -} - -void al_rxeoc(sc) - struct al_softc *sc; -{ - - al_rxeof(sc); - AL_CLRBIT(sc, AL_NETCFG, AL_NETCFG_RX_ON); - CSR_WRITE_4(sc, AL_RXADDR, vtophys(sc->al_cdata.al_rx_head->al_ptr)); - AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_RX_ON); - CSR_WRITE_4(sc, AL_RXSTART, 0xFFFFFFFF); + sc->al_cdata.al_rx_prod = i; return; } @@ -1349,78 +1218,85 @@ void al_rxeoc(sc) static void al_txeof(sc) struct al_softc *sc; { - struct al_chain *cur_tx; + struct al_desc *cur_tx = NULL; struct ifnet *ifp; + u_int32_t idx; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; - if (sc->al_cdata.al_tx_head == NULL) - return; - /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ - while(sc->al_cdata.al_tx_head->al_mbuf != NULL) { + idx = sc->al_cdata.al_tx_cons; + while(idx != sc->al_cdata.al_tx_prod) { u_int32_t txstat; - cur_tx = sc->al_cdata.al_tx_head; - txstat = AL_TXSTATUS(cur_tx); + cur_tx = &sc->al_ldata->al_tx_list[idx]; + txstat = cur_tx->al_status; if (txstat & AL_TXSTAT_OWN) break; + if (!(cur_tx->al_ctl & AL_TXCTL_LASTFRAG)) { + sc->al_cdata.al_tx_cnt--; + AL_INC(idx, AL_TX_LIST_CNT); + continue; + } + if (txstat & AL_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & AL_TXSTAT_EXCESSCOLL) ifp->if_collisions++; if (txstat & AL_TXSTAT_LATECOLL) ifp->if_collisions++; + al_init(sc); + return; } ifp->if_collisions += (txstat & AL_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; - m_freem(cur_tx->al_mbuf); - cur_tx->al_mbuf = NULL; - - if (sc->al_cdata.al_tx_head == sc->al_cdata.al_tx_tail) { - sc->al_cdata.al_tx_head = NULL; - sc->al_cdata.al_tx_tail = NULL; - ifp->if_flags &= ~IFF_OACTIVE; - break; + if (cur_tx->al_mbuf != NULL) { + m_freem(cur_tx->al_mbuf); + cur_tx->al_mbuf = NULL; } - sc->al_cdata.al_tx_head = cur_tx->al_nextdesc; + sc->al_cdata.al_tx_cnt--; + AL_INC(idx, AL_TX_LIST_CNT); + ifp->if_timer = 0; } + sc->al_cdata.al_tx_cons = idx; + + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + return; } -/* - * TX 'end of channel' interrupt handler. - */ -static void al_txeoc(sc) - struct al_softc *sc; +static void al_tick(xsc) + void *xsc; { - struct ifnet *ifp; + struct al_softc *sc; + struct mii_data *mii; + int s; - ifp = &sc->arpcom.ac_if; + s = splimp(); - ifp->if_timer = 0; + sc = xsc; + mii = device_get_softc(sc->al_miibus); + mii_tick(mii); - if (sc->al_cdata.al_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->al_cdata.al_tx_tail = NULL; - if (sc->al_want_auto) - al_autoneg_mii(sc, AL_FLAG_DELAYTIMEO, 1); - } + sc->al_stat_ch = timeout(al_tick, sc, hz); + + splx(s); return; -} +}; static void al_intr(arg) void *arg; @@ -1429,6 +1305,7 @@ static void al_intr(arg) struct ifnet *ifp; u_int32_t status; + sc = arg; ifp = &sc->arpcom.ac_if; @@ -1449,15 +1326,13 @@ static void al_intr(arg) if ((status & AL_INTRS) == 0) break; - if (status & AL_ISR_TX_OK) + if ((status & AL_ISR_TX_OK) || + (status & AL_ISR_TX_NOBUF)) al_txeof(sc); - if (status & AL_ISR_TX_NOBUF) - al_txeoc(sc); - if (status & AL_ISR_TX_IDLE) { al_txeof(sc); - if (sc->al_cdata.al_tx_head != NULL) { + if (sc->al_cdata.al_tx_cnt) { AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_TX_ON); CSR_WRITE_4(sc, AL_TXSTART, 0xFFFFFFFF); } @@ -1475,9 +1350,12 @@ static void al_intr(arg) if (status & AL_ISR_RX_OK) al_rxeof(sc); - if ((status & AL_ISR_RX_WATDOGTIMEO) - || (status & AL_ISR_RX_NOBUF)) - al_rxeoc(sc); + if ((status & AL_ISR_RX_WATDOGTIMEO) || + (status & AL_ISR_RX_IDLE) || + (status & AL_ISR_RX_NOBUF)) { + al_rxeof(sc); + al_init(sc); + } if (status & AL_ISR_BUS_ERR) { al_reset(sc); @@ -1499,15 +1377,14 @@ static void al_intr(arg) * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ -static int al_encap(sc, c, m_head) +static int al_encap(sc, m_head, txidx) struct al_softc *sc; - struct al_chain *c; struct mbuf *m_head; + u_int32_t *txidx; { - int frag = 0; - volatile struct al_desc *f = NULL; - int total_len; + struct al_desc *f = NULL; struct mbuf *m; + int frag, cur, cnt = 0; /* * Start packing the mbufs in this chain into @@ -1515,68 +1392,52 @@ static int al_encap(sc, c, m_head) * of fragments or hit the end of the mbuf chain. */ m = m_head; - total_len = 0; + cur = frag = *txidx; - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { + for (m = m_head; m != NULL; m = m->m_next) { if (m->m_len != 0) { - if (frag == AL_MAXFRAGS) - break; - total_len += m->m_len; - f = &c->al_ptr->al_frag[frag]; +#ifdef AL_TX_STALL_WAR + /* + * Work around some strange behavior in the Comet. For + * some reason, the transmitter will sometimes wedge if + * we queue up a descriptor chain that wraps from the end + * of the transmit list back to the beginning. If we reach + * the end of the list and still have more packets to queue, + * don't queue them now: end the transmit session here and + * then wait until it finishes before sending the other + * packets. + */ + if (*txidx != sc->al_cdata.al_tx_prod && + frag == (AL_TX_LIST_CNT - 1)) + return(ENOBUFS); +#endif + if ((AL_TX_LIST_CNT - + (sc->al_cdata.al_tx_cnt + cnt)) < 2) + return(ENOBUFS); + f = &sc->al_ldata->al_tx_list[frag]; f->al_ctl = AL_TXCTL_TLINK | m->m_len; - if (frag == 0) { + if (cnt == 0) { f->al_status = 0; f->al_ctl |= AL_TXCTL_FIRSTFRAG; } else f->al_status = AL_TXSTAT_OWN; - f->al_next = vtophys(&c->al_ptr->al_frag[frag + 1]); f->al_data = vtophys(mtod(m, vm_offset_t)); - frag++; + cur = frag; + AL_INC(frag, AL_TX_LIST_CNT); + cnt++; } } - /* - * Handle special case: we ran out of fragments, - * but we have more mbufs left in the chain. Copy the - * data into an mbuf cluster. Note that we don't - * bother clearing the values in the other fragment - * pointers/counters; it wouldn't gain us anything, - * and would waste cycles. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; + if (m != NULL) + return(ENOBUFS); - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("al%d: no memory for tx list", sc->al_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("al%d: no memory for tx list", - sc->al_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->al_ptr->al_frag[0]; - f->al_status = 0; - f->al_data = vtophys(mtod(m_new, caddr_t)); - f->al_ctl = total_len = m_new->m_len; - f->al_ctl |= AL_TXCTL_TLINK|AL_TXCTL_FIRSTFRAG; - frag = 1; - } + sc->al_ldata->al_tx_list[cur].al_mbuf = m_head; + sc->al_ldata->al_tx_list[cur].al_ctl |= + AL_TXCTL_LASTFRAG|AL_TXCTL_FINT; + sc->al_ldata->al_tx_list[*txidx].al_status |= AL_TXSTAT_OWN; + sc->al_cdata.al_tx_cnt += cnt; + *txidx = frag; - c->al_mbuf = m_head; - c->al_lastdesc = frag - 1; - AL_TXCTL(c) |= AL_TXCTL_LASTFRAG|AL_TXCTL_FINT; - AL_TXNEXT(c) = vtophys(&c->al_nextdesc->al_ptr->al_frag[0]); return(0); } @@ -1592,40 +1453,25 @@ static void al_start(ifp) { struct al_softc *sc; struct mbuf *m_head = NULL; - struct al_chain *cur_tx = NULL, *start_tx; + u_int32_t idx; sc = ifp->if_softc; if (ifp->if_flags & IFF_OACTIVE) return; - if (sc->al_autoneg) { - sc->al_tx_pend = 1; - return; - } - - /* - * Check for an available queue slot. If there are none, - * punt. - */ - if (sc->al_cdata.al_tx_free->al_mbuf != NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } + idx = sc->al_cdata.al_tx_prod; - start_tx = sc->al_cdata.al_tx_free; - - while(sc->al_cdata.al_tx_free->al_mbuf == NULL) { + while(sc->al_ldata->al_tx_list[idx].al_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; - /* Pick a descriptor off the free list. */ - cur_tx = sc->al_cdata.al_tx_free; - sc->al_cdata.al_tx_free = cur_tx->al_nextdesc; - - /* Pack the data into the descriptor. */ - al_encap(sc, cur_tx, m_head); + if (al_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } #if NBPF > 0 /* @@ -1633,31 +1479,13 @@ static void al_start(ifp) * to him. */ if (ifp->if_bpf) - bpf_mtap(ifp, cur_tx->al_mbuf); -#endif - AL_TXOWN(cur_tx) = AL_TXSTAT_OWN; - CSR_WRITE_4(sc, AL_TXSTART, 0xFFFFFFFF); -#ifdef AL_TX_STALL_WAR - /* - * Work around some strange behavior in the Comet. For - * some reason, the transmitter will sometimes wedge if - * we queue up a descriptor chain that wraps from the end - * of the transmit list back to the beginning. If we reach - * the end of the list and still have more packets to queue, - * don't queue them now: end the transmit session here and - * then wait until it finishes before sending the other - * packets. - */ - if (cur_tx == &sc->al_cdata.al_tx_chain[AL_TX_LIST_CNT - 1]) { - ifp->if_flags |= IFF_OACTIVE; - break; - } + bpf_mtap(ifp, m_head); #endif } - sc->al_cdata.al_tx_tail = cur_tx; - if (sc->al_cdata.al_tx_head == NULL) - sc->al_cdata.al_tx_head = start_tx; + /* Transmit */ + sc->al_cdata.al_tx_prod = idx; + CSR_WRITE_4(sc, AL_TXSTART, 0xFFFFFFFF); /* * Set a timeout in case the chip goes out to lunch. @@ -1672,16 +1500,12 @@ static void al_init(xsc) { struct al_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; - u_int16_t phy_bmcr = 0; + struct mii_data *mii; int s; - if (sc->al_autoneg) - return; - s = splimp(); - if (sc->al_pinfo != NULL) - phy_bmcr = al_phy_readreg(sc, PHY_BMCR); + mii = device_get_softc(sc->al_miibus); /* * Cancel pending I/O and free all RX/TX buffers. @@ -1753,7 +1577,7 @@ static void al_init(xsc) /* * Load the address of the RX list. */ - CSR_WRITE_4(sc, AL_RXADDR, vtophys(sc->al_cdata.al_rx_head->al_ptr)); + CSR_WRITE_4(sc, AL_RXADDR, vtophys(&sc->al_ldata->al_rx_list[0])); CSR_WRITE_4(sc, AL_TXADDR, vtophys(&sc->al_ldata->al_tx_list[0])); /* @@ -1766,15 +1590,15 @@ static void al_init(xsc) AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_TX_ON|AL_NETCFG_RX_ON); CSR_WRITE_4(sc, AL_RXSTART, 0xFFFFFFFF); - /* Restore state of BMCR */ - if (sc->al_pinfo != NULL) - al_phy_writereg(sc, PHY_BMCR, phy_bmcr); + mii_mediachg(mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; (void)splx(s); + sc->al_stat_ch = timeout(al_tick, sc, hz); + return; } @@ -1785,19 +1609,11 @@ static int al_ifmedia_upd(ifp) struct ifnet *ifp; { struct al_softc *sc; - struct ifmedia *ifm; sc = ifp->if_softc; - ifm = &sc->ifmedia; - - if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) - return(EINVAL); - if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) - al_autoneg_mii(sc, AL_FLAG_SCHEDDELAY, 1); - else { - al_setmode_mii(sc, ifm->ifm_media); - } + if (ifp->if_flags & IFF_UP) + al_init(sc); return(0); } @@ -1810,42 +1626,14 @@ static void al_ifmedia_sts(ifp, ifmr) struct ifmediareq *ifmr; { struct al_softc *sc; - u_int16_t advert = 0, ability = 0; + struct mii_data *mii; sc = ifp->if_softc; - ifmr->ifm_active = IFM_ETHER; - - if (!(al_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { - if (al_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 (al_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - return; - } - - ability = al_phy_readreg(sc, PHY_LPAR); - advert = al_phy_readreg(sc, PHY_ANAR); - 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; - } + mii = device_get_softc(sc->al_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; return; } @@ -1857,6 +1645,7 @@ static int al_ioctl(ifp, command, data) { struct al_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; int s, error = 0; s = splimp(); @@ -1883,7 +1672,8 @@ static int al_ioctl(ifp, command, data) break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + mii = device_get_softc(sc->al_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = EINVAL; @@ -1902,20 +1692,9 @@ static void al_watchdog(ifp) sc = ifp->if_softc; - if (sc->al_autoneg) { - al_autoneg_mii(sc, AL_FLAG_DELAYTIMEO, 1); - return; - } - ifp->if_oerrors++; printf("al%d: watchdog timeout\n", sc->al_unit); - if (sc->al_pinfo != NULL) { - if (!(al_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) - printf("al%d: no carrier - transceiver " - "cable problem?\n", sc->al_unit); - } - al_stop(sc); al_reset(sc); al_init(sc); @@ -1939,6 +1718,7 @@ static void al_stop(sc) ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; + untimeout(al_tick, sc, sc->al_stat_ch); AL_CLRBIT(sc, AL_NETCFG, (AL_NETCFG_RX_ON|AL_NETCFG_TX_ON)); CSR_WRITE_4(sc, AL_IMR, 0x00000000); CSR_WRITE_4(sc, AL_TXADDR, 0x00000000); @@ -1948,9 +1728,9 @@ static void al_stop(sc) * Free data in the RX lists. */ for (i = 0; i < AL_RX_LIST_CNT; i++) { - if (sc->al_cdata.al_rx_chain[i].al_mbuf != NULL) { - m_freem(sc->al_cdata.al_rx_chain[i].al_mbuf); - sc->al_cdata.al_rx_chain[i].al_mbuf = NULL; + if (sc->al_ldata->al_rx_list[i].al_mbuf != NULL) { + m_freem(sc->al_ldata->al_rx_list[i].al_mbuf); + sc->al_ldata->al_rx_list[i].al_mbuf = NULL; } } bzero((char *)&sc->al_ldata->al_rx_list, @@ -1960,9 +1740,9 @@ static void al_stop(sc) * Free the TX list buffers. */ for (i = 0; i < AL_TX_LIST_CNT; i++) { - if (sc->al_cdata.al_tx_chain[i].al_mbuf != NULL) { - m_freem(sc->al_cdata.al_tx_chain[i].al_mbuf); - sc->al_cdata.al_tx_chain[i].al_mbuf = NULL; + if (sc->al_ldata->al_tx_list[i].al_mbuf != NULL) { + m_freem(sc->al_ldata->al_tx_list[i].al_mbuf); + sc->al_ldata->al_tx_list[i].al_mbuf = NULL; } } @@ -1978,22 +1758,13 @@ static void al_stop(sc) * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ -static void al_shutdown(arg, howto) - void *arg; - int howto; +static void al_shutdown(dev) + device_t dev; { - struct al_softc *sc = (struct al_softc *)arg; + struct al_softc *sc; - al_stop(sc); + sc = device_get_softc(dev); + /*al_stop(sc); */ return; } - -static struct pci_device al_device = { - "al", - al_probe, - al_attach, - &al_count, - NULL -}; -COMPAT_PCI_DRIVER(al, al_device); diff --git a/sys/pci/if_alreg.h b/sys/pci/if_alreg.h index 2af519b..243903e 100644 --- a/sys/pci/if_alreg.h +++ b/sys/pci/if_alreg.h @@ -214,6 +214,10 @@ #define AL_SIO_EESEL 0x00000800 #define AL_SIO_ROMCTL_WRITE 0x00002000 #define AL_SIO_ROMCTL_READ 0x00004000 +#define AL_SIO_MII_CLK 0x00010000 /* MDIO clock */ +#define AL_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ +#define AL_SIO_MII_DIR 0x00040000 /* MDIO dir */ +#define AL_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ #define AL_EECMD_WRITE 0x140 #define AL_EECMD_READ 0x180 @@ -348,10 +352,16 @@ struct al_wakup_record { */ struct al_desc { - volatile u_int32_t al_status; - volatile u_int32_t al_ctl; - volatile u_int32_t al_ptr1; - volatile u_int32_t al_ptr2; + u_int32_t al_status; + u_int32_t al_ctl; + u_int32_t al_ptr1; + u_int32_t al_ptr2; + /* Driver specific stuff. */ +#ifdef __i386__ + u_int32_t al_pad; +#endif + struct mbuf *al_mbuf; + struct al_desc *al_nextdesc; }; #define al_data al_ptr1 @@ -378,6 +388,7 @@ struct al_desc { #define AL_RXCTL_BUFLEN1 0x00000FFF #define AL_RXCTL_BUFLEN2 0x00FFF000 +#define AL_RXCTL_RLINK 0x01000000 #define AL_RXCTL_RLAST 0x02000000 #define AL_TXSTAT_DEFER 0x00000001 @@ -407,51 +418,20 @@ struct al_desc { #define AL_RX_LIST_CNT 64 #define AL_TX_LIST_CNT 128 #define AL_MIN_FRAMELEN 60 +#define AL_RXLEN 1536 -/* - * A tx 'super descriptor' is actually 16 regular descriptors - * back to back. - */ -struct al_txdesc { - volatile struct al_desc al_frag[AL_MAXFRAGS]; -}; - -#define AL_TXNEXT(x) x->al_ptr->al_frag[x->al_lastdesc].al_next -#define AL_TXSTATUS(x) x->al_ptr->al_frag[x->al_lastdesc].al_status -#define AL_TXCTL(x) x->al_ptr->al_frag[x->al_lastdesc].al_ctl -#define AL_TXDATA(x) x->al_ptr->al_frag[x->al_lastdesc].al_data - -#define AL_TXOWN(x) x->al_ptr->al_frag[0].al_status - -#define AL_UNSENT 0x12341234 +#define AL_INC(x, y) (x) = (x + 1) % y struct al_list_data { - volatile struct al_desc al_rx_list[AL_RX_LIST_CNT]; - volatile struct al_txdesc al_tx_list[AL_TX_LIST_CNT]; -}; - -struct al_chain { - volatile struct al_txdesc *al_ptr; - struct mbuf *al_mbuf; - struct al_chain *al_nextdesc; - u_int8_t al_lastdesc; -}; - -struct al_chain_onefrag { - volatile struct al_desc *al_ptr; - struct mbuf *al_mbuf; - struct al_chain_onefrag *al_nextdesc; + struct al_desc al_rx_list[AL_RX_LIST_CNT]; + struct al_desc al_tx_list[AL_TX_LIST_CNT]; }; struct al_chain_data { - struct al_chain_onefrag al_rx_chain[AL_RX_LIST_CNT]; - struct al_chain al_tx_chain[AL_TX_LIST_CNT]; - - struct al_chain_onefrag *al_rx_head; - - struct al_chain *al_tx_head; - struct al_chain *al_tx_tail; - struct al_chain *al_tx_free; + int al_tx_prod; + int al_tx_cons; + int al_tx_cnt; + int al_rx_prod; }; struct al_type { @@ -469,35 +449,27 @@ struct al_mii_frame { u_int16_t mii_data; }; -/* - * MII constants - */ #define AL_MII_STARTDELIM 0x01 #define AL_MII_READOP 0x02 #define AL_MII_WRITEOP 0x01 #define AL_MII_TURNAROUND 0x02 -#define AL_FLAG_FORCEDELAY 1 -#define AL_FLAG_SCHEDDELAY 2 -#define AL_FLAG_DELAYTIMEO 3 - struct al_softc { struct arpcom arpcom; /* interface info */ struct ifmedia ifmedia; /* media info */ bus_space_handle_t al_bhandle; /* bus space handle */ bus_space_tag_t al_btag; /* bus space tag */ + struct resource *al_res; + struct resource *al_irq; + void *al_intrhand; + device_t al_miibus; struct al_type *al_info; /* COMET adapter info */ - struct al_type *al_pinfo; /* phy info */ + int al_did; u_int8_t al_unit; /* interface number */ - u_int8_t al_type; - u_int8_t al_phy_addr; /* PHY address */ - u_int8_t al_tx_pend; /* TX pending */ - u_int8_t al_want_auto; - u_int8_t al_autoneg; - caddr_t al_ldata_ptr; struct al_list_data *al_ldata; struct al_chain_data al_cdata; u_int8_t al_cachesize; + struct callout_handle al_stat_ch; }; /* @@ -518,6 +490,7 @@ struct al_softc { bus_space_read_1(sc->al_btag, sc->al_bhandle, reg) #define AL_TIMEOUT 1000 +#define ETHER_ALIGN 2 /* * General constants that are fun to know. @@ -532,36 +505,9 @@ struct al_softc { #define AL_DEVICEID_AL981 0x0981 /* - * Texas Instruments PHY identifiers - */ -#define TI_PHY_VENDORID 0x4000 -#define TI_PHY_10BT 0x501F -#define TI_PHY_100VGPMI 0x502F - -/* - * These ID values are for the NS DP83840A 10/100 PHY - */ -#define NS_PHY_VENDORID 0x2000 -#define NS_PHY_83840A 0x5C0F - -/* - * Level 1 10/100 PHY + * AN985 device IDs. */ -#define LEVEL1_PHY_VENDORID 0x7810 -#define LEVEL1_PHY_LXT970 0x000F - -/* - * Intel 82555 10/100 PHY - */ -#define INTEL_PHY_VENDORID 0x0A28 -#define INTEL_PHY_82555 0x015F - -/* - * SEEQ 80220 10/100 PHY - */ -#define SEEQ_PHY_VENDORID 0x0016 -#define SEEQ_PHY_80220 0xF83F - +#define AL_DEVICEID_AN985 0x0985 /* * PCI low memory base and low I/O base register, and @@ -601,105 +547,6 @@ struct al_softc { #define AL_PME_EN 0x0010 #define AL_PME_STATUS 0x8000 -#define PHY_UNKNOWN 6 - -#define AL_PHYADDR_MIN 0x00 -#define AL_PHYADDR_MAL 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_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 0x0200 -#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 - -/* - * 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_RSVD1 0x0040 /* write as zero, don't care */ -#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 - #ifdef __alpha__ #undef vtophys #define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) |