1210040Scognet/*-
2210040Scognet * Copyright (c) 2010 Yohanes Nugroho <yohanes@gmail.com>
3210040Scognet * All rights reserved.
4210040Scognet *
5210040Scognet * Redistribution and use in source and binary forms, with or without
6210040Scognet * modification, are permitted provided that the following conditions
7210040Scognet * are met:
8210040Scognet * 1. Redistributions of source code must retain the above copyright
9210040Scognet *    notice, this list of conditions and the following disclaimer.
10210040Scognet * 2. Redistributions in binary form must reproduce the above copyright
11210040Scognet *    notice, this list of conditions and the following disclaimer in the
12210040Scognet *    documentation and/or other materials provided with the distribution.
13210040Scognet *
14210040Scognet * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15210040Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16210040Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17210040Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18210040Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19210040Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20210040Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21210040Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22210040Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23210040Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24210040Scognet * SUCH DAMAGE.
25210040Scognet */
26210040Scognet
27210040Scognet#include <sys/cdefs.h>
28210040Scognet__FBSDID("$FreeBSD$");
29210040Scognet
30210040Scognet#include <sys/param.h>
31210040Scognet#include <sys/systm.h>
32210040Scognet#include <sys/bus.h>
33210040Scognet#include <sys/kernel.h>
34210040Scognet#include <sys/lock.h>
35210040Scognet#include <sys/mbuf.h>
36210040Scognet#include <sys/malloc.h>
37210040Scognet#include <sys/module.h>
38210040Scognet#include <sys/rman.h>
39210040Scognet#include <sys/socket.h>
40210040Scognet#include <sys/sockio.h>
41210040Scognet#include <sys/sysctl.h>
42210040Scognet#include <sys/taskqueue.h>
43210040Scognet
44210040Scognet#include <net/ethernet.h>
45210040Scognet#include <net/if.h>
46210040Scognet#include <net/if_arp.h>
47210040Scognet#include <net/if_dl.h>
48210040Scognet#include <net/if_media.h>
49210040Scognet#include <net/if_types.h>
50210040Scognet#include <net/if_vlan_var.h>
51210040Scognet
52210040Scognet#ifdef INET
53210040Scognet#include <netinet/in.h>
54210040Scognet#include <netinet/in_systm.h>
55210040Scognet#include <netinet/in_var.h>
56210040Scognet#include <netinet/ip.h>
57210040Scognet#endif
58210040Scognet
59210040Scognet#include <net/bpf.h>
60210040Scognet#include <net/bpfdesc.h>
61210040Scognet
62210040Scognet#include <dev/mii/mii.h>
63210040Scognet#include <dev/mii/miivar.h>
64210040Scognet
65210040Scognet#include <arm/at91/at91_pmcvar.h>
66210040Scognet#include <arm/at91/if_macbreg.h>
67210040Scognet#include <arm/at91/if_macbvar.h>
68210040Scognet#include <arm/at91/at91_piovar.h>
69210040Scognet
70210040Scognet#include <arm/at91/at91sam9g20reg.h>
71210040Scognet
72210040Scognet#include <machine/bus.h>
73210040Scognet#include <machine/intr.h>
74210040Scognet
75210040Scognet/* "device miibus" required.  See GENERIC if you get errors here. */
76210040Scognet#include "miibus_if.h"
77210040Scognet
78210040Scognet
79210040Scognet#define	MACB_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
80210040Scognet#define	MACB_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
81210040Scognet#define	MACB_LOCK_INIT(_sc)					\
82210040Scognet	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev),	\
83210040Scognet	    MTX_NETWORK_LOCK, MTX_DEF)
84210040Scognet#define	MACB_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
85210040Scognet#define	MACB_LOCK_ASSERT(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
86210040Scognet#define	MACB_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
87210040Scognet
88210040Scognet
89210040Scognetstatic inline uint32_t
90210040Scognetread_4(struct macb_softc *sc, bus_size_t off)
91210040Scognet{
92210040Scognet
93210040Scognet	return (bus_read_4(sc->mem_res, off));
94210040Scognet}
95210040Scognet
96210040Scognetstatic inline void
97210040Scognetwrite_4(struct macb_softc *sc, bus_size_t off, uint32_t val)
98210040Scognet{
99210040Scognet
100210040Scognet	bus_write_4(sc->mem_res, off, val);
101210040Scognet}
102210040Scognet
103210040Scognet
104210040Scognetstatic devclass_t macb_devclass;
105210040Scognet
106210040Scognet/* ifnet entry points */
107210040Scognet
108210040Scognetstatic void	macbinit_locked(void *);
109210040Scognetstatic void	macbstart_locked(struct ifnet *);
110210040Scognet
111210040Scognetstatic void	macbinit(void *);
112210040Scognetstatic void	macbstart(struct ifnet *);
113210040Scognetstatic void	macbstop(struct macb_softc *);
114210040Scognetstatic int	macbioctl(struct ifnet * ifp, u_long, caddr_t);
115210040Scognet
116210040Scognet/* bus entry points */
117210040Scognet
118210040Scognetstatic int	macb_probe(device_t dev);
119210040Scognetstatic int	macb_attach(device_t dev);
120210040Scognetstatic int	macb_detach(device_t dev);
121210040Scognet
122210040Scognet/* helper functions */
123210040Scognetstatic int
124210040Scognetmacb_new_rxbuf(struct macb_softc *sc, int index);
125210040Scognet
126210040Scognetstatic void
127210040Scognetmacb_free_desc_dma_tx(struct macb_softc *sc);
128210040Scognet
129210040Scognetstatic void
130210040Scognetmacb_free_desc_dma_rx(struct macb_softc *sc);
131210040Scognet
132210040Scognetstatic void
133210040Scognetmacb_init_desc_dma_tx(struct macb_softc *sc);
134210040Scognet
135210040Scognetstatic void
136210040Scognetmacb_watchdog(struct macb_softc *sc);
137210040Scognet
138210040Scognetstatic int macb_intr_rx_locked(struct macb_softc *sc, int count);
139210040Scognetstatic void macb_intr_task(void *arg, int pending __unused);
140210040Scognetstatic void macb_intr(void *xsc);
141210040Scognet
142210040Scognetstatic void
143210040Scognetmacb_tx_cleanup(struct macb_softc *sc);
144210040Scognet
145210040Scognetstatic inline int
146210040Scognetphy_write(struct macb_softc *sc, int phy, int reg, int data);
147210040Scognet
148210040Scognetstatic void	macb_reset(struct macb_softc *sc);
149210040Scognet
150210040Scognetstatic void
151210040Scognetmacb_deactivate(device_t dev)
152210040Scognet{
153210040Scognet	struct macb_softc *sc;
154210040Scognet
155210040Scognet	sc = device_get_softc(dev);
156210040Scognet
157210040Scognet	macb_free_desc_dma_tx(sc);
158210040Scognet	macb_free_desc_dma_rx(sc);
159210040Scognet
160210040Scognet}
161210040Scognet
162210040Scognetstatic void
163210040Scognetmacb_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
164210040Scognet{
165210040Scognet	bus_addr_t *paddr;
166210040Scognet
167210040Scognet	KASSERT(nsegs == 1, ("wrong number of segments, should be 1"));
168210040Scognet	paddr = arg;
169210040Scognet	*paddr = segs->ds_addr;
170210040Scognet}
171210040Scognet
172210040Scognetstatic int
173210040Scognetmacb_alloc_desc_dma_tx(struct macb_softc *sc)
174210040Scognet{
175210040Scognet	int error, i;
176210040Scognet
177210040Scognet	/* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */
178210040Scognet	error = bus_dma_tag_create(sc->sc_parent_tag,	/* parent */
179210040Scognet	    16, 0,			/* alignment, boundary */
180210040Scognet	    BUS_SPACE_MAXADDR,		/* lowaddr */
181210040Scognet	    BUS_SPACE_MAXADDR,		/* highaddr */
182210040Scognet	    NULL, NULL,			/* filtfunc, filtfuncarg */
183210040Scognet	    sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS, /* max size */
184210040Scognet	    1,				/* nsegments */
185210040Scognet	    sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS,
186210040Scognet	    0,				/* flags */
187210040Scognet	    NULL, NULL,			/* lockfunc, lockfuncarg */
188210040Scognet	    &sc->dmatag_data_tx);	/* dmat */
189210040Scognet	if (error != 0) {
190210040Scognet		device_printf(sc->dev,
191210040Scognet		    "Couldn't create TX descriptor dma tag\n");
192210040Scognet		return (error);
193210040Scognet	}
194210040Scognet	/* Allocate memory for TX ring. */
195210040Scognet	error = bus_dmamem_alloc(sc->dmatag_data_tx,
196210040Scognet	    (void**)&(sc->desc_tx), BUS_DMA_NOWAIT | BUS_DMA_ZERO |
197210040Scognet	    BUS_DMA_COHERENT, &sc->dmamap_ring_tx);
198210040Scognet	if (error != 0) {
199210040Scognet		device_printf(sc->dev, "failed to allocate TX dma memory\n");
200210040Scognet		return (error);
201210040Scognet	}
202210040Scognet	/* Load Ring DMA. */
203210040Scognet	error = bus_dmamap_load(sc->dmatag_data_tx, sc->dmamap_ring_tx,
204210040Scognet	    sc->desc_tx, sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS,
205210040Scognet	    macb_getaddr, &sc->ring_paddr_tx, BUS_DMA_NOWAIT);
206210040Scognet	if (error != 0) {
207210040Scognet		device_printf(sc->dev, "can't load TX descriptor dma map\n");
208210040Scognet		return (error);
209210040Scognet	}
210210040Scognet	/* Allocate a busdma tag for mbufs. No alignment restriction applys. */
211210040Scognet	error = bus_dma_tag_create(sc->sc_parent_tag,	/* parent */
212210040Scognet	    1, 0,			/* alignment, boundary */
213210040Scognet	    BUS_SPACE_MAXADDR,		/* lowaddr */
214210040Scognet	    BUS_SPACE_MAXADDR,		/* highaddr */
215210040Scognet	    NULL, NULL,			/* filtfunc, filtfuncarg */
216210040Scognet	    MCLBYTES * MAX_FRAGMENT,	/* maxsize */
217210040Scognet	    MAX_FRAGMENT,		/* nsegments */
218210040Scognet	    MCLBYTES, 0,		/* maxsegsz, flags */
219210040Scognet	    NULL, NULL,			/* lockfunc, lockfuncarg */
220210040Scognet	    &sc->dmatag_ring_tx);	/* dmat */
221210040Scognet	if (error != 0) {
222210040Scognet		device_printf(sc->dev, "failed to create TX mbuf dma tag\n");
223210040Scognet		return (error);
224210040Scognet	}
225210040Scognet
226210040Scognet	for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
227210040Scognet		/* Create dma map for each descriptor. */
228210040Scognet		error = bus_dmamap_create(sc->dmatag_ring_tx, 0,
229210040Scognet		    &sc->tx_desc[i].dmamap);
230210040Scognet		if (error != 0) {
231210040Scognet			device_printf(sc->dev,
232210040Scognet			    "failed to create TX mbuf dma map\n");
233210040Scognet			return (error);
234210040Scognet		}
235210040Scognet	}
236210040Scognet	return (0);
237210040Scognet}
238210040Scognet
239210040Scognetstatic void
240210040Scognetmacb_free_desc_dma_tx(struct macb_softc *sc)
241210040Scognet{
242210040Scognet	struct tx_desc_info *td;
243210040Scognet	int i;
244210040Scognet
245210040Scognet	/* TX buffers. */
246210040Scognet	if (sc->dmatag_ring_tx != NULL) {
247210040Scognet		for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
248210040Scognet			td = &sc->tx_desc[i];
249210040Scognet			if (td->dmamap != NULL) {
250210040Scognet				bus_dmamap_destroy(sc->dmatag_ring_tx,
251210040Scognet				    td->dmamap);
252210040Scognet				td->dmamap = NULL;
253210040Scognet			}
254210040Scognet		}
255210040Scognet		bus_dma_tag_destroy(sc->dmatag_ring_tx);
256210040Scognet		sc->dmatag_ring_tx = NULL;
257210040Scognet	}
258210040Scognet
259210040Scognet	/* TX descriptor ring. */
260210040Scognet	if (sc->dmatag_data_tx != NULL) {
261210040Scognet		if (sc->dmamap_ring_tx != NULL)
262210040Scognet			bus_dmamap_unload(sc->dmatag_data_tx,
263210040Scognet			    sc->dmamap_ring_tx);
264210040Scognet		if (sc->dmamap_ring_tx != NULL && sc->desc_tx != NULL)
265210040Scognet			bus_dmamem_free(sc->dmatag_data_tx, sc->desc_tx,
266210040Scognet			    sc->dmamap_ring_tx);
267210040Scognet		sc->dmamap_ring_tx = NULL;
268210040Scognet		sc->dmamap_ring_tx = NULL;
269210040Scognet		bus_dma_tag_destroy(sc->dmatag_data_tx);
270210040Scognet		sc->dmatag_data_tx = NULL;
271210040Scognet	}
272210040Scognet}
273210040Scognet
274210040Scognetstatic void
275210040Scognetmacb_init_desc_dma_tx(struct macb_softc *sc)
276210040Scognet{
277210040Scognet	struct eth_tx_desc *desc;
278210040Scognet	int i;
279210040Scognet
280210040Scognet	MACB_LOCK_ASSERT(sc);
281210040Scognet
282210040Scognet	sc->tx_prod = 0;
283210040Scognet	sc->tx_cons = 0;
284210040Scognet	sc->tx_cnt = 0;
285210040Scognet
286210040Scognet	desc = &sc->desc_tx[0];
287210040Scognet	bzero(desc, sizeof(struct eth_tx_desc) * MACB_MAX_TX_BUFFERS);
288210040Scognet
289210040Scognet	for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
290210040Scognet		desc = &sc->desc_tx[i];
291210040Scognet		if (i == MACB_MAX_TX_BUFFERS - 1)
292210040Scognet			desc->flags = TD_OWN | TD_WRAP_MASK;
293210040Scognet		else
294210040Scognet			desc->flags = TD_OWN;
295210040Scognet	}
296210040Scognet
297210040Scognet	bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx,
298210040Scognet	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
299210040Scognet}
300210040Scognet
301210040Scognetstatic int
302210040Scognetmacb_alloc_desc_dma_rx(struct macb_softc *sc)
303210040Scognet{
304210040Scognet	int error, i;
305210040Scognet
306210040Scognet	/* Allocate a busdma tag and DMA safe memory for RX descriptors. */
307210040Scognet	error = bus_dma_tag_create(sc->sc_parent_tag,	/* parent */
308210040Scognet	    16, 0,			/* alignment, boundary */
309210040Scognet	    BUS_SPACE_MAXADDR,		/* lowaddr */
310210040Scognet	    BUS_SPACE_MAXADDR,		/* highaddr */
311210040Scognet	    NULL, NULL,			/* filtfunc, filtfuncarg */
312210040Scognet	    /* maxsize, nsegments */
313210040Scognet	    sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS, 1,
314210040Scognet	    /* maxsegsz, flags */
315210040Scognet	    sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS, 0,
316210040Scognet	    NULL, NULL,			/* lockfunc, lockfuncarg */
317210040Scognet	    &sc->dmatag_data_rx);	/* dmat */
318210040Scognet	if (error != 0) {
319210040Scognet		device_printf(sc->dev,
320210040Scognet		    "Couldn't create RX descriptor dma tag\n");
321210040Scognet		return (error);
322210040Scognet	}
323210040Scognet	/* Allocate RX ring. */
324210040Scognet	error = bus_dmamem_alloc(sc->dmatag_data_rx, (void**)&(sc->desc_rx),
325210040Scognet	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
326210040Scognet	    &sc->dmamap_ring_rx);
327210040Scognet	if (error != 0) {
328210040Scognet		device_printf(sc->dev,
329210040Scognet		    "failed to allocate RX descriptor dma memory\n");
330210040Scognet		return (error);
331210040Scognet	}
332210040Scognet
333210040Scognet	/* Load dmamap. */
334210040Scognet	error = bus_dmamap_load(sc->dmatag_data_rx, sc->dmamap_ring_rx,
335210040Scognet	    sc->desc_rx, sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS,
336210040Scognet	    macb_getaddr, &sc->ring_paddr_rx, BUS_DMA_NOWAIT);
337210040Scognet	if (error != 0) {
338210040Scognet		device_printf(sc->dev, "can't load RX descriptor dma map\n");
339210040Scognet		return (error);
340210040Scognet	}
341210040Scognet
342210040Scognet	/* Allocate a busdma tag for mbufs. */
343210040Scognet	error = bus_dma_tag_create(sc->sc_parent_tag,/* parent */
344210040Scognet	    16, 0,			/* alignment, boundary */
345210040Scognet	    BUS_SPACE_MAXADDR,		/* lowaddr */
346210040Scognet	    BUS_SPACE_MAXADDR,		/* highaddr */
347210040Scognet	    NULL, NULL,			/* filtfunc, filtfuncarg */
348210040Scognet	    MCLBYTES, 1,		/* maxsize, nsegments */
349210040Scognet	    MCLBYTES, 0,		/* maxsegsz, flags */
350210040Scognet	    NULL, NULL,			/* lockfunc, lockfuncarg */
351210040Scognet	    &sc->dmatag_ring_rx);	/* dmat */
352210040Scognet
353210040Scognet	if (error != 0) {
354210040Scognet		device_printf(sc->dev, "failed to create RX mbuf dma tag\n");
355210040Scognet		return (error);
356210040Scognet	}
357210040Scognet
358210040Scognet	for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) {
359210040Scognet		error = bus_dmamap_create(sc->dmatag_ring_rx, 0,
360210040Scognet		    &sc->rx_desc[i].dmamap);
361210040Scognet		if (error != 0) {
362210040Scognet			device_printf(sc->dev,
363210040Scognet			    "failed to create RX mbuf dmamap\n");
364210040Scognet			return (error);
365210040Scognet		}
366210040Scognet	}
367210040Scognet
368210040Scognet	return (0);
369210040Scognet}
370210040Scognet
371210040Scognetstatic void
372210040Scognetmacb_free_desc_dma_rx(struct macb_softc *sc)
373210040Scognet{
374210040Scognet	struct rx_desc_info *rd;
375210040Scognet	int i;
376210040Scognet
377210040Scognet	/* RX buffers. */
378210040Scognet	if (sc->dmatag_ring_rx != NULL) {
379210040Scognet		for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) {
380210040Scognet			rd = &sc->rx_desc[i];
381210040Scognet			if (rd->dmamap != NULL) {
382210040Scognet				bus_dmamap_destroy(sc->dmatag_ring_rx,
383210040Scognet				    rd->dmamap);
384210040Scognet				rd->dmamap = NULL;
385210040Scognet			}
386210040Scognet		}
387210040Scognet		bus_dma_tag_destroy(sc->dmatag_ring_rx);
388210040Scognet		sc->dmatag_ring_rx = NULL;
389210040Scognet	}
390210040Scognet	/* RX descriptor ring. */
391210040Scognet	if (sc->dmatag_data_rx != NULL) {
392210040Scognet		if (sc->dmamap_ring_rx != NULL)
393210040Scognet			bus_dmamap_unload(sc->dmatag_data_rx,
394210040Scognet			    sc->dmamap_ring_rx);
395210040Scognet		if (sc->dmamap_ring_rx != NULL &&
396210040Scognet		    sc->desc_rx != NULL)
397210040Scognet			bus_dmamem_free(sc->dmatag_data_rx, sc->desc_rx,
398210040Scognet			    sc->dmamap_ring_rx);
399210040Scognet		sc->desc_rx = NULL;
400210040Scognet		sc->dmamap_ring_rx = NULL;
401210040Scognet		bus_dma_tag_destroy(sc->dmatag_data_rx);
402210040Scognet		sc->dmatag_data_rx = NULL;
403210040Scognet	}
404210040Scognet}
405210040Scognet
406210040Scognetstatic int
407210040Scognetmacb_init_desc_dma_rx(struct macb_softc *sc)
408210040Scognet{
409210040Scognet	struct eth_rx_desc *desc;
410210040Scognet	struct rx_desc_info *rd;
411210040Scognet	int i;
412210040Scognet
413210040Scognet	MACB_LOCK_ASSERT(sc);
414210040Scognet
415210040Scognet	sc->rx_cons = 0;
416210040Scognet	desc = &sc->desc_rx[0];
417210040Scognet	bzero(desc, sizeof(struct eth_rx_desc) * MACB_MAX_RX_BUFFERS);
418210040Scognet	for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) {
419210040Scognet		rd = &sc->rx_desc[i];
420210040Scognet		rd->buff = NULL;
421210040Scognet		if (macb_new_rxbuf(sc, i) != 0)
422210040Scognet			return (ENOBUFS);
423210040Scognet	}
424210040Scognet	bus_dmamap_sync(sc->dmatag_ring_rx, sc->dmamap_ring_rx,
425210040Scognet	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
426210040Scognet	return (0);
427210040Scognet}
428210040Scognet
429210040Scognetstatic int
430210040Scognetmacb_new_rxbuf(struct macb_softc *sc, int index)
431210040Scognet{
432210040Scognet	struct rx_desc_info *rd;
433210040Scognet	struct eth_rx_desc *desc;
434210040Scognet	struct mbuf *m;
435210040Scognet	bus_dma_segment_t seg[1];
436210040Scognet	int error, nsegs;
437210040Scognet
438243882Sglebius	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
439210040Scognet	if (m == NULL)
440210040Scognet		return (ENOBUFS);
441210040Scognet	m->m_len = m->m_pkthdr.len = MCLBYTES - ETHER_ALIGN;
442210040Scognet	rd = &sc->rx_desc[index];
443210040Scognet	bus_dmamap_unload(sc->dmatag_ring_rx, rd->dmamap);
444210040Scognet	error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_rx, rd->dmamap, m,
445210040Scognet	    seg, &nsegs, 0);
446210040Scognet	KASSERT(nsegs == 1, ("Too many segments returned!"));
447210040Scognet	if (error != 0) {
448210040Scognet		m_free(m);
449210040Scognet		return (error);
450210040Scognet	}
451210040Scognet
452210040Scognet	bus_dmamap_sync(sc->dmatag_ring_rx, rd->dmamap, BUS_DMASYNC_PREREAD);
453210040Scognet	rd->buff = m;
454210040Scognet
455210040Scognet	desc = &sc->desc_rx[index];
456210040Scognet	desc->addr = seg[0].ds_addr;
457210040Scognet
458210040Scognet	desc->flags = DATA_SIZE;
459210040Scognet
460210040Scognet	if (index == MACB_MAX_RX_BUFFERS - 1)
461210040Scognet		desc->addr |= RD_WRAP_MASK;
462210040Scognet
463210040Scognet	return (0);
464210040Scognet}
465210040Scognet
466210040Scognetstatic int
467210040Scognetmacb_allocate_dma(struct macb_softc *sc)
468210040Scognet{
469210040Scognet	int error;
470210040Scognet
471210040Scognet	/* Create parent tag for tx and rx */
472210040Scognet	error = bus_dma_tag_create(
473210040Scognet	    bus_get_dma_tag(sc->dev),	/* parent */
474210040Scognet	    1, 0,			/* alignment, boundary */
475210040Scognet	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
476210040Scognet	    BUS_SPACE_MAXADDR,		/* highaddr */
477210040Scognet	    NULL, NULL,			/* filter, filterarg */
478210040Scognet	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsize, nsegments */
479210040Scognet	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
480210040Scognet	    0,				/* flags */
481210040Scognet	    NULL, NULL,		/* lockfunc, lockarg */
482210040Scognet	    &sc->sc_parent_tag);
483210040Scognet	if (error != 0) {
484210040Scognet		device_printf(sc->dev, "Couldn't create parent DMA tag\n");
485210040Scognet		return (error);
486210040Scognet	}
487210040Scognet
488210040Scognet	if ((error = macb_alloc_desc_dma_tx(sc)) != 0)
489210040Scognet		return (error);
490210040Scognet	if ((error = macb_alloc_desc_dma_rx(sc)) != 0)
491210040Scognet		return (error);
492210040Scognet	return (0);
493210040Scognet}
494210040Scognet
495210040Scognet
496210040Scognetstatic void
497210040Scognetmacb_tick(void *xsc)
498210040Scognet{
499210040Scognet	struct macb_softc *sc;
500210040Scognet	struct mii_data *mii;
501210040Scognet
502210040Scognet	sc = xsc;
503210040Scognet	mii = device_get_softc(sc->miibus);
504210040Scognet	mii_tick(mii);
505210040Scognet	macb_watchdog(sc);
506210040Scognet	/*
507210040Scognet	 * Schedule another timeout one second from now.
508210040Scognet	 */
509210040Scognet	callout_reset(&sc->tick_ch, hz, macb_tick, sc);
510210040Scognet}
511210040Scognet
512210040Scognet
513210040Scognetstatic void
514210040Scognetmacb_watchdog(struct macb_softc *sc)
515210040Scognet{
516210040Scognet	struct ifnet *ifp;
517210040Scognet
518210040Scognet	MACB_LOCK_ASSERT(sc);
519210040Scognet
520210040Scognet	if (sc->macb_watchdog_timer == 0 || --sc->macb_watchdog_timer)
521210040Scognet		return;
522210040Scognet
523210040Scognet	ifp = sc->ifp;
524210040Scognet	if ((sc->flags & MACB_FLAG_LINK) == 0) {
525210040Scognet		if_printf(ifp, "watchdog timeout (missed link)\n");
526210040Scognet		ifp->if_oerrors++;
527210040Scognet		return;
528210040Scognet	}
529210040Scognet
530210040Scognet	if_printf(ifp, "watchdog timeout\n");
531210040Scognet	ifp->if_oerrors++;
532210040Scognet	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
533210040Scognet	macbinit_locked(sc);
534210040Scognet	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
535217062Sjhb		macbstart_locked(ifp);
536210040Scognet}
537210040Scognet
538210040Scognet
539210040Scognet
540210040Scognetstatic void
541210040Scognetmacbinit_locked(void *xsc)
542210040Scognet{
543210040Scognet	struct macb_softc *sc;
544210040Scognet	struct ifnet *ifp;
545210040Scognet	int err;
546210040Scognet	uint32_t config;
547210040Scognet	struct mii_data *mii;
548210040Scognet
549210040Scognet	sc = xsc;
550210040Scognet	ifp = sc->ifp;
551210040Scognet
552210040Scognet	MACB_LOCK_ASSERT(sc);
553210040Scognet
554210040Scognet	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
555210040Scognet		return;
556210040Scognet
557210040Scognet	if ((err = macb_init_desc_dma_rx(sc)) != 0) {
558210040Scognet		device_printf(sc->dev, "no memory for RX buffers\n");
559210040Scognet		//ecestop(sc);
560210040Scognet		return;
561210040Scognet	}
562210040Scognet	macb_init_desc_dma_tx(sc);
563210040Scognet
564210040Scognet	config = read_4(sc, EMAC_NCFGR) | (sc->clock << 10); /*set clock*/
565210040Scognet	config |= CFG_PAE;		/* PAuse Enable */
566210040Scognet	config |= CFG_DRFCS;		/* Discard Rx FCS */
567210040Scognet	config |= CFG_SPD;		/* 100 mbps*/
568210040Scognet	//config |= CFG_CAF;
569210040Scognet	config |= CFG_FD;
570210040Scognet
571210040Scognet	config |= CFG_RBOF_2; /*offset +2*/
572210040Scognet
573210040Scognet	write_4(sc, EMAC_NCFGR, config);
574210040Scognet
575210040Scognet	/* Initialize TX and RX buffers */
576210040Scognet	write_4(sc, EMAC_RBQP, sc->ring_paddr_rx);
577210040Scognet	write_4(sc, EMAC_TBQP, sc->ring_paddr_tx);
578210040Scognet
579210040Scognet	/* Enable TX and RX */
580210040Scognet	write_4(sc, EMAC_NCR, RX_ENABLE | TX_ENABLE | MPE_ENABLE);
581210040Scognet
582210040Scognet
583210040Scognet	/* Enable interrupts */
584210040Scognet	write_4(sc, EMAC_IER, (RCOMP_INTERRUPT |
585210040Scognet			       RXUBR_INTERRUPT |
586210040Scognet			       TUND_INTERRUPT |
587210040Scognet			       RLE_INTERRUPT |
588210040Scognet			       TXERR_INTERRUPT |
589210040Scognet			       ROVR_INTERRUPT |
590210040Scognet			       HRESP_INTERRUPT|
591210040Scognet			       TCOMP_INTERRUPT
592210040Scognet			));
593210040Scognet
594210040Scognet	/*
595210040Scognet	 * Set 'running' flag, and clear output active flag
596210040Scognet	 * and attempt to start the output
597210040Scognet	 */
598210040Scognet	ifp->if_drv_flags |= IFF_DRV_RUNNING;
599210040Scognet	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
600210040Scognet
601210040Scognet	mii = device_get_softc(sc->miibus);
602210040Scognet
603210040Scognet	sc->flags |= MACB_FLAG_LINK;
604210040Scognet
605210040Scognet	mii_mediachg(mii);
606210040Scognet
607210040Scognet	callout_reset(&sc->tick_ch, hz, macb_tick, sc);
608210040Scognet}
609210040Scognet
610210040Scognet
611210040Scognetstatic void
612210040Scognetmacb_tx_cleanup(struct macb_softc *sc)
613210040Scognet{
614210040Scognet	struct ifnet *ifp;
615210040Scognet	struct eth_tx_desc *desc;
616210040Scognet	struct tx_desc_info *td;
617210040Scognet	int flags;
618210040Scognet	int status;
619210040Scognet	int i;
620210040Scognet
621210040Scognet	MACB_LOCK_ASSERT(sc);
622210040Scognet
623210040Scognet	status = read_4(sc, EMAC_TSR);
624210040Scognet
625210040Scognet	write_4(sc, EMAC_TSR, status);
626210040Scognet
627210040Scognet	/*buffer underrun*/
628210040Scognet	if ((status & TSR_UND) != 0) {
629210040Scognet		/*reset buffers*/
630210040Scognet		printf("underrun\n");
631210040Scognet		bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx,
632210040Scognet		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
633210040Scognet		sc->tx_cons = sc->tx_prod = 0;
634210040Scognet		for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
635210040Scognet			desc = &sc->desc_tx[i];
636210040Scognet			desc->flags = TD_OWN;
637210040Scognet		}
638210040Scognet
639210040Scognet		for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
640210040Scognet			td = &sc->tx_desc[i];
641210040Scognet			if (td->buff != NULL) {
642210040Scognet				/* We are finished with this descriptor. */
643210040Scognet				bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap,
644210040Scognet						BUS_DMASYNC_POSTWRITE);
645210040Scognet				/* ... and unload, so we can reuse. */
646210040Scognet				bus_dmamap_unload(sc->dmatag_data_tx,
647210040Scognet						  td->dmamap);
648210040Scognet				m_freem(td->buff);
649210040Scognet				td->buff = NULL;
650210040Scognet			}
651210040Scognet		}
652210040Scognet	}
653210040Scognet
654210040Scognet	if ((status & TSR_COMP) == 0)
655210040Scognet		return;
656210040Scognet
657210040Scognet
658210040Scognet	if (sc->tx_cons == sc->tx_prod)
659210040Scognet		return;
660210040Scognet
661210040Scognet	ifp = sc->ifp;
662210040Scognet
663210040Scognet	/* Prepare to read the ring (owner bit). */
664210040Scognet	bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx,
665210040Scognet	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
666210040Scognet	while (sc->tx_cons != sc->tx_prod) {
667210040Scognet		desc = &sc->desc_tx[sc->tx_cons];
668210040Scognet		if ((desc->flags & TD_OWN) == 0)
669210040Scognet			break;
670210040Scognet
671210040Scognet		td = &sc->tx_desc[sc->tx_cons];
672210040Scognet		if (td->buff != NULL) {
673210040Scognet			/* We are finished with this descriptor. */
674210040Scognet			bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap,
675210040Scognet					BUS_DMASYNC_POSTWRITE);
676210040Scognet			/* ... and unload, so we can reuse. */
677210040Scognet			bus_dmamap_unload(sc->dmatag_data_tx,
678210040Scognet					  td->dmamap);
679210040Scognet			m_freem(td->buff);
680210040Scognet			td->buff = NULL;
681210040Scognet			ifp->if_opackets++;
682210040Scognet		}
683210040Scognet
684210040Scognet		do {
685210040Scognet			sc->tx_cnt--;
686210040Scognet			MACB_DESC_INC(sc->tx_cons, MACB_MAX_TX_BUFFERS);
687210040Scognet			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
688210040Scognet			flags = desc->flags;
689210040Scognet			desc->flags = TD_OWN;
690210040Scognet			desc = &sc->desc_tx[sc->tx_cons];
691210040Scognet			if (flags & TD_LAST) {
692210040Scognet				break;
693210040Scognet			}
694210040Scognet		} while (sc->tx_cons != sc->tx_prod);
695210040Scognet	}
696210040Scognet
697210040Scognet	/* Unarm watchog timer when there is no pending descriptors in queue. */
698210040Scognet	if (sc->tx_cnt == 0)
699210040Scognet		sc->macb_watchdog_timer = 0;
700210040Scognet}
701210040Scognet
702210040Scognetstatic void
703210040Scognetmacb_rx(struct macb_softc *sc)
704210040Scognet{
705210040Scognet	struct eth_rx_desc	*rxdesc;
706210040Scognet	struct ifnet *ifp;
707210040Scognet	struct mbuf *m;
708210040Scognet	int rxbytes;
709210040Scognet	int flags;
710210040Scognet	int nsegs;
711210040Scognet	int first;
712210040Scognet
713210040Scognet	rxdesc = &(sc->desc_rx[sc->rx_cons]);
714210040Scognet
715210040Scognet	ifp = sc->ifp;
716210040Scognet
717210040Scognet	bus_dmamap_sync(sc->dmatag_ring_rx, sc->dmamap_ring_rx,
718210040Scognet	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
719210040Scognet
720210040Scognet
721210040Scognet	nsegs = 0;
722210040Scognet	while (rxdesc->addr & RD_OWN) {
723210040Scognet
724210040Scognet		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
725210040Scognet			break;
726210040Scognet
727210040Scognet		flags = rxdesc->flags;
728210040Scognet
729210040Scognet		rxbytes = flags & RD_LEN_MASK;
730210040Scognet
731210040Scognet		m = sc->rx_desc[sc->rx_cons].buff;
732210040Scognet
733236989Simp		bus_dmamap_sync(sc->dmatag_ring_rx,
734210040Scognet		    sc->rx_desc[sc->rx_cons].dmamap, BUS_DMASYNC_POSTREAD);
735210040Scognet		if (macb_new_rxbuf(sc, sc->rx_cons) != 0) {
736210040Scognet			ifp->if_iqdrops++;
737210040Scognet			first = sc->rx_cons;
738210040Scognet
739210040Scognet			do  {
740210040Scognet				rxdesc->flags = DATA_SIZE;
741210040Scognet				MACB_DESC_INC(sc->rx_cons, MACB_MAX_RX_BUFFERS);
742236989Simp				if ((rxdesc->flags & RD_EOF) != 0)
743210040Scognet					break;
744210040Scognet				rxdesc = &(sc->desc_rx[sc->rx_cons]);
745210040Scognet			} while (sc->rx_cons != first);
746210040Scognet
747210040Scognet			if (sc->macb_cdata.rxhead != NULL) {
748210040Scognet				m_freem(sc->macb_cdata.rxhead);
749210040Scognet				sc->macb_cdata.rxhead = NULL;
750210040Scognet				sc->macb_cdata.rxtail = NULL;
751210040Scognet			}
752210040Scognet
753210040Scognet			break;
754210040Scognet		}
755210040Scognet
756210040Scognet		nsegs++;
757210040Scognet
758210040Scognet		/* Chain received mbufs. */
759210040Scognet		if (sc->macb_cdata.rxhead == NULL) {
760210040Scognet			m->m_data += 2;
761210040Scognet			sc->macb_cdata.rxhead = m;
762210040Scognet			sc->macb_cdata.rxtail = m;
763210040Scognet			if (flags & RD_EOF)
764210040Scognet				m->m_len = rxbytes;
765210040Scognet			else
766210040Scognet				m->m_len = DATA_SIZE - 2;
767210040Scognet		} else {
768210040Scognet			m->m_flags &= ~M_PKTHDR;
769210040Scognet			m->m_len = DATA_SIZE;
770210040Scognet			sc->macb_cdata.rxtail->m_next = m;
771210040Scognet			sc->macb_cdata.rxtail = m;
772210040Scognet		}
773210040Scognet
774210040Scognet		if (flags & RD_EOF) {
775210040Scognet
776210040Scognet			if (nsegs > 1) {
777210040Scognet				sc->macb_cdata.rxtail->m_len = (rxbytes -
778210040Scognet				    ((nsegs - 1) * DATA_SIZE)) + 2;
779236989Simp			}
780210040Scognet
781210040Scognet			m = sc->macb_cdata.rxhead;
782210040Scognet			m->m_flags |= M_PKTHDR;
783210040Scognet			m->m_pkthdr.len = rxbytes;
784210040Scognet			m->m_pkthdr.rcvif = ifp;
785210040Scognet			ifp->if_ipackets++;
786210040Scognet
787210040Scognet			nsegs = 0;
788210040Scognet			MACB_UNLOCK(sc);
789210040Scognet			(*ifp->if_input)(ifp, m);
790210040Scognet			MACB_LOCK(sc);
791210040Scognet			sc->macb_cdata.rxhead = NULL;
792210040Scognet			sc->macb_cdata.rxtail = NULL;
793210040Scognet
794210040Scognet		}
795210040Scognet
796210040Scognet		rxdesc->addr &= ~RD_OWN;
797210040Scognet
798210040Scognet		MACB_DESC_INC(sc->rx_cons, MACB_MAX_RX_BUFFERS);
799210040Scognet
800210040Scognet		rxdesc = &(sc->desc_rx[sc->rx_cons]);
801210040Scognet	}
802210040Scognet
803210040Scognet	write_4(sc, EMAC_IER, (RCOMP_INTERRUPT|RXUBR_INTERRUPT));
804210040Scognet
805210040Scognet}
806210040Scognet
807210040Scognetstatic int
808210040Scognetmacb_intr_rx_locked(struct macb_softc *sc, int count)
809210040Scognet{
810210040Scognet	macb_rx(sc);
811210040Scognet	return (0);
812210040Scognet}
813210040Scognet
814210040Scognetstatic void
815210040Scognetmacb_intr_task(void *arg, int pending __unused)
816210040Scognet{
817210040Scognet	struct macb_softc *sc;
818210040Scognet
819210040Scognet	sc = arg;
820210040Scognet	MACB_LOCK(sc);
821210040Scognet	macb_intr_rx_locked(sc, -1);
822210040Scognet	MACB_UNLOCK(sc);
823210040Scognet}
824210040Scognet
825210040Scognetstatic void
826210040Scognetmacb_intr(void *xsc)
827210040Scognet{
828210040Scognet	struct macb_softc *sc;
829210040Scognet	struct ifnet *ifp;
830210040Scognet	uint32_t status;
831210040Scognet
832210040Scognet	sc = xsc;
833210040Scognet	ifp = sc->ifp;
834210040Scognet	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
835210040Scognet		printf("not running\n");
836210040Scognet		return;
837210040Scognet	}
838210040Scognet
839217062Sjhb	MACB_LOCK(sc);
840210040Scognet	status = read_4(sc, EMAC_ISR);
841210040Scognet
842210040Scognet	while (status) {
843210040Scognet		if (status & RCOMP_INTERRUPT) {
844210040Scognet			write_4(sc, EMAC_IDR, (RCOMP_INTERRUPT|RXUBR_INTERRUPT));
845210040Scognet			taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
846210040Scognet		}
847210040Scognet
848210040Scognet		if (status & TCOMP_INTERRUPT) {
849210040Scognet			macb_tx_cleanup(sc);
850210040Scognet		}
851210040Scognet
852210040Scognet		status = read_4(sc, EMAC_ISR);
853210040Scognet	}
854210040Scognet
855210040Scognet	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
856217062Sjhb		macbstart_locked(ifp);
857217062Sjhb	MACB_UNLOCK(sc);
858210040Scognet}
859210040Scognet
860210040Scognetstatic inline int
861210040Scognetmacb_encap(struct macb_softc *sc, struct mbuf **m_head)
862210040Scognet{
863210040Scognet	struct eth_tx_desc *desc;
864210040Scognet	struct tx_desc_info *txd, *txd_last;
865210040Scognet	struct mbuf *m;
866210040Scognet	bus_dma_segment_t segs[MAX_FRAGMENT];
867210040Scognet	bus_dmamap_t map;
868210040Scognet	uint32_t csum_flags;
869210040Scognet	int error, i, nsegs, prod, si;
870210040Scognet
871210040Scognet	M_ASSERTPKTHDR((*m_head));
872210040Scognet
873210040Scognet	prod = sc->tx_prod;
874210040Scognet
875210040Scognet	m = *m_head;
876210040Scognet
877210040Scognet	txd = txd_last = &sc->tx_desc[prod];
878210040Scognet	error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_tx, txd->dmamap,
879210040Scognet	    *m_head, segs, &nsegs, 0);
880210040Scognet	if (error == EFBIG) {
881243882Sglebius		m = m_collapse(*m_head, M_NOWAIT, MAX_FRAGMENT);
882210040Scognet		if (m == NULL) {
883210040Scognet			m_freem(*m_head);
884210040Scognet			*m_head = NULL;
885210040Scognet			return (ENOMEM);
886210040Scognet		}
887210040Scognet		*m_head = m;
888210040Scognet		error = bus_dmamap_load_mbuf_sg(sc->dmatag_ring_tx, txd->dmamap,
889210040Scognet		    *m_head, segs, &nsegs, 0);
890210040Scognet		if (error != 0) {
891210040Scognet			m_freem(*m_head);
892210040Scognet			*m_head = NULL;
893210040Scognet			return (error);
894210040Scognet		}
895210040Scognet	} else if (error != 0) {
896210040Scognet		return (error);
897210040Scognet	}
898210040Scognet	/* Check for TX descriptor overruns. */
899210040Scognet	if (sc->tx_cnt + nsegs > MACB_MAX_TX_BUFFERS - 1) {
900210040Scognet		bus_dmamap_unload(sc->dmatag_ring_tx, txd->dmamap);
901210040Scognet		return (ENOBUFS);
902210040Scognet	}
903210040Scognet	bus_dmamap_sync(sc->dmatag_ring_tx, txd->dmamap, BUS_DMASYNC_PREWRITE);
904210040Scognet	m = *m_head;
905210040Scognet
906210040Scognet	/* TODO: VLAN hardware tag insertion. */
907210040Scognet
908210040Scognet	csum_flags = 0;
909210040Scognet	si = prod;
910210040Scognet	desc = NULL;
911210040Scognet
912210040Scognet	for (i = 0; i < nsegs; i++) {
913210040Scognet		desc = &sc->desc_tx[prod];
914210040Scognet		desc->addr = segs[i].ds_addr;
915210040Scognet
916210040Scognet		if (i == 0 ) {
917210040Scognet			desc->flags = segs[i].ds_len | TD_OWN;
918210040Scognet		} else {
919210040Scognet			desc->flags = segs[i].ds_len;
920210040Scognet		}
921210040Scognet
922210040Scognet		if (prod == MACB_MAX_TX_BUFFERS - 1)
923210040Scognet			desc->flags |= TD_WRAP_MASK;
924210040Scognet
925210040Scognet		sc->tx_cnt++;
926210040Scognet		MACB_DESC_INC(prod, MACB_MAX_TX_BUFFERS);
927210040Scognet	}
928210040Scognet	/*
929210040Scognet	 * Set EOP on the last fragment.
930210040Scognet	 */
931210040Scognet
932210040Scognet	desc->flags |= TD_LAST;
933210040Scognet	desc = &sc->desc_tx[si];
934210040Scognet	desc->flags &= ~TD_OWN;
935210040Scognet
936210040Scognet	sc->tx_prod = prod;
937210040Scognet
938210040Scognet	/* Swap the first dma map and the last. */
939210040Scognet	map = txd_last->dmamap;
940210040Scognet	txd_last->dmamap = txd->dmamap;
941210040Scognet	txd->dmamap = map;
942210040Scognet	txd->buff = m;
943210040Scognet
944210040Scognet	return (0);
945210040Scognet}
946210040Scognet
947210040Scognet
948210040Scognetstatic void
949210040Scognetmacbstart_locked(struct ifnet *ifp)
950210040Scognet{
951210040Scognet
952210040Scognet
953210040Scognet
954210040Scognet	struct macb_softc *sc;
955210040Scognet	struct mbuf *m0;
956210040Scognet#if 0
957210040Scognet	struct mbuf *m_new;
958210040Scognet#endif
959210040Scognet	int queued = 0;
960210040Scognet
961210040Scognet	sc = ifp->if_softc;
962210040Scognet
963210040Scognet	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
964210040Scognet	    IFF_DRV_RUNNING || (sc->flags & MACB_FLAG_LINK) == 0) {
965210040Scognet		return;
966210040Scognet	}
967210040Scognet
968210040Scognet	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
969210040Scognet		/* Get packet from the queue */
970210040Scognet		IF_DEQUEUE(&ifp->if_snd, m0);
971210040Scognet		if (m0 == NULL)
972210040Scognet			break;
973210040Scognet#if 0
974210040Scognet		if (m0->m_next != NULL) {
975210040Scognet			/* Fragmented mbuf chain, collapse it. */
976243882Sglebius			m_new = m_defrag(m0, M_NOWAIT);
977210040Scognet			if (m_new != NULL) {
978210040Scognet				/* Original frame freed. */
979210040Scognet				m0 = m_new;
980210040Scognet			} else {
981210040Scognet				/* Defragmentation failed, just use the chain. */
982210040Scognet			}
983210040Scognet		}
984210040Scognet#endif
985210040Scognet		if (macb_encap(sc, &m0)) {
986210040Scognet			if (m0 == NULL)
987210040Scognet				break;
988210040Scognet			IF_PREPEND(&ifp->if_snd, m0);
989210040Scognet			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
990210040Scognet			break;
991210040Scognet		}
992210040Scognet		queued++;
993210040Scognet		BPF_MTAP(ifp, m0);
994210040Scognet	}
995210040Scognet	if (IFQ_DRV_IS_EMPTY(&ifp->if_snd))
996210040Scognet		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
997210040Scognet	if (queued) {
998210040Scognet		bus_dmamap_sync(sc->dmatag_data_tx, sc->dmamap_ring_tx,
999210040Scognet		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1000210040Scognet		write_4(sc, EMAC_NCR, read_4(sc, EMAC_NCR) | TRANSMIT_START);
1001210040Scognet		sc->macb_watchdog_timer = MACB_TIMEOUT;
1002210040Scognet	}
1003210040Scognet}
1004210040Scognet
1005210040Scognetstatic void
1006210040Scognetmacbinit(void *xsc)
1007210040Scognet{
1008210040Scognet	struct macb_softc *sc = xsc;
1009210040Scognet
1010210040Scognet	MACB_LOCK(sc);
1011210040Scognet	macbinit_locked(sc);
1012210040Scognet	MACB_UNLOCK(sc);
1013210040Scognet}
1014210040Scognet
1015210040Scognetstatic void
1016210040Scognetmacbstart(struct ifnet *ifp)
1017210040Scognet{
1018210040Scognet	struct macb_softc *sc = ifp->if_softc;
1019210040Scognet	MACB_ASSERT_UNLOCKED(sc);
1020210040Scognet	MACB_LOCK(sc);
1021210040Scognet	macbstart_locked(ifp);
1022210040Scognet	MACB_UNLOCK(sc);
1023210040Scognet
1024210040Scognet}
1025210040Scognet
1026210040Scognet
1027210040Scognetstatic void
1028210040Scognetmacbstop(struct macb_softc *sc)
1029210040Scognet{
1030210040Scognet	struct ifnet *ifp = sc->ifp;
1031210040Scognet	struct rx_desc_info *rd;
1032210040Scognet	struct tx_desc_info *td;
1033210040Scognet	int i;
1034210040Scognet
1035210040Scognet	ifp = sc->ifp;
1036210040Scognet
1037210040Scognet	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1038210040Scognet
1039210040Scognet	macb_reset(sc);
1040210040Scognet
1041210040Scognet	sc->flags &= ~MACB_FLAG_LINK;
1042210040Scognet	callout_stop(&sc->tick_ch);
1043210040Scognet	sc->macb_watchdog_timer = 0;
1044210040Scognet
1045210040Scognet	/* Free TX/RX mbufs still in the queues. */
1046210040Scognet	for (i = 0; i < MACB_MAX_TX_BUFFERS; i++) {
1047210040Scognet		td = &sc->tx_desc[i];
1048210040Scognet		if (td->buff != NULL) {
1049210040Scognet			bus_dmamap_sync(sc->dmatag_ring_tx, td->dmamap,
1050210040Scognet			    BUS_DMASYNC_POSTWRITE);
1051210040Scognet			bus_dmamap_unload(sc->dmatag_data_tx, td->dmamap);
1052210040Scognet			m_freem(td->buff);
1053210040Scognet			td->buff = NULL;
1054210040Scognet		}
1055210040Scognet	}
1056210040Scognet	for (i = 0; i < MACB_MAX_RX_BUFFERS; i++) {
1057210040Scognet		rd = &sc->rx_desc[i];
1058210040Scognet		if (rd->buff != NULL) {
1059210040Scognet			bus_dmamap_sync(sc->dmatag_ring_rx, rd->dmamap,
1060210040Scognet			    BUS_DMASYNC_POSTREAD);
1061210040Scognet			bus_dmamap_unload(sc->dmatag_data_rx, rd->dmamap);
1062210040Scognet			m_freem(rd->buff);
1063210040Scognet			rd->buff = NULL;
1064210040Scognet		}
1065210040Scognet	}
1066210040Scognet}
1067210040Scognet
1068210040Scognetstatic int
1069210040Scognetget_hash_index(uint8_t *mac)
1070210040Scognet{
1071210040Scognet	int i, j, k;
1072210040Scognet	int result;
1073210040Scognet	int bit;
1074210040Scognet
1075210040Scognet	result = 0;
1076210040Scognet	for (i = 0; i < 6; i++) {
1077210040Scognet		bit = 0;
1078210040Scognet		for (j = 0; j < 8;  j++) {
1079210040Scognet			k = j * 6 + i;
1080210040Scognet			bit ^= (mac[k/8] & (1 << (k % 8)) ) != 0;
1081210040Scognet		}
1082210040Scognet		result |= bit;
1083210040Scognet	}
1084210040Scognet	return result;
1085210040Scognet}
1086210040Scognet
1087210040Scognetstatic void
1088210040Scognetset_mac_filter(uint32_t *filter, uint8_t *mac)
1089210040Scognet{
1090210040Scognet	int bits;
1091210040Scognet
1092210040Scognet	bits = get_hash_index(mac);
1093210040Scognet	filter[bits >> 5] |= 1 << (bits & 31);
1094210040Scognet}
1095210040Scognet
1096210040Scognetstatic void
1097210040Scognetset_filter(struct macb_softc *sc)
1098210040Scognet{
1099210040Scognet	struct ifnet *ifp;
1100210040Scognet	struct ifmultiaddr *ifma;
1101210040Scognet	int config;
1102210040Scognet	int count;
1103210040Scognet	uint32_t multicast_filter[2];
1104210040Scognet
1105236989Simp	ifp = sc->ifp;
1106210040Scognet
1107210040Scognet	config = read_4(sc, EMAC_NCFGR);
1108210040Scognet
1109210040Scognet	config &= ~(CFG_CAF | CFG_MTI);
1110210040Scognet	write_4(sc, EMAC_HRB, 0);
1111210040Scognet	write_4(sc, EMAC_HRT, 0);
1112210040Scognet
1113210040Scognet	if ((ifp->if_flags & (IFF_ALLMULTI |IFF_PROMISC)) != 0){
1114210040Scognet		if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
1115210040Scognet			write_4(sc, EMAC_HRB, ~0);
1116210040Scognet			write_4(sc, EMAC_HRT, ~0);
1117210040Scognet			config |= CFG_MTI;
1118210040Scognet		}
1119210040Scognet		if ((ifp->if_flags & IFF_PROMISC) != 0) {
1120210040Scognet			config |= CFG_CAF;
1121210040Scognet		}
1122210040Scognet		write_4(sc, EMAC_NCFGR, config);
1123210040Scognet		return;
1124210040Scognet	}
1125210040Scognet
1126210040Scognet	if_maddr_rlock(ifp);
1127210040Scognet	count = 0;
1128210040Scognet	multicast_filter[0] = 0;
1129210040Scognet	multicast_filter[1] = 0;
1130210040Scognet
1131210040Scognet	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
1132210040Scognet		if (ifma->ifma_addr->sa_family != AF_LINK)
1133210040Scognet			continue;
1134210040Scognet		count++;
1135236989Simp		set_mac_filter(multicast_filter,
1136210040Scognet			   LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
1137210040Scognet	}
1138210040Scognet	if (count) {
1139210040Scognet		write_4(sc, EMAC_HRB, multicast_filter[0]);
1140210040Scognet		write_4(sc, EMAC_HRT, multicast_filter[1]);
1141210040Scognet		write_4(sc, EMAC_NCFGR, config|CFG_MTI);
1142210040Scognet	}
1143210040Scognet	if_maddr_runlock(ifp);
1144210040Scognet}
1145210040Scognet
1146210040Scognetstatic int
1147210040Scognetmacbioctl(struct ifnet * ifp, u_long cmd, caddr_t data)
1148210040Scognet{
1149210040Scognet
1150210040Scognet	struct macb_softc *sc = ifp->if_softc;
1151210040Scognet	struct mii_data *mii;
1152210040Scognet	struct ifreq *ifr = (struct ifreq *)data;
1153210040Scognet
1154210040Scognet	int error = 0;
1155210040Scognet
1156210040Scognet	switch (cmd) {
1157210040Scognet	case SIOCSIFFLAGS:
1158210040Scognet		MACB_LOCK(sc);
1159210040Scognet
1160210040Scognet		if ((ifp->if_flags & IFF_UP) != 0) {
1161210040Scognet			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1162210040Scognet				if (((ifp->if_flags ^ sc->if_flags)
1163210040Scognet				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1164210040Scognet					set_filter(sc);
1165210040Scognet			} else {
1166210040Scognet				macbinit_locked(sc);
1167210040Scognet			}
1168210040Scognet		} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1169210040Scognet			macbstop(sc);
1170210040Scognet		}
1171210040Scognet		sc->if_flags = ifp->if_flags;
1172210040Scognet		MACB_UNLOCK(sc);
1173210040Scognet		break;
1174210040Scognet	case SIOCADDMULTI:
1175210040Scognet	case SIOCDELMULTI:
1176210040Scognet		MACB_LOCK(sc);
1177210040Scognet		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
1178210040Scognet			set_filter(sc);
1179210040Scognet
1180210040Scognet		MACB_UNLOCK(sc);
1181210040Scognet		break;
1182210040Scognet	case SIOCSIFMEDIA:
1183210040Scognet	case SIOCGIFMEDIA:
1184210040Scognet		mii = device_get_softc(sc->miibus);
1185210040Scognet		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
1186210040Scognet		break;
1187210040Scognet	default:
1188210040Scognet		error = ether_ioctl(ifp, cmd, data);
1189210040Scognet		break;
1190210040Scognet	}
1191210040Scognet	return (error);
1192210040Scognet
1193210040Scognet}
1194210040Scognet
1195210040Scognet/* bus entry points */
1196210040Scognet
1197210040Scognetstatic int
1198210040Scognetmacb_probe(device_t dev)
1199210040Scognet{
1200210040Scognet	device_set_desc(dev, "macb");
1201210040Scognet	return (0);
1202210040Scognet}
1203210040Scognet
1204210040Scognet/*
1205210040Scognet * Change media according to request.
1206210040Scognet */
1207210040Scognetstatic int
1208210040Scognetmacb_ifmedia_upd(struct ifnet *ifp)
1209210040Scognet{
1210210040Scognet	struct macb_softc *sc = ifp->if_softc;
1211210040Scognet	struct mii_data *mii;
1212210040Scognet
1213210040Scognet	mii = device_get_softc(sc->miibus);
1214210040Scognet	MACB_LOCK(sc);
1215210040Scognet	mii_mediachg(mii);
1216210040Scognet	MACB_UNLOCK(sc);
1217210040Scognet	return (0);
1218210040Scognet}
1219210040Scognet
1220210040Scognet/*
1221210040Scognet * Notify the world which media we're using.
1222210040Scognet */
1223210040Scognetstatic void
1224210040Scognetmacb_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1225210040Scognet{
1226210040Scognet	struct macb_softc *sc = ifp->if_softc;
1227210040Scognet	struct mii_data *mii;
1228210040Scognet
1229210040Scognet	mii = device_get_softc(sc->miibus);
1230210040Scognet
1231210040Scognet	MACB_LOCK(sc);
1232210040Scognet	/* Don't report link state if driver is not running. */
1233210040Scognet	if ((ifp->if_flags & IFF_UP) == 0) {
1234210040Scognet		MACB_UNLOCK(sc);
1235210040Scognet		return;
1236210040Scognet	}
1237210040Scognet	mii_pollstat(mii);
1238210040Scognet	ifmr->ifm_active = mii->mii_media_active;
1239210040Scognet	ifmr->ifm_status = mii->mii_media_status;
1240210040Scognet	MACB_UNLOCK(sc);
1241210040Scognet}
1242210040Scognet
1243210040Scognetstatic void
1244210040Scognetmacb_reset(struct macb_softc *sc)
1245210040Scognet{
1246210040Scognet	/*
1247210040Scognet	 * Disable RX and TX
1248210040Scognet	 */
1249210040Scognet	write_4(sc, EMAC_NCR, 0);
1250210040Scognet
1251210040Scognet	write_4(sc, EMAC_NCR, CLEAR_STAT);
1252210040Scognet
1253210040Scognet	/* Clear all status flags */
1254210040Scognet	write_4(sc, EMAC_TSR, ~0UL);
1255210040Scognet	write_4(sc, EMAC_RSR, ~0UL);
1256210040Scognet
1257210040Scognet	/* Disable all interrupts */
1258210040Scognet	write_4(sc, EMAC_IDR, ~0UL);
1259210040Scognet	read_4(sc, EMAC_ISR);
1260210040Scognet
1261210040Scognet}
1262210040Scognet
1263210040Scognet
1264210040Scognetstatic int
1265210040Scognetmacb_get_mac(struct macb_softc *sc, u_char *eaddr)
1266210040Scognet{
1267210040Scognet	uint32_t bottom;
1268210040Scognet	uint16_t top;
1269210040Scognet
1270210040Scognet	bottom = read_4(sc, EMAC_SA1B);
1271210040Scognet	top = read_4(sc, EMAC_SA1T);
1272210040Scognet
1273210040Scognet	eaddr[0] = bottom & 0xff;
1274210040Scognet	eaddr[1] = (bottom >> 8) & 0xff;
1275210040Scognet	eaddr[2] = (bottom >> 16) & 0xff;
1276210040Scognet	eaddr[3] = (bottom >> 24) & 0xff;
1277210040Scognet	eaddr[4] = top & 0xff;
1278210040Scognet	eaddr[5] = (top >> 8) & 0xff;
1279210040Scognet
1280210040Scognet	return (0);
1281210040Scognet}
1282210040Scognet
1283210040Scognet
1284210040Scognetstatic int
1285210040Scognetmacb_attach(device_t dev)
1286210040Scognet{
1287210040Scognet	struct macb_softc *sc;
1288210040Scognet	struct ifnet *ifp = NULL;
1289210040Scognet	struct sysctl_ctx_list *sctx;
1290210040Scognet	struct sysctl_oid *soid;
1291210040Scognet	int pclk_hz;
1292210040Scognet	u_char eaddr[ETHER_ADDR_LEN];
1293210040Scognet	int rid;
1294210040Scognet	int err;
1295210040Scognet	struct at91_pmc_clock *master;
1296210040Scognet
1297210040Scognet
1298210040Scognet	err = 0;
1299210040Scognet
1300210040Scognet	sc = device_get_softc(dev);
1301210040Scognet	sc->dev = dev;
1302210040Scognet
1303210040Scognet	MACB_LOCK_INIT(sc);
1304210040Scognet
1305210040Scognet	callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0);
1306210040Scognet
1307210040Scognet	/*
1308210040Scognet	 * Allocate resources.
1309210040Scognet	 */
1310210040Scognet	rid = 0;
1311210040Scognet	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1312210040Scognet	    RF_ACTIVE);
1313210040Scognet	if (sc->mem_res == NULL) {
1314210040Scognet		device_printf(dev, "could not allocate memory resources.\n");
1315210040Scognet		err = ENOMEM;
1316210040Scognet		goto out;
1317210040Scognet	}
1318210040Scognet	rid = 0;
1319210040Scognet	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1320210040Scognet	    RF_ACTIVE);
1321210040Scognet	if (sc->irq_res == NULL) {
1322210040Scognet		device_printf(dev, "could not allocate interrupt resources.\n");
1323210040Scognet		err = ENOMEM;
1324210040Scognet		goto out;
1325210040Scognet	}
1326210040Scognet
1327210040Scognet	/*setup clock*/
1328213496Scognet	sc->clk = at91_pmc_clock_ref(device_get_nameunit(sc->dev));
1329210040Scognet	at91_pmc_clock_enable(sc->clk);
1330210040Scognet
1331210040Scognet	macb_reset(sc);
1332210040Scognet	macb_get_mac(sc, eaddr);
1333210040Scognet
1334210040Scognet	master = at91_pmc_clock_ref("mck");
1335210040Scognet
1336210040Scognet	pclk_hz = master->hz;
1337210040Scognet
1338210040Scognet	sc->clock = CFG_CLK_8;
1339210040Scognet	if (pclk_hz <= 20000000)
1340210040Scognet		sc->clock = CFG_CLK_8;
1341210040Scognet	else if (pclk_hz <= 40000000)
1342210040Scognet		sc->clock = CFG_CLK_16;
1343210040Scognet	else if (pclk_hz <= 80000000)
1344210040Scognet		sc->clock = CFG_CLK_32;
1345210040Scognet	else
1346210040Scognet		sc->clock = CFG_CLK_64;
1347210040Scognet
1348210040Scognet	sc->clock = sc->clock << 10;
1349210040Scognet
1350210040Scognet	write_4(sc, EMAC_NCFGR, sc->clock);
1351210040Scognet	write_4(sc, EMAC_USRIO, USRIO_CLOCK);       //enable clock
1352210040Scognet
1353210040Scognet	write_4(sc, EMAC_NCR, MPE_ENABLE); //enable MPE
1354210040Scognet
1355210040Scognet	sc->ifp = ifp = if_alloc(IFT_ETHER);
1356213894Smarius	err = mii_attach(dev, &sc->miibus, ifp, macb_ifmedia_upd,
1357213894Smarius	    macb_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
1358213894Smarius	if (err != 0) {
1359213894Smarius		device_printf(dev, "attaching PHYs failed\n");
1360210040Scognet		goto out;
1361210040Scognet	}
1362210040Scognet
1363210040Scognet	if (macb_allocate_dma(sc) != 0)
1364210040Scognet		goto out;
1365210040Scognet
1366210040Scognet	/* Sysctls */
1367210040Scognet	sctx = device_get_sysctl_ctx(dev);
1368210040Scognet	soid = device_get_sysctl_tree(dev);
1369210040Scognet
1370210040Scognet	ifp->if_softc = sc;
1371210040Scognet	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
1372210040Scognet	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
1373210040Scognet	ifp->if_capabilities |= IFCAP_VLAN_MTU;
1374210040Scognet	ifp->if_capenable |= IFCAP_VLAN_MTU;	/* The hw bits already set. */
1375210040Scognet	ifp->if_start = macbstart;
1376210040Scognet	ifp->if_ioctl = macbioctl;
1377210040Scognet	ifp->if_init = macbinit;
1378210040Scognet	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
1379210040Scognet	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
1380210040Scognet	IFQ_SET_READY(&ifp->if_snd);
1381210040Scognet	sc->if_flags = ifp->if_flags;
1382210040Scognet
1383210040Scognet	TASK_INIT(&sc->sc_intr_task, 0, macb_intr_task, sc);
1384210040Scognet
1385210040Scognet	sc->sc_tq = taskqueue_create_fast("macb_taskq", M_WAITOK,
1386210040Scognet	    taskqueue_thread_enqueue, &sc->sc_tq);
1387210040Scognet	if (sc->sc_tq == NULL) {
1388210040Scognet		device_printf(sc->dev, "could not create taskqueue\n");
1389210040Scognet		goto out;
1390210040Scognet	}
1391210040Scognet
1392210040Scognet	ether_ifattach(ifp, eaddr);
1393210040Scognet
1394210040Scognet	/*
1395210040Scognet	 * Activate the interrupt.
1396210040Scognet	 */
1397210040Scognet	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
1398210040Scognet	    NULL, macb_intr, sc, &sc->intrhand);
1399210040Scognet	if (err) {
1400210040Scognet		device_printf(dev, "could not establish interrupt handler.\n");
1401210040Scognet		ether_ifdetach(ifp);
1402210040Scognet		goto out;
1403210040Scognet	}
1404210040Scognet
1405210040Scognet	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
1406210040Scognet	    device_get_nameunit(sc->dev));
1407210040Scognet
1408210040Scognet	sc->macb_cdata.rxhead = 0;
1409210040Scognet	sc->macb_cdata.rxtail = 0;
1410210040Scognet
1411210040Scognet	phy_write(sc, 0, 0, 0x3300); //force autoneg
1412210040Scognet
1413210040Scognet	return (0);
1414210040Scognetout:
1415210040Scognet
1416210040Scognet	return (err);
1417210040Scognet}
1418210040Scognet
1419210040Scognetstatic int
1420210040Scognetmacb_detach(device_t dev)
1421210040Scognet{
1422210040Scognet	struct macb_softc *sc;
1423210040Scognet
1424210040Scognet	sc = device_get_softc(dev);
1425217062Sjhb	ether_ifdetach(sc->ifp);
1426217062Sjhb	MACB_LOCK(sc);
1427210040Scognet	macbstop(sc);
1428217062Sjhb	MACB_UNLOCK(sc);
1429217062Sjhb	callout_drain(&sc->tick_ch);
1430217062Sjhb	bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
1431217062Sjhb	taskqueue_drain(sc->sc_tq, &sc->sc_intr_task);
1432217062Sjhb	taskqueue_free(sc->sc_tq);
1433210040Scognet	macb_deactivate(dev);
1434217062Sjhb	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
1435217062Sjhb	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
1436217062Sjhb	MACB_LOCK_DESTROY(sc);
1437210040Scognet
1438210040Scognet	return (0);
1439210040Scognet}
1440210040Scognet
1441210040Scognet/*PHY related functions*/
1442210040Scognetstatic inline int
1443210040Scognetphy_read(struct macb_softc *sc, int phy, int reg)
1444210040Scognet{
1445210040Scognet	int val;
1446210040Scognet
1447210040Scognet	write_4(sc, EMAC_MAN, EMAC_MAN_REG_RD(phy, reg));
1448210040Scognet	while ((read_4(sc, EMAC_SR) & EMAC_SR_IDLE) == 0)
1449210040Scognet		continue;
1450210040Scognet	val = read_4(sc, EMAC_MAN) & EMAC_MAN_VALUE_MASK;
1451210040Scognet
1452210040Scognet	return (val);
1453210040Scognet}
1454210040Scognet
1455210040Scognetstatic inline int
1456210040Scognetphy_write(struct macb_softc *sc, int phy, int reg, int data)
1457210040Scognet{
1458210040Scognet
1459210040Scognet	write_4(sc, EMAC_MAN, EMAC_MAN_REG_WR(phy, reg, data));
1460210040Scognet	while ((read_4(sc, EMAC_SR) & EMAC_SR_IDLE) == 0)
1461210040Scognet		continue;
1462210040Scognet
1463210040Scognet	return (0);
1464210040Scognet}
1465210040Scognet
1466210040Scognet/*
1467210040Scognet * MII bus support routines.
1468210040Scognet */
1469210040Scognetstatic int
1470210040Scognetmacb_miibus_readreg(device_t dev, int phy, int reg)
1471210040Scognet{
1472210040Scognet	struct macb_softc *sc;
1473210040Scognet	sc = device_get_softc(dev);
1474210040Scognet	return (phy_read(sc, phy, reg));
1475210040Scognet}
1476210040Scognet
1477210040Scognetstatic int
1478210040Scognetmacb_miibus_writereg(device_t dev, int phy, int reg, int data)
1479210040Scognet{
1480210040Scognet	struct macb_softc *sc;
1481210040Scognet	sc = device_get_softc(dev);
1482210040Scognet	return (phy_write(sc, phy, reg, data));
1483210040Scognet}
1484210040Scognet
1485210040Scognetstatic void
1486210040Scognetmacb_child_detached(device_t dev, device_t child)
1487210040Scognet{
1488210040Scognet	struct macb_softc *sc;
1489210040Scognet	sc = device_get_softc(dev);
1490210040Scognet
1491210040Scognet}
1492210040Scognet
1493210040Scognetstatic void
1494210040Scognetmacb_miibus_statchg(device_t dev)
1495210040Scognet{
1496210040Scognet	struct macb_softc *sc;
1497210040Scognet	struct mii_data *mii;
1498210040Scognet	int config;
1499210040Scognet
1500210040Scognet	sc = device_get_softc(dev);
1501210040Scognet
1502210040Scognet	mii = device_get_softc(sc->miibus);
1503210040Scognet
1504236989Simp	sc->flags &= ~MACB_FLAG_LINK;
1505210040Scognet
1506210040Scognet	config = read_4(sc, EMAC_NCFGR);
1507210040Scognet
1508210040Scognet	if ((mii->mii_media_status & IFM_AVALID) != 0) {
1509210040Scognet		switch (IFM_SUBTYPE(mii->mii_media_active)) {
1510210040Scognet		case IFM_10_T:
1511210040Scognet			config &= ~(CFG_SPD);
1512210040Scognet			sc->flags |= MACB_FLAG_LINK;
1513210040Scognet			break;
1514210040Scognet		case IFM_100_TX:
1515210040Scognet			config |= CFG_SPD;
1516210040Scognet			sc->flags |= MACB_FLAG_LINK;
1517210040Scognet			break;
1518210040Scognet		default:
1519210040Scognet			break;
1520210040Scognet		}
1521210040Scognet	}
1522210040Scognet
1523210040Scognet	config |= CFG_FD;
1524210040Scognet	write_4(sc, EMAC_NCFGR, config);
1525210040Scognet}
1526210040Scognet
1527210040Scognetstatic device_method_t macb_methods[] = {
1528210040Scognet	/* Device interface */
1529210040Scognet	DEVMETHOD(device_probe,	macb_probe),
1530210040Scognet	DEVMETHOD(device_attach,	macb_attach),
1531210040Scognet	DEVMETHOD(device_detach,	macb_detach),
1532210040Scognet
1533210040Scognet	/* Bus interface */
1534210040Scognet	DEVMETHOD(bus_child_detached,	macb_child_detached),
1535210040Scognet
1536210040Scognet	/* MII interface */
1537210040Scognet	DEVMETHOD(miibus_readreg,	macb_miibus_readreg),
1538210040Scognet	DEVMETHOD(miibus_writereg,	macb_miibus_writereg),
1539210040Scognet	DEVMETHOD(miibus_statchg,	macb_miibus_statchg),
1540210040Scognet	{ 0, 0 }
1541210040Scognet};
1542210040Scognet
1543210040Scognetstatic driver_t macb_driver = {
1544210040Scognet	"macb",
1545210040Scognet	macb_methods,
1546210040Scognet	sizeof(struct macb_softc),
1547210040Scognet};
1548210040Scognet
1549210040Scognet
1550210040ScognetDRIVER_MODULE(macb, atmelarm, macb_driver, macb_devclass, 0, 0);
1551210040ScognetDRIVER_MODULE(miibus, macb, miibus_driver, miibus_devclass, 0, 0);
1552210040ScognetMODULE_DEPEND(macb, miibus, 1, 1, 1);
1553210040ScognetMODULE_DEPEND(macb, ether, 1, 1, 1);
1554