xref: /freebsd-src/sys/powerpc/ps3/if_glc.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
103479763SNathan Whitehorn /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
371e3c308SPedro F. Giffuni  *
403479763SNathan Whitehorn  * Copyright (C) 2010 Nathan Whitehorn
503479763SNathan Whitehorn  * All rights reserved.
603479763SNathan Whitehorn  *
703479763SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
803479763SNathan Whitehorn  * modification, are permitted provided that the following conditions
903479763SNathan Whitehorn  * are met:
1003479763SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
1103479763SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
1203479763SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
1303479763SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
1403479763SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
1503479763SNathan Whitehorn  *
1603479763SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1703479763SNathan Whitehorn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1803479763SNathan Whitehorn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1903479763SNathan Whitehorn  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2003479763SNathan Whitehorn  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2103479763SNathan Whitehorn  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2203479763SNathan Whitehorn  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2303479763SNathan Whitehorn  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2403479763SNathan Whitehorn  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2503479763SNathan Whitehorn  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2603479763SNathan Whitehorn  */
2703479763SNathan Whitehorn 
2803479763SNathan Whitehorn #include <sys/param.h>
2903479763SNathan Whitehorn #include <sys/systm.h>
3003479763SNathan Whitehorn #include <sys/sockio.h>
3103479763SNathan Whitehorn #include <sys/endian.h>
3266e01d73SGleb Smirnoff #include <sys/lock.h>
3303479763SNathan Whitehorn #include <sys/mbuf.h>
3403479763SNathan Whitehorn #include <sys/module.h>
3503479763SNathan Whitehorn #include <sys/malloc.h>
3666e01d73SGleb Smirnoff #include <sys/mutex.h>
3703479763SNathan Whitehorn #include <sys/kernel.h>
3803479763SNathan Whitehorn #include <sys/socket.h>
3903479763SNathan Whitehorn 
4003479763SNathan Whitehorn #include <vm/vm.h>
4103479763SNathan Whitehorn #include <vm/pmap.h>
4203479763SNathan Whitehorn 
4303479763SNathan Whitehorn #include <net/bpf.h>
4403479763SNathan Whitehorn #include <net/if.h>
4566e01d73SGleb Smirnoff #include <net/if_var.h>
4603479763SNathan Whitehorn #include <net/ethernet.h>
4703479763SNathan Whitehorn #include <net/if_media.h>
4803479763SNathan Whitehorn #include <net/if_types.h>
4966e01d73SGleb Smirnoff #include <net/if_dl.h>
5003479763SNathan Whitehorn 
5103479763SNathan Whitehorn #include <machine/pio.h>
5203479763SNathan Whitehorn #include <machine/bus.h>
5303479763SNathan Whitehorn #include <machine/platform.h>
5403479763SNathan Whitehorn #include <machine/resource.h>
5503479763SNathan Whitehorn #include <sys/bus.h>
5603479763SNathan Whitehorn #include <sys/rman.h>
5703479763SNathan Whitehorn 
5803479763SNathan Whitehorn #include "ps3bus.h"
5903479763SNathan Whitehorn #include "ps3-hvcall.h"
6003479763SNathan Whitehorn #include "if_glcreg.h"
6103479763SNathan Whitehorn 
6203479763SNathan Whitehorn static int	glc_probe(device_t);
6303479763SNathan Whitehorn static int	glc_attach(device_t);
6403479763SNathan Whitehorn static void	glc_init(void *xsc);
653df047e3SJustin Hibbits static void	glc_start(if_t ifp);
663df047e3SJustin Hibbits static int	glc_ioctl(if_t ifp, u_long cmd, caddr_t data);
6703479763SNathan Whitehorn static void	glc_set_multicast(struct glc_softc *sc);
6803479763SNathan Whitehorn static int	glc_add_rxbuf(struct glc_softc *sc, int idx);
6903479763SNathan Whitehorn static int	glc_add_rxbuf_dma(struct glc_softc *sc, int idx);
7003479763SNathan Whitehorn static int	glc_encap(struct glc_softc *sc, struct mbuf **m_head,
7103479763SNathan Whitehorn 		    bus_addr_t *pktdesc);
7203479763SNathan Whitehorn static int	glc_intr_filter(void *xsc);
7303479763SNathan Whitehorn static void	glc_intr(void *xsc);
7403479763SNathan Whitehorn static void	glc_tick(void *xsc);
753df047e3SJustin Hibbits static void	glc_media_status(if_t ifp, struct ifmediareq *ifmr);
763df047e3SJustin Hibbits static int	glc_media_change(if_t ifp);
7703479763SNathan Whitehorn 
7803479763SNathan Whitehorn static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
7903479763SNathan Whitehorn 
8003479763SNathan Whitehorn static device_method_t glc_methods[] = {
8103479763SNathan Whitehorn 	/* Device interface */
8203479763SNathan Whitehorn 	DEVMETHOD(device_probe,		glc_probe),
8303479763SNathan Whitehorn 	DEVMETHOD(device_attach,	glc_attach),
8403479763SNathan Whitehorn 	{ 0, 0 }
8503479763SNathan Whitehorn };
8603479763SNathan Whitehorn 
8703479763SNathan Whitehorn static driver_t glc_driver = {
8803479763SNathan Whitehorn 	"glc",
8903479763SNathan Whitehorn 	glc_methods,
9003479763SNathan Whitehorn 	sizeof(struct glc_softc)
9103479763SNathan Whitehorn };
9203479763SNathan Whitehorn 
93c331b0e4SJohn Baldwin DRIVER_MODULE(glc, ps3bus, glc_driver, 0, 0);
9403479763SNathan Whitehorn 
9503479763SNathan Whitehorn static int
glc_probe(device_t dev)9603479763SNathan Whitehorn glc_probe(device_t dev)
9703479763SNathan Whitehorn {
9803479763SNathan Whitehorn 
9903479763SNathan Whitehorn 	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
10003479763SNathan Whitehorn 	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_GELIC)
10103479763SNathan Whitehorn 		return (ENXIO);
10203479763SNathan Whitehorn 
10303479763SNathan Whitehorn 	device_set_desc(dev, "Playstation 3 GELIC Network Controller");
10403479763SNathan Whitehorn 	return (BUS_PROBE_SPECIFIC);
10503479763SNathan Whitehorn }
10603479763SNathan Whitehorn 
10703479763SNathan Whitehorn static void
glc_getphys(void * xaddr,bus_dma_segment_t * segs,int nsegs,int error)10803479763SNathan Whitehorn glc_getphys(void *xaddr, bus_dma_segment_t *segs, int nsegs, int error)
10903479763SNathan Whitehorn {
11003479763SNathan Whitehorn 	if (error != 0)
11103479763SNathan Whitehorn 		return;
11203479763SNathan Whitehorn 
11303479763SNathan Whitehorn 	*(bus_addr_t *)xaddr = segs[0].ds_addr;
11403479763SNathan Whitehorn }
11503479763SNathan Whitehorn 
11603479763SNathan Whitehorn static int
glc_attach(device_t dev)11703479763SNathan Whitehorn glc_attach(device_t dev)
11803479763SNathan Whitehorn {
11903479763SNathan Whitehorn 	struct glc_softc *sc;
12003479763SNathan Whitehorn 	struct glc_txsoft *txs;
12103479763SNathan Whitehorn 	uint64_t mac64, val, junk;
12203479763SNathan Whitehorn 	int i, err;
12303479763SNathan Whitehorn 
12403479763SNathan Whitehorn 	sc = device_get_softc(dev);
12503479763SNathan Whitehorn 
12603479763SNathan Whitehorn 	sc->sc_bus = ps3bus_get_bus(dev);
12703479763SNathan Whitehorn 	sc->sc_dev = ps3bus_get_device(dev);
12803479763SNathan Whitehorn 	sc->sc_self = dev;
12903479763SNathan Whitehorn 
13003479763SNathan Whitehorn 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
13103479763SNathan Whitehorn 	    MTX_DEF);
13203479763SNathan Whitehorn 	callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
13303479763SNathan Whitehorn 	sc->next_txdma_slot = 0;
13403479763SNathan Whitehorn 	sc->bsy_txdma_slots = 0;
135044ae576SNathan Whitehorn 	sc->sc_next_rxdma_slot = 0;
13603479763SNathan Whitehorn 	sc->first_used_txdma_slot = -1;
13703479763SNathan Whitehorn 
13803479763SNathan Whitehorn 	/*
13903479763SNathan Whitehorn 	 * Shut down existing tasks.
14003479763SNathan Whitehorn 	 */
14103479763SNathan Whitehorn 
14203479763SNathan Whitehorn 	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
14303479763SNathan Whitehorn 	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
14403479763SNathan Whitehorn 
14503479763SNathan Whitehorn 	sc->sc_ifp = if_alloc(IFT_ETHER);
1463df047e3SJustin Hibbits 	if_setsoftc(sc->sc_ifp, sc);
14703479763SNathan Whitehorn 
14803479763SNathan Whitehorn 	/*
14903479763SNathan Whitehorn 	 * Get MAC address and VLAN id
15003479763SNathan Whitehorn 	 */
15103479763SNathan Whitehorn 
15203479763SNathan Whitehorn 	lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_MAC_ADDRESS,
15303479763SNathan Whitehorn 	    0, 0, 0, &mac64, &junk);
15403479763SNathan Whitehorn 	memcpy(sc->sc_enaddr, &((uint8_t *)&mac64)[2], sizeof(sc->sc_enaddr));
15503479763SNathan Whitehorn 	sc->sc_tx_vlan = sc->sc_rx_vlan = -1;
15603479763SNathan Whitehorn 	err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
15703479763SNathan Whitehorn 	    GELIC_VLAN_TX_ETHERNET, 0, 0, &val, &junk);
15803479763SNathan Whitehorn 	if (err == 0)
15903479763SNathan Whitehorn 		sc->sc_tx_vlan = val;
16003479763SNathan Whitehorn 	err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
16103479763SNathan Whitehorn 	    GELIC_VLAN_RX_ETHERNET, 0, 0, &val, &junk);
16203479763SNathan Whitehorn 	if (err == 0)
16303479763SNathan Whitehorn 		sc->sc_rx_vlan = val;
16403479763SNathan Whitehorn 
16503479763SNathan Whitehorn 	/*
16603479763SNathan Whitehorn 	 * Set up interrupt handler
16703479763SNathan Whitehorn 	 */
16803479763SNathan Whitehorn 	sc->sc_irqid = 0;
16903479763SNathan Whitehorn 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
17003479763SNathan Whitehorn 	    RF_ACTIVE);
17103479763SNathan Whitehorn 	if (sc->sc_irq == NULL) {
17203479763SNathan Whitehorn 		device_printf(dev, "Could not allocate IRQ!\n");
17303479763SNathan Whitehorn 		mtx_destroy(&sc->sc_mtx);
17403479763SNathan Whitehorn 		return (ENXIO);
17503479763SNathan Whitehorn 	}
17603479763SNathan Whitehorn 
17703479763SNathan Whitehorn 	bus_setup_intr(dev, sc->sc_irq,
178534c6b89SNathan Whitehorn 	    INTR_TYPE_NET | INTR_MPSAFE | INTR_ENTROPY,
17903479763SNathan Whitehorn 	    glc_intr_filter, glc_intr, sc, &sc->sc_irqctx);
18003479763SNathan Whitehorn 	sc->sc_hwirq_status = (uint64_t *)contigmalloc(8, M_GLC, M_ZERO, 0,
18103479763SNathan Whitehorn 	    BUS_SPACE_MAXADDR_32BIT, 8, PAGE_SIZE);
18203479763SNathan Whitehorn 	lv1_net_set_interrupt_status_indicator(sc->sc_bus, sc->sc_dev,
18303479763SNathan Whitehorn 	    vtophys(sc->sc_hwirq_status), 0);
18403479763SNathan Whitehorn 	lv1_net_set_interrupt_mask(sc->sc_bus, sc->sc_dev,
18503479763SNathan Whitehorn 	    GELIC_INT_RXDONE | GELIC_INT_RXFRAME | GELIC_INT_PHY |
18603479763SNathan Whitehorn 	    GELIC_INT_TX_CHAIN_END, 0);
18703479763SNathan Whitehorn 
18803479763SNathan Whitehorn 	/*
18903479763SNathan Whitehorn 	 * Set up DMA.
19003479763SNathan Whitehorn 	 */
19103479763SNathan Whitehorn 
19203479763SNathan Whitehorn 	err = bus_dma_tag_create(bus_get_dma_tag(dev), 32, 0,
19303479763SNathan Whitehorn 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
19403479763SNathan Whitehorn 	    129*sizeof(struct glc_dmadesc), 1, 128*sizeof(struct glc_dmadesc),
19503479763SNathan Whitehorn 	    0, NULL,NULL, &sc->sc_dmadesc_tag);
19603479763SNathan Whitehorn 
19703479763SNathan Whitehorn 	err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_txdmadesc,
19803479763SNathan Whitehorn 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
19903479763SNathan Whitehorn 	    &sc->sc_txdmadesc_map);
20003479763SNathan Whitehorn 	err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
20103479763SNathan Whitehorn 	    sc->sc_txdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
20203479763SNathan Whitehorn 	    &sc->sc_txdmadesc_phys, 0);
20303479763SNathan Whitehorn 	err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_rxdmadesc,
20403479763SNathan Whitehorn 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
20503479763SNathan Whitehorn 	    &sc->sc_rxdmadesc_map);
20603479763SNathan Whitehorn 	err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
20703479763SNathan Whitehorn 	    sc->sc_rxdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
20803479763SNathan Whitehorn 	    &sc->sc_rxdmadesc_phys, 0);
20903479763SNathan Whitehorn 
21003479763SNathan Whitehorn 	err = bus_dma_tag_create(bus_get_dma_tag(dev), 128, 0,
21103479763SNathan Whitehorn 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
21203479763SNathan Whitehorn 	    BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
21303479763SNathan Whitehorn 	    &sc->sc_rxdma_tag);
21403479763SNathan Whitehorn 	err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
21503479763SNathan Whitehorn 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
21603479763SNathan Whitehorn 	    BUS_SPACE_MAXSIZE_32BIT, 16, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
21703479763SNathan Whitehorn 	    &sc->sc_txdma_tag);
21803479763SNathan Whitehorn 
21903479763SNathan Whitehorn 	/* init transmit descriptors */
22003479763SNathan Whitehorn 	STAILQ_INIT(&sc->sc_txfreeq);
22103479763SNathan Whitehorn 	STAILQ_INIT(&sc->sc_txdirtyq);
22203479763SNathan Whitehorn 
22303479763SNathan Whitehorn 	/* create TX DMA maps */
22403479763SNathan Whitehorn 	err = ENOMEM;
22503479763SNathan Whitehorn 	for (i = 0; i < GLC_MAX_TX_PACKETS; i++) {
22603479763SNathan Whitehorn 		txs = &sc->sc_txsoft[i];
22703479763SNathan Whitehorn 		txs->txs_mbuf = NULL;
22803479763SNathan Whitehorn 		err = bus_dmamap_create(sc->sc_txdma_tag, 0, &txs->txs_dmamap);
22903479763SNathan Whitehorn 		if (err) {
23003479763SNathan Whitehorn 			device_printf(dev,
23103479763SNathan Whitehorn 			    "unable to create TX DMA map %d, error = %d\n",
23203479763SNathan Whitehorn 			    i, err);
23303479763SNathan Whitehorn 		}
23403479763SNathan Whitehorn 		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
23503479763SNathan Whitehorn 	}
23603479763SNathan Whitehorn 
23703479763SNathan Whitehorn 	/* Create the receive buffer DMA maps. */
23803479763SNathan Whitehorn 	for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
23903479763SNathan Whitehorn 		err = bus_dmamap_create(sc->sc_rxdma_tag, 0,
24003479763SNathan Whitehorn 		    &sc->sc_rxsoft[i].rxs_dmamap);
24103479763SNathan Whitehorn 		if (err) {
24203479763SNathan Whitehorn 			device_printf(dev,
24303479763SNathan Whitehorn 			    "unable to create RX DMA map %d, error = %d\n",
24403479763SNathan Whitehorn 			    i, err);
24503479763SNathan Whitehorn 		}
24603479763SNathan Whitehorn 		sc->sc_rxsoft[i].rxs_mbuf = NULL;
24703479763SNathan Whitehorn 	}
24803479763SNathan Whitehorn 
24903479763SNathan Whitehorn 	/*
25003479763SNathan Whitehorn 	 * Attach to network stack
25103479763SNathan Whitehorn 	 */
25203479763SNathan Whitehorn 
25303479763SNathan Whitehorn 	if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev));
2543df047e3SJustin Hibbits 	if_setmtu(sc->sc_ifp, ETHERMTU);
2553df047e3SJustin Hibbits 	if_setflags(sc->sc_ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
2563df047e3SJustin Hibbits 	if_sethwassist(sc->sc_ifp, CSUM_TCP | CSUM_UDP);
2573df047e3SJustin Hibbits 	if_setcapabilities(sc->sc_ifp, IFCAP_HWCSUM | IFCAP_RXCSUM);
2583df047e3SJustin Hibbits 	if_setcapenable(sc->sc_ifp, IFCAP_HWCSUM | IFCAP_RXCSUM);
2593df047e3SJustin Hibbits 	if_setstartfn(sc->sc_ifp, glc_start);
2603df047e3SJustin Hibbits 	if_setioctlfn(sc->sc_ifp, glc_ioctl);
2613df047e3SJustin Hibbits 	if_setinitfn(sc->sc_ifp, glc_init);
26203479763SNathan Whitehorn 
26303479763SNathan Whitehorn 	ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change,
26403479763SNathan Whitehorn 	    glc_media_status);
26503479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
26603479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
26703479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
26803479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
26903479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
27003479763SNathan Whitehorn 	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
27103479763SNathan Whitehorn 	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
27203479763SNathan Whitehorn 
2733df047e3SJustin Hibbits 	if_setsendqlen(sc->sc_ifp, GLC_MAX_TX_PACKETS);
2743df047e3SJustin Hibbits 	if_setsendqready(sc->sc_ifp);
27503479763SNathan Whitehorn 
27603479763SNathan Whitehorn 	ether_ifattach(sc->sc_ifp, sc->sc_enaddr);
2773df047e3SJustin Hibbits 	if_sethwassist(sc->sc_ifp, 0);
27803479763SNathan Whitehorn 
27903479763SNathan Whitehorn 	return (0);
28003479763SNathan Whitehorn 
28103479763SNathan Whitehorn 	mtx_destroy(&sc->sc_mtx);
28203479763SNathan Whitehorn 	if_free(sc->sc_ifp);
28303479763SNathan Whitehorn 	return (ENXIO);
28403479763SNathan Whitehorn }
28503479763SNathan Whitehorn 
28603479763SNathan Whitehorn static void
glc_init_locked(struct glc_softc * sc)28703479763SNathan Whitehorn glc_init_locked(struct glc_softc *sc)
28803479763SNathan Whitehorn {
28903479763SNathan Whitehorn 	int i, error;
29003479763SNathan Whitehorn 	struct glc_rxsoft *rxs;
29103479763SNathan Whitehorn 	struct glc_txsoft *txs;
29203479763SNathan Whitehorn 
29303479763SNathan Whitehorn 	mtx_assert(&sc->sc_mtx, MA_OWNED);
29403479763SNathan Whitehorn 
29503479763SNathan Whitehorn 	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
29603479763SNathan Whitehorn 	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
29703479763SNathan Whitehorn 
29803479763SNathan Whitehorn 	glc_set_multicast(sc);
29903479763SNathan Whitehorn 
30003479763SNathan Whitehorn 	for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
30103479763SNathan Whitehorn 		rxs = &sc->sc_rxsoft[i];
30203479763SNathan Whitehorn 		rxs->rxs_desc_slot = i;
30303479763SNathan Whitehorn 
30403479763SNathan Whitehorn 		if (rxs->rxs_mbuf == NULL) {
30503479763SNathan Whitehorn 			glc_add_rxbuf(sc, i);
30603479763SNathan Whitehorn 
30703479763SNathan Whitehorn 			if (rxs->rxs_mbuf == NULL) {
30803479763SNathan Whitehorn 				rxs->rxs_desc_slot = -1;
30903479763SNathan Whitehorn 				break;
31003479763SNathan Whitehorn 			}
31103479763SNathan Whitehorn 		}
31203479763SNathan Whitehorn 
31303479763SNathan Whitehorn 		glc_add_rxbuf_dma(sc, i);
31403479763SNathan Whitehorn 		bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
31503479763SNathan Whitehorn 		    BUS_DMASYNC_PREREAD);
31603479763SNathan Whitehorn 	}
31703479763SNathan Whitehorn 
31803479763SNathan Whitehorn 	/* Clear TX dirty queue */
31903479763SNathan Whitehorn 	while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
32003479763SNathan Whitehorn 		STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
32103479763SNathan Whitehorn 		bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
32203479763SNathan Whitehorn 
32303479763SNathan Whitehorn 		if (txs->txs_mbuf != NULL) {
32403479763SNathan Whitehorn 			m_freem(txs->txs_mbuf);
32503479763SNathan Whitehorn 			txs->txs_mbuf = NULL;
32603479763SNathan Whitehorn 		}
32703479763SNathan Whitehorn 
32803479763SNathan Whitehorn 		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
32903479763SNathan Whitehorn 	}
33003479763SNathan Whitehorn 	sc->first_used_txdma_slot = -1;
33103479763SNathan Whitehorn 	sc->bsy_txdma_slots = 0;
33203479763SNathan Whitehorn 
33303479763SNathan Whitehorn 	error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
33403479763SNathan Whitehorn 	    sc->sc_rxsoft[0].rxs_desc, 0);
33503479763SNathan Whitehorn 	if (error != 0)
33603479763SNathan Whitehorn 		device_printf(sc->sc_self,
33703479763SNathan Whitehorn 		    "lv1_net_start_rx_dma error: %d\n", error);
33803479763SNathan Whitehorn 
3393df047e3SJustin Hibbits 	if_setdrvflagbits(sc->sc_ifp, IFF_DRV_RUNNING, 0);
3403df047e3SJustin Hibbits 	if_setdrvflagbits(sc->sc_ifp, 0, IFF_DRV_OACTIVE);
3413df047e3SJustin Hibbits 	sc->sc_ifpflags = if_getflags(sc->sc_ifp);
34203479763SNathan Whitehorn 
34303479763SNathan Whitehorn 	sc->sc_wdog_timer = 0;
34403479763SNathan Whitehorn 	callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
34503479763SNathan Whitehorn }
34603479763SNathan Whitehorn 
34703479763SNathan Whitehorn static void
glc_stop(void * xsc)34803479763SNathan Whitehorn glc_stop(void *xsc)
34903479763SNathan Whitehorn {
35003479763SNathan Whitehorn 	struct glc_softc *sc = xsc;
35103479763SNathan Whitehorn 
35203479763SNathan Whitehorn 	mtx_assert(&sc->sc_mtx, MA_OWNED);
35303479763SNathan Whitehorn 
35403479763SNathan Whitehorn 	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
35503479763SNathan Whitehorn 	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
35603479763SNathan Whitehorn }
35703479763SNathan Whitehorn 
35803479763SNathan Whitehorn static void
glc_init(void * xsc)35903479763SNathan Whitehorn glc_init(void *xsc)
36003479763SNathan Whitehorn {
36103479763SNathan Whitehorn 	struct glc_softc *sc = xsc;
36203479763SNathan Whitehorn 
36303479763SNathan Whitehorn 	mtx_lock(&sc->sc_mtx);
36403479763SNathan Whitehorn 	glc_init_locked(sc);
36503479763SNathan Whitehorn 	mtx_unlock(&sc->sc_mtx);
36603479763SNathan Whitehorn }
36703479763SNathan Whitehorn 
36803479763SNathan Whitehorn static void
glc_tick(void * xsc)36903479763SNathan Whitehorn glc_tick(void *xsc)
37003479763SNathan Whitehorn {
37103479763SNathan Whitehorn 	struct glc_softc *sc = xsc;
37203479763SNathan Whitehorn 
37303479763SNathan Whitehorn 	mtx_assert(&sc->sc_mtx, MA_OWNED);
37403479763SNathan Whitehorn 
375044ae576SNathan Whitehorn 	/*
376044ae576SNathan Whitehorn 	 * XXX: Sometimes the RX queue gets stuck. Poke it periodically until
377044ae576SNathan Whitehorn 	 * we figure out why. This will fail harmlessly if the RX queue is
378044ae576SNathan Whitehorn 	 * already running.
379044ae576SNathan Whitehorn 	 */
380044ae576SNathan Whitehorn 	lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
381044ae576SNathan Whitehorn 	    sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
382044ae576SNathan Whitehorn 
38303479763SNathan Whitehorn 	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
38403479763SNathan Whitehorn 		callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
38503479763SNathan Whitehorn 		return;
38603479763SNathan Whitehorn 	}
38703479763SNathan Whitehorn 
38803479763SNathan Whitehorn 	/* Problems */
38903479763SNathan Whitehorn 	device_printf(sc->sc_self, "device timeout\n");
39003479763SNathan Whitehorn 
39103479763SNathan Whitehorn 	glc_init_locked(sc);
39203479763SNathan Whitehorn }
39303479763SNathan Whitehorn 
39403479763SNathan Whitehorn static void
glc_start_locked(if_t ifp)3953df047e3SJustin Hibbits glc_start_locked(if_t ifp)
39603479763SNathan Whitehorn {
3973df047e3SJustin Hibbits 	struct glc_softc *sc = if_getsoftc(ifp);
39803479763SNathan Whitehorn 	bus_addr_t first, pktdesc;
39903479763SNathan Whitehorn 	int kickstart = 0;
40003479763SNathan Whitehorn 	int error;
40103479763SNathan Whitehorn 	struct mbuf *mb_head;
40203479763SNathan Whitehorn 
40303479763SNathan Whitehorn 	mtx_assert(&sc->sc_mtx, MA_OWNED);
40403479763SNathan Whitehorn 	first = 0;
40503479763SNathan Whitehorn 
4063df047e3SJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
40703479763SNathan Whitehorn 	    IFF_DRV_RUNNING)
40803479763SNathan Whitehorn 		return;
40903479763SNathan Whitehorn 
41003479763SNathan Whitehorn 	if (STAILQ_EMPTY(&sc->sc_txdirtyq))
41103479763SNathan Whitehorn 		kickstart = 1;
41203479763SNathan Whitehorn 
4133df047e3SJustin Hibbits 	while (!if_sendq_empty(ifp)) {
4143df047e3SJustin Hibbits 		mb_head = if_dequeue(ifp);
41503479763SNathan Whitehorn 
41603479763SNathan Whitehorn 		if (mb_head == NULL)
41703479763SNathan Whitehorn 			break;
41803479763SNathan Whitehorn 
41903479763SNathan Whitehorn 		/* Check if the ring buffer is full */
42003479763SNathan Whitehorn 		if (sc->bsy_txdma_slots > 125) {
42103479763SNathan Whitehorn 			/* Put the packet back and stop */
4223df047e3SJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
4233df047e3SJustin Hibbits 			if_sendq_prepend(ifp, mb_head);
42403479763SNathan Whitehorn 			break;
42503479763SNathan Whitehorn 		}
42603479763SNathan Whitehorn 
42703479763SNathan Whitehorn 		BPF_MTAP(ifp, mb_head);
42803479763SNathan Whitehorn 
42903479763SNathan Whitehorn 		if (sc->sc_tx_vlan >= 0)
43003479763SNathan Whitehorn 			mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan);
43103479763SNathan Whitehorn 
43203479763SNathan Whitehorn 		if (glc_encap(sc, &mb_head, &pktdesc)) {
4333df047e3SJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
43403479763SNathan Whitehorn 			break;
43503479763SNathan Whitehorn 		}
43603479763SNathan Whitehorn 
43703479763SNathan Whitehorn 		if (first == 0)
43803479763SNathan Whitehorn 			first = pktdesc;
43903479763SNathan Whitehorn 	}
44003479763SNathan Whitehorn 
44103479763SNathan Whitehorn 	if (kickstart && first != 0) {
44203479763SNathan Whitehorn 		error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0);
44303479763SNathan Whitehorn 		if (error != 0)
44403479763SNathan Whitehorn 			device_printf(sc->sc_self,
44503479763SNathan Whitehorn 			    "lv1_net_start_tx_dma error: %d\n", error);
44603479763SNathan Whitehorn 		sc->sc_wdog_timer = 5;
44703479763SNathan Whitehorn 	}
44803479763SNathan Whitehorn }
44903479763SNathan Whitehorn 
45003479763SNathan Whitehorn static void
glc_start(if_t ifp)4513df047e3SJustin Hibbits glc_start(if_t ifp)
45203479763SNathan Whitehorn {
4533df047e3SJustin Hibbits 	struct glc_softc *sc = if_getsoftc(ifp);
45403479763SNathan Whitehorn 
45503479763SNathan Whitehorn 	mtx_lock(&sc->sc_mtx);
45603479763SNathan Whitehorn 	glc_start_locked(ifp);
45703479763SNathan Whitehorn 	mtx_unlock(&sc->sc_mtx);
45803479763SNathan Whitehorn }
45903479763SNathan Whitehorn 
46003479763SNathan Whitehorn static int
glc_ioctl(if_t ifp,u_long cmd,caddr_t data)4613df047e3SJustin Hibbits glc_ioctl(if_t ifp, u_long cmd, caddr_t data)
46203479763SNathan Whitehorn {
4633df047e3SJustin Hibbits 	struct glc_softc *sc = if_getsoftc(ifp);
46403479763SNathan Whitehorn 	struct ifreq *ifr = (struct ifreq *)data;
46503479763SNathan Whitehorn 	int err = 0;
46603479763SNathan Whitehorn 
46703479763SNathan Whitehorn 	switch (cmd) {
46803479763SNathan Whitehorn 	case SIOCSIFFLAGS:
46903479763SNathan Whitehorn                 mtx_lock(&sc->sc_mtx);
4703df047e3SJustin Hibbits 		if ((if_getflags(ifp) & IFF_UP) != 0) {
4713df047e3SJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0 &&
4723df047e3SJustin Hibbits 			   ((if_getflags(ifp) ^ sc->sc_ifpflags) &
47303479763SNathan Whitehorn 			    (IFF_ALLMULTI | IFF_PROMISC)) != 0)
47403479763SNathan Whitehorn 				glc_set_multicast(sc);
47503479763SNathan Whitehorn 			else
47603479763SNathan Whitehorn 				glc_init_locked(sc);
47703479763SNathan Whitehorn 		}
4783df047e3SJustin Hibbits 		else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
47903479763SNathan Whitehorn 			glc_stop(sc);
4803df047e3SJustin Hibbits 		sc->sc_ifpflags = if_getflags(ifp);
48103479763SNathan Whitehorn 		mtx_unlock(&sc->sc_mtx);
48203479763SNathan Whitehorn 		break;
48303479763SNathan Whitehorn 	case SIOCADDMULTI:
48403479763SNathan Whitehorn 	case SIOCDELMULTI:
48503479763SNathan Whitehorn                 mtx_lock(&sc->sc_mtx);
48603479763SNathan Whitehorn 		glc_set_multicast(sc);
48703479763SNathan Whitehorn                 mtx_unlock(&sc->sc_mtx);
48803479763SNathan Whitehorn 		break;
48903479763SNathan Whitehorn 	case SIOCGIFMEDIA:
49003479763SNathan Whitehorn 	case SIOCSIFMEDIA:
49103479763SNathan Whitehorn 		err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
49203479763SNathan Whitehorn 		break;
49303479763SNathan Whitehorn 	default:
49403479763SNathan Whitehorn 		err = ether_ioctl(ifp, cmd, data);
49503479763SNathan Whitehorn 		break;
49603479763SNathan Whitehorn 	}
49703479763SNathan Whitehorn 
49803479763SNathan Whitehorn 	return (err);
49903479763SNathan Whitehorn }
50003479763SNathan Whitehorn 
501d0153208SGleb Smirnoff static u_int
glc_add_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)502d0153208SGleb Smirnoff glc_add_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
503d0153208SGleb Smirnoff {
504d0153208SGleb Smirnoff 	struct glc_softc *sc = arg;
505d0153208SGleb Smirnoff 	uint64_t addr;
506d0153208SGleb Smirnoff 
507d0153208SGleb Smirnoff 	/*
508d0153208SGleb Smirnoff 	 * Filter can only hold 32 addresses, so fall back to
509d0153208SGleb Smirnoff 	 * the IFF_ALLMULTI case if we have too many. +1 is for
510d0153208SGleb Smirnoff 	 * broadcast.
511d0153208SGleb Smirnoff 	 */
512d0153208SGleb Smirnoff 	if (cnt + 1 == 32)
513d0153208SGleb Smirnoff 		return (0);
514d0153208SGleb Smirnoff 
515d0153208SGleb Smirnoff 	addr = 0;
516d0153208SGleb Smirnoff 	memcpy(&((uint8_t *)(&addr))[2], LLADDR(sdl), ETHER_ADDR_LEN);
517d0153208SGleb Smirnoff 	lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, addr, 0);
518d0153208SGleb Smirnoff 
519d0153208SGleb Smirnoff 	return (1);
520d0153208SGleb Smirnoff }
521d0153208SGleb Smirnoff 
52203479763SNathan Whitehorn static void
glc_set_multicast(struct glc_softc * sc)52303479763SNathan Whitehorn glc_set_multicast(struct glc_softc *sc)
52403479763SNathan Whitehorn {
5253df047e3SJustin Hibbits 	if_t ifp = sc->sc_ifp;
52603479763SNathan Whitehorn 	int naddrs;
52703479763SNathan Whitehorn 
52803479763SNathan Whitehorn 	/* Clear multicast filter */
52903479763SNathan Whitehorn 	lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
53003479763SNathan Whitehorn 
53103479763SNathan Whitehorn 	/* Add broadcast */
53203479763SNathan Whitehorn 	lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
53303479763SNathan Whitehorn 	    0xffffffffffffL, 0);
53403479763SNathan Whitehorn 
5353df047e3SJustin Hibbits 	if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
53603479763SNathan Whitehorn 		lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
53703479763SNathan Whitehorn 	} else {
538d0153208SGleb Smirnoff 		naddrs = if_foreach_llmaddr(ifp, glc_add_maddr, sc);
539d0153208SGleb Smirnoff 		if (naddrs + 1 == 32)
54003479763SNathan Whitehorn 			lv1_net_add_multicast_address(sc->sc_bus,
54103479763SNathan Whitehorn 			    sc->sc_dev, 0, 1);
54203479763SNathan Whitehorn 	}
54303479763SNathan Whitehorn }
54403479763SNathan Whitehorn 
54503479763SNathan Whitehorn static int
glc_add_rxbuf(struct glc_softc * sc,int idx)54603479763SNathan Whitehorn glc_add_rxbuf(struct glc_softc *sc, int idx)
54703479763SNathan Whitehorn {
54803479763SNathan Whitehorn 	struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
54903479763SNathan Whitehorn 	struct mbuf *m;
55003479763SNathan Whitehorn 	bus_dma_segment_t segs[1];
55103479763SNathan Whitehorn 	int error, nsegs;
55203479763SNathan Whitehorn 
553eb1b1807SGleb Smirnoff 	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
55403479763SNathan Whitehorn 	if (m == NULL)
55503479763SNathan Whitehorn 		return (ENOBUFS);
55603479763SNathan Whitehorn 	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
55703479763SNathan Whitehorn 
55803479763SNathan Whitehorn 	if (rxs->rxs_mbuf != NULL) {
55903479763SNathan Whitehorn 		bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap,
56003479763SNathan Whitehorn 		    BUS_DMASYNC_POSTREAD);
56103479763SNathan Whitehorn 		bus_dmamap_unload(sc->sc_rxdma_tag, rxs->rxs_dmamap);
56203479763SNathan Whitehorn 	}
56303479763SNathan Whitehorn 
56403479763SNathan Whitehorn 	error = bus_dmamap_load_mbuf_sg(sc->sc_rxdma_tag, rxs->rxs_dmamap, m,
56503479763SNathan Whitehorn 	    segs, &nsegs, BUS_DMA_NOWAIT);
56603479763SNathan Whitehorn 	if (error != 0) {
56703479763SNathan Whitehorn 		device_printf(sc->sc_self,
56803479763SNathan Whitehorn 		    "cannot load RS DMA map %d, error = %d\n", idx, error);
56903479763SNathan Whitehorn 		m_freem(m);
57003479763SNathan Whitehorn 		return (error);
57103479763SNathan Whitehorn 	}
57203479763SNathan Whitehorn 	/* If nsegs is wrong then the stack is corrupt. */
57303479763SNathan Whitehorn 	KASSERT(nsegs == 1,
57403479763SNathan Whitehorn 	    ("%s: too many DMA segments (%d)", __func__, nsegs));
57503479763SNathan Whitehorn 	rxs->rxs_mbuf = m;
57603479763SNathan Whitehorn 	rxs->segment = segs[0];
57703479763SNathan Whitehorn 
57803479763SNathan Whitehorn 	bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD);
57903479763SNathan Whitehorn 
58003479763SNathan Whitehorn 	return (0);
58103479763SNathan Whitehorn }
58203479763SNathan Whitehorn 
58303479763SNathan Whitehorn static int
glc_add_rxbuf_dma(struct glc_softc * sc,int idx)58403479763SNathan Whitehorn glc_add_rxbuf_dma(struct glc_softc *sc, int idx)
58503479763SNathan Whitehorn {
58603479763SNathan Whitehorn 	struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
58703479763SNathan Whitehorn 
58803479763SNathan Whitehorn 	bzero(&sc->sc_rxdmadesc[idx], sizeof(sc->sc_rxdmadesc[idx]));
58903479763SNathan Whitehorn 	sc->sc_rxdmadesc[idx].paddr = rxs->segment.ds_addr;
59003479763SNathan Whitehorn 	sc->sc_rxdmadesc[idx].len = rxs->segment.ds_len;
59103479763SNathan Whitehorn 	sc->sc_rxdmadesc[idx].next = sc->sc_rxdmadesc_phys +
59203479763SNathan Whitehorn 	    ((idx + 1) % GLC_MAX_RX_PACKETS)*sizeof(sc->sc_rxdmadesc[idx]);
59303479763SNathan Whitehorn 	sc->sc_rxdmadesc[idx].cmd_stat = GELIC_DESCR_OWNED;
59403479763SNathan Whitehorn 
59503479763SNathan Whitehorn 	rxs->rxs_desc_slot = idx;
59603479763SNathan Whitehorn 	rxs->rxs_desc = sc->sc_rxdmadesc_phys + idx*sizeof(struct glc_dmadesc);
59703479763SNathan Whitehorn 
59803479763SNathan Whitehorn         return (0);
59903479763SNathan Whitehorn }
60003479763SNathan Whitehorn 
60103479763SNathan Whitehorn static int
glc_encap(struct glc_softc * sc,struct mbuf ** m_head,bus_addr_t * pktdesc)60203479763SNathan Whitehorn glc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc)
60303479763SNathan Whitehorn {
60403479763SNathan Whitehorn 	bus_dma_segment_t segs[16];
60503479763SNathan Whitehorn 	struct glc_txsoft *txs;
60603479763SNathan Whitehorn 	struct mbuf *m;
60703479763SNathan Whitehorn 	bus_addr_t firstslotphys;
60803479763SNathan Whitehorn 	int i, idx, nsegs, nsegs_max;
60903479763SNathan Whitehorn 	int err = 0;
61003479763SNathan Whitehorn 
61103479763SNathan Whitehorn 	/* Max number of segments is the number of free DMA slots */
61203479763SNathan Whitehorn 	nsegs_max = 128 - sc->bsy_txdma_slots;
61303479763SNathan Whitehorn 
61403479763SNathan Whitehorn 	if (nsegs_max > 16 || sc->first_used_txdma_slot < 0)
61503479763SNathan Whitehorn 		nsegs_max = 16;
61603479763SNathan Whitehorn 
61703479763SNathan Whitehorn 	/* Get a work queue entry. */
61803479763SNathan Whitehorn 	if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
61903479763SNathan Whitehorn 		/* Ran out of descriptors. */
62003479763SNathan Whitehorn 		return (ENOBUFS);
62103479763SNathan Whitehorn 	}
62203479763SNathan Whitehorn 
62303479763SNathan Whitehorn 	nsegs = 0;
62403479763SNathan Whitehorn 	for (m = *m_head; m != NULL; m = m->m_next)
62503479763SNathan Whitehorn 		nsegs++;
62603479763SNathan Whitehorn 
62703479763SNathan Whitehorn 	if (nsegs > nsegs_max) {
628eb1b1807SGleb Smirnoff 		m = m_collapse(*m_head, M_NOWAIT, nsegs_max);
62903479763SNathan Whitehorn 		if (m == NULL) {
63003479763SNathan Whitehorn 			m_freem(*m_head);
63103479763SNathan Whitehorn 			*m_head = NULL;
63203479763SNathan Whitehorn 			return (ENOBUFS);
63303479763SNathan Whitehorn 		}
63403479763SNathan Whitehorn 		*m_head = m;
63503479763SNathan Whitehorn 	}
63603479763SNathan Whitehorn 
63703479763SNathan Whitehorn 	err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
63803479763SNathan Whitehorn 	    *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
63903479763SNathan Whitehorn 	if (err != 0) {
64003479763SNathan Whitehorn 		m_freem(*m_head);
64103479763SNathan Whitehorn 		*m_head = NULL;
64203479763SNathan Whitehorn 		return (err);
64303479763SNathan Whitehorn 	}
64403479763SNathan Whitehorn 
64503479763SNathan Whitehorn 	KASSERT(nsegs <= 128 - sc->bsy_txdma_slots,
64603479763SNathan Whitehorn 	    ("GLC: Mapped too many (%d) DMA segments with %d available",
64703479763SNathan Whitehorn 	    nsegs, 128 - sc->bsy_txdma_slots));
64803479763SNathan Whitehorn 
64903479763SNathan Whitehorn 	if (nsegs == 0) {
65003479763SNathan Whitehorn 		m_freem(*m_head);
65103479763SNathan Whitehorn 		*m_head = NULL;
65203479763SNathan Whitehorn 		return (EIO);
65303479763SNathan Whitehorn 	}
65403479763SNathan Whitehorn 
65503479763SNathan Whitehorn 	txs->txs_ndescs = nsegs;
65603479763SNathan Whitehorn 	txs->txs_firstdesc = sc->next_txdma_slot;
65703479763SNathan Whitehorn 
65803479763SNathan Whitehorn 	idx = txs->txs_firstdesc;
65903479763SNathan Whitehorn 	firstslotphys = sc->sc_txdmadesc_phys +
66003479763SNathan Whitehorn 	    txs->txs_firstdesc*sizeof(struct glc_dmadesc);
66103479763SNathan Whitehorn 
66203479763SNathan Whitehorn 	for (i = 0; i < nsegs; i++) {
66303479763SNathan Whitehorn 		bzero(&sc->sc_txdmadesc[idx], sizeof(sc->sc_txdmadesc[idx]));
66403479763SNathan Whitehorn 		sc->sc_txdmadesc[idx].paddr = segs[i].ds_addr;
66503479763SNathan Whitehorn 		sc->sc_txdmadesc[idx].len = segs[i].ds_len;
66603479763SNathan Whitehorn 		sc->sc_txdmadesc[idx].next = sc->sc_txdmadesc_phys +
66703479763SNathan Whitehorn 		    ((idx + 1) % GLC_MAX_TX_PACKETS)*sizeof(struct glc_dmadesc);
66803479763SNathan Whitehorn 		sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_NOIPSEC;
66903479763SNathan Whitehorn 
67003479763SNathan Whitehorn 		if (i+1 == nsegs) {
67103479763SNathan Whitehorn 			txs->txs_lastdesc = idx;
67203479763SNathan Whitehorn 			sc->sc_txdmadesc[idx].next = 0;
67303479763SNathan Whitehorn 			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST;
67403479763SNathan Whitehorn 		}
67503479763SNathan Whitehorn 
67603479763SNathan Whitehorn 		if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
67703479763SNathan Whitehorn 			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP;
67803479763SNathan Whitehorn 		if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
67903479763SNathan Whitehorn 			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP;
68003479763SNathan Whitehorn 		sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED;
68103479763SNathan Whitehorn 
68203479763SNathan Whitehorn 		idx = (idx + 1) % GLC_MAX_TX_PACKETS;
68303479763SNathan Whitehorn 	}
68403479763SNathan Whitehorn 	sc->next_txdma_slot = idx;
68503479763SNathan Whitehorn 	sc->bsy_txdma_slots += nsegs;
68603479763SNathan Whitehorn 	if (txs->txs_firstdesc != 0)
68703479763SNathan Whitehorn 		idx = txs->txs_firstdesc - 1;
68803479763SNathan Whitehorn 	else
68903479763SNathan Whitehorn 		idx = GLC_MAX_TX_PACKETS - 1;
69003479763SNathan Whitehorn 
69103479763SNathan Whitehorn 	if (sc->first_used_txdma_slot < 0)
69203479763SNathan Whitehorn 		sc->first_used_txdma_slot = txs->txs_firstdesc;
69303479763SNathan Whitehorn 
69403479763SNathan Whitehorn 	bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap,
69503479763SNathan Whitehorn 	    BUS_DMASYNC_PREWRITE);
69603479763SNathan Whitehorn 	sc->sc_txdmadesc[idx].next = firstslotphys;
69703479763SNathan Whitehorn 
69803479763SNathan Whitehorn 	STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q);
69903479763SNathan Whitehorn 	STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q);
70003479763SNathan Whitehorn 	txs->txs_mbuf = *m_head;
70103479763SNathan Whitehorn 	*pktdesc = firstslotphys;
70203479763SNathan Whitehorn 
70303479763SNathan Whitehorn 	return (0);
70403479763SNathan Whitehorn }
70503479763SNathan Whitehorn 
70603479763SNathan Whitehorn static void
glc_rxintr(struct glc_softc * sc)70703479763SNathan Whitehorn glc_rxintr(struct glc_softc *sc)
70803479763SNathan Whitehorn {
70903479763SNathan Whitehorn 	int i, restart_rxdma, error;
71003479763SNathan Whitehorn 	struct mbuf *m;
7113df047e3SJustin Hibbits 	if_t ifp = sc->sc_ifp;
71203479763SNathan Whitehorn 
71303479763SNathan Whitehorn 	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
714044ae576SNathan Whitehorn 	    BUS_DMASYNC_POSTREAD);
71503479763SNathan Whitehorn 
71603479763SNathan Whitehorn 	restart_rxdma = 0;
71703479763SNathan Whitehorn 	while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat &
71803479763SNathan Whitehorn 	   GELIC_DESCR_OWNED) == 0) {
71903479763SNathan Whitehorn 		i = sc->sc_next_rxdma_slot;
720044ae576SNathan Whitehorn 		sc->sc_next_rxdma_slot++;
721044ae576SNathan Whitehorn 		if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS)
722044ae576SNathan Whitehorn 			sc->sc_next_rxdma_slot = 0;
723044ae576SNathan Whitehorn 
724044ae576SNathan Whitehorn 		if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END)
725044ae576SNathan Whitehorn 			restart_rxdma = 1;
726044ae576SNathan Whitehorn 
72703479763SNathan Whitehorn 		if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) {
728e2efa70eSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
72903479763SNathan Whitehorn 			goto requeue;
73003479763SNathan Whitehorn 		}
73103479763SNathan Whitehorn 
73203479763SNathan Whitehorn 		m = sc->sc_rxsoft[i].rxs_mbuf;
73303479763SNathan Whitehorn 		if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) {
73403479763SNathan Whitehorn 			m->m_pkthdr.csum_flags |=
73503479763SNathan Whitehorn 			    CSUM_IP_CHECKED | CSUM_IP_VALID;
73603479763SNathan Whitehorn 		}
73703479763SNathan Whitehorn 		if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) {
73803479763SNathan Whitehorn 			m->m_pkthdr.csum_flags |=
73903479763SNathan Whitehorn 			    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
74003479763SNathan Whitehorn 			m->m_pkthdr.csum_data = 0xffff;
74103479763SNathan Whitehorn 		}
74203479763SNathan Whitehorn 
74303479763SNathan Whitehorn 		if (glc_add_rxbuf(sc, i)) {
744e2efa70eSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
74503479763SNathan Whitehorn 			goto requeue;
74603479763SNathan Whitehorn 		}
74703479763SNathan Whitehorn 
748e2efa70eSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
74903479763SNathan Whitehorn 		m->m_pkthdr.rcvif = ifp;
75003479763SNathan Whitehorn 		m->m_len = sc->sc_rxdmadesc[i].valid_size;
75103479763SNathan Whitehorn 		m->m_pkthdr.len = m->m_len;
75203479763SNathan Whitehorn 
753534c6b89SNathan Whitehorn 		/*
754534c6b89SNathan Whitehorn 		 * Remove VLAN tag. Even on early firmwares that do not allow
755534c6b89SNathan Whitehorn 		 * multiple VLANs, the VLAN tag is still in place here.
756534c6b89SNathan Whitehorn 		 */
75703479763SNathan Whitehorn 		m_adj(m, 2);
75803479763SNathan Whitehorn 
75903479763SNathan Whitehorn 		mtx_unlock(&sc->sc_mtx);
7603df047e3SJustin Hibbits 		if_input(ifp, m);
76103479763SNathan Whitehorn 		mtx_lock(&sc->sc_mtx);
76203479763SNathan Whitehorn 
76303479763SNathan Whitehorn 	    requeue:
76403479763SNathan Whitehorn 		glc_add_rxbuf_dma(sc, i);
765044ae576SNathan Whitehorn 	}
766044ae576SNathan Whitehorn 
767044ae576SNathan Whitehorn 	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
768044ae576SNathan Whitehorn 	    BUS_DMASYNC_PREWRITE);
769044ae576SNathan Whitehorn 
77003479763SNathan Whitehorn 	if (restart_rxdma) {
77103479763SNathan Whitehorn 		error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
772044ae576SNathan Whitehorn 		    sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
77303479763SNathan Whitehorn 		if (error != 0)
77403479763SNathan Whitehorn 			device_printf(sc->sc_self,
77503479763SNathan Whitehorn 			    "lv1_net_start_rx_dma error: %d\n", error);
77603479763SNathan Whitehorn 	}
77703479763SNathan Whitehorn }
77803479763SNathan Whitehorn 
77903479763SNathan Whitehorn static void
glc_txintr(struct glc_softc * sc)78003479763SNathan Whitehorn glc_txintr(struct glc_softc *sc)
78103479763SNathan Whitehorn {
7823df047e3SJustin Hibbits 	if_t ifp = sc->sc_ifp;
78303479763SNathan Whitehorn 	struct glc_txsoft *txs;
78403479763SNathan Whitehorn 	int progress = 0, kickstart = 0, error;
78503479763SNathan Whitehorn 
786044ae576SNathan Whitehorn 	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
787044ae576SNathan Whitehorn 	    BUS_DMASYNC_POSTREAD);
788044ae576SNathan Whitehorn 
78903479763SNathan Whitehorn 	while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
79003479763SNathan Whitehorn 		if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat
79103479763SNathan Whitehorn 		    & GELIC_DESCR_OWNED)
79203479763SNathan Whitehorn 			break;
79303479763SNathan Whitehorn 
79403479763SNathan Whitehorn 		STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
79503479763SNathan Whitehorn 		bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
79603479763SNathan Whitehorn 		sc->bsy_txdma_slots -= txs->txs_ndescs;
79703479763SNathan Whitehorn 
79803479763SNathan Whitehorn 		if (txs->txs_mbuf != NULL) {
79903479763SNathan Whitehorn 			m_freem(txs->txs_mbuf);
80003479763SNathan Whitehorn 			txs->txs_mbuf = NULL;
80103479763SNathan Whitehorn 		}
80203479763SNathan Whitehorn 
80303479763SNathan Whitehorn 		if ((sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & 0xf0000000)
80403479763SNathan Whitehorn 		    != 0) {
80503479763SNathan Whitehorn 			lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
80603479763SNathan Whitehorn 			kickstart = 1;
807e2efa70eSGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
80803479763SNathan Whitehorn 		}
80903479763SNathan Whitehorn 
81003479763SNathan Whitehorn 		if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat &
81103479763SNathan Whitehorn 		    GELIC_CMDSTAT_CHAIN_END)
81203479763SNathan Whitehorn 			kickstart = 1;
81303479763SNathan Whitehorn 
81403479763SNathan Whitehorn 		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
815e2efa70eSGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
81603479763SNathan Whitehorn 		progress = 1;
81703479763SNathan Whitehorn 	}
81803479763SNathan Whitehorn 
81903479763SNathan Whitehorn 	if (txs != NULL)
82003479763SNathan Whitehorn 		sc->first_used_txdma_slot = txs->txs_firstdesc;
82103479763SNathan Whitehorn 	else
82203479763SNathan Whitehorn 		sc->first_used_txdma_slot = -1;
82303479763SNathan Whitehorn 
824044ae576SNathan Whitehorn 	if (kickstart || txs != NULL) {
825044ae576SNathan Whitehorn 		/* Speculatively (or necessarily) start the TX queue again */
82603479763SNathan Whitehorn 		error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev,
82703479763SNathan Whitehorn 		    sc->sc_txdmadesc_phys +
828f638d505SNathan Whitehorn 		    ((txs == NULL) ? 0 : txs->txs_firstdesc)*
829f638d505SNathan Whitehorn 		     sizeof(struct glc_dmadesc), 0);
83003479763SNathan Whitehorn 		if (error != 0)
83103479763SNathan Whitehorn 			device_printf(sc->sc_self,
83203479763SNathan Whitehorn 			    "lv1_net_start_tx_dma error: %d\n", error);
83303479763SNathan Whitehorn 	}
83403479763SNathan Whitehorn 
83503479763SNathan Whitehorn 	if (progress) {
83603479763SNathan Whitehorn 		/*
83703479763SNathan Whitehorn 		 * We freed some descriptors, so reset IFF_DRV_OACTIVE
83803479763SNathan Whitehorn 		 * and restart.
83903479763SNathan Whitehorn 		 */
8403df047e3SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
84103479763SNathan Whitehorn 		sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
84203479763SNathan Whitehorn 
8433df047e3SJustin Hibbits 		if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) &&
8443df047e3SJustin Hibbits 		    !if_sendq_empty(ifp))
84503479763SNathan Whitehorn 			glc_start_locked(ifp);
84603479763SNathan Whitehorn 	}
84703479763SNathan Whitehorn }
84803479763SNathan Whitehorn 
84903479763SNathan Whitehorn static int
glc_intr_filter(void * xsc)85003479763SNathan Whitehorn glc_intr_filter(void *xsc)
85103479763SNathan Whitehorn {
85203479763SNathan Whitehorn 	struct glc_softc *sc = xsc;
85303479763SNathan Whitehorn 
85403479763SNathan Whitehorn 	powerpc_sync();
85503479763SNathan Whitehorn 	atomic_set_64(&sc->sc_interrupt_status, *sc->sc_hwirq_status);
85603479763SNathan Whitehorn 	return (FILTER_SCHEDULE_THREAD);
85703479763SNathan Whitehorn }
85803479763SNathan Whitehorn 
85903479763SNathan Whitehorn static void
glc_intr(void * xsc)86003479763SNathan Whitehorn glc_intr(void *xsc)
86103479763SNathan Whitehorn {
86203479763SNathan Whitehorn 	struct glc_softc *sc = xsc;
86303479763SNathan Whitehorn 	uint64_t status, linkstat, junk;
86403479763SNathan Whitehorn 
86503479763SNathan Whitehorn 	mtx_lock(&sc->sc_mtx);
86603479763SNathan Whitehorn 
86703479763SNathan Whitehorn 	status = atomic_readandclear_64(&sc->sc_interrupt_status);
86803479763SNathan Whitehorn 
86903479763SNathan Whitehorn 	if (status == 0) {
87003479763SNathan Whitehorn 		mtx_unlock(&sc->sc_mtx);
87103479763SNathan Whitehorn 		return;
87203479763SNathan Whitehorn 	}
87303479763SNathan Whitehorn 
87403479763SNathan Whitehorn 	if (status & (GELIC_INT_RXDONE | GELIC_INT_RXFRAME))
87503479763SNathan Whitehorn 		glc_rxintr(sc);
87603479763SNathan Whitehorn 
87703479763SNathan Whitehorn 	if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END))
87803479763SNathan Whitehorn 		glc_txintr(sc);
87903479763SNathan Whitehorn 
88003479763SNathan Whitehorn 	if (status & GELIC_INT_PHY) {
88103479763SNathan Whitehorn 		lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
88203479763SNathan Whitehorn 		    GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk);
88303479763SNathan Whitehorn 
88403479763SNathan Whitehorn 		linkstat = (linkstat & GELIC_LINK_UP) ?
88503479763SNathan Whitehorn 		    LINK_STATE_UP : LINK_STATE_DOWN;
88603479763SNathan Whitehorn 		if_link_state_change(sc->sc_ifp, linkstat);
88703479763SNathan Whitehorn 	}
88803479763SNathan Whitehorn 
88903479763SNathan Whitehorn 	mtx_unlock(&sc->sc_mtx);
89003479763SNathan Whitehorn }
89103479763SNathan Whitehorn 
89203479763SNathan Whitehorn static void
glc_media_status(if_t ifp,struct ifmediareq * ifmr)8933df047e3SJustin Hibbits glc_media_status(if_t ifp, struct ifmediareq *ifmr)
89403479763SNathan Whitehorn {
8953df047e3SJustin Hibbits 	struct glc_softc *sc = if_getsoftc(ifp);
89603479763SNathan Whitehorn 	uint64_t status, junk;
89703479763SNathan Whitehorn 
89803479763SNathan Whitehorn 	ifmr->ifm_status = IFM_AVALID;
89903479763SNathan Whitehorn 	ifmr->ifm_active = IFM_ETHER;
90003479763SNathan Whitehorn 
90103479763SNathan Whitehorn 	lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
90203479763SNathan Whitehorn 	    GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk);
90303479763SNathan Whitehorn 
90403479763SNathan Whitehorn 	if (status & GELIC_LINK_UP)
90503479763SNathan Whitehorn 		ifmr->ifm_status |= IFM_ACTIVE;
90603479763SNathan Whitehorn 
90703479763SNathan Whitehorn 	if (status & GELIC_SPEED_10)
90803479763SNathan Whitehorn 		ifmr->ifm_active |= IFM_10_T;
90903479763SNathan Whitehorn 	else if (status & GELIC_SPEED_100)
91003479763SNathan Whitehorn 		ifmr->ifm_active |= IFM_100_TX;
91103479763SNathan Whitehorn 	else if (status & GELIC_SPEED_1000)
91203479763SNathan Whitehorn 		ifmr->ifm_active |= IFM_1000_T;
91303479763SNathan Whitehorn 
91403479763SNathan Whitehorn 	if (status & GELIC_FULL_DUPLEX)
91503479763SNathan Whitehorn 		ifmr->ifm_active |= IFM_FDX;
91603479763SNathan Whitehorn 	else
91703479763SNathan Whitehorn 		ifmr->ifm_active |= IFM_HDX;
91803479763SNathan Whitehorn }
91903479763SNathan Whitehorn 
92003479763SNathan Whitehorn static int
glc_media_change(if_t ifp)9213df047e3SJustin Hibbits glc_media_change(if_t ifp)
92203479763SNathan Whitehorn {
9233df047e3SJustin Hibbits 	struct glc_softc *sc = if_getsoftc(ifp);
92403479763SNathan Whitehorn 	uint64_t mode, junk;
92503479763SNathan Whitehorn 	int result;
92603479763SNathan Whitehorn 
92703479763SNathan Whitehorn 	if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
92803479763SNathan Whitehorn 		return (EINVAL);
92903479763SNathan Whitehorn 
93003479763SNathan Whitehorn 	switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
93103479763SNathan Whitehorn 	case IFM_AUTO:
93203479763SNathan Whitehorn 		mode = GELIC_AUTO_NEG;
93303479763SNathan Whitehorn 		break;
93403479763SNathan Whitehorn 	case IFM_10_T:
93503479763SNathan Whitehorn 		mode = GELIC_SPEED_10;
93603479763SNathan Whitehorn 		break;
93703479763SNathan Whitehorn 	case IFM_100_TX:
93803479763SNathan Whitehorn 		mode = GELIC_SPEED_100;
93903479763SNathan Whitehorn 		break;
94003479763SNathan Whitehorn 	case IFM_1000_T:
94103479763SNathan Whitehorn 		mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX;
94203479763SNathan Whitehorn 		break;
94303479763SNathan Whitehorn 	default:
94403479763SNathan Whitehorn 		return (EINVAL);
94503479763SNathan Whitehorn 	}
94603479763SNathan Whitehorn 
94703479763SNathan Whitehorn 	if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX)
94803479763SNathan Whitehorn 		mode |= GELIC_FULL_DUPLEX;
94903479763SNathan Whitehorn 
95003479763SNathan Whitehorn 	result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE,
95103479763SNathan Whitehorn 	    GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk);
95203479763SNathan Whitehorn 
95303479763SNathan Whitehorn 	return (result ? EIO : 0);
95403479763SNathan Whitehorn }
955