Index: head/sys/arm/at91/if_ate.c =================================================================== --- head/sys/arm/at91/if_ate.c (revision 290515) +++ head/sys/arm/at91/if_ate.c (revision 290516) @@ -1,1478 +1,1531 @@ /*- * Copyright (c) 2006 M. Warner Losh. All rights reserved. * Copyright (c) 2009 Greg Ansley. 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* TODO * * 1) Turn on the clock in pmc? Turn off? * 2) GPIO initializtion in board setup code. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #endif #include #include #include #include #include "opt_at91.h" #include #include #include #ifdef FDT #include #include #include #endif #include "miibus_if.h" /* * Driver-specific flags. */ #define ATE_FLAG_DETACHING 0x01 #define ATE_FLAG_MULTICAST 0x02 /* * Old EMAC assumes whole packet fits in one buffer; * new EBACB assumes all receive buffers are 128 bytes */ #define RX_BUF_SIZE(sc) (sc->is_emacb ? 128 : MCLBYTES) /* * EMACB has an 11 bit counter for Rx/Tx Descriptors * for max total of 1024 decriptors each. */ #define ATE_MAX_RX_DESCR 1024 #define ATE_MAX_TX_DESCR 1024 /* How many buffers to allocate */ #define ATE_MAX_TX_BUFFERS 4 /* We have ping-pong tx buffers */ /* How much memory to use for rx buffers */ #define ATE_RX_MEMORY (ATE_MAX_RX_DESCR * 128) /* Actual number of descriptors we allocate */ #define ATE_NUM_RX_DESCR ATE_MAX_RX_DESCR #define ATE_NUM_TX_DESCR ATE_MAX_TX_BUFFERS #if ATE_NUM_TX_DESCR > ATE_MAX_TX_DESCR #error "Can't have more TX buffers that descriptors" #endif #if ATE_NUM_RX_DESCR > ATE_MAX_RX_DESCR #error "Can't have more RX buffers that descriptors" #endif /* Wrap indexes the same way the hardware does */ #define NEXT_RX_IDX(sc, cur) \ ((sc->rx_descs[cur].addr & ETH_WRAP_BIT) ? 0 : (cur + 1)) #define NEXT_TX_IDX(sc, cur) \ ((sc->tx_descs[cur].status & ETHB_TX_WRAP) ? 0 : (cur + 1)) struct ate_softc { struct ifnet *ifp; /* ifnet pointer */ struct mtx sc_mtx; /* Basically a perimeter lock */ device_t dev; /* Myself */ device_t miibus; /* My child miibus */ struct resource *irq_res; /* IRQ resource */ struct resource *mem_res; /* Memory resource */ struct callout tick_ch; /* Tick callout */ struct ifmib_iso_8802_3 mibdata; /* Stuff for network mgmt */ bus_dma_tag_t mtag; /* bus dma tag for mbufs */ bus_dma_tag_t rx_tag; bus_dma_tag_t rx_desc_tag; bus_dmamap_t rx_desc_map; bus_dmamap_t rx_map[ATE_MAX_RX_DESCR]; bus_addr_t rx_desc_phys; /* PA of rx descriptors */ eth_rx_desc_t *rx_descs; /* VA of rx descriptors */ void *rx_buf[ATE_NUM_RX_DESCR]; /* RX buffer space */ int rxhead; /* Current RX map/desc index */ uint32_t rx_buf_size; /* Size of Rx buffers */ bus_dma_tag_t tx_desc_tag; bus_dmamap_t tx_desc_map; bus_dmamap_t tx_map[ATE_MAX_TX_BUFFERS]; bus_addr_t tx_desc_phys; /* PA of tx descriptors */ eth_tx_desc_t *tx_descs; /* VA of tx descriptors */ int txhead; /* Current TX map/desc index */ int txtail; /* Current TX map/desc index */ struct mbuf *sent_mbuf[ATE_MAX_TX_BUFFERS]; /* Sent mbufs */ void *intrhand; /* Interrupt handle */ int flags; int if_flags; int use_rmii; int is_emacb; /* SAM9x hardware version */ }; static inline uint32_t RD4(struct ate_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct ate_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } static inline void BARRIER(struct ate_softc *sc, bus_size_t off, bus_size_t len, int flags) { bus_barrier(sc->mem_res, off, len, flags); } #define ATE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define ATE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define ATE_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define ATE_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define ATE_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define ATE_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); static devclass_t ate_devclass; /* * ifnet entry points. */ static void ateinit_locked(void *); static void atestart_locked(struct ifnet *); static void ateinit(void *); static void atestart(struct ifnet *); static void atestop(struct ate_softc *); static int ateioctl(struct ifnet * ifp, u_long, caddr_t); /* * Bus entry points. */ static int ate_probe(device_t dev); static int ate_attach(device_t dev); static int ate_detach(device_t dev); static void ate_intr(void *); /* * Helper routines. */ static int ate_activate(device_t dev); static void ate_deactivate(struct ate_softc *sc); static int ate_ifmedia_upd(struct ifnet *ifp); static void ate_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); static int ate_get_mac(struct ate_softc *sc, u_char *eaddr); static void ate_set_mac(struct ate_softc *sc, u_char *eaddr); static void ate_rxfilter(struct ate_softc *sc); static int ate_miibus_readreg(device_t dev, int phy, int reg); static int ate_miibus_writereg(device_t dev, int phy, int reg, int data); + /* * The AT91 family of products has the ethernet interface called EMAC. * However, it isn't self identifying. It is anticipated that the parent bus * code will take care to only add ate devices where they really are. As * such, we do nothing here to identify the device and just set its name. + * However, FDT makes it self-identifying. */ static int ate_probe(device_t dev) { #ifdef FDT - if (!ofw_bus_is_compatible(dev, "cdns,at32ap7000-macb")) + if (!ofw_bus_is_compatible(dev, "cdns,at91rm9200-emac") && + !ofw_bus_is_compatible(dev, "cdns,emac") && + !ofw_bus_is_compatible(dev, "cdns,at32ap7000-macb")) return (ENXIO); #endif device_set_desc(dev, "EMAC"); return (0); } +#ifdef FDT +/* + * We have to know if we're using MII or RMII attachment + * for the MACB to talk to the PHY correctly. With FDT, + * we must use rmii if there's a proprety phy-mode + * equal to "rmii". Otherwise we MII mode is used. + */ +static void +ate_set_rmii(struct ate_softc *sc) +{ + phandle_t node; + char prop[10]; + ssize_t len; + + node = ofw_bus_get_node(sc->dev); + memset(prop, 0 ,sizeof(prop)); + len = OF_getproplen(node, "phy-mode"); + if (len != 4) + return; + if (OF_getprop(node, "phy-mode", prop, len) != len) + return; + if (strncmp(prop, "rmii", 4) == 0) + sc->use_rmii = 1; +} + +#else +/* + * We have to know if we're using MII or RMII attachment + * for the MACB to talk to the PHY correctly. Without FDT, + * there's no good way to do this. So, if the config file + * has 'option AT91_ATE_USE_RMII', then we'll force RMII. + * Otherwise, we'll use what the bootloader setup. Either + * it setup RMII or MII, in which case we'll get it right, + * or it did nothing, and we'll fall back to MII and the + * option would override if present. + */ +static void +ata_set_rmii(struct ate_softc *sc) +{ + + /* Default to what boot rom did */ + if (!sc->is_emacb) + sc->use_rmii = + (RD4(sc, ETH_CFG) & ETH_CFG_RMII) == ETH_CFG_RMII; + else + sc->use_rmii = + (RD4(sc, ETHB_UIO) & ETHB_UIO_RMII) == ETHB_UIO_RMII; + +#ifdef AT91_ATE_USE_RMII + /* Compile time override */ + sc->use_rmii = 1; +#endif +} +#endif + static int ate_attach(device_t dev) { struct ate_softc *sc; struct ifnet *ifp = NULL; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; u_char eaddr[ETHER_ADDR_LEN]; uint32_t rnd; int rid, err; sc = device_get_softc(dev); sc->dev = dev; ATE_LOCK_INIT(sc); rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "could not allocate memory resources.\n"); err = ENOMEM; goto out; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "could not allocate interrupt resources.\n"); err = ENOMEM; goto out; } /* New or old version, chooses buffer size. */ +#ifdef FDT + sc->is_emacb = ofw_bus_is_compatible(dev, "cdns,at32ap7000-macb"); +#else sc->is_emacb = at91_is_sam9() || at91_is_sam9xe(); +#endif sc->rx_buf_size = RX_BUF_SIZE(sc); err = ate_activate(dev); if (err) goto out; - /* Default to what boot rom did */ - if (!sc->is_emacb) - sc->use_rmii = - (RD4(sc, ETH_CFG) & ETH_CFG_RMII) == ETH_CFG_RMII; - else - sc->use_rmii = - (RD4(sc, ETHB_UIO) & ETHB_UIO_RMII) == ETHB_UIO_RMII; + ate_set_rmii(sc); -#ifdef AT91_ATE_USE_RMII - /* Compile time override */ - sc->use_rmii = 1; -#endif /* Sysctls */ sctx = device_get_sysctl_ctx(dev); soid = device_get_sysctl_tree(dev); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "rmii", CTLFLAG_RW, &sc->use_rmii, 0, "rmii in use"); /* Calling atestop before ifp is set is OK. */ ATE_LOCK(sc); atestop(sc); ATE_UNLOCK(sc); callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); if ((err = ate_get_mac(sc, eaddr)) != 0) { /* No MAC address configured. Generate the random one. */ if (bootverbose) device_printf(dev, "Generating random ethernet address.\n"); rnd = arc4random(); /* * Set OUI to convenient locally assigned address. 'b' * is 0x62, which has the locally assigned bit set, and * the broadcast/multicast bit clear. */ eaddr[0] = 'b'; eaddr[1] = 's'; eaddr[2] = 'd'; eaddr[3] = (rnd >> 16) & 0xff; eaddr[4] = (rnd >> 8) & 0xff; eaddr[5] = (rnd >> 0) & 0xff; } sc->ifp = ifp = if_alloc(IFT_ETHER); err = mii_attach(dev, &sc->miibus, ifp, ate_ifmedia_upd, ate_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (err != 0) { device_printf(dev, "attaching PHYs failed\n"); goto out; } /* * XXX: Clear the isolate bit, or we won't get up, * at least on the HL201 */ ate_miibus_writereg(dev, 0, 0, 0x3000); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable |= IFCAP_VLAN_MTU; /* The hw bits already set. */ ifp->if_start = atestart; ifp->if_ioctl = ateioctl; ifp->if_init = ateinit; ifp->if_baudrate = 10000000; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ifp->if_linkmib = &sc->mibdata; ifp->if_linkmiblen = sizeof(sc->mibdata); sc->mibdata.dot3Compliance = DOT3COMPLIANCE_COLLS; sc->if_flags = ifp->if_flags; ether_ifattach(ifp, eaddr); /* Activate the interrupt. */ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, ate_intr, sc, &sc->intrhand); if (err) { device_printf(dev, "could not establish interrupt handler.\n"); ether_ifdetach(ifp); goto out; } out: if (err) ate_detach(dev); return (err); } static int ate_detach(device_t dev) { struct ate_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(sc != NULL, ("[ate: %d]: sc is NULL", __LINE__)); ifp = sc->ifp; if (device_is_attached(dev)) { ATE_LOCK(sc); sc->flags |= ATE_FLAG_DETACHING; atestop(sc); ATE_UNLOCK(sc); callout_drain(&sc->tick_ch); ether_ifdetach(ifp); } if (sc->miibus != NULL) { device_delete_child(dev, sc->miibus); sc->miibus = NULL; } bus_generic_detach(sc->dev); ate_deactivate(sc); if (sc->intrhand != NULL) { bus_teardown_intr(dev, sc->irq_res, sc->intrhand); sc->intrhand = NULL; } if (ifp != NULL) { if_free(ifp); sc->ifp = NULL; } if (sc->mem_res != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rman_get_rid(sc->mem_res), sc->mem_res); sc->mem_res = NULL; } if (sc->irq_res != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), sc->irq_res); sc->irq_res = NULL; } ATE_LOCK_DESTROY(sc); return (0); } static void ate_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } static void ate_load_rx_buf(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct ate_softc *sc; if (error != 0) return; sc = (struct ate_softc *)arg; bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_PREWRITE); sc->rx_descs[sc->rxhead].addr = segs[0].ds_addr; sc->rx_descs[sc->rxhead].status = 0; bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_POSTWRITE); } static uint32_t ate_mac_hash(const uint8_t *buf) { uint32_t index = 0; for (int i = 0; i < 48; i++) { index ^= ((buf[i >> 3] >> (i & 7)) & 1) << (i % 6); } return (index); } /* * Compute the multicast filter for this device. */ static int ate_setmcast(struct ate_softc *sc) { uint32_t index; uint32_t mcaf[2]; u_char *af = (u_char *) mcaf; struct ifmultiaddr *ifma; struct ifnet *ifp; ifp = sc->ifp; if ((ifp->if_flags & IFF_PROMISC) != 0) return (0); if ((ifp->if_flags & IFF_ALLMULTI) != 0) { WR4(sc, ETH_HSL, 0xffffffff); WR4(sc, ETH_HSH, 0xffffffff); return (1); } /* Compute the multicast hash. */ mcaf[0] = 0; mcaf[1] = 0; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; index = ate_mac_hash(LLADDR((struct sockaddr_dl *) ifma->ifma_addr)); af[index >> 3] |= 1 << (index & 7); } if_maddr_runlock(ifp); /* * Write the hash to the hash register. This card can also * accept unicast packets as well as multicast packets using this * register for easier bridging operations, but we don't take * advantage of that. Locks here are to avoid LOR with the * if_maddr_rlock, but might not be strictly necessary. */ WR4(sc, ETH_HSL, mcaf[0]); WR4(sc, ETH_HSH, mcaf[1]); return (mcaf[0] || mcaf[1]); } static int ate_activate(device_t dev) { struct ate_softc *sc; int i; sc = device_get_softc(dev); /* Allocate DMA tags and maps for TX mbufs */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, busdma_lock_mutex, &sc->sc_mtx, &sc->mtag)) goto errout; for (i = 0; i < ATE_MAX_TX_BUFFERS; i++) { if ( bus_dmamap_create(sc->mtag, 0, &sc->tx_map[i])) goto errout; } /* DMA tag and map for the RX descriptors. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), sizeof(eth_rx_desc_t), 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, ATE_NUM_RX_DESCR * sizeof(eth_rx_desc_t), 1, ATE_NUM_RX_DESCR * sizeof(eth_rx_desc_t), 0, busdma_lock_mutex, &sc->sc_mtx, &sc->rx_desc_tag)) goto errout; if (bus_dmamem_alloc(sc->rx_desc_tag, (void **)&sc->rx_descs, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->rx_desc_map) != 0) goto errout; if (bus_dmamap_load(sc->rx_desc_tag, sc->rx_desc_map, sc->rx_descs, ATE_NUM_RX_DESCR * sizeof(eth_rx_desc_t), ate_getaddr, &sc->rx_desc_phys, 0) != 0) goto errout; /* Allocate DMA tags and maps for RX. buffers */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sc->rx_buf_size, 1, sc->rx_buf_size, 0, busdma_lock_mutex, &sc->sc_mtx, &sc->rx_tag)) goto errout; /* * Allocate our RX buffers. * This chip has a RX structure that's filled in. * XXX On MACB (SAM9 part) we should receive directly into mbuf * to avoid the copy. XXX */ sc->rxhead = 0; for (sc->rxhead = 0; sc->rxhead < ATE_RX_MEMORY/sc->rx_buf_size; sc->rxhead++) { if (bus_dmamem_alloc(sc->rx_tag, (void **)&sc->rx_buf[sc->rxhead], BUS_DMA_NOWAIT, &sc->rx_map[sc->rxhead]) != 0) goto errout; if (bus_dmamap_load(sc->rx_tag, sc->rx_map[sc->rxhead], sc->rx_buf[sc->rxhead], sc->rx_buf_size, ate_load_rx_buf, sc, 0) != 0) { printf("bus_dmamem_load\n"); goto errout; } bus_dmamap_sync(sc->rx_tag, sc->rx_map[sc->rxhead], BUS_DMASYNC_PREREAD); } /* * For the last buffer, set the wrap bit so the controller * restarts from the first descriptor. */ sc->rx_descs[--sc->rxhead].addr |= ETH_WRAP_BIT; sc->rxhead = 0; /* Flush the memory for the EMAC rx descriptor. */ bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_PREWRITE); /* Write the descriptor queue address. */ WR4(sc, ETH_RBQP, sc->rx_desc_phys); /* * DMA tag and map for the TX descriptors. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), sizeof(eth_tx_desc_t), 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, ATE_MAX_TX_BUFFERS * sizeof(eth_tx_desc_t), 1, ATE_MAX_TX_BUFFERS * sizeof(eth_tx_desc_t), 0, busdma_lock_mutex, &sc->sc_mtx, &sc->tx_desc_tag) != 0) goto errout; if (bus_dmamem_alloc(sc->tx_desc_tag, (void **)&sc->tx_descs, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &sc->tx_desc_map) != 0) goto errout; if (bus_dmamap_load(sc->tx_desc_tag, sc->tx_desc_map, sc->tx_descs, ATE_MAX_TX_BUFFERS * sizeof(eth_tx_desc_t), ate_getaddr, &sc->tx_desc_phys, 0) != 0) goto errout; /* Initilize descriptors; mark all empty */ for (i = 0; i < ATE_MAX_TX_BUFFERS; i++) { sc->tx_descs[i].addr =0; sc->tx_descs[i].status = ETHB_TX_USED; sc->sent_mbuf[i] = NULL; } /* Mark last entry to cause wrap when indexing through */ sc->tx_descs[ATE_MAX_TX_BUFFERS - 1].status = ETHB_TX_WRAP | ETHB_TX_USED; /* Flush the memory for the EMAC tx descriptor. */ bus_dmamap_sync(sc->tx_desc_tag, sc->tx_desc_map, BUS_DMASYNC_PREWRITE); sc->txhead = sc->txtail = 0; if (sc->is_emacb) { /* Write the descriptor queue address. */ WR4(sc, ETHB_TBQP, sc->tx_desc_phys); /* EMACB: Enable transceiver input clock */ WR4(sc, ETHB_UIO, RD4(sc, ETHB_UIO) | ETHB_UIO_CLKE); } return (0); errout: return (ENOMEM); } static void ate_deactivate(struct ate_softc *sc) { int i; KASSERT(sc != NULL, ("[ate, %d]: sc is NULL!", __LINE__)); if (sc->mtag != NULL) { for (i = 0; i < ATE_MAX_TX_BUFFERS; i++) { if (sc->sent_mbuf[i] != NULL) { bus_dmamap_sync(sc->mtag, sc->tx_map[i], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mtag, sc->tx_map[i]); m_freem(sc->sent_mbuf[i]); } bus_dmamap_destroy(sc->mtag, sc->tx_map[i]); sc->sent_mbuf[i] = NULL; sc->tx_map[i] = NULL; } bus_dma_tag_destroy(sc->mtag); } if (sc->rx_desc_tag != NULL) { if (sc->rx_descs != NULL) { if (sc->rx_desc_phys != 0) { bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rx_desc_tag, sc->rx_desc_map); sc->rx_desc_phys = 0; } } } if (sc->rx_tag != NULL) { for (i = 0; sc->rx_buf[i] != NULL; i++) { if (sc->rx_descs[i].addr != 0) { bus_dmamap_sync(sc->rx_tag, sc->rx_map[i], BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rx_tag, sc->rx_map[i]); sc->rx_descs[i].addr = 0; } bus_dmamem_free(sc->rx_tag, sc->rx_buf[i], sc->rx_map[i]); sc->rx_buf[i] = NULL; } bus_dma_tag_destroy(sc->rx_tag); } if (sc->rx_desc_tag != NULL) { if (sc->rx_descs != NULL) bus_dmamem_free(sc->rx_desc_tag, sc->rx_descs, sc->rx_desc_map); bus_dma_tag_destroy(sc->rx_desc_tag); sc->rx_descs = NULL; sc->rx_desc_tag = NULL; } if (sc->is_emacb) WR4(sc, ETHB_UIO, RD4(sc, ETHB_UIO) & ~ETHB_UIO_CLKE); } /* * Change media according to request. */ static int ate_ifmedia_upd(struct ifnet *ifp) { struct ate_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->miibus); ATE_LOCK(sc); mii_mediachg(mii); ATE_UNLOCK(sc); return (0); } /* * Notify the world which media we're using. */ static void ate_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct ate_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->miibus); ATE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; ATE_UNLOCK(sc); } static void ate_stat_update(struct ate_softc *sc, int active) { uint32_t reg; /* * The speed and full/half-duplex state needs to be reflected * in the ETH_CFG register. */ reg = RD4(sc, ETH_CFG); reg &= ~(ETH_CFG_SPD | ETH_CFG_FD); if (IFM_SUBTYPE(active) != IFM_10_T) reg |= ETH_CFG_SPD; if (active & IFM_FDX) reg |= ETH_CFG_FD; WR4(sc, ETH_CFG, reg); } static void ate_tick(void *xsc) { struct ate_softc *sc = xsc; struct ifnet *ifp = sc->ifp; struct mii_data *mii; int active; uint32_t c; /* * The KB920x boot loader tests ETH_SR & ETH_SR_LINK and will ask * the MII if there's a link if this bit is clear. Not sure if we * should do the same thing here or not. */ ATE_ASSERT_LOCKED(sc); if (sc->miibus != NULL) { mii = device_get_softc(sc->miibus); active = mii->mii_media_active; mii_tick(mii); if (mii->mii_media_status & IFM_ACTIVE && active != mii->mii_media_active) ate_stat_update(sc, mii->mii_media_active); } /* * Update the stats as best we can. When we're done, clear * the status counters and start over. We're supposed to read these * registers often enough that they won't overflow. Hopefully * once a second is often enough. Some don't map well to * the dot3Stats mib, so for those we just count them as general * errors. Stats for iframes, ibutes, oframes and obytes are * collected elsewhere. These registers zero on a read to prevent * races. For all the collision stats, also update the collision * stats for the interface. */ sc->mibdata.dot3StatsAlignmentErrors += RD4(sc, ETH_ALE); sc->mibdata.dot3StatsFCSErrors += RD4(sc, ETH_SEQE); c = RD4(sc, ETH_SCOL); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, c); sc->mibdata.dot3StatsSingleCollisionFrames += c; c = RD4(sc, ETH_MCOL); sc->mibdata.dot3StatsMultipleCollisionFrames += c; if_inc_counter(ifp, IFCOUNTER_COLLISIONS, c); sc->mibdata.dot3StatsSQETestErrors += RD4(sc, ETH_SQEE); sc->mibdata.dot3StatsDeferredTransmissions += RD4(sc, ETH_DTE); c = RD4(sc, ETH_LCOL); sc->mibdata.dot3StatsLateCollisions += c; if_inc_counter(ifp, IFCOUNTER_COLLISIONS, c); c = RD4(sc, ETH_ECOL); sc->mibdata.dot3StatsExcessiveCollisions += c; if_inc_counter(ifp, IFCOUNTER_COLLISIONS, c); sc->mibdata.dot3StatsCarrierSenseErrors += RD4(sc, ETH_CSE); sc->mibdata.dot3StatsFrameTooLongs += RD4(sc, ETH_ELR); sc->mibdata.dot3StatsInternalMacReceiveErrors += RD4(sc, ETH_DRFC); /* * Not sure where to lump these, so count them against the errors * for the interface. */ if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, RD4(sc, ETH_TUE)); if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, RD4(sc, ETH_CDE) + RD4(sc, ETH_RJB) + RD4(sc, ETH_USF)); /* Schedule another timeout one second from now. */ callout_reset(&sc->tick_ch, hz, ate_tick, sc); } static void ate_set_mac(struct ate_softc *sc, u_char *eaddr) { WR4(sc, ETH_SA1L, (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); WR4(sc, ETH_SA1H, (eaddr[5] << 8) | (eaddr[4])); } static int ate_get_mac(struct ate_softc *sc, u_char *eaddr) { bus_size_t sa_low_reg[] = { ETH_SA1L, ETH_SA2L, ETH_SA3L, ETH_SA4L }; bus_size_t sa_high_reg[] = { ETH_SA1H, ETH_SA2H, ETH_SA3H, ETH_SA4H }; uint32_t low, high; int i; /* * The boot loader may setup the MAC with an address(es), grab the * first MAC address from the SA[1-4][HL] registers. */ for (i = 0; i < 4; i++) { low = RD4(sc, sa_low_reg[i]); high = RD4(sc, sa_high_reg[i]); if ((low | (high & 0xffff)) != 0) { eaddr[0] = low & 0xff; eaddr[1] = (low >> 8) & 0xff; eaddr[2] = (low >> 16) & 0xff; eaddr[3] = (low >> 24) & 0xff; eaddr[4] = high & 0xff; eaddr[5] = (high >> 8) & 0xff; return (0); } } return (ENXIO); } static void ate_intr(void *xsc) { struct ate_softc *sc = xsc; struct ifnet *ifp = sc->ifp; struct mbuf *mb; eth_rx_desc_t *rxdhead; uint32_t status, reg, idx; int remain, count, done; status = RD4(sc, ETH_ISR); if (status == 0) return; if (status & ETH_ISR_RCOM) { bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_POSTREAD); rxdhead = &sc->rx_descs[sc->rxhead]; while (rxdhead->addr & ETH_CPU_OWNER) { if (!sc->is_emacb) { /* * Simulate SAM9 FIRST/LAST bits for RM9200. * RM9200 EMAC has only on Rx buffer per packet. * But sometime we are handed a zero lenght packet. */ if ((rxdhead->status & ETH_LEN_MASK) == 0) rxdhead->status = 0; /* Mark error */ else rxdhead->status |= ETH_BUF_FIRST | ETH_BUF_LAST; } if ((rxdhead->status & ETH_BUF_FIRST) == 0) { /* Something went wrong during RX so release back to EMAC all buffers of invalid packets. */ rxdhead->status = 0; rxdhead->addr &= ~ETH_CPU_OWNER; sc->rxhead = NEXT_RX_IDX(sc, sc->rxhead); rxdhead = &sc->rx_descs[sc->rxhead]; continue; } /* Find end of packet or start of next */ idx = sc->rxhead; if ((sc->rx_descs[idx].status & ETH_BUF_LAST) == 0) { idx = NEXT_RX_IDX(sc, idx); while ((sc->rx_descs[idx].addr & ETH_CPU_OWNER) && ((sc->rx_descs[idx].status & (ETH_BUF_FIRST|ETH_BUF_LAST))== 0)) idx = NEXT_RX_IDX(sc, idx); } /* Packet NOT yet completely in memory; we are done */ if ((sc->rx_descs[idx].addr & ETH_CPU_OWNER) == 0 || ((sc->rx_descs[idx].status & (ETH_BUF_FIRST|ETH_BUF_LAST))== 0)) break; /* Packets with no end descriptor are invalid. */ if ((sc->rx_descs[idx].status & ETH_BUF_LAST) == 0) { rxdhead->status &= ~ETH_BUF_FIRST; continue; } /* FCS is not coppied into mbuf. */ remain = (sc->rx_descs[idx].status & ETH_LEN_MASK) - 4; /* Get an appropriately sized mbuf. */ mb = m_get2(remain + ETHER_ALIGN, M_NOWAIT, MT_DATA, M_PKTHDR); if (mb == NULL) { if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); rxdhead->status = 0; continue; } mb->m_data += ETHER_ALIGN; mb->m_pkthdr.rcvif = ifp; WR4(sc, ETH_RSR, RD4(sc, ETH_RSR)); /* Reset status */ /* Now we process the buffers that make up the packet */ do { /* Last buffer may just be 1-4 bytes of FCS so remain * may be zero for last decriptor. */ if (remain > 0) { /* Make sure we get the current bytes */ bus_dmamap_sync(sc->rx_tag, sc->rx_map[sc->rxhead], BUS_DMASYNC_POSTREAD); count = MIN(remain, sc->rx_buf_size); /* XXX Performance robbing copy. Could * recieve directly to mbufs if not an * RM9200. And even then we could likely * copy just the protocol headers. XXX */ m_append(mb, count, sc->rx_buf[sc->rxhead]); remain -= count; } done = (rxdhead->status & ETH_BUF_LAST) != 0; /* Return the descriptor to the EMAC */ rxdhead->status = 0; rxdhead->addr &= ~ETH_CPU_OWNER; bus_dmamap_sync(sc->rx_desc_tag, sc->rx_desc_map, BUS_DMASYNC_PREWRITE); /* Move on to next descriptor with wrap */ sc->rxhead = NEXT_RX_IDX(sc, sc->rxhead); rxdhead = &sc->rx_descs[sc->rxhead]; } while (!done); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); (*ifp->if_input)(ifp, mb); } } if (status & ETH_ISR_TCOM) { bus_dmamap_sync(sc->tx_desc_tag, sc->tx_desc_map, BUS_DMASYNC_POSTREAD); ATE_LOCK(sc); /* XXX TSR register should be cleared */ if (!sc->is_emacb) { /* Simulate Transmit descriptor table */ /* First packet done */ if (sc->txtail < sc->txhead) sc->tx_descs[sc->txtail].status |= ETHB_TX_USED; /* Second Packet done */ if (sc->txtail + 1 < sc->txhead && RD4(sc, ETH_TSR) & ETH_TSR_IDLE) sc->tx_descs[sc->txtail + 1].status |= ETHB_TX_USED; } while ((sc->tx_descs[sc->txtail].status & ETHB_TX_USED) && sc->sent_mbuf[sc->txtail] != NULL) { bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txtail], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mtag, sc->tx_map[sc->txtail]); m_freem(sc->sent_mbuf[sc->txtail]); sc->tx_descs[sc->txtail].addr = 0; sc->sent_mbuf[sc->txtail] = NULL; if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); sc->txtail = NEXT_TX_IDX(sc, sc->txtail); } /* Flush descriptors to EMAC */ bus_dmamap_sync(sc->tx_desc_tag, sc->tx_desc_map, BUS_DMASYNC_PREWRITE); /* * We're no longer busy, so clear the busy flag and call the * start routine to xmit more packets. */ sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; atestart_locked(sc->ifp); ATE_UNLOCK(sc); } if (status & ETH_ISR_RBNA) { /* Workaround RM9200 Errata #11 */ if (bootverbose) device_printf(sc->dev, "RBNA workaround\n"); reg = RD4(sc, ETH_CTL); WR4(sc, ETH_CTL, reg & ~ETH_CTL_RE); BARRIER(sc, ETH_CTL, 4, BUS_SPACE_BARRIER_WRITE); WR4(sc, ETH_CTL, reg | ETH_CTL_RE); } /* XXX need to work around SAM9260 errata 43.2.4.1: * disable the mac, reset tx buffer, enable mac on TUND */ } /* * Reset and initialize the chip. */ static void ateinit_locked(void *xsc) { struct ate_softc *sc = xsc; struct ifnet *ifp = sc->ifp; struct mii_data *mii; uint8_t eaddr[ETHER_ADDR_LEN]; uint32_t reg; ATE_ASSERT_LOCKED(sc); /* * XXX TODO(3) * we need to turn on the EMAC clock in the pmc. With the * default boot loader, this is already turned on. However, we * need to think about how best to turn it on/off as the interface * is brought up/down, as well as dealing with the mii bus... * * We also need to multiplex the pins correctly (in board_xxx.c). */ /* * There are two different ways that the mii bus is connected * to this chip mii or rmii. */ if (!sc->is_emacb) { /* RM9200 */ reg = RD4(sc, ETH_CFG); if (sc->use_rmii) reg |= ETH_CFG_RMII; else reg &= ~ETH_CFG_RMII; WR4(sc, ETH_CFG, reg); } else { /* SAM9 */ reg = ETHB_UIO_CLKE; reg |= (sc->use_rmii) ? ETHB_UIO_RMII : 0; WR4(sc, ETHB_UIO, reg); } ate_rxfilter(sc); /* * Set the chip MAC address. */ bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN); ate_set_mac(sc, eaddr); /* Make sure we know state of TX queue */ sc->txhead = sc->txtail = 0; if (sc->is_emacb) { /* Write the descriptor queue address. */ WR4(sc, ETHB_TBQP, sc->tx_desc_phys); } /* * Turn on MACs and interrupt processing. */ WR4(sc, ETH_CTL, RD4(sc, ETH_CTL) | ETH_CTL_TE | ETH_CTL_RE); WR4(sc, ETH_IER, ETH_ISR_RCOM | ETH_ISR_TCOM | ETH_ISR_RBNA); /* Enable big packets. */ WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_BIG); /* * Set 'running' flag, and clear output active flag * and attempt to start the output. */ ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mii = device_get_softc(sc->miibus); mii_pollstat(mii); ate_stat_update(sc, mii->mii_media_active); atestart_locked(ifp); callout_reset(&sc->tick_ch, hz, ate_tick, sc); } /* * Dequeue packets and transmit. */ static void atestart_locked(struct ifnet *ifp) { struct ate_softc *sc = ifp->if_softc; struct mbuf *m, *mdefrag; bus_dma_segment_t segs[1]; int nseg, e; ATE_ASSERT_LOCKED(sc); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) return; while (sc->tx_descs[sc->txhead].status & ETHB_TX_USED) { /* * Check to see if there's room to put another packet into the * xmit queue. The old EMAC version has a ping-pong buffer for * xmit packets. We use OACTIVE to indicate "we can stuff more * into our buffers (clear) or not (set)." */ /* RM9200 has only two hardware entries */ if (!sc->is_emacb && (RD4(sc, ETH_TSR) & ETH_TSR_BNQ) == 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; return; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == 0) break; e = bus_dmamap_load_mbuf_sg(sc->mtag, sc->tx_map[sc->txhead], m, segs, &nseg, 0); if (e == EFBIG) { mdefrag = m_defrag(m, M_NOWAIT); if (mdefrag == NULL) { IFQ_DRV_PREPEND(&ifp->if_snd, m); return; } m = mdefrag; e = bus_dmamap_load_mbuf_sg(sc->mtag, sc->tx_map[sc->txhead], m, segs, &nseg, 0); } if (e != 0) { m_freem(m); continue; } /* * There's a small race between the loop in ate_intr finishing * and the check above to see if the packet was finished, as well * as when atestart gets called via other paths. Lose the race * gracefully and free the mbuf... */ if (sc->sent_mbuf[sc->txhead] != NULL) { bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txtail], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mtag, sc->tx_map[sc->txtail]); m_free(sc->sent_mbuf[sc->txhead]); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } sc->sent_mbuf[sc->txhead] = m; bus_dmamap_sync(sc->mtag, sc->tx_map[sc->txhead], BUS_DMASYNC_PREWRITE); /* Tell the hardware to xmit the packet. */ if (!sc->is_emacb) { WR4(sc, ETH_TAR, segs[0].ds_addr); BARRIER(sc, ETH_TAR, 4, BUS_SPACE_BARRIER_WRITE); WR4(sc, ETH_TCR, segs[0].ds_len); } else { bus_dmamap_sync(sc->tx_desc_tag, sc->tx_desc_map, BUS_DMASYNC_POSTWRITE); sc->tx_descs[sc->txhead].addr = segs[0].ds_addr; sc->tx_descs[sc->txhead].status = segs[0].ds_len | (sc->tx_descs[sc->txhead].status & ETHB_TX_WRAP) | ETHB_TX_BUF_LAST; bus_dmamap_sync(sc->tx_desc_tag, sc->tx_desc_map, BUS_DMASYNC_PREWRITE); WR4(sc, ETH_CTL, RD4(sc, ETH_CTL) | ETHB_CTL_TGO); } sc->txhead = NEXT_TX_IDX(sc, sc->txhead); /* Tap off here if there is a bpf listener. */ BPF_MTAP(ifp, m); } if ((sc->tx_descs[sc->txhead].status & ETHB_TX_USED) == 0) ifp->if_drv_flags |= IFF_DRV_OACTIVE; } static void ateinit(void *xsc) { struct ate_softc *sc = xsc; ATE_LOCK(sc); ateinit_locked(sc); ATE_UNLOCK(sc); } static void atestart(struct ifnet *ifp) { struct ate_softc *sc = ifp->if_softc; ATE_LOCK(sc); atestart_locked(ifp); ATE_UNLOCK(sc); } /* * Turn off interrupts, and stop the NIC. Can be called with sc->ifp NULL, * so be careful. */ static void atestop(struct ate_softc *sc) { struct ifnet *ifp; int i; ATE_ASSERT_LOCKED(sc); ifp = sc->ifp; if (ifp) { //ifp->if_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } callout_stop(&sc->tick_ch); /* * Enable some parts of the MAC that are needed always (like the * MII bus. This turns off the RE and TE bits, which will remain * off until ateinit() is called to turn them on. With RE and TE * turned off, there's no DMA to worry about after this write. */ WR4(sc, ETH_CTL, ETH_CTL_MPE); /* * Turn off all the configured options and revert to defaults. */ /* Make sure thate the MDIO clk is less than * 2.5 Mhz. Can no longer default to /32 since * SAM9 family may have MCK > 80 Mhz */ if (at91_master_clock <= 2000000) WR4(sc, ETH_CFG, ETH_CFG_CLK_8); else if (at91_master_clock <= 4000000) WR4(sc, ETH_CFG, ETH_CFG_CLK_16); else if (at91_master_clock <= 800000) WR4(sc, ETH_CFG, ETH_CFG_CLK_32); else WR4(sc, ETH_CFG, ETH_CFG_CLK_64); /* * Turn off all the interrupts, and ack any pending ones by reading * the ISR. */ WR4(sc, ETH_IDR, 0xffffffff); RD4(sc, ETH_ISR); /* * Clear out the Transmit and Receiver Status registers of any * errors they may be reporting */ WR4(sc, ETH_TSR, 0xffffffff); WR4(sc, ETH_RSR, 0xffffffff); /* Release TX resources. */ for (i = 0; i < ATE_MAX_TX_BUFFERS; i++) { if (sc->sent_mbuf[i] != NULL) { bus_dmamap_sync(sc->mtag, sc->tx_map[i], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mtag, sc->tx_map[i]); m_freem(sc->sent_mbuf[i]); sc->sent_mbuf[i] = NULL; } } /* Turn off transeiver input clock */ if (sc->is_emacb) WR4(sc, ETHB_UIO, RD4(sc, ETHB_UIO) & ~ETHB_UIO_CLKE); /* * XXX we should power down the EMAC if it isn't in use, after * putting it into loopback mode. This saves about 400uA according * to the datasheet. */ } static void ate_rxfilter(struct ate_softc *sc) { struct ifnet *ifp; uint32_t reg; int enabled; KASSERT(sc != NULL, ("[ate, %d]: sc is NULL!", __LINE__)); ATE_ASSERT_LOCKED(sc); ifp = sc->ifp; /* Wipe out old filter settings. */ reg = RD4(sc, ETH_CFG); reg &= ~(ETH_CFG_CAF | ETH_CFG_MTI | ETH_CFG_UNI); reg |= ETH_CFG_NBC; sc->flags &= ~ATE_FLAG_MULTICAST; /* Set new parameters. */ if ((ifp->if_flags & IFF_BROADCAST) != 0) reg &= ~ETH_CFG_NBC; if ((ifp->if_flags & IFF_PROMISC) != 0) { reg |= ETH_CFG_CAF; } else { enabled = ate_setmcast(sc); if (enabled != 0) { reg |= ETH_CFG_MTI; sc->flags |= ATE_FLAG_MULTICAST; } } WR4(sc, ETH_CFG, reg); } static int ateioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ate_softc *sc = ifp->if_softc; struct mii_data *mii; struct ifreq *ifr = (struct ifreq *)data; int drv_flags, flags; int mask, error, enabled; error = 0; flags = ifp->if_flags; drv_flags = ifp->if_drv_flags; switch (cmd) { case SIOCSIFFLAGS: ATE_LOCK(sc); if ((flags & IFF_UP) != 0) { if ((drv_flags & IFF_DRV_RUNNING) != 0) { if (((flags ^ sc->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) ate_rxfilter(sc); } else { if ((sc->flags & ATE_FLAG_DETACHING) == 0) ateinit_locked(sc); } } else if ((drv_flags & IFF_DRV_RUNNING) != 0) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; atestop(sc); } sc->if_flags = flags; ATE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: if ((drv_flags & IFF_DRV_RUNNING) != 0) { ATE_LOCK(sc); enabled = ate_setmcast(sc); if (enabled != (sc->flags & ATE_FLAG_MULTICAST)) ate_rxfilter(sc); ATE_UNLOCK(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = device_get_softc(sc->miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: mask = ifp->if_capenable ^ ifr->ifr_reqcap; if (mask & IFCAP_VLAN_MTU) { ATE_LOCK(sc); if (ifr->ifr_reqcap & IFCAP_VLAN_MTU) { WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) | ETH_CFG_BIG); ifp->if_capenable |= IFCAP_VLAN_MTU; } else { WR4(sc, ETH_CFG, RD4(sc, ETH_CFG) & ~ETH_CFG_BIG); ifp->if_capenable &= ~IFCAP_VLAN_MTU; } ATE_UNLOCK(sc); } default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void ate_child_detached(device_t dev, device_t child) { struct ate_softc *sc; sc = device_get_softc(dev); if (child == sc->miibus) sc->miibus = NULL; } /* * MII bus support routines. */ static int ate_miibus_readreg(device_t dev, int phy, int reg) { struct ate_softc *sc; int val; /* * XXX if we implement agressive power savings, then we need * XXX to make sure that the clock to the emac is on here */ sc = device_get_softc(dev); DELAY(1); /* Hangs w/o this delay really 30.5us atm */ WR4(sc, ETH_MAN, ETH_MAN_REG_RD(phy, reg)); while ((RD4(sc, ETH_SR) & ETH_SR_IDLE) == 0) continue; val = RD4(sc, ETH_MAN) & ETH_MAN_VALUE_MASK; return (val); } static int ate_miibus_writereg(device_t dev, int phy, int reg, int data) { struct ate_softc *sc; /* * XXX if we implement agressive power savings, then we need * XXX to make sure that the clock to the emac is on here */ sc = device_get_softc(dev); WR4(sc, ETH_MAN, ETH_MAN_REG_WR(phy, reg, data)); while ((RD4(sc, ETH_SR) & ETH_SR_IDLE) == 0) continue; return (0); } static device_method_t ate_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ate_probe), DEVMETHOD(device_attach, ate_attach), DEVMETHOD(device_detach, ate_detach), /* Bus interface */ DEVMETHOD(bus_child_detached, ate_child_detached), /* MII interface */ DEVMETHOD(miibus_readreg, ate_miibus_readreg), DEVMETHOD(miibus_writereg, ate_miibus_writereg), DEVMETHOD_END }; static driver_t ate_driver = { "ate", ate_methods, sizeof(struct ate_softc), }; #ifdef FDT DRIVER_MODULE(ate, simplebus, ate_driver, ate_devclass, NULL, NULL); #else DRIVER_MODULE(ate, atmelarm, ate_driver, ate_devclass, NULL, NULL); #endif DRIVER_MODULE(miibus, ate, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(ate, miibus, 1, 1, 1); MODULE_DEPEND(ate, ether, 1, 1, 1); Index: head/sys/arm/at91/if_macb.c =================================================================== --- head/sys/arm/at91/if_macb.c (revision 290515) +++ head/sys/arm/at91/if_macb.c (revision 290516) @@ -1,1576 +1,1617 @@ /*- * Copyright (c) 2010 Yohanes Nugroho * 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. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_platform.h" #include "opt_at91.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #define MACB_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MACB_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define MACB_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define MACB_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define MACB_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define MACB_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); static inline uint32_t read_4(struct macb_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void write_4(struct macb_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } static devclass_t macb_devclass; /* ifnet entry points */ static void macbinit_locked(void *); static void macbstart_locked(struct ifnet *); static void macbinit(void *); static void macbstart(struct ifnet *); static void macbstop(struct macb_softc *); static int macbioctl(struct ifnet * ifp, u_long, caddr_t); /* bus entry points */ static int macb_probe(device_t dev); static int macb_attach(device_t dev); static int macb_detach(device_t dev); /* helper functions */ static int macb_new_rxbuf(struct macb_softc *sc, int index); static void macb_free_desc_dma_tx(struct macb_softc *sc); static void macb_free_desc_dma_rx(struct macb_softc *sc); static void macb_init_desc_dma_tx(struct macb_softc *sc); static void macb_watchdog(struct macb_softc *sc); static int macb_intr_rx_locked(struct macb_softc *sc, int count); static void macb_intr_task(void *arg, int pending __unused); static void macb_intr(void *xsc); static void macb_tx_cleanup(struct macb_softc *sc); static inline int phy_write(struct macb_softc *sc, int phy, int reg, int data); static void macb_reset(struct macb_softc *sc); static void macb_deactivate(device_t dev) { struct macb_softc *sc; sc = device_get_softc(dev); macb_free_desc_dma_tx(sc); macb_free_desc_dma_rx(sc); } static void macb_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { bus_addr_t *paddr; KASSERT(nsegs == 1, ("wrong number of segments, should be 1")); paddr = arg; *paddr = segs->ds_addr; } static int macb_alloc_desc_dma_tx(struct macb_softc *sc) { int error, i; /* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */ error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ 16, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS, /* max size */ 1, /* nsegments */ sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS, 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->dmatag_data_tx); /* dmat */ if (error != 0) { device_printf(sc->dev, "Couldn't create TX descriptor dma tag\n"); return (error); } /* Allocate memory for TX ring. */ error = bus_dmamem_alloc(sc->dmatag_data_tx, (void**)&(sc->desc_tx), BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dmamap_ring_tx); if (error != 0) { device_printf(sc->dev, "failed to allocate TX dma memory\n"); return (error); } /* Load Ring DMA. */ error = bus_dmamap_load(sc->dmatag_data_tx, sc->dmamap_ring_tx, sc->desc_tx, sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS, macb_getaddr, &sc->ring_paddr_tx, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->dev, "can't load TX descriptor dma map\n"); return (error); } /* Allocate a busdma tag for mbufs. No alignment restriction applys. */ error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ MCLBYTES * MAX_FRAGMENT, /* maxsize */ MAX_FRAGMENT, /* nsegments */ MCLBYTES, 0, /* maxsegsz, flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->dmatag_ring_tx); /* dmat */ if (error != 0) { device_printf(sc->dev, "failed to create TX mbuf dma tag\n"); return (error); } for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { /* Create dma map for each descriptor. */ error = bus_dmamap_create(sc->dmatag_ring_tx, 0, &sc->tx_desc[i].dmamap); if (error != 0) { device_printf(sc->dev, "failed to create TX mbuf dma map\n"); return (error); } } return (0); } static void macb_free_desc_dma_tx(struct macb_softc *sc) { struct tx_desc_info *td; int i; /* TX buffers. */ if (sc->dmatag_ring_tx != NULL) { for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { td = &sc->tx_desc[i]; if (td->dmamap != NULL) { bus_dmamap_destroy(sc->dmatag_ring_tx, td->dmamap); td->dmamap = NULL; } } bus_dma_tag_destroy(sc->dmatag_ring_tx); sc->dmatag_ring_tx = NULL; } /* TX descriptor ring. */ if (sc->dmatag_data_tx != NULL) { if (sc->ring_paddr_tx != 0) bus_dmamap_unload(sc->dmatag_data_tx, sc->dmamap_ring_tx); if (sc->desc_tx != NULL) bus_dmamem_free(sc->dmatag_data_tx, sc->desc_tx, sc->dmamap_ring_tx); sc->ring_paddr_tx = 0; sc->desc_tx = NULL; bus_dma_tag_destroy(sc->dmatag_data_tx); sc->dmatag_data_tx = NULL; } } static void macb_init_desc_dma_tx(struct macb_softc *sc) { struct eth_tx_desc *desc; int i; MACB_LOCK_ASSERT(sc); sc->tx_prod = 0; sc->tx_cons = 0; sc->tx_cnt = 0; desc = &sc->desc_tx[0]; bzero(desc, sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS); for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { desc = &sc->desc_tx[i]; if (i == MACB_MAX_TX_BUFFERS - 1) desc->flags = TD_OWN | TD_WRAP_MASK; else desc->flags = TD_OWN; } bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static int macb_alloc_desc_dma_rx(struct macb_softc *sc) { int error, i; /* Allocate a busdma tag and DMA safe memory for RX descriptors. */ error = bus_dma_tag_create(sc->sc_parent_tag, /* parent */ 16, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ /* maxsize, nsegments */ sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS, 1, /* maxsegsz, flags */ sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS, 0, NULL, NULL, /* lockfunc, lockfuncarg */ &sc->dmatag_data_rx); /* dmat */ if (error != 0) { device_printf(sc->dev, "Couldn't create RX descriptor dma tag\n"); return (error); } /* Allocate RX ring. */ error = bus_dmamem_alloc(sc->dmatag_data_rx, (void**)&(sc->desc_rx), BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dmamap_ring_rx); if (error != 0) { device_printf(sc->dev, "failed to allocate RX descriptor dma memory\n"); return (error); } /* Load dmamap. */ error = bus_dmamap_load(sc->dmatag_data_rx, sc->dmamap_ring_rx, sc->desc_rx, sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS, macb_getaddr, &sc->ring_paddr_rx, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->dev, "can't load RX descriptor dma map\n"); return (error); } /* Allocate a busdma tag for mbufs. */ error = bus_dma_tag_create(sc->sc_parent_tag,/* parent */ 16, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ MCLBYTES, 1, /* maxsize, nsegments */ MCLBYTES, 0, /* maxsegsz, flags */ NULL, NULL, /* lockfunc, lockfuncarg */ &sc->dmatag_ring_rx); /* dmat */ if (error != 0) { device_printf(sc->dev, "failed to create RX mbuf dma tag\n"); return (error); } for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) { error = bus_dmamap_create(sc->dmatag_ring_rx, 0, &sc->rx_desc[i].dmamap); if (error != 0) { device_printf(sc->dev, "failed to create RX mbuf dmamap\n"); return (error); } } return (0); } static void macb_free_desc_dma_rx(struct macb_softc *sc) { struct rx_desc_info *rd; int i; /* RX buffers. */ if (sc->dmatag_ring_rx != NULL) { for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) { rd = &sc->rx_desc[i]; if (rd->dmamap != NULL) { bus_dmamap_destroy(sc->dmatag_ring_rx, rd->dmamap); rd->dmamap = NULL; } } bus_dma_tag_destroy(sc->dmatag_ring_rx); sc->dmatag_ring_rx = NULL; } /* RX descriptor ring. */ if (sc->dmatag_data_rx != NULL) { if (sc->ring_paddr_rx != 0) bus_dmamap_unload(sc->dmatag_data_rx, sc->dmamap_ring_rx); if (sc->desc_rx != NULL) bus_dmamem_free(sc->dmatag_data_rx, sc->desc_rx, sc->dmamap_ring_rx); sc->ring_paddr_rx = 0; sc->desc_rx = NULL; bus_dma_tag_destroy(sc->dmatag_data_rx); sc->dmatag_data_rx = NULL; } } static int macb_init_desc_dma_rx(struct macb_softc *sc) { struct eth_rx_desc *desc; struct rx_desc_info *rd; int i; MACB_LOCK_ASSERT(sc); sc->rx_cons = 0; desc = &sc->desc_rx[0]; bzero(desc, sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS); for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) { rd = &sc->rx_desc[i]; rd->buff = NULL; if (macb_new_rxbuf(sc, i) != 0) return (ENOBUFS); } bus_dmamap_sync(sc->dmatag_ring_rx, sc->dmamap_ring_rx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static int macb_new_rxbuf(struct macb_softc *sc, int index) { struct rx_desc_info *rd; struct eth_rx_desc *desc; struct mbuf *m; bus_dma_segment_t seg[1]; int error, nsegs; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES - ETHER_ALIGN; rd = &sc->rx_desc[index]; bus_dmamap_unload(sc->dmatag_ring_rx, rd->dmamap); error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_rx, rd->dmamap, m, seg, &nsegs, 0); KASSERT(nsegs == 1, ("Too many segments returned!")); if (error != 0) { m_free(m); return (error); } bus_dmamap_sync(sc->dmatag_ring_rx, rd->dmamap, BUS_DMASYNC_PREREAD); rd->buff = m; desc = &sc->desc_rx[index]; desc->addr = seg[0].ds_addr; desc->flags = DATA_SIZE; if (index == MACB_MAX_RX_BUFFERS - 1) desc->addr |= RD_WRAP_MASK; return (0); } static int macb_allocate_dma(struct macb_softc *sc) { int error; /* Create parent tag for tx and rx */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->sc_parent_tag); if (error != 0) { device_printf(sc->dev, "Couldn't create parent DMA tag\n"); return (error); } if ((error = macb_alloc_desc_dma_tx(sc)) != 0) return (error); if ((error = macb_alloc_desc_dma_rx(sc)) != 0) return (error); return (0); } static void macb_tick(void *xsc) { struct macb_softc *sc; struct mii_data *mii; sc = xsc; mii = device_get_softc(sc->miibus); mii_tick(mii); macb_watchdog(sc); /* * Schedule another timeout one second from now. */ callout_reset(&sc->tick_ch, hz, macb_tick, sc); } static void macb_watchdog(struct macb_softc *sc) { struct ifnet *ifp; MACB_LOCK_ASSERT(sc); if (sc->macb_watchdog_timer == 0 || --sc->macb_watchdog_timer) return; ifp = sc->ifp; if ((sc->flags & MACB_FLAG_LINK) == 0) { if_printf(ifp, "watchdog timeout (missed link)\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } if_printf(ifp, "watchdog timeout\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; macbinit_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) macbstart_locked(ifp); } static void macbinit_locked(void *xsc) { struct macb_softc *sc; struct ifnet *ifp; int err; uint32_t config; struct mii_data *mii; sc = xsc; ifp = sc->ifp; MACB_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; if ((err = macb_init_desc_dma_rx(sc)) != 0) { device_printf(sc->dev, "no memory for RX buffers\n"); //ecestop(sc); return; } macb_init_desc_dma_tx(sc); config = read_4(sc, EMAC_NCFGR) | (sc->clock << 10); /*set clock*/ config |= CFG_PAE; /* PAuse Enable */ config |= CFG_DRFCS; /* Discard Rx FCS */ config |= CFG_SPD; /* 100 mbps*/ //config |= CFG_CAF; config |= CFG_FD; config |= CFG_RBOF_2; /*offset +2*/ write_4(sc, EMAC_NCFGR, config); /* Initialize TX and RX buffers */ write_4(sc, EMAC_RBQP, sc->ring_paddr_rx); write_4(sc, EMAC_TBQP, sc->ring_paddr_tx); /* Enable TX and RX */ write_4(sc, EMAC_NCR, RX_ENABLE | TX_ENABLE | MPE_ENABLE); /* Enable interrupts */ write_4(sc, EMAC_IER, (RCOMP_INTERRUPT | RXUBR_INTERRUPT | TUND_INTERRUPT | RLE_INTERRUPT | TXERR_INTERRUPT | ROVR_INTERRUPT | HRESP_INTERRUPT| TCOMP_INTERRUPT )); /* * Set 'running' flag, and clear output active flag * and attempt to start the output */ ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mii = device_get_softc(sc->miibus); sc->flags |= MACB_FLAG_LINK; mii_mediachg(mii); callout_reset(&sc->tick_ch, hz, macb_tick, sc); } static void macb_tx_cleanup(struct macb_softc *sc) { struct ifnet *ifp; struct eth_tx_desc *desc; struct tx_desc_info *td; int flags; int status; int i; MACB_LOCK_ASSERT(sc); status = read_4(sc, EMAC_TSR); write_4(sc, EMAC_TSR, status); /*buffer underrun*/ if ((status & TSR_UND) != 0) { /*reset buffers*/ printf("underrun\n"); bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); sc->tx_cons = sc->tx_prod = 0; for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { desc = &sc->desc_tx[i]; desc->flags = TD_OWN; } for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { td = &sc->tx_desc[i]; if (td->buff != NULL) { /* We are finished with this descriptor. */ bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap, BUS_DMASYNC_POSTWRITE); /* ... and unload, so we can reuse. */ bus_dmamap_unload(sc->dmatag_data_tx, td->dmamap); m_freem(td->buff); td->buff = NULL; } } } if ((status & TSR_COMP) == 0) return; if (sc->tx_cons == sc->tx_prod) return; ifp = sc->ifp; /* Prepare to read the ring (owner bit). */ bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); while (sc->tx_cons != sc->tx_prod) { desc = &sc->desc_tx[sc->tx_cons]; if ((desc->flags & TD_OWN) == 0) break; td = &sc->tx_desc[sc->tx_cons]; if (td->buff != NULL) { /* We are finished with this descriptor. */ bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap, BUS_DMASYNC_POSTWRITE); /* ... and unload, so we can reuse. */ bus_dmamap_unload(sc->dmatag_data_tx, td->dmamap); m_freem(td->buff); td->buff = NULL; if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } do { sc->tx_cnt--; MACB_DESC_INC(sc->tx_cons, MACB_MAX_TX_BUFFERS); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; flags = desc->flags; desc->flags = TD_OWN; desc = &sc->desc_tx[sc->tx_cons]; if (flags & TD_LAST) { break; } } while (sc->tx_cons != sc->tx_prod); } /* Unarm watchog timer when there is no pending descriptors in queue. */ if (sc->tx_cnt == 0) sc->macb_watchdog_timer = 0; } static void macb_rx(struct macb_softc *sc) { struct eth_rx_desc *rxdesc; struct ifnet *ifp; struct mbuf *m; int rxbytes; int flags; int nsegs; int first; rxdesc = &(sc->desc_rx[sc->rx_cons]); ifp = sc->ifp; bus_dmamap_sync(sc->dmatag_ring_rx, sc->dmamap_ring_rx, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); nsegs = 0; while (rxdesc->addr & RD_OWN) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) break; flags = rxdesc->flags; rxbytes = flags & RD_LEN_MASK; m = sc->rx_desc[sc->rx_cons].buff; bus_dmamap_sync(sc->dmatag_ring_rx, sc->rx_desc[sc->rx_cons].dmamap, BUS_DMASYNC_POSTREAD); if (macb_new_rxbuf(sc, sc->rx_cons) != 0) { if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); first = sc->rx_cons; do { rxdesc->flags = DATA_SIZE; MACB_DESC_INC(sc->rx_cons, MACB_MAX_RX_BUFFERS); if ((rxdesc->flags & RD_EOF) != 0) break; rxdesc = &(sc->desc_rx[sc->rx_cons]); } while (sc->rx_cons != first); if (sc->macb_cdata.rxhead != NULL) { m_freem(sc->macb_cdata.rxhead); sc->macb_cdata.rxhead = NULL; sc->macb_cdata.rxtail = NULL; } break; } nsegs++; /* Chain received mbufs. */ if (sc->macb_cdata.rxhead == NULL) { m->m_data += 2; sc->macb_cdata.rxhead = m; sc->macb_cdata.rxtail = m; if (flags & RD_EOF) m->m_len = rxbytes; else m->m_len = DATA_SIZE - 2; } else { m->m_flags &= ~M_PKTHDR; m->m_len = DATA_SIZE; sc->macb_cdata.rxtail->m_next = m; sc->macb_cdata.rxtail = m; } if (flags & RD_EOF) { if (nsegs > 1) { sc->macb_cdata.rxtail->m_len = (rxbytes - ((nsegs - 1) * DATA_SIZE)) + 2; } m = sc->macb_cdata.rxhead; m->m_flags |= M_PKTHDR; m->m_pkthdr.len = rxbytes; m->m_pkthdr.rcvif = ifp; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); nsegs = 0; MACB_UNLOCK(sc); (*ifp->if_input)(ifp, m); MACB_LOCK(sc); sc->macb_cdata.rxhead = NULL; sc->macb_cdata.rxtail = NULL; } rxdesc->addr &= ~RD_OWN; MACB_DESC_INC(sc->rx_cons, MACB_MAX_RX_BUFFERS); rxdesc = &(sc->desc_rx[sc->rx_cons]); } write_4(sc, EMAC_IER, (RCOMP_INTERRUPT|RXUBR_INTERRUPT)); } static int macb_intr_rx_locked(struct macb_softc *sc, int count) { macb_rx(sc); return (0); } static void macb_intr_task(void *arg, int pending __unused) { struct macb_softc *sc; sc = arg; MACB_LOCK(sc); macb_intr_rx_locked(sc, -1); MACB_UNLOCK(sc); } static void macb_intr(void *xsc) { struct macb_softc *sc; struct ifnet *ifp; uint32_t status; sc = xsc; ifp = sc->ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { printf("not running\n"); return; } MACB_LOCK(sc); status = read_4(sc, EMAC_ISR); while (status) { if (status & RCOMP_INTERRUPT) { write_4(sc, EMAC_IDR, (RCOMP_INTERRUPT|RXUBR_INTERRUPT)); taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task); } if (status & TCOMP_INTERRUPT) { macb_tx_cleanup(sc); } status = read_4(sc, EMAC_ISR); } if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) macbstart_locked(ifp); MACB_UNLOCK(sc); } static inline int macb_encap(struct macb_softc *sc, struct mbuf **m_head) { struct eth_tx_desc *desc; struct tx_desc_info *txd, *txd_last; struct mbuf *m; bus_dma_segment_t segs[MAX_FRAGMENT]; bus_dmamap_t map; uint32_t csum_flags; int error, i, nsegs, prod, si; M_ASSERTPKTHDR((*m_head)); prod = sc->tx_prod; m = *m_head; txd = txd_last = &sc->tx_desc[prod]; error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_tx, txd->dmamap, *m_head, segs, &nsegs, 0); if (error == EFBIG) { m = m_collapse(*m_head, M_NOWAIT, MAX_FRAGMENT); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_tx, txd->dmamap, *m_head, segs, &nsegs, 0); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) { return (error); } /* Check for TX descriptor overruns. */ if (sc->tx_cnt + nsegs > MACB_MAX_TX_BUFFERS - 1) { bus_dmamap_unload(sc->dmatag_ring_tx, txd->dmamap); return (ENOBUFS); } bus_dmamap_sync(sc->dmatag_ring_tx, txd->dmamap, BUS_DMASYNC_PREWRITE); m = *m_head; /* TODO: VLAN hardware tag insertion. */ csum_flags = 0; si = prod; desc = NULL; for (i = 0; i < nsegs; i++) { desc = &sc->desc_tx[prod]; desc->addr = segs[i].ds_addr; if (i == 0 ) { desc->flags = segs[i].ds_len | TD_OWN; } else { desc->flags = segs[i].ds_len; } if (prod == MACB_MAX_TX_BUFFERS - 1) desc->flags |= TD_WRAP_MASK; sc->tx_cnt++; MACB_DESC_INC(prod, MACB_MAX_TX_BUFFERS); } /* * Set EOP on the last fragment. */ desc->flags |= TD_LAST; desc = &sc->desc_tx[si]; desc->flags &= ~TD_OWN; sc->tx_prod = prod; /* Swap the first dma map and the last. */ map = txd_last->dmamap; txd_last->dmamap = txd->dmamap; txd->dmamap = map; txd->buff = m; return (0); } static void macbstart_locked(struct ifnet *ifp) { struct macb_softc *sc; struct mbuf *m0; #if 0 struct mbuf *m_new; #endif int queued = 0; sc = ifp->if_softc; if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->flags & MACB_FLAG_LINK) == 0) { return; } while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { /* Get packet from the queue */ IF_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; #if 0 if (m0->m_next != NULL) { /* Fragmented mbuf chain, collapse it. */ m_new = m_defrag(m0, M_NOWAIT); if (m_new != NULL) { /* Original frame freed. */ m0 = m_new; } else { /* Defragmentation failed, just use the chain. */ } } #endif if (macb_encap(sc, &m0)) { if (m0 == NULL) break; IF_PREPEND(&ifp->if_snd, m0); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } queued++; BPF_MTAP(ifp, m0); } if (IFQ_DRV_IS_EMPTY(&ifp->if_snd)) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (queued) { bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); write_4(sc, EMAC_NCR, read_4(sc, EMAC_NCR) | TRANSMIT_START); sc->macb_watchdog_timer = MACB_TIMEOUT; } } static void macbinit(void *xsc) { struct macb_softc *sc = xsc; MACB_LOCK(sc); macbinit_locked(sc); MACB_UNLOCK(sc); } static void macbstart(struct ifnet *ifp) { struct macb_softc *sc = ifp->if_softc; MACB_ASSERT_UNLOCKED(sc); MACB_LOCK(sc); macbstart_locked(ifp); MACB_UNLOCK(sc); } static void macbstop(struct macb_softc *sc) { struct ifnet *ifp = sc->ifp; struct rx_desc_info *rd; struct tx_desc_info *td; int i; ifp = sc->ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); macb_reset(sc); sc->flags &= ~MACB_FLAG_LINK; callout_stop(&sc->tick_ch); sc->macb_watchdog_timer = 0; /* Free TX/RX mbufs still in the queues. */ for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) { td = &sc->tx_desc[i]; if (td->buff != NULL) { bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->dmatag_data_tx, td->dmamap); m_freem(td->buff); td->buff = NULL; } } for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) { rd = &sc->rx_desc[i]; if (rd->buff != NULL) { bus_dmamap_sync(sc->dmatag_ring_rx, rd->dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->dmatag_data_rx, rd->dmamap); m_freem(rd->buff); rd->buff = NULL; } } } static int get_hash_index(uint8_t *mac) { int i, j, k; int result; int bit; result = 0; for (i = 0; i < 6; i++) { bit = 0; for (j = 0; j < 8; j++) { k = j * 6 + i; bit ^= (mac[k/8] & (1 << (k % 8)) ) != 0; } result |= bit; } return result; } static void set_mac_filter(uint32_t *filter, uint8_t *mac) { int bits; bits = get_hash_index(mac); filter[bits >> 5] |= 1 << (bits & 31); } static void set_filter(struct macb_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int config; int count; uint32_t multicast_filter[2]; ifp = sc->ifp; config = read_4(sc, EMAC_NCFGR); config &= ~(CFG_CAF | CFG_MTI); write_4(sc, EMAC_HRB, 0); write_4(sc, EMAC_HRT, 0); if ((ifp->if_flags & (IFF_ALLMULTI |IFF_PROMISC)) != 0){ if ((ifp->if_flags & IFF_ALLMULTI) != 0) { write_4(sc, EMAC_HRB, ~0); write_4(sc, EMAC_HRT, ~0); config |= CFG_MTI; } if ((ifp->if_flags & IFF_PROMISC) != 0) { config |= CFG_CAF; } write_4(sc, EMAC_NCFGR, config); return; } if_maddr_rlock(ifp); count = 0; multicast_filter[0] = 0; multicast_filter[1] = 0; TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; count++; set_mac_filter(multicast_filter, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); } if (count) { write_4(sc, EMAC_HRB, multicast_filter[0]); write_4(sc, EMAC_HRT, multicast_filter[1]); write_4(sc, EMAC_NCFGR, config|CFG_MTI); } if_maddr_runlock(ifp); } static int macbioctl(struct ifnet * ifp, u_long cmd, caddr_t data) { struct macb_softc *sc = ifp->if_softc; struct mii_data *mii; struct ifreq *ifr = (struct ifreq *)data; int error = 0; switch (cmd) { case SIOCSIFFLAGS: MACB_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) set_filter(sc); } else { macbinit_locked(sc); } } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { macbstop(sc); } sc->if_flags = ifp->if_flags; MACB_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: MACB_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) set_filter(sc); MACB_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = device_get_softc(sc->miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } /* bus entry points */ static int macb_probe(device_t dev) { #ifdef FDT if (!ofw_bus_is_compatible(dev, "cdns,at32ap7000-macb")) return (ENXIO); #endif device_set_desc(dev, "macb"); return (0); } /* * Change media according to request. */ static int macb_ifmedia_upd(struct ifnet *ifp) { struct macb_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->miibus); MACB_LOCK(sc); mii_mediachg(mii); MACB_UNLOCK(sc); return (0); } /* * Notify the world which media we're using. */ static void macb_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct macb_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->miibus); MACB_LOCK(sc); /* Don't report link state if driver is not running. */ if ((ifp->if_flags & IFF_UP) == 0) { MACB_UNLOCK(sc); return; } mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; MACB_UNLOCK(sc); } static void macb_reset(struct macb_softc *sc) { /* * Disable RX and TX */ write_4(sc, EMAC_NCR, 0); write_4(sc, EMAC_NCR, CLEAR_STAT); /* Clear all status flags */ write_4(sc, EMAC_TSR, ~0UL); write_4(sc, EMAC_RSR, ~0UL); /* Disable all interrupts */ write_4(sc, EMAC_IDR, ~0UL); read_4(sc, EMAC_ISR); } static int macb_get_mac(struct macb_softc *sc, u_char *eaddr) { uint32_t bottom; uint16_t top; bottom = read_4(sc, EMAC_SA1B); top = read_4(sc, EMAC_SA1T); eaddr[0] = bottom & 0xff; eaddr[1] = (bottom >> 8) & 0xff; eaddr[2] = (bottom >> 16) & 0xff; eaddr[3] = (bottom >> 24) & 0xff; eaddr[4] = top & 0xff; eaddr[5] = (top >> 8) & 0xff; return (0); } +#ifdef FDT +/* + * We have to know if we're using MII or RMII attachment + * for the MACB to talk to the PHY correctly. With FDT, + * we must use rmii if there's a proprety phy-mode + * equal to "rmii". Otherwise we MII mode is used. + */ +static void +macb_set_rmii(struct macb_softc *sc) +{ + phandle_t node; + char prop[10]; + ssize_t len; + + node = ofw_bus_get_node(sc->dev); + memset(prop, 0 ,sizeof(prop)); + len = OF_getproplen(node, "phy-mode"); + if (len != 4) + return; + if (OF_getprop(node, "phy-mode", prop, len) != len) + return; + if (strncmp(prop, "rmii", 4) == 0) + sc->use_rmii = USRIO_RMII; +} +#else +/* + * We have to know if we're using MII or RMII attachment + * for the MACB to talk to the PHY correctly. Without FDT, + * there's no good way to do this. So, if the config file + * has 'option AT91_MACB_USE_RMII', then we'll force RMII. + * Otherwise, we'll use what the bootloader setup. Either + * it setup RMII or MII, in which case we'll get it right, + * or it did nothing, and we'll fall back to MII and the + * option would override if present. + */ +static void +macb_set_rmii(struct macb_softc *sc) +{ +#ifdef AT91_MACB_USE_RMII + sc->use_rmii = USRIO_RMII; +#else + sc->use_rmii = read_4(sc, EMAC_USRIO) & USRIO_RMII; +#endif +} +#endif + static int macb_attach(device_t dev) { struct macb_softc *sc; struct ifnet *ifp = NULL; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; int pclk_hz; u_char eaddr[ETHER_ADDR_LEN]; int rid; int err; struct at91_pmc_clock *master; err = 0; sc = device_get_softc(dev); sc->dev = dev; MACB_LOCK_INIT(sc); callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); /* * Allocate resources. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "could not allocate memory resources.\n"); err = ENOMEM; goto out; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "could not allocate interrupt resources.\n"); err = ENOMEM; goto out; } /*setup clock*/ sc->clk = at91_pmc_clock_ref(device_get_nameunit(sc->dev)); at91_pmc_clock_enable(sc->clk); macb_reset(sc); macb_get_mac(sc, eaddr); master = at91_pmc_clock_ref("mck"); pclk_hz = master->hz; sc->clock = CFG_CLK_8; if (pclk_hz <= 20000000) sc->clock = CFG_CLK_8; else if (pclk_hz <= 40000000) sc->clock = CFG_CLK_16; else if (pclk_hz <= 80000000) sc->clock = CFG_CLK_32; else sc->clock = CFG_CLK_64; sc->clock = sc->clock << 10; -#ifdef AT91_MACB_USE_RMII - sc->use_rmii = USRIO_RMII; -#else - sc->use_rmii = read_4(sc, EMAC_USRIO) & USRIO_RMII; -#endif - + macb_set_rmii(sc); write_4(sc, EMAC_NCFGR, sc->clock); write_4(sc, EMAC_USRIO, USRIO_CLOCK | sc->use_rmii); //enable clock write_4(sc, EMAC_NCR, MPE_ENABLE); //enable MPE sc->ifp = ifp = if_alloc(IFT_ETHER); err = mii_attach(dev, &sc->miibus, ifp, macb_ifmedia_upd, macb_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (err != 0) { device_printf(dev, "attaching PHYs failed\n"); goto out; } if (macb_allocate_dma(sc) != 0) goto out; /* Sysctls */ sctx = device_get_sysctl_ctx(dev); soid = device_get_sysctl_tree(dev); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable |= IFCAP_VLAN_MTU; /* The hw bits already set. */ ifp->if_start = macbstart; ifp->if_ioctl = macbioctl; ifp->if_init = macbinit; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); sc->if_flags = ifp->if_flags; TASK_INIT(&sc->sc_intr_task, 0, macb_intr_task, sc); sc->sc_tq = taskqueue_create_fast("macb_taskq", M_WAITOK, taskqueue_thread_enqueue, &sc->sc_tq); if (sc->sc_tq == NULL) { device_printf(sc->dev, "could not create taskqueue\n"); goto out; } ether_ifattach(ifp, eaddr); /* * Activate the interrupt. */ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, macb_intr, sc, &sc->intrhand); if (err) { device_printf(dev, "could not establish interrupt handler.\n"); ether_ifdetach(ifp); goto out; } taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->dev)); sc->macb_cdata.rxhead = 0; sc->macb_cdata.rxtail = 0; phy_write(sc, 0, 0, 0x3300); //force autoneg return (0); out: return (err); } static int macb_detach(device_t dev) { struct macb_softc *sc; sc = device_get_softc(dev); ether_ifdetach(sc->ifp); MACB_LOCK(sc); macbstop(sc); MACB_UNLOCK(sc); callout_drain(&sc->tick_ch); bus_teardown_intr(dev, sc->irq_res, sc->intrhand); taskqueue_drain(sc->sc_tq, &sc->sc_intr_task); taskqueue_free(sc->sc_tq); macb_deactivate(dev); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); MACB_LOCK_DESTROY(sc); return (0); } /*PHY related functions*/ static inline int phy_read(struct macb_softc *sc, int phy, int reg) { int val; write_4(sc, EMAC_MAN, EMAC_MAN_REG_RD(phy, reg)); while ((read_4(sc, EMAC_SR) & EMAC_SR_IDLE) == 0) continue; val = read_4(sc, EMAC_MAN) & EMAC_MAN_VALUE_MASK; return (val); } static inline int phy_write(struct macb_softc *sc, int phy, int reg, int data) { write_4(sc, EMAC_MAN, EMAC_MAN_REG_WR(phy, reg, data)); while ((read_4(sc, EMAC_SR) & EMAC_SR_IDLE) == 0) continue; return (0); } /* * MII bus support routines. */ static int macb_miibus_readreg(device_t dev, int phy, int reg) { struct macb_softc *sc; sc = device_get_softc(dev); return (phy_read(sc, phy, reg)); } static int macb_miibus_writereg(device_t dev, int phy, int reg, int data) { struct macb_softc *sc; sc = device_get_softc(dev); return (phy_write(sc, phy, reg, data)); } static void macb_child_detached(device_t dev, device_t child) { struct macb_softc *sc; sc = device_get_softc(dev); } static void macb_miibus_statchg(device_t dev) { struct macb_softc *sc; struct mii_data *mii; int config; sc = device_get_softc(dev); mii = device_get_softc(sc->miibus); sc->flags &= ~MACB_FLAG_LINK; config = read_4(sc, EMAC_NCFGR); if ((mii->mii_media_status & IFM_AVALID) != 0) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: config &= ~(CFG_SPD); sc->flags |= MACB_FLAG_LINK; break; case IFM_100_TX: config |= CFG_SPD; sc->flags |= MACB_FLAG_LINK; break; default: break; } } config |= CFG_FD; write_4(sc, EMAC_NCFGR, config); } static device_method_t macb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, macb_probe), DEVMETHOD(device_attach, macb_attach), DEVMETHOD(device_detach, macb_detach), /* Bus interface */ DEVMETHOD(bus_child_detached, macb_child_detached), /* MII interface */ DEVMETHOD(miibus_readreg, macb_miibus_readreg), DEVMETHOD(miibus_writereg, macb_miibus_writereg), DEVMETHOD(miibus_statchg, macb_miibus_statchg), { 0, 0 } }; static driver_t macb_driver = { "macb", macb_methods, sizeof(struct macb_softc), }; #ifdef FDT DRIVER_MODULE(macb, simplebus, macb_driver, macb_devclass, NULL, NULL); #else DRIVER_MODULE(macb, atmelarm, macb_driver, macb_devclass, 0, 0); #endif DRIVER_MODULE(miibus, macb, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(macb, miibus, 1, 1, 1); MODULE_DEPEND(macb, ether, 1, 1, 1);