1217044Snwhitehorn/*-
2217044Snwhitehorn * Copyright (C) 2010 Nathan Whitehorn
3217044Snwhitehorn * All rights reserved.
4217044Snwhitehorn *
5217044Snwhitehorn * Redistribution and use in source and binary forms, with or without
6217044Snwhitehorn * modification, are permitted provided that the following conditions
7217044Snwhitehorn * are met:
8217044Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9217044Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10217044Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11217044Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12217044Snwhitehorn *    documentation and/or other materials provided with the distribution.
13217044Snwhitehorn *
14217044Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15217044Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16217044Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17217044Snwhitehorn * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18217044Snwhitehorn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19217044Snwhitehorn * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20217044Snwhitehorn * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21217044Snwhitehorn * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22217044Snwhitehorn * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23217044Snwhitehorn * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24217044Snwhitehorn *
25217044Snwhitehorn * $FreeBSD$
26217044Snwhitehorn */
27217044Snwhitehorn
28217044Snwhitehorn#include <sys/param.h>
29217044Snwhitehorn#include <sys/systm.h>
30217044Snwhitehorn#include <sys/sockio.h>
31217044Snwhitehorn#include <sys/endian.h>
32257324Sglebius#include <sys/lock.h>
33217044Snwhitehorn#include <sys/mbuf.h>
34217044Snwhitehorn#include <sys/module.h>
35217044Snwhitehorn#include <sys/malloc.h>
36257324Sglebius#include <sys/mutex.h>
37217044Snwhitehorn#include <sys/kernel.h>
38217044Snwhitehorn#include <sys/socket.h>
39217044Snwhitehorn
40217044Snwhitehorn#include <vm/vm.h>
41217044Snwhitehorn#include <vm/pmap.h>
42217044Snwhitehorn
43217044Snwhitehorn#include <net/bpf.h>
44217044Snwhitehorn#include <net/if.h>
45257324Sglebius#include <net/if_var.h>
46217044Snwhitehorn#include <net/ethernet.h>
47217044Snwhitehorn#include <net/if_media.h>
48217044Snwhitehorn#include <net/if_types.h>
49257324Sglebius#include <net/if_dl.h>
50217044Snwhitehorn
51217044Snwhitehorn#include <machine/pio.h>
52217044Snwhitehorn#include <machine/bus.h>
53217044Snwhitehorn#include <machine/platform.h>
54217044Snwhitehorn#include <machine/resource.h>
55217044Snwhitehorn#include <sys/bus.h>
56217044Snwhitehorn#include <sys/rman.h>
57217044Snwhitehorn
58217044Snwhitehorn#include "ps3bus.h"
59217044Snwhitehorn#include "ps3-hvcall.h"
60217044Snwhitehorn#include "if_glcreg.h"
61217044Snwhitehorn
62217044Snwhitehornstatic int	glc_probe(device_t);
63217044Snwhitehornstatic int	glc_attach(device_t);
64217044Snwhitehornstatic void	glc_init(void *xsc);
65217044Snwhitehornstatic void	glc_start(struct ifnet *ifp);
66217044Snwhitehornstatic int	glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
67217044Snwhitehornstatic void	glc_set_multicast(struct glc_softc *sc);
68217044Snwhitehornstatic int	glc_add_rxbuf(struct glc_softc *sc, int idx);
69217044Snwhitehornstatic int	glc_add_rxbuf_dma(struct glc_softc *sc, int idx);
70217044Snwhitehornstatic int	glc_encap(struct glc_softc *sc, struct mbuf **m_head,
71217044Snwhitehorn		    bus_addr_t *pktdesc);
72217044Snwhitehornstatic int	glc_intr_filter(void *xsc);
73217044Snwhitehornstatic void	glc_intr(void *xsc);
74217044Snwhitehornstatic void	glc_tick(void *xsc);
75217044Snwhitehornstatic void	glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
76217044Snwhitehornstatic int	glc_media_change(struct ifnet *ifp);
77217044Snwhitehorn
78217044Snwhitehornstatic MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
79217044Snwhitehorn
80217044Snwhitehornstatic device_method_t glc_methods[] = {
81217044Snwhitehorn	/* Device interface */
82217044Snwhitehorn	DEVMETHOD(device_probe,		glc_probe),
83217044Snwhitehorn	DEVMETHOD(device_attach,	glc_attach),
84217044Snwhitehorn
85217044Snwhitehorn	{ 0, 0 }
86217044Snwhitehorn};
87217044Snwhitehorn
88217044Snwhitehornstatic driver_t glc_driver = {
89217044Snwhitehorn	"glc",
90217044Snwhitehorn	glc_methods,
91217044Snwhitehorn	sizeof(struct glc_softc)
92217044Snwhitehorn};
93217044Snwhitehorn
94217044Snwhitehornstatic devclass_t glc_devclass;
95217044Snwhitehorn
96217044SnwhitehornDRIVER_MODULE(glc, ps3bus, glc_driver, glc_devclass, 0, 0);
97217044Snwhitehorn
98217044Snwhitehornstatic int
99217044Snwhitehornglc_probe(device_t dev)
100217044Snwhitehorn{
101217044Snwhitehorn
102217044Snwhitehorn	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_SYSBUS ||
103217044Snwhitehorn	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_GELIC)
104217044Snwhitehorn		return (ENXIO);
105217044Snwhitehorn
106217044Snwhitehorn	device_set_desc(dev, "Playstation 3 GELIC Network Controller");
107217044Snwhitehorn	return (BUS_PROBE_SPECIFIC);
108217044Snwhitehorn}
109217044Snwhitehorn
110217044Snwhitehornstatic void
111217044Snwhitehornglc_getphys(void *xaddr, bus_dma_segment_t *segs, int nsegs, int error)
112217044Snwhitehorn{
113217044Snwhitehorn	if (error != 0)
114217044Snwhitehorn		return;
115217044Snwhitehorn
116217044Snwhitehorn	*(bus_addr_t *)xaddr = segs[0].ds_addr;
117217044Snwhitehorn}
118217044Snwhitehorn
119217044Snwhitehornstatic int
120217044Snwhitehornglc_attach(device_t dev)
121217044Snwhitehorn{
122217044Snwhitehorn	struct glc_softc *sc;
123217044Snwhitehorn	struct glc_txsoft *txs;
124217044Snwhitehorn	uint64_t mac64, val, junk;
125217044Snwhitehorn	int i, err;
126217044Snwhitehorn
127217044Snwhitehorn	sc = device_get_softc(dev);
128217044Snwhitehorn
129217044Snwhitehorn	sc->sc_bus = ps3bus_get_bus(dev);
130217044Snwhitehorn	sc->sc_dev = ps3bus_get_device(dev);
131217044Snwhitehorn	sc->sc_self = dev;
132217044Snwhitehorn
133217044Snwhitehorn	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
134217044Snwhitehorn	    MTX_DEF);
135217044Snwhitehorn	callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
136217044Snwhitehorn	sc->next_txdma_slot = 0;
137217044Snwhitehorn	sc->bsy_txdma_slots = 0;
138223324Snwhitehorn	sc->sc_next_rxdma_slot = 0;
139217044Snwhitehorn	sc->first_used_txdma_slot = -1;
140217044Snwhitehorn
141217044Snwhitehorn	/*
142217044Snwhitehorn	 * Shut down existing tasks.
143217044Snwhitehorn	 */
144217044Snwhitehorn
145217044Snwhitehorn	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
146217044Snwhitehorn	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
147217044Snwhitehorn
148217044Snwhitehorn	sc->sc_ifp = if_alloc(IFT_ETHER);
149217044Snwhitehorn	sc->sc_ifp->if_softc = sc;
150217044Snwhitehorn
151217044Snwhitehorn	/*
152217044Snwhitehorn	 * Get MAC address and VLAN id
153217044Snwhitehorn	 */
154217044Snwhitehorn
155217044Snwhitehorn	lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_MAC_ADDRESS,
156217044Snwhitehorn	    0, 0, 0, &mac64, &junk);
157217044Snwhitehorn	memcpy(sc->sc_enaddr, &((uint8_t *)&mac64)[2], sizeof(sc->sc_enaddr));
158223792Snwhitehorn	sc->sc_tx_vlan = sc->sc_rx_vlan = -1;
159217044Snwhitehorn	err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
160217044Snwhitehorn	    GELIC_VLAN_TX_ETHERNET, 0, 0, &val, &junk);
161217044Snwhitehorn	if (err == 0)
162217044Snwhitehorn		sc->sc_tx_vlan = val;
163217044Snwhitehorn	err = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_VLAN_ID,
164217044Snwhitehorn	    GELIC_VLAN_RX_ETHERNET, 0, 0, &val, &junk);
165217044Snwhitehorn	if (err == 0)
166217044Snwhitehorn		sc->sc_rx_vlan = val;
167217044Snwhitehorn
168217044Snwhitehorn	/*
169217044Snwhitehorn	 * Set up interrupt handler
170217044Snwhitehorn	 */
171217044Snwhitehorn	sc->sc_irqid = 0;
172217044Snwhitehorn	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
173217044Snwhitehorn	    RF_ACTIVE);
174217044Snwhitehorn	if (sc->sc_irq == NULL) {
175217044Snwhitehorn		device_printf(dev, "Could not allocate IRQ!\n");
176217044Snwhitehorn		mtx_destroy(&sc->sc_mtx);
177217044Snwhitehorn		return (ENXIO);
178217044Snwhitehorn	}
179217044Snwhitehorn
180217044Snwhitehorn	bus_setup_intr(dev, sc->sc_irq,
181223792Snwhitehorn	    INTR_TYPE_NET | INTR_MPSAFE | INTR_ENTROPY,
182217044Snwhitehorn	    glc_intr_filter, glc_intr, sc, &sc->sc_irqctx);
183217044Snwhitehorn	sc->sc_hwirq_status = (uint64_t *)contigmalloc(8, M_GLC, M_ZERO, 0,
184217044Snwhitehorn	    BUS_SPACE_MAXADDR_32BIT, 8, PAGE_SIZE);
185217044Snwhitehorn	lv1_net_set_interrupt_status_indicator(sc->sc_bus, sc->sc_dev,
186217044Snwhitehorn	    vtophys(sc->sc_hwirq_status), 0);
187217044Snwhitehorn	lv1_net_set_interrupt_mask(sc->sc_bus, sc->sc_dev,
188217044Snwhitehorn	    GELIC_INT_RXDONE | GELIC_INT_RXFRAME | GELIC_INT_PHY |
189217044Snwhitehorn	    GELIC_INT_TX_CHAIN_END, 0);
190217044Snwhitehorn
191217044Snwhitehorn	/*
192217044Snwhitehorn	 * Set up DMA.
193217044Snwhitehorn	 */
194217044Snwhitehorn
195217044Snwhitehorn	err = bus_dma_tag_create(bus_get_dma_tag(dev), 32, 0,
196217044Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
197217044Snwhitehorn	    129*sizeof(struct glc_dmadesc), 1, 128*sizeof(struct glc_dmadesc),
198217044Snwhitehorn	    0, NULL,NULL, &sc->sc_dmadesc_tag);
199217044Snwhitehorn
200217044Snwhitehorn	err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_txdmadesc,
201217044Snwhitehorn	    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
202217044Snwhitehorn	    &sc->sc_txdmadesc_map);
203217044Snwhitehorn	err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
204217044Snwhitehorn	    sc->sc_txdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
205217044Snwhitehorn	    &sc->sc_txdmadesc_phys, 0);
206217044Snwhitehorn	err = bus_dmamem_alloc(sc->sc_dmadesc_tag, (void **)&sc->sc_rxdmadesc,
207217044Snwhitehorn	    BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
208217044Snwhitehorn	    &sc->sc_rxdmadesc_map);
209217044Snwhitehorn	err = bus_dmamap_load(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
210217044Snwhitehorn	    sc->sc_rxdmadesc, 128*sizeof(struct glc_dmadesc), glc_getphys,
211217044Snwhitehorn	    &sc->sc_rxdmadesc_phys, 0);
212217044Snwhitehorn
213217044Snwhitehorn	err = bus_dma_tag_create(bus_get_dma_tag(dev), 128, 0,
214217044Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
215217044Snwhitehorn	    BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
216217044Snwhitehorn	    &sc->sc_rxdma_tag);
217217044Snwhitehorn	err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
218217044Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
219217044Snwhitehorn	    BUS_SPACE_MAXSIZE_32BIT, 16, BUS_SPACE_MAXSIZE_32BIT, 0, NULL,NULL,
220217044Snwhitehorn	    &sc->sc_txdma_tag);
221217044Snwhitehorn
222217044Snwhitehorn	/* init transmit descriptors */
223217044Snwhitehorn	STAILQ_INIT(&sc->sc_txfreeq);
224217044Snwhitehorn	STAILQ_INIT(&sc->sc_txdirtyq);
225217044Snwhitehorn
226217044Snwhitehorn	/* create TX DMA maps */
227217044Snwhitehorn	err = ENOMEM;
228217044Snwhitehorn	for (i = 0; i < GLC_MAX_TX_PACKETS; i++) {
229217044Snwhitehorn		txs = &sc->sc_txsoft[i];
230217044Snwhitehorn		txs->txs_mbuf = NULL;
231217044Snwhitehorn		err = bus_dmamap_create(sc->sc_txdma_tag, 0, &txs->txs_dmamap);
232217044Snwhitehorn		if (err) {
233217044Snwhitehorn			device_printf(dev,
234217044Snwhitehorn			    "unable to create TX DMA map %d, error = %d\n",
235217044Snwhitehorn			    i, err);
236217044Snwhitehorn		}
237217044Snwhitehorn		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
238217044Snwhitehorn	}
239217044Snwhitehorn
240217044Snwhitehorn	/* Create the receive buffer DMA maps. */
241217044Snwhitehorn	for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
242217044Snwhitehorn		err = bus_dmamap_create(sc->sc_rxdma_tag, 0,
243217044Snwhitehorn		    &sc->sc_rxsoft[i].rxs_dmamap);
244217044Snwhitehorn		if (err) {
245217044Snwhitehorn			device_printf(dev,
246217044Snwhitehorn			    "unable to create RX DMA map %d, error = %d\n",
247217044Snwhitehorn			    i, err);
248217044Snwhitehorn		}
249217044Snwhitehorn		sc->sc_rxsoft[i].rxs_mbuf = NULL;
250217044Snwhitehorn	}
251217044Snwhitehorn
252217044Snwhitehorn	/*
253217044Snwhitehorn	 * Attach to network stack
254217044Snwhitehorn	 */
255217044Snwhitehorn
256217044Snwhitehorn	if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev));
257217044Snwhitehorn	sc->sc_ifp->if_mtu = ETHERMTU;
258217044Snwhitehorn	sc->sc_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
259217044Snwhitehorn	sc->sc_ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
260217044Snwhitehorn	sc->sc_ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_RXCSUM;
261217044Snwhitehorn	sc->sc_ifp->if_capenable = IFCAP_HWCSUM | IFCAP_RXCSUM;
262217044Snwhitehorn	sc->sc_ifp->if_start = glc_start;
263217044Snwhitehorn	sc->sc_ifp->if_ioctl = glc_ioctl;
264217044Snwhitehorn	sc->sc_ifp->if_init = glc_init;
265217044Snwhitehorn
266217044Snwhitehorn	ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change,
267217044Snwhitehorn	    glc_media_status);
268217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
269217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
270217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
271217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
272217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
273217044Snwhitehorn	ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
274217044Snwhitehorn	ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
275217044Snwhitehorn
276217044Snwhitehorn	IFQ_SET_MAXLEN(&sc->sc_ifp->if_snd, GLC_MAX_TX_PACKETS);
277217044Snwhitehorn	sc->sc_ifp->if_snd.ifq_drv_maxlen = GLC_MAX_TX_PACKETS;
278217044Snwhitehorn	IFQ_SET_READY(&sc->sc_ifp->if_snd);
279217044Snwhitehorn
280217044Snwhitehorn	ether_ifattach(sc->sc_ifp, sc->sc_enaddr);
281217044Snwhitehorn	sc->sc_ifp->if_hwassist = 0;
282217044Snwhitehorn
283217044Snwhitehorn	return (0);
284217044Snwhitehorn
285217044Snwhitehorn	mtx_destroy(&sc->sc_mtx);
286217044Snwhitehorn	if_free(sc->sc_ifp);
287217044Snwhitehorn	return (ENXIO);
288217044Snwhitehorn}
289217044Snwhitehorn
290217044Snwhitehornstatic void
291217044Snwhitehornglc_init_locked(struct glc_softc *sc)
292217044Snwhitehorn{
293217044Snwhitehorn	int i, error;
294217044Snwhitehorn	struct glc_rxsoft *rxs;
295217044Snwhitehorn	struct glc_txsoft *txs;
296217044Snwhitehorn
297217044Snwhitehorn	mtx_assert(&sc->sc_mtx, MA_OWNED);
298217044Snwhitehorn
299217044Snwhitehorn	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
300217044Snwhitehorn	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
301217044Snwhitehorn
302217044Snwhitehorn	glc_set_multicast(sc);
303217044Snwhitehorn
304217044Snwhitehorn	for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
305217044Snwhitehorn		rxs = &sc->sc_rxsoft[i];
306217044Snwhitehorn		rxs->rxs_desc_slot = i;
307217044Snwhitehorn
308217044Snwhitehorn		if (rxs->rxs_mbuf == NULL) {
309217044Snwhitehorn			glc_add_rxbuf(sc, i);
310217044Snwhitehorn
311217044Snwhitehorn			if (rxs->rxs_mbuf == NULL) {
312217044Snwhitehorn				rxs->rxs_desc_slot = -1;
313217044Snwhitehorn				break;
314217044Snwhitehorn			}
315217044Snwhitehorn		}
316217044Snwhitehorn
317217044Snwhitehorn		glc_add_rxbuf_dma(sc, i);
318217044Snwhitehorn		bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
319217044Snwhitehorn		    BUS_DMASYNC_PREREAD);
320217044Snwhitehorn	}
321217044Snwhitehorn
322217044Snwhitehorn	/* Clear TX dirty queue */
323217044Snwhitehorn	while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
324217044Snwhitehorn		STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
325217044Snwhitehorn		bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
326217044Snwhitehorn
327217044Snwhitehorn		if (txs->txs_mbuf != NULL) {
328217044Snwhitehorn			m_freem(txs->txs_mbuf);
329217044Snwhitehorn			txs->txs_mbuf = NULL;
330217044Snwhitehorn		}
331217044Snwhitehorn
332217044Snwhitehorn		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
333217044Snwhitehorn	}
334217044Snwhitehorn	sc->first_used_txdma_slot = -1;
335217044Snwhitehorn	sc->bsy_txdma_slots = 0;
336217044Snwhitehorn
337217044Snwhitehorn	error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
338217044Snwhitehorn	    sc->sc_rxsoft[0].rxs_desc, 0);
339217044Snwhitehorn	if (error != 0)
340217044Snwhitehorn		device_printf(sc->sc_self,
341217044Snwhitehorn		    "lv1_net_start_rx_dma error: %d\n", error);
342217044Snwhitehorn
343217044Snwhitehorn	sc->sc_ifp->if_drv_flags |= IFF_DRV_RUNNING;
344217044Snwhitehorn	sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
345217044Snwhitehorn	sc->sc_ifpflags = sc->sc_ifp->if_flags;
346217044Snwhitehorn
347217044Snwhitehorn	sc->sc_wdog_timer = 0;
348217044Snwhitehorn	callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
349217044Snwhitehorn}
350217044Snwhitehorn
351217044Snwhitehornstatic void
352217044Snwhitehornglc_stop(void *xsc)
353217044Snwhitehorn{
354217044Snwhitehorn	struct glc_softc *sc = xsc;
355217044Snwhitehorn
356217044Snwhitehorn	mtx_assert(&sc->sc_mtx, MA_OWNED);
357217044Snwhitehorn
358217044Snwhitehorn	lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
359217044Snwhitehorn	lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
360217044Snwhitehorn}
361217044Snwhitehorn
362217044Snwhitehornstatic void
363217044Snwhitehornglc_init(void *xsc)
364217044Snwhitehorn{
365217044Snwhitehorn	struct glc_softc *sc = xsc;
366217044Snwhitehorn
367217044Snwhitehorn	mtx_lock(&sc->sc_mtx);
368217044Snwhitehorn	glc_init_locked(sc);
369217044Snwhitehorn	mtx_unlock(&sc->sc_mtx);
370217044Snwhitehorn}
371217044Snwhitehorn
372217044Snwhitehornstatic void
373217044Snwhitehornglc_tick(void *xsc)
374217044Snwhitehorn{
375217044Snwhitehorn	struct glc_softc *sc = xsc;
376217044Snwhitehorn
377217044Snwhitehorn	mtx_assert(&sc->sc_mtx, MA_OWNED);
378217044Snwhitehorn
379223324Snwhitehorn	/*
380223324Snwhitehorn	 * XXX: Sometimes the RX queue gets stuck. Poke it periodically until
381223324Snwhitehorn	 * we figure out why. This will fail harmlessly if the RX queue is
382223324Snwhitehorn	 * already running.
383223324Snwhitehorn	 */
384223324Snwhitehorn	lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
385223324Snwhitehorn	    sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
386223324Snwhitehorn
387217044Snwhitehorn	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) {
388217044Snwhitehorn		callout_reset(&sc->sc_tick_ch, hz, glc_tick, sc);
389217044Snwhitehorn		return;
390217044Snwhitehorn	}
391217044Snwhitehorn
392217044Snwhitehorn	/* Problems */
393217044Snwhitehorn	device_printf(sc->sc_self, "device timeout\n");
394217044Snwhitehorn
395217044Snwhitehorn	glc_init_locked(sc);
396217044Snwhitehorn}
397217044Snwhitehorn
398217044Snwhitehornstatic void
399217044Snwhitehornglc_start_locked(struct ifnet *ifp)
400217044Snwhitehorn{
401217044Snwhitehorn	struct glc_softc *sc = ifp->if_softc;
402217044Snwhitehorn	bus_addr_t first, pktdesc;
403217044Snwhitehorn	int kickstart = 0;
404217044Snwhitehorn	int error;
405217044Snwhitehorn	struct mbuf *mb_head;
406217044Snwhitehorn
407217044Snwhitehorn	mtx_assert(&sc->sc_mtx, MA_OWNED);
408217044Snwhitehorn	first = 0;
409217044Snwhitehorn
410217044Snwhitehorn	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
411217044Snwhitehorn	    IFF_DRV_RUNNING)
412217044Snwhitehorn		return;
413217044Snwhitehorn
414217044Snwhitehorn	if (STAILQ_EMPTY(&sc->sc_txdirtyq))
415217044Snwhitehorn		kickstart = 1;
416217044Snwhitehorn
417217044Snwhitehorn	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
418217044Snwhitehorn		IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head);
419217044Snwhitehorn
420217044Snwhitehorn		if (mb_head == NULL)
421217044Snwhitehorn			break;
422217044Snwhitehorn
423217044Snwhitehorn		/* Check if the ring buffer is full */
424217044Snwhitehorn		if (sc->bsy_txdma_slots > 125) {
425217044Snwhitehorn			/* Put the packet back and stop */
426217044Snwhitehorn			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
427217044Snwhitehorn			IFQ_DRV_PREPEND(&ifp->if_snd, mb_head);
428217044Snwhitehorn			break;
429217044Snwhitehorn		}
430217044Snwhitehorn
431217044Snwhitehorn		BPF_MTAP(ifp, mb_head);
432217044Snwhitehorn
433217044Snwhitehorn		if (sc->sc_tx_vlan >= 0)
434217044Snwhitehorn			mb_head = ether_vlanencap(mb_head, sc->sc_tx_vlan);
435217044Snwhitehorn
436217044Snwhitehorn		if (glc_encap(sc, &mb_head, &pktdesc)) {
437217044Snwhitehorn			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
438217044Snwhitehorn			break;
439217044Snwhitehorn		}
440217044Snwhitehorn
441217044Snwhitehorn		if (first == 0)
442217044Snwhitehorn			first = pktdesc;
443217044Snwhitehorn	}
444217044Snwhitehorn
445217044Snwhitehorn	if (kickstart && first != 0) {
446217044Snwhitehorn		error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev, first, 0);
447217044Snwhitehorn		if (error != 0)
448217044Snwhitehorn			device_printf(sc->sc_self,
449217044Snwhitehorn			    "lv1_net_start_tx_dma error: %d\n", error);
450217044Snwhitehorn		sc->sc_wdog_timer = 5;
451217044Snwhitehorn	}
452217044Snwhitehorn}
453217044Snwhitehorn
454217044Snwhitehornstatic void
455217044Snwhitehornglc_start(struct ifnet *ifp)
456217044Snwhitehorn{
457217044Snwhitehorn	struct glc_softc *sc = ifp->if_softc;
458217044Snwhitehorn
459217044Snwhitehorn	mtx_lock(&sc->sc_mtx);
460217044Snwhitehorn	glc_start_locked(ifp);
461217044Snwhitehorn	mtx_unlock(&sc->sc_mtx);
462217044Snwhitehorn}
463217044Snwhitehorn
464217044Snwhitehornstatic int
465217044Snwhitehornglc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
466217044Snwhitehorn{
467217044Snwhitehorn	struct glc_softc *sc = ifp->if_softc;
468217044Snwhitehorn	struct ifreq *ifr = (struct ifreq *)data;
469217044Snwhitehorn	int err = 0;
470217044Snwhitehorn
471217044Snwhitehorn	switch (cmd) {
472217044Snwhitehorn	case SIOCSIFFLAGS:
473217044Snwhitehorn                mtx_lock(&sc->sc_mtx);
474217044Snwhitehorn		if ((ifp->if_flags & IFF_UP) != 0) {
475217044Snwhitehorn			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 &&
476217044Snwhitehorn			   ((ifp->if_flags ^ sc->sc_ifpflags) &
477217044Snwhitehorn			    (IFF_ALLMULTI | IFF_PROMISC)) != 0)
478217044Snwhitehorn				glc_set_multicast(sc);
479217044Snwhitehorn			else
480217044Snwhitehorn				glc_init_locked(sc);
481217044Snwhitehorn		}
482217044Snwhitehorn		else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
483217044Snwhitehorn			glc_stop(sc);
484217044Snwhitehorn		sc->sc_ifpflags = ifp->if_flags;
485217044Snwhitehorn		mtx_unlock(&sc->sc_mtx);
486217044Snwhitehorn		break;
487217044Snwhitehorn	case SIOCADDMULTI:
488217044Snwhitehorn	case SIOCDELMULTI:
489217044Snwhitehorn                mtx_lock(&sc->sc_mtx);
490217044Snwhitehorn		glc_set_multicast(sc);
491217044Snwhitehorn                mtx_unlock(&sc->sc_mtx);
492217044Snwhitehorn		break;
493217044Snwhitehorn	case SIOCGIFMEDIA:
494217044Snwhitehorn	case SIOCSIFMEDIA:
495217044Snwhitehorn		err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
496217044Snwhitehorn		break;
497217044Snwhitehorn	default:
498217044Snwhitehorn		err = ether_ioctl(ifp, cmd, data);
499217044Snwhitehorn		break;
500217044Snwhitehorn	}
501217044Snwhitehorn
502217044Snwhitehorn	return (err);
503217044Snwhitehorn}
504217044Snwhitehorn
505217044Snwhitehornstatic void
506217044Snwhitehornglc_set_multicast(struct glc_softc *sc)
507217044Snwhitehorn{
508217044Snwhitehorn	struct ifnet *ifp = sc->sc_ifp;
509217044Snwhitehorn	struct ifmultiaddr *inm;
510217044Snwhitehorn	uint64_t addr;
511217044Snwhitehorn	int naddrs;
512217044Snwhitehorn
513217044Snwhitehorn	/* Clear multicast filter */
514217044Snwhitehorn	lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
515217044Snwhitehorn
516217044Snwhitehorn	/* Add broadcast */
517217044Snwhitehorn	lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
518217044Snwhitehorn	    0xffffffffffffL, 0);
519217044Snwhitehorn
520217044Snwhitehorn	if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
521217044Snwhitehorn		lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
522217044Snwhitehorn	} else {
523217044Snwhitehorn		if_maddr_rlock(ifp);
524217044Snwhitehorn		naddrs = 1; /* Include broadcast */
525217044Snwhitehorn		TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) {
526217044Snwhitehorn			if (inm->ifma_addr->sa_family != AF_LINK)
527217044Snwhitehorn				continue;
528217044Snwhitehorn			addr = 0;
529217044Snwhitehorn			memcpy(&((uint8_t *)(&addr))[2],
530217044Snwhitehorn			    LLADDR((struct sockaddr_dl *)inm->ifma_addr),
531217044Snwhitehorn			    ETHER_ADDR_LEN);
532217044Snwhitehorn
533217044Snwhitehorn			lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
534217044Snwhitehorn			    addr, 0);
535217044Snwhitehorn
536217044Snwhitehorn			/*
537217044Snwhitehorn			 * Filter can only hold 32 addresses, so fall back to
538217044Snwhitehorn			 * the IFF_ALLMULTI case if we have too many.
539217044Snwhitehorn			 */
540217044Snwhitehorn			if (++naddrs >= 32) {
541217044Snwhitehorn				lv1_net_add_multicast_address(sc->sc_bus,
542217044Snwhitehorn				    sc->sc_dev, 0, 1);
543217044Snwhitehorn				break;
544217044Snwhitehorn			}
545217044Snwhitehorn		}
546217044Snwhitehorn		if_maddr_runlock(ifp);
547217044Snwhitehorn	}
548217044Snwhitehorn}
549217044Snwhitehorn
550217044Snwhitehornstatic int
551217044Snwhitehornglc_add_rxbuf(struct glc_softc *sc, int idx)
552217044Snwhitehorn{
553217044Snwhitehorn	struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
554217044Snwhitehorn	struct mbuf *m;
555217044Snwhitehorn	bus_dma_segment_t segs[1];
556217044Snwhitehorn	int error, nsegs;
557217044Snwhitehorn
558243882Sglebius	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
559217044Snwhitehorn	if (m == NULL)
560217044Snwhitehorn		return (ENOBUFS);
561217044Snwhitehorn	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
562217044Snwhitehorn
563217044Snwhitehorn	if (rxs->rxs_mbuf != NULL) {
564217044Snwhitehorn		bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap,
565217044Snwhitehorn		    BUS_DMASYNC_POSTREAD);
566217044Snwhitehorn		bus_dmamap_unload(sc->sc_rxdma_tag, rxs->rxs_dmamap);
567217044Snwhitehorn	}
568217044Snwhitehorn
569217044Snwhitehorn	error = bus_dmamap_load_mbuf_sg(sc->sc_rxdma_tag, rxs->rxs_dmamap, m,
570217044Snwhitehorn	    segs, &nsegs, BUS_DMA_NOWAIT);
571217044Snwhitehorn	if (error != 0) {
572217044Snwhitehorn		device_printf(sc->sc_self,
573217044Snwhitehorn		    "cannot load RS DMA map %d, error = %d\n", idx, error);
574217044Snwhitehorn		m_freem(m);
575217044Snwhitehorn		return (error);
576217044Snwhitehorn	}
577217044Snwhitehorn	/* If nsegs is wrong then the stack is corrupt. */
578217044Snwhitehorn	KASSERT(nsegs == 1,
579217044Snwhitehorn	    ("%s: too many DMA segments (%d)", __func__, nsegs));
580217044Snwhitehorn	rxs->rxs_mbuf = m;
581217044Snwhitehorn	rxs->segment = segs[0];
582217044Snwhitehorn
583217044Snwhitehorn	bus_dmamap_sync(sc->sc_rxdma_tag, rxs->rxs_dmamap, BUS_DMASYNC_PREREAD);
584217044Snwhitehorn
585217044Snwhitehorn	return (0);
586217044Snwhitehorn}
587217044Snwhitehorn
588217044Snwhitehornstatic int
589217044Snwhitehornglc_add_rxbuf_dma(struct glc_softc *sc, int idx)
590217044Snwhitehorn{
591217044Snwhitehorn	struct glc_rxsoft *rxs = &sc->sc_rxsoft[idx];
592217044Snwhitehorn
593217044Snwhitehorn	bzero(&sc->sc_rxdmadesc[idx], sizeof(sc->sc_rxdmadesc[idx]));
594217044Snwhitehorn	sc->sc_rxdmadesc[idx].paddr = rxs->segment.ds_addr;
595217044Snwhitehorn	sc->sc_rxdmadesc[idx].len = rxs->segment.ds_len;
596217044Snwhitehorn	sc->sc_rxdmadesc[idx].next = sc->sc_rxdmadesc_phys +
597217044Snwhitehorn	    ((idx + 1) % GLC_MAX_RX_PACKETS)*sizeof(sc->sc_rxdmadesc[idx]);
598217044Snwhitehorn	sc->sc_rxdmadesc[idx].cmd_stat = GELIC_DESCR_OWNED;
599217044Snwhitehorn
600217044Snwhitehorn	rxs->rxs_desc_slot = idx;
601217044Snwhitehorn	rxs->rxs_desc = sc->sc_rxdmadesc_phys + idx*sizeof(struct glc_dmadesc);
602217044Snwhitehorn
603217044Snwhitehorn        return (0);
604217044Snwhitehorn}
605217044Snwhitehorn
606217044Snwhitehornstatic int
607217044Snwhitehornglc_encap(struct glc_softc *sc, struct mbuf **m_head, bus_addr_t *pktdesc)
608217044Snwhitehorn{
609217044Snwhitehorn	bus_dma_segment_t segs[16];
610217044Snwhitehorn	struct glc_txsoft *txs;
611217044Snwhitehorn	struct mbuf *m;
612217044Snwhitehorn	bus_addr_t firstslotphys;
613217044Snwhitehorn	int i, idx, nsegs, nsegs_max;
614217044Snwhitehorn	int err = 0;
615217044Snwhitehorn
616217044Snwhitehorn	/* Max number of segments is the number of free DMA slots */
617217044Snwhitehorn	nsegs_max = 128 - sc->bsy_txdma_slots;
618217044Snwhitehorn
619217044Snwhitehorn	if (nsegs_max > 16 || sc->first_used_txdma_slot < 0)
620217044Snwhitehorn		nsegs_max = 16;
621217044Snwhitehorn
622217044Snwhitehorn	/* Get a work queue entry. */
623217044Snwhitehorn	if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) {
624217044Snwhitehorn		/* Ran out of descriptors. */
625217044Snwhitehorn		return (ENOBUFS);
626217044Snwhitehorn	}
627217044Snwhitehorn
628217044Snwhitehorn	nsegs = 0;
629217044Snwhitehorn	for (m = *m_head; m != NULL; m = m->m_next)
630217044Snwhitehorn		nsegs++;
631217044Snwhitehorn
632217044Snwhitehorn	if (nsegs > nsegs_max) {
633243882Sglebius		m = m_collapse(*m_head, M_NOWAIT, nsegs_max);
634217044Snwhitehorn		if (m == NULL) {
635217044Snwhitehorn			m_freem(*m_head);
636217044Snwhitehorn			*m_head = NULL;
637217044Snwhitehorn			return (ENOBUFS);
638217044Snwhitehorn		}
639217044Snwhitehorn		*m_head = m;
640217044Snwhitehorn	}
641217044Snwhitehorn
642217044Snwhitehorn	err = bus_dmamap_load_mbuf_sg(sc->sc_txdma_tag, txs->txs_dmamap,
643217044Snwhitehorn	    *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
644217044Snwhitehorn	if (err != 0) {
645217044Snwhitehorn		m_freem(*m_head);
646217044Snwhitehorn		*m_head = NULL;
647217044Snwhitehorn		return (err);
648217044Snwhitehorn	}
649217044Snwhitehorn
650217044Snwhitehorn	KASSERT(nsegs <= 128 - sc->bsy_txdma_slots,
651217044Snwhitehorn	    ("GLC: Mapped too many (%d) DMA segments with %d available",
652217044Snwhitehorn	    nsegs, 128 - sc->bsy_txdma_slots));
653217044Snwhitehorn
654217044Snwhitehorn	if (nsegs == 0) {
655217044Snwhitehorn		m_freem(*m_head);
656217044Snwhitehorn		*m_head = NULL;
657217044Snwhitehorn		return (EIO);
658217044Snwhitehorn	}
659217044Snwhitehorn
660217044Snwhitehorn	txs->txs_ndescs = nsegs;
661217044Snwhitehorn	txs->txs_firstdesc = sc->next_txdma_slot;
662217044Snwhitehorn
663217044Snwhitehorn	idx = txs->txs_firstdesc;
664217044Snwhitehorn	firstslotphys = sc->sc_txdmadesc_phys +
665217044Snwhitehorn	    txs->txs_firstdesc*sizeof(struct glc_dmadesc);
666217044Snwhitehorn
667217044Snwhitehorn	for (i = 0; i < nsegs; i++) {
668217044Snwhitehorn		bzero(&sc->sc_txdmadesc[idx], sizeof(sc->sc_txdmadesc[idx]));
669217044Snwhitehorn		sc->sc_txdmadesc[idx].paddr = segs[i].ds_addr;
670217044Snwhitehorn		sc->sc_txdmadesc[idx].len = segs[i].ds_len;
671217044Snwhitehorn		sc->sc_txdmadesc[idx].next = sc->sc_txdmadesc_phys +
672217044Snwhitehorn		    ((idx + 1) % GLC_MAX_TX_PACKETS)*sizeof(struct glc_dmadesc);
673217044Snwhitehorn		sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_NOIPSEC;
674217044Snwhitehorn
675217044Snwhitehorn		if (i+1 == nsegs) {
676217044Snwhitehorn			txs->txs_lastdesc = idx;
677217044Snwhitehorn			sc->sc_txdmadesc[idx].next = 0;
678217044Snwhitehorn			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST;
679217044Snwhitehorn		}
680217044Snwhitehorn
681217044Snwhitehorn		if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
682217044Snwhitehorn			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP;
683217044Snwhitehorn		if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
684217044Snwhitehorn			sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP;
685217044Snwhitehorn		sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED;
686217044Snwhitehorn
687217044Snwhitehorn		idx = (idx + 1) % GLC_MAX_TX_PACKETS;
688217044Snwhitehorn	}
689217044Snwhitehorn	sc->next_txdma_slot = idx;
690217044Snwhitehorn	sc->bsy_txdma_slots += nsegs;
691217044Snwhitehorn	if (txs->txs_firstdesc != 0)
692217044Snwhitehorn		idx = txs->txs_firstdesc - 1;
693217044Snwhitehorn	else
694217044Snwhitehorn		idx = GLC_MAX_TX_PACKETS - 1;
695217044Snwhitehorn
696217044Snwhitehorn	if (sc->first_used_txdma_slot < 0)
697217044Snwhitehorn		sc->first_used_txdma_slot = txs->txs_firstdesc;
698217044Snwhitehorn
699217044Snwhitehorn	bus_dmamap_sync(sc->sc_txdma_tag, txs->txs_dmamap,
700217044Snwhitehorn	    BUS_DMASYNC_PREWRITE);
701217044Snwhitehorn	sc->sc_txdmadesc[idx].next = firstslotphys;
702217044Snwhitehorn
703217044Snwhitehorn	STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q);
704217044Snwhitehorn	STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q);
705217044Snwhitehorn	txs->txs_mbuf = *m_head;
706217044Snwhitehorn	*pktdesc = firstslotphys;
707217044Snwhitehorn
708217044Snwhitehorn	return (0);
709217044Snwhitehorn}
710217044Snwhitehorn
711217044Snwhitehornstatic void
712217044Snwhitehornglc_rxintr(struct glc_softc *sc)
713217044Snwhitehorn{
714217044Snwhitehorn	int i, restart_rxdma, error;
715217044Snwhitehorn	struct mbuf *m;
716217044Snwhitehorn	struct ifnet *ifp = sc->sc_ifp;
717217044Snwhitehorn
718217044Snwhitehorn	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
719223324Snwhitehorn	    BUS_DMASYNC_POSTREAD);
720217044Snwhitehorn
721217044Snwhitehorn	restart_rxdma = 0;
722217044Snwhitehorn	while ((sc->sc_rxdmadesc[sc->sc_next_rxdma_slot].cmd_stat &
723217044Snwhitehorn	   GELIC_DESCR_OWNED) == 0) {
724217044Snwhitehorn		i = sc->sc_next_rxdma_slot;
725223324Snwhitehorn		sc->sc_next_rxdma_slot++;
726223324Snwhitehorn		if (sc->sc_next_rxdma_slot >= GLC_MAX_RX_PACKETS)
727223324Snwhitehorn			sc->sc_next_rxdma_slot = 0;
728223324Snwhitehorn
729223324Snwhitehorn		if (sc->sc_rxdmadesc[i].cmd_stat & GELIC_CMDSTAT_CHAIN_END)
730223324Snwhitehorn			restart_rxdma = 1;
731223324Snwhitehorn
732217044Snwhitehorn		if (sc->sc_rxdmadesc[i].rxerror & GELIC_RXERRORS) {
733271860Sglebius			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
734217044Snwhitehorn			goto requeue;
735217044Snwhitehorn		}
736217044Snwhitehorn
737217044Snwhitehorn		m = sc->sc_rxsoft[i].rxs_mbuf;
738217044Snwhitehorn		if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) {
739217044Snwhitehorn			m->m_pkthdr.csum_flags |=
740217044Snwhitehorn			    CSUM_IP_CHECKED | CSUM_IP_VALID;
741217044Snwhitehorn		}
742217044Snwhitehorn		if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) {
743217044Snwhitehorn			m->m_pkthdr.csum_flags |=
744217044Snwhitehorn			    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
745217044Snwhitehorn			m->m_pkthdr.csum_data = 0xffff;
746217044Snwhitehorn		}
747217044Snwhitehorn
748217044Snwhitehorn		if (glc_add_rxbuf(sc, i)) {
749271860Sglebius			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
750217044Snwhitehorn			goto requeue;
751217044Snwhitehorn		}
752217044Snwhitehorn
753271860Sglebius		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
754217044Snwhitehorn		m->m_pkthdr.rcvif = ifp;
755217044Snwhitehorn		m->m_len = sc->sc_rxdmadesc[i].valid_size;
756217044Snwhitehorn		m->m_pkthdr.len = m->m_len;
757217044Snwhitehorn
758223792Snwhitehorn		/*
759223792Snwhitehorn		 * Remove VLAN tag. Even on early firmwares that do not allow
760223792Snwhitehorn		 * multiple VLANs, the VLAN tag is still in place here.
761223792Snwhitehorn		 */
762223792Snwhitehorn		m_adj(m, 2);
763217044Snwhitehorn
764217044Snwhitehorn		mtx_unlock(&sc->sc_mtx);
765217044Snwhitehorn		(*ifp->if_input)(ifp, m);
766217044Snwhitehorn		mtx_lock(&sc->sc_mtx);
767217044Snwhitehorn
768217044Snwhitehorn	    requeue:
769217044Snwhitehorn		glc_add_rxbuf_dma(sc, i);
770217044Snwhitehorn	}
771223324Snwhitehorn
772223324Snwhitehorn	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_rxdmadesc_map,
773223324Snwhitehorn	    BUS_DMASYNC_PREWRITE);
774223324Snwhitehorn
775223324Snwhitehorn	if (restart_rxdma) {
776223324Snwhitehorn		error = lv1_net_start_rx_dma(sc->sc_bus, sc->sc_dev,
777223324Snwhitehorn		    sc->sc_rxsoft[sc->sc_next_rxdma_slot].rxs_desc, 0);
778223324Snwhitehorn		if (error != 0)
779223324Snwhitehorn			device_printf(sc->sc_self,
780223324Snwhitehorn			    "lv1_net_start_rx_dma error: %d\n", error);
781223324Snwhitehorn	}
782217044Snwhitehorn}
783217044Snwhitehorn
784217044Snwhitehornstatic void
785217044Snwhitehornglc_txintr(struct glc_softc *sc)
786217044Snwhitehorn{
787217044Snwhitehorn	struct ifnet *ifp = sc->sc_ifp;
788217044Snwhitehorn	struct glc_txsoft *txs;
789217044Snwhitehorn	int progress = 0, kickstart = 0, error;
790217044Snwhitehorn
791223324Snwhitehorn	bus_dmamap_sync(sc->sc_dmadesc_tag, sc->sc_txdmadesc_map,
792223324Snwhitehorn	    BUS_DMASYNC_POSTREAD);
793223324Snwhitehorn
794217044Snwhitehorn	while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) {
795217044Snwhitehorn		if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat
796217044Snwhitehorn		    & GELIC_DESCR_OWNED)
797217044Snwhitehorn			break;
798217044Snwhitehorn
799217044Snwhitehorn		STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q);
800217044Snwhitehorn		bus_dmamap_unload(sc->sc_txdma_tag, txs->txs_dmamap);
801217044Snwhitehorn		sc->bsy_txdma_slots -= txs->txs_ndescs;
802217044Snwhitehorn
803217044Snwhitehorn		if (txs->txs_mbuf != NULL) {
804217044Snwhitehorn			m_freem(txs->txs_mbuf);
805217044Snwhitehorn			txs->txs_mbuf = NULL;
806217044Snwhitehorn		}
807217044Snwhitehorn
808217044Snwhitehorn		if ((sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat & 0xf0000000)
809217044Snwhitehorn		    != 0) {
810217044Snwhitehorn			lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
811217044Snwhitehorn			kickstart = 1;
812271860Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
813217044Snwhitehorn		}
814217044Snwhitehorn
815217044Snwhitehorn		if (sc->sc_txdmadesc[txs->txs_lastdesc].cmd_stat &
816217044Snwhitehorn		    GELIC_CMDSTAT_CHAIN_END)
817217044Snwhitehorn			kickstart = 1;
818217044Snwhitehorn
819217044Snwhitehorn		STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q);
820271860Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
821217044Snwhitehorn		progress = 1;
822217044Snwhitehorn	}
823217044Snwhitehorn
824217044Snwhitehorn	if (txs != NULL)
825217044Snwhitehorn		sc->first_used_txdma_slot = txs->txs_firstdesc;
826217044Snwhitehorn	else
827217044Snwhitehorn		sc->first_used_txdma_slot = -1;
828217044Snwhitehorn
829223324Snwhitehorn	if (kickstart || txs != NULL) {
830223324Snwhitehorn		/* Speculatively (or necessarily) start the TX queue again */
831217044Snwhitehorn		error = lv1_net_start_tx_dma(sc->sc_bus, sc->sc_dev,
832217044Snwhitehorn		    sc->sc_txdmadesc_phys +
833217044Snwhitehorn		    txs->txs_firstdesc*sizeof(struct glc_dmadesc), 0);
834217044Snwhitehorn		if (error != 0)
835217044Snwhitehorn			device_printf(sc->sc_self,
836217044Snwhitehorn			    "lv1_net_start_tx_dma error: %d\n", error);
837217044Snwhitehorn	}
838217044Snwhitehorn
839217044Snwhitehorn	if (progress) {
840217044Snwhitehorn		/*
841217044Snwhitehorn		 * We freed some descriptors, so reset IFF_DRV_OACTIVE
842217044Snwhitehorn		 * and restart.
843217044Snwhitehorn		 */
844217044Snwhitehorn		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
845217044Snwhitehorn		sc->sc_wdog_timer = STAILQ_EMPTY(&sc->sc_txdirtyq) ? 0 : 5;
846217044Snwhitehorn
847217044Snwhitehorn		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
848217044Snwhitehorn		    !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
849217044Snwhitehorn			glc_start_locked(ifp);
850217044Snwhitehorn	}
851217044Snwhitehorn}
852217044Snwhitehorn
853217044Snwhitehornstatic int
854217044Snwhitehornglc_intr_filter(void *xsc)
855217044Snwhitehorn{
856217044Snwhitehorn	struct glc_softc *sc = xsc;
857217044Snwhitehorn
858217044Snwhitehorn	powerpc_sync();
859217044Snwhitehorn	atomic_set_64(&sc->sc_interrupt_status, *sc->sc_hwirq_status);
860217044Snwhitehorn	return (FILTER_SCHEDULE_THREAD);
861217044Snwhitehorn}
862217044Snwhitehorn
863217044Snwhitehornstatic void
864217044Snwhitehornglc_intr(void *xsc)
865217044Snwhitehorn{
866217044Snwhitehorn	struct glc_softc *sc = xsc;
867217044Snwhitehorn	uint64_t status, linkstat, junk;
868217044Snwhitehorn
869217044Snwhitehorn	mtx_lock(&sc->sc_mtx);
870217044Snwhitehorn
871217044Snwhitehorn	status = atomic_readandclear_64(&sc->sc_interrupt_status);
872217044Snwhitehorn
873217044Snwhitehorn	if (status == 0) {
874217044Snwhitehorn		mtx_unlock(&sc->sc_mtx);
875217044Snwhitehorn		return;
876217044Snwhitehorn	}
877217044Snwhitehorn
878217044Snwhitehorn	if (status & (GELIC_INT_RXDONE | GELIC_INT_RXFRAME))
879217044Snwhitehorn		glc_rxintr(sc);
880217044Snwhitehorn
881217044Snwhitehorn	if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END))
882217044Snwhitehorn		glc_txintr(sc);
883217044Snwhitehorn
884217044Snwhitehorn	if (status & GELIC_INT_PHY) {
885217044Snwhitehorn		lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
886217044Snwhitehorn		    GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk);
887217044Snwhitehorn
888217044Snwhitehorn		linkstat = (linkstat & GELIC_LINK_UP) ?
889217044Snwhitehorn		    LINK_STATE_UP : LINK_STATE_DOWN;
890217044Snwhitehorn		if (linkstat != sc->sc_ifp->if_link_state)
891217044Snwhitehorn			if_link_state_change(sc->sc_ifp, linkstat);
892217044Snwhitehorn	}
893217044Snwhitehorn
894217044Snwhitehorn	mtx_unlock(&sc->sc_mtx);
895217044Snwhitehorn}
896217044Snwhitehorn
897217044Snwhitehornstatic void
898217044Snwhitehornglc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
899217044Snwhitehorn{
900217044Snwhitehorn	struct glc_softc *sc = ifp->if_softc;
901217044Snwhitehorn	uint64_t status, junk;
902217044Snwhitehorn
903217044Snwhitehorn	ifmr->ifm_status = IFM_AVALID;
904217044Snwhitehorn	ifmr->ifm_active = IFM_ETHER;
905217044Snwhitehorn
906217044Snwhitehorn	lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
907217044Snwhitehorn	    GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk);
908217044Snwhitehorn
909217044Snwhitehorn	if (status & GELIC_LINK_UP)
910217044Snwhitehorn		ifmr->ifm_status |= IFM_ACTIVE;
911217044Snwhitehorn
912217044Snwhitehorn	if (status & GELIC_SPEED_10)
913217044Snwhitehorn		ifmr->ifm_active |= IFM_10_T;
914217044Snwhitehorn	else if (status & GELIC_SPEED_100)
915217044Snwhitehorn		ifmr->ifm_active |= IFM_100_TX;
916217044Snwhitehorn	else if (status & GELIC_SPEED_1000)
917217044Snwhitehorn		ifmr->ifm_active |= IFM_1000_T;
918217044Snwhitehorn
919217044Snwhitehorn	if (status & GELIC_FULL_DUPLEX)
920217044Snwhitehorn		ifmr->ifm_active |= IFM_FDX;
921217044Snwhitehorn	else
922217044Snwhitehorn		ifmr->ifm_active |= IFM_HDX;
923217044Snwhitehorn}
924217044Snwhitehorn
925217044Snwhitehornstatic int
926217044Snwhitehornglc_media_change(struct ifnet *ifp)
927217044Snwhitehorn{
928217044Snwhitehorn	struct glc_softc *sc = ifp->if_softc;
929217044Snwhitehorn	uint64_t mode, junk;
930217044Snwhitehorn	int result;
931217044Snwhitehorn
932217044Snwhitehorn	if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
933217044Snwhitehorn		return (EINVAL);
934217044Snwhitehorn
935217044Snwhitehorn	switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
936217044Snwhitehorn	case IFM_AUTO:
937217044Snwhitehorn		mode = GELIC_AUTO_NEG;
938217044Snwhitehorn		break;
939217044Snwhitehorn	case IFM_10_T:
940217044Snwhitehorn		mode = GELIC_SPEED_10;
941217044Snwhitehorn		break;
942217044Snwhitehorn	case IFM_100_TX:
943217044Snwhitehorn		mode = GELIC_SPEED_100;
944217044Snwhitehorn		break;
945217044Snwhitehorn	case IFM_1000_T:
946217044Snwhitehorn		mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX;
947217044Snwhitehorn		break;
948217044Snwhitehorn	default:
949217044Snwhitehorn		return (EINVAL);
950217044Snwhitehorn	}
951217044Snwhitehorn
952217044Snwhitehorn	if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX)
953217044Snwhitehorn		mode |= GELIC_FULL_DUPLEX;
954217044Snwhitehorn
955217044Snwhitehorn	result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE,
956217044Snwhitehorn	    GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk);
957217044Snwhitehorn
958217044Snwhitehorn	return (result ? EIO : 0);
959217044Snwhitehorn}
960217044Snwhitehorn
961