xref: /dflybsd-src/sys/dev/netif/sln/if_sln.c (revision b272101acc636ac635f83d03265ef6a44a3ba51a)
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 
47dcb4b80dSSascha Wildner #include "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/if.h>
56bff82488SAaron LI #include <net/if_var.h>
573fabb142SSascha Wildner #include <net/if_arp.h>
583fabb142SSascha Wildner #include <net/if_dl.h>
593fabb142SSascha Wildner #include <net/if_media.h>
60bff82488SAaron LI #include <net/ifq_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 
138d3c9c58eSSascha Wildner 	DEVMETHOD_END
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
sln_probe(device_t dev)1525d302545SFrançois Tigeot sln_probe(device_t 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
sln_reset(struct sln_softc * sc)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
sln_attach(device_t dev)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 
2594c77af2dSSepherosa Ziehau 	ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->sln_irq));
2604c77af2dSSepherosa Ziehau 
2613fabb142SSascha Wildner 	error = bus_setup_intr(dev, sc->sln_irq, INTR_MPSAFE, sln_interrupt, sc,
2623fabb142SSascha Wildner 			       &sc->sln_intrhand, ifp->if_serializer);
2633fabb142SSascha Wildner 	if (error) {
2643fabb142SSascha Wildner 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sln_irq);
2653fabb142SSascha Wildner 		bus_release_resource(dev, SL_RES, SL_RID, sc->sln_res);
2663fabb142SSascha Wildner 		ether_ifdetach(ifp);
2673fabb142SSascha Wildner 		device_printf(dev, "couldn't set up irq\n");
2683fabb142SSascha Wildner 		goto fail;
2693fabb142SSascha Wildner 	}
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
sln_stop(struct sln_softc * sc)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);
2963d13a951SSepherosa 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
sln_detach(device_t dev)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
sln_media_upd(struct ifnet * ifp)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
sln_media_stat(struct ifnet * ifp,struct ifmediareq * ifmr)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
sln_mii_cmd(struct sln_softc * sc,uint32_t cmd,u_long * phys)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
sln_media_cfg(struct sln_softc * sc)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
sln_mac_cfg(struct sln_softc * sc)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
sln_ether_crc32(caddr_t addr)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
sln_set_multi(struct sln_softc * sc)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
sln_init_tx(struct sln_softc * sc)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
sln_init(void * x)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
sln_tx(struct ifnet * ifp,struct ifaltq_subque * ifsq)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 
750ac9843a1SSepherosa Ziehau 		m_head = ifq_dequeue(&ifp->if_snd);
7513fabb142SSascha Wildner 		if (m_head == NULL)
7523fabb142SSascha Wildner 			break;
7533fabb142SSascha Wildner 
754b5523eacSSascha Wildner 		MGETHDR(m_new, M_NOWAIT, 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)) {
761b5523eacSSascha Wildner 			MCLGET(m_new, M_NOWAIT);
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 		}
76905d02a38SAaron LI 		m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, void *));
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
sln_rx(struct sln_softc * sc)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) {
853d40991efSSepherosa Ziehau #ifdef SLN_DEBUG
854d40991efSSepherosa Ziehau 		u_long ipkts;
855d40991efSSepherosa Ziehau #endif
856d40991efSSepherosa Ziehau 
8573fabb142SSascha Wildner 		rx_bufpos = sc->sln_bufdata.sln_rx_buf + rx_offset;
8583fabb142SSascha Wildner 		rxstat = *(uint32_t *) rx_bufpos;
8593fabb142SSascha Wildner 		rx_size = (rxstat >> 20) & 0x0FFF;
8603fabb142SSascha Wildner 		rx_size_align = (rx_size + 3) & ~3;	/* for 4 bytes aligned */
8613fabb142SSascha Wildner 		pkt_size = rx_size - ETHER_CRC_LEN;	/* Omit the four octet
8623fabb142SSascha Wildner 							 * CRC from the length. */
8633fabb142SSascha Wildner 
8643fabb142SSascha Wildner 		PDEBUG("rx len: %ld  rx frame size:%ld  rx state:0x%x\n", rx_len, rx_size, rxstat);
8653fabb142SSascha Wildner 
8663fabb142SSascha Wildner 		/* errors receive packets caculatation */
8673fabb142SSascha Wildner 		if (rxstat == 0 || rx_size < 16 || !(rxstat & SL_RXSTAT_RXOK)) {
868d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, ierrors, 1);
8693fabb142SSascha Wildner 
8703fabb142SSascha Wildner 			if (!(rxstat & SL_RXSTAT_RXOK))
8713fabb142SSascha Wildner 				if_printf(ifp, "receiver ok error\n");
8723fabb142SSascha Wildner 
8733fabb142SSascha Wildner 			if (!(rxstat & SL_RXSTAT_CRCOK))
8743fabb142SSascha Wildner 				if_printf(ifp, "crc error\n");
8753fabb142SSascha Wildner 
8763fabb142SSascha Wildner 			if (rxstat & SL_RXSTAT_ALIGNERR)
8773fabb142SSascha Wildner 				if_printf(ifp, "frame alignment error\n");
8783fabb142SSascha Wildner 
8793fabb142SSascha Wildner 			if (rxstat & (SL_RXSTAT_HUGEFRM | SL_RXSTAT_SMALLFRM))
8803fabb142SSascha Wildner 				if_printf(ifp, "received frame length is error\n");
8813fabb142SSascha Wildner 
8823fabb142SSascha Wildner 			break;
8833fabb142SSascha Wildner 		}
8843fabb142SSascha Wildner 		rx_len -= (long)(rx_size_align + 4);	/* 4 bytes for receive
8853fabb142SSascha Wildner 							 * frame head */
8863fabb142SSascha Wildner 
8873fabb142SSascha Wildner 		if (rx_len < 0) {
8883fabb142SSascha Wildner 			kprintf("rx packets len is too small\n");
8893fabb142SSascha Wildner 			break;
8903fabb142SSascha Wildner 		}
8913fabb142SSascha Wildner #ifdef SLN_PDEBUG
8923fabb142SSascha Wildner 		caddr_t p = NULL;
8933fabb142SSascha Wildner 
8943fabb142SSascha Wildner 		if_printf(ifp, "rx frame content\n");
8953fabb142SSascha Wildner 		p = rx_bufpos;
8963fabb142SSascha Wildner 		for (i = 0; i < 30; i++, p++) {
8973fabb142SSascha Wildner 			if (i % 10 == 0)
8983fabb142SSascha Wildner 				kprintf("\n");
8993fabb142SSascha Wildner 			if_printf(ifp, "%x  ", (u_char)*p);
9003fabb142SSascha Wildner 		}
9013fabb142SSascha Wildner 		if_printf(ifp, "\n");
9023fabb142SSascha Wildner #endif
9033fabb142SSascha Wildner 		/* No errors; receive the packet. */
9043fabb142SSascha Wildner 		if (rx_bufpos == (sc->sln_bufdata.sln_rx_buf + SL_RX_BUFLEN))
9053fabb142SSascha Wildner 			rx_bufpos = sc->sln_bufdata.sln_rx_buf;
9063fabb142SSascha Wildner 
9073fabb142SSascha Wildner 		rx_bufpos = rx_bufpos + 4;	/* 4 bytes for receive frame
9083fabb142SSascha Wildner 						 * header */
9093fabb142SSascha Wildner 		rx_space = (u_long)((sc->sln_bufdata.sln_rx_buf + SL_RX_BUFLEN) - rx_bufpos);
9103fabb142SSascha Wildner 
9113fabb142SSascha Wildner 		if (pkt_size > rx_space) {
912afd2da4dSMatthew Dillon 			/* 2 for etherer head align */
913afd2da4dSMatthew Dillon 			m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp);
9143fabb142SSascha Wildner 
9153fabb142SSascha Wildner 			if (m == NULL) {
916d40991efSSepherosa Ziehau 				IFNET_STAT_INC(ifp, ierrors, 1);
9173fabb142SSascha Wildner 				if_printf(ifp,
9183fabb142SSascha Wildner 				    "out of mbufs, tried to copy %ld bytes\n",
9193fabb142SSascha Wildner 				    rx_space);
9203fabb142SSascha Wildner 			} else {
9213fabb142SSascha Wildner 				m_adj(m, 2);
922*b272101aSAaron LI 				m_copyback(m, rx_space, pkt_size - rx_space,
923*b272101aSAaron LI 				    sc->sln_bufdata.sln_rx_buf);
9243fabb142SSascha Wildner 			}
9253fabb142SSascha Wildner 		} else {
926afd2da4dSMatthew Dillon 			m = m_devget(rx_bufpos - 2, pkt_size + 2, 0, ifp);
9273fabb142SSascha Wildner 
9283fabb142SSascha Wildner 			if (m == NULL) {
929d40991efSSepherosa Ziehau 				u_long ierr;
930d40991efSSepherosa Ziehau 
931d40991efSSepherosa Ziehau 				IFNET_STAT_INC(ifp, ierrors, 1);
9323fabb142SSascha Wildner 				if_printf(ifp,
9333fabb142SSascha Wildner 				    "out of mbufs, tried to copy %ld bytes\n",
9343fabb142SSascha Wildner 				    pkt_size);
9353fabb142SSascha Wildner 
936d40991efSSepherosa Ziehau 				IFNET_STAT_GET(ifp, ierrors, ierr);
937d40991efSSepherosa Ziehau 				if_printf(ifp, "ierrors = %lu\n", ierr);
9383fabb142SSascha Wildner 			} else {
9393fabb142SSascha Wildner 				m_adj(m, 2);
9403fabb142SSascha Wildner 			}
9413fabb142SSascha Wildner 		}
9423fabb142SSascha Wildner 
943d40991efSSepherosa Ziehau 		IFNET_STAT_INC(ifp, ipackets, 1);
944d40991efSSepherosa Ziehau #ifdef SLN_DEBUG
945d40991efSSepherosa Ziehau 		IFNET_STAT_GET(ifp, ipackets, ipkts);
946d40991efSSepherosa Ziehau 		PDEBUG("ipackets = %lu\n", ipkts);
947d40991efSSepherosa Ziehau #endif
9483fabb142SSascha Wildner 
94973029d08SFranco Fichtner 		ifp->if_input(ifp, m, NULL, -1);
9503fabb142SSascha Wildner 
9513fabb142SSascha Wildner 		rx_offset = (rx_offset + rx_size + 4) & (u_long) (SL_RX_BUFLEN - 1);	/* 4 bytes for receive
9523fabb142SSascha Wildner 											 * frame head */
9533fabb142SSascha Wildner 	}
9543fabb142SSascha Wildner 
9553fabb142SSascha Wildner 	sc->sln_bufdata.dirty_rx = cur_rx;
9563fabb142SSascha Wildner 
9573fabb142SSascha Wildner 	SLN_WRITE_4(sc, SL_RBR_PTR, cur_rx);
9583fabb142SSascha Wildner }
9593fabb142SSascha Wildner 
9603fabb142SSascha Wildner /* Transmit OK/ERR handler */
9613fabb142SSascha Wildner static void
sln_tx_intr(struct sln_softc * sc)9623fabb142SSascha Wildner sln_tx_intr(struct sln_softc *sc)
9633fabb142SSascha Wildner {
9643fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
9653fabb142SSascha Wildner 	uint32_t txstat;
9663fabb142SSascha Wildner 	int entry;
9673fabb142SSascha Wildner 
9683fabb142SSascha Wildner 	do {
9693fabb142SSascha Wildner 		entry = sc->sln_bufdata.dirty_tx;
9703fabb142SSascha Wildner 		txstat = SLN_READ_4(sc, SL_TSD0 + entry * 4);
9713fabb142SSascha Wildner 
9723fabb142SSascha Wildner 		if (!(txstat & (SL_TXSD_TOK | SL_TXSD_TUN | SL_TXSD_TABT)))
9733fabb142SSascha Wildner 			break;	/* It still hasn't been sent */
9743fabb142SSascha Wildner 
9753fabb142SSascha Wildner 		if (SL_DIRTY_TXBUF(sc) != NULL) {	/* SL_DIRTY_TXBUF(x) =
9763fabb142SSascha Wildner 							 * x->sln_bufdata.sln_tx_
9773fabb142SSascha Wildner 							 * buf[x->sln_bufdata.dir
9783fabb142SSascha Wildner 							 * ty_tx] */
9793fabb142SSascha Wildner 			m_freem(SL_DIRTY_TXBUF(sc));
9803fabb142SSascha Wildner 			SL_DIRTY_TXBUF(sc) = NULL;
9813fabb142SSascha Wildner 		}
9823fabb142SSascha Wildner 		if (txstat & SL_TXSD_TOK) {
983d40991efSSepherosa Ziehau #ifdef SLN_DEBUG
984d40991efSSepherosa Ziehau 			u_long opkts;
985d40991efSSepherosa Ziehau #endif
986d40991efSSepherosa Ziehau 
987d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, opackets, 1);
988d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, obytes, txstat & SL_TXSD_LENMASK);
989d40991efSSepherosa Ziehau #ifdef SLN_DEBUG
990d40991efSSepherosa Ziehau 			IFNET_STAT_GET(ifp, opackets, opkts);
991d40991efSSepherosa Ziehau 			PDEBUG("opackets = %lu\n", opkts);
992d40991efSSepherosa Ziehau #endif
993d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, collisions,
994d40991efSSepherosa Ziehau 			    (txstat & SL_TXSD_NCC) >> 22);
9953fabb142SSascha Wildner 		} else {
996d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, oerrors, 1);
9973fabb142SSascha Wildner 			if ((txstat & (SL_TXSD_TABT | SL_TXSD_OWC))) {
9983fabb142SSascha Wildner 				sc->txcfg = TX_CFG_DEFAULT;
9993fabb142SSascha Wildner 
10003fabb142SSascha Wildner 				if (sc->txenablepad)
10013fabb142SSascha Wildner 					sc->txcfg |= 0x20000000;
10023fabb142SSascha Wildner 
10033fabb142SSascha Wildner 				SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg);
10043fabb142SSascha Wildner 			}
10053fabb142SSascha Wildner 		}
10063fabb142SSascha Wildner 		PDEBUG("tx done descriprtor %x\n", entry);
10073fabb142SSascha Wildner 		sc->sln_bufdata.dirty_tx = (entry + 1) % SL_TXD_CNT;
10083fabb142SSascha Wildner 
10099ed293e0SSepherosa Ziehau 		ifq_clr_oactive(&ifp->if_snd);
10103fabb142SSascha Wildner 	} while (sc->sln_bufdata.dirty_tx != sc->sln_bufdata.cur_tx);
10113fabb142SSascha Wildner 
10123fabb142SSascha Wildner 	if (sc->sln_bufdata.dirty_tx == sc->sln_bufdata.cur_tx)
10133fabb142SSascha Wildner 		ifp->if_timer = 0;
10143fabb142SSascha Wildner 	else
10153fabb142SSascha Wildner 		ifp->if_timer = 5;
10163fabb142SSascha Wildner }
10173fabb142SSascha Wildner 
10183fabb142SSascha Wildner static void
sln_media_intr(struct sln_softc * sc)10193fabb142SSascha Wildner sln_media_intr(struct sln_softc *sc)
10203fabb142SSascha Wildner {
10213fabb142SSascha Wildner 	u_long phys[2];
10223fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
10233fabb142SSascha Wildner 
10243fabb142SSascha Wildner 	phys[0] = SL_MII_STAT;
10253fabb142SSascha Wildner 	sln_mii_cmd(sc, SL_MII0_READ, phys);
10263fabb142SSascha Wildner 
10273fabb142SSascha Wildner 	PDEBUG("mii_stat:0x%lx\n", phys[1]);
10283fabb142SSascha Wildner 
10293fabb142SSascha Wildner 	if (0 == (phys[1] & SL_MIISTAT_LINK)) {
10303fabb142SSascha Wildner 		kprintf("media is unconnect,linked down,or uncompatible\n");
10313fabb142SSascha Wildner 		sc->connect = 0;
10323fabb142SSascha Wildner 		sln_mii_cmd(sc, SL_MII0_SCAN, phys);
10333fabb142SSascha Wildner 		/* disable tx/rx */
10343fabb142SSascha Wildner 		sc->txcfg &= ~SL_TXCFG_EN;
10353fabb142SSascha Wildner 		sc->rxcfg &= ~SL_RXCFG_EN;
10363fabb142SSascha Wildner 		SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg);
10373fabb142SSascha Wildner 		SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg);
10383fabb142SSascha Wildner 
10393fabb142SSascha Wildner 		return;
10403fabb142SSascha Wildner 	}
10413fabb142SSascha Wildner 	/* Link is good. Report modes and set duplex mode. */
10423fabb142SSascha Wildner 	PDEBUG("media is connecting---> ");
10433fabb142SSascha Wildner 	sc->connect = 1;
10443fabb142SSascha Wildner 
10453fabb142SSascha Wildner 	phys[0] = SL_MII_STAT_OUTPUT;
10463fabb142SSascha Wildner 	sln_mii_cmd(sc, SL_MII0_READ, phys);
10473fabb142SSascha Wildner 	sc->media_duplex = ((phys[1] & 0x0004) == 0) ? IFM_HDX : IFM_FDX;
10483fabb142SSascha Wildner 	sc->media_speed = ((phys[1] & 0x0002) == 0) ? IFM_10_T : IFM_100_TX;
10493fabb142SSascha Wildner 
10503fabb142SSascha Wildner 	if_printf(ifp, "media option:%dM %s-duplex\n",
10513fabb142SSascha Wildner 	    sc->media_speed == 0x6 ? 100 : 10,
10523fabb142SSascha Wildner 	    sc->media_duplex == 0x100000 ? "full" : "half");
10533fabb142SSascha Wildner 
10543fabb142SSascha Wildner 	sln_mii_cmd(sc, SL_MII0_SCAN, phys);
10553fabb142SSascha Wildner 
10563fabb142SSascha Wildner 	sln_mac_cfg(sc);
10573fabb142SSascha Wildner 
10583fabb142SSascha Wildner 	/* Enable tx/rx */
10593fabb142SSascha Wildner 	sc->rxcfg |= SL_RXCFG_EN;
10603fabb142SSascha Wildner 	sc->txcfg |= SL_TXCFG_EN;
10613fabb142SSascha Wildner 	SLN_WRITE_4(sc, SL_TX_CONFIG, sc->txcfg);
10623fabb142SSascha Wildner 	SLN_WRITE_4(sc, SL_RX_CONFIG, sc->rxcfg);
10633fabb142SSascha Wildner }
10643fabb142SSascha Wildner 
10653fabb142SSascha Wildner /* Interrupt Handler */
10663fabb142SSascha Wildner static void
sln_interrupt(void * arg)10673fabb142SSascha Wildner sln_interrupt(void *arg)
10683fabb142SSascha Wildner {
10693fabb142SSascha Wildner 	struct sln_softc *sc = arg;
10703fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
10713fabb142SSascha Wildner 	uint32_t int_status;
10723fabb142SSascha Wildner 
10733fabb142SSascha Wildner 	ASSERT_SERIALIZED(ifp->if_serializer);
10743fabb142SSascha Wildner 
10753fabb142SSascha Wildner 	if (sc->suspended || (ifp->if_flags & IFF_RUNNING) == 0)
10763fabb142SSascha Wildner 		return;
10773fabb142SSascha Wildner 
10783fabb142SSascha Wildner 	/* Disable interrupts. */
10793fabb142SSascha Wildner 	SLN_WRITE_4(sc, SL_INT_MASK, 0);
10803fabb142SSascha Wildner 
10813fabb142SSascha Wildner 	int_status = SLN_READ_4(sc, SL_INT_STATUS);
10823fabb142SSascha Wildner 
10833fabb142SSascha Wildner 	if ((int_status == 0xffffffff) || (int_status & SL_INRTS) == 0)
10843fabb142SSascha Wildner 		goto back;
10853fabb142SSascha Wildner 
10863fabb142SSascha Wildner 	int_status = int_status & SL_INRTS;
10873fabb142SSascha Wildner 	PDEBUG("int_status = 0x%x\n", int_status);
10883fabb142SSascha Wildner 
10893fabb142SSascha Wildner 	while (0 != int_status) {
10903fabb142SSascha Wildner 		if (int_status & SL_INT_ROK)
10913fabb142SSascha Wildner 			sln_rx(sc);
10923fabb142SSascha Wildner 
10933fabb142SSascha Wildner 		if (int_status & SL_INT_TOK)
10943fabb142SSascha Wildner 			sln_tx_intr(sc);
10953fabb142SSascha Wildner 
10963fabb142SSascha Wildner 		if (int_status & SL_INT_RBO) {
1097d40991efSSepherosa Ziehau 			IFNET_STAT_INC(ifp, ierrors, 1);
10983fabb142SSascha Wildner 			PDEBUG("rx buffer is overflow\n");
10993fabb142SSascha Wildner 		}
11003fabb142SSascha Wildner 
11013fabb142SSascha Wildner 		if (int_status & (SL_INT_LINKFAIL | SL_INT_LINKOK))
11023fabb142SSascha Wildner 			sln_media_intr(sc);
11033fabb142SSascha Wildner 
11043fabb142SSascha Wildner 		int_status = SLN_READ_4(sc, SL_INT_STATUS);
11053fabb142SSascha Wildner 	}
11063fabb142SSascha Wildner 
11073fabb142SSascha Wildner 	/* Data in Tx buffer waiting for transimission */
11083fabb142SSascha Wildner 	if (!ifq_is_empty(&ifp->if_snd))
11099db4b353SSepherosa Ziehau 		if_devstart(ifp);
11103fabb142SSascha Wildner back:
11113fabb142SSascha Wildner 	/* Re-enable interrupts. */
11123fabb142SSascha Wildner 	SLN_WRITE_4(sc, SL_INT_MASK, SL_INRTS);
11133fabb142SSascha Wildner }
11143fabb142SSascha Wildner 
11153fabb142SSascha Wildner static void
sln_tick(void * x)11163fabb142SSascha Wildner sln_tick(void *x)
11173fabb142SSascha Wildner {
11183fabb142SSascha Wildner 	struct sln_softc *sc = x;
11193fabb142SSascha Wildner 
11203fabb142SSascha Wildner 	callout_reset(&sc->sln_state, hz, sln_tick, sc);
11213fabb142SSascha Wildner }
11223fabb142SSascha Wildner 
11233fabb142SSascha Wildner static int
sln_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data,struct ucred * cr)11243fabb142SSascha Wildner sln_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
11253fabb142SSascha Wildner {
11263fabb142SSascha Wildner 	struct sln_softc *sc = ifp->if_softc;
11273fabb142SSascha Wildner 	struct ifreq *ifr = (struct ifreq *)data;
11283fabb142SSascha Wildner 	int error = 0;
11293fabb142SSascha Wildner 
11303fabb142SSascha Wildner 	ASSERT_SERIALIZED(ifp->if_serializer);
11313fabb142SSascha Wildner 
11323fabb142SSascha Wildner 	switch (cmd) {
11333fabb142SSascha Wildner 	case SIOCSIFFLAGS:
11343fabb142SSascha Wildner 		if (ifp->if_flags & IFF_UP) {
11353fabb142SSascha Wildner 			if ((ifp->if_flags & IFF_RUNNING) == 0)
11363fabb142SSascha Wildner 				sln_init(sc);
11373fabb142SSascha Wildner 		} else {
11383fabb142SSascha Wildner 			if (ifp->if_flags & IFF_RUNNING)
11393fabb142SSascha Wildner 				sln_stop(sc);
11403fabb142SSascha Wildner 		}
11413fabb142SSascha Wildner 		break;
11423fabb142SSascha Wildner 	case SIOCADDMULTI:
11433fabb142SSascha Wildner 	case SIOCDELMULTI:
11443fabb142SSascha Wildner 		sln_set_multi(sc);
11453fabb142SSascha Wildner 		break;
11463fabb142SSascha Wildner 	case SIOCGIFMEDIA:
11473fabb142SSascha Wildner 	case SIOCSIFMEDIA:
11483fabb142SSascha Wildner 		error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd);
11493fabb142SSascha Wildner 		break;
11503fabb142SSascha Wildner 	default:
11513fabb142SSascha Wildner 		error = ether_ioctl(ifp, cmd, data);
11523fabb142SSascha Wildner 		break;
11533fabb142SSascha Wildner 	}
11543fabb142SSascha Wildner 	return error;
11553fabb142SSascha Wildner }
11563fabb142SSascha Wildner 
11573fabb142SSascha Wildner static void
sln_watchdog(struct ifnet * ifp)11583fabb142SSascha Wildner sln_watchdog(struct ifnet *ifp)
11593fabb142SSascha Wildner {
11603fabb142SSascha Wildner 	struct sln_softc *sc = ifp->if_softc;
11613fabb142SSascha Wildner 
11623fabb142SSascha Wildner 	ASSERT_SERIALIZED(ifp->if_serializer);
11633fabb142SSascha Wildner 
11643fabb142SSascha Wildner 	if_printf(ifp, "watchdog timeout!\n");
1165d40991efSSepherosa Ziehau 	IFNET_STAT_INC(ifp, oerrors, 1);
11663fabb142SSascha Wildner 
11673fabb142SSascha Wildner 	sln_tx_intr(sc);
11683fabb142SSascha Wildner 	sln_rx(sc);
11693fabb142SSascha Wildner 	sln_init(sc);
11703fabb142SSascha Wildner 
11713fabb142SSascha Wildner 	if (!ifq_is_empty(&ifp->if_snd))
11729db4b353SSepherosa Ziehau 		if_devstart(ifp);
11733fabb142SSascha Wildner }
11743fabb142SSascha Wildner 
11753fabb142SSascha Wildner /* Stop all chip I/O */
11763fabb142SSascha Wildner static int
sln_shutdown(device_t dev)11773fabb142SSascha Wildner sln_shutdown(device_t dev)
11783fabb142SSascha Wildner {
11793fabb142SSascha Wildner 	struct sln_softc *sc = device_get_softc(dev);
11803fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
11813fabb142SSascha Wildner 
11823fabb142SSascha Wildner 	lwkt_serialize_enter(ifp->if_serializer);
11833fabb142SSascha Wildner 	sln_stop(sc);
11843fabb142SSascha Wildner 	lwkt_serialize_exit(ifp->if_serializer);
11853fabb142SSascha Wildner 
11863fabb142SSascha Wildner 	return 0;
11873fabb142SSascha Wildner }
11883fabb142SSascha Wildner 
11893fabb142SSascha Wildner /* device suspend routine */
11903fabb142SSascha Wildner static int
sln_suspend(device_t dev)11913fabb142SSascha Wildner sln_suspend(device_t dev)
11923fabb142SSascha Wildner {
11933fabb142SSascha Wildner 	struct sln_softc *sc = device_get_softc(dev);
11943fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
11953fabb142SSascha Wildner 
11963fabb142SSascha Wildner 	lwkt_serialize_enter(ifp->if_serializer);
11973fabb142SSascha Wildner 	sln_stop(sc);
11983fabb142SSascha Wildner 	sc->suspended = 1;
11993fabb142SSascha Wildner 	lwkt_serialize_exit(ifp->if_serializer);
12003fabb142SSascha Wildner 
12013fabb142SSascha Wildner 	return 0;
12023fabb142SSascha Wildner }
12033fabb142SSascha Wildner 
12043fabb142SSascha Wildner /* device resume routine */
12053fabb142SSascha Wildner static int
sln_resume(device_t dev)12063fabb142SSascha Wildner sln_resume(device_t dev)
12073fabb142SSascha Wildner {
12083fabb142SSascha Wildner 	struct sln_softc *sc = device_get_softc(dev);
12093fabb142SSascha Wildner 	struct ifnet *ifp = &sc->arpcom.ac_if;
12103fabb142SSascha Wildner 
12113fabb142SSascha Wildner 	lwkt_serialize_enter(ifp->if_serializer);
12123fabb142SSascha Wildner 	if (ifp->if_flags & IFF_UP)
12133fabb142SSascha Wildner 		sln_init(sc);
12143fabb142SSascha Wildner 	sc->suspended = 0;
12153fabb142SSascha Wildner 	lwkt_serialize_exit(ifp->if_serializer);
12163fabb142SSascha Wildner 
12173fabb142SSascha Wildner 	return 0;
12183fabb142SSascha Wildner }
1219