1178173Simp/* $NetBSD: if_admsw.c,v 1.3 2007/04/22 19:26:25 dyoung Exp $ */
2178173Simp
3178173Simp/*-
4178173Simp * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko.
5178173Simp * All rights reserved.
6178173Simp *
7178173Simp * Redistribution and use in source and binary forms, with or
8178173Simp * without modification, are permitted provided that the following
9178173Simp * conditions are met:
10178173Simp * 1. Redistributions of source code must retain the above copyright
11178173Simp *    notice, this list of conditions and the following disclaimer.
12178173Simp * 2. Redistributions in binary form must reproduce the above
13178173Simp *    copyright notice, this list of conditions and the following
14178173Simp *    disclaimer in the documentation and/or other materials provided
15178173Simp *    with the distribution.
16178173Simp * 3. The names of the authors may not be used to endorse or promote
17178173Simp *    products derived from this software without specific prior
18178173Simp *    written permission.
19178173Simp *
20178173Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY
21178173Simp * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22178173Simp * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23178173Simp * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS
24178173Simp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25178173Simp * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26178173Simp * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
27178173Simp * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28178173Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29178173Simp * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30178173Simp * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31178173Simp * OF SUCH DAMAGE.
32178173Simp */
33178173Simp/*
34178173Simp * Copyright (c) 2001 Wasabi Systems, Inc.
35178173Simp * All rights reserved.
36178173Simp *
37178173Simp * Written by Jason R. Thorpe for Wasabi Systems, Inc.
38178173Simp *
39178173Simp * Redistribution and use in source and binary forms, with or without
40178173Simp * modification, are permitted provided that the following conditions
41178173Simp * are met:
42178173Simp * 1. Redistributions of source code must retain the above copyright
43178173Simp *    notice, this list of conditions and the following disclaimer.
44178173Simp * 2. Redistributions in binary form must reproduce the above copyright
45178173Simp *    notice, this list of conditions and the following disclaimer in the
46178173Simp *    documentation and/or other materials provided with the distribution.
47178173Simp * 3. All advertising materials mentioning features or use of this software
48178173Simp *    must display the following acknowledgement:
49178173Simp *	This product includes software developed for the NetBSD Project by
50178173Simp *	Wasabi Systems, Inc.
51178173Simp * 4. The name of Wasabi Systems, Inc. may not be used to endorse
52178173Simp *    or promote products derived from this software without specific prior
53178173Simp *    written permission.
54178173Simp *
55178173Simp * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
56178173Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57178173Simp * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58178173Simp * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
59178173Simp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60178173Simp * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61178173Simp * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62178173Simp * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63178173Simp * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64178173Simp * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65178173Simp * POSSIBILITY OF SUCH DAMAGE.
66178173Simp */
67178173Simp
68178173Simp/*
69178173Simp * Device driver for Alchemy Semiconductor Au1x00 Ethernet Media
70178173Simp * Access Controller.
71178173Simp *
72178173Simp * TODO:
73178173Simp *
74178173Simp *	Better Rx buffer management; we want to get new Rx buffers
75178173Simp *	to the chip more quickly than we currently do.
76178173Simp */
77178173Simp
78178173Simp#include <sys/cdefs.h>
79178173Simp__FBSDID("$FreeBSD$");
80178173Simp
81178173Simp#include <sys/param.h>
82178173Simp#include <sys/systm.h>
83178173Simp#include <sys/bus.h>
84178173Simp#include <sys/kernel.h>
85178173Simp#include <sys/mbuf.h>
86178173Simp#include <sys/malloc.h>
87178173Simp#include <sys/module.h>
88178173Simp#include <sys/rman.h>
89178173Simp#include <sys/socket.h>
90178173Simp#include <sys/sockio.h>
91178173Simp#include <sys/sysctl.h>
92178173Simp#include <machine/bus.h>
93178173Simp
94178173Simp#include <net/ethernet.h>
95178173Simp#include <net/if.h>
96178173Simp#include <net/if_arp.h>
97178173Simp#include <net/if_dl.h>
98178173Simp#include <net/if_media.h>
99178173Simp#include <net/if_mib.h>
100178173Simp#include <net/if_types.h>
101178173Simp
102178173Simp#ifdef INET
103178173Simp#include <netinet/in.h>
104178173Simp#include <netinet/in_systm.h>
105178173Simp#include <netinet/in_var.h>
106178173Simp#include <netinet/ip.h>
107178173Simp#endif
108178173Simp
109178173Simp#include <net/bpf.h>
110178173Simp#include <net/bpfdesc.h>
111178173Simp
112182901Sgonzo#include <mips/adm5120/adm5120reg.h>
113182901Sgonzo#include <mips/adm5120/if_admswreg.h>
114182901Sgonzo#include <mips/adm5120/if_admswvar.h>
115178173Simp
116178173Simp/* TODO: add locking */
117178173Simp#define ADMSW_LOCK(sc) do {} while(0);
118178173Simp#define ADMSW_UNLOCK(sc) do {} while(0);
119178173Simp
120178173Simpstatic uint8_t vlan_matrix[SW_DEVS] = {
121178173Simp	(1 << 6) | (1 << 0),		/* CPU + port0 */
122178173Simp	(1 << 6) | (1 << 1),		/* CPU + port1 */
123178173Simp	(1 << 6) | (1 << 2),		/* CPU + port2 */
124178173Simp	(1 << 6) | (1 << 3),		/* CPU + port3 */
125178173Simp	(1 << 6) | (1 << 4),		/* CPU + port4 */
126178173Simp	(1 << 6) | (1 << 5),		/* CPU + port5 */
127178173Simp};
128178173Simp
129178173Simp/* ifnet entry points */
130178173Simpstatic void	admsw_start(struct ifnet *);
131199762Sjhbstatic void	admsw_watchdog(void *);
132178173Simpstatic int	admsw_ioctl(struct ifnet *, u_long, caddr_t);
133178173Simpstatic void	admsw_init(void *);
134178173Simpstatic void	admsw_stop(struct ifnet *, int);
135178173Simp
136178173Simpstatic void	admsw_reset(struct admsw_softc *);
137178173Simpstatic void	admsw_set_filter(struct admsw_softc *);
138178173Simp
139178173Simpstatic void	admsw_txintr(struct admsw_softc *, int);
140178173Simpstatic void	admsw_rxintr(struct admsw_softc *, int);
141178173Simpstatic int	admsw_add_rxbuf(struct admsw_softc *, int, int);
142178173Simp#define	admsw_add_rxhbuf(sc, idx)	admsw_add_rxbuf(sc, idx, 1)
143178173Simp#define	admsw_add_rxlbuf(sc, idx)	admsw_add_rxbuf(sc, idx, 0)
144178173Simp
145178173Simpstatic int	admsw_mediachange(struct ifnet *);
146178173Simpstatic void	admsw_mediastatus(struct ifnet *, struct ifmediareq *);
147178173Simp
148178173Simpstatic int	admsw_intr(void *);
149178173Simp
150178173Simp/* bus entry points */
151178173Simpstatic int	admsw_probe(device_t dev);
152178173Simpstatic int	admsw_attach(device_t dev);
153178173Simpstatic int	admsw_detach(device_t dev);
154194342Sbzstatic int	admsw_shutdown(device_t dev);
155178173Simp
156178173Simpstatic void
157178173Simpadmsw_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
158178173Simp{
159178173Simp	uint32_t *addr;
160178173Simp
161178173Simp	if (error)
162178173Simp		return;
163178173Simp
164178173Simp	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
165178173Simp	addr = arg;
166178173Simp	*addr = segs->ds_addr;
167178173Simp}
168178173Simp
169178173Simpstatic void
170178173Simpadmsw_rxbuf_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
171178173Simp{
172178173Simp	struct admsw_descsoft *ds;
173178173Simp
174178173Simp	if (error)
175178173Simp		return;
176178173Simp
177178173Simp	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
178178173Simp
179178173Simp	ds = arg;
180178173Simp	ds->ds_nsegs = nseg;
181178173Simp	ds->ds_addr[0] = segs[0].ds_addr;
182178173Simp	ds->ds_len[0] = segs[0].ds_len;
183178173Simp
184178173Simp}
185178173Simp
186178173Simpstatic void
187178173Simpadmsw_mbuf_map_addr(void *arg, bus_dma_segment_t *segs, int nseg,
188178173Simp    bus_size_t mapsize, int error)
189178173Simp{
190178173Simp	struct admsw_descsoft *ds;
191178173Simp
192178173Simp	if (error)
193178173Simp		return;
194178173Simp
195178173Simp	ds = arg;
196178173Simp
197178173Simp	if((nseg != 1) && (nseg != 2))
198178173Simp		panic("%s: nseg == %d\n", __func__, nseg);
199178173Simp
200178173Simp	ds->ds_nsegs = nseg;
201178173Simp	ds->ds_addr[0] = segs[0].ds_addr;
202178173Simp	ds->ds_len[0] = segs[0].ds_len;
203178173Simp
204178173Simp	if(nseg > 1) {
205178173Simp		ds->ds_addr[1] = segs[1].ds_addr;
206178173Simp		ds->ds_len[1] = segs[1].ds_len;
207178173Simp	}
208178173Simp}
209178173Simp
210178173Simp
211178173Simp
212178173Simpstatic int
213178173Simpadmsw_probe(device_t dev)
214178173Simp{
215178173Simp
216178173Simp	device_set_desc(dev, "ADM5120 Switch Engine");
217178173Simp	return (0);
218178173Simp}
219178173Simp
220178173Simp#define	REG_READ(o)	bus_read_4((sc)->mem_res, (o))
221178173Simp#define	REG_WRITE(o,v)	bus_write_4((sc)->mem_res, (o),(v))
222178173Simp
223178173Simpstatic void
224178173Simpadmsw_init_bufs(struct admsw_softc *sc)
225178173Simp{
226178173Simp	int i;
227178173Simp	struct admsw_desc *desc;
228178173Simp
229178173Simp	for (i = 0; i < ADMSW_NTXHDESC; i++) {
230178173Simp		if (sc->sc_txhsoft[i].ds_mbuf != NULL) {
231178173Simp			m_freem(sc->sc_txhsoft[i].ds_mbuf);
232178173Simp			sc->sc_txhsoft[i].ds_mbuf = NULL;
233178173Simp		}
234178173Simp		desc = &sc->sc_txhdescs[i];
235178173Simp		desc->data = 0;
236178173Simp		desc->cntl = 0;
237178173Simp		desc->len = MAC_BUFLEN;
238178173Simp		desc->status = 0;
239178173Simp		ADMSW_CDTXHSYNC(sc, i,
240178173Simp		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
241178173Simp	}
242178173Simp	sc->sc_txhdescs[ADMSW_NTXHDESC - 1].data |= ADM5120_DMA_RINGEND;
243178173Simp	ADMSW_CDTXHSYNC(sc, ADMSW_NTXHDESC - 1,
244178173Simp	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
245178173Simp
246178173Simp	for (i = 0; i < ADMSW_NRXHDESC; i++) {
247178173Simp		if (sc->sc_rxhsoft[i].ds_mbuf == NULL) {
248178173Simp			if (admsw_add_rxhbuf(sc, i) != 0)
249178173Simp				panic("admsw_init_bufs\n");
250178173Simp		} else
251178173Simp			ADMSW_INIT_RXHDESC(sc, i);
252178173Simp	}
253178173Simp
254178173Simp	for (i = 0; i < ADMSW_NTXLDESC; i++) {
255178173Simp		if (sc->sc_txlsoft[i].ds_mbuf != NULL) {
256178173Simp			m_freem(sc->sc_txlsoft[i].ds_mbuf);
257178173Simp			sc->sc_txlsoft[i].ds_mbuf = NULL;
258178173Simp		}
259178173Simp		desc = &sc->sc_txldescs[i];
260178173Simp		desc->data = 0;
261178173Simp		desc->cntl = 0;
262178173Simp		desc->len = MAC_BUFLEN;
263178173Simp		desc->status = 0;
264178173Simp		ADMSW_CDTXLSYNC(sc, i,
265178173Simp		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
266178173Simp	}
267178173Simp	sc->sc_txldescs[ADMSW_NTXLDESC - 1].data |= ADM5120_DMA_RINGEND;
268178173Simp	ADMSW_CDTXLSYNC(sc, ADMSW_NTXLDESC - 1,
269178173Simp	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
270178173Simp
271178173Simp	for (i = 0; i < ADMSW_NRXLDESC; i++) {
272178173Simp		if (sc->sc_rxlsoft[i].ds_mbuf == NULL) {
273178173Simp			if (admsw_add_rxlbuf(sc, i) != 0)
274178173Simp				panic("admsw_init_bufs\n");
275178173Simp		} else
276178173Simp			ADMSW_INIT_RXLDESC(sc, i);
277178173Simp	}
278178173Simp
279178173Simp	REG_WRITE(SEND_HBADDR_REG, ADMSW_CDTXHADDR(sc, 0));
280178173Simp	REG_WRITE(SEND_LBADDR_REG, ADMSW_CDTXLADDR(sc, 0));
281178173Simp	REG_WRITE(RECV_HBADDR_REG, ADMSW_CDRXHADDR(sc, 0));
282178173Simp	REG_WRITE(RECV_LBADDR_REG, ADMSW_CDRXLADDR(sc, 0));
283178173Simp
284178173Simp	sc->sc_txfree = ADMSW_NTXLDESC;
285178173Simp	sc->sc_txnext = 0;
286178173Simp	sc->sc_txdirty = 0;
287178173Simp	sc->sc_rxptr = 0;
288178173Simp}
289178173Simp
290178173Simpstatic void
291178173Simpadmsw_setvlan(struct admsw_softc *sc, char matrix[6])
292178173Simp{
293178173Simp	uint32_t i;
294178173Simp
295178173Simp	i = matrix[0] + (matrix[1] << 8) + (matrix[2] << 16) + (matrix[3] << 24);
296178173Simp	REG_WRITE(VLAN_G1_REG, i);
297178173Simp	i = matrix[4] + (matrix[5] << 8);
298178173Simp	REG_WRITE(VLAN_G2_REG, i);
299178173Simp}
300178173Simp
301178173Simpstatic void
302178173Simpadmsw_reset(struct admsw_softc *sc)
303178173Simp{
304178173Simp	uint32_t wdog1;
305178173Simp	int i;
306178173Simp
307178173Simp	REG_WRITE(PORT_CONF0_REG,
308178173Simp	    REG_READ(PORT_CONF0_REG) | PORT_CONF0_DP_MASK);
309178173Simp	REG_WRITE(CPUP_CONF_REG,
310178173Simp	    REG_READ(CPUP_CONF_REG) | CPUP_CONF_DCPUP);
311178173Simp
312178173Simp	/* Wait for DMA to complete.  Overkill.  In 3ms, we can
313178173Simp	 * send at least two entire 1500-byte packets at 10 Mb/s.
314178173Simp	 */
315178173Simp	DELAY(3000);
316178173Simp
317178173Simp	/* The datasheet recommends that we move all PHYs to reset
318178173Simp	 * state prior to software reset.
319178173Simp	 */
320178173Simp	REG_WRITE(PHY_CNTL2_REG,
321178173Simp	    REG_READ(PHY_CNTL2_REG) & ~PHY_CNTL2_PHYR_MASK);
322178173Simp
323178173Simp	/* Reset the switch. */
324178173Simp	REG_WRITE(ADMSW_SW_RES, 0x1);
325178173Simp
326178173Simp	DELAY(100 * 1000);
327178173Simp
328178173Simp	REG_WRITE(ADMSW_BOOT_DONE, ADMSW_BOOT_DONE_BO);
329178173Simp
330178173Simp	/* begin old code */
331178173Simp	REG_WRITE(CPUP_CONF_REG,
332178173Simp	    CPUP_CONF_DCPUP | CPUP_CONF_CRCP | CPUP_CONF_DUNP_MASK |
333178173Simp	    CPUP_CONF_DMCP_MASK);
334178173Simp
335178173Simp	REG_WRITE(PORT_CONF0_REG, PORT_CONF0_EMCP_MASK | PORT_CONF0_EMBP_MASK);
336178173Simp
337178173Simp	REG_WRITE(PHY_CNTL2_REG,
338178173Simp	    REG_READ(PHY_CNTL2_REG) | PHY_CNTL2_ANE_MASK | PHY_CNTL2_PHYR_MASK |
339178173Simp	    PHY_CNTL2_AMDIX_MASK);
340178173Simp
341178173Simp	REG_WRITE(PHY_CNTL3_REG, REG_READ(PHY_CNTL3_REG) | PHY_CNTL3_RNT);
342178173Simp
343178173Simp	REG_WRITE(ADMSW_INT_MASK, INT_MASK);
344178173Simp	REG_WRITE(ADMSW_INT_ST, INT_MASK);
345178173Simp
346178173Simp	/*
347178173Simp	 * While in DDB, we stop servicing interrupts, RX ring
348178173Simp	 * fills up and when free block counter falls behind FC
349178173Simp	 * threshold, the switch starts to emit 802.3x PAUSE
350178173Simp	 * frames.  This can upset peer switches.
351178173Simp	 *
352178173Simp	 * Stop this from happening by disabling FC and D2
353178173Simp	 * thresholds.
354178173Simp	 */
355178173Simp	REG_WRITE(FC_TH_REG,
356178173Simp	    REG_READ(FC_TH_REG) & ~(FC_TH_FCS_MASK | FC_TH_D2S_MASK));
357178173Simp
358178173Simp	admsw_setvlan(sc, vlan_matrix);
359178173Simp
360178173Simp	for (i = 0; i < SW_DEVS; i++) {
361178173Simp		REG_WRITE(MAC_WT1_REG,
362178173Simp		    sc->sc_enaddr[2] |
363178173Simp		    (sc->sc_enaddr[3]<<8) |
364178173Simp		    (sc->sc_enaddr[4]<<16) |
365178173Simp		    ((sc->sc_enaddr[5]+i)<<24));
366178173Simp		REG_WRITE(MAC_WT0_REG, (i<<MAC_WT0_VLANID_SHIFT) |
367178173Simp		    (sc->sc_enaddr[0]<<16) | (sc->sc_enaddr[1]<<24) |
368178173Simp		    MAC_WT0_WRITE | MAC_WT0_VLANID_EN);
369178173Simp
370178173Simp		while (!(REG_READ(MAC_WT0_REG) & MAC_WT0_WRITE_DONE));
371178173Simp	}
372178173Simp
373178173Simp	wdog1 = REG_READ(ADM5120_WDOG1);
374178173Simp	REG_WRITE(ADM5120_WDOG1, wdog1 & ~ADM5120_WDOG1_WDE);
375178173Simp}
376178173Simp
377178173Simpstatic int
378178173Simpadmsw_attach(device_t dev)
379178173Simp{
380178173Simp	uint8_t enaddr[ETHER_ADDR_LEN];
381178173Simp	struct admsw_softc *sc = (struct admsw_softc *) device_get_softc(dev);
382178173Simp	struct ifnet *ifp;
383178173Simp	int error, i, rid;
384178173Simp
385178173Simp	sc->sc_dev = dev;
386178173Simp	device_printf(dev, "ADM5120 Switch Engine, %d ports\n", SW_DEVS);
387178173Simp	sc->ndevs = 0;
388178173Simp
389178173Simp	/* XXXMIPS: fix it */
390178173Simp	enaddr[0] = 0x00;
391178173Simp	enaddr[1] = 0x0C;
392178173Simp	enaddr[2] = 0x42;
393178173Simp	enaddr[3] = 0x07;
394178173Simp	enaddr[4] = 0xB2;
395178173Simp	enaddr[5] = 0x4E;
396178173Simp
397178173Simp	memcpy(sc->sc_enaddr, enaddr, sizeof(sc->sc_enaddr));
398178173Simp
399178173Simp	device_printf(sc->sc_dev, "base Ethernet address %s\n",
400178173Simp	    ether_sprintf(enaddr));
401199762Sjhb	callout_init(&sc->sc_watchdog, 1);
402178173Simp
403178173Simp	rid = 0;
404178173Simp	if ((sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
405178173Simp	    RF_ACTIVE)) == NULL) {
406178173Simp                device_printf(dev, "unable to allocate memory resource\n");
407178173Simp                return (ENXIO);
408178173Simp        }
409178173Simp
410178173Simp	/* Hook up the interrupt handler. */
411178173Simp	rid = 0;
412178173Simp	if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
413178173Simp	    RF_SHAREABLE | RF_ACTIVE)) == NULL) {
414178173Simp                device_printf(dev, "unable to allocate IRQ resource\n");
415178173Simp                return (ENXIO);
416178173Simp        }
417178173Simp
418178173Simp	if ((error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
419178173Simp	    admsw_intr, NULL, sc, &sc->sc_ih)) != 0) {
420178173Simp                device_printf(dev,
421178173Simp                    "WARNING: unable to register interrupt handler\n");
422178173Simp                return (error);
423178173Simp        }
424178173Simp
425178173Simp	/*
426178173Simp	 * Allocate the control data structures, and create and load the
427178173Simp	 * DMA map for it.
428178173Simp	 */
429178173Simp	if ((error = bus_dma_tag_create(NULL, 4, 0,
430178173Simp	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
431178173Simp	    NULL, NULL, sizeof(struct admsw_control_data), 1,
432178173Simp	    sizeof(struct admsw_control_data), 0, NULL, NULL,
433178173Simp	    &sc->sc_control_dmat)) != 0) {
434178173Simp		device_printf(sc->sc_dev,
435178173Simp		    "unable to create control data DMA map, error = %d\n",
436178173Simp		    error);
437178173Simp		return (error);
438178173Simp	}
439178173Simp
440178173Simp	if ((error = bus_dmamem_alloc(sc->sc_control_dmat,
441178173Simp	    (void **)&sc->sc_control_data, BUS_DMA_NOWAIT,
442178173Simp	    &sc->sc_cddmamap)) != 0) {
443178173Simp		device_printf(sc->sc_dev,
444178173Simp		    "unable to allocate control data, error = %d\n", error);
445178173Simp		return (error);
446178173Simp	}
447178173Simp
448178173Simp	if ((error = bus_dmamap_load(sc->sc_control_dmat, sc->sc_cddmamap,
449178173Simp	    sc->sc_control_data, sizeof(struct admsw_control_data),
450178173Simp	    admsw_dma_map_addr, &sc->sc_cddma, 0)) != 0) {
451178173Simp		device_printf(sc->sc_dev,
452178173Simp		    "unable to load control data DMA map, error = %d\n", error);
453178173Simp		return (error);
454178173Simp	}
455178173Simp
456178173Simp	/*
457178173Simp	 * Create the transmit buffer DMA maps.
458178173Simp	 */
459178173Simp	if ((error = bus_dma_tag_create(NULL, 1, 0,
460178173Simp	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
461178173Simp	    NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL,
462178173Simp	    &sc->sc_bufs_dmat)) != 0) {
463178173Simp		device_printf(sc->sc_dev,
464178173Simp		    "unable to create control data DMA map, error = %d\n",
465178173Simp		    error);
466178173Simp		return (error);
467178173Simp	}
468178173Simp
469178173Simp	for (i = 0; i < ADMSW_NTXHDESC; i++) {
470178173Simp		if ((error = bus_dmamap_create(sc->sc_bufs_dmat, 0,
471178173Simp		    &sc->sc_txhsoft[i].ds_dmamap)) != 0) {
472178173Simp			device_printf(sc->sc_dev,
473178173Simp			    "unable to create txh DMA map %d, error = %d\n",
474178173Simp			    i, error);
475178173Simp			return (error);
476178173Simp		}
477178173Simp		sc->sc_txhsoft[i].ds_mbuf = NULL;
478178173Simp	}
479178173Simp
480178173Simp	for (i = 0; i < ADMSW_NTXLDESC; i++) {
481178173Simp		if ((error = bus_dmamap_create(sc->sc_bufs_dmat, 0,
482178173Simp		    &sc->sc_txlsoft[i].ds_dmamap)) != 0) {
483178173Simp			device_printf(sc->sc_dev,
484178173Simp			    "unable to create txl DMA map %d, error = %d\n",
485178173Simp			    i, error);
486178173Simp			return (error);
487178173Simp		}
488178173Simp		sc->sc_txlsoft[i].ds_mbuf = NULL;
489178173Simp	}
490178173Simp
491178173Simp	/*
492178173Simp	 * Create the receive buffer DMA maps.
493178173Simp	 */
494178173Simp	for (i = 0; i < ADMSW_NRXHDESC; i++) {
495178173Simp		if ((error = bus_dmamap_create(sc->sc_bufs_dmat, 0,
496178173Simp		     &sc->sc_rxhsoft[i].ds_dmamap)) != 0) {
497178173Simp			device_printf(sc->sc_dev,
498178173Simp			    "unable to create rxh DMA map %d, error = %d\n",
499178173Simp			    i, error);
500178173Simp			return (error);
501178173Simp		}
502178173Simp		sc->sc_rxhsoft[i].ds_mbuf = NULL;
503178173Simp	}
504178173Simp
505178173Simp	for (i = 0; i < ADMSW_NRXLDESC; i++) {
506178173Simp		if ((error = bus_dmamap_create(sc->sc_bufs_dmat, 0,
507178173Simp		    &sc->sc_rxlsoft[i].ds_dmamap)) != 0) {
508178173Simp			device_printf(sc->sc_dev,
509178173Simp			    "unable to create rxl DMA map %d, error = %d\n",
510178173Simp			    i, error);
511178173Simp			return (error);
512178173Simp		}
513178173Simp		sc->sc_rxlsoft[i].ds_mbuf = NULL;
514178173Simp	}
515178173Simp
516178173Simp	admsw_init_bufs(sc);
517178173Simp	admsw_reset(sc);
518178173Simp
519178173Simp	for (i = 0; i < SW_DEVS; i++) {
520178173Simp		ifmedia_init(&sc->sc_ifmedia[i], 0, admsw_mediachange,
521178173Simp		    admsw_mediastatus);
522178173Simp		ifmedia_add(&sc->sc_ifmedia[i], IFM_ETHER|IFM_10_T, 0, NULL);
523178173Simp		ifmedia_add(&sc->sc_ifmedia[i],
524178173Simp		    IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
525178173Simp		ifmedia_add(&sc->sc_ifmedia[i], IFM_ETHER|IFM_100_TX, 0, NULL);
526178173Simp		ifmedia_add(&sc->sc_ifmedia[i],
527178173Simp		    IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
528178173Simp		ifmedia_add(&sc->sc_ifmedia[i], IFM_ETHER|IFM_AUTO, 0, NULL);
529178173Simp		ifmedia_set(&sc->sc_ifmedia[i], IFM_ETHER|IFM_AUTO);
530178173Simp
531208022Simp		ifp = sc->sc_ifnet[i] = if_alloc(IFT_ETHER);
532178173Simp
533178173Simp		/* Setup interface parameters */
534178173Simp		ifp->if_softc = sc;
535178173Simp		if_initname(ifp, device_get_name(dev), i);
536178173Simp		ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
537178173Simp		ifp->if_ioctl = admsw_ioctl;
538178173Simp		ifp->if_output = ether_output;
539178173Simp		ifp->if_start = admsw_start;
540178173Simp		ifp->if_init = admsw_init;
541178173Simp		ifp->if_mtu = ETHERMTU;
542178173Simp		ifp->if_baudrate = IF_Mbps(100);
543207554Ssobomax		IFQ_SET_MAXLEN(&ifp->if_snd, max(ADMSW_NTXLDESC, ifqmaxlen));
544207554Ssobomax		ifp->if_snd.ifq_drv_maxlen = max(ADMSW_NTXLDESC, ifqmaxlen);
545178173Simp		IFQ_SET_READY(&ifp->if_snd);
546178173Simp		ifp->if_capabilities |= IFCAP_VLAN_MTU;
547178173Simp
548178173Simp		/* Attach the interface. */
549178173Simp		ether_ifattach(ifp, enaddr);
550178173Simp		enaddr[5]++;
551178173Simp	}
552178173Simp
553178173Simp	/* XXX: admwdog_attach(sc); */
554178173Simp
555178173Simp	/* leave interrupts and cpu port disabled */
556178173Simp	return (0);
557178173Simp}
558178173Simp
559178173Simpstatic int
560178173Simpadmsw_detach(device_t dev)
561178173Simp{
562178173Simp
563178173Simp	printf("TODO: DETACH\n");
564178173Simp	return (0);
565178173Simp}
566178173Simp
567178173Simp/*
568178173Simp * admsw_shutdown:
569178173Simp *
570178173Simp *	Make sure the interface is stopped at reboot time.
571178173Simp */
572194342Sbzstatic int
573194342Sbzadmsw_shutdown(device_t dev)
574178173Simp{
575194342Sbz	struct admsw_softc *sc;
576178173Simp	int i;
577178173Simp
578194342Sbz	sc = device_get_softc(dev);
579178173Simp	for (i = 0; i < SW_DEVS; i++)
580178173Simp		admsw_stop(sc->sc_ifnet[i], 1);
581194342Sbz
582194342Sbz	return (0);
583178173Simp}
584178173Simp
585178173Simp/*
586178173Simp * admsw_start:		[ifnet interface function]
587178173Simp *
588178173Simp *	Start packet transmission on the interface.
589178173Simp */
590178173Simpstatic void
591178173Simpadmsw_start(struct ifnet *ifp)
592178173Simp{
593178173Simp	struct admsw_softc *sc = ifp->if_softc;
594178173Simp	struct mbuf *m0, *m;
595178173Simp	struct admsw_descsoft *ds;
596178173Simp	struct admsw_desc *desc;
597178173Simp	bus_dmamap_t dmamap;
598178173Simp	struct ether_header *eh;
599178173Simp	int error, nexttx, len, i;
600178173Simp	static int vlan = 0;
601178173Simp
602178173Simp	/*
603178173Simp	 * Loop through the send queues, setting up transmit descriptors
604178173Simp	 * unitl we drain the queues, or use up all available transmit
605178173Simp	 * descriptors.
606178173Simp	 */
607178173Simp	for (;;) {
608178173Simp		vlan++;
609178173Simp		if (vlan == SW_DEVS)
610178173Simp			vlan = 0;
611178173Simp		i = vlan;
612178173Simp		for (;;) {
613178173Simp			ifp = sc->sc_ifnet[i];
614178173Simp			if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE))
615178173Simp			    == IFF_DRV_RUNNING) {
616178173Simp				/* Grab a packet off the queue. */
617178173Simp				IF_DEQUEUE(&ifp->if_snd, m0);
618178173Simp				if (m0 != NULL)
619178173Simp					break;
620178173Simp			}
621178173Simp			i++;
622178173Simp			if (i == SW_DEVS)
623178173Simp				i = 0;
624178173Simp			if (i == vlan)
625178173Simp				return;
626178173Simp		}
627178173Simp		vlan = i;
628178173Simp		m = NULL;
629178173Simp
630178173Simp		/* Get a spare descriptor. */
631178173Simp		if (sc->sc_txfree == 0) {
632178173Simp			/* No more slots left; notify upper layer. */
633178173Simp			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
634178173Simp			break;
635178173Simp		}
636178173Simp		nexttx = sc->sc_txnext;
637178173Simp		desc = &sc->sc_txldescs[nexttx];
638178173Simp		ds = &sc->sc_txlsoft[nexttx];
639178173Simp		dmamap = ds->ds_dmamap;
640178173Simp
641178173Simp		/*
642178173Simp		 * Load the DMA map.  If this fails, the packet either
643178173Simp		 * didn't fit in the alloted number of segments, or we
644178173Simp		 * were short on resources.  In this case, we'll copy
645178173Simp		 * and try again.
646178173Simp		 */
647178173Simp		if (m0->m_pkthdr.len < ETHER_MIN_LEN ||
648178173Simp		    bus_dmamap_load_mbuf(sc->sc_bufs_dmat, dmamap, m0,
649178173Simp		    admsw_mbuf_map_addr, ds, BUS_DMA_NOWAIT) != 0) {
650243882Sglebius			MGETHDR(m, M_NOWAIT, MT_DATA);
651178173Simp			if (m == NULL) {
652178173Simp				device_printf(sc->sc_dev,
653178173Simp				    "unable to allocate Tx mbuf\n");
654178173Simp				break;
655178173Simp			}
656178173Simp			if (m0->m_pkthdr.len > MHLEN) {
657243882Sglebius				MCLGET(m, M_NOWAIT);
658178173Simp				if ((m->m_flags & M_EXT) == 0) {
659178173Simp					device_printf(sc->sc_dev,
660178173Simp					    "unable to allocate Tx cluster\n");
661178173Simp					m_freem(m);
662178173Simp					break;
663178173Simp				}
664178173Simp			}
665178173Simp			m->m_pkthdr.csum_flags = m0->m_pkthdr.csum_flags;
666178173Simp			m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, void *));
667178173Simp			m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len;
668178173Simp			if (m->m_pkthdr.len < ETHER_MIN_LEN) {
669178173Simp				if (M_TRAILINGSPACE(m) < ETHER_MIN_LEN - m->m_pkthdr.len)
670178173Simp					panic("admsw_start: M_TRAILINGSPACE\n");
671178173Simp				memset(mtod(m, uint8_t *) + m->m_pkthdr.len, 0,
672178173Simp				    ETHER_MIN_LEN - ETHER_CRC_LEN - m->m_pkthdr.len);
673178173Simp				m->m_pkthdr.len = m->m_len = ETHER_MIN_LEN;
674178173Simp			}
675178173Simp			error = bus_dmamap_load_mbuf(sc->sc_bufs_dmat,
676178173Simp			    dmamap, m, admsw_mbuf_map_addr, ds, BUS_DMA_NOWAIT);
677178173Simp			if (error) {
678178173Simp				device_printf(sc->sc_dev,
679178173Simp				    "unable to load Tx buffer, error = %d\n",
680178173Simp				    error);
681178173Simp				break;
682178173Simp			}
683178173Simp		}
684178173Simp
685178173Simp		if (m != NULL) {
686178173Simp			m_freem(m0);
687178173Simp			m0 = m;
688178173Simp		}
689178173Simp
690178173Simp		/*
691178173Simp		 * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET.
692178173Simp		 */
693178173Simp
694178173Simp		/* Sync the DMA map. */
695178173Simp		bus_dmamap_sync(sc->sc_bufs_dmat, dmamap, BUS_DMASYNC_PREWRITE);
696178173Simp
697178173Simp		if (ds->ds_nsegs != 1 && ds->ds_nsegs != 2)
698178173Simp			panic("admsw_start: nsegs == %d\n", ds->ds_nsegs);
699178173Simp		desc->data = ds->ds_addr[0];
700178173Simp		desc->len = len = ds->ds_len[0];
701178173Simp		if (ds->ds_nsegs > 1) {
702178173Simp			len += ds->ds_len[1];
703178173Simp			desc->cntl = ds->ds_addr[1] | ADM5120_DMA_BUF2ENABLE;
704178173Simp		} else
705178173Simp			desc->cntl = 0;
706178173Simp		desc->status = (len << ADM5120_DMA_LENSHIFT) | (1 << vlan);
707178173Simp		eh = mtod(m0, struct ether_header *);
708178173Simp		if (ntohs(eh->ether_type) == ETHERTYPE_IP &&
709178173Simp		    m0->m_pkthdr.csum_flags & CSUM_IP)
710178173Simp			desc->status |= ADM5120_DMA_CSUM;
711178173Simp		if (nexttx == ADMSW_NTXLDESC - 1)
712178173Simp			desc->data |= ADM5120_DMA_RINGEND;
713178173Simp		desc->data |= ADM5120_DMA_OWN;
714178173Simp
715178173Simp		/* Sync the descriptor. */
716178173Simp		ADMSW_CDTXLSYNC(sc, nexttx,
717178173Simp		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
718178173Simp
719178173Simp		REG_WRITE(SEND_TRIG_REG, 1);
720178173Simp		/* printf("send slot %d\n",nexttx); */
721178173Simp
722178173Simp		/*
723178173Simp		 * Store a pointer to the packet so we can free it later.
724178173Simp		 */
725178173Simp		ds->ds_mbuf = m0;
726178173Simp
727178173Simp		/* Advance the Tx pointer. */
728178173Simp		sc->sc_txfree--;
729178173Simp		sc->sc_txnext = ADMSW_NEXTTXL(nexttx);
730178173Simp
731178173Simp		/* Pass the packet to any BPF listeners. */
732178173Simp		BPF_MTAP(ifp, m0);
733178173Simp
734178173Simp		/* Set a watchdog timer in case the chip flakes out. */
735199762Sjhb		sc->sc_timer = 5;
736178173Simp	}
737178173Simp}
738178173Simp
739178173Simp/*
740178173Simp * admsw_watchdog:	[ifnet interface function]
741178173Simp *
742178173Simp *	Watchdog timer handler.
743178173Simp */
744178173Simpstatic void
745199762Sjhbadmsw_watchdog(void *arg)
746178173Simp{
747199762Sjhb	struct admsw_softc *sc = arg;
748200484Sbz	struct ifnet *ifp;
749178173Simp	int vlan;
750178173Simp
751200484Sbz	callout_reset(&sc->sc_watchdog, hz, admsw_watchdog, sc);
752200484Sbz	if (sc->sc_timer == 0 || --sc->sc_timer > 0)
753199762Sjhb		return;
754199762Sjhb
755178173Simp	/* Check if an interrupt was lost. */
756178173Simp	if (sc->sc_txfree == ADMSW_NTXLDESC) {
757178173Simp		device_printf(sc->sc_dev, "watchdog false alarm\n");
758178173Simp		return;
759178173Simp	}
760199762Sjhb	if (sc->sc_timer != 0)
761178173Simp		device_printf(sc->sc_dev, "watchdog timer is %d!\n",
762199762Sjhb		    sc->sc_timer);
763178173Simp	admsw_txintr(sc, 0);
764178173Simp	if (sc->sc_txfree == ADMSW_NTXLDESC) {
765178173Simp		device_printf(sc->sc_dev, "tx IRQ lost (queue empty)\n");
766178173Simp		return;
767178173Simp	}
768199762Sjhb	if (sc->sc_timer != 0) {
769178173Simp		device_printf(sc->sc_dev, "tx IRQ lost (timer recharged)\n");
770178173Simp		return;
771178173Simp	}
772178173Simp
773178173Simp	device_printf(sc->sc_dev, "device timeout, txfree = %d\n",
774178173Simp	    sc->sc_txfree);
775178173Simp	for (vlan = 0; vlan < SW_DEVS; vlan++)
776178173Simp		admsw_stop(sc->sc_ifnet[vlan], 0);
777178173Simp	admsw_init(sc);
778178173Simp
779200484Sbz	ifp = sc->sc_ifnet[0];
780200484Sbz
781178173Simp	/* Try to get more packets going. */
782178173Simp	admsw_start(ifp);
783178173Simp}
784178173Simp
785178173Simp/*
786178173Simp * admsw_ioctl:		[ifnet interface function]
787178173Simp *
788178173Simp *	Handle control requests from the operator.
789178173Simp */
790178173Simpstatic int
791178173Simpadmsw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
792178173Simp{
793178173Simp	struct admsw_softc *sc = ifp->if_softc;
794178173Simp	struct ifdrv *ifd;
795178173Simp	int error, port;
796178173Simp
797178173Simp	ADMSW_LOCK(sc);
798178173Simp
799178173Simp	switch (cmd) {
800178173Simp	case SIOCSIFMEDIA:
801178173Simp	case SIOCGIFMEDIA:
802178173Simp		port = 0;
803178173Simp		while(port < SW_DEVS)
804178173Simp			if(ifp == sc->sc_ifnet[port])
805178173Simp				 break;
806178173Simp			else
807178173Simp				port++;
808178173Simp		if (port >= SW_DEVS)
809178173Simp			error = EOPNOTSUPP;
810178173Simp		else
811178173Simp			error = ifmedia_ioctl(ifp, (struct ifreq *)data,
812178173Simp			    &sc->sc_ifmedia[port], cmd);
813178173Simp		break;
814178173Simp
815178173Simp	case SIOCGDRVSPEC:
816178173Simp	case SIOCSDRVSPEC:
817178173Simp		ifd = (struct ifdrv *) data;
818178173Simp		if (ifd->ifd_cmd != 0 || ifd->ifd_len != sizeof(vlan_matrix)) {
819178173Simp			error = EINVAL;
820178173Simp			break;
821178173Simp		}
822178173Simp		if (cmd == SIOCGDRVSPEC) {
823178173Simp			error = copyout(vlan_matrix, ifd->ifd_data,
824178173Simp			    sizeof(vlan_matrix));
825178173Simp		} else {
826178173Simp			error = copyin(ifd->ifd_data, vlan_matrix,
827178173Simp			    sizeof(vlan_matrix));
828178173Simp			admsw_setvlan(sc, vlan_matrix);
829178173Simp		}
830178173Simp		break;
831178173Simp
832178173Simp	default:
833178173Simp		error = ether_ioctl(ifp, cmd, data);
834178173Simp		if (error == ENETRESET) {
835178173Simp			/*
836178173Simp			 * Multicast list has changed; set the hardware filter
837178173Simp			 * accordingly.
838178173Simp			 */
839178173Simp			admsw_set_filter(sc);
840178173Simp			error = 0;
841178173Simp		}
842178173Simp		break;
843178173Simp	}
844178173Simp
845178173Simp	/* Try to get more packets going. */
846178173Simp	admsw_start(ifp);
847178173Simp
848178173Simp	ADMSW_UNLOCK(sc);
849178173Simp	return (error);
850178173Simp}
851178173Simp
852178173Simp
853178173Simp/*
854178173Simp * admsw_intr:
855178173Simp *
856178173Simp *	Interrupt service routine.
857178173Simp */
858178173Simpstatic int
859178173Simpadmsw_intr(void *arg)
860178173Simp{
861178173Simp	struct admsw_softc *sc = arg;
862178173Simp	uint32_t pending;
863178173Simp
864178173Simp	pending = REG_READ(ADMSW_INT_ST);
865178173Simp	REG_WRITE(ADMSW_INT_ST, pending);
866178173Simp
867178173Simp	if (sc->ndevs == 0)
868178173Simp		return (FILTER_STRAY);
869178173Simp
870178173Simp	if ((pending & ADMSW_INTR_RHD) != 0)
871178173Simp		admsw_rxintr(sc, 1);
872178173Simp
873178173Simp	if ((pending & ADMSW_INTR_RLD) != 0)
874178173Simp		admsw_rxintr(sc, 0);
875178173Simp
876178173Simp	if ((pending & ADMSW_INTR_SHD) != 0)
877178173Simp		admsw_txintr(sc, 1);
878178173Simp
879178173Simp	if ((pending & ADMSW_INTR_SLD) != 0)
880178173Simp		admsw_txintr(sc, 0);
881178173Simp
882178173Simp	return (FILTER_HANDLED);
883178173Simp}
884178173Simp
885178173Simp/*
886178173Simp * admsw_txintr:
887178173Simp *
888178173Simp *	Helper; handle transmit interrupts.
889178173Simp */
890178173Simpstatic void
891178173Simpadmsw_txintr(struct admsw_softc *sc, int prio)
892178173Simp{
893178173Simp	struct ifnet *ifp;
894178173Simp	struct admsw_desc *desc;
895178173Simp	struct admsw_descsoft *ds;
896178173Simp	int i, vlan;
897178173Simp	int gotone = 0;
898178173Simp
899178173Simp	/* printf("txintr: txdirty: %d, txfree: %d\n",sc->sc_txdirty, sc->sc_txfree); */
900178173Simp	for (i = sc->sc_txdirty; sc->sc_txfree != ADMSW_NTXLDESC;
901178173Simp	    i = ADMSW_NEXTTXL(i)) {
902178173Simp
903178173Simp		ADMSW_CDTXLSYNC(sc, i,
904178173Simp		    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
905178173Simp
906178173Simp		desc = &sc->sc_txldescs[i];
907178173Simp		ds = &sc->sc_txlsoft[i];
908178173Simp		if (desc->data & ADM5120_DMA_OWN) {
909178173Simp			ADMSW_CDTXLSYNC(sc, i,
910178173Simp			    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
911178173Simp			break;
912178173Simp		}
913178173Simp
914178173Simp		bus_dmamap_sync(sc->sc_bufs_dmat, ds->ds_dmamap,
915178173Simp		    BUS_DMASYNC_POSTWRITE);
916178173Simp		bus_dmamap_unload(sc->sc_bufs_dmat, ds->ds_dmamap);
917178173Simp		m_freem(ds->ds_mbuf);
918178173Simp		ds->ds_mbuf = NULL;
919178173Simp
920178173Simp		vlan = ffs(desc->status & 0x3f) - 1;
921178173Simp		if (vlan < 0 || vlan >= SW_DEVS)
922178173Simp			panic("admsw_txintr: bad vlan\n");
923178173Simp		ifp = sc->sc_ifnet[vlan];
924178173Simp		gotone = 1;
925178173Simp		/* printf("clear tx slot %d\n",i); */
926178173Simp
927178173Simp		ifp->if_opackets++;
928178173Simp
929178173Simp		sc->sc_txfree++;
930178173Simp	}
931178173Simp
932178173Simp	if (gotone) {
933178173Simp		sc->sc_txdirty = i;
934178173Simp		for (vlan = 0; vlan < SW_DEVS; vlan++)
935178173Simp			sc->sc_ifnet[vlan]->if_drv_flags &= ~IFF_DRV_OACTIVE;
936178173Simp
937178173Simp		ifp = sc->sc_ifnet[0];
938178173Simp
939178173Simp		/* Try to queue more packets. */
940178173Simp		admsw_start(ifp);
941178173Simp
942178173Simp		/*
943178173Simp		 * If there are no more pending transmissions,
944178173Simp		 * cancel the watchdog timer.
945178173Simp		 */
946178173Simp		if (sc->sc_txfree == ADMSW_NTXLDESC)
947199762Sjhb			sc->sc_timer = 0;
948178173Simp
949178173Simp	}
950178173Simp
951178173Simp	/* printf("txintr end: txdirty: %d, txfree: %d\n",sc->sc_txdirty, sc->sc_txfree); */
952178173Simp}
953178173Simp
954178173Simp/*
955178173Simp * admsw_rxintr:
956178173Simp *
957178173Simp *	Helper; handle receive interrupts.
958178173Simp */
959178173Simpstatic void
960178173Simpadmsw_rxintr(struct admsw_softc *sc, int high)
961178173Simp{
962178173Simp	struct ifnet *ifp;
963178173Simp	struct admsw_descsoft *ds;
964178173Simp	struct mbuf *m;
965178173Simp	uint32_t stat;
966178173Simp	int i, len, port, vlan;
967178173Simp
968178173Simp	/* printf("rxintr\n"); */
969178173Simp
970178173Simp	if (high)
971178173Simp		panic("admsw_rxintr: high priority packet\n");
972178173Simp
973178173Simp#if 1
974178173Simp	ADMSW_CDRXLSYNC(sc, sc->sc_rxptr,
975178173Simp	    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
976178173Simp	if ((sc->sc_rxldescs[sc->sc_rxptr].data & ADM5120_DMA_OWN) == 0)
977178173Simp		ADMSW_CDRXLSYNC(sc, sc->sc_rxptr,
978178173Simp		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
979178173Simp	else {
980178173Simp		i = sc->sc_rxptr;
981178173Simp		do {
982178173Simp			ADMSW_CDRXLSYNC(sc, i,
983178173Simp			    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
984178173Simp			i = ADMSW_NEXTRXL(i);
985178173Simp			/* the ring is empty, just return. */
986178173Simp			if (i == sc->sc_rxptr)
987178173Simp				return;
988178173Simp			ADMSW_CDRXLSYNC(sc, i,
989178173Simp			    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
990178173Simp		} while (sc->sc_rxldescs[i].data & ADM5120_DMA_OWN);
991178173Simp
992178173Simp		ADMSW_CDRXLSYNC(sc, i,
993178173Simp		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
994178173Simp
995178173Simp		ADMSW_CDRXLSYNC(sc, sc->sc_rxptr,
996178173Simp		    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
997178173Simp
998178173Simp		if ((sc->sc_rxldescs[sc->sc_rxptr].data & ADM5120_DMA_OWN) == 0)
999178173Simp			ADMSW_CDRXLSYNC(sc, sc->sc_rxptr,
1000178173Simp			    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
1001178173Simp		else {
1002178173Simp			ADMSW_CDRXLSYNC(sc, sc->sc_rxptr,
1003178173Simp			    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
1004178173Simp			/* We've fallen behind the chip: catch it. */
1005178173Simp#if 0
1006178173Simp			device_printf(sc->sc_dev,
1007178173Simp			   "RX ring resync, base=%x, work=%x, %d -> %d\n",
1008178173Simp			    REG_READ(RECV_LBADDR_REG),
1009178173Simp			    REG_READ(RECV_LWADDR_REG), sc->sc_rxptr, i);
1010178173Simp#endif
1011178173Simp			sc->sc_rxptr = i;
1012178173Simp			/* ADMSW_EVCNT_INCR(&sc->sc_ev_rxsync); */
1013178173Simp		}
1014178173Simp	}
1015178173Simp#endif
1016178173Simp	for (i = sc->sc_rxptr;; i = ADMSW_NEXTRXL(i)) {
1017178173Simp		ds = &sc->sc_rxlsoft[i];
1018178173Simp
1019178173Simp		ADMSW_CDRXLSYNC(sc, i,
1020178173Simp		    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
1021178173Simp
1022178173Simp		if (sc->sc_rxldescs[i].data & ADM5120_DMA_OWN) {
1023178173Simp			ADMSW_CDRXLSYNC(sc, i,
1024178173Simp			    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
1025178173Simp			break;
1026178173Simp		}
1027178173Simp
1028178173Simp		/* printf("process slot %d\n",i); */
1029178173Simp
1030178173Simp		bus_dmamap_sync(sc->sc_bufs_dmat, ds->ds_dmamap,
1031178173Simp		    BUS_DMASYNC_POSTREAD);
1032178173Simp
1033178173Simp		stat = sc->sc_rxldescs[i].status;
1034178173Simp		len = (stat & ADM5120_DMA_LEN) >> ADM5120_DMA_LENSHIFT;
1035178173Simp		len -= ETHER_CRC_LEN;
1036178173Simp		port = (stat & ADM5120_DMA_PORTID) >> ADM5120_DMA_PORTSHIFT;
1037178173Simp
1038178173Simp		for (vlan = 0; vlan < SW_DEVS; vlan++)
1039178173Simp			if ((1 << port) & vlan_matrix[vlan])
1040178173Simp				break;
1041178173Simp
1042178173Simp		if (vlan == SW_DEVS)
1043178173Simp			vlan = 0;
1044178173Simp
1045178173Simp		ifp = sc->sc_ifnet[vlan];
1046178173Simp
1047178173Simp		m = ds->ds_mbuf;
1048178173Simp		if (admsw_add_rxlbuf(sc, i) != 0) {
1049178173Simp			ifp->if_ierrors++;
1050178173Simp			ADMSW_INIT_RXLDESC(sc, i);
1051178173Simp			bus_dmamap_sync(sc->sc_bufs_dmat, ds->ds_dmamap,
1052178173Simp			    BUS_DMASYNC_PREREAD);
1053178173Simp			continue;
1054178173Simp		}
1055178173Simp
1056178173Simp		m->m_pkthdr.rcvif = ifp;
1057178173Simp		m->m_pkthdr.len = m->m_len = len;
1058178173Simp		if ((stat & ADM5120_DMA_TYPE) == ADM5120_DMA_TYPE_IP) {
1059178173Simp			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
1060178173Simp			if (!(stat & ADM5120_DMA_CSUMFAIL))
1061178173Simp				m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
1062178173Simp		}
1063178173Simp
1064178173Simp		BPF_MTAP(ifp, m);
1065178173Simp
1066178173Simp		/* Pass it on. */
1067178173Simp		(*ifp->if_input)(ifp, m);
1068178173Simp		ifp->if_ipackets++;
1069178173Simp	}
1070178173Simp
1071178173Simp	/* Update the receive pointer. */
1072178173Simp	sc->sc_rxptr = i;
1073178173Simp}
1074178173Simp
1075178173Simp/*
1076178173Simp * admsw_init:		[ifnet interface function]
1077178173Simp *
1078183427Simp *	Initialize the interface.
1079178173Simp */
1080178173Simpstatic void
1081178173Simpadmsw_init(void *xsc)
1082178173Simp{
1083178173Simp	struct admsw_softc *sc = xsc;
1084178173Simp	struct ifnet *ifp;
1085178173Simp	int i;
1086178173Simp
1087178173Simp	for (i = 0; i < SW_DEVS; i++) {
1088178173Simp		ifp = sc->sc_ifnet[i];
1089178173Simp		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1090178173Simp			if (sc->ndevs == 0) {
1091178173Simp				admsw_init_bufs(sc);
1092178173Simp				admsw_reset(sc);
1093178173Simp				REG_WRITE(CPUP_CONF_REG,
1094178173Simp				    CPUP_CONF_CRCP | CPUP_CONF_DUNP_MASK |
1095178173Simp				    CPUP_CONF_DMCP_MASK);
1096178173Simp				/* clear all pending interrupts */
1097178173Simp				REG_WRITE(ADMSW_INT_ST, INT_MASK);
1098178173Simp
1099178173Simp				/* enable needed interrupts */
1100178173Simp				REG_WRITE(ADMSW_INT_MASK,
1101178173Simp				    REG_READ(ADMSW_INT_MASK) &
1102178173Simp				    ~(ADMSW_INTR_SHD | ADMSW_INTR_SLD |
1103178173Simp					ADMSW_INTR_RHD | ADMSW_INTR_RLD |
1104178173Simp					ADMSW_INTR_HDF | ADMSW_INTR_LDF));
1105199762Sjhb
1106199762Sjhb				callout_reset(&sc->sc_watchdog, hz,
1107199762Sjhb				    admsw_watchdog, sc);
1108178173Simp			}
1109178173Simp			sc->ndevs++;
1110178173Simp		}
1111178173Simp
1112178173Simp
1113178173Simp		/* mark iface as running */
1114178173Simp		ifp->if_drv_flags |= IFF_DRV_RUNNING;
1115178173Simp		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1116178173Simp	}
1117178173Simp
1118178173Simp	/* Set the receive filter. */
1119178173Simp	admsw_set_filter(sc);
1120178173Simp}
1121178173Simp
1122178173Simp/*
1123178173Simp * admsw_stop:		[ifnet interface function]
1124178173Simp *
1125178173Simp *	Stop transmission on the interface.
1126178173Simp */
1127178173Simpstatic void
1128178173Simpadmsw_stop(struct ifnet *ifp, int disable)
1129178173Simp{
1130178173Simp	struct admsw_softc *sc = ifp->if_softc;
1131178173Simp
1132178173Simp	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
1133178173Simp		return;
1134178173Simp
1135178173Simp	if (--sc->ndevs == 0) {
1136178173Simp		/* printf("debug: de-initializing hardware\n"); */
1137178173Simp
1138178173Simp		/* disable cpu port */
1139178173Simp		REG_WRITE(CPUP_CONF_REG,
1140178173Simp				CPUP_CONF_DCPUP | CPUP_CONF_CRCP |
1141178173Simp				CPUP_CONF_DUNP_MASK | CPUP_CONF_DMCP_MASK);
1142178173Simp
1143178173Simp		/* XXX We should disable, then clear? --dyoung */
1144178173Simp		/* clear all pending interrupts */
1145178173Simp		REG_WRITE(ADMSW_INT_ST, INT_MASK);
1146178173Simp
1147178173Simp		/* disable interrupts */
1148178173Simp		REG_WRITE(ADMSW_INT_MASK, INT_MASK);
1149199762Sjhb
1150199762Sjhb		/* Cancel the watchdog timer. */
1151199762Sjhb		sc->sc_timer = 0;
1152199762Sjhb		callout_stop(&sc->sc_watchdog);
1153178173Simp	}
1154178173Simp
1155199762Sjhb	/* Mark the interface as down. */
1156178173Simp	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1157178173Simp
1158178173Simp	return;
1159178173Simp}
1160178173Simp
1161178173Simp/*
1162178173Simp * admsw_set_filter:
1163178173Simp *
1164178173Simp *	Set up the receive filter.
1165178173Simp */
1166178173Simpstatic void
1167178173Simpadmsw_set_filter(struct admsw_softc *sc)
1168178173Simp{
1169178173Simp	int i;
1170178173Simp	uint32_t allmc, anymc, conf, promisc;
1171178173Simp	struct ifnet *ifp;
1172178173Simp	struct ifmultiaddr *ifma;
1173178173Simp
1174178173Simp	/* Find which ports should be operated in promisc mode. */
1175178173Simp	allmc = anymc = promisc = 0;
1176178173Simp	for (i = 0; i < SW_DEVS; i++) {
1177178173Simp		ifp = sc->sc_ifnet[i];
1178178173Simp		if (ifp->if_flags & IFF_PROMISC)
1179178173Simp			promisc |= vlan_matrix[i];
1180178173Simp
1181178173Simp		ifp->if_flags &= ~IFF_ALLMULTI;
1182178173Simp
1183195049Srwatson		if_maddr_rlock(ifp);
1184178173Simp		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
1185178173Simp		{
1186178173Simp			if (ifma->ifma_addr->sa_family != AF_LINK)
1187178173Simp				continue;
1188178173Simp
1189178173Simp			anymc |= vlan_matrix[i];
1190178173Simp		}
1191195049Srwatson		if_maddr_runlock(ifp);
1192178173Simp	}
1193178173Simp
1194178173Simp	conf = REG_READ(CPUP_CONF_REG);
1195178173Simp	/* 1 Disable forwarding of unknown & multicast packets to
1196178173Simp	 *   CPU on all ports.
1197178173Simp	 * 2 Enable forwarding of unknown & multicast packets to
1198178173Simp	 *   CPU on ports where IFF_PROMISC or IFF_ALLMULTI is set.
1199178173Simp	 */
1200178173Simp	conf |= CPUP_CONF_DUNP_MASK | CPUP_CONF_DMCP_MASK;
1201178173Simp	/* Enable forwarding of unknown packets to CPU on selected ports. */
1202178173Simp	conf ^= ((promisc << CPUP_CONF_DUNP_SHIFT) & CPUP_CONF_DUNP_MASK);
1203178173Simp	conf ^= ((allmc << CPUP_CONF_DMCP_SHIFT) & CPUP_CONF_DMCP_MASK);
1204178173Simp	conf ^= ((anymc << CPUP_CONF_DMCP_SHIFT) & CPUP_CONF_DMCP_MASK);
1205178173Simp	REG_WRITE(CPUP_CONF_REG, conf);
1206178173Simp}
1207178173Simp
1208178173Simp/*
1209178173Simp * admsw_add_rxbuf:
1210178173Simp *
1211178173Simp *	Add a receive buffer to the indicated descriptor.
1212178173Simp */
1213178173Simpint
1214178173Simpadmsw_add_rxbuf(struct admsw_softc *sc, int idx, int high)
1215178173Simp{
1216178173Simp	struct admsw_descsoft *ds;
1217178173Simp	struct mbuf *m;
1218178173Simp	int error;
1219178173Simp
1220178173Simp	if (high)
1221178173Simp		ds = &sc->sc_rxhsoft[idx];
1222178173Simp	else
1223178173Simp		ds = &sc->sc_rxlsoft[idx];
1224178173Simp
1225243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
1226178173Simp	if (m == NULL)
1227178173Simp		return (ENOBUFS);
1228178173Simp
1229243882Sglebius	MCLGET(m, M_NOWAIT);
1230178173Simp	if ((m->m_flags & M_EXT) == 0) {
1231178173Simp		m_freem(m);
1232178173Simp		return (ENOBUFS);
1233178173Simp	}
1234178173Simp
1235178173Simp	if (ds->ds_mbuf != NULL)
1236178173Simp		bus_dmamap_unload(sc->sc_bufs_dmat, ds->ds_dmamap);
1237178173Simp
1238178173Simp	ds->ds_mbuf = m;
1239178173Simp
1240178173Simp	error = bus_dmamap_load(sc->sc_bufs_dmat, ds->ds_dmamap,
1241178173Simp	    m->m_ext.ext_buf, m->m_ext.ext_size, admsw_rxbuf_map_addr,
1242178173Simp	    ds, BUS_DMA_NOWAIT);
1243178173Simp	if (error) {
1244178173Simp		device_printf(sc->sc_dev,
1245178173Simp		    "can't load rx DMA map %d, error = %d\n", idx, error);
1246178173Simp		panic("admsw_add_rxbuf");	/* XXX */
1247178173Simp	}
1248178173Simp
1249178173Simp	bus_dmamap_sync(sc->sc_bufs_dmat, ds->ds_dmamap, BUS_DMASYNC_PREREAD);
1250178173Simp
1251178173Simp	if (high)
1252178173Simp		ADMSW_INIT_RXHDESC(sc, idx);
1253178173Simp	else
1254178173Simp		ADMSW_INIT_RXLDESC(sc, idx);
1255178173Simp
1256178173Simp	return (0);
1257178173Simp}
1258178173Simp
1259178173Simpint
1260178173Simpadmsw_mediachange(struct ifnet *ifp)
1261178173Simp{
1262178173Simp	struct admsw_softc *sc = ifp->if_softc;
1263178173Simp	int port = 0;
1264178173Simp	struct ifmedia *ifm;
1265178173Simp	int old, new, val;
1266178173Simp
1267178173Simp	while(port < SW_DEVS) {
1268178173Simp		if(ifp == sc->sc_ifnet[port])
1269178173Simp			break;
1270178173Simp		else
1271178173Simp			port++;
1272178173Simp	}
1273178173Simp
1274178173Simp	ifm = &sc->sc_ifmedia[port];
1275178173Simp
1276178173Simp	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
1277178173Simp		return (EINVAL);
1278178173Simp
1279178173Simp	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
1280178173Simp		val = PHY_CNTL2_AUTONEG|PHY_CNTL2_100M|PHY_CNTL2_FDX;
1281178173Simp	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) {
1282178173Simp		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
1283178173Simp			val = PHY_CNTL2_100M|PHY_CNTL2_FDX;
1284178173Simp		else
1285178173Simp			val = PHY_CNTL2_100M;
1286178173Simp	} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
1287178173Simp		if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
1288178173Simp			val = PHY_CNTL2_FDX;
1289178173Simp		else
1290178173Simp			val = 0;
1291178173Simp	} else
1292178173Simp		return (EINVAL);
1293178173Simp
1294178173Simp	old = REG_READ(PHY_CNTL2_REG);
1295178173Simp	new = old & ~((PHY_CNTL2_AUTONEG|PHY_CNTL2_100M|PHY_CNTL2_FDX) << port);
1296178173Simp	new |= (val << port);
1297178173Simp
1298178173Simp	if (new != old)
1299178173Simp		REG_WRITE(PHY_CNTL2_REG, new);
1300178173Simp
1301178173Simp	return (0);
1302178173Simp}
1303178173Simp
1304178173Simpvoid
1305178173Simpadmsw_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
1306178173Simp{
1307178173Simp	struct admsw_softc *sc = ifp->if_softc;
1308178173Simp	int port = 0;
1309178173Simp	int status;
1310178173Simp
1311178173Simp	while(port < SW_DEVS) {
1312178173Simp		if(ifp == sc->sc_ifnet[port])
1313178173Simp			break;
1314178173Simp		else
1315178173Simp			port++;
1316178173Simp	}
1317178173Simp
1318178173Simp	ifmr->ifm_status = IFM_AVALID;
1319178173Simp	ifmr->ifm_active = IFM_ETHER;
1320178173Simp
1321178173Simp	status = REG_READ(PHY_ST_REG) >> port;
1322178173Simp
1323178173Simp	if ((status & PHY_ST_LINKUP) == 0) {
1324178173Simp		ifmr->ifm_active |= IFM_NONE;
1325178173Simp		return;
1326178173Simp	}
1327178173Simp
1328178173Simp	ifmr->ifm_status |= IFM_ACTIVE;
1329178173Simp	ifmr->ifm_active |= (status & PHY_ST_100M) ? IFM_100_TX : IFM_10_T;
1330178173Simp	if (status & PHY_ST_FDX)
1331178173Simp		ifmr->ifm_active |= IFM_FDX;
1332178173Simp}
1333178173Simp
1334178173Simpstatic device_method_t admsw_methods[] = {
1335178173Simp	/* Device interface */
1336178173Simp	DEVMETHOD(device_probe,		admsw_probe),
1337178173Simp	DEVMETHOD(device_attach,	admsw_attach),
1338178173Simp	DEVMETHOD(device_detach,	admsw_detach),
1339178173Simp	DEVMETHOD(device_shutdown,	admsw_shutdown),
1340178173Simp
1341178173Simp	{ 0, 0 }
1342178173Simp};
1343178173Simp
1344178173Simpstatic devclass_t admsw_devclass;
1345178173Simp
1346178173Simpstatic driver_t admsw_driver = {
1347178173Simp	"admsw",
1348178173Simp	admsw_methods,
1349178173Simp	sizeof(struct admsw_softc),
1350178173Simp};
1351178173Simp
1352178173SimpDRIVER_MODULE(admsw, obio, admsw_driver, admsw_devclass, 0, 0);
1353178173SimpMODULE_DEPEND(admsw, ether, 1, 1, 1);
1354