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