/* $OpenBSD: if_ef_isapnp.c,v 1.9 2000/11/11 23:49:20 mickey Exp $ */ /* * Copyright (c) 1999 Jason L. Wright (jason@thought.net) * 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 Jason L. Wright * 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 "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include #include #include #include #include #include #undef EF_DEBUG struct ef_softc { struct device sc_dv; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; struct arpcom sc_arpcom; struct mii_data sc_mii; struct timeout sc_tick_tmo; void * sc_ih; int sc_tx_start_thresh; int sc_tx_succ_ok; int sc_busmaster; }; #define ETHER_MIN_LEN 64 #define ETHER_MAX_LEN 1518 #define ETHER_ADDR_LEN 6 #define EF_W0_EEPROM_COMMAND 0x200a #define EF_EEPROM_BUSY (1 << 9) #define EF_EEPROM_READ (1 << 7) #define EF_W0_EEPROM_DATA 0x200c #define EF_W1_TX_PIO_WR_1 0x10 #define EF_W1_RX_PIO_RR_1 0x10 #define EF_W1_RX_ERRORS 0x14 #define EF_W1_RX_STATUS 0x18 #define EF_W1_TX_STATUS 0x1b #define EF_W1_FREE_TX 0x1c #define EF_W4_MEDIA 0x0a #define EF_MEDIA_SQE 0x0008 /* sqe error for aui */ #define EF_MEDIA_TP 0x00c0 /* link/jabber, 10baseT */ #define EF_MEDIA_LNK 0x0080 /* linkbeat, 100baseTX/FX */ #define EF_MEDIA_LNKBEAT 0x0800 /* Window 4: EP_W4_CTRLR_STATUS: mii manipulation */ #define EF_MII_CLK 0x01 /* clock bit */ #define EF_MII_DATA 0x02 /* data bit */ #define EF_MII_DIR 0x04 /* direction */ int ef_isapnp_match __P((struct device *, void *, void *)); void ef_isapnp_attach __P((struct device *, struct device *, void *)); void efstart __P((struct ifnet *)); int efioctl __P((struct ifnet *, u_long, caddr_t)); void efwatchdog __P((struct ifnet *)); void efreset __P((struct ef_softc *)); void efstop __P((struct ef_softc *)); void efsetmulti __P((struct ef_softc *)); int efbusyeeprom __P((struct ef_softc *)); int efintr __P((void *)); void efinit __P((struct ef_softc *)); void efcompletecmd __P((struct ef_softc *, u_int, u_int)); void eftxstat __P((struct ef_softc *)); void efread __P((struct ef_softc *)); struct mbuf *efget __P((struct ef_softc *, int totlen)); void ef_miibus_writereg __P((struct device *, int, int, int)); void ef_miibus_statchg __P((struct device *)); int ef_miibus_readreg __P((struct device *, int, int)); void ef_mii_writeb __P((struct ef_softc *, int)); void ef_mii_sync __P((struct ef_softc *)); int ef_ifmedia_upd __P((struct ifnet *)); void ef_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); void ef_tick __P((void *)); struct cfdriver ef_cd = { NULL, "ef", DV_IFNET }; struct cfattach ef_isapnp_ca = { sizeof(struct ef_softc), ef_isapnp_match, ef_isapnp_attach }; int ef_isapnp_match(parent, match, aux) struct device *parent; void *match, *aux; { return (1); } void ef_isapnp_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct ef_softc *sc = (void *)self; struct isa_attach_args *ia = aux; struct ifnet *ifp = &sc->sc_arpcom.ac_if; bus_space_tag_t iot; bus_space_handle_t ioh; int i; u_int16_t x; u_int32_t cfg; sc->sc_iot = iot = ia->ia_iot; sc->sc_ioh = ioh = ia->ipa_io[0].h; efcompletecmd(sc, EP_COMMAND, GLOBAL_RESET); DELAY(1500); for (i = 0; i < 3; i++) { if (efbusyeeprom(sc)) return; bus_space_write_2(iot, ioh, EF_W0_EEPROM_COMMAND, EF_EEPROM_READ | i); if (efbusyeeprom(sc)) return; x = bus_space_read_2(iot, ioh, EF_W0_EEPROM_DATA); sc->sc_arpcom.ac_enaddr[(i << 1)] = x >> 8; sc->sc_arpcom.ac_enaddr[(i << 1) + 1] = x; } printf(": address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr)); GO_WINDOW(3); cfg = bus_space_read_4(iot, ioh, EP_W3_INTERNAL_CONFIG); cfg &= ~(0x00f00000); cfg |= (0x06 << 20); bus_space_write_4(iot, ioh, EP_W3_INTERNAL_CONFIG, cfg); sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, IPL_NET, efintr, sc, sc->sc_dv.dv_xname); if (ia->ia_drq != DRQUNK) isadma_cascade(ia->ia_drq); timeout_set(&sc->sc_tick_tmo, ef_tick, sc); bcopy(sc->sc_dv.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_softc = sc; ifp->if_start = efstart; ifp->if_ioctl = efioctl; ifp->if_watchdog = efwatchdog; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; sc->sc_mii.mii_ifp = ifp; sc->sc_mii.mii_readreg = ef_miibus_readreg; sc->sc_mii.mii_writereg = ef_miibus_writereg; sc->sc_mii.mii_statchg = ef_miibus_statchg; ifmedia_init(&sc->sc_mii.mii_media, 0, ef_ifmedia_upd, ef_ifmedia_sts); mii_attach(self, &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); if_attach(ifp); ether_ifattach(ifp); #if NBPFILTER > 0 bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif sc->sc_tx_start_thresh = 20; efcompletecmd(sc, EP_COMMAND, RX_RESET); efcompletecmd(sc, EP_COMMAND, TX_RESET); } void efstart(ifp) struct ifnet *ifp; { struct ef_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; int s, len, pad, i; int fillcnt = 0; u_int32_t filler = 0; if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) return; startagain: m0 = ifp->if_snd.ifq_head; if (m0 == NULL) return; if ((m0->m_flags & M_PKTHDR) == 0) panic("efstart: no header mbuf"); len = m0->m_pkthdr.len; pad = (4 - len) & 3; if (len + pad > ETHER_MAX_LEN) { ifp->if_oerrors++; IF_DEQUEUE(&ifp->if_snd, m0); m_freem(m0); goto startagain; } if (bus_space_read_2(iot, ioh, EF_W1_FREE_TX) < len + pad + 4) { bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | ((len + pad) >> 2)); ifp->if_flags |= IFF_OACTIVE; return; } else { bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | EP_THRESH_DISABLE); } bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_START_THRESH | ((len / 4 + sc->sc_tx_start_thresh))); #if NBPFILTER if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m0); #endif IF_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) /* XXX not needed */ return; s = splhigh(); bus_space_write_4(iot, ioh, EF_W1_TX_PIO_WR_1, len); for (m = m0; m; ) { if (fillcnt) { while (m->m_len && fillcnt < 4) { fillcnt++; filler >>= 8; filler |= m->m_data[0] << 24; m->m_data++; m->m_len--; } if (fillcnt == 4) { bus_space_write_4(iot, ioh, EF_W1_TX_PIO_WR_1, filler); filler = 0; fillcnt = 0; } } if (m->m_len & ~3) bus_space_write_multi_4(iot, ioh, EF_W1_TX_PIO_WR_1, (u_int32_t *)m->m_data, m->m_len >> 2); for (i = 0; i < (m->m_len & 3); i++) { fillcnt++; filler >>= 8; filler |= m->m_data[(m->m_len & ~3) + i] << 24; } MFREE(m, m0); m = m0; } if (fillcnt) { bus_space_write_4(iot, ioh, EF_W1_TX_PIO_WR_1, filler >> (32 - (8 * fillcnt))); fillcnt = 0; filler = 0; } splx(s); ifp->if_opackets++; goto startagain; } int efioctl(ifp, cmd, data) struct ifnet *ifp; u_long cmd; caddr_t data; { struct ef_softc *sc = ifp->if_softc; struct ifaddr *ifa = (struct ifaddr *)data; struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; s = splimp(); if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { splx(s); return (error); } switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: efinit(sc); arp_ifinit(&sc->sc_arpcom, ifa); break; #endif default: efinit(sc); break; } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_flags & IFF_RUNNING) != 0) { efstop(sc); ifp->if_flags &= ~IFF_RUNNING; } else if ((ifp->if_flags & IFF_UP) != 0 && (ifp->if_flags & IFF_RUNNING) == 0) { efinit(sc); } efsetmulti(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: error = (cmd == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->sc_arpcom) : ether_delmulti(ifr, &sc->sc_arpcom); if (error == ENETRESET) { efreset(sc); error = 0; } efsetmulti(sc); break; default: error = EINVAL; break; } splx(s); return (error); } void efinit(sc) struct ef_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; int i, s; s = splimp(); efstop(sc); while (bus_space_read_2(iot, ioh, EP_STATUS) & S_COMMAND_IN_PROGRESS) ; GO_WINDOW(2); for (i = 0; i < 6; i++) bus_space_write_1(iot, ioh, EP_W2_ADDR_0 + i, sc->sc_arpcom.ac_enaddr[i]); for (i = 0; i < 3; i += 2) bus_space_write_2(iot, ioh, EP_W2_RECVMASK_0 + (i * 2), 0); efcompletecmd(sc, EP_COMMAND, RX_RESET); efcompletecmd(sc, EP_COMMAND, TX_RESET); bus_space_write_2(iot, ioh, EP_COMMAND, SET_TX_AVAIL_THRESH | (1536 >> 2)); efsetmulti(sc); bus_space_write_2(iot, ioh, EP_COMMAND, STATUS_ENABLE | 0); GO_WINDOW(6); for (i = 0; i < 10; i++) (void)bus_space_read_1(iot, ioh, i); (void)bus_space_read_2(iot, ioh, 10); (void)bus_space_read_2(iot, ioh, 12); GO_WINDOW(4); (void)bus_space_read_1(iot, ioh, 12); bus_space_write_2(iot, ioh, EP_W4_NET_DIAG, 0x0040); GO_WINDOW(7); efsetmulti(sc); bus_space_write_2(iot, ioh, EP_COMMAND, RX_ENABLE); bus_space_write_2(iot, ioh, EP_COMMAND, TX_ENABLE); bus_space_write_2(iot, ioh, EP_COMMAND, STATUS_ENABLE | S_CARD_FAILURE | S_INT_RQD | S_UPD_STATS | S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | (sc->sc_busmaster ? S_DMA_DONE : 0)); bus_space_write_2(iot, ioh, EP_COMMAND, ACK_INTR | S_INTR_LATCH | S_TX_AVAIL | S_RX_EARLY | S_INT_RQD); bus_space_write_2(iot, ioh, EP_COMMAND, SET_INTR_MASK | S_INTR_LATCH | S_TX_AVAIL | S_RX_COMPLETE | S_UPD_STATS | (sc->sc_busmaster ? S_DMA_DONE : 0) | S_UP_COMPLETE | S_DOWN_COMPLETE | S_CARD_FAILURE | S_TX_COMPLETE); mii_mediachg(&sc->sc_mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; splx(s); timeout_add(&sc->sc_tick_tmo, hz); efstart(ifp); } void efreset(sc) struct ef_softc *sc; { int s; s = splnet(); efstop(sc); efinit(sc); splx(s); } void efstop(sc) struct ef_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_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); timeout_del(&sc->sc_tick_tmo); bus_space_write_2(iot, ioh, EP_COMMAND, RX_DISABLE); efcompletecmd(sc, EP_COMMAND, RX_DISCARD_TOP_PACK); bus_space_write_2(iot, ioh, EP_COMMAND, TX_DISABLE); bus_space_write_2(iot, ioh, EP_COMMAND, STOP_TRANSCEIVER); efcompletecmd(sc, EP_COMMAND, RX_RESET); efcompletecmd(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); } void efcompletecmd(sc, cmd, arg) struct ef_softc *sc; u_int cmd, 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); while (bus_space_read_2(iot, ioh, EP_STATUS) & S_COMMAND_IN_PROGRESS) ; } int efintr(vsc) void *vsc; { struct ef_softc *sc = vsc; 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 r = 0; status = bus_space_read_2(iot, ioh, EP_STATUS); do { if (status & S_RX_COMPLETE) { r = 1; bus_space_write_2(iot, ioh, EP_STATUS, C_RX_COMPLETE); efread(sc); } if (status & S_TX_AVAIL) { bus_space_write_2(iot, ioh, EP_STATUS, C_TX_AVAIL); r = 1; sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE; efstart(&sc->sc_arpcom.ac_if); } if (status & S_CARD_FAILURE) { r = 1; efreset(sc); printf("%s: adapter failure (%x)\n", sc->sc_dv.dv_xname, status); bus_space_write_2(iot, ioh, EP_COMMAND, C_CARD_FAILURE); return (1); } if (status & S_TX_COMPLETE) { r = 1; eftxstat(sc); efstart(ifp); } bus_space_write_2(iot, ioh, EP_COMMAND, C_INTR_LATCH | C_INT_RQD); } while ((status = bus_space_read_2(iot, ioh, EP_STATUS)) & (S_INT_RQD | S_RX_COMPLETE)); return (r); } void eftxstat(sc) struct ef_softc *sc; { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int i; while ((i = bus_space_read_1(iot, ioh, EF_W1_TX_STATUS)) & TXS_COMPLETE) { bus_space_write_1(iot, ioh, EF_W1_TX_STATUS, 0); if (i & TXS_JABBER) { sc->sc_arpcom.ac_if.if_oerrors++; #ifdef EF_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: jabber (%x)\n", sc->sc_dv.dv_xname, i); #endif efreset(sc); } else if (i & TXS_UNDERRUN) { sc->sc_arpcom.ac_if.if_oerrors++; #ifdef EF_DEBUG if (sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) printf("%s: fifo underrun (%x) @%d\n", sc->sc_dv.dv_xname, i, sc->sc_tx_start_thresh); #endif if (sc->sc_tx_succ_ok < 100) sc->sc_tx_start_thresh = min(ETHER_MAX_LEN, sc->sc_tx_start_thresh + 20); sc->sc_tx_succ_ok = 0; efreset(sc); } else if (i & TXS_MAX_COLLISION) { sc->sc_arpcom.ac_if.if_collisions++; bus_space_write_2(iot, ioh, EP_COMMAND, TX_ENABLE); sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE; } else sc->sc_tx_succ_ok = (sc->sc_tx_succ_ok + 1) & 127; } } int efbusyeeprom(sc) struct ef_softc *sc; { int i = 100, j; while (i--) { j = bus_space_read_2(sc->sc_iot, sc->sc_ioh, EF_W0_EEPROM_COMMAND); if (j & EF_EEPROM_BUSY) delay(100); else break; } if (i == 0) { printf("%s: eeprom failed to come ready\n", sc->sc_dv.dv_xname); return (1); } return (0); } void efwatchdog(ifp) struct ifnet *ifp; { struct ef_softc *sc = ifp->if_softc; printf("%s: device timeout\n", sc->sc_dv.dv_xname); sc->sc_arpcom.ac_if.if_oerrors++; efreset(sc); } void efsetmulti(sc) struct ef_softc *sc; { struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct arpcom *ac = &sc->sc_arpcom; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; struct ether_multi *enm; struct ether_multistep step; u_int16_t cmd = SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST; int mcnt = 0; ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { mcnt++; ETHER_NEXT_MULTI(step, enm); } if (mcnt || ifp->if_flags & IFF_ALLMULTI) cmd |= FIL_MULTICAST; if (ifp->if_flags & IFF_PROMISC) cmd |= FIL_PROMISC; bus_space_write_2(iot, ioh, EP_COMMAND, cmd); } void efread(sc) struct ef_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 *m; struct ether_header *eh; int len; len = bus_space_read_2(iot, ioh, EF_W1_RX_STATUS); #ifdef EF_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_dv.dv_xname, s); } #endif if (len & ERR_INCOMPLETE) return; if (len & ERR_RX) { ifp->if_ierrors++; efcompletecmd(sc, EP_COMMAND, RX_DISCARD_TOP_PACK); return; } len &= RX_BYTES_MASK; m = efget(sc, len); if (m == NULL) { ifp->if_ierrors++; efcompletecmd(sc, EP_COMMAND, RX_DISCARD_TOP_PACK); return; } ifp->if_ipackets++; eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m); #endif m_adj(m, sizeof(struct ether_header)); ether_input(ifp, eh, m); } struct mbuf * efget(sc, totlen) struct ef_softc *sc; int totlen; { 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 *top, **mp, *m; int len, pad, s; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (NULL); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = totlen; pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header); m->m_data += pad; len = MHLEN -pad; top = 0; mp = ⊤ s = splhigh(); while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) { m_freem(top); splx(s); return (NULL); } len = MLEN; } if (top && totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) len = MCLBYTES; } len = min(totlen, len); if (len > 1) { len &= ~1; bus_space_read_raw_multi_2(iot, ioh, EF_W1_RX_PIO_RR_1, mtod(m, u_int8_t *), len); } else *(mtod(m, u_int8_t *)) = bus_space_read_1(iot, ioh, EF_W1_RX_PIO_RR_1); m->m_len = len; totlen -= len; *mp = m; mp = &m->m_next; } efcompletecmd(sc, EP_COMMAND, RX_DISCARD_TOP_PACK); splx(s); return (top); } #define MII_SET(sc, x) \ bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, EP_W4_CTRLR_STATUS, \ bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, EP_W4_CTRLR_STATUS) \ | (x)) #define MII_CLR(sc, x) \ bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, EP_W4_CTRLR_STATUS, \ bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, EP_W4_CTRLR_STATUS) \ & (~(x))) void ef_mii_writeb(sc, b) struct ef_softc *sc; int b; { MII_CLR(sc, EF_MII_CLK); if (b) MII_SET(sc, EF_MII_DATA); else MII_CLR(sc, EF_MII_DATA); MII_CLR(sc, EF_MII_CLK); DELAY(1); MII_SET(sc, EF_MII_CLK); DELAY(1); } void ef_mii_sync(sc) struct ef_softc *sc; { int i; for (i = 0; i < 32; i++) ef_mii_writeb(sc, 1); } int ef_miibus_readreg(dev, phy, reg) struct device *dev; int phy, reg; { struct ef_softc *sc = (struct ef_softc *)dev; int i, ack, s, val = 0; s = splimp(); GO_WINDOW(4); bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W4_CTRLR_STATUS, 0); /* Turn on xmit */ MII_SET(sc, EF_MII_DIR); MII_CLR(sc, EF_MII_CLK); ef_mii_sync(sc); /* Transmit start sequence */ ef_mii_writeb(sc, 0); ef_mii_writeb(sc, 1); /* Transmit read sequence */ ef_mii_writeb(sc, 1); ef_mii_writeb(sc, 0); /* Transmit phy addr */ for (i = 0x10; i; i >>= 1) ef_mii_writeb(sc, (phy & i) ? 1 : 0); /* Transmit reg addr */ for (i = 0x10; i; i >>= 1) ef_mii_writeb(sc, (reg & i) ? 1 : 0); /* First cycle of turnaround */ MII_CLR(sc, EF_MII_CLK | EF_MII_DATA); DELAY(1); MII_SET(sc, EF_MII_CLK); DELAY(1); /* Turn off xmit */ MII_CLR(sc, EF_MII_DIR); /* Second cycle of turnaround */ MII_CLR(sc, EF_MII_CLK); DELAY(1); MII_SET(sc, EF_MII_CLK); DELAY(1); ack = bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W4_CTRLR_STATUS) & EF_MII_DATA; /* Read 16bit data */ for (i = 0x8000; i; i >>= 1) { MII_CLR(sc, EF_MII_CLK); DELAY(1); if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, EP_W4_CTRLR_STATUS) & EF_MII_DATA) val |= i; MII_SET(sc, EF_MII_CLK); DELAY(1); } MII_CLR(sc, EF_MII_CLK); DELAY(1); MII_SET(sc, EF_MII_CLK); DELAY(1); splx(s); return (val); } void ef_miibus_writereg(dev, phy, reg, val) struct device *dev; int phy, reg, val; { struct ef_softc *sc = (struct ef_softc *)dev; int s, i; s = splimp(); GO_WINDOW(4); bus_space_write_2(sc->sc_iot, sc->sc_ioh, EP_W4_CTRLR_STATUS, 0); /* Turn on xmit */ MII_SET(sc, EF_MII_DIR); ef_mii_sync(sc); ef_mii_writeb(sc, 0); ef_mii_writeb(sc, 1); ef_mii_writeb(sc, 0); ef_mii_writeb(sc, 1); for (i = 0x10; i; i >>= 1) ef_mii_writeb(sc, (phy & i) ? 1 : 0); for (i = 0x10; i; i >>= 1) ef_mii_writeb(sc, (reg & i) ? 1 : 0); ef_mii_writeb(sc, 1); ef_mii_writeb(sc, 0); for (i = 0x8000; i; i >>= 1) ef_mii_writeb(sc, (val & i) ? 1 : 0); splx(s); } int ef_ifmedia_upd(ifp) struct ifnet *ifp; { struct ef_softc *sc = ifp->if_softc; mii_mediachg(&sc->sc_mii); return (0); } void ef_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct ef_softc *sc = ifp->if_softc; mii_pollstat(&sc->sc_mii); ifmr->ifm_status = sc->sc_mii.mii_media_status; ifmr->ifm_active = sc->sc_mii.mii_media_active; } void ef_miibus_statchg(self) struct device *self; { struct ef_softc *sc = (struct ef_softc *)self; int s; s = splimp(); GO_WINDOW(3); /* Set duplex bit appropriately */ if ((sc->sc_mii.mii_media_active & IFM_GMASK) == IFM_FDX) bus_space_write_1(sc->sc_iot, sc->sc_ioh, EP_W3_MAC_CONTROL, 0x20); else bus_space_write_1(sc->sc_iot, sc->sc_ioh, EP_W3_MAC_CONTROL, 0x00); GO_WINDOW(7); splx(s); } void ef_tick(v) void *v; { struct ef_softc *sc = v; int s; s = splimp(); mii_tick(&sc->sc_mii); splx(s); timeout_add(&sc->sc_tick_tmo, hz); }