/* $OpenBSD: elink3.c,v 1.101 2023/11/10 15:51:20 bluhm Exp $ */ /* $NetBSD: elink3.c,v 1.32 1997/05/14 00:22:00 thorpej Exp $ */ /* * Copyright (c) 1996, 1997 Jonathan Stone * Copyright (c) 1994 Herb Peyerl * 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 Herb Peyerl. * 4. The name of Herb Peyerl 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 "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include #include #include /* * Structure to map media-present bits in boards to * ifmedia codes and printable media names. Used for table-driven * ifmedia initialization. */ struct ep_media { int epm_eeprom_data; /* bitmask for eeprom config */ int epm_conn; /* sc->ep_connectors code for medium */ char *epm_name; /* name of medium */ uint64_t epm_ifmedia; /* ifmedia word for medium */ int epm_ifdata; }; /* * ep_media table for Vortex/Demon/Boomerang: * map from media-present bits in register RESET_OPTIONS+2 * to ifmedia "media words" and printable names. * * XXX indexed directly by INTERNAL_CONFIG default_media field, * (i.e., EPMEDIA_ constants) forcing order of entries. * Note that 3 is reserved. */ const struct ep_media ep_vortex_media[] = { { EP_PCI_UTP, EPC_UTP, "utp", IFM_ETHER|IFM_10_T, EPMEDIA_10BASE_T }, { EP_PCI_AUI, EPC_AUI, "aui", IFM_ETHER|IFM_10_5, EPMEDIA_AUI }, { 0, 0, "reserved", IFM_NONE, EPMEDIA_RESV1 }, { EP_PCI_BNC, EPC_BNC, "bnc", IFM_ETHER|IFM_10_2, EPMEDIA_10BASE_2 }, { EP_PCI_100BASE_TX, EPC_100TX, "100-TX", IFM_ETHER|IFM_100_TX, EPMEDIA_100BASE_TX }, { EP_PCI_100BASE_FX, EPC_100FX, "100-FX", IFM_ETHER|IFM_100_FX, EPMEDIA_100BASE_FX }, { EP_PCI_100BASE_MII,EPC_MII, "mii", IFM_ETHER|IFM_100_TX, EPMEDIA_MII }, { EP_PCI_100BASE_T4, EPC_100T4, "100-T4", IFM_ETHER|IFM_100_T4, EPMEDIA_100BASE_T4 } }; /* * ep_media table for 3c509/3c509b/3c579/3c589: * map from media-present bits in register CNFG_CNTRL * (window 0, offset ?) to ifmedia "media words" and printable names. */ struct ep_media ep_isa_media[] = { { EP_W0_CC_UTP, EPC_UTP, "utp", IFM_ETHER|IFM_10_T, EPMEDIA_10BASE_T }, { EP_W0_CC_AUI, EPC_AUI, "aui", IFM_ETHER|IFM_10_5, EPMEDIA_AUI }, { EP_W0_CC_BNC, EPC_BNC, "bnc", IFM_ETHER|IFM_10_2, EPMEDIA_10BASE_2 }, }; /* Map vortex reset_options bits to if_media codes. */ const uint64_t ep_default_to_media[] = { IFM_ETHER | IFM_10_T, IFM_ETHER | IFM_10_5, 0, /* reserved by 3Com */ IFM_ETHER | IFM_10_2, IFM_ETHER | IFM_100_TX, IFM_ETHER | IFM_100_FX, IFM_ETHER | IFM_100_TX, /* XXX really MII: need to talk to PHY */ IFM_ETHER | IFM_100_T4, }; struct cfdriver ep_cd = { NULL, "ep", DV_IFNET }; void ep_vortex_probemedia(struct ep_softc *sc); void ep_isa_probemedia(struct ep_softc *sc); void eptxstat(struct ep_softc *); int epstatus(struct ep_softc *); int epioctl(struct ifnet *, u_long, caddr_t); void epstart(struct ifnet *); void epwatchdog(struct ifnet *); void epreset(struct ep_softc *); void epread(struct ep_softc *); struct mbuf *epget(struct ep_softc *, int); void epmbuffill(void *); void epmbufempty(struct ep_softc *); void epsetfilter(struct ep_softc *); void ep_roadrunner_mii_enable(struct ep_softc *); int epsetmedia(struct ep_softc *, int); /* ifmedia callbacks */ int ep_media_change(struct ifnet *); void ep_media_status(struct ifnet *, struct ifmediareq *); /* MII callbacks */ int ep_mii_readreg(struct device *, int, int); void ep_mii_writereg(struct device *, int, int, int); void ep_statchg(struct device *); void ep_mii_setbit(struct ep_softc *, u_int16_t); void ep_mii_clrbit(struct ep_softc *, u_int16_t); u_int16_t ep_mii_readbit(struct ep_softc *, u_int16_t); void ep_mii_sync(struct ep_softc *); void ep_mii_sendbits(struct ep_softc *, u_int32_t, int); int epbusyeeprom(struct ep_softc *); u_int16_t ep_read_eeprom(struct ep_softc *, u_int16_t); static inline void ep_reset_cmd(struct ep_softc *sc, u_int cmd,u_int arg); static inline void ep_finish_reset(bus_space_tag_t, bus_space_handle_t); static inline void ep_discard_rxtop(bus_space_tag_t, bus_space_handle_t); static __inline int ep_w1_reg(struct ep_softc *, int); /* * Issue a (reset) command, and be sure it has completed. * Used for global reset, TX_RESET, RX_RESET. */ static inline void ep_reset_cmd(struct ep_softc *sc, u_int cmd, u_int arg) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; bus_space_write_2(iot, ioh, cmd, arg); ep_finish_reset(iot, ioh); } /* * Wait for any pending reset to complete. */ static inline void ep_finish_reset(bus_space_tag_t iot, bus_space_handle_t ioh) { int i; for (i = 0; i < 10000; i++) { if ((bus_space_read_2(iot, ioh, EP_STATUS) & S_COMMAND_IN_PROGRESS) == 0) break; DELAY(10); } } static inline void ep_discard_rxtop(bus_space_tag_t iot, bus_space_handle_t ioh) { int i; bus_space_write_2(iot, ioh, EP_COMMAND, RX_DISCARD_TOP_PACK); /* * Spin for about 1 msec, to avoid forcing a DELAY() between * every received packet (adding latency and limiting pkt-recv rate). * On PCI, at 4 30-nsec PCI bus cycles for a read, 8000 iterations * is about right. */ for (i = 0; i < 8000; i++) { if ((bus_space_read_2(iot, ioh, EP_STATUS) & S_COMMAND_IN_PROGRESS) == 0) return; } /* not fast enough, do DELAY()s */ ep_finish_reset(iot, ioh); } /* * Some chips (i.e., 3c574 RoadRunner) have Window 1 registers offset. */ static __inline int ep_w1_reg(struct ep_softc *sc, int reg) { switch (sc->ep_chipset) { case EP_CHIPSET_ROADRUNNER: switch (reg) { case EP_W1_FREE_TX: case EP_W1_RUNNER_RDCTL: case EP_W1_RUNNER_WRCTL: return (reg); } return (reg + 0x10); } return (reg); } /* * Back-end attach and configure. */ void epconfig(struct ep_softc *sc, u_short chipset, u_int8_t *enaddr) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int16_t i; sc->ep_chipset = chipset; /* * We could have been groveling around in other register * windows in the front-end; make sure we're in window 0 * to read the EEPROM. */ GO_WINDOW(0); if (enaddr == NULL) { /* * Read the station address from the eeprom. */ for (i = 0; i < 3; i++) { u_int16_t x = ep_read_eeprom(sc, i); sc->sc_arpcom.ac_enaddr[(i << 1)] = x >> 8; sc->sc_arpcom.ac_enaddr[(i << 1) + 1] = x; } } else { bcopy(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); } printf(" address %s", ether_sprintf(sc->sc_arpcom.ac_enaddr)); if (sc->ep_flags & EP_FLAGS_MII) printf("\n"); else printf(", "); /* * Vortex-based (3c59x pci,eisa) cards allow FDDI-sized (4500) byte * packets. Commands only take an 11-bit parameter, and 11 bits * isn't enough to hold a full-size packet length. * Commands to these cards implicitly upshift a packet size * or threshold by 2 bits. * To detect cards with large-packet support, we probe by setting * the transmit threshold register, then change windows and * read back the threshold register directly, and see if the * threshold value was shifted or not. */ bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | EP_LARGEWIN_PROBE ); GO_WINDOW(5); i = bus_space_read_2(iot, ioh, EP_W5_TX_AVAIL_THRESH); GO_WINDOW(1); switch (i) { case EP_LARGEWIN_PROBE: case (EP_LARGEWIN_PROBE & EP_LARGEWIN_MASK): sc->txashift = 0; break; case (EP_LARGEWIN_PROBE << 2): sc->txashift = 2; /* XXX does the 3c515 support Vortex-style RESET_OPTIONS? */ break; default: printf("wrote %x to TX_AVAIL_THRESH, read back %x. " "Interface disabled\n", EP_THRESH_DISABLE, (int) i); return; } timeout_set(&sc->sc_epmbuffill_tmo, epmbuffill, sc); /* * Ensure Tx-available interrupts are enabled for * start the interface. * XXX should be in epinit()? */ bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | (1600 >> sc->txashift)); bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_softc = sc; ifp->if_start = epstart; ifp->if_ioctl = epioctl; ifp->if_watchdog = epwatchdog; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; /* 64 packets are around 100ms on 10Mbps */ ifq_init_maxlen(&ifp->if_snd, 64); if_attach(ifp); ether_ifattach(ifp); /* * Finish configuration: * determine chipset if the front-end couldn't do so, * show board details, set media. */ GO_WINDOW(0); ifmedia_init(&sc->sc_mii.mii_media, 0, ep_media_change, ep_media_status); sc->sc_mii.mii_ifp = ifp; sc->sc_mii.mii_readreg = ep_mii_readreg; sc->sc_mii.mii_writereg = ep_mii_writereg; sc->sc_mii.mii_statchg = ep_statchg; /* * If we've got an indirect (ISA, PCMCIA?) board, the chipset * is unknown. If the board has large-packet support, it's a * Vortex/Boomerang, otherwise it's a 3c509. * XXX use eeprom capability word instead? */ if (sc->ep_chipset == EP_CHIPSET_UNKNOWN && sc->txashift) { printf("warning: unknown chipset, possibly 3c515?\n"); #ifdef notyet sc->sc_chipset = EP_CHIPSET_VORTEX; #endif /* notyet */ } /* * Ascertain which media types are present and inform ifmedia. */ switch (sc->ep_chipset) { case EP_CHIPSET_ROADRUNNER: if (sc->ep_flags & EP_FLAGS_MII) { ep_roadrunner_mii_enable(sc); GO_WINDOW(0); } /* FALLTHROUGH */ case EP_CHIPSET_BOOMERANG: /* * If the device has MII, probe it. We won't be using * any `native' media in this case, only PHYs. If * we don't, just treat the Boomerang like the Vortex. */ if (sc->ep_flags & EP_FLAGS_MII) { mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); } else { ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); } break; } /* FALLTHROUGH */ /* on a direct bus, the attach routine can tell, but check anyway. */ case EP_CHIPSET_VORTEX: case EP_CHIPSET_BOOMERANG2: ep_vortex_probemedia(sc); break; /* on ISA we can't yet tell 3c509 from 3c515. Assume the former. */ case EP_CHIPSET_3C509: default: ep_isa_probemedia(sc); break; } GO_WINDOW(1); /* Window 1 is operating window */ sc->tx_start_thresh = 20; /* probably a good starting point. */ ep_reset_cmd(sc, EP_COMMAND, RX_RESET); ep_reset_cmd(sc, EP_COMMAND, TX_RESET); } int ep_detach(struct device *self) { struct ep_softc *sc = (struct ep_softc *)self; struct ifnet *ifp = &sc->sc_arpcom.ac_if; if (sc->ep_flags & EP_FLAGS_MII) mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY); ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY); ether_ifdetach(ifp); if_detach(ifp); return (0); } /* * Find supported media on 3c509-generation hardware that doesn't have * a "reset_options" register in window 3. * Use the config_cntrl register in window 0 instead. * Used on original, 10Mbit ISA (3c509), 3c509B, and pre-Demon EISA cards * that implement CONFIG_CTRL. We don't have a good way to set the * default active medium; punt to ifconfig instead. * * XXX what about 3c515, pcmcia 10/100? */ void ep_isa_probemedia(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct ifmedia *ifm = &sc->sc_mii.mii_media; int conn, i; u_int16_t ep_w0_config, port; conn = 0; GO_WINDOW(0); ep_w0_config = bus_space_read_2(iot, ioh, EP_W0_CONFIG_CTRL); for (i = 0; i < nitems(ep_isa_media); i++) { struct ep_media * epm = ep_isa_media + i; if ((ep_w0_config & epm->epm_eeprom_data) != 0) { ifmedia_add(ifm, epm->epm_ifmedia, epm->epm_ifdata, 0); if (conn) printf("/"); printf("%s", epm->epm_name); conn |= epm->epm_conn; } } sc->ep_connectors = conn; /* get default medium from EEPROM */ if (epbusyeeprom(sc)) return; /* XXX why is eeprom busy? */ bus_space_write_2(iot, ioh, EP_W0_EEPROM_COMMAND, READ_EEPROM | EEPROM_ADDR_CFG); if (epbusyeeprom(sc)) return; /* XXX why is eeprom busy? */ port = bus_space_read_2(iot, ioh, EP_W0_EEPROM_DATA); port = port >> 14; printf(" (default %s)\n", ep_vortex_media[port].epm_name); /* tell ifconfig what currently-active media is. */ ifmedia_set(ifm, ep_default_to_media[port]); /* XXX autoselect not yet implemented */ } /* * Find media present on large-packet-capable elink3 devices. * Show onboard configuration of large-packet-capable elink3 devices * (Demon, Vortex, Boomerang), which do not implement CONFIG_CTRL in window 0. * Use media and card-version info in window 3 instead. * * XXX how much of this works with 3c515, pcmcia 10/100? */ void ep_vortex_probemedia(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct ifmedia *ifm = &sc->sc_mii.mii_media; u_int config1, conn; int reset_options; int default_media; /* 3-bit encoding of default (EEPROM) media */ int autoselect; /* boolean: should default to autoselect */ const char *medium_name; register int i; GO_WINDOW(3); config1 = (u_int)bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2); reset_options = (int)bus_space_read_1(iot, ioh, EP_W3_RESET_OPTIONS); GO_WINDOW(0); default_media = (config1 & CONFIG_MEDIAMASK) >> CONFIG_MEDIAMASK_SHIFT; autoselect = (config1 & CONFIG_AUTOSELECT) >> CONFIG_AUTOSELECT_SHIFT; /* set available media options */ conn = 0; for (i = 0; i < nitems(ep_vortex_media); i++) { const struct ep_media *epm = ep_vortex_media + i; if ((reset_options & epm->epm_eeprom_data) != 0) { if (conn) printf("/"); printf("%s", epm->epm_name); conn |= epm->epm_conn; ifmedia_add(ifm, epm->epm_ifmedia, epm->epm_ifdata, 0); } } sc->ep_connectors = conn; /* Show eeprom's idea of default media. */ medium_name = (default_media > nitems(ep_vortex_media) - 1) ? "(unknown/impossible media)" : ep_vortex_media[default_media].epm_name; printf(" default %s%s", medium_name, (autoselect) ? "/autoselect" : ""); /* sc->sc_media = ep_vortex_media[default_media].epm_ifdata;*/ #ifdef notyet /* * Set default: either the active interface the card * reads from the EEPROM, or if autoselect is true, * whatever we find is actually connected. * * XXX autoselect not yet implemented. */ #endif /* notyet */ /* tell ifconfig what currently-active media is. */ ifmedia_set(ifm, ep_default_to_media[default_media]); } /* * Bring device up. * * The order in here seems important. Otherwise we may not receive * interrupts. ?! */ void epinit(struct ep_softc *sc) { register struct ifnet *ifp = &sc->sc_arpcom.ac_if; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int i; /* make sure any pending reset has completed before touching board */ ep_finish_reset(iot, ioh); /* cancel any pending I/O */ epstop(sc); if (sc->bustype != EP_BUS_PCI) { GO_WINDOW(0); bus_space_write_2(iot, ioh, EP_W0_CONFIG_CTRL, 0); bus_space_write_2(iot, ioh, EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); } if (sc->bustype == EP_BUS_PCMCIA) { bus_space_write_2(iot, ioh, EP_W0_RESOURCE_CFG, 0x3f00); } GO_WINDOW(2); for (i = 0; i < 6; i++) /* Reload the ether_addr. */ bus_space_write_1(iot, ioh, EP_W2_ADDR_0 + i, sc->sc_arpcom.ac_enaddr[i]); if (sc->bustype == EP_BUS_PCI || sc->bustype == EP_BUS_EISA) /* * Reset the station-address receive filter. * A bug workaround for busmastering (Vortex, Demon) cards. */ for (i = 0; i < 6; i++) bus_space_write_1(iot, ioh, EP_W2_RECVMASK_0 + i, 0); ep_reset_cmd(sc, EP_COMMAND, RX_RESET); ep_reset_cmd(sc, EP_COMMAND, TX_RESET); GO_WINDOW(1); /* Window 1 is operating window */ for (i = 0; i < 31; i++) bus_space_read_1(iot, ioh, ep_w1_reg(sc, EP_W1_TX_STATUS)); /* Set threshold for Tx-space available interrupt. */ bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | (1600 >> sc->txashift)); if (sc->ep_chipset == EP_CHIPSET_ROADRUNNER) { /* Enable options in the PCMCIA LAN COR register, via * RoadRunner Window 1. * * XXX MAGIC CONSTANTS! */ u_int16_t cor; bus_space_write_2(iot, ioh, EP_W1_RUNNER_RDCTL, (1 << 11)); cor = bus_space_read_2(iot, ioh, 0) & ~0x30; bus_space_write_2(iot, ioh, 0, cor); bus_space_write_2(iot, ioh, EP_W1_RUNNER_WRCTL, 0); bus_space_write_2(iot, ioh, EP_W1_RUNNER_RDCTL, 0); if (sc->ep_flags & EP_FLAGS_MII) { ep_roadrunner_mii_enable(sc); GO_WINDOW(1); } } /* Enable interrupts. */ bus_space_write_2(iot, ioh, EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); bus_space_write_2(iot, ioh, EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); /* * Attempt to get rid of any stray interrupts that occurred during * configuration. On the i386 this isn't possible because one may * already be queued. However, a single stray interrupt is * unimportant. */ bus_space_write_2(iot, ioh, EP_COMMAND, ACK_INTR | 0xff); epsetfilter(sc); epsetmedia(sc, sc->sc_mii.mii_media.ifm_cur->ifm_data); bus_space_write_2(iot, ioh, EP_COMMAND, RX_ENABLE); bus_space_write_2(iot, ioh, EP_COMMAND, TX_ENABLE); epmbuffill(sc); /* Interface is now `running', with no output active. */ ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); /* Attempt to start output, if any. */ epstart(ifp); } /* * Set multicast receive filter. * elink3 hardware has no selective multicast filter in hardware. * Enable reception of all multicasts and filter in software. */ void epsetfilter(struct ep_softc *sc) { register struct ifnet *ifp = &sc->sc_arpcom.ac_if; GO_WINDOW(1); /* Window 1 is operating window */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST | ((ifp->if_flags & IFF_MULTICAST) ? FIL_MULTICAST : 0 ) | ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0 )); } int ep_media_change(struct ifnet *ifp) { register struct ep_softc *sc = ifp->if_softc; return epsetmedia(sc, sc->sc_mii.mii_media.ifm_cur->ifm_data); } /* * Reset and enable the MII on the RoadRunner. */ void ep_roadrunner_mii_enable(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; GO_WINDOW(3); bus_space_write_2(iot, ioh, EP_W3_RESET_OPTIONS, EP_PCI_100BASE_MII|EP_RUNNER_ENABLE_MII); delay(1000); bus_space_write_2(iot, ioh, EP_W3_RESET_OPTIONS, EP_PCI_100BASE_MII|EP_RUNNER_MII_RESET|EP_RUNNER_ENABLE_MII); ep_reset_cmd(sc, EP_COMMAND, TX_RESET); ep_reset_cmd(sc, EP_COMMAND, RX_RESET); delay(1000); bus_space_write_2(iot, ioh, EP_W3_RESET_OPTIONS, EP_PCI_100BASE_MII|EP_RUNNER_ENABLE_MII); } /* * Set active media to a specific given EPMEDIA_<> value. * For vortex/demon/boomerang cards, update media field in w3_internal_config, * and power on selected transceiver. * For 3c509-generation cards (3c509/3c579/3c589/3c509B), * update media field in w0_address_config, and power on selected xcvr. */ int epsetmedia(struct ep_softc *sc, int medium) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int w4_media; int config0, config1; /* * you can `ifconfig (link0|-link0) ep0' to get the following * behaviour: * -link0 disable AUI/UTP. enable BNC. * link0 disable BNC. enable AUI. * link1 if the card has a UTP connector, and link0 is * set too, then you get the UTP port. */ /* * First, change the media-control bits in EP_W4_MEDIA_TYPE. */ /* Turn everything off. First turn off linkbeat and UTP. */ GO_WINDOW(4); w4_media = bus_space_read_2(iot, ioh, EP_W4_MEDIA_TYPE); w4_media = w4_media & ~(ENABLE_UTP|SQE_ENABLE); bus_space_write_2(iot, ioh, EP_W4_MEDIA_TYPE, w4_media); /* Turn off coax */ bus_space_write_2(iot, ioh, EP_COMMAND, STOP_TRANSCEIVER); delay(1000); /* If the device has MII, select it, and then tell the * PHY which media to use. */ if (sc->ep_flags & EP_FLAGS_MII) { GO_WINDOW(3); if (sc->ep_chipset == EP_CHIPSET_ROADRUNNER) { int resopt; resopt = bus_space_read_2(iot, ioh, EP_W3_RESET_OPTIONS); bus_space_write_2(iot, ioh, EP_W3_RESET_OPTIONS, resopt | EP_RUNNER_ENABLE_MII); } config0 = (u_int)bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG); config1 = (u_int)bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2); config1 = config1 & ~CONFIG_MEDIAMASK; config1 |= (EPMEDIA_MII << CONFIG_MEDIAMASK_SHIFT); bus_space_write_2(iot, ioh, EP_W3_INTERNAL_CONFIG, config0); bus_space_write_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2, config1); GO_WINDOW(1); /* back to operating window */ mii_mediachg(&sc->sc_mii); return (0); } /* * Now turn on the selected media/transceiver. */ GO_WINDOW(4); switch (medium) { case EPMEDIA_10BASE_T: bus_space_write_2(iot, ioh, EP_W4_MEDIA_TYPE, (ENABLE_UTP | (sc->bustype == EP_BUS_PCMCIA ? MEDIA_LED : 0))); break; case EPMEDIA_10BASE_2: bus_space_write_2(iot, ioh, EP_COMMAND, START_TRANSCEIVER); DELAY(1000); /* 50ms not enough? */ break; /* XXX following only for new-generation cards */ case EPMEDIA_100BASE_TX: case EPMEDIA_100BASE_FX: case EPMEDIA_100BASE_T4: /* XXX check documentation */ bus_space_write_2(iot, ioh, EP_W4_MEDIA_TYPE, w4_media | LINKBEAT_ENABLE); DELAY(1000); /* not strictly necessary? */ break; case EPMEDIA_AUI: bus_space_write_2(iot, ioh, EP_W4_MEDIA_TYPE, w4_media | SQE_ENABLE); DELAY(1000); /* not strictly necessary? */ break; case EPMEDIA_MII: break; default: #if defined(EP_DEBUG) printf("%s unknown media 0x%x\n", sc->sc_dev.dv_xname, medium); #endif break; } /* * Tell the chip which PHY [sic] to use. */ switch (sc->ep_chipset) { case EP_CHIPSET_VORTEX: case EP_CHIPSET_BOOMERANG2: GO_WINDOW(3); config0 = (u_int)bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG); config1 = (u_int)bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2); #if defined(EP_DEBUG) printf("%s: read 0x%x, 0x%x from EP_W3_CONFIG register\n", sc->sc_dev.dv_xname, config0, config1); #endif config1 = config1 & ~CONFIG_MEDIAMASK; config1 |= (medium << CONFIG_MEDIAMASK_SHIFT); #if defined(EP_DEBUG) printf("epsetmedia: %s: medium 0x%x, 0x%x to EP_W3_CONFIG\n", sc->sc_dev.dv_xname, medium, config1); #endif bus_space_write_2(iot, ioh, EP_W3_INTERNAL_CONFIG, config0); bus_space_write_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2, config1); break; default: GO_WINDOW(0); config0 = bus_space_read_2(iot, ioh, EP_W0_ADDRESS_CFG); config0 &= 0x3fff; bus_space_write_2(iot, ioh, EP_W0_ADDRESS_CFG, config0 | (medium << 14)); DELAY(1000); break; } GO_WINDOW(1); /* Window 1 is operating window */ return (0); } /* * Get currently-selected media from card. * (if_media callback, may be called before interface is brought up). */ void ep_media_status(struct ifnet *ifp, struct ifmediareq *req) { register struct ep_softc *sc = ifp->if_softc; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int config1; u_int ep_mediastatus; /* * If we have MII, go ask the PHY what's going on. */ if (sc->ep_flags & EP_FLAGS_MII) { mii_pollstat(&sc->sc_mii); req->ifm_active = sc->sc_mii.mii_media_active; req->ifm_status = sc->sc_mii.mii_media_status; return; } /* XXX read from softc when we start autosensing media */ req->ifm_active = sc->sc_mii.mii_media.ifm_cur->ifm_media; switch (sc->ep_chipset) { case EP_CHIPSET_VORTEX: case EP_CHIPSET_BOOMERANG: GO_WINDOW(3); delay(5000); config1 = bus_space_read_2(iot, ioh, EP_W3_INTERNAL_CONFIG + 2); GO_WINDOW(1); config1 = (config1 & CONFIG_MEDIAMASK) >> CONFIG_MEDIAMASK_SHIFT; req->ifm_active = ep_default_to_media[config1]; /* XXX check full-duplex bits? */ GO_WINDOW(4); req->ifm_status = IFM_AVALID; /* XXX */ ep_mediastatus = bus_space_read_2(iot, ioh, EP_W4_MEDIA_TYPE); if (ep_mediastatus & LINKBEAT_DETECT) req->ifm_status |= IFM_ACTIVE; /* XXX automedia */ break; case EP_CHIPSET_UNKNOWN: case EP_CHIPSET_3C509: req->ifm_status = 0; /* XXX */ break; default: printf("%s: media_status on unknown chipset 0x%x\n", ifp->if_xname, sc->ep_chipset); break; } /* XXX look for softc heartbeat for other chips or media */ GO_WINDOW(1); return; } /* * Start outputting on the interface. * Always called as splnet(). */ void epstart(struct ifnet *ifp) { register struct ep_softc *sc = ifp->if_softc; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct mbuf *m, *m0; caddr_t data; int sh, len, pad, txreg; /* Don't transmit if interface is busy or not running */ if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) return; startagain: /* Sneak a peek at the next packet */ m0 = ifq_deq_begin(&ifp->if_snd); if (m0 == NULL) return; /* We need to use m->m_pkthdr.len, so require the header */ if ((m0->m_flags & M_PKTHDR) == 0) panic("epstart: no header mbuf"); len = m0->m_pkthdr.len; pad = (4 - len) & 3; /* * The 3c509 automatically pads short packets to minimum ethernet * length, but we drop packets that are too large. Perhaps we should * truncate them instead? */ if (len + pad > ETHER_MAX_LEN) { /* packet is obviously too large: toss it */ ++ifp->if_oerrors; ifq_deq_commit(&ifp->if_snd, m0); m_freem(m0); goto readcheck; } if (bus_space_read_2(iot, ioh, ep_w1_reg(sc, EP_W1_FREE_TX)) < len + pad + 4) { bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | ((len + pad + 4) >> sc->txashift)); /* not enough room in FIFO */ ifq_deq_rollback(&ifp->if_snd, m0); ifq_set_oactive(&ifp->if_snd); return; } else { bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | EP_THRESH_DISABLE); } ifq_deq_commit(&ifp->if_snd, m0); if (m0 == NULL) return; bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_START_THRESH | ((len / 4 + sc->tx_start_thresh) /*>> sc->txashift*/)); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); #endif /* * Do the output at splhigh() so that an interrupt from another device * won't cause a FIFO underrun. */ sh = splhigh(); txreg = ep_w1_reg(sc, EP_W1_TX_PIO_WR_1); bus_space_write_2(iot, ioh, txreg, len); bus_space_write_2(iot, ioh, txreg, 0xffff); /* Second is meaningless */ if (EP_IS_BUS_32(sc->bustype)) { for (m = m0; m; ) { data = mtod(m, u_int8_t *); if (m->m_len > 3 && ALIGNED_POINTER(data, uint32_t)) { bus_space_write_raw_multi_4(iot, ioh, txreg, data, m->m_len & ~3); if (m->m_len & 3) bus_space_write_multi_1(iot, ioh, txreg, data + (m->m_len & ~3), m->m_len & 3); } else bus_space_write_multi_1(iot, ioh, txreg, data, m->m_len); m0 = m_free(m); m = m0; } } else { for (m = m0; m; ) { data = mtod(m, u_int8_t *); if (m->m_len > 1 && ALIGNED_POINTER(data, uint16_t)) { bus_space_write_raw_multi_2(iot, ioh, txreg, data, m->m_len & ~1); if (m->m_len & 1) bus_space_write_1(iot, ioh, txreg, *(data + m->m_len - 1)); } else bus_space_write_multi_1(iot, ioh, txreg, data, m->m_len); m0 = m_free(m); m = m0; } } while (pad--) bus_space_write_1(iot, ioh, txreg, 0); splx(sh); readcheck: if ((bus_space_read_2(iot, ioh, ep_w1_reg(sc, EP_W1_RX_STATUS)) & ERR_INCOMPLETE) == 0) { /* We received a complete packet. */ u_int16_t status = bus_space_read_2(iot, ioh, EP_STATUS); if ((status & S_INTR_LATCH) == 0) { /* * No interrupt, read the packet and continue * Is this supposed to happen? Is my motherboard * completely busted? */ epread(sc); } else /* Got an interrupt, return to get it serviced. */ return; } else { /* Check if we are stuck and reset [see XXX comment] */ if (epstatus(sc)) { #ifdef EP_DEBUG if (ifp->if_flags & IFF_DEBUG) printf("%s: adapter reset\n", sc->sc_dev.dv_xname); #endif epreset(sc); } } goto startagain; } /* * XXX: The 3c509 card can get in a mode where both the fifo status bit * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set * We detect this situation and we reset the adapter. * It happens at times when there is a lot of broadcast traffic * on the cable (once in a blue moon). */ int epstatus(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int16_t fifost; /* * Check the FIFO status and act accordingly */ GO_WINDOW(4); fifost = bus_space_read_2(iot, ioh, EP_W4_FIFO_DIAG); GO_WINDOW(1); if (fifost & FIFOS_RX_UNDERRUN) { #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: RX underrun\n", sc->sc_dev.dv_xname); #endif epreset(sc); return 0; } if (fifost & FIFOS_RX_STATUS_OVERRUN) { #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: RX Status overrun\n", sc->sc_dev.dv_xname); #endif return 1; } if (fifost & FIFOS_RX_OVERRUN) { #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: RX overrun\n", sc->sc_dev.dv_xname); #endif return 1; } if (fifost & FIFOS_TX_OVERRUN) { #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: TX overrun\n", sc->sc_dev.dv_xname); #endif epreset(sc); return 0; } return 0; } void eptxstat(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int i; /* * We need to read+write TX_STATUS until we get a 0 status * in order to turn off the interrupt flag. */ while ((i = bus_space_read_1(iot, ioh, ep_w1_reg(sc, EP_W1_TX_STATUS))) & TXS_COMPLETE) { bus_space_write_1(iot, ioh, ep_w1_reg(sc, EP_W1_TX_STATUS), 0x0); if (i & TXS_JABBER) { ++sc->sc_arpcom.ac_if.if_oerrors; #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: jabber (%x)\n", sc->sc_dev.dv_xname, i); #endif epreset(sc); } else if (i & TXS_UNDERRUN) { ++sc->sc_arpcom.ac_if.if_oerrors; #ifdef EP_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: fifo underrun (%x) @%d\n", sc->sc_dev.dv_xname, i, sc->tx_start_thresh); #endif if (sc->tx_succ_ok < 100) sc->tx_start_thresh = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20); sc->tx_succ_ok = 0; epreset(sc); } else if (i & TXS_MAX_COLLISION) { ++sc->sc_arpcom.ac_if.if_collisions; bus_space_write_2(iot, ioh, EP_COMMAND, TX_ENABLE); ifq_clr_oactive(&sc->sc_arpcom.ac_if.if_snd); } else sc->tx_succ_ok = (sc->tx_succ_ok+1) & 127; } } int epintr(void *arg) { register struct ep_softc *sc = arg; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_int16_t status; int ret = 0; for (;;) { bus_space_write_2(iot, ioh, EP_COMMAND, C_INTR_LATCH); status = bus_space_read_2(iot, ioh, EP_STATUS); if ((status & (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE)) == 0) break; ret = 1; /* * Acknowledge any interrupts. It's important that we do this * first, since there would otherwise be a race condition. * Due to the i386 interrupt queueing, we may get spurious * interrupts occasionally. */ bus_space_write_2(iot, ioh, EP_COMMAND, ACK_INTR | status); if (status & S_RX_COMPLETE) epread(sc); if (status & S_TX_AVAIL) { ifq_clr_oactive(&ifp->if_snd); epstart(ifp); } if (status & S_CARD_FAILURE) { epreset(sc); return (1); } if (status & S_TX_COMPLETE) { eptxstat(sc); epstart(ifp); } } /* no more interrupts */ return (ret); } void epread(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); struct mbuf *m; int len, error = 0; len = bus_space_read_2(iot, ioh, ep_w1_reg(sc, EP_W1_RX_STATUS)); again: #ifdef EP_DEBUG if (ifp->if_flags & IFF_DEBUG) { int err = len & ERR_MASK; char *s = NULL; if (len & ERR_INCOMPLETE) s = "incomplete packet"; else if (err == ERR_OVERRUN) s = "packet overrun"; else if (err == ERR_RUNT) s = "runt packet"; else if (err == ERR_ALIGNMENT) s = "bad alignment"; else if (err == ERR_CRC) s = "bad crc"; else if (err == ERR_OVERSIZE) s = "oversized packet"; else if (err == ERR_DRIBBLE) s = "dribble bits"; if (s) printf("%s: %s\n", sc->sc_dev.dv_xname, s); } #endif if (len & ERR_INCOMPLETE) goto done; if (len & ERR_RX) { ++ifp->if_ierrors; error = 1; goto done; } len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ /* Pull packet off interface. */ m = epget(sc, len); if (m == NULL) { ifp->if_ierrors++; error = 1; goto done; } ml_enqueue(&ml, m); /* * In periods of high traffic we can actually receive enough * packets so that the fifo overrun bit will be set at this point, * even though we just read a packet. In this case we * are not going to receive any more interrupts. We check for * this condition and read again until the fifo is not full. * We could simplify this test by not using epstatus(), but * rechecking the RX_STATUS register directly. This test could * result in unnecessary looping in cases where there is a new * packet but the fifo is not full, but it will not fix the * stuck behavior. * * Even with this improvement, we still get packet overrun errors * which are hurting performance. Maybe when I get some more time * I'll modify epread() so that it can handle RX_EARLY interrupts. */ if (epstatus(sc)) { len = bus_space_read_2(iot, ioh, ep_w1_reg(sc, EP_W1_RX_STATUS)); /* Check if we are stuck and reset [see XXX comment] */ if (len & ERR_INCOMPLETE) { #ifdef EP_DEBUG if (ifp->if_flags & IFF_DEBUG) printf("%s: adapter reset\n", sc->sc_dev.dv_xname); #endif epreset(sc); goto done; } goto again; } done: if (error) ep_discard_rxtop(iot, ioh); if_input(ifp, &ml); } struct mbuf * epget(struct ep_softc *sc, int totlen) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct mbuf *m; caddr_t data; int len, pad, off, sh, rxreg; splassert(IPL_NET); m = sc->mb[sc->next_mb]; sc->mb[sc->next_mb] = NULL; if (m == NULL) { m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES); /* If the queue is no longer full, refill. */ if (!timeout_pending(&sc->sc_epmbuffill_tmo)) timeout_add(&sc->sc_epmbuffill_tmo, 1); } if (!m) return (NULL); sc->next_mb = (sc->next_mb + 1) % MAX_MBS; len = MCLBYTES; m->m_pkthdr.len = totlen; m->m_len = totlen; pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header); m->m_data += pad; len -= pad; /* * We read the packet at splhigh() so that an interrupt from another * device doesn't cause the card's buffer to overflow while we're * reading it. We may still lose packets at other times. */ sh = splhigh(); rxreg = ep_w1_reg(sc, EP_W1_RX_PIO_RD_1); off = 0; while (totlen) { len = min(totlen, m_trailingspace(m)); if (len == 0) panic("ep_get: packet does not fit in MCLBYTES"); data = mtod(m, u_int8_t *); if (EP_IS_BUS_32(sc->bustype)) pad = 4 - ((u_long)(data + off) & 0x3); else pad = (u_long)(data + off) & 0x1; if (pad) { if (pad < len) pad = len; bus_space_read_multi_1(iot, ioh, rxreg, data + off, pad); len = pad; } else if (EP_IS_BUS_32(sc->bustype) && len > 3 && ALIGNED_POINTER(data, uint32_t)) { len &= ~3; bus_space_read_raw_multi_4(iot, ioh, rxreg, data + off, len); } else if (len > 1 && ALIGNED_POINTER(data, uint16_t)) { len &= ~1; bus_space_read_raw_multi_2(iot, ioh, rxreg, data + off, len); } else bus_space_read_multi_1(iot, ioh, rxreg, data + off, len); off += len; totlen -= len; } ep_discard_rxtop(iot, ioh); splx(sh); return m; } int epioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ep_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; s = splnet(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; if (!(ifp->if_flags & IFF_RUNNING)) epinit(sc); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) error = ENETRESET; else epinit(sc); } else { if (ifp->if_flags & IFF_RUNNING) epstop(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); break; default: error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data); } if (error == ENETRESET) { if (ifp->if_flags & IFF_RUNNING) epreset(sc); error = 0; } splx(s); return (error); } void epreset(struct ep_softc *sc) { int s; s = splnet(); epinit(sc); splx(s); } void epwatchdog(struct ifnet *ifp) { struct ep_softc *sc = ifp->if_softc; log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname); ++sc->sc_arpcom.ac_if.if_oerrors; epreset(sc); } void epstop(struct ep_softc *sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; ifp->if_flags &= ~IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); if (sc->ep_flags & EP_FLAGS_MII) { mii_down(&sc->sc_mii); } if (sc->ep_chipset == EP_CHIPSET_ROADRUNNER) { /* Clear the FIFO buffer count, thus halting * any currently-running transactions. */ GO_WINDOW(1); /* sanity */ bus_space_write_2(iot, ioh, EP_W1_RUNNER_WRCTL, 0); bus_space_write_2(iot, ioh, EP_W1_RUNNER_RDCTL, 0); } bus_space_write_2(iot, ioh, EP_COMMAND, RX_DISABLE); ep_discard_rxtop(iot, ioh); bus_space_write_2(iot, ioh, EP_COMMAND, TX_DISABLE); bus_space_write_2(iot, ioh, EP_COMMAND, STOP_TRANSCEIVER); ep_reset_cmd(sc, EP_COMMAND, RX_RESET); ep_reset_cmd(sc, EP_COMMAND, TX_RESET); bus_space_write_2(iot, ioh, EP_COMMAND, C_INTR_LATCH); bus_space_write_2(iot, ioh, EP_COMMAND, SET_RD_0_MASK); bus_space_write_2(iot, ioh, EP_COMMAND, SET_INTR_MASK); bus_space_write_2(iot, ioh, EP_COMMAND, SET_RX_FILTER); epmbufempty(sc); } /* * We get eeprom data from the id_port given an offset into the * eeprom. Basically; after the ID_sequence is sent to all of * the cards; they enter the ID_CMD state where they will accept * command requests. 0x80-0xbf loads the eeprom data. We then * read the port 16 times and with every read; the cards check * for contention (ie: if one card writes a 0 bit and another * writes a 1 bit then the host sees a 0. At the end of the cycle; * each card compares the data on the bus; if there is a difference * then that card goes into ID_WAIT state again). In the meantime; * one bit of data is returned in the AX register which is conveniently * returned to us by bus_space_read_1(). Hence; we read 16 times getting one * bit of data with each read. * * NOTE: the caller must provide an i/o handle for ELINK_ID_PORT! */ u_int16_t epreadeeprom(bus_space_tag_t iot, bus_space_handle_t ioh, int offset) { u_int16_t data = 0; int i; bus_space_write_1(iot, ioh, 0, 0x80 + offset); delay(1000); for (i = 0; i < 16; i++) data = (data << 1) | (bus_space_read_2(iot, ioh, 0) & 1); return (data); } int epbusyeeprom(struct ep_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int i = 100, j; while (i--) { j = bus_space_read_2(iot, ioh, EP_W0_EEPROM_COMMAND); if (j & EEPROM_BUSY) delay(100); else break; } if (!i) { printf("\n%s: eeprom failed to come ready\n", sc->sc_dev.dv_xname); return (1); } if (sc->bustype != EP_BUS_PCMCIA && sc->bustype != EP_BUS_PCI && (j & EEPROM_TST_MODE)) { printf("\n%s: erase pencil mark, or disable PnP mode!\n", sc->sc_dev.dv_xname); return (1); } return (0); } u_int16_t ep_read_eeprom(struct ep_softc *sc, u_int16_t offset) { u_int16_t readcmd; /* * RoadRunner has a larger EEPROM, so a different read command * is required. */ if (sc->ep_chipset == EP_CHIPSET_ROADRUNNER) readcmd = READ_EEPROM_RR; else readcmd = READ_EEPROM; if (epbusyeeprom(sc)) return (0); /* XXX why is eeprom busy? */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W0_EEPROM_COMMAND, readcmd | offset); if (epbusyeeprom(sc)) return (0); /* XXX why is eeprom busy? */ return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W0_EEPROM_DATA)); } void epmbuffill(void *v) { struct ep_softc *sc = v; int s, i; s = splnet(); for (i = 0; i < MAX_MBS; i++) { if (sc->mb[i] == NULL) { sc->mb[i] = MCLGETL(NULL, M_DONTWAIT, MCLBYTES); if (sc->mb[i] == NULL) break; } } /* If the queue was not filled, try again. */ if (i < MAX_MBS) timeout_add(&sc->sc_epmbuffill_tmo, 1); splx(s); } void epmbufempty(struct ep_softc *sc) { int s, i; s = splnet(); for (i = 0; imb[i]) { m_freem(sc->mb[i]); sc->mb[i] = NULL; } } sc->next_mb = 0; timeout_del(&sc->sc_epmbuffill_tmo); splx(s); } void ep_mii_setbit(struct ep_softc *sc, u_int16_t bit) { u_int16_t val; /* We assume we're already in Window 4 */ val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT); bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT, val | bit); } void ep_mii_clrbit(struct ep_softc *sc, u_int16_t bit) { u_int16_t val; /* We assume we're already in Window 4 */ val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT); bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT, val & ~bit); } u_int16_t ep_mii_readbit(struct ep_softc *sc, u_int16_t bit) { /* We assume we're already in Window 4 */ return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT) & bit); } void ep_mii_sync(struct ep_softc *sc) { int i; /* We assume we're already in Window 4 */ ep_mii_clrbit(sc, PHYSMGMT_DIR); for (i = 0; i < 32; i++) { ep_mii_clrbit(sc, PHYSMGMT_CLK); ep_mii_setbit(sc, PHYSMGMT_CLK); } } void ep_mii_sendbits(struct ep_softc *sc, u_int32_t data, int nbits) { int i; /* We assume we're already in Window 4 */ ep_mii_setbit(sc, PHYSMGMT_DIR); for (i = 1 << (nbits - 1); i; i = i >> 1) { ep_mii_clrbit(sc, PHYSMGMT_CLK); ep_mii_readbit(sc, PHYSMGMT_CLK); if (data & i) ep_mii_setbit(sc, PHYSMGMT_DATA); else ep_mii_clrbit(sc, PHYSMGMT_DATA); ep_mii_setbit(sc, PHYSMGMT_CLK); ep_mii_readbit(sc, PHYSMGMT_CLK); } } int ep_mii_readreg(struct device *self, int phy, int reg) { struct ep_softc *sc = (struct ep_softc *)self; int val = 0, i, err; /* * Read the PHY register by manually driving the MII control lines. */ GO_WINDOW(4); bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W4_BOOM_PHYSMGMT, 0); ep_mii_sync(sc); ep_mii_sendbits(sc, MII_COMMAND_START, 2); ep_mii_sendbits(sc, MII_COMMAND_READ, 2); ep_mii_sendbits(sc, phy, 5); ep_mii_sendbits(sc, reg, 5); ep_mii_clrbit(sc, PHYSMGMT_DIR); ep_mii_clrbit(sc, PHYSMGMT_CLK); ep_mii_setbit(sc, PHYSMGMT_CLK); ep_mii_clrbit(sc, PHYSMGMT_CLK); err = ep_mii_readbit(sc, PHYSMGMT_DATA); ep_mii_setbit(sc, PHYSMGMT_CLK); /* Even if an error occurs, must still clock out the cycle. */ for (i = 0; i < 16; i++) { val <<= 1; ep_mii_clrbit(sc, PHYSMGMT_CLK); if (err == 0 && ep_mii_readbit(sc, PHYSMGMT_DATA)) val |= 1; ep_mii_setbit(sc, PHYSMGMT_CLK); } ep_mii_clrbit(sc, PHYSMGMT_CLK); ep_mii_setbit(sc, PHYSMGMT_CLK); GO_WINDOW(1); /* back to operating window */ return (err ? 0 : val); } void ep_mii_writereg(struct device *self, int phy, int reg, int val) { struct ep_softc *sc = (struct ep_softc *)self; /* * Write the PHY register by manually driving the MII control lines. */ GO_WINDOW(4); ep_mii_sync(sc); ep_mii_sendbits(sc, MII_COMMAND_START, 2); ep_mii_sendbits(sc, MII_COMMAND_WRITE, 2); ep_mii_sendbits(sc, phy, 5); ep_mii_sendbits(sc, reg, 5); ep_mii_sendbits(sc, MII_COMMAND_ACK, 2); ep_mii_sendbits(sc, val, 16); ep_mii_clrbit(sc, PHYSMGMT_CLK); ep_mii_setbit(sc, PHYSMGMT_CLK); GO_WINDOW(1); /* back to operating window */ } void ep_statchg(struct device *self) { struct ep_softc *sc = (struct ep_softc *)self; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int mctl; /* XXX Update ifp->if_baudrate */ GO_WINDOW(3); mctl = bus_space_read_2(iot, ioh, EP_W3_MAC_CONTROL); if (sc->sc_mii.mii_media_active & IFM_FDX) mctl |= MAC_CONTROL_FDX; else mctl &= ~MAC_CONTROL_FDX; bus_space_write_2(iot, ioh, EP_W3_MAC_CONTROL, mctl); GO_WINDOW(1); /* back to operating window */ }