if_arge.c revision 188808
1188808Sgonzo/*-
2188808Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko
3188808Sgonzo * All rights reserved.
4188808Sgonzo *
5188808Sgonzo * Redistribution and use in source and binary forms, with or without
6188808Sgonzo * modification, are permitted provided that the following conditions
7188808Sgonzo * are met:
8188808Sgonzo * 1. Redistributions of source code must retain the above copyright
9188808Sgonzo *    notice unmodified, this list of conditions, and the following
10188808Sgonzo *    disclaimer.
11188808Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12188808Sgonzo *    notice, this list of conditions and the following disclaimer in the
13188808Sgonzo *    documentation and/or other materials provided with the distribution.
14188808Sgonzo *
15188808Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16188808Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17188808Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18188808Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19188808Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20188808Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21188808Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22188808Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23188808Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24188808Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25188808Sgonzo * SUCH DAMAGE.
26188808Sgonzo */
27188808Sgonzo
28188808Sgonzo#include <sys/cdefs.h>
29188808Sgonzo__FBSDID("$FreeBSD$");
30188808Sgonzo
31188808Sgonzo/*
32188808Sgonzo * AR71XX gigabit ethernet driver
33188808Sgonzo */
34188808Sgonzo#include <sys/param.h>
35188808Sgonzo#include <sys/endian.h>
36188808Sgonzo#include <sys/systm.h>
37188808Sgonzo#include <sys/sockio.h>
38188808Sgonzo#include <sys/mbuf.h>
39188808Sgonzo#include <sys/malloc.h>
40188808Sgonzo#include <sys/kernel.h>
41188808Sgonzo#include <sys/module.h>
42188808Sgonzo#include <sys/socket.h>
43188808Sgonzo#include <sys/taskqueue.h>
44188808Sgonzo
45188808Sgonzo#include <net/if.h>
46188808Sgonzo#include <net/if_arp.h>
47188808Sgonzo#include <net/ethernet.h>
48188808Sgonzo#include <net/if_dl.h>
49188808Sgonzo#include <net/if_media.h>
50188808Sgonzo#include <net/if_types.h>
51188808Sgonzo
52188808Sgonzo#include <net/bpf.h>
53188808Sgonzo
54188808Sgonzo#include <machine/bus.h>
55188808Sgonzo#include <machine/cache.h>
56188808Sgonzo#include <machine/resource.h>
57188808Sgonzo#include <vm/vm_param.h>
58188808Sgonzo#include <vm/vm.h>
59188808Sgonzo#include <vm/pmap.h>
60188808Sgonzo#include <machine/pmap.h>
61188808Sgonzo#include <sys/bus.h>
62188808Sgonzo#include <sys/rman.h>
63188808Sgonzo
64188808Sgonzo#include <dev/mii/mii.h>
65188808Sgonzo#include <dev/mii/miivar.h>
66188808Sgonzo
67188808Sgonzo#include <dev/pci/pcireg.h>
68188808Sgonzo#include <dev/pci/pcivar.h>
69188808Sgonzo
70188808SgonzoMODULE_DEPEND(arge, ether, 1, 1, 1);
71188808SgonzoMODULE_DEPEND(arge, miibus, 1, 1, 1);
72188808Sgonzo
73188808Sgonzo#include "miibus_if.h"
74188808Sgonzo
75188808Sgonzo#include <mips/atheros/ar71xxreg.h>
76188808Sgonzo#include <mips/atheros/if_argevar.h>
77188808Sgonzo
78188808Sgonzo#undef ARGE_DEBUG
79188808Sgonzo#ifdef ARGE_DEBUG
80188808Sgonzo#define dprintf printf
81188808Sgonzo#else
82188808Sgonzo#define dprintf(x, arg...)
83188808Sgonzo#endif
84188808Sgonzo
85188808Sgonzostatic int arge_attach(device_t);
86188808Sgonzostatic int arge_detach(device_t);
87188808Sgonzostatic int arge_fix_chain(struct mbuf **mp);
88188808Sgonzostatic void arge_flush_ddr(struct arge_softc *);
89188808Sgonzostatic int arge_ifmedia_upd(struct ifnet *);
90188808Sgonzostatic void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
91188808Sgonzostatic int arge_ioctl(struct ifnet *, u_long, caddr_t);
92188808Sgonzostatic void arge_init(void *);
93188808Sgonzostatic void arge_init_locked(struct arge_softc *);
94188808Sgonzostatic void arge_link_task(void *, int);
95188808Sgonzostatic int arge_miibus_readreg(device_t, int, int);
96188808Sgonzostatic void arge_miibus_statchg(device_t);
97188808Sgonzostatic int arge_miibus_writereg(device_t, int, int, int);
98188808Sgonzostatic int arge_probe(device_t);
99188808Sgonzostatic void arge_reset_dma(struct arge_softc *);
100188808Sgonzostatic int arge_resume(device_t);
101188808Sgonzostatic int arge_rx_ring_init(struct arge_softc *);
102188808Sgonzostatic int arge_tx_ring_init(struct arge_softc *);
103188808Sgonzostatic void arge_shutdown(device_t);
104188808Sgonzostatic void arge_start(struct ifnet *);
105188808Sgonzostatic void arge_start_locked(struct ifnet *);
106188808Sgonzostatic void arge_stop(struct arge_softc *);
107188808Sgonzostatic int arge_suspend(device_t);
108188808Sgonzo
109188808Sgonzostatic void arge_rx_locked(struct arge_softc *);
110188808Sgonzostatic void arge_tx_locked(struct arge_softc *);
111188808Sgonzostatic void arge_intr(void *);
112188808Sgonzostatic int arge_intr_filter(void *);
113188808Sgonzostatic void arge_tx_intr(struct arge_softc *, uint32_t);
114188808Sgonzostatic void arge_rx_intr(struct arge_softc *, uint32_t);
115188808Sgonzostatic void arge_tick(void *);
116188808Sgonzo
117188808Sgonzostatic void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int);
118188808Sgonzostatic int arge_dma_alloc(struct arge_softc *);
119188808Sgonzostatic void arge_dma_free(struct arge_softc *);
120188808Sgonzostatic int arge_newbuf(struct arge_softc *, int);
121188808Sgonzostatic __inline void arge_fixup_rx(struct mbuf *);
122188808Sgonzo
123188808Sgonzostatic device_method_t arge_methods[] = {
124188808Sgonzo	/* Device interface */
125188808Sgonzo	DEVMETHOD(device_probe,		arge_probe),
126188808Sgonzo	DEVMETHOD(device_attach,	arge_attach),
127188808Sgonzo	DEVMETHOD(device_detach,	arge_detach),
128188808Sgonzo	DEVMETHOD(device_suspend,	arge_suspend),
129188808Sgonzo	DEVMETHOD(device_resume,	arge_resume),
130188808Sgonzo	DEVMETHOD(device_shutdown,	arge_shutdown),
131188808Sgonzo
132188808Sgonzo	/* bus interface */
133188808Sgonzo	DEVMETHOD(bus_print_child,	bus_generic_print_child),
134188808Sgonzo	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
135188808Sgonzo
136188808Sgonzo	/* MII interface */
137188808Sgonzo	DEVMETHOD(miibus_readreg,	arge_miibus_readreg),
138188808Sgonzo	DEVMETHOD(miibus_writereg,	arge_miibus_writereg),
139188808Sgonzo	DEVMETHOD(miibus_statchg,	arge_miibus_statchg),
140188808Sgonzo
141188808Sgonzo	{ 0, 0 }
142188808Sgonzo};
143188808Sgonzo
144188808Sgonzostatic driver_t arge_driver = {
145188808Sgonzo	"arge",
146188808Sgonzo	arge_methods,
147188808Sgonzo	sizeof(struct arge_softc)
148188808Sgonzo};
149188808Sgonzo
150188808Sgonzostatic devclass_t arge_devclass;
151188808Sgonzo
152188808SgonzoDRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0);
153188808SgonzoDRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0);
154188808Sgonzo
155188808Sgonzo/*
156188808Sgonzo * Flushes all
157188808Sgonzo */
158188808Sgonzostatic void
159188808Sgonzoarge_flush_ddr(struct arge_softc *sc)
160188808Sgonzo{
161188808Sgonzo
162188808Sgonzo	ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1);
163188808Sgonzo	while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1)
164188808Sgonzo		;
165188808Sgonzo
166188808Sgonzo	ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1);
167188808Sgonzo	while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1)
168188808Sgonzo		;
169188808Sgonzo}
170188808Sgonzo
171188808Sgonzostatic int
172188808Sgonzoarge_probe(device_t dev)
173188808Sgonzo{
174188808Sgonzo
175188808Sgonzo	device_set_desc(dev, "Atheros AR71xx built-in ethernet interface");
176188808Sgonzo	return (0);
177188808Sgonzo}
178188808Sgonzo
179188808Sgonzostatic int
180188808Sgonzoarge_attach(device_t dev)
181188808Sgonzo{
182188808Sgonzo	uint8_t			eaddr[ETHER_ADDR_LEN];
183188808Sgonzo	struct ifnet		*ifp;
184188808Sgonzo	struct arge_softc	*sc;
185188808Sgonzo	int			error = 0, rid, phynum;
186188808Sgonzo	uint32_t		reg;
187188808Sgonzo
188188808Sgonzo	sc = device_get_softc(dev);
189188808Sgonzo	sc->arge_dev = dev;
190188808Sgonzo	sc->arge_mac_unit = device_get_unit(dev);
191188808Sgonzo
192188808Sgonzo	KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)),
193188808Sgonzo	    ("if_arge: Only MAC0 and MAC1 supported"));
194188808Sgonzo	if (sc->arge_mac_unit == 0) {
195188808Sgonzo		sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE0;
196188808Sgonzo		sc->arge_pll_reg = AR71XX_PLL_ETH_INT0_CLK;
197188808Sgonzo	} else {
198188808Sgonzo		sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE1;
199188808Sgonzo		sc->arge_pll_reg = AR71XX_PLL_ETH_INT1_CLK;
200188808Sgonzo	}
201188808Sgonzo
202188808Sgonzo	/*
203188808Sgonzo	 *  Get which PHY of 5 available we should use for this unit
204188808Sgonzo	 */
205188808Sgonzo	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
206188808Sgonzo	    "phy", &phynum) != 0) {
207188808Sgonzo		/*
208188808Sgonzo		 * Use port 4 (WAN) for GE0. For any other port use
209188808Sgonzo		 * its PHY the same as its unit number
210188808Sgonzo		 */
211188808Sgonzo		if (sc->arge_mac_unit == 0)
212188808Sgonzo			phynum = 4;
213188808Sgonzo		else
214188808Sgonzo			phynum = sc->arge_mac_unit;
215188808Sgonzo
216188808Sgonzo		device_printf(dev, "No PHY specified, using %d\n", phynum);
217188808Sgonzo	}
218188808Sgonzo
219188808Sgonzo	sc->arge_phy_num = phynum;
220188808Sgonzo
221188808Sgonzo
222188808Sgonzo	mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
223188808Sgonzo	    MTX_DEF);
224188808Sgonzo	callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0);
225188808Sgonzo	TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc);
226188808Sgonzo
227188808Sgonzo	/* Map control/status registers. */
228188808Sgonzo	sc->arge_rid = 0;
229188808Sgonzo	sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
230188808Sgonzo	    &sc->arge_rid, RF_ACTIVE);
231188808Sgonzo
232188808Sgonzo	if (sc->arge_res == NULL) {
233188808Sgonzo		device_printf(dev, "couldn't map memory\n");
234188808Sgonzo		error = ENXIO;
235188808Sgonzo		goto fail;
236188808Sgonzo	}
237188808Sgonzo
238188808Sgonzo	/* Allocate interrupts */
239188808Sgonzo	rid = 0;
240188808Sgonzo	sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
241188808Sgonzo	    RF_SHAREABLE | RF_ACTIVE);
242188808Sgonzo
243188808Sgonzo	if (sc->arge_irq == NULL) {
244188808Sgonzo		device_printf(dev, "couldn't map interrupt\n");
245188808Sgonzo		error = ENXIO;
246188808Sgonzo		goto fail;
247188808Sgonzo	}
248188808Sgonzo
249188808Sgonzo	/* Allocate ifnet structure. */
250188808Sgonzo	ifp = sc->arge_ifp = if_alloc(IFT_ETHER);
251188808Sgonzo
252188808Sgonzo	if (ifp == NULL) {
253188808Sgonzo		device_printf(dev, "couldn't allocate ifnet structure\n");
254188808Sgonzo		error = ENOSPC;
255188808Sgonzo		goto fail;
256188808Sgonzo	}
257188808Sgonzo
258188808Sgonzo	ifp->if_softc = sc;
259188808Sgonzo	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
260188808Sgonzo	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
261188808Sgonzo	ifp->if_ioctl = arge_ioctl;
262188808Sgonzo	ifp->if_start = arge_start;
263188808Sgonzo	ifp->if_init = arge_init;
264188808Sgonzo
265188808Sgonzo	/* XXX: add real size */
266188808Sgonzo	IFQ_SET_MAXLEN(&ifp->if_snd, 9);
267188808Sgonzo	ifp->if_snd.ifq_maxlen = 9;
268188808Sgonzo	IFQ_SET_READY(&ifp->if_snd);
269188808Sgonzo
270188808Sgonzo	ifp->if_capenable = ifp->if_capabilities;
271188808Sgonzo
272188808Sgonzo	eaddr[0] = 0x00;
273188808Sgonzo	eaddr[1] = 0x15;
274188808Sgonzo	eaddr[2] = 0x6d;
275188808Sgonzo	eaddr[3] = 0xc1;
276188808Sgonzo	eaddr[4] = 0x28;
277188808Sgonzo	eaddr[5] = 0x2e;
278188808Sgonzo
279188808Sgonzo	if (arge_dma_alloc(sc) != 0) {
280188808Sgonzo		error = ENXIO;
281188808Sgonzo		goto fail;
282188808Sgonzo	}
283188808Sgonzo
284188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_CFG1,
285188808Sgonzo		MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE |
286188808Sgonzo		MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE);
287188808Sgonzo
288188808Sgonzo	reg = ARGE_READ(sc, AR71XX_MAC_CFG2);
289188808Sgonzo	reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ;
290188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg);
291188808Sgonzo
292188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536);
293188808Sgonzo
294188808Sgonzo	/* Reset MII bus */
295188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET);
296188808Sgonzo	DELAY(100);
297188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28);
298188808Sgonzo	DELAY(100);
299188808Sgonzo
300188808Sgonzo	/*
301188808Sgonzo	 * Set all Ethernet address registers to the same initial values
302188808Sgonzo	 * set all four addresses to 66-88-aa-cc-dd-ee
303188808Sgonzo	 */
304188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, 0x6dc1282e);
305188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, 0x00000015);
306188808Sgonzo
307188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0,
308188808Sgonzo	    FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT);
309188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000);
310188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff);
311188808Sgonzo
312188808Sgonzo	reg = FIFO_RX_FILTMATCH_ALL;
313188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, reg);
314188808Sgonzo
315188808Sgonzo	reg = FIFO_RX_FILTMASK_ALL;
316188808Sgonzo	reg &= ~FIFO_RX_FILTMASK_BYTE_MODE;
317188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, reg);
318188808Sgonzo
319188808Sgonzo	/* Do MII setup. */
320188808Sgonzo	if (mii_phy_probe(dev, &sc->arge_miibus,
321188808Sgonzo	    arge_ifmedia_upd, arge_ifmedia_sts)) {
322188808Sgonzo		device_printf(dev, "MII without any phy!\n");
323188808Sgonzo		error = ENXIO;
324188808Sgonzo		goto fail;
325188808Sgonzo	}
326188808Sgonzo
327188808Sgonzo	/* Call MI attach routine. */
328188808Sgonzo	ether_ifattach(ifp, eaddr);
329188808Sgonzo
330188808Sgonzo	/* Hook interrupt last to avoid having to lock softc */
331188808Sgonzo	error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE,
332188808Sgonzo	    arge_intr_filter, arge_intr, sc, &sc->arge_intrhand);
333188808Sgonzo
334188808Sgonzo	if (error) {
335188808Sgonzo		device_printf(dev, "couldn't set up irq\n");
336188808Sgonzo		ether_ifdetach(ifp);
337188808Sgonzo		goto fail;
338188808Sgonzo	}
339188808Sgonzo
340188808Sgonzofail:
341188808Sgonzo	if (error)
342188808Sgonzo		arge_detach(dev);
343188808Sgonzo
344188808Sgonzo	return (error);
345188808Sgonzo}
346188808Sgonzo
347188808Sgonzostatic int
348188808Sgonzoarge_detach(device_t dev)
349188808Sgonzo{
350188808Sgonzo	struct arge_softc		*sc = device_get_softc(dev);
351188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
352188808Sgonzo
353188808Sgonzo	KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized"));
354188808Sgonzo
355188808Sgonzo	/* These should only be active if attach succeeded */
356188808Sgonzo	if (device_is_attached(dev)) {
357188808Sgonzo		ARGE_LOCK(sc);
358188808Sgonzo		sc->arge_detach = 1;
359188808Sgonzo		arge_stop(sc);
360188808Sgonzo		ARGE_UNLOCK(sc);
361188808Sgonzo		taskqueue_drain(taskqueue_swi, &sc->arge_link_task);
362188808Sgonzo		ether_ifdetach(ifp);
363188808Sgonzo	}
364188808Sgonzo
365188808Sgonzo	if (sc->arge_miibus)
366188808Sgonzo		device_delete_child(dev, sc->arge_miibus);
367188808Sgonzo	bus_generic_detach(dev);
368188808Sgonzo
369188808Sgonzo	if (sc->arge_intrhand)
370188808Sgonzo		bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand);
371188808Sgonzo
372188808Sgonzo	if (sc->arge_res)
373188808Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid,
374188808Sgonzo		    sc->arge_res);
375188808Sgonzo
376188808Sgonzo	if (ifp)
377188808Sgonzo		if_free(ifp);
378188808Sgonzo
379188808Sgonzo	arge_dma_free(sc);
380188808Sgonzo
381188808Sgonzo	mtx_destroy(&sc->arge_mtx);
382188808Sgonzo
383188808Sgonzo	return (0);
384188808Sgonzo
385188808Sgonzo}
386188808Sgonzo
387188808Sgonzostatic int
388188808Sgonzoarge_suspend(device_t dev)
389188808Sgonzo{
390188808Sgonzo
391188808Sgonzo	panic("%s", __func__);
392188808Sgonzo	return 0;
393188808Sgonzo}
394188808Sgonzo
395188808Sgonzostatic int
396188808Sgonzoarge_resume(device_t dev)
397188808Sgonzo{
398188808Sgonzo
399188808Sgonzo	panic("%s", __func__);
400188808Sgonzo	return 0;
401188808Sgonzo}
402188808Sgonzo
403188808Sgonzostatic void
404188808Sgonzoarge_shutdown(device_t dev)
405188808Sgonzo{
406188808Sgonzo	struct arge_softc	*sc;
407188808Sgonzo
408188808Sgonzo	sc = device_get_softc(dev);
409188808Sgonzo
410188808Sgonzo	ARGE_LOCK(sc);
411188808Sgonzo	arge_stop(sc);
412188808Sgonzo	ARGE_UNLOCK(sc);
413188808Sgonzo}
414188808Sgonzo
415188808Sgonzostatic int
416188808Sgonzoarge_miibus_readreg(device_t dev, int phy, int reg)
417188808Sgonzo{
418188808Sgonzo	struct arge_softc * sc = device_get_softc(dev);
419188808Sgonzo	int i, result;
420188808Sgonzo	uint32_t addr = 0x1000 | (phy << MAC_MII_PHY_ADDR_SHIFT)
421188808Sgonzo	    | (reg & MAC_MII_REG_MASK);
422188808Sgonzo
423188808Sgonzo	if (phy != sc->arge_phy_num)
424188808Sgonzo		return (0);
425188808Sgonzo
426188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
427188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
428188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ);
429188808Sgonzo
430188808Sgonzo	i = ARGE_MII_TIMEOUT;
431188808Sgonzo	while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) &
432188808Sgonzo	    MAC_MII_INDICATOR_BUSY) && (i--))
433188808Sgonzo		DELAY(5);
434188808Sgonzo
435188808Sgonzo	if (i < 0) {
436188808Sgonzo		dprintf("%s timedout\n", __func__);
437188808Sgonzo		/* XXX: return ERRNO istead? */
438188808Sgonzo		return (-1);
439188808Sgonzo	}
440188808Sgonzo
441188808Sgonzo	result = ARGE_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK;
442188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
443188808Sgonzo	dprintf("%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__,
444188808Sgonzo		 phy, reg, addr, result);
445188808Sgonzo
446188808Sgonzo	return (result);
447188808Sgonzo}
448188808Sgonzo
449188808Sgonzostatic int
450188808Sgonzoarge_miibus_writereg(device_t dev, int phy, int reg, int data)
451188808Sgonzo{
452188808Sgonzo	struct arge_softc * sc = device_get_softc(dev);
453188808Sgonzo	int i;
454188808Sgonzo	uint32_t addr = 0x1000
455188808Sgonzo	    | (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK);
456188808Sgonzo
457188808Sgonzo	dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__,
458188808Sgonzo	    phy, reg, data);
459188808Sgonzo
460188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
461188808Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_MII_CONTROL, data);
462188808Sgonzo
463188808Sgonzo	i = ARGE_MII_TIMEOUT;
464188808Sgonzo	while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) &
465188808Sgonzo	    MAC_MII_INDICATOR_BUSY) && (i--))
466188808Sgonzo		DELAY(5);
467188808Sgonzo
468188808Sgonzo	if (i < 0) {
469188808Sgonzo		dprintf("%s timedout\n", __func__);
470188808Sgonzo		/* XXX: return ERRNO istead? */
471188808Sgonzo		return (-1);
472188808Sgonzo	}
473188808Sgonzo
474188808Sgonzo	return (0);
475188808Sgonzo}
476188808Sgonzo
477188808Sgonzostatic void
478188808Sgonzoarge_miibus_statchg(device_t dev)
479188808Sgonzo{
480188808Sgonzo	struct arge_softc		*sc;
481188808Sgonzo
482188808Sgonzo	sc = device_get_softc(dev);
483188808Sgonzo	taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task);
484188808Sgonzo}
485188808Sgonzo
486188808Sgonzostatic void
487188808Sgonzoarge_link_task(void *arg, int pending)
488188808Sgonzo{
489188808Sgonzo	struct arge_softc	*sc;
490188808Sgonzo	struct mii_data		*mii;
491188808Sgonzo	struct ifnet		*ifp;
492188808Sgonzo	uint32_t		media;
493188808Sgonzo	uint32_t		cfg, ifcontrol, rx_filtmask, pll, sec_cfg;
494188808Sgonzo
495188808Sgonzo	sc = (struct arge_softc *)arg;
496188808Sgonzo
497188808Sgonzo	ARGE_LOCK(sc);
498188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
499188808Sgonzo	ifp = sc->arge_ifp;
500188808Sgonzo	if (mii == NULL || ifp == NULL ||
501188808Sgonzo	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
502188808Sgonzo		ARGE_UNLOCK(sc);
503188808Sgonzo		return;
504188808Sgonzo	}
505188808Sgonzo
506188808Sgonzo	if (mii->mii_media_status & IFM_ACTIVE) {
507188808Sgonzo
508188808Sgonzo		media = IFM_SUBTYPE(mii->mii_media_active);
509188808Sgonzo
510188808Sgonzo		if (media != IFM_NONE) {
511188808Sgonzo			sc->arge_link_status = 1;
512188808Sgonzo
513188808Sgonzo			cfg = ARGE_READ(sc, AR71XX_MAC_CFG2);
514188808Sgonzo			ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL);
515188808Sgonzo			rx_filtmask =
516188808Sgonzo			    ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK);
517188808Sgonzo
518188808Sgonzo			cfg &= ~(MAC_CFG2_IFACE_MODE_1000
519188808Sgonzo			    | MAC_CFG2_IFACE_MODE_10_100
520188808Sgonzo			    | MAC_CFG2_FULL_DUPLEX);
521188808Sgonzo			ifcontrol &= ~MAC_IFCONTROL_SPEED;
522188808Sgonzo			rx_filtmask &= ~FIFO_RX_FILTMASK_BYTE_MODE;
523188808Sgonzo
524188808Sgonzo			switch(media) {
525188808Sgonzo			case IFM_10_T:
526188808Sgonzo				cfg |= MAC_CFG2_IFACE_MODE_10_100;
527188808Sgonzo				pll = PLL_ETH_INT_CLK_10;
528188808Sgonzo				break;
529188808Sgonzo			case IFM_100_TX:
530188808Sgonzo				cfg |= MAC_CFG2_IFACE_MODE_10_100;
531188808Sgonzo				ifcontrol |= MAC_IFCONTROL_SPEED;
532188808Sgonzo				pll = PLL_ETH_INT_CLK_100;
533188808Sgonzo				break;
534188808Sgonzo			case IFM_1000_T:
535188808Sgonzo			case IFM_1000_SX:
536188808Sgonzo				cfg |= MAC_CFG2_IFACE_MODE_1000;
537188808Sgonzo				rx_filtmask |= FIFO_RX_FILTMASK_BYTE_MODE;
538188808Sgonzo				pll = PLL_ETH_INT_CLK_1000;
539188808Sgonzo				break;
540188808Sgonzo			default:
541188808Sgonzo				pll = PLL_ETH_INT_CLK_100;
542188808Sgonzo				device_printf(sc->arge_dev,
543188808Sgonzo				    "Unknown media %d\n", media);
544188808Sgonzo			}
545188808Sgonzo
546188808Sgonzo			ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD,
547188808Sgonzo			    0x008001ff);
548188808Sgonzo
549188808Sgonzo			ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg);
550188808Sgonzo			ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol);
551188808Sgonzo			ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
552188808Sgonzo			    rx_filtmask);
553188808Sgonzo
554188808Sgonzo			/* set PLL registers */
555188808Sgonzo			sec_cfg = ATH_READ_REG(AR71XX_PLL_CPU_CONFIG);
556188808Sgonzo			sec_cfg &= ~(3 << 17);
557188808Sgonzo			sec_cfg |= (2 << 17);
558188808Sgonzo
559188808Sgonzo			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
560188808Sgonzo			DELAY(100);
561188808Sgonzo
562188808Sgonzo			ATH_WRITE_REG(sc->arge_pll_reg, pll);
563188808Sgonzo
564188808Sgonzo			sec_cfg |= (3 << 17);
565188808Sgonzo			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
566188808Sgonzo			DELAY(100);
567188808Sgonzo
568188808Sgonzo			sec_cfg &= ~(3 << 17);
569188808Sgonzo			ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg);
570188808Sgonzo			DELAY(100);
571188808Sgonzo		}
572188808Sgonzo	} else
573188808Sgonzo		sc->arge_link_status = 0;
574188808Sgonzo
575188808Sgonzo	ARGE_UNLOCK(sc);
576188808Sgonzo}
577188808Sgonzo
578188808Sgonzostatic void
579188808Sgonzoarge_reset_dma(struct arge_softc *sc)
580188808Sgonzo{
581188808Sgonzo	unsigned int i;
582188808Sgonzo
583188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0);
584188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0);
585188808Sgonzo
586188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0);
587188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0);
588188808Sgonzo
589188808Sgonzo	/* Clear all possible RX interrupts */
590188808Sgonzo	for (i = 0; i < ARGE_RX_RING_COUNT; i++)
591188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
592188808Sgonzo
593188808Sgonzo	/*
594188808Sgonzo	 * Clear all possible TX interrupts
595188808Sgonzo	 */
596188808Sgonzo	for (i = 0; i < ARGE_TX_RING_COUNT; i++)
597188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
598188808Sgonzo
599188808Sgonzo	/*
600188808Sgonzo	 * Now Rx/Tx errors
601188808Sgonzo	 */
602188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS,
603188808Sgonzo	    DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW);
604188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS,
605188808Sgonzo	    DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN);
606188808Sgonzo}
607188808Sgonzo
608188808Sgonzo
609188808Sgonzo
610188808Sgonzostatic void
611188808Sgonzoarge_init(void *xsc)
612188808Sgonzo{
613188808Sgonzo	struct arge_softc	 *sc = xsc;
614188808Sgonzo
615188808Sgonzo	ARGE_LOCK(sc);
616188808Sgonzo	arge_init_locked(sc);
617188808Sgonzo	ARGE_UNLOCK(sc);
618188808Sgonzo}
619188808Sgonzo
620188808Sgonzostatic void
621188808Sgonzoarge_init_locked(struct arge_softc *sc)
622188808Sgonzo{
623188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
624188808Sgonzo	struct mii_data		*mii;
625188808Sgonzo
626188808Sgonzo	ARGE_LOCK_ASSERT(sc);
627188808Sgonzo
628188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
629188808Sgonzo
630188808Sgonzo	arge_stop(sc);
631188808Sgonzo
632188808Sgonzo	/* Init circular RX list. */
633188808Sgonzo	if (arge_rx_ring_init(sc) != 0) {
634188808Sgonzo		device_printf(sc->arge_dev,
635188808Sgonzo		    "initialization failed: no memory for rx buffers\n");
636188808Sgonzo		arge_stop(sc);
637188808Sgonzo		return;
638188808Sgonzo	}
639188808Sgonzo
640188808Sgonzo	/* Init tx descriptors. */
641188808Sgonzo	arge_tx_ring_init(sc);
642188808Sgonzo
643188808Sgonzo	arge_reset_dma(sc);
644188808Sgonzo
645188808Sgonzo	sc->arge_link_status = 0;
646188808Sgonzo	mii_mediachg(mii);
647188808Sgonzo
648188808Sgonzo	ifp->if_drv_flags |= IFF_DRV_RUNNING;
649188808Sgonzo	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
650188808Sgonzo
651188808Sgonzo	callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
652188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0));
653188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0));
654188808Sgonzo
655188808Sgonzo	/* Start listening */
656188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
657188808Sgonzo
658188808Sgonzo	/* Enable interrupts */
659188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
660188808Sgonzo}
661188808Sgonzo
662188808Sgonzo/*
663188808Sgonzo * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
664188808Sgonzo * pointers to the fragment pointers.
665188808Sgonzo */
666188808Sgonzostatic int
667188808Sgonzoarge_encap(struct arge_softc *sc, struct mbuf **m_head)
668188808Sgonzo{
669188808Sgonzo	struct arge_txdesc	*txd;
670188808Sgonzo	struct arge_desc	*desc, *prev_desc;
671188808Sgonzo	bus_dma_segment_t	txsegs[ARGE_MAXFRAGS];
672188808Sgonzo	int			error, i, nsegs, prod, si, prev_prod;
673188808Sgonzo
674188808Sgonzo	ARGE_LOCK_ASSERT(sc);
675188808Sgonzo
676188808Sgonzo	prod = sc->arge_cdata.arge_tx_prod;
677188808Sgonzo	txd = &sc->arge_cdata.arge_txdesc[prod];
678188808Sgonzo	error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag,
679188808Sgonzo	    txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT);
680188808Sgonzo
681188808Sgonzo	if (error == EFBIG) {
682188808Sgonzo		panic("EFBIG");
683188808Sgonzo	} else if (error != 0)
684188808Sgonzo		return (error);
685188808Sgonzo
686188808Sgonzo	if (nsegs == 0) {
687188808Sgonzo		m_freem(*m_head);
688188808Sgonzo		*m_head = NULL;
689188808Sgonzo		return (EIO);
690188808Sgonzo	}
691188808Sgonzo
692188808Sgonzo	/* Check number of available descriptors. */
693188808Sgonzo	if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) {
694188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
695188808Sgonzo		return (ENOBUFS);
696188808Sgonzo	}
697188808Sgonzo
698188808Sgonzo	txd->tx_m = *m_head;
699188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
700188808Sgonzo	    BUS_DMASYNC_PREWRITE);
701188808Sgonzo
702188808Sgonzo	si = prod;
703188808Sgonzo
704188808Sgonzo	/*
705188808Sgonzo	 * Make a list of descriptors for this packet. DMA controller will
706188808Sgonzo	 * walk through it while arge_link is not zero.
707188808Sgonzo	 */
708188808Sgonzo	prev_prod = prod;
709188808Sgonzo	desc = prev_desc = NULL;
710188808Sgonzo	for (i = 0; i < nsegs; i++) {
711188808Sgonzo		desc = &sc->arge_rdata.arge_tx_ring[prod];
712188808Sgonzo		desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len);
713188808Sgonzo
714188808Sgonzo		desc->packet_addr = txsegs[i].ds_addr;
715188808Sgonzo		/* link with previous descriptor */
716188808Sgonzo		if (prev_desc)
717188808Sgonzo			prev_desc->packet_ctrl |= ARGE_DESC_MORE;
718188808Sgonzo
719188808Sgonzo		sc->arge_cdata.arge_tx_cnt++;
720188808Sgonzo		prev_desc = desc;
721188808Sgonzo		ARGE_INC(prod, ARGE_TX_RING_COUNT);
722188808Sgonzo	}
723188808Sgonzo
724188808Sgonzo	/* Update producer index. */
725188808Sgonzo	sc->arge_cdata.arge_tx_prod = prod;
726188808Sgonzo
727188808Sgonzo	/* Sync descriptors. */
728188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
729188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
730188808Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
731188808Sgonzo
732188808Sgonzo	/* Start transmitting */
733188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN);
734188808Sgonzo	return (0);
735188808Sgonzo}
736188808Sgonzo
737188808Sgonzostatic void
738188808Sgonzoarge_start(struct ifnet *ifp)
739188808Sgonzo{
740188808Sgonzo	struct arge_softc	 *sc;
741188808Sgonzo
742188808Sgonzo	sc = ifp->if_softc;
743188808Sgonzo
744188808Sgonzo	ARGE_LOCK(sc);
745188808Sgonzo	arge_start_locked(ifp);
746188808Sgonzo	ARGE_UNLOCK(sc);
747188808Sgonzo}
748188808Sgonzo
749188808Sgonzostatic void
750188808Sgonzoarge_start_locked(struct ifnet *ifp)
751188808Sgonzo{
752188808Sgonzo	struct arge_softc	*sc;
753188808Sgonzo	struct mbuf		*m_head;
754188808Sgonzo	int			enq;
755188808Sgonzo
756188808Sgonzo	sc = ifp->if_softc;
757188808Sgonzo
758188808Sgonzo	ARGE_LOCK_ASSERT(sc);
759188808Sgonzo
760188808Sgonzo	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
761188808Sgonzo	    IFF_DRV_RUNNING || sc->arge_link_status == 0 )
762188808Sgonzo		return;
763188808Sgonzo
764188808Sgonzo	arge_flush_ddr(sc);
765188808Sgonzo
766188808Sgonzo	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
767188808Sgonzo	    sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) {
768188808Sgonzo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
769188808Sgonzo		if (m_head == NULL)
770188808Sgonzo			break;
771188808Sgonzo
772188808Sgonzo		/*
773188808Sgonzo		 * Fix mbuf chain, all fragments should be 4 bytes aligned and
774188808Sgonzo		 * even 4 bytes
775188808Sgonzo		 */
776188808Sgonzo		arge_fix_chain(&m_head);
777188808Sgonzo
778188808Sgonzo		if (m_head == NULL) {
779188808Sgonzo			dprintf("failed to adjust mbuf chain\n");
780188808Sgonzo		}
781188808Sgonzo
782188808Sgonzo		/*
783188808Sgonzo		 * Pack the data into the transmit ring.
784188808Sgonzo		 */
785188808Sgonzo		if (arge_encap(sc, &m_head)) {
786188808Sgonzo			if (m_head == NULL)
787188808Sgonzo				break;
788188808Sgonzo			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
789188808Sgonzo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
790188808Sgonzo			break;
791188808Sgonzo		}
792188808Sgonzo
793188808Sgonzo		enq++;
794188808Sgonzo		/*
795188808Sgonzo		 * If there's a BPF listener, bounce a copy of this frame
796188808Sgonzo		 * to him.
797188808Sgonzo		 */
798188808Sgonzo		ETHER_BPF_MTAP(ifp, m_head);
799188808Sgonzo	}
800188808Sgonzo}
801188808Sgonzo
802188808Sgonzostatic void
803188808Sgonzoarge_stop(struct arge_softc *sc)
804188808Sgonzo{
805188808Sgonzo	struct ifnet	    *ifp;
806188808Sgonzo
807188808Sgonzo	ARGE_LOCK_ASSERT(sc);
808188808Sgonzo
809188808Sgonzo	ifp = sc->arge_ifp;
810188808Sgonzo	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
811188808Sgonzo	callout_stop(&sc->arge_stat_callout);
812188808Sgonzo
813188808Sgonzo	/* mask out interrupts */
814188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
815188808Sgonzo
816188808Sgonzo	arge_reset_dma(sc);
817188808Sgonzo}
818188808Sgonzo
819188808Sgonzo
820188808Sgonzostatic int
821188808Sgonzoarge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
822188808Sgonzo{
823188808Sgonzo	struct arge_softc		*sc = ifp->if_softc;
824188808Sgonzo	struct ifreq		*ifr = (struct ifreq *) data;
825188808Sgonzo	struct mii_data		*mii;
826188808Sgonzo	int			error;
827188808Sgonzo
828188808Sgonzo	switch (command) {
829188808Sgonzo	case SIOCSIFFLAGS:
830188808Sgonzo		printf("Implement me: SIOCSIFFLAGS\n");
831188808Sgonzo		error = 0;
832188808Sgonzo		break;
833188808Sgonzo	case SIOCADDMULTI:
834188808Sgonzo	case SIOCDELMULTI:
835188808Sgonzo		printf("Implement me: SIOCDELMULTI\n");
836188808Sgonzo		error = 0;
837188808Sgonzo		break;
838188808Sgonzo	case SIOCGIFMEDIA:
839188808Sgonzo	case SIOCSIFMEDIA:
840188808Sgonzo		printf("Implement me: SIOCSIFMEDIA\n");
841188808Sgonzo		mii = device_get_softc(sc->arge_miibus);
842188808Sgonzo		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
843188808Sgonzo		break;
844188808Sgonzo	case SIOCSIFCAP:
845188808Sgonzo		error = 0;
846188808Sgonzo		ifp->if_hwassist = 0;
847188808Sgonzo		printf("Implement me: SIOCSIFCAP\n");
848188808Sgonzo		break;
849188808Sgonzo	default:
850188808Sgonzo		error = ether_ioctl(ifp, command, data);
851188808Sgonzo		break;
852188808Sgonzo	}
853188808Sgonzo
854188808Sgonzo	return (error);
855188808Sgonzo}
856188808Sgonzo
857188808Sgonzo/*
858188808Sgonzo * Set media options.
859188808Sgonzo */
860188808Sgonzostatic int
861188808Sgonzoarge_ifmedia_upd(struct ifnet *ifp)
862188808Sgonzo{
863188808Sgonzo	struct arge_softc		*sc;
864188808Sgonzo	struct mii_data		*mii;
865188808Sgonzo	struct mii_softc	*miisc;
866188808Sgonzo	int			error;
867188808Sgonzo
868188808Sgonzo	sc = ifp->if_softc;
869188808Sgonzo	ARGE_LOCK(sc);
870188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
871188808Sgonzo	if (mii->mii_instance) {
872188808Sgonzo		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
873188808Sgonzo			mii_phy_reset(miisc);
874188808Sgonzo	}
875188808Sgonzo	error = mii_mediachg(mii);
876188808Sgonzo	ARGE_UNLOCK(sc);
877188808Sgonzo
878188808Sgonzo	return (error);
879188808Sgonzo}
880188808Sgonzo
881188808Sgonzo/*
882188808Sgonzo * Report current media status.
883188808Sgonzo */
884188808Sgonzostatic void
885188808Sgonzoarge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
886188808Sgonzo{
887188808Sgonzo	struct arge_softc		*sc = ifp->if_softc;
888188808Sgonzo	struct mii_data		*mii;
889188808Sgonzo
890188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
891188808Sgonzo	ARGE_LOCK(sc);
892188808Sgonzo	mii_pollstat(mii);
893188808Sgonzo	ARGE_UNLOCK(sc);
894188808Sgonzo	ifmr->ifm_active = mii->mii_media_active;
895188808Sgonzo	ifmr->ifm_status = mii->mii_media_status;
896188808Sgonzo}
897188808Sgonzo
898188808Sgonzostruct arge_dmamap_arg {
899188808Sgonzo	bus_addr_t	arge_busaddr;
900188808Sgonzo};
901188808Sgonzo
902188808Sgonzostatic void
903188808Sgonzoarge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
904188808Sgonzo{
905188808Sgonzo	struct arge_dmamap_arg	*ctx;
906188808Sgonzo
907188808Sgonzo	if (error != 0)
908188808Sgonzo		return;
909188808Sgonzo	ctx = arg;
910188808Sgonzo	ctx->arge_busaddr = segs[0].ds_addr;
911188808Sgonzo}
912188808Sgonzo
913188808Sgonzostatic int
914188808Sgonzoarge_dma_alloc(struct arge_softc *sc)
915188808Sgonzo{
916188808Sgonzo	struct arge_dmamap_arg	ctx;
917188808Sgonzo	struct arge_txdesc	*txd;
918188808Sgonzo	struct arge_rxdesc	*rxd;
919188808Sgonzo	int			error, i;
920188808Sgonzo
921188808Sgonzo	/* Create parent DMA tag. */
922188808Sgonzo	error = bus_dma_tag_create(
923188808Sgonzo	    bus_get_dma_tag(sc->arge_dev),	/* parent */
924188808Sgonzo	    1, 0,			/* alignment, boundary */
925188808Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
926188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
927188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
928188808Sgonzo	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
929188808Sgonzo	    0,				/* nsegments */
930188808Sgonzo	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
931188808Sgonzo	    0,				/* flags */
932188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
933188808Sgonzo	    &sc->arge_cdata.arge_parent_tag);
934188808Sgonzo	if (error != 0) {
935188808Sgonzo		device_printf(sc->arge_dev, "failed to create parent DMA tag\n");
936188808Sgonzo		goto fail;
937188808Sgonzo	}
938188808Sgonzo	/* Create tag for Tx ring. */
939188808Sgonzo	error = bus_dma_tag_create(
940188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
941188808Sgonzo	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
942188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
943188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
944188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
945188808Sgonzo	    ARGE_TX_DMA_SIZE,		/* maxsize */
946188808Sgonzo	    1,				/* nsegments */
947188808Sgonzo	    ARGE_TX_DMA_SIZE,		/* maxsegsize */
948188808Sgonzo	    0,				/* flags */
949188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
950188808Sgonzo	    &sc->arge_cdata.arge_tx_ring_tag);
951188808Sgonzo	if (error != 0) {
952188808Sgonzo		device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n");
953188808Sgonzo		goto fail;
954188808Sgonzo	}
955188808Sgonzo
956188808Sgonzo	/* Create tag for Rx ring. */
957188808Sgonzo	error = bus_dma_tag_create(
958188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
959188808Sgonzo	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
960188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
961188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
962188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
963188808Sgonzo	    ARGE_RX_DMA_SIZE,		/* maxsize */
964188808Sgonzo	    1,				/* nsegments */
965188808Sgonzo	    ARGE_RX_DMA_SIZE,		/* maxsegsize */
966188808Sgonzo	    0,				/* flags */
967188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
968188808Sgonzo	    &sc->arge_cdata.arge_rx_ring_tag);
969188808Sgonzo	if (error != 0) {
970188808Sgonzo		device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n");
971188808Sgonzo		goto fail;
972188808Sgonzo	}
973188808Sgonzo
974188808Sgonzo	/* Create tag for Tx buffers. */
975188808Sgonzo	error = bus_dma_tag_create(
976188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
977188808Sgonzo	    sizeof(uint32_t), 0,	/* alignment, boundary */
978188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
979188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
980188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
981188808Sgonzo	    MCLBYTES * ARGE_MAXFRAGS,	/* maxsize */
982188808Sgonzo	    ARGE_MAXFRAGS,		/* nsegments */
983188808Sgonzo	    MCLBYTES,			/* maxsegsize */
984188808Sgonzo	    0,				/* flags */
985188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
986188808Sgonzo	    &sc->arge_cdata.arge_tx_tag);
987188808Sgonzo	if (error != 0) {
988188808Sgonzo		device_printf(sc->arge_dev, "failed to create Tx DMA tag\n");
989188808Sgonzo		goto fail;
990188808Sgonzo	}
991188808Sgonzo
992188808Sgonzo	/* Create tag for Rx buffers. */
993188808Sgonzo	error = bus_dma_tag_create(
994188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
995188808Sgonzo	    ARGE_RX_ALIGN, 0,		/* alignment, boundary */
996188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
997188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
998188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
999188808Sgonzo	    MCLBYTES,			/* maxsize */
1000188808Sgonzo	    1,				/* nsegments */
1001188808Sgonzo	    MCLBYTES,			/* maxsegsize */
1002188808Sgonzo	    0,				/* flags */
1003188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1004188808Sgonzo	    &sc->arge_cdata.arge_rx_tag);
1005188808Sgonzo	if (error != 0) {
1006188808Sgonzo		device_printf(sc->arge_dev, "failed to create Rx DMA tag\n");
1007188808Sgonzo		goto fail;
1008188808Sgonzo	}
1009188808Sgonzo
1010188808Sgonzo	/* Allocate DMA'able memory and load the DMA map for Tx ring. */
1011188808Sgonzo	error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag,
1012188808Sgonzo	    (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK |
1013188808Sgonzo	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map);
1014188808Sgonzo	if (error != 0) {
1015188808Sgonzo		device_printf(sc->arge_dev,
1016188808Sgonzo		    "failed to allocate DMA'able memory for Tx ring\n");
1017188808Sgonzo		goto fail;
1018188808Sgonzo	}
1019188808Sgonzo
1020188808Sgonzo	ctx.arge_busaddr = 0;
1021188808Sgonzo	error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag,
1022188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring,
1023188808Sgonzo	    ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1024188808Sgonzo	if (error != 0 || ctx.arge_busaddr == 0) {
1025188808Sgonzo		device_printf(sc->arge_dev,
1026188808Sgonzo		    "failed to load DMA'able memory for Tx ring\n");
1027188808Sgonzo		goto fail;
1028188808Sgonzo	}
1029188808Sgonzo	sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr;
1030188808Sgonzo
1031188808Sgonzo	/* Allocate DMA'able memory and load the DMA map for Rx ring. */
1032188808Sgonzo	error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag,
1033188808Sgonzo	    (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK |
1034188808Sgonzo	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map);
1035188808Sgonzo	if (error != 0) {
1036188808Sgonzo		device_printf(sc->arge_dev,
1037188808Sgonzo		    "failed to allocate DMA'able memory for Rx ring\n");
1038188808Sgonzo		goto fail;
1039188808Sgonzo	}
1040188808Sgonzo
1041188808Sgonzo	ctx.arge_busaddr = 0;
1042188808Sgonzo	error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag,
1043188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring,
1044188808Sgonzo	    ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1045188808Sgonzo	if (error != 0 || ctx.arge_busaddr == 0) {
1046188808Sgonzo		device_printf(sc->arge_dev,
1047188808Sgonzo		    "failed to load DMA'able memory for Rx ring\n");
1048188808Sgonzo		goto fail;
1049188808Sgonzo	}
1050188808Sgonzo	sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr;
1051188808Sgonzo
1052188808Sgonzo	/* Create DMA maps for Tx buffers. */
1053188808Sgonzo	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1054188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[i];
1055188808Sgonzo		txd->tx_m = NULL;
1056188808Sgonzo		txd->tx_dmamap = NULL;
1057188808Sgonzo		error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0,
1058188808Sgonzo		    &txd->tx_dmamap);
1059188808Sgonzo		if (error != 0) {
1060188808Sgonzo			device_printf(sc->arge_dev,
1061188808Sgonzo			    "failed to create Tx dmamap\n");
1062188808Sgonzo			goto fail;
1063188808Sgonzo		}
1064188808Sgonzo	}
1065188808Sgonzo	/* Create DMA maps for Rx buffers. */
1066188808Sgonzo	if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1067188808Sgonzo	    &sc->arge_cdata.arge_rx_sparemap)) != 0) {
1068188808Sgonzo		device_printf(sc->arge_dev,
1069188808Sgonzo		    "failed to create spare Rx dmamap\n");
1070188808Sgonzo		goto fail;
1071188808Sgonzo	}
1072188808Sgonzo	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1073188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[i];
1074188808Sgonzo		rxd->rx_m = NULL;
1075188808Sgonzo		rxd->rx_dmamap = NULL;
1076188808Sgonzo		error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1077188808Sgonzo		    &rxd->rx_dmamap);
1078188808Sgonzo		if (error != 0) {
1079188808Sgonzo			device_printf(sc->arge_dev,
1080188808Sgonzo			    "failed to create Rx dmamap\n");
1081188808Sgonzo			goto fail;
1082188808Sgonzo		}
1083188808Sgonzo	}
1084188808Sgonzo
1085188808Sgonzofail:
1086188808Sgonzo	return (error);
1087188808Sgonzo}
1088188808Sgonzo
1089188808Sgonzostatic void
1090188808Sgonzoarge_dma_free(struct arge_softc *sc)
1091188808Sgonzo{
1092188808Sgonzo	struct arge_txdesc	*txd;
1093188808Sgonzo	struct arge_rxdesc	*rxd;
1094188808Sgonzo	int			i;
1095188808Sgonzo
1096188808Sgonzo	/* Tx ring. */
1097188808Sgonzo	if (sc->arge_cdata.arge_tx_ring_tag) {
1098188808Sgonzo		if (sc->arge_cdata.arge_tx_ring_map)
1099188808Sgonzo			bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag,
1100188808Sgonzo			    sc->arge_cdata.arge_tx_ring_map);
1101188808Sgonzo		if (sc->arge_cdata.arge_tx_ring_map &&
1102188808Sgonzo		    sc->arge_rdata.arge_tx_ring)
1103188808Sgonzo			bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag,
1104188808Sgonzo			    sc->arge_rdata.arge_tx_ring,
1105188808Sgonzo			    sc->arge_cdata.arge_tx_ring_map);
1106188808Sgonzo		sc->arge_rdata.arge_tx_ring = NULL;
1107188808Sgonzo		sc->arge_cdata.arge_tx_ring_map = NULL;
1108188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag);
1109188808Sgonzo		sc->arge_cdata.arge_tx_ring_tag = NULL;
1110188808Sgonzo	}
1111188808Sgonzo	/* Rx ring. */
1112188808Sgonzo	if (sc->arge_cdata.arge_rx_ring_tag) {
1113188808Sgonzo		if (sc->arge_cdata.arge_rx_ring_map)
1114188808Sgonzo			bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag,
1115188808Sgonzo			    sc->arge_cdata.arge_rx_ring_map);
1116188808Sgonzo		if (sc->arge_cdata.arge_rx_ring_map &&
1117188808Sgonzo		    sc->arge_rdata.arge_rx_ring)
1118188808Sgonzo			bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag,
1119188808Sgonzo			    sc->arge_rdata.arge_rx_ring,
1120188808Sgonzo			    sc->arge_cdata.arge_rx_ring_map);
1121188808Sgonzo		sc->arge_rdata.arge_rx_ring = NULL;
1122188808Sgonzo		sc->arge_cdata.arge_rx_ring_map = NULL;
1123188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag);
1124188808Sgonzo		sc->arge_cdata.arge_rx_ring_tag = NULL;
1125188808Sgonzo	}
1126188808Sgonzo	/* Tx buffers. */
1127188808Sgonzo	if (sc->arge_cdata.arge_tx_tag) {
1128188808Sgonzo		for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1129188808Sgonzo			txd = &sc->arge_cdata.arge_txdesc[i];
1130188808Sgonzo			if (txd->tx_dmamap) {
1131188808Sgonzo				bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag,
1132188808Sgonzo				    txd->tx_dmamap);
1133188808Sgonzo				txd->tx_dmamap = NULL;
1134188808Sgonzo			}
1135188808Sgonzo		}
1136188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag);
1137188808Sgonzo		sc->arge_cdata.arge_tx_tag = NULL;
1138188808Sgonzo	}
1139188808Sgonzo	/* Rx buffers. */
1140188808Sgonzo	if (sc->arge_cdata.arge_rx_tag) {
1141188808Sgonzo		for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1142188808Sgonzo			rxd = &sc->arge_cdata.arge_rxdesc[i];
1143188808Sgonzo			if (rxd->rx_dmamap) {
1144188808Sgonzo				bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1145188808Sgonzo				    rxd->rx_dmamap);
1146188808Sgonzo				rxd->rx_dmamap = NULL;
1147188808Sgonzo			}
1148188808Sgonzo		}
1149188808Sgonzo		if (sc->arge_cdata.arge_rx_sparemap) {
1150188808Sgonzo			bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1151188808Sgonzo			    sc->arge_cdata.arge_rx_sparemap);
1152188808Sgonzo			sc->arge_cdata.arge_rx_sparemap = 0;
1153188808Sgonzo		}
1154188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag);
1155188808Sgonzo		sc->arge_cdata.arge_rx_tag = NULL;
1156188808Sgonzo	}
1157188808Sgonzo
1158188808Sgonzo	if (sc->arge_cdata.arge_parent_tag) {
1159188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag);
1160188808Sgonzo		sc->arge_cdata.arge_parent_tag = NULL;
1161188808Sgonzo	}
1162188808Sgonzo}
1163188808Sgonzo
1164188808Sgonzo/*
1165188808Sgonzo * Initialize the transmit descriptors.
1166188808Sgonzo */
1167188808Sgonzostatic int
1168188808Sgonzoarge_tx_ring_init(struct arge_softc *sc)
1169188808Sgonzo{
1170188808Sgonzo	struct arge_ring_data	*rd;
1171188808Sgonzo	struct arge_txdesc	*txd;
1172188808Sgonzo	bus_addr_t		addr;
1173188808Sgonzo	int			i;
1174188808Sgonzo
1175188808Sgonzo	sc->arge_cdata.arge_tx_prod = 0;
1176188808Sgonzo	sc->arge_cdata.arge_tx_cons = 0;
1177188808Sgonzo	sc->arge_cdata.arge_tx_cnt = 0;
1178188808Sgonzo	sc->arge_cdata.arge_tx_pkts = 0;
1179188808Sgonzo
1180188808Sgonzo	rd = &sc->arge_rdata;
1181188808Sgonzo	bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring));
1182188808Sgonzo	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1183188808Sgonzo		if (i == ARGE_TX_RING_COUNT - 1)
1184188808Sgonzo			addr = ARGE_TX_RING_ADDR(sc, 0);
1185188808Sgonzo		else
1186188808Sgonzo			addr = ARGE_TX_RING_ADDR(sc, i + 1);
1187188808Sgonzo		rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
1188188808Sgonzo		rd->arge_tx_ring[i].next_desc = addr;
1189188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[i];
1190188808Sgonzo		txd->tx_m = NULL;
1191188808Sgonzo	}
1192188808Sgonzo
1193188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1194188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
1195188808Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1196188808Sgonzo
1197188808Sgonzo	return (0);
1198188808Sgonzo}
1199188808Sgonzo
1200188808Sgonzo/*
1201188808Sgonzo * Initialize the RX descriptors and allocate mbufs for them. Note that
1202188808Sgonzo * we arrange the descriptors in a closed ring, so that the last descriptor
1203188808Sgonzo * points back to the first.
1204188808Sgonzo */
1205188808Sgonzostatic int
1206188808Sgonzoarge_rx_ring_init(struct arge_softc *sc)
1207188808Sgonzo{
1208188808Sgonzo	struct arge_ring_data	*rd;
1209188808Sgonzo	struct arge_rxdesc	*rxd;
1210188808Sgonzo	bus_addr_t		addr;
1211188808Sgonzo	int			i;
1212188808Sgonzo
1213188808Sgonzo	sc->arge_cdata.arge_rx_cons = 0;
1214188808Sgonzo
1215188808Sgonzo	rd = &sc->arge_rdata;
1216188808Sgonzo	bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring));
1217188808Sgonzo	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1218188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[i];
1219188808Sgonzo		rxd->rx_m = NULL;
1220188808Sgonzo		rxd->desc = &rd->arge_rx_ring[i];
1221188808Sgonzo		if (i == ARGE_RX_RING_COUNT - 1)
1222188808Sgonzo			addr = ARGE_RX_RING_ADDR(sc, 0);
1223188808Sgonzo		else
1224188808Sgonzo			addr = ARGE_RX_RING_ADDR(sc, i + 1);
1225188808Sgonzo		rd->arge_rx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
1226188808Sgonzo		rd->arge_rx_ring[i].next_desc = addr;
1227188808Sgonzo		if (arge_newbuf(sc, i) != 0)
1228188808Sgonzo			return (ENOBUFS);
1229188808Sgonzo	}
1230188808Sgonzo
1231188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1232188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map,
1233188808Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1234188808Sgonzo
1235188808Sgonzo	return (0);
1236188808Sgonzo}
1237188808Sgonzo
1238188808Sgonzo/*
1239188808Sgonzo * Initialize an RX descriptor and attach an MBUF cluster.
1240188808Sgonzo */
1241188808Sgonzostatic int
1242188808Sgonzoarge_newbuf(struct arge_softc *sc, int idx)
1243188808Sgonzo{
1244188808Sgonzo	struct arge_desc		*desc;
1245188808Sgonzo	struct arge_rxdesc	*rxd;
1246188808Sgonzo	struct mbuf		*m;
1247188808Sgonzo	bus_dma_segment_t	segs[1];
1248188808Sgonzo	bus_dmamap_t		map;
1249188808Sgonzo	int			nsegs;
1250188808Sgonzo
1251188808Sgonzo	m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1252188808Sgonzo	if (m == NULL)
1253188808Sgonzo		return (ENOBUFS);
1254188808Sgonzo	m->m_len = m->m_pkthdr.len = MCLBYTES;
1255188808Sgonzo	m_adj(m, sizeof(uint64_t));
1256188808Sgonzo
1257188808Sgonzo	if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag,
1258188808Sgonzo	    sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) {
1259188808Sgonzo		m_freem(m);
1260188808Sgonzo		return (ENOBUFS);
1261188808Sgonzo	}
1262188808Sgonzo	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1263188808Sgonzo
1264188808Sgonzo	rxd = &sc->arge_cdata.arge_rxdesc[idx];
1265188808Sgonzo	if (rxd->rx_m != NULL) {
1266188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1267188808Sgonzo		    BUS_DMASYNC_POSTREAD);
1268188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap);
1269188808Sgonzo	}
1270188808Sgonzo	map = rxd->rx_dmamap;
1271188808Sgonzo	rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap;
1272188808Sgonzo	sc->arge_cdata.arge_rx_sparemap = map;
1273188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1274188808Sgonzo	    BUS_DMASYNC_PREREAD);
1275188808Sgonzo	rxd->rx_m = m;
1276188808Sgonzo	desc = rxd->desc;
1277188808Sgonzo	desc->packet_addr = segs[0].ds_addr;
1278188808Sgonzo	desc->packet_ctrl =  (desc->packet_ctrl & ~ARGE_DESC_SIZE_MASK)
1279188808Sgonzo	    | ARGE_DMASIZE(segs[0].ds_len);
1280188808Sgonzo
1281188808Sgonzo	return (0);
1282188808Sgonzo}
1283188808Sgonzo
1284188808Sgonzostatic __inline void
1285188808Sgonzoarge_fixup_rx(struct mbuf *m)
1286188808Sgonzo{
1287188808Sgonzo        int		i;
1288188808Sgonzo        uint16_t	*src, *dst;
1289188808Sgonzo
1290188808Sgonzo	src = mtod(m, uint16_t *);
1291188808Sgonzo	dst = src - 1;
1292188808Sgonzo
1293188808Sgonzo	for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
1294188808Sgonzo		*dst++ = *src++;
1295188808Sgonzo
1296188808Sgonzo	m->m_data -= ETHER_ALIGN;
1297188808Sgonzo}
1298188808Sgonzo
1299188808Sgonzo
1300188808Sgonzostatic void
1301188808Sgonzoarge_tx_locked(struct arge_softc *sc)
1302188808Sgonzo{
1303188808Sgonzo	struct arge_txdesc	*txd;
1304188808Sgonzo	struct arge_desc	*cur_tx;
1305188808Sgonzo	struct ifnet		*ifp;
1306188808Sgonzo	uint32_t		ctrl;
1307188808Sgonzo	int			cons, prod;
1308188808Sgonzo
1309188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1310188808Sgonzo
1311188808Sgonzo	cons = sc->arge_cdata.arge_tx_cons;
1312188808Sgonzo	prod = sc->arge_cdata.arge_tx_prod;
1313188808Sgonzo	if (cons == prod)
1314188808Sgonzo		return;
1315188808Sgonzo
1316188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1317188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
1318188808Sgonzo	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1319188808Sgonzo
1320188808Sgonzo	ifp = sc->arge_ifp;
1321188808Sgonzo	/*
1322188808Sgonzo	 * Go through our tx list and free mbufs for those
1323188808Sgonzo	 * frames that have been transmitted.
1324188808Sgonzo	 */
1325188808Sgonzo	for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) {
1326188808Sgonzo		cur_tx = &sc->arge_rdata.arge_tx_ring[cons];
1327188808Sgonzo		ctrl = cur_tx->packet_ctrl;
1328188808Sgonzo		/* Check if descriptor has "finished" flag */
1329188808Sgonzo		if ((ctrl & ARGE_DESC_EMPTY) == 0)
1330188808Sgonzo			break;
1331188808Sgonzo
1332188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
1333188808Sgonzo
1334188808Sgonzo		sc->arge_cdata.arge_tx_cnt--;
1335188808Sgonzo		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1336188808Sgonzo
1337188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[cons];
1338188808Sgonzo
1339188808Sgonzo		cur_tx->packet_ctrl = ARGE_DESC_EMPTY;
1340188808Sgonzo
1341188808Sgonzo		ifp->if_opackets++;
1342188808Sgonzo
1343188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
1344188808Sgonzo		    BUS_DMASYNC_POSTWRITE);
1345188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
1346188808Sgonzo
1347188808Sgonzo		/* Free only if it's first descriptor in list */
1348188808Sgonzo		if (txd->tx_m)
1349188808Sgonzo			m_freem(txd->tx_m);
1350188808Sgonzo		txd->tx_m = NULL;
1351188808Sgonzo
1352188808Sgonzo		/* reset descriptor */
1353188808Sgonzo		cur_tx->packet_ctrl = ARGE_DESC_EMPTY;
1354188808Sgonzo		cur_tx->packet_addr = 0;
1355188808Sgonzo	}
1356188808Sgonzo
1357188808Sgonzo	sc->arge_cdata.arge_tx_cons = cons;
1358188808Sgonzo
1359188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1360188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE);
1361188808Sgonzo}
1362188808Sgonzo
1363188808Sgonzo
1364188808Sgonzostatic void
1365188808Sgonzoarge_rx_locked(struct arge_softc *sc)
1366188808Sgonzo{
1367188808Sgonzo	struct arge_rxdesc	*rxd;
1368188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
1369188808Sgonzo	int			cons, prog, packet_len;
1370188808Sgonzo	struct arge_desc	*cur_rx;
1371188808Sgonzo	struct mbuf		*m;
1372188808Sgonzo
1373188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1374188808Sgonzo
1375188808Sgonzo	cons = sc->arge_cdata.arge_rx_cons;
1376188808Sgonzo
1377188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1378188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map,
1379188808Sgonzo	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1380188808Sgonzo
1381188808Sgonzo	for (prog = 0; prog < ARGE_RX_RING_COUNT;
1382188808Sgonzo	    ARGE_INC(cons, ARGE_RX_RING_COUNT)) {
1383188808Sgonzo		cur_rx = &sc->arge_rdata.arge_rx_ring[cons];
1384188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[cons];
1385188808Sgonzo		m = rxd->rx_m;
1386188808Sgonzo
1387188808Sgonzo		if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0)
1388188808Sgonzo		       break;
1389188808Sgonzo
1390188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
1391188808Sgonzo
1392188808Sgonzo		prog++;
1393188808Sgonzo
1394188808Sgonzo		packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl);
1395188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1396188808Sgonzo		    BUS_DMASYNC_PREREAD);
1397188808Sgonzo		m = rxd->rx_m;
1398188808Sgonzo
1399188808Sgonzo		arge_fixup_rx(m);
1400188808Sgonzo		m->m_pkthdr.rcvif = ifp;
1401188808Sgonzo		/* Skip 4 bytes of CRC */
1402188808Sgonzo		m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN;
1403188808Sgonzo		ifp->if_ipackets++;
1404188808Sgonzo
1405188808Sgonzo		ARGE_UNLOCK(sc);
1406188808Sgonzo		(*ifp->if_input)(ifp, m);
1407188808Sgonzo		ARGE_LOCK(sc);
1408188808Sgonzo
1409188808Sgonzo		/* Reinit descriptor */
1410188808Sgonzo		cur_rx->packet_ctrl = ARGE_DESC_EMPTY;
1411188808Sgonzo		cur_rx->packet_addr = 0;
1412188808Sgonzo		if (arge_newbuf(sc, cons) != 0) {
1413188808Sgonzo			device_printf(sc->arge_dev,
1414188808Sgonzo			    "Failed to allocate buffer\n");
1415188808Sgonzo			break;
1416188808Sgonzo		}
1417188808Sgonzo
1418188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1419188808Sgonzo		    sc->arge_cdata.arge_rx_ring_map,
1420188808Sgonzo		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1421188808Sgonzo
1422188808Sgonzo	}
1423188808Sgonzo
1424188808Sgonzo	if (prog > 0) {
1425188808Sgonzo		sc->arge_cdata.arge_rx_cons = cons;
1426188808Sgonzo
1427188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1428188808Sgonzo		    sc->arge_cdata.arge_rx_ring_map,
1429188808Sgonzo		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1430188808Sgonzo	}
1431188808Sgonzo}
1432188808Sgonzo
1433188808Sgonzostatic void
1434188808Sgonzoarge_rx_intr(struct arge_softc *sc, uint32_t status)
1435188808Sgonzo{
1436188808Sgonzo
1437188808Sgonzo	ARGE_LOCK(sc);
1438188808Sgonzo	/* interrupts are masked by filter */
1439188808Sgonzo	arge_rx_locked(sc);
1440188808Sgonzo
1441188808Sgonzo	/* unmask interrupts */
1442188808Sgonzo	ARGE_SET_BITS(sc,
1443188808Sgonzo	    AR71XX_DMA_INTR, DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD);
1444188808Sgonzo	ARGE_UNLOCK(sc);
1445188808Sgonzo}
1446188808Sgonzo
1447188808Sgonzostatic int
1448188808Sgonzoarge_intr_filter(void *arg)
1449188808Sgonzo{
1450188808Sgonzo	struct arge_softc	*sc = arg;
1451188808Sgonzo	uint32_t		status, ints;
1452188808Sgonzo
1453188808Sgonzo	status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
1454188808Sgonzo	ints = ARGE_READ(sc, AR71XX_DMA_INTR);
1455188808Sgonzo
1456188808Sgonzo#if 0
1457188808Sgonzo	dprintf("int mask(filter) = %b\n", ints,
1458188808Sgonzo	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
1459188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1460188808Sgonzo	dprintf("status(filter) = %b\n", status,
1461188808Sgonzo	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
1462188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1463188808Sgonzo#endif
1464188808Sgonzo
1465188808Sgonzo	if (status & DMA_INTR_ALL) {
1466188808Sgonzo		if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW))
1467188808Sgonzo			ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR,
1468188808Sgonzo			    DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD);
1469188808Sgonzo
1470188808Sgonzo		if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN))
1471188808Sgonzo			ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR,
1472188808Sgonzo			    DMA_INTR_TX_UNDERRUN | DMA_INTR_TX_PKT_SENT);
1473188808Sgonzo
1474188808Sgonzo		sc->arge_intr_status = status;
1475188808Sgonzo		return (FILTER_SCHEDULE_THREAD);
1476188808Sgonzo	}
1477188808Sgonzo
1478188808Sgonzo	sc->arge_intr_status = 0;
1479188808Sgonzo	return (FILTER_STRAY);
1480188808Sgonzo}
1481188808Sgonzo
1482188808Sgonzostatic void
1483188808Sgonzoarge_intr(void *arg)
1484188808Sgonzo{
1485188808Sgonzo	struct arge_softc	*sc = arg;
1486188808Sgonzo	uint32_t		status;
1487188808Sgonzo
1488188808Sgonzo	status = sc->arge_intr_status;
1489188808Sgonzo
1490188808Sgonzo#if 0
1491188808Sgonzo	dprintf("int status(intr) = %b\n", status,
1492188808Sgonzo	    "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD"
1493188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
1494188808Sgonzo#endif
1495188808Sgonzo
1496188808Sgonzo	/*
1497188808Sgonzo	 * Is it our interrupt at all?
1498188808Sgonzo	 */
1499188808Sgonzo	if (status == 0)
1500188808Sgonzo		return;
1501188808Sgonzo
1502188808Sgonzo	if (status & DMA_INTR_RX_BUS_ERROR) {
1503188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR);
1504188808Sgonzo		device_printf(sc->arge_dev, "RX bus error");
1505188808Sgonzo		return;
1506188808Sgonzo	}
1507188808Sgonzo
1508188808Sgonzo	if (status & DMA_INTR_TX_BUS_ERROR) {
1509188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR);
1510188808Sgonzo		device_printf(sc->arge_dev, "TX bus error");
1511188808Sgonzo		return;
1512188808Sgonzo	}
1513188808Sgonzo
1514188808Sgonzo	if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW))
1515188808Sgonzo		arge_rx_intr(sc, status);
1516188808Sgonzo
1517188808Sgonzo	if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN))
1518188808Sgonzo		arge_tx_intr(sc, status);
1519188808Sgonzo}
1520188808Sgonzo
1521188808Sgonzostatic void
1522188808Sgonzoarge_tx_intr(struct arge_softc *sc, uint32_t status)
1523188808Sgonzo{
1524188808Sgonzo	ARGE_LOCK(sc);
1525188808Sgonzo
1526188808Sgonzo	/* Interrupts are masked by filter */
1527188808Sgonzo	arge_tx_locked(sc);
1528188808Sgonzo
1529188808Sgonzo	ARGE_UNLOCK(sc);
1530188808Sgonzo}
1531188808Sgonzo
1532188808Sgonzostatic void
1533188808Sgonzoarge_tick(void *xsc)
1534188808Sgonzo{
1535188808Sgonzo	struct arge_softc	*sc = xsc;
1536188808Sgonzo	struct mii_data		*mii;
1537188808Sgonzo
1538188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1539188808Sgonzo
1540188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
1541188808Sgonzo	mii_tick(mii);
1542188808Sgonzo	callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
1543188808Sgonzo}
1544188808Sgonzo
1545188808Sgonzo/*
1546188808Sgonzo * Create a copy of a single mbuf. It can have either internal or
1547188808Sgonzo * external data, it may have a packet header. External data is really
1548188808Sgonzo * copied, so the new buffer is writeable.
1549188808Sgonzo */
1550188808Sgonzostatic struct mbuf *
1551188808Sgonzocopy_mbuf(struct mbuf *m)
1552188808Sgonzo{
1553188808Sgonzo	struct mbuf *new;
1554188808Sgonzo
1555188808Sgonzo	MGET(new, M_DONTWAIT, MT_DATA);
1556188808Sgonzo	if (new == NULL)
1557188808Sgonzo		return (NULL);
1558188808Sgonzo
1559188808Sgonzo	if (m->m_flags & M_PKTHDR) {
1560188808Sgonzo		M_MOVE_PKTHDR(new, m);
1561188808Sgonzo		if (m->m_len > MHLEN)
1562188808Sgonzo			MCLGET(new, M_WAIT);
1563188808Sgonzo	} else {
1564188808Sgonzo		if (m->m_len > MLEN)
1565188808Sgonzo			MCLGET(new, M_WAIT);
1566188808Sgonzo	}
1567188808Sgonzo
1568188808Sgonzo	bcopy(m->m_data, new->m_data, m->m_len);
1569188808Sgonzo	new->m_len = m->m_len;
1570188808Sgonzo	new->m_flags &= ~M_RDONLY;
1571188808Sgonzo
1572188808Sgonzo	return (new);
1573188808Sgonzo}
1574188808Sgonzo
1575188808Sgonzo
1576188808Sgonzo
1577188808Sgonzostatic int
1578188808Sgonzoarge_fix_chain(struct mbuf **mp)
1579188808Sgonzo{
1580188808Sgonzo	struct mbuf *m = *mp, *prev = NULL, *next, *new;
1581188808Sgonzo	u_int mlen = 0, fill = 0;
1582188808Sgonzo	int first, off;
1583188808Sgonzo	u_char *d, *cp;
1584188808Sgonzo
1585188808Sgonzo	do {
1586188808Sgonzo		next = m->m_next;
1587188808Sgonzo
1588188808Sgonzo		if ((uintptr_t)mtod(m, void *) % 4 != 0 ||
1589188808Sgonzo		   (m->m_len % 4 != 0 && next)) {
1590188808Sgonzo			/*
1591188808Sgonzo			 * Needs fixing
1592188808Sgonzo			 */
1593188808Sgonzo			first = (m == *mp);
1594188808Sgonzo
1595188808Sgonzo			d = mtod(m, u_char *);
1596188808Sgonzo			if ((off = (uintptr_t)(void *)d % 4) != 0) {
1597188808Sgonzo				if (M_WRITABLE(m)) {
1598188808Sgonzo					bcopy(d, d - off, m->m_len);
1599188808Sgonzo					m->m_data = (caddr_t)(d - off);
1600188808Sgonzo				} else {
1601188808Sgonzo					if ((new = copy_mbuf(m)) == NULL) {
1602188808Sgonzo						goto fail;
1603188808Sgonzo					}
1604188808Sgonzo					if (prev)
1605188808Sgonzo						prev->m_next = new;
1606188808Sgonzo					new->m_next = next;
1607188808Sgonzo					m_free(m);
1608188808Sgonzo					m = new;
1609188808Sgonzo				}
1610188808Sgonzo			}
1611188808Sgonzo
1612188808Sgonzo			if ((off = m->m_len % 4) != 0) {
1613188808Sgonzo				if (!M_WRITABLE(m)) {
1614188808Sgonzo					if ((new = copy_mbuf(m)) == NULL) {
1615188808Sgonzo						goto fail;
1616188808Sgonzo					}
1617188808Sgonzo					if (prev)
1618188808Sgonzo						prev->m_next = new;
1619188808Sgonzo					new->m_next = next;
1620188808Sgonzo					m_free(m);
1621188808Sgonzo					m = new;
1622188808Sgonzo				}
1623188808Sgonzo				d = mtod(m, u_char *) + m->m_len;
1624188808Sgonzo				off = 4 - off;
1625188808Sgonzo				while (off) {
1626188808Sgonzo					if (next == NULL) {
1627188808Sgonzo						*d++ = 0;
1628188808Sgonzo						fill++;
1629188808Sgonzo					} else if (next->m_len == 0) {
1630188808Sgonzo						next = m_free(next);
1631188808Sgonzo						continue;
1632188808Sgonzo					} else {
1633188808Sgonzo						cp = mtod(next, u_char *);
1634188808Sgonzo						*d++ = *cp++;
1635188808Sgonzo						next->m_len--;
1636188808Sgonzo						next->m_data = (caddr_t)cp;
1637188808Sgonzo					}
1638188808Sgonzo					off--;
1639188808Sgonzo					m->m_len++;
1640188808Sgonzo				}
1641188808Sgonzo			}
1642188808Sgonzo
1643188808Sgonzo			if (first)
1644188808Sgonzo				*mp = m;
1645188808Sgonzo		}
1646188808Sgonzo
1647188808Sgonzo		mlen += m->m_len;
1648188808Sgonzo		prev = m;
1649188808Sgonzo	} while ((m = next) != NULL);
1650188808Sgonzo
1651188808Sgonzo	return (mlen - fill);
1652188808Sgonzo
1653188808Sgonzo  fail:
1654188808Sgonzo	m_freem(*mp);
1655188808Sgonzo	*mp = NULL;
1656188808Sgonzo	return (0);
1657188808Sgonzo}
1658