diff --git a/src/add-ons/kernel/drivers/network/via-rhine/Jamfile b/src/add-ons/kernel/drivers/network/via-rhine/Jamfile index b1e00188f1..f212607505 100644 --- a/src/add-ons/kernel/drivers/network/via-rhine/Jamfile +++ b/src/add-ons/kernel/drivers/network/via-rhine/Jamfile @@ -43,3 +43,5 @@ HaikuInstallRelSymLink install-networking : /boot/home/config/add-ons/kernel/dri installed-symlink ; +SubInclude HAIKU_TOP src add-ons kernel drivers network via-rhine dev ; +SubInclude HAIKU_TOP src add-ons kernel drivers network via-rhine pci ; diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/Jamfile b/src/add-ons/kernel/drivers/network/via-rhine/dev/Jamfile new file mode 100644 index 0000000000..d50f5e27f6 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network via-rhine dev ; + +SubInclude HAIKU_TOP src add-ons kernel drivers network via-rhine dev mii ; diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/Jamfile b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/Jamfile new file mode 100644 index 0000000000..a21dbecf25 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network via-rhine dev mii ; + +UsePrivateHeaders kernel net ; + +UseHeaders [ FDirName $(SUBDIR) .. .. ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] : true ; + +SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 ] ; + +KernelStaticLibrary via_rhine_mii.a + : + ciphy.c + ukphy.c + ukphy_subr.c + ; + diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphy.c b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphy.c new file mode 100644 index 0000000000..e437af27d0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphy.c @@ -0,0 +1,435 @@ +/*- + * Copyright (c) 2004 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $ + */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/mii/ciphy.c,v 1.2 2005/01/06 01:42:55 imp Exp $"); + +/* + * Driver for the Cicada CS8201 10/100/1000 copper PHY. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include "miidevs.h" + +#include + +#include "miibus_if.h" + +#include +/* +#include +*/ +static int ciphy_probe(device_t); +static int ciphy_attach(device_t); + +static device_method_t ciphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ciphy_probe), + DEVMETHOD(device_attach, ciphy_attach), + DEVMETHOD(device_detach, mii_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t ciphy_devclass; + +static driver_t ciphy_driver = { + "ciphy", + ciphy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(ciphy, miibus, ciphy_driver, ciphy_devclass, 0, 0); + +static int ciphy_service(struct mii_softc *, struct mii_data *, int); +static void ciphy_status(struct mii_softc *); +static void ciphy_reset(struct mii_softc *); +static void ciphy_fixup(struct mii_softc *); + +static int +ciphy_probe(dev) + device_t dev; +{ + struct mii_attach_args *ma; + + ma = device_get_ivars(dev); + +device_printf(dev, "OUI: %x\n", MII_OUI(ma->mii_id1, ma->mii_id2)); +device_printf(dev, "MODEL: %x\n", MII_MODEL(ma->mii_id2)); + if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA && + MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201) { + device_set_desc(dev, MII_STR_CICADA_CS8201); + return(0); + } + + if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA && + MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201A) { + device_set_desc(dev, MII_STR_CICADA_CS8201A); + return(0); + } + + if (MII_OUI(ma->mii_id1, ma->mii_id2) == MII_OUI_CICADA && + MII_MODEL(ma->mii_id2) == MII_MODEL_CICADA_CS8201B) { + device_set_desc(dev, MII_STR_CICADA_CS8201B); + return(0); + } + + return(ENXIO); +} + +static int +ciphy_attach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = ciphy_service; + sc->mii_pdata = mii; + + sc->mii_flags |= MIIF_NOISOLATE; + mii->mii_instance++; + + ciphy_reset(sc); + + sc->mii_capabilities = + PHY_READ(sc, MII_BMSR) & ma->mii_capmask; + if (sc->mii_capabilities & BMSR_EXTSTAT) + sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); + device_printf(dev, " "); + mii_phy_add_media(sc); + printf("\n"); + + MIIBUS_MEDIAINIT(sc->mii_dev); + return(0); +} + +static int +ciphy_service(sc, mii, cmd) + struct mii_softc *sc; + struct mii_data *mii; + int cmd; +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int reg, speed, gig; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + reg = PHY_READ(sc, MII_BMCR); + PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + ciphy_fixup(sc); /* XXX hardware bug work-around */ + + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: +#ifdef foo + /* + * If we're already in auto mode, just return. + */ + if (PHY_READ(sc, CIPHY_MII_BMCR) & CIPHY_BMCR_AUTOEN) + return (0); +#endif + (void) mii_phy_auto(sc); + break; + case IFM_1000_T: + speed = CIPHY_S1000; + goto setit; + case IFM_100_TX: + speed = CIPHY_S100; + goto setit; + case IFM_10_T: + speed = CIPHY_S10; +setit: + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) { + speed |= CIPHY_BMCR_FDX; + gig = CIPHY_1000CTL_AFD; + } else { + gig = CIPHY_1000CTL_AHD; + } + + PHY_WRITE(sc, CIPHY_MII_1000CTL, 0); + PHY_WRITE(sc, CIPHY_MII_BMCR, speed); + PHY_WRITE(sc, CIPHY_MII_ANAR, CIPHY_SEL_TYPE); + + if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T) + break; + + PHY_WRITE(sc, CIPHY_MII_1000CTL, gig); + PHY_WRITE(sc, CIPHY_MII_BMCR, + speed|CIPHY_BMCR_AUTOEN|CIPHY_BMCR_STARTNEG); + + /* + * When setting the link manually, one side must + * be the master and the other the slave. However + * ifmedia doesn't give us a good way to specify + * this, so we fake it by using one of the LINK + * flags. If LINK0 is set, we program the PHY to + * be a master, otherwise it's a slave. + */ + if ((mii->mii_ifp->if_flags & IFF_LINK0)) { + PHY_WRITE(sc, CIPHY_MII_1000CTL, + gig|CIPHY_1000CTL_MSE|CIPHY_1000CTL_MSC); + } else { + PHY_WRITE(sc, CIPHY_MII_1000CTL, + gig|CIPHY_1000CTL_MSE); + } + break; + case IFM_NONE: + PHY_WRITE(sc, MII_BMCR, BMCR_ISO|BMCR_PDOWN); + break; + case IFM_100_T4: + default: + return (EINVAL); + } + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + break; + + /* + * Check to see if we have link. If we do, we don't + * need to restart the autonegotiation process. Read + * the BMSR twice in case it's latched. + */ + reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + if (reg & BMSR_LINK) + break; + + /* + * Only retry autonegotiation every 5 seconds. + */ + if (++sc->mii_ticks <= 5/*10*/) + break; + + sc->mii_ticks = 0; + mii_phy_auto(sc); + return (0); + } + + /* Update the media status. */ + ciphy_status(sc); + + /* + * Callback if something changed. Note that we need to poke + * apply fixups for certain PHY revs. + */ + if (sc->mii_media_active != mii->mii_media_active || + sc->mii_media_status != mii->mii_media_status || + cmd == MII_MEDIACHG) { + ciphy_fixup(sc); + } + mii_phy_update(sc, cmd); + return (0); +} + +static void +ciphy_status(sc) + struct mii_softc *sc; +{ + struct mii_data *mii = sc->mii_pdata; + int bmsr, bmcr; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR); + + if (bmsr & BMSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(sc, CIPHY_MII_BMCR); + + if (bmcr & CIPHY_BMCR_LOOP) + mii->mii_media_active |= IFM_LOOP; + + if (bmcr & CIPHY_BMCR_AUTOEN) { + if ((bmsr & CIPHY_BMSR_ACOMP) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + } + + bmsr = PHY_READ(sc, CIPHY_MII_AUXCSR); + switch (bmsr & CIPHY_AUXCSR_SPEED) { + case CIPHY_SPEED10: + mii->mii_media_active |= IFM_10_T; + break; + case CIPHY_SPEED100: + mii->mii_media_active |= IFM_100_TX; + break; + case CIPHY_SPEED1000: + mii->mii_media_active |= IFM_1000_T; + break; + default: + device_printf(sc->mii_dev, "unknown PHY speed %x\n", + bmsr & CIPHY_AUXCSR_SPEED); + break; + } + + if (bmsr & CIPHY_AUXCSR_FDX) + mii->mii_media_active |= IFM_FDX; + + return; +} + +static void +ciphy_reset(struct mii_softc *sc) +{ + mii_phy_reset(sc); + DELAY(1000); + + return; +} + +#define PHY_SETBIT(x, y, z) \ + PHY_WRITE(x, y, (PHY_READ(x, y) | (z))) +#define PHY_CLRBIT(x, y, z) \ + PHY_WRITE(x, y, (PHY_READ(x, y) & ~(z))) + +static void +ciphy_fixup(struct mii_softc *sc) +{ + uint16_t model; + uint16_t status, speed; + + model = MII_MODEL(PHY_READ(sc, CIPHY_MII_PHYIDR2)); + status = PHY_READ(sc, CIPHY_MII_AUXCSR); + speed = status & CIPHY_AUXCSR_SPEED; + + switch (model) { + case MII_MODEL_CICADA_CS8201: + + /* Turn off "aux mode" (whatever that means) */ + PHY_SETBIT(sc, CIPHY_MII_AUXCSR, CIPHY_AUXCSR_MDPPS); + + /* + * Work around speed polling bug in VT3119/VT3216 + * when using MII in full duplex mode. + */ + if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) && + (status & CIPHY_AUXCSR_FDX)) { + PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); + } else { + PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); + } + + /* Enable link/activity LED blink. */ + PHY_SETBIT(sc, CIPHY_MII_LED, CIPHY_LED_LINKACTBLINK); + + break; + + case MII_MODEL_CICADA_CS8201A: + case MII_MODEL_CICADA_CS8201B: + + /* + * Work around speed polling bug in VT3119/VT3216 + * when using MII in full duplex mode. + */ + if ((speed == CIPHY_SPEED10 || speed == CIPHY_SPEED100) && + (status & CIPHY_AUXCSR_FDX)) { + PHY_SETBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); + } else { + PHY_CLRBIT(sc, CIPHY_MII_10BTCSR, CIPHY_10BTCSR_ECHO); + } + + break; + default: + device_printf(sc->mii_dev, "unknown CICADA PHY model %x\n", + model); + break; + } + + return; +} diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphyreg.h b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphyreg.h new file mode 100644 index 0000000000..727441ab1b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ciphyreg.h @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 2004 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/mii/ciphyreg.h,v 1.2 2005/01/06 01:42:55 imp Exp $ + */ + +#ifndef _DEV_MII_CIPHYREG_H_ +#define _DEV_MII_CIPHYREG_H_ + +/* + * Register definitions for the Cicada CS8201 10/100/1000 gigE copper + * PHY, embedded within the VIA Networks VT6122 controller. + */ + +/* Command register */ +#define CIPHY_MII_BMCR 0x00 +#define CIPHY_BMCR_RESET 0x8000 +#define CIPHY_BMCR_LOOP 0x4000 +#define CIPHY_BMCR_SPD0 0x2000 /* speed select, lower bit */ +#define CIPHY_BMCR_AUTOEN 0x1000 /* Autoneg enabled */ +#define CIPHY_BMCR_PDOWN 0x0800 /* Power down */ +#define CIPHY_BMCR_STARTNEG 0x0200 /* Restart autoneg */ +#define CIPHY_BMCR_FDX 0x0100 /* Duplex mode */ +#define CIPHY_BMCR_CTEST 0x0080 /* Collision test enable */ +#define CIPHY_BMCR_SPD1 0x0040 /* Speed select, upper bit */ + +#define CIPHY_S1000 CIPHY_BMCR_SPD1 /* 1000mbps */ +#define CIPHY_S100 CIPHY_BMCR_SPD0 /* 100mpbs */ +#define CIPHY_S10 0 /* 10mbps */ + +/* Status register */ +#define CIPHY_MII_BMSR 0x01 +#define CIPHY_BMSR_100T4 0x8000 /* 100 base T4 capable */ +#define CIPHY_BMSR_100TXFDX 0x4000 /* 100 base Tx full duplex capable */ +#define CIPHY_BMSR_100TXHDX 0x2000 /* 100 base Tx half duplex capable */ +#define CIPHY_BMSR_10TFDX 0x1000 /* 10 base T full duplex capable */ +#define CIPHY_BMSR_10THDX 0x0800 /* 10 base T half duplex capable */ +#define CIPHY_BMSR_100T2FDX 0x0400 /* 100 base T2 full duplex capable */ +#define CIPHY_BMSR_100T2HDX 0x0200 /* 100 base T2 half duplex capable */ +#define CIPHY_BMSR_EXTSTS 0x0100 /* Extended status present */ +#define CIPHY_BMSR_PRESUB 0x0040 /* Preamble surpression */ +#define CIPHY_BMSR_ACOMP 0x0020 /* Autoneg complete */ +#define CIPHY_BMSR_RFAULT 0x0010 /* Remote fault condition occured */ +#define CIPHY_BMSR_ANEG 0x0008 /* Autoneg capable */ +#define CIPHY_BMSR_LINK 0x0004 /* Link status */ +#define CIPHY_BMSR_JABBER 0x0002 /* Jabber detected */ +#define CIPHY_BMSR_EXT 0x0001 /* Extended capability */ + +/* PHY ID registers */ +#define CIPHY_MII_PHYIDR1 0x02 +#define CIPHY_MII_PHYIDR2 0x03 + +/* Autoneg advertisement */ +#define CIPHY_MII_ANAR 0x04 +#define CIPHY_ANAR_NP 0x8000 /* Next page */ +#define CIPHY_ANAR_RF 0x2000 /* Remote fault */ +#define CIPHY_ANAR_ASP 0x0800 /* Asymmetric Pause */ +#define CIPHY_ANAR_PC 0x0400 /* Pause capable */ +#define CIPHY_ANAR_T4 0x0200 /* local device supports 100bT4 */ +#define CIPHY_ANAR_TX_FD 0x0100 /* local device supports 100bTx FD */ +#define CIPHY_ANAR_TX 0x0080 /* local device supports 100bTx */ +#define CIPHY_ANAR_10_FD 0x0040 /* local device supports 10bT FD */ +#define CIPHY_ANAR_10 0x0020 /* local device supports 10bT */ +#define CIPHY_ANAR_SEL 0x001F /* selector field, 00001=Ethernet */ + +/* Autoneg link partner ability */ +#define CIPHY_MII_ANLPAR 0x05 +#define CIPHY_ANLPAR_NP 0x8000 /* Next page */ +#define CIPHY_ANLPAR_ACK 0x4000 /* link partner acknowledge */ +#define CIPHY_ANLPAR_RF 0x2000 /* Remote fault */ +#define CIPHY_ANLPAR_ASP 0x0800 /* Asymmetric Pause */ +#define CIPHY_ANLPAR_PC 0x0400 /* Pause capable */ +#define CIPHY_ANLPAR_T4 0x0200 /* link partner supports 100bT4 */ +#define CIPHY_ANLPAR_TX_FD 0x0100 /* link partner supports 100bTx FD */ +#define CIPHY_ANLPAR_TX 0x0080 /* link partner supports 100bTx */ +#define CIPHY_ANLPAR_10_FD 0x0040 /* link partner supports 10bT FD */ +#define CIPHY_ANLPAR_10 0x0020 /* link partner supports 10bT */ +#define CIPHY_ANLPAR_SEL 0x001F /* selector field, 00001=Ethernet */ + +#define CIPHY_SEL_TYPE 0x0001 /* ethernet */ + +/* Antoneg expansion register */ +#define CIPHY_MII_ANER 0x06 +#define CIPHY_ANER_PDF 0x0010 /* Parallel detection fault */ +#define CIPHY_ANER_LPNP 0x0008 /* Link partner can next page */ +#define CIPHY_ANER_NP 0x0004 /* Local PHY can next page */ +#define CIPHY_ANER_RX 0x0002 /* Next page received */ +#define CIPHY_ANER_LPAN 0x0001 /* Link partner autoneg capable */ + +/* Autoneg next page transmit regisyer */ +#define CIPHY_MII_NEXTP 0x07 +#define CIPHY_NEXTP_MOREP 0x8000 /* More pages to follow */ +#define CIPHY_NEXTP_MESS 0x2000 /* 1 = message page, 0 = unformatted */ +#define CIPHY_NEXTP_ACK2 0x1000 /* MAC acknowledge */ +#define CIPHY_NEXTP_TOGGLE 0x0800 /* Toggle */ +#define CIPHY_NEXTP_CODE 0x07FF /* Code bits */ + +/* Autoneg link partner next page receive register */ +#define CIPHY_MII_NEXTP_LP 0x08 +#define CIPHY_NEXTPLP_MOREP 0x8000 /* More pages to follow */ +#define CIPHY_NEXTPLP_MESS 0x2000 /* 1 = message page, 0 = unformatted */ +#define CIPHY_NEXTPLP_ACK2 0x1000 /* MAC acknowledge */ +#define CIPHY_NEXTPLP_TOGGLE 0x0800 /* Toggle */ +#define CIPHY_NEXTPLP_CODE 0x07FF /* Code bits */ + +/* 1000BT control register */ +#define CIPHY_MII_1000CTL 0x09 +#define CIPHY_1000CTL_TST 0xE000 /* test modes */ +#define CIPHY_1000CTL_MSE 0x1000 /* Master/Slave manual enable */ +#define CIPHY_1000CTL_MSC 0x0800 /* Master/Slave select */ +#define CIPHY_1000CTL_RD 0x0400 /* Repeater/DTE */ +#define CIPHY_1000CTL_AFD 0x0200 /* Advertise full duplex */ +#define CIPHY_1000CTL_AHD 0x0100 /* Advertise half duplex */ + +#define CIPHY_TEST_TX_JITTER 0x2000 +#define CIPHY_TEST_TX_JITTER_MASTER_MODE 0x4000 +#define CIPHY_TEST_TX_JITTER_SLAVE_MODE 0x6000 +#define CIPHY_TEST_TX_DISTORTION 0x8000 + +/* 1000BT status register */ +#define CIPHY_MII_1000STS 0x0A +#define CIPHY_1000STS_MSF 0x8000 /* Master/slave fault */ +#define CIPHY_1000STS_MSR 0x4000 /* Master/slave result */ +#define CIPHY_1000STS_LRS 0x2000 /* Local receiver status */ +#define CIPHY_1000STS_RRS 0x1000 /* Remote receiver status */ +#define CIPHY_1000STS_LPFD 0x0800 /* Link partner can FD */ +#define CIPHY_1000STS_LPHD 0x0400 /* Link partner can HD */ +#define CIPHY_1000STS_IEC 0x00FF /* Idle error count */ + +#define CIPHY_MII_EXTSTS 0x0F /* Extended status */ +#define CIPHY_EXTSTS_X_FD_CAP 0x8000 /* 1000base-X FD capable */ +#define CIPHY_EXTSTS_X_HD_CAP 0x4000 /* 1000base-X HD capable */ +#define CIPHY_EXTSTS_T_FD_CAP 0x2000 /* 1000base-T FD capable */ +#define CIPHY_EXTSTS_T_HD_CAP 0x1000 /* 1000base-T HD capable */ + +/* 1000BT status extension register #1 */ +#define CIPHY_MII_1000STS1 0x0F +#define CIPHY_1000STS1_1000XFDX 0x8000 /* 1000baseX FDX capable */ +#define CIPHY_1000STS1_1000XHDX 0x4000 /* 1000baseX HDX capable */ +#define CIPHY_1000STS1_1000TFDX 0x2000 /* 1000baseT FDX capable */ +#define CIPHY_1000STS1_1000THDX 0x1000 /* 1000baseT HDX capable */ + +/* Vendor-specific PHY registers */ + +/* 100baseTX status extention register */ +#define CIPHY_MII_100STS 0x10 +#define CIPHY_100STS_DESLCK 0x8000 /* descrambler locked */ +#define CIPHY_100STS_LKCERR 0x4000 /* lock error detected/lock lost */ +#define CIPHY_100STS_DISC 0x2000 /* disconnect state */ +#define CIPHY_100STS_LINK 0x1000 /* current link state */ +#define CIPHY_100STS_RXERR 0x0800 /* receive error detected */ +#define CIPHY_100STS_TXERR 0x0400 /* transmit error detected */ +#define CIPHY_100STS_SSDERR 0x0200 /* false carrier error detected */ +#define CIPHY_100STS_ESDERR 0x0100 /* premature end of stream error */ + +/* 1000BT status extention register #2 */ +#define CIPHY_MII_1000STS2 0x11 +#define CIPHY_1000STS2_DESLCK 0x8000 /* descrambler locked */ +#define CIPHY_1000STS2_LKCERR 0x4000 /* lock error detected/lock lost */ +#define CIPHY_1000STS2_DISC 0x2000 /* disconnect state */ +#define CIPHY_1000STS2_LINK 0x1000 /* current link state */ +#define CIPHY_1000STS2_RXERR 0x0800 /* receive error detected */ +#define CIPHY_1000STS2_TXERR 0x0400 /* transmit error detected */ +#define CIPHY_1000STS2_SSDERR 0x0200 /* false carrier error detected */ +#define CIPHY_1000STS2_ESDERR 0x0100 /* premature end of stream error */ +#define CIPHY_1000STS2_CARREXT 0x0080 /* carrier extention err detected */ +#define CIPHY_1000STS2_BCM5400 0x0040 /* non-complient BCM5400 detected */ + +/* Bypass control register */ +#define CIPHY_MII_BYPASS 0x12 +#define CIPHY_BYPASS_TX 0x8000 /* transmit disable */ +#define CIPHY_BYPASS_4B5B 0x4000 /* bypass the 4B5B encoder */ +#define CIPHY_BYPASS_SCRAM 0x2000 /* bypass scrambler */ +#define CIPHY_BYPASS_DSCAM 0x1000 /* bypass descrambler */ +#define CIPHY_BYPASS_PCSRX 0x0800 /* bypass PCS receive */ +#define CIPHY_BYPASS_PCSTX 0x0400 /* bypass PCS transmit */ +#define CIPHY_BYPASS_LFI 0x0200 /* bypass LFI timer */ +#define CIPHY_BYPASS_TXCLK 0x0100 /* enable transmit clock on LED4 pin */ +#define CIPHY_BYPASS_BCM5400_F 0x0080 /* force BCM5400 detect */ +#define CIPHY_BYPASS_BCM5400 0x0040 /* bypass BCM5400 detect */ +#define CIPHY_BYPASS_PAIRSWAP 0x0020 /* disable automatic pair swap */ +#define CIPHY_BYPASS_POLARITY 0x0010 /* disable polarity correction */ +#define CIPHY_BYPASS_PARALLEL 0x0008 /* parallel detect enable */ +#define CIPHY_BYPASS_PULSE 0x0004 /* disable pulse shaping filter */ +#define CIPHY_BYPASS_1000BNP 0x0002 /* disable 1000BT next page exchange */ + +/* RX error count register */ +#define CIPHY_MII_RXERR 0x13 + +/* False carrier sense count register */ +#define CIPHY_MII_FCSERR 0x14 + +/* Ddisconnect error counter */ +#define CIPHY_MII_DISCERR 0x15 + +/* 10baseT control/status register */ +#define CIPHY_MII_10BTCSR 0x16 +#define CIPHY_10BTCSR_DLIT 0x8000 /* Disable data link integrity test */ +#define CIPHY_10BTCSR_JABBER 0x4000 /* Disable jabber detect */ +#define CIPHY_10BTCSR_ECHO 0x2000 /* Disable echo mode */ +#define CIPHY_10BTCSR_SQE 0x1000 /* Disable signal quality error */ +#define CIPHY_10BTCSR_SQUENCH 0x0C00 /* Squelch control */ +#define CIPHY_10BTCSR_EOFERR 0x0100 /* End of Frame error */ +#define CIPHY_10BTCSR_DISC 0x0080 /* Disconnect status */ +#define CIPHY_10BTCSR_LINK 0x0040 /* current link state */ +#define CIPHY_10BTCSR_ITRIM 0x0038 /* current reference trim */ +#define CIPHY_10BTCSR_CSR 0x0006 /* CSR behavior control */ + +#define CIPHY_SQUELCH_300MV 0x0000 +#define CIPHY_SQUELCH_197MV 0x0400 +#define CIPHY_SQUELCH_450MV 0x0800 +#define CIPHY_SQUELCH_RSVD 0x0C00 + +#define CIPHY_ITRIM_PLUS2 0x0000 +#define CIPHY_ITRIM_PLUS4 0x0008 +#define CIPHY_ITRIM_PLUS6 0x0010 +#define CIPHY_ITRIM_PLUS6_ 0x0018 +#define CIPHY_ITRIM_MINUS4 0x0020 +#define CIPHY_ITRIM_MINUS4_ 0x0028 +#define CIPHY_ITRIM_MINUS2 0x0030 +#define CIPHY_ITRIM_ZERO 0x0038 + +/* Extended PHY control register #1 */ +#define CIPHY_MII_ECTL1 0x17 +#define CIPHY_ECTL1_ACTIPHY 0x0020 /* Enable ActiPHY power saving */ + +/* Extended PHY control register #2 */ +#define CIPHY_MII_ECTL2 0x18 +#define CIPHY_ECTL2_ERATE 0xE000 /* 10/1000 edge rate control */ +#define CIPHY_ECTL2_VTRIM 0x1C00 /* voltage reference trim */ +#define CIPHY_ECTL2_CABLELEN 0x000E /* Cable quality/length */ +#define CIPHY_ECTL2_ANALOGLOOP 0x0001 /* 1000BT analog loopback */ + +#define CIPHY_CABLELEN_0TO10M 0x0000 +#define CIPHY_CABLELEN_10TO20M 0x0002 +#define CIPHY_CABLELEN_20TO40M 0x0004 +#define CIPHY_CABLELEN_40TO80M 0x0006 +#define CIPHY_CABLELEN_80TO100M 0x0008 +#define CIPHY_CABLELEN_100TO140M 0x000A +#define CIPHY_CABLELEN_140TO180M 0x000C +#define CIPHY_CABLELEN_OVER180M 0x000E + +/* Interrupt mask register */ +#define CIPHY_MII_IMR 0x19 +#define CIPHY_IMR_PINENABLE 0x8000 /* Interrupt pin enable */ +#define CIPHY_IMR_SPEED 0x4000 /* speed changed event */ +#define CIPHY_IMR_LINK 0x2000 /* link change/ActiPHY event */ +#define CIPHY_IMR_DPX 0x1000 /* duplex change event */ +#define CIPHY_IMR_ANEGERR 0x0800 /* autoneg error event */ +#define CIPHY_IMR_ANEGDONE 0x0400 /* autoneg done event */ +#define CIPHY_IMR_NPRX 0x0200 /* page received event */ +#define CIPHY_IMR_SYMERR 0x0100 /* symbol error event */ +#define CIPHY_IMR_LOCKERR 0x0080 /* descrambler lock lost event */ +#define CIPHY_IMR_XOVER 0x0040 /* MDI crossover change event */ +#define CIPHY_IMR_POLARITY 0x0020 /* polarity change event */ +#define CIPHY_IMR_JABBER 0x0010 /* jabber detect event */ +#define CIPHY_IMR_SSDERR 0x0008 /* false carrier detect event */ +#define CIPHY_IMR_ESDERR 0x0004 /* parallel detect error event */ +#define CIPHY_IMR_MASTERSLAVE 0x0002 /* master/slave resolve done event */ +#define CIPHY_IMR_RXERR 0x0001 /* RX error event */ + +/* Interrupt status register */ +#define CIPHY_MII_ISR 0x1A +#define CIPHY_ISR_IPENDING 0x8000 /* Interrupt is pending */ +#define CIPHY_ISR_SPEED 0x4000 /* speed changed event */ +#define CIPHY_ISR_LINK 0x2000 /* link change/ActiPHY event */ +#define CIPHY_ISR_DPX 0x1000 /* duplex change event */ +#define CIPHY_ISR_ANEGERR 0x0800 /* autoneg error event */ +#define CIPHY_ISR_ANEGDONE 0x0400 /* autoneg done event */ +#define CIPHY_ISR_NPRX 0x0200 /* page received event */ +#define CIPHY_ISR_SYMERR 0x0100 /* symbol error event */ +#define CIPHY_ISR_LOCKERR 0x0080 /* descrambler lock lost event */ +#define CIPHY_ISR_XOVER 0x0040 /* MDI crossover change event */ +#define CIPHY_ISR_POLARITY 0x0020 /* polarity change event */ +#define CIPHY_ISR_JABBER 0x0010 /* jabber detect event */ +#define CIPHY_ISR_SSDERR 0x0008 /* false carrier detect event */ +#define CIPHY_ISR_ESDERR 0x0004 /* parallel detect error event */ +#define CIPHY_ISR_MASTERSLAVE 0x0002 /* master/slave resolve done event */ +#define CIPHY_ISR_RXERR 0x0001 /* RX error event */ + +/* LED control register */ +#define CIPHY_MII_LED 0x1B +#define CIPHY_LED_LINK10FORCE 0x8000 /* Force on link10 LED */ +#define CIPHY_LED_LINK10DIS 0x4000 /* Disable link10 LED */ +#define CIPHY_LED_LINK100FORCE 0x2000 /* Force on link10 LED */ +#define CIPHY_LED_LINK100DIS 0x1000 /* Disable link100 LED */ +#define CIPHY_LED_LINK1000FORCE 0x0800 /* Force on link1000 LED */ +#define CIPHY_LED_LINK1000DIS 0x0400 /* Disable link1000 LED */ +#define CIPHY_LED_FDXFORCE 0x0200 /* Force on duplex LED */ +#define CIPHY_LED_FDXDIS 0x0100 /* Disable duplex LED */ +#define CIPHY_LED_ACTFORCE 0x0080 /* Force on activity LED */ +#define CIPHY_LED_ACTDIS 0x0040 /* Disable activity LED */ +#define CIPHY_LED_PULSE 0x0008 /* LED pulse enable */ +#define CIPHY_LED_LINKACTBLINK 0x0004 /* enable link/activity LED blink */ +#define CIPHY_LED_BLINKRATE 0x0002 /* blink rate 0=10hz, 1=5hz */ + +/* Auxilliary control and status register */ +#define CIPHY_MII_AUXCSR 0x1C +#define CIPHY_AUXCSR_ANEGDONE 0x8000 /* Autoneg complete */ +#define CIPHY_AUXCSR_ANEGOFF 0x4000 /* Autoneg disabled */ +#define CIPHY_AUXCSR_XOVER 0x2000 /* MDI/MDI-X crossover indication */ +#define CIPHY_AUXCSR_PAIRSWAP 0x1000 /* pair swap indication */ +#define CIPHY_AUXCSR_APOLARITY 0x0800 /* polarity inversion pair A */ +#define CIPHY_AUXCSR_BPOLARITY 0x0400 /* polarity inversion pair B */ +#define CIPHY_AUXCSR_CPOLARITY 0x0200 /* polarity inversion pair C */ +#define CIPHY_AUXCSR_DPOLARITY 0x0100 /* polarity inversion pair D */ +#define CIPHY_AUXCSR_FDX 0x0020 /* duplex 1=full, 0=half */ +#define CIPHY_AUXCSR_SPEED 0x0018 /* speed */ +#define CIPHY_AUXCSR_MDPPS 0x0004 /* No idea, not documented */ +#define CIPHY_AUXCSR_STICKYREST 0x0002 /* reset clears sticky bits */ + +#define CIPHY_SPEED10 0x0000 +#define CIPHY_SPEED100 0x0008 +#define CIPHY_SPEED1000 0x0010 + +/* Delay skew status register */ +#define CIPHY_MII_DSKEW 0x1D +#define CIPHY_DSKEW_PAIRA 0x7000 /* Pair A skew in symbol times */ +#define CIPHY_DSKEW_PAIRB 0x0700 /* Pair B skew in symbol times */ +#define CIPHY_DSKEW_PAIRC 0x0070 /* Pair C skew in symbol times */ +#define CIPHY_DSKEW_PAIRD 0x0007 /* Pair D skew in symbol times */ + +#endif /* _DEV_CIPHY_MIIREG_H_ */ diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/miidevs.h b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/miidevs.h new file mode 100644 index 0000000000..5b93b049fe --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/miidevs.h @@ -0,0 +1,18 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _FBSD_MII_DEVS_H +#define _FBSD_MII_DEVS_H + +#define MII_OUI_CICADA 0x0003f1 + +#define MII_MODEL_CICADA_CS8201 0x0001 +#define MII_MODEL_CICADA_CS8201A 0x0020 +#define MII_MODEL_CICADA_CS8201B 0x0021 + +#define MII_STR_CICADA_CS8201 "Cicada CS8201 10/100/1000TX PHY" +#define MII_STR_CICADA_CS8201A MII_STR_CICADA_CS8201 +#define MII_STR_CICADA_CS8201B MII_STR_CICADA_CS8201 + +#endif /* _FBSD_MII_DEVS_H */ diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy.c b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy.c new file mode 100644 index 0000000000..4ce5a9c771 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy.c @@ -0,0 +1,227 @@ +/* $NetBSD: ukphy.c,v 1.2 1999/04/23 04:24:32 thorpej Exp $ */ + +/*- + * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, and by Frank van der Linden. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1997 Manuel Bouyer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy.c,v 1.17 2005/01/06 01:42:56 imp Exp $"); + +/* + * driver for generic unknown PHYs + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "miibus_if.h" + +static int ukphy_probe(device_t); +static int ukphy_attach(device_t); + +static device_method_t ukphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, ukphy_probe), + DEVMETHOD(device_attach, ukphy_attach), + DEVMETHOD(device_detach, mii_phy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t ukphy_devclass; + +static driver_t ukphy_driver = { + "ukphy", + ukphy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(ukphy, miibus, ukphy_driver, ukphy_devclass, 0, 0); + +static int ukphy_service(struct mii_softc *, struct mii_data *, int); + +static int +ukphy_probe(dev) + device_t dev; +{ + + /* + * We know something is here, so always match at a low priority. + */ + device_set_desc(dev, "Generic IEEE 802.3u media interface"); + return (-100); +} + +static int +ukphy_attach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + if (bootverbose) + device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n", + MII_OUI(ma->mii_id1, ma->mii_id2), + MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2)); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = ukphy_service; + sc->mii_pdata = mii; + + mii->mii_instance++; + + sc->mii_flags |= MIIF_NOISOLATE; + + mii_phy_reset(sc); + + sc->mii_capabilities = + PHY_READ(sc, MII_BMSR) & ma->mii_capmask; + if (sc->mii_capabilities & BMSR_EXTSTAT) + sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); + device_printf(dev, " "); + mii_phy_add_media(sc); + printf("\n"); + + MIIBUS_MEDIAINIT(sc->mii_dev); + mii_phy_setmedia(sc); + + return(0); +} + +static int +ukphy_service(sc, mii, cmd) + struct mii_softc *sc; + struct mii_data *mii; + int cmd; +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int reg; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + reg = PHY_READ(sc, MII_BMCR); + PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO); + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + mii_phy_setmedia(sc); + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + if (mii_phy_tick(sc) == EJUSTRETURN) + return (0); + break; + } + + /* Update the media status. */ + ukphy_status(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + return (0); +} diff --git a/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy_subr.c b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy_subr.c new file mode 100644 index 0000000000..fb40b75c1d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/dev/mii/ukphy_subr.c @@ -0,0 +1,129 @@ +/* $NetBSD: ukphy_subr.c,v 1.2 1998/11/05 04:08:02 thorpej Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, and by Frank van der Linden. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/sys/dev/mii/ukphy_subr.c,v 1.8.8.1 2006/07/19 04:40:26 yongari Exp $"); + +/* + * Subroutines shared by the ukphy driver and other PHY drivers. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "miibus_if.h" + +/* + * Media status subroutine. If a PHY driver does media detection simply + * by decoding the NWay autonegotiation, use this routine. + */ +void +ukphy_status(struct mii_softc *phy) +{ + struct mii_data *mii = phy->mii_pdata; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int bmsr, bmcr, anlpar, gtcr, gtsr; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR); + if (bmsr & BMSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(phy, MII_BMCR); + if (bmcr & BMCR_ISO) { + mii->mii_media_active |= IFM_NONE; + mii->mii_media_status = 0; + return; + } + + if (bmcr & BMCR_LOOP) + mii->mii_media_active |= IFM_LOOP; + + if (bmcr & BMCR_AUTOEN) { + /* + * NWay autonegotiation takes the highest-order common + * bit of the ANAR and ANLPAR (i.e. best media advertised + * both by us and our link partner). + */ + if ((bmsr & BMSR_ACOMP) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + + anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR); + if ((phy->mii_flags & MIIF_HAVE_GTCR) != 0 && + (phy->mii_extcapabilities & + (EXTSR_1000THDX | EXTSR_1000TFDX)) != 0) { + gtcr = PHY_READ(phy, MII_100T2CR); + gtsr = PHY_READ(phy, MII_100T2SR); + } else + gtcr = gtsr = 0; + + if ((gtcr & GTCR_ADV_1000TFDX) && (gtsr & GTSR_LP_1000TFDX)) + mii->mii_media_active |= IFM_1000_T|IFM_FDX; + else if ((gtcr & GTCR_ADV_1000THDX) && + (gtsr & GTSR_LP_1000THDX)) + mii->mii_media_active |= IFM_1000_T; + else if (anlpar & ANLPAR_T4) + mii->mii_media_active |= IFM_100_T4; + else if (anlpar & ANLPAR_TX_FD) + mii->mii_media_active |= IFM_100_TX|IFM_FDX; + else if (anlpar & ANLPAR_TX) + mii->mii_media_active |= IFM_100_TX; + else if (anlpar & ANLPAR_10_FD) + mii->mii_media_active |= IFM_10_T|IFM_FDX; + else if (anlpar & ANLPAR_10) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_NONE; + } else + mii->mii_media_active = ife->ifm_media; +} diff --git a/src/add-ons/kernel/drivers/network/via-rhine/pci/Jamfile b/src/add-ons/kernel/drivers/network/via-rhine/pci/Jamfile new file mode 100644 index 0000000000..7b1b8735e1 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/pci/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network via-rhine pci ; + +SubDirCcFlags -Wall ; + +UsePrivateHeaders kernel net ; + +UseHeaders [ FDirName $(SUBDIR) .. ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] : true ; + +SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 ] ; + +KernelAddon via_rhine : + if_vr.c + glue.c + : libfreebsd_network.a via_rhine_mii.a + ; + diff --git a/src/add-ons/kernel/drivers/network/via-rhine/pci/glue.c b/src/add-ons/kernel/drivers/network/via-rhine/pci/glue.c new file mode 100644 index 0000000000..6e3187263b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/pci/glue.c @@ -0,0 +1,53 @@ +/* + * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "if_vrreg.h" + + +HAIKU_FBSD_DRIVER_GLUE(via_rhine, vr, pci); +HAIKU_DRIVER_REQUIREMENTS(FBSD_TASKQUEUES | FBSD_SWI_TASKQUEUE); + + +extern driver_t *DRIVER_MODULE_NAME(ciphy, miibus); +extern driver_t *DRIVER_MODULE_NAME(ukphy, miibus); + + +driver_t * +__haiku_select_miibus_driver(device_t dev) +{ + driver_t *drivers[] = { + DRIVER_MODULE_NAME(ciphy, miibus), + DRIVER_MODULE_NAME(ukphy, miibus) + }; + + return __haiku_probe_miibus(dev, drivers, 2); +} + + +int +__haiku_disable_interrupts(device_t dev) +{ + struct vr_softc *sc = device_get_softc(dev); + + if (CSR_READ_2(sc, VR_ISR) == 0) + return 0; + + CSR_WRITE_2(sc, VR_IMR, 0x0000); + return 1; +} + + +void +__haiku_reenable_interrupts(device_t dev) +{ + struct vr_softc *sc = device_get_softc(dev); + + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); +} + + diff --git a/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vr.c b/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vr.c new file mode 100644 index 0000000000..3d2f0ffc23 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vr.c @@ -0,0 +1,1707 @@ +/*- + * Copyright (c) 1997, 1998 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/sys/pci/if_vr.c,v 1.104.2.6 2006/03/17 21:30:57 glebius Exp $"); + +/* + * VIA Rhine fast ethernet PCI NIC driver + * + * Supports various network adapters based on the VIA Rhine + * and Rhine II PCI controllers, including the D-Link DFE530TX. + * Datasheets are available at http://www.via.com.tw. + * + * Written by Bill Paul + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The VIA Rhine controllers are similar in some respects to the + * the DEC tulip chips, except less complicated. The controller + * uses an MII bus and an external physical layer interface. The + * receiver has a one entry perfect filter and a 64-bit hash table + * multicast filter. Transmit and receive descriptors are similar + * to the tulip. + * + * The Rhine has a serious flaw in its transmit DMA mechanism: + * transmit buffers must be longword aligned. Unfortunately, + * FreeBSD doesn't guarantee that mbufs will be filled in starting + * at longword boundaries, so we have to do a buffer copy before + * transmission. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include /* for vtophys */ +#include /* for vtophys */ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define VR_USEIOSPACE + +#include + +MODULE_DEPEND(vr, pci, 1, 1, 1); +MODULE_DEPEND(vr, ether, 1, 1, 1); +MODULE_DEPEND(vr, miibus, 1, 1, 1); + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#undef VR_USESWSHIFT + +/* + * Various supported device vendors/types and their names. + */ +static struct vr_type vr_devs[] = { + { VIA_VENDORID, VIA_DEVICEID_RHINE, + "VIA VT3043 Rhine I 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_II, + "VIA VT86C100A Rhine II 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_II_2, + "VIA VT6102 Rhine II 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_III, + "VIA VT6105 Rhine III 10/100BaseTX" }, + { VIA_VENDORID, VIA_DEVICEID_RHINE_III_M, + "VIA VT6105M Rhine III 10/100BaseTX" }, + { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, + "Delta Electronics Rhine II 10/100BaseTX" }, + { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, + "Addtron Technology Rhine II 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int vr_probe(device_t); +static int vr_attach(device_t); +static int vr_detach(device_t); + +static int vr_newbuf(struct vr_softc *, struct vr_chain_onefrag *, + struct mbuf *); +static int vr_encap(struct vr_softc *, struct vr_chain *, struct mbuf * ); + +static void vr_rxeof(struct vr_softc *); +static void vr_rxeoc(struct vr_softc *); +static void vr_txeof(struct vr_softc *); +static void vr_tick(void *); +static void vr_intr(void *); +static void vr_start(struct ifnet *); +static void vr_start_locked(struct ifnet *); +static int vr_ioctl(struct ifnet *, u_long, caddr_t); +static void vr_init(void *); +static void vr_init_locked(struct vr_softc *); +static void vr_stop(struct vr_softc *); +static void vr_watchdog(struct ifnet *); +static void vr_shutdown(device_t); +static int vr_ifmedia_upd(struct ifnet *); +static void vr_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +#ifdef VR_USESWSHIFT +static void vr_mii_sync(struct vr_softc *); +static void vr_mii_send(struct vr_softc *, uint32_t, int); +#endif +static int vr_mii_readreg(struct vr_softc *, struct vr_mii_frame *); +static int vr_mii_writereg(struct vr_softc *, struct vr_mii_frame *); +static int vr_miibus_readreg(device_t, uint16_t, uint16_t); +static int vr_miibus_writereg(device_t, uint16_t, uint16_t, uint16_t); +static void vr_miibus_statchg(device_t); + +static void vr_setcfg(struct vr_softc *, int); +static void vr_setmulti(struct vr_softc *); +static void vr_reset(struct vr_softc *); +static int vr_list_rx_init(struct vr_softc *); +static int vr_list_tx_init(struct vr_softc *); + +#ifdef VR_USEIOSPACE +#define VR_RES SYS_RES_IOPORT +#define VR_RID VR_PCI_LOIO +#else +#define VR_RES SYS_RES_MEMORY +#define VR_RID VR_PCI_LOMEM +#endif + +static device_method_t vr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, vr_probe), + DEVMETHOD(device_attach, vr_attach), + DEVMETHOD(device_detach, vr_detach), + DEVMETHOD(device_shutdown, vr_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, vr_miibus_readreg), + DEVMETHOD(miibus_writereg, vr_miibus_writereg), + DEVMETHOD(miibus_statchg, vr_miibus_statchg), + + { 0, 0 } +}; + +static driver_t vr_driver = { + "vr", + vr_methods, + sizeof(struct vr_softc) +}; + +static devclass_t vr_devclass; + +DRIVER_MODULE(vr, pci, vr_driver, vr_devclass, 0, 0); +DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); + +#define VR_SETBIT(sc, reg, x) \ + CSR_WRITE_1(sc, reg, \ + CSR_READ_1(sc, reg) | (x)) + +#define VR_CLRBIT(sc, reg, x) \ + CSR_WRITE_1(sc, reg, \ + CSR_READ_1(sc, reg) & ~(x)) + +#define VR_SETBIT16(sc, reg, x) \ + CSR_WRITE_2(sc, reg, \ + CSR_READ_2(sc, reg) | (x)) + +#define VR_CLRBIT16(sc, reg, x) \ + CSR_WRITE_2(sc, reg, \ + CSR_READ_2(sc, reg) & ~(x)) + +#define VR_SETBIT32(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | (x)) + +#define VR_CLRBIT32(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~(x)) + +#define SIO_SET(x) \ + CSR_WRITE_1(sc, VR_MIICMD, \ + CSR_READ_1(sc, VR_MIICMD) | (x)) + +#define SIO_CLR(x) \ + CSR_WRITE_1(sc, VR_MIICMD, \ + CSR_READ_1(sc, VR_MIICMD) & ~(x)) + +#ifdef VR_USESWSHIFT +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void +vr_mii_sync(struct vr_softc *sc) +{ + register int i; + + SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); + + for (i = 0; i < 32; i++) { + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + } +} + +/* + * Clock a series of bits through the MII. + */ +static void +vr_mii_send(struct vr_softc *sc, uint32_t bits, int cnt) +{ + int i; + + SIO_CLR(VR_MIICMD_CLK); + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) { + if (bits & i) { + SIO_SET(VR_MIICMD_DATAIN); + } else { + SIO_CLR(VR_MIICMD_DATAIN); + } + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + } +} +#endif + +/* + * Read an PHY register through the MII. + */ +static int +vr_mii_readreg(struct vr_softc *sc, struct vr_mii_frame *frame) +#ifdef VR_USESWSHIFT +{ + int i, ack; + + /* Set up frame for RX. */ + frame->mii_stdelim = VR_MII_STARTDELIM; + frame->mii_opcode = VR_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + CSR_WRITE_1(sc, VR_MIICMD, 0); + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); + + /* Turn on data xmit. */ + SIO_SET(VR_MIICMD_DIR); + + vr_mii_sync(sc); + + /* Send command/address info. */ + vr_mii_send(sc, frame->mii_stdelim, 2); + vr_mii_send(sc, frame->mii_opcode, 2); + vr_mii_send(sc, frame->mii_phyaddr, 5); + vr_mii_send(sc, frame->mii_regaddr, 5); + + /* Idle bit. */ + SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + + /* Turn off xmit. */ + SIO_CLR(VR_MIICMD_DIR); + + /* Check for ack */ + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + + /* + * 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++) { + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + if (!ack) { + if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) + frame->mii_data |= i; + DELAY(1); + } + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + } + +fail: + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + + if (ack) + return (1); + return (0); +} +#else +{ + int i; + + /* Set the PHY address. */ + CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| + frame->mii_phyaddr); + + /* Set the register address. */ + CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_READ_ENB); + + for (i = 0; i < 10000; i++) { + if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_READ_ENB) == 0) + break; + DELAY(1); + } + frame->mii_data = CSR_READ_2(sc, VR_MIIDATA); + + return (0); +} +#endif + + +/* + * Write to a PHY register through the MII. + */ +static int +vr_mii_writereg(struct vr_softc *sc, struct vr_mii_frame *frame) +#ifdef VR_USESWSHIFT +{ + CSR_WRITE_1(sc, VR_MIICMD, 0); + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); + + /* Set up frame for TX. */ + frame->mii_stdelim = VR_MII_STARTDELIM; + frame->mii_opcode = VR_MII_WRITEOP; + frame->mii_turnaround = VR_MII_TURNAROUND; + + /* Turn on data output. */ + SIO_SET(VR_MIICMD_DIR); + + vr_mii_sync(sc); + + vr_mii_send(sc, frame->mii_stdelim, 2); + vr_mii_send(sc, frame->mii_opcode, 2); + vr_mii_send(sc, frame->mii_phyaddr, 5); + vr_mii_send(sc, frame->mii_regaddr, 5); + vr_mii_send(sc, frame->mii_turnaround, 2); + vr_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + SIO_SET(VR_MIICMD_CLK); + DELAY(1); + SIO_CLR(VR_MIICMD_CLK); + DELAY(1); + + /* Turn off xmit. */ + SIO_CLR(VR_MIICMD_DIR); + + return (0); +} +#else +{ + int i; + + /* Set the PHY address. */ + CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| + frame->mii_phyaddr); + + /* Set the register address and data to write. */ + CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); + CSR_WRITE_2(sc, VR_MIIDATA, frame->mii_data); + + VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_WRITE_ENB); + + for (i = 0; i < 10000; i++) { + if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_WRITE_ENB) == 0) + break; + DELAY(1); + } + + return (0); +} +#endif + +static int +vr_miibus_readreg(device_t dev, uint16_t phy, uint16_t reg) +{ + struct vr_mii_frame frame; + struct vr_softc *sc = device_get_softc(dev); + + switch (sc->vr_revid) { + case REV_ID_VT6102_APOLLO: + if (phy != 1) { + frame.mii_data = 0; + goto out; + } + default: + break; + } + + bzero((char *)&frame, sizeof(frame)); + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + vr_mii_readreg(sc, &frame); + +out: + return (frame.mii_data); +} + +static int +vr_miibus_writereg(device_t dev, uint16_t phy, uint16_t reg, uint16_t data) +{ + struct vr_mii_frame frame; + struct vr_softc *sc = device_get_softc(dev); + + switch (sc->vr_revid) { + case REV_ID_VT6102_APOLLO: + if (phy != 1) + return (0); + default: + break; + } + + bzero((char *)&frame, sizeof(frame)); + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + vr_mii_writereg(sc, &frame); + + return (0); +} + +static void +vr_miibus_statchg(device_t dev) +{ + struct mii_data *mii; + struct vr_softc *sc = device_get_softc(dev); + + mii = device_get_softc(sc->vr_miibus); + vr_setcfg(sc, mii->mii_media_active); +} + +/* + * Program the 64-bit multicast hash filter. + */ +static void +vr_setmulti(struct vr_softc *sc) +{ + struct ifnet *ifp = sc->vr_ifp; + int h = 0; + uint32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + uint8_t rxfilt; + int mcnt = 0; + + VR_LOCK_ASSERT(sc); + + rxfilt = CSR_READ_1(sc, VR_RXCFG); + + if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { + rxfilt |= VR_RXCFG_RX_MULTI; + CSR_WRITE_1(sc, VR_RXCFG, rxfilt); + CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); + return; + } + + /* First, zero out all the existing hash bits. */ + CSR_WRITE_4(sc, VR_MAR0, 0); + CSR_WRITE_4(sc, VR_MAR1, 0); + + /* Now program new ones. */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = ether_crc32_be(LLADDR((struct sockaddr_dl *) + ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + mcnt++; + } + IF_ADDR_UNLOCK(ifp); + + if (mcnt) + rxfilt |= VR_RXCFG_RX_MULTI; + else + rxfilt &= ~VR_RXCFG_RX_MULTI; + + CSR_WRITE_4(sc, VR_MAR0, hashes[0]); + CSR_WRITE_4(sc, VR_MAR1, hashes[1]); + CSR_WRITE_1(sc, VR_RXCFG, rxfilt); +} + +/* + * 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 +vr_setcfg(struct vr_softc *sc, int media) +{ + int restart = 0; + + VR_LOCK_ASSERT(sc); + + if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { + restart = 1; + VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); + } + + if ((media & IFM_GMASK) == IFM_FDX) + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); + else + VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); + + if (restart) + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); +} + +static void +vr_reset(struct vr_softc *sc) +{ + register int i; + + /*VR_LOCK_ASSERT(sc);*/ /* XXX: Called during attach w/o lock. */ + + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); + + for (i = 0; i < VR_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) + break; + } + if (i == VR_TIMEOUT) { + if (sc->vr_revid < REV_ID_VT3065_A) + if_printf(sc->vr_ifp, "reset never completed!\n"); + else { + /* Use newer force reset command */ + if_printf(sc->vr_ifp, "Using force reset command.\n"); + VR_SETBIT(sc, VR_MISC_CR1, VR_MISCCR1_FORSRST); + } + } + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); +} + +/* + * Probe for a VIA Rhine chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +vr_probe(device_t dev) +{ + struct vr_type *t = vr_devs; + + while (t->vr_name != NULL) { + if ((pci_get_vendor(dev) == t->vr_vid) && + (pci_get_device(dev) == t->vr_did)) { + device_set_desc(dev, t->vr_name); + return (BUS_PROBE_DEFAULT); + } + t++; + } + + return (ENXIO); +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int +vr_attach(dev) + device_t dev; +{ + int i; + u_char eaddr[ETHER_ADDR_LEN]; + struct vr_softc *sc; + struct ifnet *ifp; + int unit, error = 0, rid; + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + + mtx_init(&sc->vr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + callout_init_mtx(&sc->vr_stat_callout, &sc->vr_mtx, 0); + + /* + * Map control/status registers. + */ + pci_enable_busmaster(dev); + sc->vr_revid = pci_read_config(dev, VR_PCI_REVID, 4) & 0x000000FF; + + rid = VR_RID; + sc->vr_res = bus_alloc_resource_any(dev, VR_RES, &rid, RF_ACTIVE); + + if (sc->vr_res == NULL) { + device_printf(dev, "couldn't map ports/memory\n"); + error = ENXIO; + goto fail; + } + + sc->vr_btag = rman_get_bustag(sc->vr_res); + sc->vr_bhandle = rman_get_bushandle(sc->vr_res); + + /* Allocate interrupt */ + rid = 0; + sc->vr_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->vr_irq == NULL) { + device_printf(dev, "couldn't map interrupt\n"); + error = ENXIO; + goto fail; + } + + /* Allocate ifnet structure. */ + ifp = sc->vr_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "can not if_alloc()\n"); + error = ENOSPC; + goto fail; + } + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = vr_ioctl; + ifp->if_start = vr_start; + ifp->if_watchdog = vr_watchdog; + ifp->if_init = vr_init; + IFQ_SET_MAXLEN(&ifp->if_snd, VR_TX_LIST_CNT - 1); + ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; + IFQ_SET_READY(&ifp->if_snd); + ifp->if_capenable = ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif + + /* + * Windows may put the chip in suspend mode when it + * shuts down. Be sure to kick it in the head to wake it + * up again. + */ + VR_CLRBIT(sc, VR_STICKHW, (VR_STICKHW_DS0|VR_STICKHW_DS1)); + + /* Reset the adapter. */ + vr_reset(sc); + + /* + * Turn on bit2 (MIION) in PCI configuration register 0x53 during + * initialization and disable AUTOPOLL. + */ + pci_write_config(dev, VR_PCI_MODE, + pci_read_config(dev, VR_PCI_MODE, 4) | (VR_MODE3_MIION << 24), 4); + VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL); + + /* + * Get station address. The way the Rhine chips work, + * you're not allowed to directly access the EEPROM once + * they've been programmed a special way. Consequently, + * we need to read the node address from the PAR0 and PAR1 + * registers. + */ + VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); + DELAY(200); + for (i = 0; i < ETHER_ADDR_LEN; i++) + eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); + + sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, + M_NOWAIT | M_ZERO, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->vr_ldata == NULL) { + device_printf(dev, "no memory for list buffers!\n"); + error = ENXIO; + goto fail; + } + + /* Do MII setup. */ + if (mii_phy_probe(dev, &sc->vr_miibus, + vr_ifmedia_upd, vr_ifmedia_sts)) { + device_printf(dev, "MII without any phy!\n"); + error = ENXIO; + goto fail; + } + + /* Call MI attach routine. */ + ether_ifattach(ifp, eaddr); + + sc->suspended = 0; + + /* Hook interrupt last to avoid having to lock softc */ + error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET | INTR_MPSAFE, + vr_intr, sc, &sc->vr_intrhand); + + if (error) { + device_printf(dev, "couldn't set up irq\n"); + ether_ifdetach(ifp); + goto fail; + } + +fail: + if (error) + vr_detach(dev); + + return (error); +} + +/* + * Shutdown hardware and free up resources. This can be called any + * time after the mutex has been initialized. It is called in both + * the error case in attach and the normal detach case so it needs + * to be careful about only freeing resources that have actually been + * allocated. + */ +static int +vr_detach(device_t dev) +{ + struct vr_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->vr_ifp; + + KASSERT(mtx_initialized(&sc->vr_mtx), ("vr mutex not initialized")); + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(ifp); +#endif + + /* These should only be active if attach succeeded */ + if (device_is_attached(dev)) { + VR_LOCK(sc); + sc->suspended = 1; + vr_stop(sc); + VR_UNLOCK(sc); + callout_drain(&sc->vr_stat_callout); + ether_ifdetach(ifp); + } + if (ifp) + if_free(ifp); + if (sc->vr_miibus) + device_delete_child(dev, sc->vr_miibus); + bus_generic_detach(dev); + + if (sc->vr_intrhand) + bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); + if (sc->vr_irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); + if (sc->vr_res) + bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); + + if (sc->vr_ldata) + contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); + + mtx_destroy(&sc->vr_mtx); + + return (0); +} + +/* + * Initialize the transmit descriptors. + */ +static int +vr_list_tx_init(struct vr_softc *sc) +{ + struct vr_chain_data *cd; + struct vr_list_data *ld; + int i; + + cd = &sc->vr_cdata; + ld = sc->vr_ldata; + for (i = 0; i < VR_TX_LIST_CNT; i++) { + cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; + if (i == (VR_TX_LIST_CNT - 1)) + cd->vr_tx_chain[i].vr_nextdesc = + &cd->vr_tx_chain[0]; + else + cd->vr_tx_chain[i].vr_nextdesc = + &cd->vr_tx_chain[i + 1]; + } + cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0]; + + 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 +vr_list_rx_init(struct vr_softc *sc) +{ + struct vr_chain_data *cd; + struct vr_list_data *ld; + int i; + + VR_LOCK_ASSERT(sc); + + cd = &sc->vr_cdata; + ld = sc->vr_ldata; + + for (i = 0; i < VR_RX_LIST_CNT; i++) { + cd->vr_rx_chain[i].vr_ptr = + (struct vr_desc *)&ld->vr_rx_list[i]; + if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) + return (ENOBUFS); + if (i == (VR_RX_LIST_CNT - 1)) { + cd->vr_rx_chain[i].vr_nextdesc = + &cd->vr_rx_chain[0]; + ld->vr_rx_list[i].vr_next = + vtophys(&ld->vr_rx_list[0]); + } else { + cd->vr_rx_chain[i].vr_nextdesc = + &cd->vr_rx_chain[i + 1]; + ld->vr_rx_list[i].vr_next = + vtophys(&ld->vr_rx_list[i + 1]); + } + } + + cd->vr_rx_head = &cd->vr_rx_chain[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 +vr_newbuf(struct vr_softc *sc, struct vr_chain_onefrag *c, struct mbuf *m) +{ + struct mbuf *m_new = NULL; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) + return (ENOBUFS); + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + m_freem(m_new); + return (ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(uint64_t)); + + c->vr_mbuf = m_new; + c->vr_ptr->vr_status = VR_RXSTAT; + c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); + c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; + + return (0); +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void +vr_rxeof(struct vr_softc *sc) +{ + struct mbuf *m, *m0; + struct ifnet *ifp; + struct vr_chain_onefrag *cur_rx; + int total_len = 0; + uint32_t rxstat; + + VR_LOCK_ASSERT(sc); + ifp = sc->vr_ifp; + + while (!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & + VR_RXSTAT_OWN)) { +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) { + if (sc->rxcycles <= 0) + break; + sc->rxcycles--; + } +#endif + m0 = NULL; + cur_rx = sc->vr_cdata.vr_rx_head; + sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; + m = cur_rx->vr_mbuf; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & VR_RXSTAT_RXERR) { + ifp->if_ierrors++; + if_printf(ifp, "rx error (%02x):", rxstat & 0x000000ff); + if (rxstat & VR_RXSTAT_CRCERR) + printf(" crc error"); + if (rxstat & VR_RXSTAT_FRAMEALIGNERR) + printf(" frame alignment error\n"); + if (rxstat & VR_RXSTAT_FIFOOFLOW) + printf(" FIFO overflow"); + if (rxstat & VR_RXSTAT_GIANT) + printf(" received giant packet"); + if (rxstat & VR_RXSTAT_RUNT) + printf(" received runt packet"); + if (rxstat & VR_RXSTAT_BUSERR) + printf(" system bus error"); + if (rxstat & VR_RXSTAT_BUFFERR) + printf("rx buffer error"); + printf("\n"); + vr_newbuf(sc, cur_rx, m); + continue; + } + + /* No errors; receive the packet. */ + total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); + + /* + * XXX The VIA Rhine chip includes the CRC with every + * received frame, and there's no way to turn this + * behavior off (at least, I can't find anything in + * the manual that explains how to do it) so we have + * to trim off the CRC manually. + */ + total_len -= ETHER_CRC_LEN; + + m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, + NULL); + vr_newbuf(sc, cur_rx, m); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m = m0; + + ifp->if_ipackets++; + VR_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + VR_LOCK(sc); + } +} + +static void +vr_rxeoc(struct vr_softc *sc) +{ + struct ifnet *ifp = sc->vr_ifp; + int i; + + VR_LOCK_ASSERT(sc); + + ifp->if_ierrors++; + + VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); + DELAY(10000); + + /* Wait for receiver to stop */ + for (i = 0x400; + i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RX_ON); + i--) { + ; + } + + if (!i) { + if_printf(ifp, "rx shutdown error!\n"); + sc->vr_flags |= VR_F_RESTART; + return; + } + + vr_rxeof(sc); + + CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void +vr_txeof(struct vr_softc *sc) +{ + struct vr_chain *cur_tx; + struct ifnet *ifp = sc->vr_ifp; + + VR_LOCK_ASSERT(sc); + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + cur_tx = sc->vr_cdata.vr_tx_cons; + while (cur_tx->vr_mbuf != NULL) { + uint32_t txstat; + int i; + + txstat = cur_tx->vr_ptr->vr_status; + + if ((txstat & VR_TXSTAT_ABRT) || + (txstat & VR_TXSTAT_UDF)) { + for (i = 0x400; + i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_TX_ON); + i--) + ; /* Wait for chip to shutdown */ + if (!i) { + if_printf(ifp, "tx shutdown timeout\n"); + sc->vr_flags |= VR_F_RESTART; + break; + } + VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + CSR_WRITE_4(sc, VR_TXADDR, vtophys(cur_tx->vr_ptr)); + break; + } + + if (txstat & VR_TXSTAT_OWN) + break; + + if (txstat & VR_TXSTAT_ERRSUM) { + ifp->if_oerrors++; + if (txstat & VR_TXSTAT_DEFER) + ifp->if_collisions++; + if (txstat & VR_TXSTAT_LATECOLL) + ifp->if_collisions++; + } + + ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + m_freem(cur_tx->vr_mbuf); + cur_tx->vr_mbuf = NULL; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + cur_tx = cur_tx->vr_nextdesc; + } + sc->vr_cdata.vr_tx_cons = cur_tx; + if (cur_tx->vr_mbuf == NULL) + ifp->if_timer = 0; +} + +static void +vr_tick(void *xsc) +{ + struct vr_softc *sc = xsc; + struct mii_data *mii; + + VR_LOCK_ASSERT(sc); + + if (sc->vr_flags & VR_F_RESTART) { + if_printf(sc->vr_ifp, "restarting\n"); + vr_stop(sc); + vr_reset(sc); + vr_init_locked(sc); + sc->vr_flags &= ~VR_F_RESTART; + } + + mii = device_get_softc(sc->vr_miibus); + mii_tick(mii); + callout_reset(&sc->vr_stat_callout, hz, vr_tick, sc); +} + +#ifdef DEVICE_POLLING +static poll_handler_t vr_poll; +static poll_handler_t vr_poll_locked; + +static void +vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct vr_softc *sc = ifp->if_softc; + + VR_LOCK(sc); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + vr_poll_locked(ifp, cmd, count); + VR_UNLOCK(sc); +} + +static void +vr_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct vr_softc *sc = ifp->if_softc; + + VR_LOCK_ASSERT(sc); + + sc->rxcycles = count; + vr_rxeof(sc); + vr_txeof(sc); + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vr_start_locked(ifp); + + if (cmd == POLL_AND_CHECK_STATUS) { + uint16_t status; + + /* Also check status register. */ + status = CSR_READ_2(sc, VR_ISR); + if (status) + CSR_WRITE_2(sc, VR_ISR, status); + + if ((status & VR_INTRS) == 0) + return; + + if (status & VR_ISR_RX_DROPPED) { + if_printf(ifp, "rx packet lost\n"); + ifp->if_ierrors++; + } + + if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || + (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { + if_printf(ifp, "receive error (%04x)", status); + if (status & VR_ISR_RX_NOBUF) + printf(" no buffers"); + if (status & VR_ISR_RX_OFLOW) + printf(" overflow"); + if (status & VR_ISR_RX_DROPPED) + printf(" packet lost"); + printf("\n"); + vr_rxeoc(sc); + } + + if ((status & VR_ISR_BUSERR) || + (status & VR_ISR_TX_UNDERRUN)) { + vr_reset(sc); + vr_init_locked(sc); + return; + } + + if ((status & VR_ISR_UDFI) || + (status & VR_ISR_TX_ABRT2) || + (status & VR_ISR_TX_ABRT)) { + ifp->if_oerrors++; + if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); + } + } + } +} +#endif /* DEVICE_POLLING */ + +static void +vr_intr(void *arg) +{ + struct vr_softc *sc = arg; + struct ifnet *ifp = sc->vr_ifp; + uint16_t status; + + VR_LOCK(sc); + + if (sc->suspended) { + /* + * Forcibly disable interrupts. + * XXX: Mobile VIA based platforms may need + * interrupt re-enable on resume. + */ + CSR_WRITE_2(sc, VR_IMR, 0x0000); + goto done_locked; + } + +#ifdef DEVICE_POLLING + if (ifp->if_capenable & IFCAP_POLLING) + goto done_locked; +#endif + + /* Suppress unwanted interrupts. */ + if (!(ifp->if_flags & IFF_UP)) { + vr_stop(sc); + goto done_locked; + } + + /* Disable interrupts. */ + CSR_WRITE_2(sc, VR_IMR, 0x0000); + + for (;;) { + status = CSR_READ_2(sc, VR_ISR); + if (status) + CSR_WRITE_2(sc, VR_ISR, status); + + if ((status & VR_INTRS) == 0) + break; + + if (status & VR_ISR_RX_OK) + vr_rxeof(sc); + + if (status & VR_ISR_RX_DROPPED) { + if_printf(ifp, "rx packet lost\n"); + ifp->if_ierrors++; + } + + if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || + (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { + if_printf(ifp, "receive error (%04x)", status); + if (status & VR_ISR_RX_NOBUF) + printf(" no buffers"); + if (status & VR_ISR_RX_OFLOW) + printf(" overflow"); + if (status & VR_ISR_RX_DROPPED) + printf(" packet lost"); + printf("\n"); + vr_rxeoc(sc); + } + + if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { + vr_reset(sc); + vr_init_locked(sc); + break; + } + + if ((status & VR_ISR_TX_OK) || (status & VR_ISR_TX_ABRT) || + (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_UDFI)) { + vr_txeof(sc); + if ((status & VR_ISR_UDFI) || + (status & VR_ISR_TX_ABRT2) || + (status & VR_ISR_TX_ABRT)) { + ifp->if_oerrors++; + if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { + VR_SETBIT16(sc, VR_COMMAND, + VR_CMD_TX_ON); + VR_SETBIT16(sc, VR_COMMAND, + VR_CMD_TX_GO); + } + } + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vr_start_locked(ifp); + +done_locked: + VR_UNLOCK(sc); +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int +vr_encap(struct vr_softc *sc, struct vr_chain *c, struct mbuf *m_head) +{ + struct vr_desc *f = NULL; + struct mbuf *m; + + VR_LOCK_ASSERT(sc); + /* + * The VIA Rhine wants packet buffers to be longword + * aligned, but very often our mbufs aren't. Rather than + * waste time trying to decide when to copy and when not + * to copy, just do it all the time. + */ + m = m_defrag(m_head, M_DONTWAIT); + if (m == NULL) + return (1); + + /* + * The Rhine chip doesn't auto-pad, so we have to make + * sure to pad short frames out to the minimum frame length + * ourselves. + */ + if (m->m_len < VR_MIN_FRAMELEN) { + m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len; + m->m_len = m->m_pkthdr.len; + } + + c->vr_mbuf = m; + f = c->vr_ptr; + f->vr_data = vtophys(mtod(m, caddr_t)); + f->vr_ctl = m->m_len; + f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; + f->vr_status = 0; + f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; + f->vr_next = vtophys(c->vr_nextdesc->vr_ptr); + + 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 +vr_start(struct ifnet *ifp) +{ + struct vr_softc *sc = ifp->if_softc; + + VR_LOCK(sc); + vr_start_locked(ifp); + VR_UNLOCK(sc); +} + +static void +vr_start_locked(struct ifnet *ifp) +{ + struct vr_softc *sc = ifp->if_softc; + struct mbuf *m_head; + struct vr_chain *cur_tx; + + if (ifp->if_drv_flags & IFF_DRV_OACTIVE) + return; + + cur_tx = sc->vr_cdata.vr_tx_prod; + while (cur_tx->vr_mbuf == NULL) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + /* Pack the data into the descriptor. */ + if (vr_encap(sc, cur_tx, m_head)) { + /* Rollback, send what we were able to encap. */ + IFQ_DRV_PREPEND(&ifp->if_snd, m_head); + break; + } + + VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + BPF_MTAP(ifp, cur_tx->vr_mbuf); + + cur_tx = cur_tx->vr_nextdesc; + } + if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) { + sc->vr_cdata.vr_tx_prod = cur_tx; + + /* Tell the chip to start transmitting. */ + VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/ VR_CMD_TX_GO); + + /* Set a timeout in case the chip goes out to lunch. */ + ifp->if_timer = 5; + + if (cur_tx->vr_mbuf != NULL) + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } +} + +static void +vr_init(void *xsc) +{ + struct vr_softc *sc = xsc; + + VR_LOCK(sc); + vr_init_locked(sc); + VR_UNLOCK(sc); +} + +static void +vr_init_locked(struct vr_softc *sc) +{ + struct ifnet *ifp = sc->vr_ifp; + struct mii_data *mii; + int i; + + VR_LOCK_ASSERT(sc); + + mii = device_get_softc(sc->vr_miibus); + + /* Cancel pending I/O and free all RX/TX buffers. */ + vr_stop(sc); + vr_reset(sc); + + /* Set our station address. */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + CSR_WRITE_1(sc, VR_PAR0 + i, IFP2ENADDR(sc->vr_ifp)[i]); + + /* Set DMA size. */ + VR_CLRBIT(sc, VR_BCR0, VR_BCR0_DMA_LENGTH); + VR_SETBIT(sc, VR_BCR0, VR_BCR0_DMA_STORENFWD); + + /* + * BCR0 and BCR1 can override the RXCFG and TXCFG registers, + * so we must set both. + */ + VR_CLRBIT(sc, VR_BCR0, VR_BCR0_RX_THRESH); + VR_SETBIT(sc, VR_BCR0, VR_BCR0_RXTHRESH128BYTES); + + VR_CLRBIT(sc, VR_BCR1, VR_BCR1_TX_THRESH); + VR_SETBIT(sc, VR_BCR1, VR_BCR1_TXTHRESHSTORENFWD); + + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); + VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_128BYTES); + + VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); + VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); + + /* Init circular RX list. */ + if (vr_list_rx_init(sc) == ENOBUFS) { + if_printf(ifp, + "initialization failed: no memory for rx buffers\n"); + vr_stop(sc); + return; + } + + /* Init tx descriptors. */ + vr_list_tx_init(sc); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); + else + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); + + /* Set capture broadcast bit to capture broadcast frames. */ + if (ifp->if_flags & IFF_BROADCAST) + VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); + else + VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); + + /* + * Program the multicast filter, if necessary. + */ + vr_setmulti(sc); + + /* + * Load the address of the RX list. + */ + CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); + + /* Enable receiver and transmitter. */ + CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| + VR_CMD_TX_ON|VR_CMD_RX_ON| + VR_CMD_RX_GO); + + CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); + + CSR_WRITE_2(sc, VR_ISR, 0xFFFF); +#ifdef DEVICE_POLLING + /* + * Disable interrupts if we are polling. + */ + if (ifp->if_capenable & IFCAP_POLLING) + CSR_WRITE_2(sc, VR_IMR, 0); + else +#endif + /* + * Enable interrupts. + */ + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); + + mii_mediachg(mii); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + callout_reset(&sc->vr_stat_callout, hz, vr_tick, sc); +} + +/* + * Set media options. + */ +static int +vr_ifmedia_upd(struct ifnet *ifp) +{ + struct vr_softc *sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) + vr_init(sc); + + return (0); +} + +/* + * Report current media status. + */ +static void +vr_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct vr_softc *sc = ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->vr_miibus); + VR_LOCK(sc); + mii_pollstat(mii); + VR_UNLOCK(sc); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static int +vr_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct vr_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int error = 0; + + switch (command) { + case SIOCSIFFLAGS: + VR_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + vr_init_locked(sc); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + vr_stop(sc); + } + VR_UNLOCK(sc); + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + VR_LOCK(sc); + vr_setmulti(sc); + VR_UNLOCK(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->vr_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + case SIOCSIFCAP: +#ifdef DEVICE_POLLING + if (ifr->ifr_reqcap & IFCAP_POLLING && + !(ifp->if_capenable & IFCAP_POLLING)) { + error = ether_poll_register(vr_poll, ifp); + if (error) + return(error); + VR_LOCK(sc); + /* Disable interrupts */ + CSR_WRITE_2(sc, VR_IMR, 0x0000); + ifp->if_capenable |= IFCAP_POLLING; + VR_UNLOCK(sc); + return (error); + + } + if (!(ifr->ifr_reqcap & IFCAP_POLLING) && + ifp->if_capenable & IFCAP_POLLING) { + error = ether_poll_deregister(ifp); + /* Enable interrupts. */ + VR_LOCK(sc); + CSR_WRITE_2(sc, VR_IMR, VR_INTRS); + ifp->if_capenable &= ~IFCAP_POLLING; + VR_UNLOCK(sc); + return (error); + } +#endif /* DEVICE_POLLING */ + break; + default: + error = ether_ioctl(ifp, command, data); + break; + } + + return (error); +} + +static void +vr_watchdog(struct ifnet *ifp) +{ + struct vr_softc *sc = ifp->if_softc; + + VR_LOCK(sc); + + ifp->if_oerrors++; + if_printf(ifp, "watchdog timeout\n"); + + vr_stop(sc); + vr_reset(sc); + vr_init_locked(sc); + + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + vr_start_locked(ifp); + + VR_UNLOCK(sc); +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void +vr_stop(struct vr_softc *sc) +{ + register int i; + struct ifnet *ifp; + + VR_LOCK_ASSERT(sc); + + ifp = sc->vr_ifp; + ifp->if_timer = 0; + + callout_stop(&sc->vr_stat_callout); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); + VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); + CSR_WRITE_2(sc, VR_IMR, 0x0000); + CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); + CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); + + /* + * Free data in the RX lists. + */ + for (i = 0; i < VR_RX_LIST_CNT; i++) { + if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { + m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); + sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; + } + } + bzero((char *)&sc->vr_ldata->vr_rx_list, + sizeof(sc->vr_ldata->vr_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < VR_TX_LIST_CNT; i++) { + if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { + m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); + sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; + } + } + bzero((char *)&sc->vr_ldata->vr_tx_list, + sizeof(sc->vr_ldata->vr_tx_list)); +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void +vr_shutdown(device_t dev) +{ + + vr_detach(dev); +} diff --git a/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vrreg.h b/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vrreg.h new file mode 100644 index 0000000000..1fe99c3c6f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/via-rhine/pci/if_vrreg.h @@ -0,0 +1,597 @@ +/*- + * Copyright (c) 1997, 1998 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/pci/if_vrreg.h,v 1.22.2.1 2005/11/08 16:05:56 jhb Exp $ + */ + +/* + * Rhine register definitions. + */ + +#define VR_PAR0 0x00 /* node address 0 to 4 */ +#define VR_PAR1 0x04 /* node address 2 to 6 */ +#define VR_RXCFG 0x06 /* receiver config register */ +#define VR_TXCFG 0x07 /* transmit config register */ +#define VR_COMMAND 0x08 /* command register */ +#define VR_ISR 0x0C /* interrupt/status register */ +#define VR_IMR 0x0E /* interrupt mask register */ +#define VR_MAR0 0x10 /* multicast hash 0 */ +#define VR_MAR1 0x14 /* multicast hash 1 */ +#define VR_RXADDR 0x18 /* rx descriptor list start addr */ +#define VR_TXADDR 0x1C /* tx descriptor list start addr */ +#define VR_CURRXDESC0 0x20 +#define VR_CURRXDESC1 0x24 +#define VR_CURRXDESC2 0x28 +#define VR_CURRXDESC3 0x2C +#define VR_NEXTRXDESC0 0x30 +#define VR_NEXTRXDESC1 0x34 +#define VR_NEXTRXDESC2 0x38 +#define VR_NEXTRXDESC3 0x3C +#define VR_CURTXDESC0 0x40 +#define VR_CURTXDESC1 0x44 +#define VR_CURTXDESC2 0x48 +#define VR_CURTXDESC3 0x4C +#define VR_NEXTTXDESC0 0x50 +#define VR_NEXTTXDESC1 0x54 +#define VR_NEXTTXDESC2 0x58 +#define VR_NEXTTXDESC3 0x5C +#define VR_CURRXDMA 0x60 /* current RX DMA address */ +#define VR_CURTXDMA 0x64 /* current TX DMA address */ +#define VR_TALLYCNT 0x68 /* tally counter test register */ +#define VR_PHYADDR 0x6C +#define VR_MIISTAT 0x6D +#define VR_BCR0 0x6E +#define VR_BCR1 0x6F +#define VR_MIICMD 0x70 +#define VR_MIIADDR 0x71 +#define VR_MIIDATA 0x72 +#define VR_EECSR 0x74 +#define VR_TEST 0x75 +#define VR_GPIO 0x76 +#define VR_CONFIG 0x78 +#define VR_MPA_CNT 0x7C +#define VR_CRC_CNT 0x7E +#define VR_STICKHW 0x83 + +/* Misc Registers */ +#define VR_MISC_CR1 0x81 +#define VR_MISCCR1_FORSRST 0x40 + +/* + * RX config bits. + */ +#define VR_RXCFG_RX_ERRPKTS 0x01 +#define VR_RXCFG_RX_RUNT 0x02 +#define VR_RXCFG_RX_MULTI 0x04 +#define VR_RXCFG_RX_BROAD 0x08 +#define VR_RXCFG_RX_PROMISC 0x10 +#define VR_RXCFG_RX_THRESH 0xE0 + +#define VR_RXTHRESH_32BYTES 0x00 +#define VR_RXTHRESH_64BYTES 0x20 +#define VR_RXTHRESH_128BYTES 0x40 +#define VR_RXTHRESH_256BYTES 0x60 +#define VR_RXTHRESH_512BYTES 0x80 +#define VR_RXTHRESH_768BYTES 0xA0 +#define VR_RXTHRESH_1024BYTES 0xC0 +#define VR_RXTHRESH_STORENFWD 0xE0 + +/* + * TX config bits. + */ +#define VR_TXCFG_RSVD0 0x01 +#define VR_TXCFG_LOOPBKMODE 0x06 +#define VR_TXCFG_BACKOFF 0x08 +#define VR_TXCFG_RSVD1 0x10 +#define VR_TXCFG_TX_THRESH 0xE0 + +#define VR_TXTHRESH_32BYTES 0x00 +#define VR_TXTHRESH_64BYTES 0x20 +#define VR_TXTHRESH_128BYTES 0x40 +#define VR_TXTHRESH_256BYTES 0x60 +#define VR_TXTHRESH_512BYTES 0x80 +#define VR_TXTHRESH_768BYTES 0xA0 +#define VR_TXTHRESH_1024BYTES 0xC0 +#define VR_TXTHRESH_STORENFWD 0xE0 + +/* + * Command register bits. + */ +#define VR_CMD_INIT 0x0001 +#define VR_CMD_START 0x0002 +#define VR_CMD_STOP 0x0004 +#define VR_CMD_RX_ON 0x0008 +#define VR_CMD_TX_ON 0x0010 +#define VR_CMD_TX_GO 0x0020 +#define VR_CMD_RX_GO 0x0040 +#define VR_CMD_RSVD 0x0080 +#define VR_CMD_RX_EARLY 0x0100 +#define VR_CMD_TX_EARLY 0x0200 +#define VR_CMD_FULLDUPLEX 0x0400 +#define VR_CMD_TX_NOPOLL 0x0800 + +#define VR_CMD_RESET 0x8000 + +/* + * Interrupt status bits. + */ +#define VR_ISR_RX_OK 0x0001 /* packet rx ok */ +#define VR_ISR_TX_OK 0x0002 /* packet tx ok */ +#define VR_ISR_RX_ERR 0x0004 /* packet rx with err */ +#define VR_ISR_TX_ABRT 0x0008 /* tx aborted due to excess colls */ +#define VR_ISR_TX_UNDERRUN 0x0010 /* tx buffer underflow */ +#define VR_ISR_RX_NOBUF 0x0020 /* no rx buffer available */ +#define VR_ISR_BUSERR 0x0040 /* PCI bus error */ +#define VR_ISR_STATSOFLOW 0x0080 /* stats counter oflow */ +#define VR_ISR_RX_EARLY 0x0100 /* rx early */ +#define VR_ISR_LINKSTAT 0x0200 /* MII status change */ +#define VR_ISR_ETI 0x0200 /* Tx early (3043/3071) */ +#define VR_ISR_UDFI 0x0200 /* Tx FIFO underflow (3065) */ +#define VR_ISR_RX_OFLOW 0x0400 /* rx FIFO overflow */ +#define VR_ISR_RX_DROPPED 0x0800 +#define VR_ISR_RX_NOBUF2 0x1000 +#define VR_ISR_TX_ABRT2 0x2000 +#define VR_ISR_LINKSTAT2 0x4000 +#define VR_ISR_MAGICPACKET 0x8000 + +/* + * Interrupt mask bits. + */ +#define VR_IMR_RX_OK 0x0001 /* packet rx ok */ +#define VR_IMR_TX_OK 0x0002 /* packet tx ok */ +#define VR_IMR_RX_ERR 0x0004 /* packet rx with err */ +#define VR_IMR_TX_ABRT 0x0008 /* tx aborted due to excess colls */ +#define VR_IMR_TX_UNDERRUN 0x0010 /* tx buffer underflow */ +#define VR_IMR_RX_NOBUF 0x0020 /* no rx buffer available */ +#define VR_IMR_BUSERR 0x0040 /* PCI bus error */ +#define VR_IMR_STATSOFLOW 0x0080 /* stats counter oflow */ +#define VR_IMR_RX_EARLY 0x0100 /* rx early */ +#define VR_IMR_LINKSTAT 0x0200 /* MII status change */ +#define VR_IMR_RX_OFLOW 0x0400 /* rx FIFO overflow */ +#define VR_IMR_RX_DROPPED 0x0800 +#define VR_IMR_RX_NOBUF2 0x1000 +#define VR_IMR_TX_ABRT2 0x2000 +#define VR_IMR_LINKSTAT2 0x4000 +#define VR_IMR_MAGICPACKET 0x8000 + +#define VR_INTRS \ + (VR_IMR_RX_OK|VR_IMR_TX_OK|VR_IMR_RX_NOBUF| \ + VR_IMR_TX_ABRT|VR_IMR_TX_UNDERRUN|VR_IMR_BUSERR| \ + VR_IMR_RX_ERR|VR_ISR_RX_DROPPED) + +/* + * MII status register. + */ + +#define VR_MIISTAT_SPEED 0x01 +#define VR_MIISTAT_LINKFAULT 0x02 +#define VR_MIISTAT_MGTREADERR 0x04 +#define VR_MIISTAT_MIIERR 0x08 +#define VR_MIISTAT_PHYOPT 0x10 +#define VR_MIISTAT_MDC_SPEED 0x20 +#define VR_MIISTAT_RSVD 0x40 +#define VR_MIISTAT_GPIO1POLL 0x80 + +/* + * MII command register bits. + */ +#define VR_MIICMD_CLK 0x01 +#define VR_MIICMD_DATAOUT 0x02 +#define VR_MIICMD_DATAIN 0x04 +#define VR_MIICMD_DIR 0x08 +#define VR_MIICMD_DIRECTPGM 0x10 +#define VR_MIICMD_WRITE_ENB 0x20 +#define VR_MIICMD_READ_ENB 0x40 +#define VR_MIICMD_AUTOPOLL 0x80 + +/* + * EEPROM control bits. + */ +#define VR_EECSR_DATAIN 0x01 /* data out */ +#define VR_EECSR_DATAOUT 0x02 /* data in */ +#define VR_EECSR_CLK 0x04 /* clock */ +#define VR_EECSR_CS 0x08 /* chip select */ +#define VR_EECSR_DPM 0x10 +#define VR_EECSR_LOAD 0x20 +#define VR_EECSR_EMBP 0x40 +#define VR_EECSR_EEPR 0x80 + +#define VR_EECMD_WRITE 0x140 +#define VR_EECMD_READ 0x180 +#define VR_EECMD_ERASE 0x1c0 + +/* + * Test register bits. + */ +#define VR_TEST_TEST0 0x01 +#define VR_TEST_TEST1 0x02 +#define VR_TEST_TEST2 0x04 +#define VR_TEST_TSTUD 0x08 +#define VR_TEST_TSTOV 0x10 +#define VR_TEST_BKOFF 0x20 +#define VR_TEST_FCOL 0x40 +#define VR_TEST_HBDES 0x80 + +/* + * Config register bits. + */ +#define VR_CFG_GPIO2OUTENB 0x00000001 +#define VR_CFG_GPIO2OUT 0x00000002 /* gen. purp. pin */ +#define VR_CFG_GPIO2IN 0x00000004 /* gen. purp. pin */ +#define VR_CFG_AUTOOPT 0x00000008 /* enable rx/tx autopoll */ +#define VR_CFG_MIIOPT 0x00000010 +#define VR_CFG_MMIENB 0x00000020 /* memory mapped mode enb */ +#define VR_CFG_JUMPER 0x00000040 /* PHY and oper. mode select */ +#define VR_CFG_EELOAD 0x00000080 /* enable EEPROM programming */ +#define VR_CFG_LATMENB 0x00000100 /* larency timer effect enb. */ +#define VR_CFG_MRREADWAIT 0x00000200 +#define VR_CFG_MRWRITEWAIT 0x00000400 +#define VR_CFG_RX_ARB 0x00000800 +#define VR_CFG_TX_ARB 0x00001000 +#define VR_CFG_READMULTI 0x00002000 +#define VR_CFG_TX_PACE 0x00004000 +#define VR_CFG_TX_QDIS 0x00008000 +#define VR_CFG_ROMSEL0 0x00010000 +#define VR_CFG_ROMSEL1 0x00020000 +#define VR_CFG_ROMSEL2 0x00040000 +#define VR_CFG_ROMTIMESEL 0x00080000 +#define VR_CFG_RSVD0 0x00100000 +#define VR_CFG_ROMDLY 0x00200000 +#define VR_CFG_ROMOPT 0x00400000 +#define VR_CFG_RSVD1 0x00800000 +#define VR_CFG_BACKOFFOPT 0x01000000 +#define VR_CFG_BACKOFFMOD 0x02000000 +#define VR_CFG_CAPEFFECT 0x04000000 +#define VR_CFG_BACKOFFRAND 0x08000000 +#define VR_CFG_MAGICKPACKET 0x10000000 +#define VR_CFG_PCIREADLINE 0x20000000 +#define VR_CFG_DIAG 0x40000000 +#define VR_CFG_GPIOEN 0x80000000 + +/* Sticky HW bits */ +#define VR_STICKHW_DS0 0x01 +#define VR_STICKHW_DS1 0x02 +#define VR_STICKHW_WOL_ENB 0x04 +#define VR_STICKHW_WOL_STS 0x08 +#define VR_STICKHW_LEGWOL_ENB 0x80 + +/* + * BCR0 register bits. (At least for the VT6102 chip.) + */ +#define VR_BCR0_DMA_LENGTH 0x07 + +#define VR_BCR0_DMA_32BYTES 0x00 +#define VR_BCR0_DMA_64BYTES 0x01 +#define VR_BCR0_DMA_128BYTES 0x02 +#define VR_BCR0_DMA_256BYTES 0x03 +#define VR_BCR0_DMA_512BYTES 0x04 +#define VR_BCR0_DMA_1024BYTES 0x05 +#define VR_BCR0_DMA_STORENFWD 0x07 + +#define VR_BCR0_RX_THRESH 0x38 + +#define VR_BCR0_RXTHRESHCFG 0x00 +#define VR_BCR0_RXTHRESH64BYTES 0x08 +#define VR_BCR0_RXTHRESH128BYTES 0x10 +#define VR_BCR0_RXTHRESH256BYTES 0x18 +#define VR_BCR0_RXTHRESH512BYTES 0x20 +#define VR_BCR0_RXTHRESH1024BYTES 0x28 +#define VR_BCR0_RXTHRESHSTORENFWD 0x38 +#define VR_BCR0_EXTLED 0x40 +#define VR_BCR0_MED2 0x80 + +/* + * BCR1 register bits. (At least for the VT6102 chip.) + */ +#define VR_BCR1_POT0 0x01 +#define VR_BCR1_POT1 0x02 +#define VR_BCR1_POT2 0x04 +#define VR_BCR1_TX_THRESH 0x38 +#define VR_BCR1_TXTHRESHCFG 0x00 +#define VR_BCR1_TXTHRESH64BYTES 0x08 +#define VR_BCR1_TXTHRESH128BYTES 0x10 +#define VR_BCR1_TXTHRESH256BYTES 0x18 +#define VR_BCR1_TXTHRESH512BYTES 0x20 +#define VR_BCR1_TXTHRESH1024BYTES 0x28 +#define VR_BCR1_TXTHRESHSTORENFWD 0x38 + +/* + * Rhine TX/RX list structure. + */ + +struct vr_desc { + u_int32_t vr_status; + u_int32_t vr_ctl; + u_int32_t vr_ptr1; + u_int32_t vr_ptr2; +}; + +#define vr_data vr_ptr1 +#define vr_next vr_ptr2 + + +#define VR_RXSTAT_RXERR 0x00000001 +#define VR_RXSTAT_CRCERR 0x00000002 +#define VR_RXSTAT_FRAMEALIGNERR 0x00000004 +#define VR_RXSTAT_FIFOOFLOW 0x00000008 +#define VR_RXSTAT_GIANT 0x00000010 +#define VR_RXSTAT_RUNT 0x00000020 +#define VR_RXSTAT_BUSERR 0x00000040 +#define VR_RXSTAT_BUFFERR 0x00000080 +#define VR_RXSTAT_LASTFRAG 0x00000100 +#define VR_RXSTAT_FIRSTFRAG 0x00000200 +#define VR_RXSTAT_RLINK 0x00000400 +#define VR_RXSTAT_RX_PHYS 0x00000800 +#define VR_RXSTAT_RX_BROAD 0x00001000 +#define VR_RXSTAT_RX_MULTI 0x00002000 +#define VR_RXSTAT_RX_OK 0x00004000 +#define VR_RXSTAT_RXLEN 0x07FF0000 +#define VR_RXSTAT_RXLEN_EXT 0x78000000 +#define VR_RXSTAT_OWN 0x80000000 + +#define VR_RXBYTES(x) ((x & VR_RXSTAT_RXLEN) >> 16) +#define VR_RXSTAT (VR_RXSTAT_FIRSTFRAG|VR_RXSTAT_LASTFRAG|VR_RXSTAT_OWN) + +#define VR_RXCTL_BUFLEN 0x000007FF +#define VR_RXCTL_BUFLEN_EXT 0x00007800 +#define VR_RXCTL_CHAIN 0x00008000 +#define VR_RXCTL_RX_INTR 0x00800000 + +#define VR_RXCTL (VR_RXCTL_CHAIN|VR_RXCTL_RX_INTR) + +#define VR_TXSTAT_DEFER 0x00000001 +#define VR_TXSTAT_UNDERRUN 0x00000002 +#define VR_TXSTAT_COLLCNT 0x00000078 +#define VR_TXSTAT_SQE 0x00000080 +#define VR_TXSTAT_ABRT 0x00000100 +#define VR_TXSTAT_LATECOLL 0x00000200 +#define VR_TXSTAT_CARRLOST 0x00000400 +#define VR_TXSTAT_UDF 0x00000800 +#define VR_TXSTAT_BUSERR 0x00002000 +#define VR_TXSTAT_JABTIMEO 0x00004000 +#define VR_TXSTAT_ERRSUM 0x00008000 +#define VR_TXSTAT_OWN 0x80000000 + +#define VR_TXCTL_BUFLEN 0x000007FF +#define VR_TXCTL_BUFLEN_EXT 0x00007800 +#define VR_TXCTL_TLINK 0x00008000 +#define VR_TXCTL_FIRSTFRAG 0x00200000 +#define VR_TXCTL_LASTFRAG 0x00400000 +#define VR_TXCTL_FINT 0x00800000 + + +#define VR_MAXFRAGS 16 +#define VR_RX_LIST_CNT 64 +#define VR_TX_LIST_CNT 128 +#define VR_MIN_FRAMELEN 60 +#define VR_FRAMELEN 1536 +#define VR_RXLEN 1520 + +#define VR_TXOWN(x) x->vr_ptr->vr_status + +struct vr_list_data { + struct vr_desc vr_rx_list[VR_RX_LIST_CNT]; + struct vr_desc vr_tx_list[VR_TX_LIST_CNT]; +}; + +struct vr_chain { + struct vr_desc *vr_ptr; + struct mbuf *vr_mbuf; + struct vr_chain *vr_nextdesc; +}; + +struct vr_chain_onefrag { + struct vr_desc *vr_ptr; + struct mbuf *vr_mbuf; + struct vr_chain_onefrag *vr_nextdesc; +}; + +struct vr_chain_data { + struct vr_chain_onefrag vr_rx_chain[VR_RX_LIST_CNT]; + struct vr_chain vr_tx_chain[VR_TX_LIST_CNT]; + + struct vr_chain_onefrag *vr_rx_head; + + struct vr_chain *vr_tx_cons; + struct vr_chain *vr_tx_prod; +}; + +struct vr_type { + u_int16_t vr_vid; + u_int16_t vr_did; + char *vr_name; +}; + +struct vr_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define VR_MII_STARTDELIM 0x01 +#define VR_MII_READOP 0x02 +#define VR_MII_WRITEOP 0x01 +#define VR_MII_TURNAROUND 0x02 + +#define VR_FLAG_FORCEDELAY 1 +#define VR_FLAG_SCHEDDELAY 2 +#define VR_FLAG_DELAYTIMEO 3 + +struct vr_softc { + struct ifnet *vr_ifp; /* interface info */ + bus_space_handle_t vr_bhandle; /* bus space handle */ + bus_space_tag_t vr_btag; /* bus space tag */ + struct resource *vr_res; + struct resource *vr_irq; + void *vr_intrhand; + device_t vr_miibus; + struct vr_type *vr_info; /* Rhine adapter info */ + u_int8_t vr_type; + u_int8_t vr_revid; /* Rhine chip revision */ + u_int8_t vr_flags; /* See VR_F_* below */ + struct vr_list_data *vr_ldata; + struct vr_chain_data vr_cdata; + struct callout vr_stat_callout; + struct mtx vr_mtx; + int suspended; /* if 1, sleeping/detaching */ +#ifdef DEVICE_POLLING + int rxcycles; +#endif +}; + +#define VR_F_RESTART 0x01 /* Restart unit on next tick */ + +#define VR_LOCK(_sc) mtx_lock(&(_sc)->vr_mtx) +#define VR_UNLOCK(_sc) mtx_unlock(&(_sc)->vr_mtx) +#define VR_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->vr_mtx, MA_OWNED) + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->vr_btag, sc->vr_bhandle, reg, val) +#define CSR_WRITE_2(sc, reg, val) \ + bus_space_write_2(sc->vr_btag, sc->vr_bhandle, reg, val) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1(sc->vr_btag, sc->vr_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->vr_btag, sc->vr_bhandle, reg) +#define CSR_READ_2(sc, reg) \ + bus_space_read_2(sc->vr_btag, sc->vr_bhandle, reg) +#define CSR_READ_1(sc, reg) \ + bus_space_read_1(sc->vr_btag, sc->vr_bhandle, reg) + +#define VR_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +/* + * General constants that are fun to know. + * + * VIA vendor ID + */ +#define VIA_VENDORID 0x1106 + +/* + * VIA Rhine device IDs. + */ +#define VIA_DEVICEID_RHINE 0x3043 +#define VIA_DEVICEID_RHINE_II 0x6100 +#define VIA_DEVICEID_RHINE_II_2 0x3065 +#define VIA_DEVICEID_RHINE_III 0x3106 +#define VIA_DEVICEID_RHINE_III_M 0x3053 + +/* + * Delta Electronics device ID. + */ +#define DELTA_VENDORID 0x1500 + +/* + * Delta device IDs. + */ +#define DELTA_DEVICEID_RHINE_II 0x1320 + +/* + * Addtron vendor ID. + */ +#define ADDTRON_VENDORID 0x4033 + +/* + * Addtron device IDs. + */ +#define ADDTRON_DEVICEID_RHINE_II 0x1320 + +/* + * VIA Rhine revision IDs + */ + +#define REV_ID_VT3043_E 0x04 +#define REV_ID_VT3071_A 0x20 +#define REV_ID_VT3071_B 0x21 +#define REV_ID_VT3065_A 0x40 +#define REV_ID_VT3065_B 0x41 +#define REV_ID_VT3065_C 0x42 +#define REV_ID_VT6102_APOLLO 0x74 +#define REV_ID_VT3106 0x80 +#define REV_ID_VT3106_J 0x80 /* 0x80-0x8F */ +#define REV_ID_VT3106_S 0x90 /* 0x90-0xA0 */ + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define VR_PCI_VENDOR_ID 0x00 +#define VR_PCI_DEVICE_ID 0x02 +#define VR_PCI_COMMAND 0x04 +#define VR_PCI_STATUS 0x06 +#define VR_PCI_REVID 0x08 +#define VR_PCI_CLASSCODE 0x09 +#define VR_PCI_LATENCY_TIMER 0x0D +#define VR_PCI_HEADER_TYPE 0x0E +#define VR_PCI_LOIO 0x10 +#define VR_PCI_LOMEM 0x14 +#define VR_PCI_BIOSROM 0x30 +#define VR_PCI_INTLINE 0x3C +#define VR_PCI_INTPIN 0x3D +#define VR_PCI_MINGNT 0x3E +#define VR_PCI_MINLAT 0x0F +#define VR_PCI_RESETOPT 0x48 +#define VR_PCI_EEPROM_DATA 0x4C +#define VR_PCI_MODE 0x50 + +#define VR_MODE3_MIION 0x04 + +/* power management registers */ +#define VR_PCI_CAPID 0xDC /* 8 bits */ +#define VR_PCI_NEXTPTR 0xDD /* 8 bits */ +#define VR_PCI_PWRMGMTCAP 0xDE /* 16 bits */ +#define VR_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ + +#define VR_PSTATE_MASK 0x0003 +#define VR_PSTATE_D0 0x0000 +#define VR_PSTATE_D1 0x0002 +#define VR_PSTATE_D2 0x0002 +#define VR_PSTATE_D3 0x0003 +#define VR_PME_EN 0x0010 +#define VR_PME_STATUS 0x8000 + + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif