if_arge.c revision 188808
1218893Sdim/*-
2193326Sed * Copyright (c) 2009, Oleksandr Tymoshenko
3193326Sed * All rights reserved.
4193326Sed *
5193326Sed * Redistribution and use in source and binary forms, with or without
6193326Sed * modification, are permitted provided that the following conditions
7193326Sed * are met:
8193326Sed * 1. Redistributions of source code must retain the above copyright
9193326Sed *    notice unmodified, this list of conditions, and the following
10193326Sed *    disclaimer.
11193326Sed * 2. Redistributions in binary form must reproduce the above copyright
12193326Sed *    notice, this list of conditions and the following disclaimer in the
13193326Sed *    documentation and/or other materials provided with the distribution.
14208600Srdivacky *
15193326Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17224145Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18199512Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20199512Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21212904Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23212904Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25226633Sdim * SUCH DAMAGE.
26224145Sdim */
27198092Srdivacky
28218893Sdim#include <sys/cdefs.h>
29218893Sdim__FBSDID("$FreeBSD$");
30193326Sed
31218893Sdim/*
32218893Sdim * AR71XX gigabit ethernet driver
33193326Sed */
34193326Sed#include <sys/param.h>
35193326Sed#include <sys/endian.h>
36234353Sdim#include <sys/systm.h>
37224145Sdim#include <sys/sockio.h>
38193326Sed#include <sys/mbuf.h>
39193326Sed#include <sys/malloc.h>
40226633Sdim#include <sys/kernel.h>
41193326Sed#include <sys/module.h>
42198092Srdivacky#include <sys/socket.h>
43193326Sed#include <sys/taskqueue.h>
44234353Sdim
45234353Sdim#include <net/if.h>
46226633Sdim#include <net/if_arp.h>
47226633Sdim#include <net/ethernet.h>
48198092Srdivacky#include <net/if_dl.h>
49234353Sdim#include <net/if_media.h>
50234353Sdim#include <net/if_types.h>
51234353Sdim
52234353Sdim#include <net/bpf.h>
53234353Sdim
54234353Sdim#include <machine/bus.h>
55234353Sdim#include <machine/cache.h>
56212904Sdim#include <machine/resource.h>
57234353Sdim#include <vm/vm_param.h>
58234353Sdim#include <vm/vm.h>
59234353Sdim#include <vm/pmap.h>
60234353Sdim#include <machine/pmap.h>
61234353Sdim#include <sys/bus.h>
62198092Srdivacky#include <sys/rman.h>
63198092Srdivacky
64212904Sdim#include <dev/mii/mii.h>
65212904Sdim#include <dev/mii/miivar.h>
66212904Sdim
67212904Sdim#include <dev/pci/pcireg.h>
68212904Sdim#include <dev/pci/pcivar.h>
69212904Sdim
70212904SdimMODULE_DEPEND(arge, ether, 1, 1, 1);
71212904SdimMODULE_DEPEND(arge, miibus, 1, 1, 1);
72212904Sdim
73212904Sdim#include "miibus_if.h"
74218893Sdim
75218893Sdim#include <mips/atheros/ar71xxreg.h>
76218893Sdim#include <mips/atheros/if_argevar.h>
77218893Sdim
78224145Sdim#undef ARGE_DEBUG
79224145Sdim#ifdef ARGE_DEBUG
80224145Sdim#define dprintf printf
81224145Sdim#else
82224145Sdim#define dprintf(x, arg...)
83224145Sdim#endif
84224145Sdim
85224145Sdimstatic int arge_attach(device_t);
86224145Sdimstatic int arge_detach(device_t);
87224145Sdimstatic int arge_fix_chain(struct mbuf **mp);
88224145Sdimstatic void arge_flush_ddr(struct arge_softc *);
89224145Sdimstatic int arge_ifmedia_upd(struct ifnet *);
90224145Sdimstatic void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
91224145Sdimstatic int arge_ioctl(struct ifnet *, u_long, caddr_t);
92224145Sdimstatic void arge_init(void *);
93224145Sdimstatic void arge_init_locked(struct arge_softc *);
94224145Sdimstatic void arge_link_task(void *, int);
95224145Sdimstatic int arge_miibus_readreg(device_t, int, int);
96234353Sdimstatic void arge_miibus_statchg(device_t);
97234353Sdimstatic int arge_miibus_writereg(device_t, int, int, int);
98234353Sdimstatic int arge_probe(device_t);
99234353Sdimstatic void arge_reset_dma(struct arge_softc *);
100224145Sdimstatic int arge_resume(device_t);
101224145Sdimstatic int arge_rx_ring_init(struct arge_softc *);
102224145Sdimstatic int arge_tx_ring_init(struct arge_softc *);
103224145Sdimstatic void arge_shutdown(device_t);
104224145Sdimstatic void arge_start(struct ifnet *);
105224145Sdimstatic void arge_start_locked(struct ifnet *);
106234353Sdimstatic void arge_stop(struct arge_softc *);
107224145Sdimstatic int arge_suspend(device_t);
108224145Sdim
109224145Sdimstatic void arge_rx_locked(struct arge_softc *);
110224145Sdimstatic void arge_tx_locked(struct arge_softc *);
111224145Sdimstatic void arge_intr(void *);
112224145Sdimstatic int arge_intr_filter(void *);
113224145Sdimstatic void arge_tx_intr(struct arge_softc *, uint32_t);
114224145Sdimstatic void arge_rx_intr(struct arge_softc *, uint32_t);
115224145Sdimstatic void arge_tick(void *);
116226633Sdim
117226633Sdimstatic void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int);
118226633Sdimstatic int arge_dma_alloc(struct arge_softc *);
119226633Sdimstatic void arge_dma_free(struct arge_softc *);
120226633Sdimstatic int arge_newbuf(struct arge_softc *, int);
121226633Sdimstatic __inline void arge_fixup_rx(struct mbuf *);
122226633Sdim
123202879Srdivackystatic device_method_t arge_methods[] = {
124226633Sdim	/* Device interface */
125226633Sdim	DEVMETHOD(device_probe,		arge_probe),
126226633Sdim	DEVMETHOD(device_attach,	arge_attach),
127226633Sdim	DEVMETHOD(device_detach,	arge_detach),
128226633Sdim	DEVMETHOD(device_suspend,	arge_suspend),
129226633Sdim	DEVMETHOD(device_resume,	arge_resume),
130226633Sdim	DEVMETHOD(device_shutdown,	arge_shutdown),
131226633Sdim
132226633Sdim	/* bus interface */
133226633Sdim	DEVMETHOD(bus_print_child,	bus_generic_print_child),
134226633Sdim	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
135202879Srdivacky
136202879Srdivacky	/* MII interface */
137226633Sdim	DEVMETHOD(miibus_readreg,	arge_miibus_readreg),
138226633Sdim	DEVMETHOD(miibus_writereg,	arge_miibus_writereg),
139226633Sdim	DEVMETHOD(miibus_statchg,	arge_miibus_statchg),
140226633Sdim
141226633Sdim	{ 0, 0 }
142226633Sdim};
143226633Sdim
144226633Sdimstatic driver_t arge_driver = {
145226633Sdim	"arge",
146226633Sdim	arge_methods,
147202879Srdivacky	sizeof(struct arge_softc)
148202879Srdivacky};
149226633Sdim
150202879Srdivackystatic devclass_t arge_devclass;
151202879Srdivacky
152202879SrdivackyDRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0);
153223017SdimDRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0);
154221345Sdim
155202879Srdivacky/*
156202879Srdivacky * Flushes all
157202879Srdivacky */
158202879Srdivackystatic void
159202879Srdivackyarge_flush_ddr(struct arge_softc *sc)
160202879Srdivacky{
161202879Srdivacky
162202879Srdivacky	ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1);
163202879Srdivacky	while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1)
164202879Srdivacky		;
165202879Srdivacky
166202879Srdivacky	ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1);
167202879Srdivacky	while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1)
168202879Srdivacky		;
169198092Srdivacky}
170193326Sed
171193326Sedstatic int
172193326Sedarge_probe(device_t dev)
173193326Sed{
174193326Sed
175193326Sed	device_set_desc(dev, "Atheros AR71xx built-in ethernet interface");
176226633Sdim	return (0);
177226633Sdim}
178226633Sdim
179212904Sdimstatic int
180212904Sdimarge_attach(device_t dev)
181212904Sdim{
182212904Sdim	uint8_t			eaddr[ETHER_ADDR_LEN];
183212904Sdim	struct ifnet		*ifp;
184223017Sdim	struct arge_softc	*sc;
185234353Sdim	int			error = 0, rid, phynum;
186234353Sdim	uint32_t		reg;
187234353Sdim
188234353Sdim	sc = device_get_softc(dev);
189223017Sdim	sc->arge_dev = dev;
190212904Sdim	sc->arge_mac_unit = device_get_unit(dev);
191212904Sdim
192212904Sdim	KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)),
193234353Sdim	    ("if_arge: Only MAC0 and MAC1 supported"));
194234353Sdim	if (sc->arge_mac_unit == 0) {
195221345Sdim		sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE0;
196221345Sdim		sc->arge_pll_reg = AR71XX_PLL_ETH_INT0_CLK;
197193326Sed	} else {
198221345Sdim		sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE1;
199221345Sdim		sc->arge_pll_reg = AR71XX_PLL_ETH_INT1_CLK;
200221345Sdim	}
201221345Sdim
202221345Sdim	/*
203226633Sdim	 *  Get which PHY of 5 available we should use for this unit
204221345Sdim	 */
205226633Sdim	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
206226633Sdim	    "phy", &phynum) != 0) {
207221345Sdim		/*
208221345Sdim		 * Use port 4 (WAN) for GE0. For any other port use
209221345Sdim		 * its PHY the same as its unit number
210221345Sdim		 */
211193326Sed		if (sc->arge_mac_unit == 0)
212193326Sed			phynum = 4;
213208600Srdivacky		else
214208600Srdivacky			phynum = sc->arge_mac_unit;
215234353Sdim
216208600Srdivacky		device_printf(dev, "No PHY specified, using %d\n", phynum);
217193326Sed	}
218193326Sed
219193326Sed	sc->arge_phy_num = phynum;
220193326Sed
221193326Sed
222226633Sdim	mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
223193326Sed	    MTX_DEF);
224193326Sed	callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0);
225193326Sed	TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc);
226234353Sdim
227193326Sed	/* Map control/status registers. */
228193326Sed	sc->arge_rid = 0;
229193326Sed	sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
230193326Sed	    &sc->arge_rid, RF_ACTIVE);
231208600Srdivacky
232208600Srdivacky	if (sc->arge_res == NULL) {
233208600Srdivacky		device_printf(dev, "couldn't map memory\n");
234208600Srdivacky		error = ENXIO;
235208600Srdivacky		goto fail;
236208600Srdivacky	}
237208600Srdivacky
238193326Sed	/* Allocate interrupts */
239198092Srdivacky	rid = 0;
240193326Sed	sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
241193326Sed	    RF_SHAREABLE | RF_ACTIVE);
242210299Sed
243210299Sed	if (sc->arge_irq == NULL) {
244226633Sdim		device_printf(dev, "couldn't map interrupt\n");
245226633Sdim		error = ENXIO;
246193326Sed		goto fail;
247193326Sed	}
248193326Sed
249193326Sed	/* Allocate ifnet structure. */
250193326Sed	ifp = sc->arge_ifp = if_alloc(IFT_ETHER);
251193326Sed
252207619Srdivacky	if (ifp == NULL) {
253234353Sdim		device_printf(dev, "couldn't allocate ifnet structure\n");
254234353Sdim		error = ENOSPC;
255198092Srdivacky		goto fail;
256218893Sdim	}
257218893Sdim
258218893Sdim	ifp->if_softc = sc;
259218893Sdim	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
260198092Srdivacky	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
261212904Sdim	ifp->if_ioctl = arge_ioctl;
262212904Sdim	ifp->if_start = arge_start;
263212904Sdim	ifp->if_init = arge_init;
264218893Sdim
265218893Sdim	/* XXX: add real size */
266221345Sdim	IFQ_SET_MAXLEN(&ifp->if_snd, 9);
267221345Sdim	ifp->if_snd.ifq_maxlen = 9;
268226633Sdim	IFQ_SET_READY(&ifp->if_snd);
269226633Sdim
270226633Sdim	ifp->if_capenable = ifp->if_capabilities;
271226633Sdim
272226633Sdim	eaddr[0] = 0x00;
273226633Sdim	eaddr[1] = 0x15;
274226633Sdim	eaddr[2] = 0x6d;
275226633Sdim	eaddr[3] = 0xc1;
276218893Sdim	eaddr[4] = 0x28;
277226633Sdim	eaddr[5] = 0x2e;
278218893Sdim
279218893Sdim	if (arge_dma_alloc(sc) != 0) {
280218893Sdim		error = ENXIO;
281226633Sdim		goto fail;
282218893Sdim	}
283218893Sdim
284218893Sdim	ARGE_WRITE(sc, AR71XX_MAC_CFG1,
285226633Sdim		MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE |
286218893Sdim		MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE);
287218893Sdim
288198092Srdivacky	reg = ARGE_READ(sc, AR71XX_MAC_CFG2);
289198092Srdivacky	reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ;
290198092Srdivacky	ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg);
291198092Srdivacky
292198092Srdivacky	ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536);
293210299Sed
294210299Sed	/* Reset MII bus */
295210299Sed	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET);
296210299Sed	DELAY(100);
297234353Sdim	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28);
298234353Sdim	DELAY(100);
299210299Sed
300210299Sed	/*
301234353Sdim	 * Set all Ethernet address registers to the same initial values
302210299Sed	 * set all four addresses to 66-88-aa-cc-dd-ee
303210299Sed	 */
304210299Sed	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, 0x6dc1282e);
305210299Sed	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, 0x00000015);
306226633Sdim
307210299Sed	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0,
308210299Sed	    FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT);
309210299Sed	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000);
310210299Sed	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff);
311210299Sed
312210299Sed	reg = FIFO_RX_FILTMATCH_ALL;
313210299Sed	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, reg);
314210299Sed
315210299Sed	reg = FIFO_RX_FILTMASK_ALL;
316210299Sed	reg &= ~FIFO_RX_FILTMASK_BYTE_MODE;
317210299Sed	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, reg);
318210299Sed
319210299Sed	/* Do MII setup. */
320210299Sed	if (mii_phy_probe(dev, &sc->arge_miibus,
321212904Sdim	    arge_ifmedia_upd, arge_ifmedia_sts)) {
322212904Sdim		device_printf(dev, "MII without any phy!\n");
323212904Sdim		error = ENXIO;
324212904Sdim		goto fail;
325212904Sdim	}
326212904Sdim
327212904Sdim	/* Call MI attach routine. */
328212904Sdim	ether_ifattach(ifp, eaddr);
329212904Sdim
330226633Sdim	/* Hook interrupt last to avoid having to lock softc */
331212904Sdim	error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE,
332212904Sdim	    arge_intr_filter, arge_intr, sc, &sc->arge_intrhand);
333212904Sdim
334212904Sdim	if (error) {
335212904Sdim		device_printf(dev, "couldn't set up irq\n");
336212904Sdim		ether_ifdetach(ifp);
337212904Sdim		goto fail;
338212904Sdim	}
339212904Sdim
340212904Sdimfail:
341212904Sdim	if (error)
342212904Sdim		arge_detach(dev);
343212904Sdim
344212904Sdim	return (error);
345212904Sdim}
346212904Sdim
347212904Sdimstatic int
348212904Sdimarge_detach(device_t dev)
349218893Sdim{
350218893Sdim	struct arge_softc		*sc = device_get_softc(dev);
351212904Sdim	struct ifnet		*ifp = sc->arge_ifp;
352212904Sdim
353212904Sdim	KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized"));
354212904Sdim
355218893Sdim	/* These should only be active if attach succeeded */
356218893Sdim	if (device_is_attached(dev)) {
357210299Sed		ARGE_LOCK(sc);
358198092Srdivacky		sc->arge_detach = 1;
359198092Srdivacky		arge_stop(sc);
360224145Sdim		ARGE_UNLOCK(sc);
361224145Sdim		taskqueue_drain(taskqueue_swi, &sc->arge_link_task);
362226633Sdim		ether_ifdetach(ifp);
363226633Sdim	}
364224145Sdim
365224145Sdim	if (sc->arge_miibus)
366224145Sdim		device_delete_child(dev, sc->arge_miibus);
367224145Sdim	bus_generic_detach(dev);
368224145Sdim
369224145Sdim	if (sc->arge_intrhand)
370224145Sdim		bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand);
371224145Sdim
372234353Sdim	if (sc->arge_res)
373234353Sdim		bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid,
374234353Sdim		    sc->arge_res);
375224145Sdim
376234353Sdim	if (ifp)
377224145Sdim		if_free(ifp);
378224145Sdim
379224145Sdim	arge_dma_free(sc);
380224145Sdim
381224145Sdim	mtx_destroy(&sc->arge_mtx);
382224145Sdim
383224145Sdim	return (0);
384224145Sdim
385224145Sdim}
386224145Sdim
387226633Sdimstatic int
388224145Sdimarge_suspend(device_t dev)
389224145Sdim{
390224145Sdim
391224145Sdim	panic("%s", __func__);
392224145Sdim	return 0;
393226633Sdim}
394224145Sdim
395224145Sdimstatic int
396224145Sdimarge_resume(device_t dev)
397224145Sdim{
398224145Sdim
399224145Sdim	panic("%s", __func__);
400224145Sdim	return 0;
401198092Srdivacky}
402198092Srdivacky
403234353Sdimstatic void
404234353Sdimarge_shutdown(device_t dev)
405234353Sdim{
406234353Sdim	struct arge_softc	*sc;
407234353Sdim
408234353Sdim	sc = device_get_softc(dev);
409234353Sdim
410234353Sdim	ARGE_LOCK(sc);
411234353Sdim	arge_stop(sc);
412234353Sdim	ARGE_UNLOCK(sc);
413202879Srdivacky}
414202879Srdivacky
415202879Srdivackystatic int
416198092Srdivackyarge_miibus_readreg(device_t dev, int phy, int reg)
417198092Srdivacky{
418198092Srdivacky	struct arge_softc * sc = device_get_softc(dev);
419198092Srdivacky	int i, result;
420198092Srdivacky	uint32_t addr = 0x1000 | (phy << MAC_MII_PHY_ADDR_SHIFT)
421198092Srdivacky	    | (reg & MAC_MII_REG_MASK);
422226633Sdim
423198092Srdivacky	if (phy != sc->arge_phy_num)
424198092Srdivacky		return (0);
425198092Srdivacky
426198092Srdivacky	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
427234353Sdim	ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
428234353Sdim	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ);
429234353Sdim
430234353Sdim	i = ARGE_MII_TIMEOUT;
431234353Sdim	while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) &
432234353Sdim	    MAC_MII_INDICATOR_BUSY) && (i--))
433234353Sdim		DELAY(5);
434234353Sdim
435234353Sdim	if (i < 0) {
436234353Sdim		dprintf("%s timedout\n", __func__);
437234353Sdim		/* XXX: return ERRNO istead? */
438234353Sdim		return (-1);
439234353Sdim	}
440234353Sdim
441234353Sdim	result = ARGE_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK;
442234353Sdim	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
443234353Sdim	dprintf("%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__,
444234353Sdim		 phy, reg, addr, result);
445234353Sdim
446234353Sdim	return (result);
447234353Sdim}
448234353Sdim
449234353Sdimstatic int
450234353Sdimarge_miibus_writereg(device_t dev, int phy, int reg, int data)
451234353Sdim{
452234353Sdim	struct arge_softc * sc = device_get_softc(dev);
453234353Sdim	int i;
454234353Sdim	uint32_t addr = 0x1000
455234353Sdim	    | (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK);
456234353Sdim
457234353Sdim	dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__,
458234353Sdim	    phy, reg, data);
459202879Srdivacky
460202879Srdivacky	ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
461198092Srdivacky	ARGE_WRITE(sc, AR71XX_MAC_MII_CONTROL, data);
462202879Srdivacky
463202879Srdivacky	i = ARGE_MII_TIMEOUT;
464203955Srdivacky	while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) &
465221345Sdim	    MAC_MII_INDICATOR_BUSY) && (i--))
466221345Sdim		DELAY(5);
467226633Sdim
468226633Sdim	if (i < 0) {
469226633Sdim		dprintf("%s timedout\n", __func__);
470202879Srdivacky		/* XXX: return ERRNO istead? */
471221345Sdim		return (-1);
472224145Sdim	}
473202879Srdivacky
474202879Srdivacky	return (0);
475202879Srdivacky}
476203955Srdivacky
477202879Srdivackystatic void
478203955Srdivackyarge_miibus_statchg(device_t dev)
479202879Srdivacky{
480202879Srdivacky	struct arge_softc		*sc;
481218893Sdim
482221345Sdim	sc = device_get_softc(dev);
483218893Sdim	taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task);
484218893Sdim}
485218893Sdim
486218893Sdimstatic void
487218893Sdimarge_link_task(void *arg, int pending)
488218893Sdim{
489218893Sdim	struct arge_softc	*sc;
490224145Sdim	struct mii_data		*mii;
491218893Sdim	struct ifnet		*ifp;
492218893Sdim	uint32_t		media;
493224145Sdim	uint32_t		cfg, ifcontrol, rx_filtmask, pll, sec_cfg;
494224145Sdim
495218893Sdim	sc = (struct arge_softc *)arg;
496202879Srdivacky
497224145Sdim	ARGE_LOCK(sc);
498202879Srdivacky	mii = device_get_softc(sc->arge_miibus);
499226633Sdim	ifp = sc->arge_ifp;
500224145Sdim	if (mii == NULL || ifp == NULL ||
501224145Sdim	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
502202879Srdivacky		ARGE_UNLOCK(sc);
503224145Sdim		return;
504224145Sdim	}
505224145Sdim
506224145Sdim	if (mii->mii_media_status & IFM_ACTIVE) {
507224145Sdim
508226633Sdim		media = IFM_SUBTYPE(mii->mii_media_active);
509224145Sdim
510224145Sdim		if (media != IFM_NONE) {
511224145Sdim			sc->arge_link_status = 1;
512226633Sdim
513224145Sdim			cfg = ARGE_READ(sc, AR71XX_MAC_CFG2);
514224145Sdim			ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL);
515224145Sdim			rx_filtmask =
516224145Sdim			    ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK);
517224145Sdim
518224145Sdim			cfg &= ~(MAC_CFG2_IFACE_MODE_1000
519224145Sdim			    | MAC_CFG2_IFACE_MODE_10_100
520224145Sdim			    | MAC_CFG2_FULL_DUPLEX);
521224145Sdim			ifcontrol &= ~MAC_IFCONTROL_SPEED;
522224145Sdim			rx_filtmask &= ~FIFO_RX_FILTMASK_BYTE_MODE;
523224145Sdim
524224145Sdim			switch(media) {
525224145Sdim			case IFM_10_T:
526198092Srdivacky				cfg |= MAC_CFG2_IFACE_MODE_10_100;
527198092Srdivacky				pll = PLL_ETH_INT_CLK_10;
528212904Sdim				break;
529201361Srdivacky			case IFM_100_TX:
530193326Sed				cfg |= MAC_CFG2_IFACE_MODE_10_100;
531203955Srdivacky				ifcontrol |= MAC_IFCONTROL_SPEED;
532221345Sdim				pll = PLL_ETH_INT_CLK_100;
533221345Sdim				break;
534221345Sdim			case IFM_1000_T:
535224145Sdim			case IFM_1000_SX:
536224145Sdim				cfg |= MAC_CFG2_IFACE_MODE_1000;
537224145Sdim				rx_filtmask |= FIFO_RX_FILTMASK_BYTE_MODE;
538224145Sdim				pll = PLL_ETH_INT_CLK_1000;
539224145Sdim				break;
540234353Sdim			default:
541224145Sdim				pll = PLL_ETH_INT_CLK_100;
542224145Sdim				device_printf(sc->arge_dev,
543226633Sdim				    "Unknown media %d\n", media);
544224145Sdim			}
545226633Sdim
546224145Sdim			ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD,
547224145Sdim			    0x008001ff);
548224145Sdim
549224145Sdim			ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg);
550226633Sdim			ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol);
551226633Sdim			ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
552224145Sdim			    rx_filtmask);
553224145Sdim
554224145Sdim			/* set PLL registers */
555224145Sdim			sec_cfg = ATH_READ_REG(AR71XX_PLL_CPU_CONFIG);
556224145Sdim			sec_cfg &= ~(3 << 17);
557224145Sdim			sec_cfg |= (2 << 17);
558221345Sdim
559226633Sdim			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
560193326Sed			DELAY(100);
561221345Sdim
562221345Sdim			ATH_WRITE_REG(sc->arge_pll_reg, pll);
563221345Sdim
564226633Sdim			sec_cfg |= (3 << 17);
565221345Sdim			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
566221345Sdim			DELAY(100);
567221345Sdim
568221345Sdim			sec_cfg &= ~(3 << 17);
569226633Sdim			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
570203955Srdivacky			DELAY(100);
571226633Sdim		}
572226633Sdim	} else
573226633Sdim		sc->arge_link_status = 0;
574226633Sdim
575226633Sdim	ARGE_UNLOCK(sc);
576226633Sdim}
577226633Sdim
578226633Sdimstatic void
579226633Sdimarge_reset_dma(struct arge_softc *sc)
580203955Srdivacky{
581226633Sdim	unsigned int i;
582226633Sdim
583234982Sdim	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0);
584226633Sdim	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0);
585226633Sdim
586226633Sdim	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0);
587226633Sdim	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0);
588226633Sdim
589226633Sdim	/* Clear all possible RX interrupts */
590226633Sdim	for (i = 0; i < ARGE_RX_RING_COUNT; i++)
591226633Sdim		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
592226633Sdim
593203955Srdivacky	/*
594226633Sdim	 * Clear all possible TX interrupts
595226633Sdim	 */
596226633Sdim	for (i = 0; i < ARGE_TX_RING_COUNT; i++)
597226633Sdim		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
598226633Sdim
599226633Sdim	/*
600221345Sdim	 * Now Rx/Tx errors
601193326Sed	 */
602203955Srdivacky	ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS,
603221345Sdim	    DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW);
604221345Sdim	ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS,
605226633Sdim	    DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN);
606226633Sdim}
607221345Sdim
608226633Sdim
609221345Sdim
610221345Sdimstatic void
611221345Sdimarge_init(void *xsc)
612221345Sdim{
613221345Sdim	struct arge_softc	 *sc = xsc;
614226633Sdim
615203955Srdivacky	ARGE_LOCK(sc);
616203955Srdivacky	arge_init_locked(sc);
617226633Sdim	ARGE_UNLOCK(sc);
618203955Srdivacky}
619226633Sdim
620203955Srdivackystatic void
621193326Sedarge_init_locked(struct arge_softc *sc)
622226633Sdim{
623198092Srdivacky	struct ifnet		*ifp = sc->arge_ifp;
624212904Sdim	struct mii_data		*mii;
625212904Sdim
626226633Sdim	ARGE_LOCK_ASSERT(sc);
627203955Srdivacky
628221345Sdim	mii = device_get_softc(sc->arge_miibus);
629221345Sdim
630226633Sdim	arge_stop(sc);
631221345Sdim
632221345Sdim	/* Init circular RX list. */
633221345Sdim	if (arge_rx_ring_init(sc) != 0) {
634221345Sdim		device_printf(sc->arge_dev,
635198092Srdivacky		    "initialization failed: no memory for rx buffers\n");
636210299Sed		arge_stop(sc);
637210299Sed		return;
638212904Sdim	}
639212904Sdim
640198092Srdivacky	/* Init tx descriptors. */
641193326Sed	arge_tx_ring_init(sc);
642198092Srdivacky
643221345Sdim	arge_reset_dma(sc);
644221345Sdim
645221345Sdim	sc->arge_link_status = 0;
646226633Sdim	mii_mediachg(mii);
647221345Sdim
648221345Sdim	ifp->if_drv_flags |= IFF_DRV_RUNNING;
649221345Sdim	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
650203955Srdivacky
651203955Srdivacky	callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
652203955Srdivacky	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0));
653203955Srdivacky	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0));
654221345Sdim
655203955Srdivacky	/* Start listening */
656203955Srdivacky	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
657221345Sdim
658226633Sdim	/* Enable interrupts */
659203955Srdivacky	ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
660203955Srdivacky}
661221345Sdim
662221345Sdim/*
663221345Sdim * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
664203955Srdivacky * pointers to the fragment pointers.
665203955Srdivacky */
666226633Sdimstatic int
667221345Sdimarge_encap(struct arge_softc *sc, struct mbuf **m_head)
668203955Srdivacky{
669221345Sdim	struct arge_txdesc	*txd;
670221345Sdim	struct arge_desc	*desc, *prev_desc;
671221345Sdim	bus_dma_segment_t	txsegs[ARGE_MAXFRAGS];
672221345Sdim	int			error, i, nsegs, prod, si, prev_prod;
673221345Sdim
674221345Sdim	ARGE_LOCK_ASSERT(sc);
675221345Sdim
676221345Sdim	prod = sc->arge_cdata.arge_tx_prod;
677221345Sdim	txd = &sc->arge_cdata.arge_txdesc[prod];
678221345Sdim	error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag,
679221345Sdim	    txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT);
680221345Sdim
681212904Sdim	if (error == EFBIG) {
682203955Srdivacky		panic("EFBIG");
683218893Sdim	} else if (error != 0)
684218893Sdim		return (error);
685218893Sdim
686218893Sdim	if (nsegs == 0) {
687218893Sdim		m_freem(*m_head);
688218893Sdim		*m_head = NULL;
689218893Sdim		return (EIO);
690218893Sdim	}
691218893Sdim
692218893Sdim	/* Check number of available descriptors. */
693218893Sdim	if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) {
694218893Sdim		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
695218893Sdim		return (ENOBUFS);
696218893Sdim	}
697218893Sdim
698218893Sdim	txd->tx_m = *m_head;
699218893Sdim	bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
700218893Sdim	    BUS_DMASYNC_PREWRITE);
701218893Sdim
702218893Sdim	si = prod;
703218893Sdim
704218893Sdim	/*
705218893Sdim	 * Make a list of descriptors for this packet. DMA controller will
706218893Sdim	 * walk through it while arge_link is not zero.
707218893Sdim	 */
708218893Sdim	prev_prod = prod;
709218893Sdim	desc = prev_desc = NULL;
710218893Sdim	for (i = 0; i < nsegs; i++) {
711218893Sdim		desc = &sc->arge_rdata.arge_tx_ring[prod];
712218893Sdim		desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len);
713218893Sdim
714218893Sdim		desc->packet_addr = txsegs[i].ds_addr;
715218893Sdim		/* link with previous descriptor */
716218893Sdim		if (prev_desc)
717234353Sdim			prev_desc->packet_ctrl |= ARGE_DESC_MORE;
718234353Sdim
719218893Sdim		sc->arge_cdata.arge_tx_cnt++;
720218893Sdim		prev_desc = desc;
721218893Sdim		ARGE_INC(prod, ARGE_TX_RING_COUNT);
722218893Sdim	}
723218893Sdim
724218893Sdim	/* Update producer index. */
725218893Sdim	sc->arge_cdata.arge_tx_prod = prod;
726218893Sdim
727218893Sdim	/* Sync descriptors. */
728218893Sdim	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
729218893Sdim	    sc->arge_cdata.arge_tx_ring_map,
730218893Sdim	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
731218893Sdim
732218893Sdim	/* Start transmitting */
733218893Sdim	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN);
734218893Sdim	return (0);
735218893Sdim}
736218893Sdim
737218893Sdimstatic void
738218893Sdimarge_start(struct ifnet *ifp)
739218893Sdim{
740218893Sdim	struct arge_softc	 *sc;
741218893Sdim
742218893Sdim	sc = ifp->if_softc;
743223017Sdim
744218893Sdim	ARGE_LOCK(sc);
745218893Sdim	arge_start_locked(ifp);
746218893Sdim	ARGE_UNLOCK(sc);
747218893Sdim}
748218893Sdim
749218893Sdimstatic void
750218893Sdimarge_start_locked(struct ifnet *ifp)
751212904Sdim{
752212904Sdim	struct arge_softc	*sc;
753212904Sdim	struct mbuf		*m_head;
754212904Sdim	int			enq;
755212904Sdim
756212904Sdim	sc = ifp->if_softc;
757212904Sdim
758212904Sdim	ARGE_LOCK_ASSERT(sc);
759212904Sdim
760212904Sdim	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
761212904Sdim	    IFF_DRV_RUNNING || sc->arge_link_status == 0 )
762212904Sdim		return;
763210299Sed
764210299Sed	arge_flush_ddr(sc);
765193326Sed
766193326Sed	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
767193326Sed	    sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) {
768224145Sdim		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
769224145Sdim		if (m_head == NULL)
770224145Sdim			break;
771193326Sed
772226633Sdim		/*
773224145Sdim		 * Fix mbuf chain, all fragments should be 4 bytes aligned and
774224145Sdim		 * even 4 bytes
775193326Sed		 */
776193326Sed		arge_fix_chain(&m_head);
777218893Sdim
778210299Sed		if (m_head == NULL) {
779210299Sed			dprintf("failed to adjust mbuf chain\n");
780193326Sed		}
781198092Srdivacky
782193326Sed		/*
783193326Sed		 * Pack the data into the transmit ring.
784193326Sed		 */
785193326Sed		if (arge_encap(sc, &m_head)) {
786193326Sed			if (m_head == NULL)
787193326Sed				break;
788193326Sed			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
789193326Sed			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
790221345Sdim			break;
791226633Sdim		}
792193326Sed
793193326Sed		enq++;
794221345Sdim		/*
795226633Sdim		 * If there's a BPF listener, bounce a copy of this frame
796221345Sdim		 * to him.
797221345Sdim		 */
798193326Sed		ETHER_BPF_MTAP(ifp, m_head);
799193326Sed	}
800193326Sed}
801193326Sed
802210299Sedstatic void
803210299Sedarge_stop(struct arge_softc *sc)
804218893Sdim{
805218893Sdim	struct ifnet	    *ifp;
806218893Sdim
807218893Sdim	ARGE_LOCK_ASSERT(sc);
808218893Sdim
809218893Sdim	ifp = sc->arge_ifp;
810218893Sdim	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
811218893Sdim	callout_stop(&sc->arge_stat_callout);
812218893Sdim
813218893Sdim	/* mask out interrupts */
814223017Sdim	ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
815218893Sdim
816218893Sdim	arge_reset_dma(sc);
817218893Sdim}
818198092Srdivacky
819193326Sed
820193326Sedstatic int
821193326Sedarge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
822193326Sed{
823199512Srdivacky	struct arge_softc		*sc = ifp->if_softc;
824193326Sed	struct ifreq		*ifr = (struct ifreq *) data;
825193326Sed	struct mii_data		*mii;
826193326Sed	int			error;
827193326Sed
828193326Sed	switch (command) {
829193326Sed	case SIOCSIFFLAGS:
830193326Sed		printf("Implement me: SIOCSIFFLAGS\n");
831210299Sed		error = 0;
832193326Sed		break;
833198092Srdivacky	case SIOCADDMULTI:
834193326Sed	case SIOCDELMULTI:
835210299Sed		printf("Implement me: SIOCDELMULTI\n");
836210299Sed		error = 0;
837193326Sed		break;
838193326Sed	case SIOCGIFMEDIA:
839193326Sed	case SIOCSIFMEDIA:
840210299Sed		printf("Implement me: SIOCSIFMEDIA\n");
841210299Sed		mii = device_get_softc(sc->arge_miibus);
842210299Sed		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
843193326Sed		break;
844193326Sed	case SIOCSIFCAP:
845193326Sed		error = 0;
846210299Sed		ifp->if_hwassist = 0;
847210299Sed		printf("Implement me: SIOCSIFCAP\n");
848210299Sed		break;
849193326Sed	default:
850193326Sed		error = ether_ioctl(ifp, command, data);
851193326Sed		break;
852210299Sed	}
853193326Sed
854193326Sed	return (error);
855193326Sed}
856210299Sed
857193326Sed/*
858193326Sed * Set media options.
859193326Sed */
860210299Sedstatic int
861193326Sedarge_ifmedia_upd(struct ifnet *ifp)
862193326Sed{
863193326Sed	struct arge_softc		*sc;
864210299Sed	struct mii_data		*mii;
865210299Sed	struct mii_softc	*miisc;
866193326Sed	int			error;
867193326Sed
868193326Sed	sc = ifp->if_softc;
869210299Sed	ARGE_LOCK(sc);
870210299Sed	mii = device_get_softc(sc->arge_miibus);
871193326Sed	if (mii->mii_instance) {
872193326Sed		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
873193326Sed			mii_phy_reset(miisc);
874210299Sed	}
875193326Sed	error = mii_mediachg(mii);
876193326Sed	ARGE_UNLOCK(sc);
877193326Sed
878210299Sed	return (error);
879193326Sed}
880193326Sed
881193326Sed/*
882193326Sed * Report current media status.
883198092Srdivacky */
884198092Srdivackystatic void
885199512Srdivackyarge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
886210299Sed{
887198092Srdivacky	struct arge_softc		*sc = ifp->if_softc;
888198092Srdivacky	struct mii_data		*mii;
889198092Srdivacky
890198092Srdivacky	mii = device_get_softc(sc->arge_miibus);
891226633Sdim	ARGE_LOCK(sc);
892198092Srdivacky	mii_pollstat(mii);
893198092Srdivacky	ARGE_UNLOCK(sc);
894198092Srdivacky	ifmr->ifm_active = mii->mii_media_active;
895198092Srdivacky	ifmr->ifm_status = mii->mii_media_status;
896198092Srdivacky}
897198092Srdivacky
898198092Srdivackystruct arge_dmamap_arg {
899198092Srdivacky	bus_addr_t	arge_busaddr;
900210299Sed};
901198092Srdivacky
902210299Sedstatic void
903198092Srdivackyarge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
904210299Sed{
905198092Srdivacky	struct arge_dmamap_arg	*ctx;
906210299Sed
907198092Srdivacky	if (error != 0)
908210299Sed		return;
909198092Srdivacky	ctx = arg;
910210299Sed	ctx->arge_busaddr = segs[0].ds_addr;
911198092Srdivacky}
912210299Sed
913198092Srdivackystatic int
914210299Sedarge_dma_alloc(struct arge_softc *sc)
915198092Srdivacky{
916198092Srdivacky	struct arge_dmamap_arg	ctx;
917210299Sed	struct arge_txdesc	*txd;
918193326Sed	struct arge_rxdesc	*rxd;
919198092Srdivacky	int			error, i;
920198092Srdivacky
921198092Srdivacky	/* Create parent DMA tag. */
922210299Sed	error = bus_dma_tag_create(
923198092Srdivacky	    bus_get_dma_tag(sc->arge_dev),	/* parent */
924210299Sed	    1, 0,			/* alignment, boundary */
925198092Srdivacky	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
926210299Sed	    BUS_SPACE_MAXADDR,		/* highaddr */
927198092Srdivacky	    NULL, NULL,			/* filter, filterarg */
928210299Sed	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
929198092Srdivacky	    0,				/* nsegments */
930210299Sed	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
931198092Srdivacky	    0,				/* flags */
932210299Sed	    NULL, NULL,			/* lockfunc, lockarg */
933198092Srdivacky	    &sc->arge_cdata.arge_parent_tag);
934210299Sed	if (error != 0) {
935193326Sed		device_printf(sc->arge_dev, "failed to create parent DMA tag\n");
936198092Srdivacky		goto fail;
937210299Sed	}
938198092Srdivacky	/* Create tag for Tx ring. */
939198092Srdivacky	error = bus_dma_tag_create(
940210299Sed	    sc->arge_cdata.arge_parent_tag,	/* parent */
941198092Srdivacky	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
942210299Sed	    BUS_SPACE_MAXADDR,		/* lowaddr */
943198092Srdivacky	    BUS_SPACE_MAXADDR,		/* highaddr */
944210299Sed	    NULL, NULL,			/* filter, filterarg */
945198092Srdivacky	    ARGE_TX_DMA_SIZE,		/* maxsize */
946210299Sed	    1,				/* nsegments */
947198092Srdivacky	    ARGE_TX_DMA_SIZE,		/* maxsegsize */
948210299Sed	    0,				/* flags */
949198092Srdivacky	    NULL, NULL,			/* lockfunc, lockarg */
950210299Sed	    &sc->arge_cdata.arge_tx_ring_tag);
951198092Srdivacky	if (error != 0) {
952198092Srdivacky		device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n");
953200583Srdivacky		goto fail;
954198092Srdivacky	}
955198092Srdivacky
956212904Sdim	/* Create tag for Rx ring. */
957212904Sdim	error = bus_dma_tag_create(
958212904Sdim	    sc->arge_cdata.arge_parent_tag,	/* parent */
959212904Sdim	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
960212904Sdim	    BUS_SPACE_MAXADDR,		/* lowaddr */
961226633Sdim	    BUS_SPACE_MAXADDR,		/* highaddr */
962226633Sdim	    NULL, NULL,			/* filter, filterarg */
963226633Sdim	    ARGE_RX_DMA_SIZE,		/* maxsize */
964226633Sdim	    1,				/* nsegments */
965226633Sdim	    ARGE_RX_DMA_SIZE,		/* maxsegsize */
966226633Sdim	    0,				/* flags */
967226633Sdim	    NULL, NULL,			/* lockfunc, lockarg */
968226633Sdim	    &sc->arge_cdata.arge_rx_ring_tag);
969226633Sdim	if (error != 0) {
970226633Sdim		device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n");
971226633Sdim		goto fail;
972226633Sdim	}
973226633Sdim
974226633Sdim	/* Create tag for Tx buffers. */
975226633Sdim	error = bus_dma_tag_create(
976226633Sdim	    sc->arge_cdata.arge_parent_tag,	/* parent */
977226633Sdim	    sizeof(uint32_t), 0,	/* alignment, boundary */
978226633Sdim	    BUS_SPACE_MAXADDR,		/* lowaddr */
979226633Sdim	    BUS_SPACE_MAXADDR,		/* highaddr */
980226633Sdim	    NULL, NULL,			/* filter, filterarg */
981226633Sdim	    MCLBYTES * ARGE_MAXFRAGS,	/* maxsize */
982226633Sdim	    ARGE_MAXFRAGS,		/* nsegments */
983193326Sed	    MCLBYTES,			/* maxsegsize */
984198092Srdivacky	    0,				/* flags */
985193326Sed	    NULL, NULL,			/* lockfunc, lockarg */
986198092Srdivacky	    &sc->arge_cdata.arge_tx_tag);
987193326Sed	if (error != 0) {
988193326Sed		device_printf(sc->arge_dev, "failed to create Tx DMA tag\n");
989193326Sed		goto fail;
990193326Sed	}
991193326Sed
992201361Srdivacky	/* Create tag for Rx buffers. */
993201361Srdivacky	error = bus_dma_tag_create(
994201361Srdivacky	    sc->arge_cdata.arge_parent_tag,	/* parent */
995201361Srdivacky	    ARGE_RX_ALIGN, 0,		/* alignment, boundary */
996201361Srdivacky	    BUS_SPACE_MAXADDR,		/* lowaddr */
997201361Srdivacky	    BUS_SPACE_MAXADDR,		/* highaddr */
998203955Srdivacky	    NULL, NULL,			/* filter, filterarg */
999203955Srdivacky	    MCLBYTES,			/* maxsize */
1000203955Srdivacky	    1,				/* nsegments */
1001203955Srdivacky	    MCLBYTES,			/* maxsegsize */
1002203955Srdivacky	    0,				/* flags */
1003203955Srdivacky	    NULL, NULL,			/* lockfunc, lockarg */
1004198092Srdivacky	    &sc->arge_cdata.arge_rx_tag);
1005193326Sed	if (error != 0) {
1006193326Sed		device_printf(sc->arge_dev, "failed to create Rx DMA tag\n");
1007193326Sed		goto fail;
1008198092Srdivacky	}
1009193326Sed
1010193326Sed	/* Allocate DMA'able memory and load the DMA map for Tx ring. */
1011193326Sed	error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag,
1012193326Sed	    (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK |
1013193326Sed	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map);
1014221345Sdim	if (error != 0) {
1015221345Sdim		device_printf(sc->arge_dev,
1016221345Sdim		    "failed to allocate DMA'able memory for Tx ring\n");
1017221345Sdim		goto fail;
1018221345Sdim	}
1019207619Srdivacky
1020207619Srdivacky	ctx.arge_busaddr = 0;
1021207619Srdivacky	error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag,
1022207619Srdivacky	    sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring,
1023207619Srdivacky	    ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1024234353Sdim	if (error != 0 || ctx.arge_busaddr == 0) {
1025234353Sdim		device_printf(sc->arge_dev,
1026234353Sdim		    "failed to load DMA'able memory for Tx ring\n");
1027234353Sdim		goto fail;
1028212904Sdim	}
1029226633Sdim	sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr;
1030226633Sdim
1031226633Sdim	/* Allocate DMA'able memory and load the DMA map for Rx ring. */
1032212904Sdim	error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag,
1033212904Sdim	    (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK |
1034193326Sed	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map);
1035193326Sed	if (error != 0) {
1036193326Sed		device_printf(sc->arge_dev,
1037193326Sed		    "failed to allocate DMA'able memory for Rx ring\n");
1038234353Sdim		goto fail;
1039234353Sdim	}
1040234353Sdim
1041234353Sdim	ctx.arge_busaddr = 0;
1042234353Sdim	error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag,
1043234353Sdim	    sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring,
1044234353Sdim	    ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1045234353Sdim	if (error != 0 || ctx.arge_busaddr == 0) {
1046234353Sdim		device_printf(sc->arge_dev,
1047234353Sdim		    "failed to load DMA'able memory for Rx ring\n");
1048234353Sdim		goto fail;
1049234353Sdim	}
1050234353Sdim	sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr;
1051234353Sdim
1052234353Sdim	/* Create DMA maps for Tx buffers. */
1053234353Sdim	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1054234353Sdim		txd = &sc->arge_cdata.arge_txdesc[i];
1055234353Sdim		txd->tx_m = NULL;
1056234353Sdim		txd->tx_dmamap = NULL;
1057234353Sdim		error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0,
1058234353Sdim		    &txd->tx_dmamap);
1059234353Sdim		if (error != 0) {
1060234353Sdim			device_printf(sc->arge_dev,
1061234353Sdim			    "failed to create Tx dmamap\n");
1062234353Sdim			goto fail;
1063234353Sdim		}
1064234353Sdim	}
1065234353Sdim	/* Create DMA maps for Rx buffers. */
1066234353Sdim	if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1067234353Sdim	    &sc->arge_cdata.arge_rx_sparemap)) != 0) {
1068234353Sdim		device_printf(sc->arge_dev,
1069234353Sdim		    "failed to create spare Rx dmamap\n");
1070234353Sdim		goto fail;
1071234353Sdim	}
1072234353Sdim	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1073234353Sdim		rxd = &sc->arge_cdata.arge_rxdesc[i];
1074234353Sdim		rxd->rx_m = NULL;
1075234353Sdim		rxd->rx_dmamap = NULL;
1076234353Sdim		error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1077234353Sdim		    &rxd->rx_dmamap);
1078234353Sdim		if (error != 0) {
1079234353Sdim			device_printf(sc->arge_dev,
1080234353Sdim			    "failed to create Rx dmamap\n");
1081234353Sdim			goto fail;
1082234353Sdim		}
1083234353Sdim	}
1084234353Sdim
1085234353Sdimfail:
1086234353Sdim	return (error);
1087234353Sdim}
1088234353Sdim
1089234982Sdimstatic void
1090234353Sdimarge_dma_free(struct arge_softc *sc)
1091234353Sdim{
1092234353Sdim	struct arge_txdesc	*txd;
1093234353Sdim	struct arge_rxdesc	*rxd;
1094234353Sdim	int			i;
1095234353Sdim
1096234353Sdim	/* Tx ring. */
1097234353Sdim	if (sc->arge_cdata.arge_tx_ring_tag) {
1098234353Sdim		if (sc->arge_cdata.arge_tx_ring_map)
1099234353Sdim			bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag,
1100234353Sdim			    sc->arge_cdata.arge_tx_ring_map);
1101234353Sdim		if (sc->arge_cdata.arge_tx_ring_map &&
1102234353Sdim		    sc->arge_rdata.arge_tx_ring)
1103234353Sdim			bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag,
1104234353Sdim			    sc->arge_rdata.arge_tx_ring,
1105234353Sdim			    sc->arge_cdata.arge_tx_ring_map);
1106234353Sdim		sc->arge_rdata.arge_tx_ring = NULL;
1107234982Sdim		sc->arge_cdata.arge_tx_ring_map = NULL;
1108234353Sdim		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag);
1109234353Sdim		sc->arge_cdata.arge_tx_ring_tag = NULL;
1110234353Sdim	}
1111234353Sdim	/* Rx ring. */
1112234353Sdim	if (sc->arge_cdata.arge_rx_ring_tag) {
1113234353Sdim		if (sc->arge_cdata.arge_rx_ring_map)
1114234353Sdim			bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag,
1115234353Sdim			    sc->arge_cdata.arge_rx_ring_map);
1116234353Sdim		if (sc->arge_cdata.arge_rx_ring_map &&
1117234353Sdim		    sc->arge_rdata.arge_rx_ring)
1118234353Sdim			bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag,
1119234353Sdim			    sc->arge_rdata.arge_rx_ring,
1120234353Sdim			    sc->arge_cdata.arge_rx_ring_map);
1121234353Sdim		sc->arge_rdata.arge_rx_ring = NULL;
1122234353Sdim		sc->arge_cdata.arge_rx_ring_map = NULL;
1123234353Sdim		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag);
1124234353Sdim		sc->arge_cdata.arge_rx_ring_tag = NULL;
1125234353Sdim	}
1126234353Sdim	/* Tx buffers. */
1127234353Sdim	if (sc->arge_cdata.arge_tx_tag) {
1128234353Sdim		for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1129234353Sdim			txd = &sc->arge_cdata.arge_txdesc[i];
1130234353Sdim			if (txd->tx_dmamap) {
1131234353Sdim				bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag,
1132234353Sdim				    txd->tx_dmamap);
1133234353Sdim				txd->tx_dmamap = NULL;
1134234353Sdim			}
1135234353Sdim		}
1136234353Sdim		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag);
1137234353Sdim		sc->arge_cdata.arge_tx_tag = NULL;
1138234353Sdim	}
1139234353Sdim	/* Rx buffers. */
1140234353Sdim	if (sc->arge_cdata.arge_rx_tag) {
1141234353Sdim		for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1142234353Sdim			rxd = &sc->arge_cdata.arge_rxdesc[i];
1143234353Sdim			if (rxd->rx_dmamap) {
1144234353Sdim				bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1145234353Sdim				    rxd->rx_dmamap);
1146234353Sdim				rxd->rx_dmamap = NULL;
1147234353Sdim			}
1148234353Sdim		}
1149234353Sdim		if (sc->arge_cdata.arge_rx_sparemap) {
1150234353Sdim			bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1151234353Sdim			    sc->arge_cdata.arge_rx_sparemap);
1152234353Sdim			sc->arge_cdata.arge_rx_sparemap = 0;
1153234353Sdim		}
1154234353Sdim		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag);
1155234353Sdim		sc->arge_cdata.arge_rx_tag = NULL;
1156234353Sdim	}
1157234353Sdim
1158234353Sdim	if (sc->arge_cdata.arge_parent_tag) {
1159234353Sdim		bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag);
1160234353Sdim		sc->arge_cdata.arge_parent_tag = NULL;
1161234353Sdim	}
1162234353Sdim}
1163234353Sdim
1164234353Sdim/*
1165234353Sdim * Initialize the transmit descriptors.
1166234353Sdim */
1167234353Sdimstatic int
1168234353Sdimarge_tx_ring_init(struct arge_softc *sc)
1169234353Sdim{
1170234353Sdim	struct arge_ring_data	*rd;
1171234353Sdim	struct arge_txdesc	*txd;
1172234353Sdim	bus_addr_t		addr;
1173234353Sdim	int			i;
1174234353Sdim
1175234353Sdim	sc->arge_cdata.arge_tx_prod = 0;
1176234353Sdim	sc->arge_cdata.arge_tx_cons = 0;
1177234353Sdim	sc->arge_cdata.arge_tx_cnt = 0;
1178234353Sdim	sc->arge_cdata.arge_tx_pkts = 0;
1179234353Sdim
1180234353Sdim	rd = &sc->arge_rdata;
1181234353Sdim	bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring));
1182234353Sdim	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1183234353Sdim		if (i == ARGE_TX_RING_COUNT - 1)
1184234353Sdim			addr = ARGE_TX_RING_ADDR(sc, 0);
1185234353Sdim		else
1186234353Sdim			addr = ARGE_TX_RING_ADDR(sc, i + 1);
1187234353Sdim		rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
1188234353Sdim		rd->arge_tx_ring[i].next_desc = addr;
1189234353Sdim		txd = &sc->arge_cdata.arge_txdesc[i];
1190234353Sdim		txd->tx_m = NULL;
1191234353Sdim	}
1192234353Sdim
1193234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1194234353Sdim	    sc->arge_cdata.arge_tx_ring_map,
1195234353Sdim	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1196234353Sdim
1197234353Sdim	return (0);
1198234353Sdim}
1199234353Sdim
1200234353Sdim/*
1201234353Sdim * Initialize the RX descriptors and allocate mbufs for them. Note that
1202234353Sdim * we arrange the descriptors in a closed ring, so that the last descriptor
1203234353Sdim * points back to the first.
1204234353Sdim */
1205234353Sdimstatic int
1206234353Sdimarge_rx_ring_init(struct arge_softc *sc)
1207234353Sdim{
1208234353Sdim	struct arge_ring_data	*rd;
1209234353Sdim	struct arge_rxdesc	*rxd;
1210234353Sdim	bus_addr_t		addr;
1211234353Sdim	int			i;
1212234353Sdim
1213234353Sdim	sc->arge_cdata.arge_rx_cons = 0;
1214234353Sdim
1215234353Sdim	rd = &sc->arge_rdata;
1216234353Sdim	bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring));
1217234353Sdim	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1218234353Sdim		rxd = &sc->arge_cdata.arge_rxdesc[i];
1219234353Sdim		rxd->rx_m = NULL;
1220234353Sdim		rxd->desc = &rd->arge_rx_ring[i];
1221234353Sdim		if (i == ARGE_RX_RING_COUNT - 1)
1222234353Sdim			addr = ARGE_RX_RING_ADDR(sc, 0);
1223234353Sdim		else
1224234353Sdim			addr = ARGE_RX_RING_ADDR(sc, i + 1);
1225234353Sdim		rd->arge_rx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
1226234353Sdim		rd->arge_rx_ring[i].next_desc = addr;
1227234353Sdim		if (arge_newbuf(sc, i) != 0)
1228234353Sdim			return (ENOBUFS);
1229234353Sdim	}
1230234353Sdim
1231234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1232234353Sdim	    sc->arge_cdata.arge_rx_ring_map,
1233234353Sdim	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1234234353Sdim
1235234353Sdim	return (0);
1236234353Sdim}
1237234353Sdim
1238234353Sdim/*
1239234353Sdim * Initialize an RX descriptor and attach an MBUF cluster.
1240234353Sdim */
1241234353Sdimstatic int
1242234353Sdimarge_newbuf(struct arge_softc *sc, int idx)
1243234353Sdim{
1244234353Sdim	struct arge_desc		*desc;
1245234353Sdim	struct arge_rxdesc	*rxd;
1246234353Sdim	struct mbuf		*m;
1247234353Sdim	bus_dma_segment_t	segs[1];
1248234353Sdim	bus_dmamap_t		map;
1249234353Sdim	int			nsegs;
1250234353Sdim
1251234353Sdim	m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1252234353Sdim	if (m == NULL)
1253234353Sdim		return (ENOBUFS);
1254234353Sdim	m->m_len = m->m_pkthdr.len = MCLBYTES;
1255234353Sdim	m_adj(m, sizeof(uint64_t));
1256234353Sdim
1257234353Sdim	if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag,
1258234353Sdim	    sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) {
1259234353Sdim		m_freem(m);
1260234353Sdim		return (ENOBUFS);
1261234353Sdim	}
1262234353Sdim	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1263234353Sdim
1264234353Sdim	rxd = &sc->arge_cdata.arge_rxdesc[idx];
1265234353Sdim	if (rxd->rx_m != NULL) {
1266234353Sdim		bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1267234353Sdim		    BUS_DMASYNC_POSTREAD);
1268234353Sdim		bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap);
1269234353Sdim	}
1270234353Sdim	map = rxd->rx_dmamap;
1271234353Sdim	rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap;
1272234353Sdim	sc->arge_cdata.arge_rx_sparemap = map;
1273234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1274234353Sdim	    BUS_DMASYNC_PREREAD);
1275234353Sdim	rxd->rx_m = m;
1276234353Sdim	desc = rxd->desc;
1277234353Sdim	desc->packet_addr = segs[0].ds_addr;
1278234353Sdim	desc->packet_ctrl =  (desc->packet_ctrl & ~ARGE_DESC_SIZE_MASK)
1279234353Sdim	    | ARGE_DMASIZE(segs[0].ds_len);
1280234353Sdim
1281234353Sdim	return (0);
1282234353Sdim}
1283234353Sdim
1284234353Sdimstatic __inline void
1285234353Sdimarge_fixup_rx(struct mbuf *m)
1286234353Sdim{
1287234353Sdim        int		i;
1288234353Sdim        uint16_t	*src, *dst;
1289234353Sdim
1290234353Sdim	src = mtod(m, uint16_t *);
1291234353Sdim	dst = src - 1;
1292234353Sdim
1293234353Sdim	for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
1294234353Sdim		*dst++ = *src++;
1295234353Sdim
1296234353Sdim	m->m_data -= ETHER_ALIGN;
1297234353Sdim}
1298234353Sdim
1299234353Sdim
1300234353Sdimstatic void
1301234353Sdimarge_tx_locked(struct arge_softc *sc)
1302234353Sdim{
1303234353Sdim	struct arge_txdesc	*txd;
1304234353Sdim	struct arge_desc	*cur_tx;
1305234353Sdim	struct ifnet		*ifp;
1306234353Sdim	uint32_t		ctrl;
1307234353Sdim	int			cons, prod;
1308234353Sdim
1309234353Sdim	ARGE_LOCK_ASSERT(sc);
1310234353Sdim
1311234353Sdim	cons = sc->arge_cdata.arge_tx_cons;
1312234353Sdim	prod = sc->arge_cdata.arge_tx_prod;
1313234353Sdim	if (cons == prod)
1314234353Sdim		return;
1315234353Sdim
1316234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1317234353Sdim	    sc->arge_cdata.arge_tx_ring_map,
1318234353Sdim	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1319234353Sdim
1320234353Sdim	ifp = sc->arge_ifp;
1321234353Sdim	/*
1322234353Sdim	 * Go through our tx list and free mbufs for those
1323234353Sdim	 * frames that have been transmitted.
1324234353Sdim	 */
1325234353Sdim	for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) {
1326234353Sdim		cur_tx = &sc->arge_rdata.arge_tx_ring[cons];
1327234353Sdim		ctrl = cur_tx->packet_ctrl;
1328234353Sdim		/* Check if descriptor has "finished" flag */
1329234353Sdim		if ((ctrl & ARGE_DESC_EMPTY) == 0)
1330234353Sdim			break;
1331234353Sdim
1332234353Sdim		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
1333234353Sdim
1334234353Sdim		sc->arge_cdata.arge_tx_cnt--;
1335234353Sdim		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1336234353Sdim
1337234353Sdim		txd = &sc->arge_cdata.arge_txdesc[cons];
1338234353Sdim
1339234353Sdim		cur_tx->packet_ctrl = ARGE_DESC_EMPTY;
1340234353Sdim
1341234353Sdim		ifp->if_opackets++;
1342234353Sdim
1343234353Sdim		bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
1344234353Sdim		    BUS_DMASYNC_POSTWRITE);
1345234353Sdim		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
1346234353Sdim
1347234353Sdim		/* Free only if it's first descriptor in list */
1348234353Sdim		if (txd->tx_m)
1349234353Sdim			m_freem(txd->tx_m);
1350234353Sdim		txd->tx_m = NULL;
1351234353Sdim
1352234353Sdim		/* reset descriptor */
1353234353Sdim		cur_tx->packet_ctrl = ARGE_DESC_EMPTY;
1354234353Sdim		cur_tx->packet_addr = 0;
1355234353Sdim	}
1356234353Sdim
1357234353Sdim	sc->arge_cdata.arge_tx_cons = cons;
1358234353Sdim
1359234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1360234353Sdim	    sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE);
1361234353Sdim}
1362234353Sdim
1363234353Sdim
1364234353Sdimstatic void
1365234353Sdimarge_rx_locked(struct arge_softc *sc)
1366234353Sdim{
1367234353Sdim	struct arge_rxdesc	*rxd;
1368234353Sdim	struct ifnet		*ifp = sc->arge_ifp;
1369234353Sdim	int			cons, prog, packet_len;
1370234353Sdim	struct arge_desc	*cur_rx;
1371234353Sdim	struct mbuf		*m;
1372234353Sdim
1373234353Sdim	ARGE_LOCK_ASSERT(sc);
1374234353Sdim
1375234353Sdim	cons = sc->arge_cdata.arge_rx_cons;
1376234353Sdim
1377234353Sdim	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1378212904Sdim	    sc->arge_cdata.arge_rx_ring_map,
1379221345Sdim	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1380212904Sdim
1381193326Sed	for (prog = 0; prog < ARGE_RX_RING_COUNT;
1382193326Sed	    ARGE_INC(cons, ARGE_RX_RING_COUNT)) {
1383193326Sed		cur_rx = &sc->arge_rdata.arge_rx_ring[cons];
1384193326Sed		rxd = &sc->arge_cdata.arge_rxdesc[cons];
1385193326Sed		m = rxd->rx_m;
1386193326Sed
1387193326Sed		if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0)
1388193326Sed		       break;
1389193326Sed
1390198092Srdivacky		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
1391221345Sdim
1392221345Sdim		prog++;
1393193326Sed
1394201361Srdivacky		packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl);
1395193326Sed		bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1396193326Sed		    BUS_DMASYNC_PREREAD);
1397193326Sed		m = rxd->rx_m;
1398193326Sed
1399193326Sed		arge_fixup_rx(m);
1400193326Sed		m->m_pkthdr.rcvif = ifp;
1401193326Sed		/* Skip 4 bytes of CRC */
1402193326Sed		m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN;
1403193326Sed		ifp->if_ipackets++;
1404226633Sdim
1405193326Sed		ARGE_UNLOCK(sc);
1406193326Sed		(*ifp->if_input)(ifp, m);
1407193326Sed		ARGE_LOCK(sc);
1408193326Sed
1409193326Sed		/* Reinit descriptor */
1410234353Sdim		cur_rx->packet_ctrl = ARGE_DESC_EMPTY;
1411193326Sed		cur_rx->packet_addr = 0;
1412193326Sed		if (arge_newbuf(sc, cons) != 0) {
1413193326Sed			device_printf(sc->arge_dev,
1414193326Sed			    "Failed to allocate buffer\n");
1415193326Sed			break;
1416193326Sed		}
1417193326Sed
1418198092Srdivacky		bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1419193326Sed		    sc->arge_cdata.arge_rx_ring_map,
1420193326Sed		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1421193326Sed
1422193326Sed	}
1423210299Sed
1424210299Sed	if (prog > 0) {
1425226633Sdim		sc->arge_cdata.arge_rx_cons = cons;
1426226633Sdim
1427193326Sed		bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1428193326Sed		    sc->arge_cdata.arge_rx_ring_map,
1429193326Sed		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1430193326Sed	}
1431193326Sed}
1432193326Sed
1433193326Sedstatic void
1434193326Sedarge_rx_intr(struct arge_softc *sc, uint32_t status)
1435193326Sed{
1436193326Sed
1437193326Sed	ARGE_LOCK(sc);
1438193326Sed	/* interrupts are masked by filter */
1439193326Sed	arge_rx_locked(sc);
1440193326Sed
1441193326Sed	/* unmask interrupts */
1442193326Sed	ARGE_SET_BITS(sc,
1443193326Sed	    AR71XX_DMA_INTR, DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD);
1444193326Sed	ARGE_UNLOCK(sc);
1445193326Sed}
1446234353Sdim
1447193326Sedstatic int
1448234353Sdimarge_intr_filter(void *arg)
1449234353Sdim{
1450234353Sdim	struct arge_softc	*sc = arg;
1451234353Sdim	uint32_t		status, ints;
1452234353Sdim
1453234353Sdim	status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
1454234353Sdim	ints = ARGE_READ(sc, AR71XX_DMA_INTR);
1455234353Sdim
1456234353Sdim#if 0
1457234353Sdim	dprintf("int mask(filter) = %b\n", ints,
1458234353Sdim	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
1459234353Sdim	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1460234353Sdim	dprintf("status(filter) = %b\n", status,
1461234353Sdim	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
1462234353Sdim	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1463234353Sdim#endif
1464234353Sdim
1465234353Sdim	if (status & DMA_INTR_ALL) {
1466234353Sdim		if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW))
1467234353Sdim			ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR,
1468234353Sdim			    DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD);
1469234353Sdim
1470234353Sdim		if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN))
1471234353Sdim			ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR,
1472234353Sdim			    DMA_INTR_TX_UNDERRUN | DMA_INTR_TX_PKT_SENT);
1473234353Sdim
1474234353Sdim		sc->arge_intr_status = status;
1475234353Sdim		return (FILTER_SCHEDULE_THREAD);
1476234353Sdim	}
1477234353Sdim
1478234353Sdim	sc->arge_intr_status = 0;
1479234353Sdim	return (FILTER_STRAY);
1480234353Sdim}
1481234353Sdim
1482234353Sdimstatic void
1483234353Sdimarge_intr(void *arg)
1484234353Sdim{
1485234353Sdim	struct arge_softc	*sc = arg;
1486234353Sdim	uint32_t		status;
1487234353Sdim
1488234353Sdim	status = sc->arge_intr_status;
1489234353Sdim
1490234353Sdim#if 0
1491234353Sdim	dprintf("int status(intr) = %b\n", status,
1492234353Sdim	    "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD"
1493234353Sdim	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1494234353Sdim#endif
1495234353Sdim
1496234353Sdim	/*
1497234353Sdim	 * Is it our interrupt at all?
1498234353Sdim	 */
1499234353Sdim	if (status == 0)
1500234353Sdim		return;
1501234353Sdim
1502234353Sdim	if (status & DMA_INTR_RX_BUS_ERROR) {
1503234353Sdim		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR);
1504234353Sdim		device_printf(sc->arge_dev, "RX bus error");
1505234353Sdim		return;
1506234353Sdim	}
1507234353Sdim
1508234353Sdim	if (status & DMA_INTR_TX_BUS_ERROR) {
1509234353Sdim		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR);
1510234353Sdim		device_printf(sc->arge_dev, "TX bus error");
1511234353Sdim		return;
1512234353Sdim	}
1513234353Sdim
1514204793Srdivacky	if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW))
1515204793Srdivacky		arge_rx_intr(sc, status);
1516204793Srdivacky
1517204793Srdivacky	if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN))
1518234353Sdim		arge_tx_intr(sc, status);
1519234353Sdim}
1520204793Srdivacky
1521204793Srdivackystatic void
1522204793Srdivackyarge_tx_intr(struct arge_softc *sc, uint32_t status)
1523204793Srdivacky{
1524204793Srdivacky	ARGE_LOCK(sc);
1525204793Srdivacky
1526204793Srdivacky	/* Interrupts are masked by filter */
1527204793Srdivacky	arge_tx_locked(sc);
1528204793Srdivacky
1529204793Srdivacky	ARGE_UNLOCK(sc);
1530204793Srdivacky}
1531204793Srdivacky
1532204793Srdivackystatic void
1533223017Sdimarge_tick(void *xsc)
1534223017Sdim{
1535204793Srdivacky	struct arge_softc	*sc = xsc;
1536204793Srdivacky	struct mii_data		*mii;
1537204793Srdivacky
1538204793Srdivacky	ARGE_LOCK_ASSERT(sc);
1539204793Srdivacky
1540204793Srdivacky	mii = device_get_softc(sc->arge_miibus);
1541204793Srdivacky	mii_tick(mii);
1542204793Srdivacky	callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
1543204793Srdivacky}
1544204793Srdivacky
1545204793Srdivacky/*
1546204793Srdivacky * Create a copy of a single mbuf. It can have either internal or
1547204793Srdivacky * external data, it may have a packet header. External data is really
1548204793Srdivacky * copied, so the new buffer is writeable.
1549223017Sdim */
1550221345Sdimstatic struct mbuf *
1551221345Sdimcopy_mbuf(struct mbuf *m)
1552204793Srdivacky{
1553204793Srdivacky	struct mbuf *new;
1554204793Srdivacky
1555204793Srdivacky	MGET(new, M_DONTWAIT, MT_DATA);
1556204793Srdivacky	if (new == NULL)
1557204793Srdivacky		return (NULL);
1558204793Srdivacky
1559204793Srdivacky	if (m->m_flags & M_PKTHDR) {
1560204793Srdivacky		M_MOVE_PKTHDR(new, m);
1561204793Srdivacky		if (m->m_len > MHLEN)
1562204793Srdivacky			MCLGET(new, M_WAIT);
1563226633Sdim	} else {
1564204793Srdivacky		if (m->m_len > MLEN)
1565204793Srdivacky			MCLGET(new, M_WAIT);
1566204793Srdivacky	}
1567204793Srdivacky
1568204793Srdivacky	bcopy(m->m_data, new->m_data, m->m_len);
1569195341Sed	new->m_len = m->m_len;
1570195341Sed	new->m_flags &= ~M_RDONLY;
1571234353Sdim
1572234353Sdim	return (new);
1573201361Srdivacky}
1574195341Sed
1575195341Sed
1576195341Sed
1577221345Sdimstatic int
1578221345Sdimarge_fix_chain(struct mbuf **mp)
1579195341Sed{
1580201361Srdivacky	struct mbuf *m = *mp, *prev = NULL, *next, *new;
1581195341Sed	u_int mlen = 0, fill = 0;
1582195341Sed	int first, off;
1583195341Sed	u_char *d, *cp;
1584195341Sed
1585218893Sdim	do {
1586218893Sdim		next = m->m_next;
1587218893Sdim
1588218893Sdim		if ((uintptr_t)mtod(m, void *) % 4 != 0 ||
1589195341Sed		   (m->m_len % 4 != 0 && next)) {
1590195341Sed			/*
1591195341Sed			 * Needs fixing
1592218893Sdim			 */
1593218893Sdim			first = (m == *mp);
1594218893Sdim
1595218893Sdim			d = mtod(m, u_char *);
1596218893Sdim			if ((off = (uintptr_t)(void *)d % 4) != 0) {
1597218893Sdim				if (M_WRITABLE(m)) {
1598218893Sdim					bcopy(d, d - off, m->m_len);
1599195341Sed					m->m_data = (caddr_t)(d - off);
1600195341Sed				} else {
1601195341Sed					if ((new = copy_mbuf(m)) == NULL) {
1602221345Sdim						goto fail;
1603195341Sed					}
1604195341Sed					if (prev)
1605195341Sed						prev->m_next = new;
1606195341Sed					new->m_next = next;
1607195341Sed					m_free(m);
1608195341Sed					m = new;
1609193326Sed				}
1610193326Sed			}
1611234353Sdim
1612234353Sdim			if ((off = m->m_len % 4) != 0) {
1613212904Sdim				if (!M_WRITABLE(m)) {
1614234353Sdim					if ((new = copy_mbuf(m)) == NULL) {
1615234353Sdim						goto fail;
1616234353Sdim					}
1617234353Sdim					if (prev)
1618234982Sdim						prev->m_next = new;
1619234982Sdim					new->m_next = next;
1620234353Sdim					m_free(m);
1621234982Sdim					m = new;
1622193326Sed				}
1623193326Sed				d = mtod(m, u_char *) + m->m_len;
1624221345Sdim				off = 4 - off;
1625221345Sdim				while (off) {
1626193326Sed					if (next == NULL) {
1627201361Srdivacky						*d++ = 0;
1628193326Sed						fill++;
1629193326Sed					} else if (next->m_len == 0) {
1630193326Sed						next = m_free(next);
1631193326Sed						continue;
1632218893Sdim					} else {
1633218893Sdim						cp = mtod(next, u_char *);
1634218893Sdim						*d++ = *cp++;
1635218893Sdim						next->m_len--;
1636193326Sed						next->m_data = (caddr_t)cp;
1637193326Sed					}
1638193326Sed					off--;
1639193326Sed					m->m_len++;
1640218893Sdim				}
1641218893Sdim			}
1642218893Sdim
1643218893Sdim			if (first)
1644218893Sdim				*mp = m;
1645193326Sed		}
1646193326Sed
1647193326Sed		mlen += m->m_len;
1648221345Sdim		prev = m;
1649193326Sed	} while ((m = next) != NULL);
1650193326Sed
1651193326Sed	return (mlen - fill);
1652193326Sed
1653193326Sed  fail:
1654193326Sed	m_freem(*mp);
1655218893Sdim	*mp = NULL;
1656218893Sdim	return (0);
1657234353Sdim}
1658234353Sdim