13fabb142SSascha Wildner /* 23fabb142SSascha Wildner * Copyright (c) 2008 The DragonFly Project. All rights reserved. 33fabb142SSascha Wildner * 43fabb142SSascha Wildner * Redistribution and use in source and binary forms, with or without 53fabb142SSascha Wildner * modification, are permitted provided that the following conditions 63fabb142SSascha Wildner * are met: 73fabb142SSascha Wildner * 83fabb142SSascha Wildner * 1. Redistributions of source code must retain the above copyright 93fabb142SSascha Wildner * notice, this list of conditions and the following disclaimer. 103fabb142SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 113fabb142SSascha Wildner * notice, this list of conditions and the following disclaimer in 123fabb142SSascha Wildner * the documentation and/or other materials provided with the 133fabb142SSascha Wildner * distribution. 143fabb142SSascha Wildner * 3. Neither the name of The DragonFly Project nor the names of its 153fabb142SSascha Wildner * contributors may be used to endorse or promote products derived 163fabb142SSascha Wildner * from this software without specific, prior written permission. 173fabb142SSascha Wildner * 183fabb142SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 193fabb142SSascha Wildner * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 203fabb142SSascha Wildner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 213fabb142SSascha Wildner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 223fabb142SSascha Wildner * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 233fabb142SSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 243fabb142SSascha Wildner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 253fabb142SSascha Wildner * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 263fabb142SSascha Wildner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 273fabb142SSascha Wildner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 283fabb142SSascha Wildner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 293fabb142SSascha Wildner * SUCH DAMAGE. 303fabb142SSascha Wildner * 313fabb142SSascha Wildner * $FreeBSD-4.7: /usr/src/sys/pci/silan.c,v 1.0 2003/01/10 gaoyonghong $ 323fabb142SSascha Wildner */ 333fabb142SSascha Wildner 34d818cbbdSSepherosa Ziehau #include <sys/param.h> 353fabb142SSascha Wildner #include <sys/bus.h> 363fabb142SSascha Wildner #include <sys/endian.h> 373fabb142SSascha Wildner #include <sys/kernel.h> 389db4b353SSepherosa Ziehau #include <sys/interrupt.h> 393fabb142SSascha Wildner #include <sys/malloc.h> 403fabb142SSascha Wildner #include <sys/mbuf.h> 413fabb142SSascha Wildner #include <sys/resource.h> 423fabb142SSascha Wildner #include <sys/rman.h> 433fabb142SSascha Wildner #include <sys/socket.h> 443fabb142SSascha Wildner #include <sys/sockio.h> 453fabb142SSascha Wildner #include <sys/systm.h> 463fabb142SSascha Wildner 473fabb142SSascha Wildner #include <bus/pci/pcidevs.h> 483fabb142SSascha Wildner #include <bus/pci/pcireg.h> 493fabb142SSascha Wildner #include <bus/pci/pcivar.h> 503fabb142SSascha Wildner 513fabb142SSascha Wildner #include <machine/clock.h> 523fabb142SSascha Wildner 533fabb142SSascha Wildner #include <net/bpf.h> 543fabb142SSascha Wildner #include <net/ethernet.h> 553fabb142SSascha Wildner #include <net/ifq_var.h> 563fabb142SSascha Wildner #include <net/if.h> 573fabb142SSascha Wildner #include <net/if_arp.h> 583fabb142SSascha Wildner #include <net/if_dl.h> 593fabb142SSascha Wildner #include <net/if_media.h> 603fabb142SSascha Wildner #include <net/if_var.h> 613fabb142SSascha Wildner 623fabb142SSascha Wildner #include <vm/pmap.h> 633fabb142SSascha Wildner #include <vm/vm.h> 643fabb142SSascha Wildner 653fabb142SSascha Wildner #include "if_slnreg.h" 663fabb142SSascha Wildner #include "if_slnvar.h" 673fabb142SSascha Wildner 683fabb142SSascha Wildner /* Default to using PIO access for netcard driver */ 693fabb142SSascha Wildner #define SL_USEIOSPACE 703fabb142SSascha Wildner 713fabb142SSascha Wildner #ifdef SLN_DEBUG 723fabb142SSascha Wildner #define PDEBUG(fmt, args...) kprintf("%s: " fmt "\n" , __func__ , ## args) 733fabb142SSascha Wildner #else 743fabb142SSascha Wildner #define PDEBUG(fmt, args...) 753fabb142SSascha Wildner #endif 763fabb142SSascha Wildner 773fabb142SSascha Wildner static const struct sln_dev { 783fabb142SSascha Wildner uint16_t vid; 793fabb142SSascha Wildner uint16_t did; 803fabb142SSascha Wildner const char *desc; 813fabb142SSascha Wildner } sln_devs[] = { 827db2822fSSascha Wildner {PCI_VENDOR_SILAN, PCI_PRODUCT_SILAN_SC92031, 833fabb142SSascha Wildner "Silan SC92031 Fast Ethernet" }, 843fabb142SSascha Wildner {PCI_VENDOR_SILAN, PCI_PRODUCT_SILAN_8139D, 853fabb142SSascha Wildner "Silan Rsltek 8139D Fast Ethernet" }, 863fabb142SSascha Wildner {0, 0, NULL} 873fabb142SSascha Wildner }; 883fabb142SSascha Wildner 893fabb142SSascha Wildner static int sln_probe(device_t); 903fabb142SSascha Wildner static int sln_attach(device_t); 913fabb142SSascha Wildner static int sln_detach(device_t); 923fabb142SSascha Wildner static int sln_shutdown(device_t); 933fabb142SSascha Wildner static int sln_suspend(device_t); 943fabb142SSascha Wildner static int sln_resume(device_t); 953fabb142SSascha Wildner 963fabb142SSascha Wildner static void sln_reset(struct sln_softc *); 973fabb142SSascha Wildner static void sln_init(void *); 983fabb142SSascha Wildner 99f0a26983SSepherosa Ziehau static void sln_tx(struct ifnet *, struct ifaltq_subque *); 1003fabb142SSascha Wildner static void sln_rx(struct sln_softc *); 1013fabb142SSascha Wildner static void sln_tx_intr(struct sln_softc *); 1023fabb142SSascha Wildner static void sln_media_intr(struct sln_softc *); 1033fabb142SSascha Wildner static void sln_interrupt(void *); 1043fabb142SSascha Wildner static int sln_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); 1053fabb142SSascha Wildner static void sln_stop(struct sln_softc *); 1063fabb142SSascha Wildner static void sln_watchdog(struct ifnet *); 1073fabb142SSascha Wildner 1083fabb142SSascha Wildner static int sln_media_upd(struct ifnet *); 1093fabb142SSascha Wildner 1103fabb142SSascha Wildner static void sln_media_stat(struct ifnet *, struct ifmediareq *); 1113fabb142SSascha Wildner static void sln_mii_cmd(struct sln_softc *, uint32_t, u_long *); 1123fabb142SSascha Wildner static void sln_media_cfg(struct sln_softc *); 1133fabb142SSascha Wildner static void sln_mac_cfg(struct sln_softc *); 1143fabb142SSascha Wildner static uint32_t sln_ether_crc32(caddr_t); 1153fabb142SSascha Wildner static void sln_set_multi(struct sln_softc *); 1163fabb142SSascha Wildner static void sln_init_tx(struct sln_softc *); 1173fabb142SSascha Wildner static void sln_tick(void *); 1183fabb142SSascha Wildner 1193fabb142SSascha Wildner #ifdef SL_USEIOSPACE 1203fabb142SSascha Wildner #define SL_RID SL_PCI_IOAD 1213fabb142SSascha Wildner #define SL_RES SYS_RES_IOPORT 1223fabb142SSascha Wildner #else 1233fabb142SSascha Wildner #define SL_RID SL_PCI_MEMAD 1243fabb142SSascha Wildner #define SL_RES SYS_RES_MEMORY 1253fabb142SSascha Wildner #endif 1263fabb142SSascha Wildner 1273fabb142SSascha Wildner static device_method_t sln_methods[] = { 1283fabb142SSascha Wildner DEVMETHOD(device_probe, sln_probe), 1293fabb142SSascha Wildner DEVMETHOD(device_attach, sln_attach), 1303fabb142SSascha Wildner DEVMETHOD(device_detach, sln_detach), 1313fabb142SSascha Wildner DEVMETHOD(device_shutdown, sln_shutdown), 1323fabb142SSascha Wildner DEVMETHOD(device_suspend, sln_suspend), 1333fabb142SSascha Wildner DEVMETHOD(device_resume, sln_resume), 1343fabb142SSascha Wildner 1353fabb142SSascha Wildner DEVMETHOD(bus_print_child, bus_generic_print_child), 1363fabb142SSascha Wildner DEVMETHOD(bus_driver_added, bus_generic_driver_added), 1373fabb142SSascha Wildner 1383fabb142SSascha Wildner {0, 0} 1393fabb142SSascha Wildner }; 1403fabb142SSascha Wildner 1413fabb142SSascha Wildner static driver_t sln_driver = { 1423fabb142SSascha Wildner "sln", 1433fabb142SSascha Wildner sln_methods, 1443fabb142SSascha Wildner sizeof(struct sln_softc) 1453fabb142SSascha Wildner }; 1463fabb142SSascha Wildner 1473fabb142SSascha Wildner static devclass_t sln_devclass; 1483fabb142SSascha Wildner 149aa2b9d05SSascha Wildner DRIVER_MODULE(sln, pci, sln_driver, sln_devclass, NULL, NULL); 1503fabb142SSascha Wildner 1513fabb142SSascha Wildner static int 1523fabb142SSascha Wildner sln_probe(struct device *dev) 1533fabb142SSascha Wildner { 1543fabb142SSascha Wildner const struct sln_dev *d; 1553fabb142SSascha Wildner uint16_t did, vid; 1563fabb142SSascha Wildner 1573fabb142SSascha Wildner vid = pci_get_vendor(dev); 1583fabb142SSascha Wildner did = pci_get_device(dev); 1593fabb142SSascha Wildner 1603fabb142SSascha Wildner for (d = sln_devs; d->desc != NULL; d++) { 1613fabb142SSascha Wildner if (vid == d->vid && did == d->did) { 1623fabb142SSascha Wildner device_set_desc(dev, d->desc); 1633fabb142SSascha Wildner return 0; 1643fabb142SSascha Wildner } 1653fabb142SSascha Wildner } 1663fabb142SSascha Wildner return ENXIO; 1673fabb142SSascha Wildner } 1683fabb142SSascha Wildner 1693fabb142SSascha Wildner /* the chip reset */ 1703fabb142SSascha Wildner static void 1713fabb142SSascha Wildner sln_reset(struct sln_softc *sc) 1723fabb142SSascha Wildner { 1733fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG0, SL_SOFT_RESET); 1743fabb142SSascha Wildner DELAY(200000); 1753fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG0, 0x0); 1763fabb142SSascha Wildner DELAY(10000); 1773fabb142SSascha Wildner } 1783fabb142SSascha Wildner 1793fabb142SSascha Wildner /* Attach the interface. Allocate softc structures */ 1803fabb142SSascha Wildner static int 1813fabb142SSascha Wildner sln_attach(device_t dev) 1823fabb142SSascha Wildner { 1833fabb142SSascha Wildner struct sln_softc *sc = device_get_softc(dev); 1843fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 1853fabb142SSascha Wildner unsigned char eaddr[ETHER_ADDR_LEN]; 1863fabb142SSascha Wildner int rid; 1873fabb142SSascha Wildner int error = 0; 1883fabb142SSascha Wildner 1893fabb142SSascha Wildner if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1903fabb142SSascha Wildner 1913fabb142SSascha Wildner /* TODO: power state change */ 1923fabb142SSascha Wildner 1933fabb142SSascha Wildner pci_enable_busmaster(dev); 1943fabb142SSascha Wildner 1953fabb142SSascha Wildner rid = SL_RID; 1963fabb142SSascha Wildner sc->sln_res = bus_alloc_resource_any(dev, SL_RES, &rid, RF_ACTIVE); 1973fabb142SSascha Wildner if (sc->sln_res == NULL) { 1983fabb142SSascha Wildner device_printf(dev, "couldn't map ports/memory\n"); 1993fabb142SSascha Wildner error = ENXIO; 2003fabb142SSascha Wildner goto fail; 2013fabb142SSascha Wildner } 2023fabb142SSascha Wildner sc->sln_bustag = rman_get_bustag(sc->sln_res); 2033fabb142SSascha Wildner sc->sln_bushandle = rman_get_bushandle(sc->sln_res); 2043fabb142SSascha Wildner 2053fabb142SSascha Wildner /* alloc pci irq */ 2063fabb142SSascha Wildner rid = 0; 2073fabb142SSascha Wildner sc->sln_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 2083fabb142SSascha Wildner RF_SHAREABLE | RF_ACTIVE); 2093fabb142SSascha Wildner if (sc->sln_irq == NULL) { 2103fabb142SSascha Wildner device_printf(dev, "couldn't map interrupt\n"); 2113fabb142SSascha Wildner bus_release_resource(dev, SL_RES, SL_RID, sc->sln_res); 2123fabb142SSascha Wildner error = ENXIO; 2133fabb142SSascha Wildner goto fail; 2143fabb142SSascha Wildner } 2153fabb142SSascha Wildner 2163fabb142SSascha Wildner /* Get MAC address */ 2173fabb142SSascha Wildner ((uint32_t *)(&eaddr))[0] = be32toh(SLN_READ_4(sc, SL_MAC_ADDR0)); 2183fabb142SSascha Wildner ((uint16_t *)(&eaddr))[2] = be16toh(SLN_READ_4(sc, SL_MAC_ADDR1)); 2193fabb142SSascha Wildner 2203fabb142SSascha Wildner /* alloc rx buffer space */ 2213fabb142SSascha Wildner sc->sln_bufdata.sln_rx_buf = contigmalloc(SL_RX_BUFLEN, 2223fabb142SSascha Wildner M_DEVBUF, M_WAITOK, 0, 0xffffffff, PAGE_SIZE, 0); 2233fabb142SSascha Wildner if (sc->sln_bufdata.sln_rx_buf == NULL) { 2243fabb142SSascha Wildner device_printf(dev, "no memory for rx buffers!\n"); 2253fabb142SSascha Wildner bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sln_irq); 2263fabb142SSascha Wildner bus_release_resource(dev, SL_RES, SL_RID, sc->sln_res); 2273fabb142SSascha Wildner error = ENXIO; 2283fabb142SSascha Wildner goto fail; 2293fabb142SSascha Wildner } 2303fabb142SSascha Wildner callout_init(&sc->sln_state); 2313fabb142SSascha Wildner 2323fabb142SSascha Wildner ifp->if_softc = sc; 2333fabb142SSascha Wildner ifp->if_mtu = ETHERMTU; 2343fabb142SSascha Wildner ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 2353fabb142SSascha Wildner ifp->if_init = sln_init; 2363fabb142SSascha Wildner ifp->if_start = sln_tx; 2373fabb142SSascha Wildner ifp->if_ioctl = sln_ioctl; 2383fabb142SSascha Wildner ifp->if_watchdog = sln_watchdog; 2393fabb142SSascha Wildner ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); 2403fabb142SSascha Wildner ifq_set_ready(&ifp->if_snd); 2413fabb142SSascha Wildner 2423fabb142SSascha Wildner /* initial media */ 2433fabb142SSascha Wildner ifmedia_init(&sc->ifmedia, 0, sln_media_upd, sln_media_stat); 2443fabb142SSascha Wildner 2453fabb142SSascha Wildner /* supported media types */ 2463fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL); 2473fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T, 0, NULL); 2483fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX, 0, NULL); 2493fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); 2503fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL); 2513fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX, 0, NULL); 2523fabb142SSascha Wildner ifmedia_add(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); 2533fabb142SSascha Wildner 2543fabb142SSascha Wildner /* Choose a default media. */ 2553fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_AUTO); 2563fabb142SSascha Wildner 2573fabb142SSascha Wildner ether_ifattach(ifp, eaddr, NULL); 2583fabb142SSascha Wildner 2593fabb142SSascha Wildner error = bus_setup_intr(dev, sc->sln_irq, INTR_MPSAFE, sln_interrupt, sc, 2603fabb142SSascha Wildner &sc->sln_intrhand, ifp->if_serializer); 2613fabb142SSascha Wildner if (error) { 2623fabb142SSascha Wildner bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sln_irq); 2633fabb142SSascha Wildner bus_release_resource(dev, SL_RES, SL_RID, sc->sln_res); 2643fabb142SSascha Wildner ether_ifdetach(ifp); 2653fabb142SSascha Wildner device_printf(dev, "couldn't set up irq\n"); 2663fabb142SSascha Wildner goto fail; 2673fabb142SSascha Wildner } 2689db4b353SSepherosa Ziehau 269dfd3b18bSSepherosa Ziehau ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->sln_irq)); 2709db4b353SSepherosa Ziehau 2713fabb142SSascha Wildner return 0; 2723fabb142SSascha Wildner fail: 2733fabb142SSascha Wildner return error; 2743fabb142SSascha Wildner } 2753fabb142SSascha Wildner 2763fabb142SSascha Wildner /* Stop the adapter and free any mbufs allocated to the RX and TX buffers */ 2773fabb142SSascha Wildner static void 2783fabb142SSascha Wildner sln_stop(struct sln_softc *sc) 2793fabb142SSascha Wildner { 2803fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 2813fabb142SSascha Wildner int i; 2823fabb142SSascha Wildner 2833fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 2843fabb142SSascha Wildner 2853fabb142SSascha Wildner ifp->if_timer = 0; 2863fabb142SSascha Wildner callout_stop(&sc->sln_state); 2873fabb142SSascha Wildner 2883fabb142SSascha Wildner /* disable Tx/Rx */ 2893fabb142SSascha Wildner sc->txcfg &= ~SL_TXCFG_EN; 2903fabb142SSascha Wildner sc->rxcfg &= ~SL_RXCFG_EN; 2913fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 2923fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 2933fabb142SSascha Wildner 2943fabb142SSascha Wildner /* Clear interrupt */ 2953fabb142SSascha Wildner SLN_WRITE_4(sc, SL_INT_MASK, 0); 296*3d13a951SSepherosa Ziehau SLN_READ_4(sc, SL_INT_STATUS); 2973fabb142SSascha Wildner 2983fabb142SSascha Wildner /* Free the TX list buffers */ 2993fabb142SSascha Wildner for (i = 0; i < SL_TXD_CNT; i++) { 3003fabb142SSascha Wildner if (sc->sln_bufdata.sln_tx_buf[i] != NULL) { 3013fabb142SSascha Wildner m_freem(sc->sln_bufdata.sln_tx_buf[i]); 3023fabb142SSascha Wildner sc->sln_bufdata.sln_tx_buf[i] = NULL; 3033fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TSAD0 + i * 4, 0); 3043fabb142SSascha Wildner } 3053fabb142SSascha Wildner } 3063fabb142SSascha Wildner 3079ed293e0SSepherosa Ziehau ifp->if_flags &= ~IFF_RUNNING; 3089ed293e0SSepherosa Ziehau ifq_clr_oactive(&ifp->if_snd); 3093fabb142SSascha Wildner } 3103fabb142SSascha Wildner 3113fabb142SSascha Wildner static int 3123fabb142SSascha Wildner sln_detach(device_t dev) 3133fabb142SSascha Wildner { 3143fabb142SSascha Wildner struct sln_softc *sc = device_get_softc(dev); 3153fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 3163fabb142SSascha Wildner 3173fabb142SSascha Wildner lwkt_serialize_enter(ifp->if_serializer); 3183fabb142SSascha Wildner sln_stop(sc); 3193fabb142SSascha Wildner bus_teardown_intr(dev, sc->sln_irq, sc->sln_intrhand); 3203fabb142SSascha Wildner lwkt_serialize_exit(ifp->if_serializer); 3213fabb142SSascha Wildner 3223fabb142SSascha Wildner ether_ifdetach(ifp); 3233fabb142SSascha Wildner 3243fabb142SSascha Wildner bus_generic_detach(dev); 3253fabb142SSascha Wildner 3263fabb142SSascha Wildner bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sln_irq); 3273fabb142SSascha Wildner bus_release_resource(dev, SL_RES, SL_RID, sc->sln_res); 3283fabb142SSascha Wildner 3293fabb142SSascha Wildner contigfree(sc->sln_bufdata.sln_rx_buf, SL_RX_BUFLEN, M_DEVBUF); 3303fabb142SSascha Wildner 3313fabb142SSascha Wildner return 0; 3323fabb142SSascha Wildner } 3333fabb142SSascha Wildner 3343fabb142SSascha Wildner static int 3353fabb142SSascha Wildner sln_media_upd(struct ifnet *ifp) 3363fabb142SSascha Wildner { 3373fabb142SSascha Wildner struct sln_softc *sc = ifp->if_softc; 3383fabb142SSascha Wildner struct ifmedia *ifm = &sc->ifmedia; 3393fabb142SSascha Wildner 3403fabb142SSascha Wildner if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 3413fabb142SSascha Wildner return EINVAL; 3423fabb142SSascha Wildner 3433fabb142SSascha Wildner if (ifp->if_flags & IFF_UP) 3443fabb142SSascha Wildner sln_init(sc); 3453fabb142SSascha Wildner 3463fabb142SSascha Wildner return 0; 3473fabb142SSascha Wildner } 3483fabb142SSascha Wildner 3493fabb142SSascha Wildner static void 3503fabb142SSascha Wildner sln_media_stat(struct ifnet *ifp, struct ifmediareq *ifmr) 3513fabb142SSascha Wildner { 3523fabb142SSascha Wildner struct sln_softc *sc = ifp->if_softc; 3533fabb142SSascha Wildner u_long phys[2]; 3543fabb142SSascha Wildner uint32_t temp; 3553fabb142SSascha Wildner 3563fabb142SSascha Wildner ifmr->ifm_status = IFM_AVALID; 3573fabb142SSascha Wildner ifmr->ifm_active = IFM_ETHER; 3583fabb142SSascha Wildner 3593fabb142SSascha Wildner phys[0] = SL_MII_STAT; 3603fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_READ, phys); 3613fabb142SSascha Wildner 3623fabb142SSascha Wildner if (phys[1] & SL_MIISTAT_LINK) 3633fabb142SSascha Wildner ifmr->ifm_status |= IFM_ACTIVE; 3643fabb142SSascha Wildner 3653fabb142SSascha Wildner temp = SLN_READ_4(sc, SL_PHY_CTRL); 3663fabb142SSascha Wildner 3673fabb142SSascha Wildner if ((temp & (SL_PHYCTL_DUX | SL_PHYCTL_SPD100 | SL_PHYCTL_SPD10)) == 0x60800000) 3683fabb142SSascha Wildner ifmr->ifm_active |= IFM_AUTO; 3693fabb142SSascha Wildner else if ((temp & (SL_PHYCTL_DUX | SL_PHYCTL_SPD100)) == 0x40800000) 3703fabb142SSascha Wildner ifmr->ifm_active |= IFM_100_TX | IFM_FDX; 3713fabb142SSascha Wildner else if ((temp & SL_PHYCTL_SPD100) == 0x40000000) 3723fabb142SSascha Wildner ifmr->ifm_active |= IFM_100_TX | IFM_HDX; 3733fabb142SSascha Wildner else if ((temp & (SL_PHYCTL_DUX | SL_PHYCTL_SPD10)) == 0x20800000) 3743fabb142SSascha Wildner ifmr->ifm_active |= IFM_10_T | IFM_FDX; 3753fabb142SSascha Wildner else if ((temp & SL_PHYCTL_SPD10) == 0x20000000) 3763fabb142SSascha Wildner ifmr->ifm_active |= IFM_10_T | IFM_HDX; 3773fabb142SSascha Wildner 3783fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_SCAN, phys); 3793fabb142SSascha Wildner } 3803fabb142SSascha Wildner 3813fabb142SSascha Wildner /* command selected in MII command register */ 3823fabb142SSascha Wildner static void 3833fabb142SSascha Wildner sln_mii_cmd(struct sln_softc *sc, uint32_t cmd, u_long *phys) 3843fabb142SSascha Wildner { 3853fabb142SSascha Wildner uint32_t mii_status; 3863fabb142SSascha Wildner 3873fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD0, SL_MII0_DIVEDER); 3883fabb142SSascha Wildner 3893fabb142SSascha Wildner do { 3903fabb142SSascha Wildner mii_status = 0; 3913fabb142SSascha Wildner DELAY(10); 3923fabb142SSascha Wildner mii_status = SLN_READ_4(sc, SL_MII_STATUS); 3933fabb142SSascha Wildner } while (mii_status & SL_MIISTAT_BUSY); 3943fabb142SSascha Wildner 3953fabb142SSascha Wildner switch (cmd) { 3963fabb142SSascha Wildner case SL_MII0_SCAN: 3973fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD1, 0x1 << 6); 3983fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD0, SL_MII0_DIVEDER | SL_MII0_SCAN); 3993fabb142SSascha Wildner break; 4003fabb142SSascha Wildner 4013fabb142SSascha Wildner case SL_MII0_READ: 4023fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD1, phys[0] << 6); 4033fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD0, SL_MII0_DIVEDER | SL_MII0_READ); 4043fabb142SSascha Wildner break; 4053fabb142SSascha Wildner 4063fabb142SSascha Wildner default: /* WRITE */ 4073fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD1, phys[0] << 6 | phys[1] << 11); 4083fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MII_CMD0, SL_MII0_DIVEDER | SL_MII0_WRITE); 4093fabb142SSascha Wildner break; 4103fabb142SSascha Wildner } 4113fabb142SSascha Wildner 4123fabb142SSascha Wildner do { 4133fabb142SSascha Wildner DELAY(10); 4143fabb142SSascha Wildner mii_status = SLN_READ_4(sc, SL_MII_STATUS); 4153fabb142SSascha Wildner } while (mii_status & SL_MIISTAT_BUSY); 4163fabb142SSascha Wildner 4173fabb142SSascha Wildner if (SL_MII0_READ == cmd) 4183fabb142SSascha Wildner phys[1] = (mii_status >> 13) & 0xffff; 4193fabb142SSascha Wildner } 4203fabb142SSascha Wildner 4213fabb142SSascha Wildner /* Set media speed and duplex mode */ 4223fabb142SSascha Wildner static void 4233fabb142SSascha Wildner sln_media_cfg(struct sln_softc *sc) 4243fabb142SSascha Wildner { 4253fabb142SSascha Wildner u_long phys[2]; 4263fabb142SSascha Wildner uint32_t mediatype; 4273fabb142SSascha Wildner uint32_t temp; 4283fabb142SSascha Wildner 4293fabb142SSascha Wildner mediatype = (&sc->ifmedia)->ifm_cur->ifm_media; 4303fabb142SSascha Wildner 4313fabb142SSascha Wildner temp = SLN_READ_4(sc, SL_PHY_CTRL); 4323fabb142SSascha Wildner temp &= ~(SL_PHYCTL_DUX | SL_PHYCTL_SPD100 | SL_PHYCTL_SPD10); 4333fabb142SSascha Wildner temp |= (SL_PHYCTL_ANE | SL_PHYCTL_RESET); 4343fabb142SSascha Wildner 4353fabb142SSascha Wildner /************************************************/ 4363fabb142SSascha Wildner /* currently set media word by selected media */ 4373fabb142SSascha Wildner /* */ 4383fabb142SSascha Wildner /* IFM_ETHER = 0x00000020 */ 4393fabb142SSascha Wildner /* IFM_AUTO=0, IFM_10_T=3, IFM_100_TX=6 */ 4403fabb142SSascha Wildner /* IFM_FDX=0x00100000 IFM_HDX=0x00200000 */ 4413fabb142SSascha Wildner /************************************************/ 4423fabb142SSascha Wildner switch (mediatype) { 4433fabb142SSascha Wildner case 0x00000020: 4443fabb142SSascha Wildner PDEBUG(" autoselet supported\n"); 4453fabb142SSascha Wildner temp |= (SL_PHYCTL_DUX | SL_PHYCTL_SPD100 | SL_PHYCTL_SPD10); 4463fabb142SSascha Wildner sc->ifmedia.ifm_media = IFM_ETHER | IFM_AUTO; 4473fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_AUTO); 4483fabb142SSascha Wildner break; 4493fabb142SSascha Wildner case 0x23: 4503fabb142SSascha Wildner case 0x00200023: 4513fabb142SSascha Wildner PDEBUG(" 10Mbps half_duplex supported\n"); 4523fabb142SSascha Wildner temp |= SL_PHYCTL_SPD10; 4533fabb142SSascha Wildner sc->ifmedia.ifm_media = IFM_ETHER | IFM_10_T | IFM_HDX; 4543fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_HDX); 4553fabb142SSascha Wildner break; 4563fabb142SSascha Wildner 4573fabb142SSascha Wildner case 0x00100023: 4583fabb142SSascha Wildner PDEBUG("10Mbps full_duplex supported\n"); 4593fabb142SSascha Wildner temp |= (SL_PHYCTL_SPD10 | SL_PHYCTL_DUX); 4603fabb142SSascha Wildner sc->ifmedia.ifm_media = IFM_ETHER | IFM_10_T | IFM_FDX; 4613fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX); 4623fabb142SSascha Wildner break; 4633fabb142SSascha Wildner 4643fabb142SSascha Wildner case 0x26: 4653fabb142SSascha Wildner case 0x00200026: 4663fabb142SSascha Wildner PDEBUG("100Mbps half_duplex supported\n"); 4673fabb142SSascha Wildner temp |= SL_PHYCTL_SPD100; 4683fabb142SSascha Wildner sc->ifmedia.ifm_media = IFM_ETHER | IFM_100_TX | IFM_HDX; 4693fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_HDX); 4703fabb142SSascha Wildner break; 4713fabb142SSascha Wildner 4723fabb142SSascha Wildner case 0x00100026: 4733fabb142SSascha Wildner PDEBUG("100Mbps full_duplex supported\n"); 4743fabb142SSascha Wildner temp |= (SL_PHYCTL_SPD100 | SL_PHYCTL_DUX); 4753fabb142SSascha Wildner sc->ifmedia.ifm_media = IFM_ETHER | IFM_100_TX | IFM_FDX; 4763fabb142SSascha Wildner ifmedia_set(&sc->ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX); 4773fabb142SSascha Wildner break; 4783fabb142SSascha Wildner 4793fabb142SSascha Wildner default: 4803fabb142SSascha Wildner break; 4813fabb142SSascha Wildner } 4823fabb142SSascha Wildner 4833fabb142SSascha Wildner SLN_WRITE_4(sc, SL_PHY_CTRL, temp); 4843fabb142SSascha Wildner 4853fabb142SSascha Wildner DELAY(10000); 4863fabb142SSascha Wildner temp &= ~SL_PHYCTL_RESET; 4873fabb142SSascha Wildner SLN_WRITE_4(sc, SL_PHY_CTRL, temp); 4883fabb142SSascha Wildner 4893fabb142SSascha Wildner DELAY(1000); 4903fabb142SSascha Wildner phys[0] = SL_MII_JAB; 4913fabb142SSascha Wildner phys[1] = SL_PHY_16_JAB_ENB | SL_PHY_16_PORT_ENB; 4923fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_WRITE, phys); 4933fabb142SSascha Wildner 4943fabb142SSascha Wildner sc->connect = 0; 4953fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_SCAN, phys); 4963fabb142SSascha Wildner } 4973fabb142SSascha Wildner 4983fabb142SSascha Wildner static void 4993fabb142SSascha Wildner sln_mac_cfg(struct sln_softc *sc) 5003fabb142SSascha Wildner { 5013fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 5023fabb142SSascha Wildner u_long flowcfg = 0; 5033fabb142SSascha Wildner 5043fabb142SSascha Wildner /* Set the initial TX/RX/Flow Control configuration */ 5053fabb142SSascha Wildner sc->rxcfg = SL_RXCFG_LOW_THRESHOLD | SL_RXCFG_HIGH_THRESHOLD; 5063fabb142SSascha Wildner sc->txcfg = TX_CFG_DEFAULT; 5073fabb142SSascha Wildner 5083fabb142SSascha Wildner if (sc->txenablepad) 5093fabb142SSascha Wildner sc->txcfg |= 0x20000000; 5103fabb142SSascha Wildner 5113fabb142SSascha Wildner if (sc->media_speed == IFM_10_T) 5123fabb142SSascha Wildner sc->txcfg |= SL_TXCFG_DATARATE; 5133fabb142SSascha Wildner 5143fabb142SSascha Wildner if (sc->media_duplex == IFM_FDX) { 5153fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_FULLDX; 5163fabb142SSascha Wildner sc->txcfg |= SL_TXCFG_FULLDX; 5173fabb142SSascha Wildner flowcfg = SL_FLOWCTL_FULLDX | SL_FLOWCTL_EN; 5183fabb142SSascha Wildner } else { 5193fabb142SSascha Wildner sc->rxcfg &= ~SL_RXCFG_FULLDX; 5203fabb142SSascha Wildner sc->txcfg &= ~SL_TXCFG_FULLDX; 5213fabb142SSascha Wildner } 5223fabb142SSascha Wildner 5233fabb142SSascha Wildner /* if promiscuous mode, set the allframes bit. */ 5243fabb142SSascha Wildner if (ifp->if_flags & IFF_PROMISC) 5253fabb142SSascha Wildner sc->rxcfg |= (SL_RXCFG_EN | SL_RXCFG_RCV_SMALL | SL_RXCFG_RCV_HUGE | SL_RXCFG_RCV_ERR | SL_RXCFG_RCV_BROAD | SL_RXCFG_RCV_MULTI | SL_RXCFG_RCV_ALL); 5263fabb142SSascha Wildner else 5273fabb142SSascha Wildner sc->rxcfg &= ~(SL_RXCFG_EN | SL_RXCFG_RCV_SMALL | SL_RXCFG_RCV_HUGE | SL_RXCFG_RCV_ERR | SL_RXCFG_RCV_BROAD | SL_RXCFG_RCV_MULTI | SL_RXCFG_RCV_ALL); 5283fabb142SSascha Wildner 5293fabb142SSascha Wildner /* Set capture broadcast bit to capture broadcast frames */ 5303fabb142SSascha Wildner if (ifp->if_flags & IFF_BROADCAST) 5313fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN | SL_RXCFG_RCV_BROAD; 5323fabb142SSascha Wildner else 5333fabb142SSascha Wildner sc->rxcfg &= ~(SL_RXCFG_EN | SL_RXCFG_RCV_BROAD); 5343fabb142SSascha Wildner 5353fabb142SSascha Wildner /* Program the multicast filter, if necessary */ 5363fabb142SSascha Wildner sln_set_multi(sc); 5373fabb142SSascha Wildner 5383fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 5393fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 5403fabb142SSascha Wildner SLN_WRITE_4(sc, SL_FLOW_CTRL, flowcfg); 5413fabb142SSascha Wildner } 5423fabb142SSascha Wildner 5433fabb142SSascha Wildner static u_char shade_map[] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 5443fabb142SSascha Wildner 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; 5453fabb142SSascha Wildner 5463fabb142SSascha Wildner /* Calculate CRC32 of a multicast group address */ 5473fabb142SSascha Wildner static uint32_t 5483fabb142SSascha Wildner sln_ether_crc32(caddr_t addr) 5493fabb142SSascha Wildner { 5503fabb142SSascha Wildner uint32_t crc, crcr; 5513fabb142SSascha Wildner int i, j; 5523fabb142SSascha Wildner unsigned char data = 0; 5533fabb142SSascha Wildner /* Compute CRC for the address value. */ 5543fabb142SSascha Wildner 5553fabb142SSascha Wildner crc = 0xFFFFFFFF; /* initial value */ 5563fabb142SSascha Wildner 5573fabb142SSascha Wildner for (i = ETHER_ADDR_LEN; i > 0; i--) { 5583fabb142SSascha Wildner data = *addr++; 5593fabb142SSascha Wildner 5603fabb142SSascha Wildner for (j = 0; j < 8; j++) { 5613fabb142SSascha Wildner if (((data & 0x1) ^ (crc & 0x1)) != 0) { 5623fabb142SSascha Wildner crc >>= 1; 5633fabb142SSascha Wildner crc ^= 0xEDB88320; 5643fabb142SSascha Wildner } else { 5653fabb142SSascha Wildner crc >>= 1; 5663fabb142SSascha Wildner } 5673fabb142SSascha Wildner data >>= 1; 5683fabb142SSascha Wildner } 5693fabb142SSascha Wildner } 5703fabb142SSascha Wildner 5713fabb142SSascha Wildner crcr = shade_map[crc >> 28]; 5723fabb142SSascha Wildner crcr |= (shade_map[(crc >> 24) & 0xf] << 4); 5733fabb142SSascha Wildner crcr |= (shade_map[(crc >> 20) & 0xf] << 8); 5743fabb142SSascha Wildner crcr |= (shade_map[(crc >> 16) & 0xf] << 12); 5753fabb142SSascha Wildner crcr |= (shade_map[(crc >> 12) & 0xf] << 16); 5763fabb142SSascha Wildner crcr |= (shade_map[(crc >> 8) & 0xf] << 20); 5773fabb142SSascha Wildner crcr |= (shade_map[(crc >> 4) & 0xf] << 24); 5783fabb142SSascha Wildner crcr |= (shade_map[crc & 0xf] << 28); 5793fabb142SSascha Wildner 5803fabb142SSascha Wildner return crcr; 5813fabb142SSascha Wildner } 5823fabb142SSascha Wildner 5833fabb142SSascha Wildner /* Program the 64-bit multicast hash filter */ 5843fabb142SSascha Wildner static void 5853fabb142SSascha Wildner sln_set_multi(struct sln_softc *sc) 5863fabb142SSascha Wildner { 5873fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 5883fabb142SSascha Wildner uint32_t crc = 0; 5893fabb142SSascha Wildner uint32_t mc_g[2] = {0, 0}; 5903fabb142SSascha Wildner struct ifmultiaddr *ifma; 591441d34b2SSascha Wildner int j; 5923fabb142SSascha Wildner 5933fabb142SSascha Wildner if (ifp->if_flags & IFF_PROMISC) { 5943fabb142SSascha Wildner kprintf("Promisc mode is enabled\n"); 5953fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN | SL_RXCFG_RCV_MULTI; 5963fabb142SSascha Wildner mc_g[0] = mc_g[1] = 0xFFFFFFFF; 5973fabb142SSascha Wildner } else if (ifp->if_flags & IFF_ALLMULTI) { 5983fabb142SSascha Wildner kprintf("Allmulti mode is enabled\n"); 5993fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN | SL_RXCFG_RCV_MULTI; 6003fabb142SSascha Wildner mc_g[0] = mc_g[1] = 0xFFFFFFFF; 6013fabb142SSascha Wildner } else if (ifp->if_flags & IFF_MULTICAST) { 6023fabb142SSascha Wildner kprintf("Multicast mode is enabled\n"); 6033fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN | SL_RXCFG_RCV_MULTI; 6043fabb142SSascha Wildner 6053fabb142SSascha Wildner /* first, zero all the existing hash bits */ 6063fabb142SSascha Wildner mc_g[0] = mc_g[1] = 0; 6073fabb142SSascha Wildner 608441d34b2SSascha Wildner TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 6093fabb142SSascha Wildner j = 0; 6103fabb142SSascha Wildner 6113fabb142SSascha Wildner if ((ifma->ifma_addr->sa_family) != AF_LINK) 6123fabb142SSascha Wildner continue; 6133fabb142SSascha Wildner 6143fabb142SSascha Wildner crc = ~sln_ether_crc32(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 6153fabb142SSascha Wildner crc >>= 24; 6163fabb142SSascha Wildner 6173fabb142SSascha Wildner if (crc & 0x1) 6183fabb142SSascha Wildner j |= 0x2; 6193fabb142SSascha Wildner if (crc & 0x2) 6203fabb142SSascha Wildner j |= 0x1; 6213fabb142SSascha Wildner if (crc & 0x10) 6223fabb142SSascha Wildner j |= 0x20; 6233fabb142SSascha Wildner if (crc & 0x20) 6243fabb142SSascha Wildner j |= 0x10; 6253fabb142SSascha Wildner if (crc & 0x40) 6263fabb142SSascha Wildner j |= 0x8; 6273fabb142SSascha Wildner if (crc & 0x80) 6283fabb142SSascha Wildner j |= 0x4; 6293fabb142SSascha Wildner 6303fabb142SSascha Wildner if (j > 31) 6313fabb142SSascha Wildner mc_g[0] |= (0x1 << (j - 32)); 6323fabb142SSascha Wildner else 6333fabb142SSascha Wildner mc_g[1] |= (0x1 << j); 6343fabb142SSascha Wildner } 6353fabb142SSascha Wildner } else { 6363fabb142SSascha Wildner sc->rxcfg &= ~(SL_RXCFG_EN | SL_RXCFG_RCV_MULTI); 6373fabb142SSascha Wildner } 6383fabb142SSascha Wildner 6393fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 6403fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MULTI_GROUP0, mc_g[0]); 6413fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MULTI_GROUP1, mc_g[1]); 6423fabb142SSascha Wildner } 6433fabb142SSascha Wildner 6443fabb142SSascha Wildner /* Initialize the TX/Rx descriptors */ 6453fabb142SSascha Wildner static void 6463fabb142SSascha Wildner sln_init_tx(struct sln_softc *sc) 6473fabb142SSascha Wildner { 6483fabb142SSascha Wildner int i; 6493fabb142SSascha Wildner 6503fabb142SSascha Wildner sc->sln_bufdata.cur_tx = 0; 6513fabb142SSascha Wildner sc->sln_bufdata.dirty_tx = 0; 6523fabb142SSascha Wildner 6533fabb142SSascha Wildner for (i = 0; i < SL_TXD_CNT; i++) { 6543fabb142SSascha Wildner sc->sln_bufdata.sln_tx_buf[i] = NULL; 6553fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TSAD0 + (i * 4), 0); 6563fabb142SSascha Wildner } 6573fabb142SSascha Wildner } 6583fabb142SSascha Wildner 6593fabb142SSascha Wildner /* Software & Hardware Initialize */ 6603fabb142SSascha Wildner static void 6613fabb142SSascha Wildner sln_init(void *x) 6623fabb142SSascha Wildner { 6633fabb142SSascha Wildner struct sln_softc *sc = x; 6643fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 6653fabb142SSascha Wildner 6663fabb142SSascha Wildner PDEBUG("sln_init\n"); 6673fabb142SSascha Wildner 6683fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 6693fabb142SSascha Wildner 6703fabb142SSascha Wildner sln_stop(sc); 6713fabb142SSascha Wildner 6723fabb142SSascha Wildner /* soft reset the chip */ 6733fabb142SSascha Wildner sln_reset(sc); 6743fabb142SSascha Wildner 6753fabb142SSascha Wildner /* disable interrupt */ 6763fabb142SSascha Wildner SLN_WRITE_4(sc, SL_INT_MASK, 0); 6773fabb142SSascha Wildner 6783fabb142SSascha Wildner /* SLN_WRITE_4(sc, SL_MII_CMD0, SL_MII0_DIVEDER); */ 6793fabb142SSascha Wildner 6803fabb142SSascha Wildner /* clear multicast address */ 6813fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MULTI_GROUP0, 0); 6823fabb142SSascha Wildner SLN_WRITE_4(sc, SL_MULTI_GROUP1, 0); 6833fabb142SSascha Wildner 6843fabb142SSascha Wildner /* Init the RX buffer start address register. */ 6853fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RBSA, vtophys(sc->sln_bufdata.sln_rx_buf)); 6863fabb142SSascha Wildner sc->sln_bufdata.dirty_rx = vtophys(sc->sln_bufdata.sln_rx_buf); 6873fabb142SSascha Wildner 6883fabb142SSascha Wildner /* Init TX descriptors. */ 6893fabb142SSascha Wildner sln_init_tx(sc); 6903fabb142SSascha Wildner 6913fabb142SSascha Wildner /* configure RX buffer size */ 6923fabb142SSascha Wildner if (sc->tx_early_ctrl && sc->rx_early_ctrl) 6933fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG1, SL_EARLY_RX | SL_EARLY_TX | SL_RXBUF_64 | SL_RXFIFO_1024BYTES); 6943fabb142SSascha Wildner else if (sc->tx_early_ctrl) 6953fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG1, SL_EARLY_TX | SL_RXBUF_64); 6963fabb142SSascha Wildner else if (sc->rx_early_ctrl) 6973fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG1, SL_EARLY_RX | SL_RXBUF_64 | SL_RXFIFO_1024BYTES); 6983fabb142SSascha Wildner else 6993fabb142SSascha Wildner SLN_WRITE_4(sc, SL_CFG1, SL_RXBUF_64); 7003fabb142SSascha Wildner 7013fabb142SSascha Wildner /* MII media configuration */ 7023fabb142SSascha Wildner sln_media_cfg(sc); 7033fabb142SSascha Wildner 7043fabb142SSascha Wildner if (sc->connect) { 7053fabb142SSascha Wildner /* Enable transmit and receive */ 7063fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN; 7073fabb142SSascha Wildner sc->txcfg |= SL_TXCFG_EN; 7083fabb142SSascha Wildner } else { 7093fabb142SSascha Wildner sc->rxcfg &= ~SL_RXCFG_EN; 7103fabb142SSascha Wildner sc->txcfg &= ~SL_TXCFG_EN; 7113fabb142SSascha Wildner } 7123fabb142SSascha Wildner 7133fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 7143fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 7153fabb142SSascha Wildner 7163fabb142SSascha Wildner /* Enable interrupts */ 7173fabb142SSascha Wildner SLN_WRITE_4(sc, SL_INT_MASK, SL_INRTS); 7183fabb142SSascha Wildner 7193fabb142SSascha Wildner sc->suspended = 0; 7203fabb142SSascha Wildner 7213fabb142SSascha Wildner ifp->if_flags |= IFF_RUNNING; 7229ed293e0SSepherosa Ziehau ifq_clr_oactive(&ifp->if_snd); 7233fabb142SSascha Wildner 7243fabb142SSascha Wildner callout_reset(&sc->sln_state, hz, sln_tick, sc); 7253fabb142SSascha Wildner } 7263fabb142SSascha Wildner 7273fabb142SSascha Wildner /* Transmit Packet */ 7283fabb142SSascha Wildner static void 729f0a26983SSepherosa Ziehau sln_tx(struct ifnet *ifp, struct ifaltq_subque *ifsq) 7303fabb142SSascha Wildner { 7313fabb142SSascha Wildner struct sln_softc *sc = ifp->if_softc; 7323fabb142SSascha Wildner struct mbuf *m_head = NULL; 7333fabb142SSascha Wildner struct mbuf *m_new = NULL; 7343fabb142SSascha Wildner int entry; 7353fabb142SSascha Wildner 736f0a26983SSepherosa Ziehau ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); 7373fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 7383fabb142SSascha Wildner 7399db4b353SSepherosa Ziehau if (!sc->connect) { 7409db4b353SSepherosa Ziehau ifq_purge(&ifp->if_snd); 7419db4b353SSepherosa Ziehau return; 7429db4b353SSepherosa Ziehau } 7439db4b353SSepherosa Ziehau 7449ed293e0SSepherosa Ziehau if ((ifp->if_flags & IFF_RUNNING) == 0 || ifq_is_oactive(&ifp->if_snd)) 7453fabb142SSascha Wildner return; 7463fabb142SSascha Wildner 7473fabb142SSascha Wildner while (SL_CUR_TXBUF(sc) == NULL) { /* SL_CUR_TXBUF(x) = x->sln_bufdata.sln_tx_buf[x->sln_bufdata.cur_tx] */ 7483fabb142SSascha Wildner entry = sc->sln_bufdata.cur_tx; 7493fabb142SSascha Wildner 7503fabb142SSascha Wildner m_head = ifq_dequeue(&ifp->if_snd, NULL); 7513fabb142SSascha Wildner if (m_head == NULL) 7523fabb142SSascha Wildner break; 7533fabb142SSascha Wildner 7543fabb142SSascha Wildner MGETHDR(m_new, MB_DONTWAIT, MT_DATA); 7553fabb142SSascha Wildner if (m_new == NULL) { 7563fabb142SSascha Wildner if_printf(ifp, "no memory for tx descriptor"); 7573fabb142SSascha Wildner m_freem(m_head); 7583fabb142SSascha Wildner break; 7593fabb142SSascha Wildner } 7603fabb142SSascha Wildner if ((m_head->m_pkthdr.len > MHLEN) || (60 > MHLEN)) { 7613fabb142SSascha Wildner MCLGET(m_new, MB_DONTWAIT); 7623fabb142SSascha Wildner if (!(m_new->m_flags & M_EXT)) { 7633fabb142SSascha Wildner m_freem(m_new); 7643fabb142SSascha Wildner m_freem(m_head); 7653fabb142SSascha Wildner if_printf(ifp, "no memory for tx descriptor"); 7663fabb142SSascha Wildner break; 7673fabb142SSascha Wildner } 7683fabb142SSascha Wildner } 7693fabb142SSascha Wildner m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); 7703fabb142SSascha Wildner m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 7713fabb142SSascha Wildner m_freem(m_head); 7723fabb142SSascha Wildner m_head = m_new; 7733fabb142SSascha Wildner SL_CUR_TXBUF(sc) = m_head; 7743fabb142SSascha Wildner 7753fabb142SSascha Wildner /* 7763fabb142SSascha Wildner * if there's a BPF listener, bounce a copy of this frame to 7773fabb142SSascha Wildner * him 7783fabb142SSascha Wildner */ 7793fabb142SSascha Wildner BPF_MTAP(ifp, SL_CUR_TXBUF(sc)); 7803fabb142SSascha Wildner 7813fabb142SSascha Wildner /* Transmit the frame */ 7823fabb142SSascha Wildner SLN_WRITE_4(sc, ((entry * 4) + SL_TSAD0), 7833fabb142SSascha Wildner vtophys(mtod(SL_CUR_TXBUF(sc), caddr_t))); 7843fabb142SSascha Wildner 7853fabb142SSascha Wildner /* calculate length of the frame */ 7863fabb142SSascha Wildner if ((SL_CUR_TXBUF(sc)->m_pkthdr.len < 60) && (!sc->txenablepad)) { 7873fabb142SSascha Wildner memset(mtod(m_head, char *)+m_head->m_pkthdr.len, 0x20, 60 - m_head->m_pkthdr.len); 7883fabb142SSascha Wildner SLN_WRITE_4(sc, (entry * 4) + SL_TSD0, 60); 7893fabb142SSascha Wildner } else if (SL_CUR_TXBUF(sc)->m_pkthdr.len < 100) 7903fabb142SSascha Wildner SLN_WRITE_4(sc, (entry * 4) + SL_TSD0, SL_CUR_TXBUF(sc)->m_pkthdr.len); 7913fabb142SSascha Wildner else if (SL_CUR_TXBUF(sc)->m_pkthdr.len < 300) 7923fabb142SSascha Wildner SLN_WRITE_4(sc, (entry * 4) + SL_TSD0, 0x30000 | SL_CUR_TXBUF(sc)->m_pkthdr.len); 7933fabb142SSascha Wildner else 7943fabb142SSascha Wildner SLN_WRITE_4(sc, (entry * 4) + SL_TSD0, 0x50000 | SL_CUR_TXBUF(sc)->m_pkthdr.len); 7953fabb142SSascha Wildner sc->sln_bufdata.cur_tx = (entry + 1) % SL_TXD_CNT; 7963fabb142SSascha Wildner 7973fabb142SSascha Wildner PDEBUG("Queue tx packet size %d to tx-descriptor %d.\n", m_head->m_pkthdr.len, entry); 7983fabb142SSascha Wildner } 7993fabb142SSascha Wildner 8003fabb142SSascha Wildner /* Tx buffer chain full */ 8013fabb142SSascha Wildner if (SL_CUR_TXBUF(sc) != NULL) 8029ed293e0SSepherosa Ziehau ifq_set_oactive(&ifp->if_snd); 8033fabb142SSascha Wildner 8043fabb142SSascha Wildner /* Set a timeout in case the chip goes out to lunch */ 8053fabb142SSascha Wildner ifp->if_timer = 5; 8063fabb142SSascha Wildner } 8073fabb142SSascha Wildner 8083fabb142SSascha Wildner /* Receive Data handler */ 8093fabb142SSascha Wildner static void 8103fabb142SSascha Wildner sln_rx(struct sln_softc *sc) 8113fabb142SSascha Wildner { 8123fabb142SSascha Wildner struct mbuf *m; 8133fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 8143fabb142SSascha Wildner uint32_t rxstat = 0; 8153fabb142SSascha Wildner uint32_t rx_offset; 8163fabb142SSascha Wildner caddr_t rx_bufpos = NULL; 8173fabb142SSascha Wildner uint32_t cur_rx = 0; 8183fabb142SSascha Wildner uint32_t dirty_rx; 8193fabb142SSascha Wildner long rx_len; 8203fabb142SSascha Wildner u_long rx_space; 8213fabb142SSascha Wildner u_long rx_size = 0; 8223fabb142SSascha Wildner u_long rx_size_align = 0; 8233fabb142SSascha Wildner u_long pkt_size = 0; 8243fabb142SSascha Wildner 8253fabb142SSascha Wildner cur_rx = SLN_READ_4(sc, SL_RBW_PTR); 8263fabb142SSascha Wildner dirty_rx = sc->sln_bufdata.dirty_rx; 8273fabb142SSascha Wildner 8283fabb142SSascha Wildner /* 8293fabb142SSascha Wildner * cur_rx is only 17 bits in the RxBufWPtr register. if cur_rx can be 8303fabb142SSascha Wildner * used in physical space, we need to change it to 32 bits physical 8313fabb142SSascha Wildner * address 8323fabb142SSascha Wildner */ 8333fabb142SSascha Wildner cur_rx |= vtophys(sc->sln_bufdata.sln_rx_buf) & (~(u_long) (SL_RX_BUFLEN - 1)); 8343fabb142SSascha Wildner 8353fabb142SSascha Wildner if (cur_rx < vtophys(sc->sln_bufdata.sln_rx_buf)) 8363fabb142SSascha Wildner cur_rx += SL_RX_BUFLEN; 8373fabb142SSascha Wildner 8383fabb142SSascha Wildner if (cur_rx >= dirty_rx) 8393fabb142SSascha Wildner rx_len = (long)(cur_rx - dirty_rx); 8403fabb142SSascha Wildner else 8413fabb142SSascha Wildner rx_len = SL_RX_BUFLEN - (long)(dirty_rx - cur_rx); 8423fabb142SSascha Wildner 8433fabb142SSascha Wildner if ((rx_len > SL_RX_BUFLEN) || (rx_len < 0)) { 8443fabb142SSascha Wildner if_printf(ifp, "rx len is fail\n"); 8453fabb142SSascha Wildner return; 8463fabb142SSascha Wildner } 8473fabb142SSascha Wildner if (rx_len == 0) 8483fabb142SSascha Wildner return; 8493fabb142SSascha Wildner 8503fabb142SSascha Wildner rx_offset = (dirty_rx - vtophys(sc->sln_bufdata.sln_rx_buf)) & (u_long) (SL_RX_BUFLEN - 1); 8513fabb142SSascha Wildner 8523fabb142SSascha Wildner while (rx_len > 0) { 8533fabb142SSascha Wildner rx_bufpos = sc->sln_bufdata.sln_rx_buf + rx_offset; 8543fabb142SSascha Wildner rxstat = *(uint32_t *) rx_bufpos; 8553fabb142SSascha Wildner rx_size = (rxstat >> 20) & 0x0FFF; 8563fabb142SSascha Wildner rx_size_align = (rx_size + 3) & ~3; /* for 4 bytes aligned */ 8573fabb142SSascha Wildner pkt_size = rx_size - ETHER_CRC_LEN; /* Omit the four octet 8583fabb142SSascha Wildner * CRC from the length. */ 8593fabb142SSascha Wildner 8603fabb142SSascha Wildner PDEBUG("rx len: %ld rx frame size:%ld rx state:0x%x\n", rx_len, rx_size, rxstat); 8613fabb142SSascha Wildner 8623fabb142SSascha Wildner /* errors receive packets caculatation */ 8633fabb142SSascha Wildner if (rxstat == 0 || rx_size < 16 || !(rxstat & SL_RXSTAT_RXOK)) { 8643fabb142SSascha Wildner ifp->if_ierrors++; 8653fabb142SSascha Wildner 8663fabb142SSascha Wildner if (!(rxstat & SL_RXSTAT_RXOK)) 8673fabb142SSascha Wildner if_printf(ifp, "receiver ok error\n"); 8683fabb142SSascha Wildner 8693fabb142SSascha Wildner if (!(rxstat & SL_RXSTAT_CRCOK)) 8703fabb142SSascha Wildner if_printf(ifp, "crc error\n"); 8713fabb142SSascha Wildner 8723fabb142SSascha Wildner if (rxstat & SL_RXSTAT_ALIGNERR) 8733fabb142SSascha Wildner if_printf(ifp, "frame alignment error\n"); 8743fabb142SSascha Wildner 8753fabb142SSascha Wildner if (rxstat & (SL_RXSTAT_HUGEFRM | SL_RXSTAT_SMALLFRM)) 8763fabb142SSascha Wildner if_printf(ifp, "received frame length is error\n"); 8773fabb142SSascha Wildner 8783fabb142SSascha Wildner break; 8793fabb142SSascha Wildner } 8803fabb142SSascha Wildner rx_len -= (long)(rx_size_align + 4); /* 4 bytes for receive 8813fabb142SSascha Wildner * frame head */ 8823fabb142SSascha Wildner 8833fabb142SSascha Wildner if (rx_len < 0) { 8843fabb142SSascha Wildner kprintf("rx packets len is too small\n"); 8853fabb142SSascha Wildner break; 8863fabb142SSascha Wildner } 8873fabb142SSascha Wildner #ifdef SLN_PDEBUG 8883fabb142SSascha Wildner caddr_t p = NULL; 8893fabb142SSascha Wildner 8903fabb142SSascha Wildner if_printf(ifp, "rx frame content\n"); 8913fabb142SSascha Wildner p = rx_bufpos; 8923fabb142SSascha Wildner for (i = 0; i < 30; i++, p++) { 8933fabb142SSascha Wildner if (i % 10 == 0) 8943fabb142SSascha Wildner kprintf("\n"); 8953fabb142SSascha Wildner if_printf(ifp, "%x ", (u_char)*p); 8963fabb142SSascha Wildner } 8973fabb142SSascha Wildner if_printf(ifp, "\n"); 8983fabb142SSascha Wildner #endif 8993fabb142SSascha Wildner /* No errors; receive the packet. */ 9003fabb142SSascha Wildner if (rx_bufpos == (sc->sln_bufdata.sln_rx_buf + SL_RX_BUFLEN)) 9013fabb142SSascha Wildner rx_bufpos = sc->sln_bufdata.sln_rx_buf; 9023fabb142SSascha Wildner 9033fabb142SSascha Wildner rx_bufpos = rx_bufpos + 4; /* 4 bytes for receive frame 9043fabb142SSascha Wildner * header */ 9053fabb142SSascha Wildner rx_space = (u_long)((sc->sln_bufdata.sln_rx_buf + SL_RX_BUFLEN) - rx_bufpos); 9063fabb142SSascha Wildner 9073fabb142SSascha Wildner if (pkt_size > rx_space) { 9083fabb142SSascha Wildner m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp, NULL); /* 2 for etherer head 9093fabb142SSascha Wildner * align */ 9103fabb142SSascha Wildner 9113fabb142SSascha Wildner if (m == NULL) { 9123fabb142SSascha Wildner ifp->if_ierrors++; 9133fabb142SSascha Wildner if_printf(ifp, 9143fabb142SSascha Wildner "out of mbufs, tried to copy %ld bytes\n", 9153fabb142SSascha Wildner rx_space); 9163fabb142SSascha Wildner } else { 9173fabb142SSascha Wildner m_adj(m, 2); 9183fabb142SSascha Wildner m_copyback(m, rx_space, pkt_size - rx_space, sc->sln_bufdata.sln_rx_buf); 9193fabb142SSascha Wildner } 9203fabb142SSascha Wildner } else { 9213fabb142SSascha Wildner m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp, NULL); 9223fabb142SSascha Wildner 9233fabb142SSascha Wildner if (m == NULL) { 9243fabb142SSascha Wildner ifp->if_ierrors++; 9253fabb142SSascha Wildner if_printf(ifp, 9263fabb142SSascha Wildner "out of mbufs, tried to copy %ld bytes\n", 9273fabb142SSascha Wildner pkt_size); 9283fabb142SSascha Wildner if_printf(ifp, "ierrors = %ld\n", ifp->if_ierrors); 9293fabb142SSascha Wildner 9303fabb142SSascha Wildner } else { 9313fabb142SSascha Wildner m_adj(m, 2); 9323fabb142SSascha Wildner } 9333fabb142SSascha Wildner } 9343fabb142SSascha Wildner 9353fabb142SSascha Wildner ifp->if_ipackets++; 9363fabb142SSascha Wildner PDEBUG("ipackets = %ld\n", ifp->if_ipackets); 9373fabb142SSascha Wildner 9383fabb142SSascha Wildner ifp->if_input(ifp, m); 9393fabb142SSascha Wildner 9403fabb142SSascha Wildner rx_offset = (rx_offset + rx_size + 4) & (u_long) (SL_RX_BUFLEN - 1); /* 4 bytes for receive 9413fabb142SSascha Wildner * frame head */ 9423fabb142SSascha Wildner } 9433fabb142SSascha Wildner 9443fabb142SSascha Wildner sc->sln_bufdata.dirty_rx = cur_rx; 9453fabb142SSascha Wildner 9463fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RBR_PTR, cur_rx); 9473fabb142SSascha Wildner } 9483fabb142SSascha Wildner 9493fabb142SSascha Wildner /* Transmit OK/ERR handler */ 9503fabb142SSascha Wildner static void 9513fabb142SSascha Wildner sln_tx_intr(struct sln_softc *sc) 9523fabb142SSascha Wildner { 9533fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 9543fabb142SSascha Wildner uint32_t txstat; 9553fabb142SSascha Wildner int entry; 9563fabb142SSascha Wildner 9573fabb142SSascha Wildner do { 9583fabb142SSascha Wildner entry = sc->sln_bufdata.dirty_tx; 9593fabb142SSascha Wildner txstat = SLN_READ_4(sc, SL_TSD0 + entry * 4); 9603fabb142SSascha Wildner 9613fabb142SSascha Wildner if (!(txstat & (SL_TXSD_TOK | SL_TXSD_TUN | SL_TXSD_TABT))) 9623fabb142SSascha Wildner break; /* It still hasn't been sent */ 9633fabb142SSascha Wildner 9643fabb142SSascha Wildner if (SL_DIRTY_TXBUF(sc) != NULL) { /* SL_DIRTY_TXBUF(x) = 9653fabb142SSascha Wildner * x->sln_bufdata.sln_tx_ 9663fabb142SSascha Wildner * buf[x->sln_bufdata.dir 9673fabb142SSascha Wildner * ty_tx] */ 9683fabb142SSascha Wildner m_freem(SL_DIRTY_TXBUF(sc)); 9693fabb142SSascha Wildner SL_DIRTY_TXBUF(sc) = NULL; 9703fabb142SSascha Wildner } 9713fabb142SSascha Wildner if (txstat & SL_TXSD_TOK) { 9723fabb142SSascha Wildner ifp->if_opackets++; 9733fabb142SSascha Wildner ifp->if_obytes += txstat & SL_TXSD_LENMASK; 9743fabb142SSascha Wildner PDEBUG("opackets = %ld\n", ifp->if_opackets); 9753fabb142SSascha Wildner ifp->if_collisions += (txstat & SL_TXSD_NCC) >> 22; 9763fabb142SSascha Wildner } else { 9773fabb142SSascha Wildner ifp->if_oerrors++; 9783fabb142SSascha Wildner if ((txstat & (SL_TXSD_TABT | SL_TXSD_OWC))) { 9793fabb142SSascha Wildner sc->txcfg = TX_CFG_DEFAULT; 9803fabb142SSascha Wildner 9813fabb142SSascha Wildner if (sc->txenablepad) 9823fabb142SSascha Wildner sc->txcfg |= 0x20000000; 9833fabb142SSascha Wildner 9843fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 9853fabb142SSascha Wildner } 9863fabb142SSascha Wildner } 9873fabb142SSascha Wildner PDEBUG("tx done descriprtor %x\n", entry); 9883fabb142SSascha Wildner sc->sln_bufdata.dirty_tx = (entry + 1) % SL_TXD_CNT; 9893fabb142SSascha Wildner 9909ed293e0SSepherosa Ziehau ifq_clr_oactive(&ifp->if_snd); 9913fabb142SSascha Wildner } while (sc->sln_bufdata.dirty_tx != sc->sln_bufdata.cur_tx); 9923fabb142SSascha Wildner 9933fabb142SSascha Wildner if (sc->sln_bufdata.dirty_tx == sc->sln_bufdata.cur_tx) 9943fabb142SSascha Wildner ifp->if_timer = 0; 9953fabb142SSascha Wildner else 9963fabb142SSascha Wildner ifp->if_timer = 5; 9973fabb142SSascha Wildner } 9983fabb142SSascha Wildner 9993fabb142SSascha Wildner static void 10003fabb142SSascha Wildner sln_media_intr(struct sln_softc *sc) 10013fabb142SSascha Wildner { 10023fabb142SSascha Wildner u_long phys[2]; 10033fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 10043fabb142SSascha Wildner 10053fabb142SSascha Wildner phys[0] = SL_MII_STAT; 10063fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_READ, phys); 10073fabb142SSascha Wildner 10083fabb142SSascha Wildner PDEBUG("mii_stat:0x%lx\n", phys[1]); 10093fabb142SSascha Wildner 10103fabb142SSascha Wildner if (0 == (phys[1] & SL_MIISTAT_LINK)) { 10113fabb142SSascha Wildner kprintf("media is unconnect,linked down,or uncompatible\n"); 10123fabb142SSascha Wildner sc->connect = 0; 10133fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_SCAN, phys); 10143fabb142SSascha Wildner /* disable tx/rx */ 10153fabb142SSascha Wildner sc->txcfg &= ~SL_TXCFG_EN; 10163fabb142SSascha Wildner sc->rxcfg &= ~SL_RXCFG_EN; 10173fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 10183fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 10193fabb142SSascha Wildner 10203fabb142SSascha Wildner return; 10213fabb142SSascha Wildner } 10223fabb142SSascha Wildner /* Link is good. Report modes and set duplex mode. */ 10233fabb142SSascha Wildner PDEBUG("media is connecting---> "); 10243fabb142SSascha Wildner sc->connect = 1; 10253fabb142SSascha Wildner 10263fabb142SSascha Wildner phys[0] = SL_MII_STAT_OUTPUT; 10273fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_READ, phys); 10283fabb142SSascha Wildner sc->media_duplex = ((phys[1] & 0x0004) == 0) ? IFM_HDX : IFM_FDX; 10293fabb142SSascha Wildner sc->media_speed = ((phys[1] & 0x0002) == 0) ? IFM_10_T : IFM_100_TX; 10303fabb142SSascha Wildner 10313fabb142SSascha Wildner if_printf(ifp, "media option:%dM %s-duplex\n", 10323fabb142SSascha Wildner sc->media_speed == 0x6 ? 100 : 10, 10333fabb142SSascha Wildner sc->media_duplex == 0x100000 ? "full" : "half"); 10343fabb142SSascha Wildner 10353fabb142SSascha Wildner sln_mii_cmd(sc, SL_MII0_SCAN, phys); 10363fabb142SSascha Wildner 10373fabb142SSascha Wildner sln_mac_cfg(sc); 10383fabb142SSascha Wildner 10393fabb142SSascha Wildner /* Enable tx/rx */ 10403fabb142SSascha Wildner sc->rxcfg |= SL_RXCFG_EN; 10413fabb142SSascha Wildner sc->txcfg |= SL_TXCFG_EN; 10423fabb142SSascha Wildner SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg); 10433fabb142SSascha Wildner SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg); 10443fabb142SSascha Wildner } 10453fabb142SSascha Wildner 10463fabb142SSascha Wildner /* Interrupt Handler */ 10473fabb142SSascha Wildner static void 10483fabb142SSascha Wildner sln_interrupt(void *arg) 10493fabb142SSascha Wildner { 10503fabb142SSascha Wildner struct sln_softc *sc = arg; 10513fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 10523fabb142SSascha Wildner uint32_t int_status; 10533fabb142SSascha Wildner 10543fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 10553fabb142SSascha Wildner 10563fabb142SSascha Wildner if (sc->suspended || (ifp->if_flags & IFF_RUNNING) == 0) 10573fabb142SSascha Wildner return; 10583fabb142SSascha Wildner 10593fabb142SSascha Wildner /* Disable interrupts. */ 10603fabb142SSascha Wildner SLN_WRITE_4(sc, SL_INT_MASK, 0); 10613fabb142SSascha Wildner 10623fabb142SSascha Wildner int_status = SLN_READ_4(sc, SL_INT_STATUS); 10633fabb142SSascha Wildner 10643fabb142SSascha Wildner if ((int_status == 0xffffffff) || (int_status & SL_INRTS) == 0) 10653fabb142SSascha Wildner goto back; 10663fabb142SSascha Wildner 10673fabb142SSascha Wildner int_status = int_status & SL_INRTS; 10683fabb142SSascha Wildner PDEBUG("int_status = 0x%x\n", int_status); 10693fabb142SSascha Wildner 10703fabb142SSascha Wildner while (0 != int_status) { 10713fabb142SSascha Wildner if (int_status & SL_INT_ROK) 10723fabb142SSascha Wildner sln_rx(sc); 10733fabb142SSascha Wildner 10743fabb142SSascha Wildner if (int_status & SL_INT_TOK) 10753fabb142SSascha Wildner sln_tx_intr(sc); 10763fabb142SSascha Wildner 10773fabb142SSascha Wildner if (int_status & SL_INT_RBO) { 10783fabb142SSascha Wildner ifp->if_ierrors++; 10793fabb142SSascha Wildner PDEBUG("rx buffer is overflow\n"); 10803fabb142SSascha Wildner } 10813fabb142SSascha Wildner 10823fabb142SSascha Wildner if (int_status & (SL_INT_LINKFAIL | SL_INT_LINKOK)) 10833fabb142SSascha Wildner sln_media_intr(sc); 10843fabb142SSascha Wildner 10853fabb142SSascha Wildner int_status = SLN_READ_4(sc, SL_INT_STATUS); 10863fabb142SSascha Wildner } 10873fabb142SSascha Wildner 10883fabb142SSascha Wildner /* Data in Tx buffer waiting for transimission */ 10893fabb142SSascha Wildner if (!ifq_is_empty(&ifp->if_snd)) 10909db4b353SSepherosa Ziehau if_devstart(ifp); 10913fabb142SSascha Wildner back: 10923fabb142SSascha Wildner /* Re-enable interrupts. */ 10933fabb142SSascha Wildner SLN_WRITE_4(sc, SL_INT_MASK, SL_INRTS); 10943fabb142SSascha Wildner } 10953fabb142SSascha Wildner 10963fabb142SSascha Wildner static void 10973fabb142SSascha Wildner sln_tick(void *x) 10983fabb142SSascha Wildner { 10993fabb142SSascha Wildner struct sln_softc *sc = x; 11003fabb142SSascha Wildner 11013fabb142SSascha Wildner callout_reset(&sc->sln_state, hz, sln_tick, sc); 11023fabb142SSascha Wildner } 11033fabb142SSascha Wildner 11043fabb142SSascha Wildner static int 11053fabb142SSascha Wildner sln_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) 11063fabb142SSascha Wildner { 11073fabb142SSascha Wildner struct sln_softc *sc = ifp->if_softc; 11083fabb142SSascha Wildner struct ifreq *ifr = (struct ifreq *)data; 11093fabb142SSascha Wildner int error = 0; 11103fabb142SSascha Wildner 11113fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 11123fabb142SSascha Wildner 11133fabb142SSascha Wildner switch (cmd) { 11143fabb142SSascha Wildner case SIOCSIFFLAGS: 11153fabb142SSascha Wildner if (ifp->if_flags & IFF_UP) { 11163fabb142SSascha Wildner if ((ifp->if_flags & IFF_RUNNING) == 0) 11173fabb142SSascha Wildner sln_init(sc); 11183fabb142SSascha Wildner } else { 11193fabb142SSascha Wildner if (ifp->if_flags & IFF_RUNNING) 11203fabb142SSascha Wildner sln_stop(sc); 11213fabb142SSascha Wildner } 11223fabb142SSascha Wildner break; 11233fabb142SSascha Wildner case SIOCADDMULTI: 11243fabb142SSascha Wildner case SIOCDELMULTI: 11253fabb142SSascha Wildner sln_set_multi(sc); 11263fabb142SSascha Wildner break; 11273fabb142SSascha Wildner case SIOCGIFMEDIA: 11283fabb142SSascha Wildner case SIOCSIFMEDIA: 11293fabb142SSascha Wildner error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 11303fabb142SSascha Wildner break; 11313fabb142SSascha Wildner default: 11323fabb142SSascha Wildner error = ether_ioctl(ifp, cmd, data); 11333fabb142SSascha Wildner break; 11343fabb142SSascha Wildner } 11353fabb142SSascha Wildner return error; 11363fabb142SSascha Wildner } 11373fabb142SSascha Wildner 11383fabb142SSascha Wildner static void 11393fabb142SSascha Wildner sln_watchdog(struct ifnet *ifp) 11403fabb142SSascha Wildner { 11413fabb142SSascha Wildner struct sln_softc *sc = ifp->if_softc; 11423fabb142SSascha Wildner 11433fabb142SSascha Wildner ASSERT_SERIALIZED(ifp->if_serializer); 11443fabb142SSascha Wildner 11453fabb142SSascha Wildner if_printf(ifp, "watchdog timeout!\n"); 11463fabb142SSascha Wildner ifp->if_oerrors++; 11473fabb142SSascha Wildner 11483fabb142SSascha Wildner sln_tx_intr(sc); 11493fabb142SSascha Wildner sln_rx(sc); 11503fabb142SSascha Wildner sln_init(sc); 11513fabb142SSascha Wildner 11523fabb142SSascha Wildner if (!ifq_is_empty(&ifp->if_snd)) 11539db4b353SSepherosa Ziehau if_devstart(ifp); 11543fabb142SSascha Wildner } 11553fabb142SSascha Wildner 11563fabb142SSascha Wildner /* Stop all chip I/O */ 11573fabb142SSascha Wildner static int 11583fabb142SSascha Wildner sln_shutdown(device_t dev) 11593fabb142SSascha Wildner { 11603fabb142SSascha Wildner struct sln_softc *sc = device_get_softc(dev); 11613fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 11623fabb142SSascha Wildner 11633fabb142SSascha Wildner lwkt_serialize_enter(ifp->if_serializer); 11643fabb142SSascha Wildner sln_stop(sc); 11653fabb142SSascha Wildner lwkt_serialize_exit(ifp->if_serializer); 11663fabb142SSascha Wildner 11673fabb142SSascha Wildner return 0; 11683fabb142SSascha Wildner } 11693fabb142SSascha Wildner 11703fabb142SSascha Wildner /* device suspend routine */ 11713fabb142SSascha Wildner static int 11723fabb142SSascha Wildner sln_suspend(device_t dev) 11733fabb142SSascha Wildner { 11743fabb142SSascha Wildner struct sln_softc *sc = device_get_softc(dev); 11753fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 11763fabb142SSascha Wildner 11773fabb142SSascha Wildner lwkt_serialize_enter(ifp->if_serializer); 11783fabb142SSascha Wildner sln_stop(sc); 11793fabb142SSascha Wildner sc->suspended = 1; 11803fabb142SSascha Wildner lwkt_serialize_exit(ifp->if_serializer); 11813fabb142SSascha Wildner 11823fabb142SSascha Wildner return 0; 11833fabb142SSascha Wildner } 11843fabb142SSascha Wildner 11853fabb142SSascha Wildner /* device resume routine */ 11863fabb142SSascha Wildner static int 11873fabb142SSascha Wildner sln_resume(device_t dev) 11883fabb142SSascha Wildner { 11893fabb142SSascha Wildner struct sln_softc *sc = device_get_softc(dev); 11903fabb142SSascha Wildner struct ifnet *ifp = &sc->arpcom.ac_if; 11913fabb142SSascha Wildner 11923fabb142SSascha Wildner lwkt_serialize_enter(ifp->if_serializer); 11933fabb142SSascha Wildner if (ifp->if_flags & IFF_UP) 11943fabb142SSascha Wildner sln_init(sc); 11953fabb142SSascha Wildner sc->suspended = 0; 11963fabb142SSascha Wildner lwkt_serialize_exit(ifp->if_serializer); 11973fabb142SSascha Wildner 11983fabb142SSascha Wildner return 0; 11993fabb142SSascha Wildner } 1200