1139749Simp/*-
2116491Sharti * Copyright (c) 2001-2003
3116491Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4116491Sharti * 	All rights reserved.
5119418Sobrien * Author: Hartmut Brandt <harti@freebsd.org>
6116491Sharti *
7116491Sharti * Redistribution and use in source and binary forms, with or without
8116491Sharti * modification, are permitted provided that the following conditions
9116491Sharti * are met:
10116491Sharti * 1. Redistributions of source code must retain the above copyright
11116491Sharti *    notice, this list of conditions and the following disclaimer.
12116491Sharti * 2. Redistributions in binary form must reproduce the above copyright
13116491Sharti *    notice, this list of conditions and the following disclaimer in the
14116491Sharti *    documentation and/or other materials provided with the distribution.
15116491Sharti *
16116491Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17116491Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18116491Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19116491Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20116491Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21116491Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22116491Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23116491Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24116491Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25116491Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26116491Sharti * SUCH DAMAGE.
27119418Sobrien */
28119418Sobrien
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD$");
31119418Sobrien
32119418Sobrien/*
33116491Sharti * ForeHE driver.
34116491Sharti *
35116491Sharti * Interrupt handler.
36116491Sharti */
37116491Sharti
38116491Sharti#include "opt_inet.h"
39116491Sharti#include "opt_natm.h"
40116491Sharti
41116491Sharti#include <sys/types.h>
42116491Sharti#include <sys/param.h>
43116491Sharti#include <sys/systm.h>
44116491Sharti#include <sys/malloc.h>
45116491Sharti#include <sys/kernel.h>
46116491Sharti#include <sys/bus.h>
47116491Sharti#include <sys/errno.h>
48116491Sharti#include <sys/conf.h>
49116491Sharti#include <sys/module.h>
50116491Sharti#include <sys/queue.h>
51116491Sharti#include <sys/syslog.h>
52116491Sharti#include <sys/condvar.h>
53116491Sharti#include <sys/sysctl.h>
54116491Sharti#include <vm/uma.h>
55116491Sharti
56116491Sharti#include <sys/sockio.h>
57116491Sharti#include <sys/mbuf.h>
58116491Sharti#include <sys/socket.h>
59116491Sharti
60116491Sharti#include <net/if.h>
61257176Sglebius#include <net/if_var.h>
62116491Sharti#include <net/if_media.h>
63116491Sharti#include <net/if_atm.h>
64116491Sharti#include <net/route.h>
65116491Sharti#include <netinet/in.h>
66116491Sharti#include <netinet/if_atm.h>
67116491Sharti
68116491Sharti#include <machine/bus.h>
69116491Sharti#include <machine/resource.h>
70116491Sharti#include <sys/bus.h>
71116491Sharti#include <sys/rman.h>
72119280Simp#include <dev/pci/pcireg.h>
73119280Simp#include <dev/pci/pcivar.h>
74116491Sharti
75116491Sharti#include <dev/utopia/utopia.h>
76116491Sharti#include <dev/hatm/if_hatmconf.h>
77116491Sharti#include <dev/hatm/if_hatmreg.h>
78116491Sharti#include <dev/hatm/if_hatmvar.h>
79116491Sharti
80116491ShartiCTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
81116491ShartiCTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
82116491ShartiCTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
83116491ShartiCTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
84116491ShartiCTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
85116491ShartiCTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
86116491Sharti
87121729ShartiCTASSERT(MBUF0_PER_PAGE <= 256);
88121729ShartiCTASSERT(MBUF1_PER_PAGE <= 256);
89121729Sharti
90121675Shartistatic void hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group);
91121675Sharti
92116491Sharti/*
93121675Sharti * Free an external mbuf to a list. We use atomic functions so that
94121675Sharti * we don't need a mutex for the list.
95121744Sharti *
96121744Sharti * Note that in general this algorithm is not safe when multiple readers
97121744Sharti * and writers are present. To cite from a mail from David Schultz
98121744Sharti * <das@freebsd.org>:
99121744Sharti *
100121744Sharti *	It looks like this is subject to the ABA problem.  For instance,
101121744Sharti *	suppose X, Y, and Z are the top things on the freelist and a
102121744Sharti *	thread attempts to make an allocation.  You set buf to X and load
103121744Sharti *	buf->link (Y) into a register.  Then the thread get preempted, and
104121744Sharti *	another thread allocates both X and Y, then frees X.  When the
105121744Sharti *	original thread gets the CPU again, X is still on top of the
106121744Sharti *	freelist, so the atomic operation succeeds.  However, the atomic
107121744Sharti *	op places Y on top of the freelist, even though Y is no longer
108121744Sharti *	free.
109121744Sharti *
110121744Sharti * We are, however sure that we have only one thread that ever allocates
111121744Sharti * buffers because the only place we're call from is the interrupt handler.
112121744Sharti * Under these circumstances the code looks safe.
113121675Sharti */
114170411Smjacobvoid
115121675Shartihatm_ext_free(struct mbufx_free **list, struct mbufx_free *buf)
116121675Sharti{
117121675Sharti	for (;;) {
118121675Sharti		buf->link = *list;
119148067Sjhb		if (atomic_cmpset_ptr((uintptr_t *)list, (uintptr_t)buf->link,
120148067Sjhb		    (uintptr_t)buf))
121121675Sharti			break;
122121675Sharti	}
123121675Sharti}
124121675Sharti
125121675Shartistatic __inline struct mbufx_free *
126121675Shartihatm_ext_alloc(struct hatm_softc *sc, u_int g)
127121675Sharti{
128121675Sharti	struct mbufx_free *buf;
129121675Sharti
130121675Sharti	for (;;) {
131121675Sharti		if ((buf = sc->mbuf_list[g]) == NULL)
132121675Sharti			break;
133148067Sjhb		if (atomic_cmpset_ptr((uintptr_t *)&sc->mbuf_list[g],
134148067Sjhb			(uintptr_t)buf, (uintptr_t)buf->link))
135121675Sharti			break;
136121675Sharti	}
137121675Sharti	if (buf == NULL) {
138121675Sharti		hatm_mbuf_page_alloc(sc, g);
139121675Sharti		for (;;) {
140121675Sharti			if ((buf = sc->mbuf_list[g]) == NULL)
141121675Sharti				break;
142148067Sjhb			if (atomic_cmpset_ptr((uintptr_t *)&sc->mbuf_list[g],
143148067Sjhb			    (uintptr_t)buf, (uintptr_t)buf->link))
144121675Sharti				break;
145121675Sharti		}
146121675Sharti	}
147121675Sharti	return (buf);
148121675Sharti}
149121675Sharti
150121675Sharti/*
151116491Sharti * Either the queue treshold was crossed or a TPD with the INTR bit set
152116491Sharti * was transmitted.
153116491Sharti */
154116491Shartistatic void
155116491Shartihe_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
156116491Sharti{
157116491Sharti	uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
158116491Sharti	u_int no;
159116491Sharti
160116491Sharti	while (q->head != (*tailp >> 2)) {
161116491Sharti		no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
162116491Sharti		    HE_REGS_TPD_ADDR;
163116491Sharti		hatm_tx_complete(sc, TPD_ADDR(sc, no),
164116491Sharti		    (q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
165116491Sharti
166116491Sharti		if (++q->head == q->size)
167116491Sharti			q->head = 0;
168116491Sharti	}
169116491Sharti	WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
170116491Sharti}
171116491Sharti
172116491Sharti/*
173116491Sharti * DMA loader function for external mbuf page.
174116491Sharti */
175116491Shartistatic void
176116491Shartihatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
177116491Sharti    int error)
178116491Sharti{
179116491Sharti	if (error) {
180116491Sharti		printf("%s: mapping error %d\n", __func__, error);
181116491Sharti		return;
182116491Sharti	}
183116491Sharti	KASSERT(nsegs == 1,
184116491Sharti	    ("too many segments for DMA: %d", nsegs));
185116491Sharti	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
186116491Sharti	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
187116491Sharti
188116491Sharti	*(uint32_t *)arg = segs[0].ds_addr;
189116491Sharti}
190116491Sharti
191116491Sharti/*
192116491Sharti * Allocate a page of external mbuf storage for the small pools.
193116491Sharti * Create a DMA map and load it. Put all the chunks onto the right
194116491Sharti * free list.
195116491Sharti */
196116491Shartistatic void
197116491Shartihatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
198116491Sharti{
199116491Sharti	struct mbuf_page *pg;
200116491Sharti	int err;
201116491Sharti	u_int i;
202116491Sharti
203121686Sharti	if (sc->mbuf_npages == sc->mbuf_max_pages)
204116491Sharti		return;
205116491Sharti	if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
206116491Sharti		return;
207116491Sharti
208116491Sharti	err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
209116491Sharti	if (err != 0) {
210147256Sbrooks		if_printf(sc->ifp, "%s -- bus_dmamap_create: %d\n",
211116491Sharti		    __func__, err);
212116491Sharti		free(pg, M_DEVBUF);
213116491Sharti		return;
214116491Sharti	}
215116491Sharti	err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
216117382Sharti	    hatm_extbuf_helper, &pg->hdr.phys, BUS_DMA_NOWAIT);
217116491Sharti	if (err != 0) {
218147256Sbrooks		if_printf(sc->ifp, "%s -- mbuf mapping failed %d\n",
219116491Sharti		    __func__, err);
220116491Sharti		bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
221116491Sharti		free(pg, M_DEVBUF);
222116491Sharti		return;
223116491Sharti	}
224116491Sharti
225116491Sharti	sc->mbuf_pages[sc->mbuf_npages] = pg;
226116491Sharti
227116491Sharti	if (group == 0) {
228116491Sharti		struct mbuf0_chunk *c;
229116491Sharti
230121729Sharti		pg->hdr.pool = 0;
231116491Sharti		pg->hdr.nchunks = MBUF0_PER_PAGE;
232116491Sharti		pg->hdr.chunksize = MBUF0_CHUNK;
233116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
234116491Sharti		c = (struct mbuf0_chunk *)pg;
235116491Sharti		for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
236116491Sharti			c->hdr.pageno = sc->mbuf_npages;
237116491Sharti			c->hdr.chunkno = i;
238122111Sharti			c->hdr.flags = 0;
239121675Sharti			hatm_ext_free(&sc->mbuf_list[0],
240121675Sharti			    (struct mbufx_free *)c);
241116491Sharti		}
242116491Sharti	} else {
243116491Sharti		struct mbuf1_chunk *c;
244116491Sharti
245121729Sharti		pg->hdr.pool = 1;
246116491Sharti		pg->hdr.nchunks = MBUF1_PER_PAGE;
247116491Sharti		pg->hdr.chunksize = MBUF1_CHUNK;
248116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
249116491Sharti		c = (struct mbuf1_chunk *)pg;
250116491Sharti		for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
251116491Sharti			c->hdr.pageno = sc->mbuf_npages;
252116491Sharti			c->hdr.chunkno = i;
253122111Sharti			c->hdr.flags = 0;
254121675Sharti			hatm_ext_free(&sc->mbuf_list[1],
255121675Sharti			    (struct mbufx_free *)c);
256116491Sharti		}
257116491Sharti	}
258116491Sharti	sc->mbuf_npages++;
259116491Sharti}
260116491Sharti
261116491Sharti/*
262116491Sharti * Free an mbuf and put it onto the free list.
263116491Sharti */
264268529Sglebiusstatic void
265254799Sandrehatm_mbuf0_free(struct mbuf *m, void *buf, void *args)
266116491Sharti{
267116491Sharti	struct hatm_softc *sc = args;
268116491Sharti	struct mbuf0_chunk *c = buf;
269116491Sharti
270121729Sharti	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
271121729Sharti	    ("freeing unused mbuf %x", c->hdr.flags));
272121729Sharti	c->hdr.flags &= ~MBUF_USED;
273121675Sharti	hatm_ext_free(&sc->mbuf_list[0], (struct mbufx_free *)c);
274116491Sharti}
275268529Sglebiusstatic void
276254799Sandrehatm_mbuf1_free(struct mbuf *m, void *buf, void *args)
277116491Sharti{
278116491Sharti	struct hatm_softc *sc = args;
279116491Sharti	struct mbuf1_chunk *c = buf;
280116491Sharti
281121729Sharti	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
282121729Sharti	    ("freeing unused mbuf %x", c->hdr.flags));
283121729Sharti	c->hdr.flags &= ~MBUF_USED;
284121675Sharti	hatm_ext_free(&sc->mbuf_list[1], (struct mbufx_free *)c);
285116491Sharti}
286116491Sharti
287116491Shartistatic void
288116491Shartihatm_mbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
289116491Sharti{
290116491Sharti	uint32_t *ptr = (uint32_t *)arg;
291116491Sharti
292116491Sharti	if (nsegs == 0) {
293116491Sharti		printf("%s: error=%d\n", __func__, error);
294116491Sharti		return;
295116491Sharti	}
296116491Sharti	KASSERT(nsegs == 1, ("too many segments for mbuf: %d", nsegs));
297116491Sharti	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
298116491Sharti	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
299116491Sharti
300116491Sharti	*ptr = segs[0].ds_addr;
301116491Sharti}
302116491Sharti
303116491Sharti/*
304116491Sharti * Receive buffer pool interrupt. This means the number of entries in the
305116491Sharti * queue has dropped below the threshold. Try to supply new buffers.
306116491Sharti */
307116491Shartistatic void
308116491Shartihe_intr_rbp(struct hatm_softc *sc, struct herbp *rbp, u_int large,
309116491Sharti    u_int group)
310116491Sharti{
311121617Sharti	u_int ntail;
312116491Sharti	struct mbuf *m;
313116491Sharti	int error;
314121680Sharti	struct mbufx_free *cf;
315121680Sharti	struct mbuf_page *pg;
316121680Sharti	struct mbuf0_chunk *buf0;
317121680Sharti	struct mbuf1_chunk *buf1;
318116491Sharti
319116491Sharti	DBG(sc, INTR, ("%s buffer supply threshold crossed for group %u",
320116491Sharti	   large ? "large" : "small", group));
321116491Sharti
322116491Sharti	rbp->head = (READ4(sc, HE_REGO_RBP_S(large, group)) >> HE_REGS_RBP_HEAD)
323116491Sharti	    & (rbp->size - 1);
324116491Sharti
325116491Sharti	for (;;) {
326116491Sharti		if ((ntail = rbp->tail + 1) == rbp->size)
327116491Sharti			ntail = 0;
328116491Sharti		if (ntail == rbp->head)
329116491Sharti			break;
330121680Sharti		m = NULL;
331116491Sharti
332116491Sharti		if (large) {
333121676Sharti			/* allocate the MBUF */
334243857Sglebius			if ((m = m_getcl(M_NOWAIT, MT_DATA,
335116491Sharti			    M_PKTHDR)) == NULL) {
336147256Sbrooks				if_printf(sc->ifp,
337116491Sharti				    "no mbuf clusters\n");
338116491Sharti				break;
339116491Sharti			}
340116491Sharti			m->m_data += MBUFL_OFFSET;
341116491Sharti
342116491Sharti			if (sc->lbufs[sc->lbufs_next] != NULL)
343116491Sharti				panic("hatm: lbufs full %u", sc->lbufs_next);
344116491Sharti			sc->lbufs[sc->lbufs_next] = m;
345116491Sharti
346116491Sharti			if ((error = bus_dmamap_load(sc->mbuf_tag,
347116491Sharti			    sc->rmaps[sc->lbufs_next],
348116491Sharti			    m->m_data, rbp->bsize, hatm_mbuf_helper,
349123810Salfred			    &rbp->rbp[rbp->tail].phys, BUS_DMA_NOWAIT)) != 0)
350116491Sharti				panic("hatm: mbuf mapping failed %d", error);
351116491Sharti
352116491Sharti			bus_dmamap_sync(sc->mbuf_tag,
353116491Sharti			    sc->rmaps[sc->lbufs_next],
354116491Sharti			    BUS_DMASYNC_PREREAD);
355116491Sharti
356121680Sharti			rbp->rbp[rbp->tail].handle =
357121680Sharti			    MBUF_MAKE_LHANDLE(sc->lbufs_next);
358116491Sharti
359116491Sharti			if (++sc->lbufs_next == sc->lbufs_size)
360116491Sharti				sc->lbufs_next = 0;
361116491Sharti
362121680Sharti		} else if (group == 0) {
363121680Sharti			/*
364121680Sharti			 * Allocate small buffer in group 0
365121680Sharti			 */
366121680Sharti			if ((cf = hatm_ext_alloc(sc, 0)) == NULL)
367116491Sharti				break;
368121680Sharti			buf0 = (struct mbuf0_chunk *)cf;
369121680Sharti			pg = sc->mbuf_pages[buf0->hdr.pageno];
370121729Sharti			buf0->hdr.flags |= MBUF_CARD;
371121680Sharti			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
372121680Sharti			    buf0->hdr.chunkno * MBUF0_CHUNK + MBUF0_OFFSET;
373121680Sharti			rbp->rbp[rbp->tail].handle =
374121680Sharti			    MBUF_MAKE_HANDLE(buf0->hdr.pageno,
375121680Sharti			    buf0->hdr.chunkno);
376121680Sharti
377121680Sharti			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
378121680Sharti			    BUS_DMASYNC_PREREAD);
379121680Sharti
380121680Sharti		} else if (group == 1) {
381121680Sharti			/*
382121680Sharti			 * Allocate small buffer in group 1
383121680Sharti			 */
384121680Sharti			if ((cf = hatm_ext_alloc(sc, 1)) == NULL)
385121680Sharti				break;
386121680Sharti			buf1 = (struct mbuf1_chunk *)cf;
387121680Sharti			pg = sc->mbuf_pages[buf1->hdr.pageno];
388121729Sharti			buf1->hdr.flags |= MBUF_CARD;
389121680Sharti			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
390121680Sharti			    buf1->hdr.chunkno * MBUF1_CHUNK + MBUF1_OFFSET;
391121680Sharti			rbp->rbp[rbp->tail].handle =
392121680Sharti			    MBUF_MAKE_HANDLE(buf1->hdr.pageno,
393121680Sharti			    buf1->hdr.chunkno);
394121680Sharti
395121680Sharti			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
396121680Sharti			    BUS_DMASYNC_PREREAD);
397121680Sharti
398121680Sharti		} else
399121680Sharti			/* ups */
400121680Sharti			break;
401121680Sharti
402116491Sharti		DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
403116491Sharti		    rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
404116491Sharti
405116491Sharti		rbp->tail = ntail;
406116491Sharti	}
407121617Sharti	WRITE4(sc, HE_REGO_RBP_T(large, group),
408121617Sharti	    (rbp->tail << HE_REGS_RBP_TAIL));
409116491Sharti}
410116491Sharti
411116491Sharti/*
412116491Sharti * Extract the buffer and hand it to the receive routine
413116491Sharti */
414116491Shartistatic struct mbuf *
415116491Shartihatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
416116491Sharti{
417116491Sharti	u_int pageno;
418116491Sharti	u_int chunkno;
419116491Sharti	struct mbuf *m;
420116491Sharti
421116491Sharti	if (handle & MBUF_LARGE_FLAG) {
422116491Sharti		/* large buffer - sync and unload */
423121680Sharti		MBUF_PARSE_LHANDLE(handle, handle);
424116491Sharti		DBG(sc, RX, ("RX large handle=%x", handle));
425116491Sharti
426116491Sharti		bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
427116491Sharti		    BUS_DMASYNC_POSTREAD);
428116491Sharti		bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
429116491Sharti
430116491Sharti		m = sc->lbufs[handle];
431116491Sharti		sc->lbufs[handle] = NULL;
432116491Sharti
433116491Sharti		return (m);
434116491Sharti	}
435116491Sharti
436116491Sharti	MBUF_PARSE_HANDLE(handle, pageno, chunkno);
437116491Sharti
438116491Sharti	DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
439116491Sharti	    pageno, chunkno));
440116491Sharti
441243857Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
442121676Sharti
443116491Sharti	if (group == 0) {
444116491Sharti		struct mbuf0_chunk *c0;
445116491Sharti
446116491Sharti		c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
447116491Sharti		KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
448116491Sharti		    c0->hdr.pageno, pageno));
449116491Sharti		KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
450116491Sharti		    c0->hdr.chunkno, chunkno));
451121729Sharti		KASSERT(c0->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
452121729Sharti		    pageno, chunkno));
453121729Sharti		KASSERT(!(c0->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
454121729Sharti		    pageno, chunkno));
455116491Sharti
456121729Sharti		c0->hdr.flags |= MBUF_USED;
457121729Sharti		c0->hdr.flags &= ~MBUF_CARD;
458121729Sharti
459121676Sharti		if (m != NULL) {
460268530Sglebius			m->m_ext.ext_cnt = &c0->hdr.ref_cnt;
461150347Sandre			MEXTADD(m, (void *)c0, MBUF0_SIZE,
462175872Sphk			    hatm_mbuf0_free, c0, sc, M_PKTHDR, EXT_EXTREF);
463121676Sharti			m->m_data += MBUF0_OFFSET;
464121676Sharti		} else
465254842Sandre			(void)hatm_mbuf0_free(NULL, c0, sc);
466116491Sharti
467116491Sharti	} else {
468116491Sharti		struct mbuf1_chunk *c1;
469116491Sharti
470116491Sharti		c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
471116491Sharti		KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
472116491Sharti		    c1->hdr.pageno, pageno));
473116491Sharti		KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
474116491Sharti		    c1->hdr.chunkno, chunkno));
475121729Sharti		KASSERT(c1->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
476121729Sharti		    pageno, chunkno));
477121729Sharti		KASSERT(!(c1->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
478121729Sharti		    pageno, chunkno));
479116491Sharti
480121729Sharti		c1->hdr.flags |= MBUF_USED;
481121729Sharti		c1->hdr.flags &= ~MBUF_CARD;
482121729Sharti
483121676Sharti		if (m != NULL) {
484268530Sglebius			m->m_ext.ext_cnt = &c1->hdr.ref_cnt;
485150347Sandre			MEXTADD(m, (void *)c1, MBUF1_SIZE,
486175872Sphk			    hatm_mbuf1_free, c1, sc, M_PKTHDR, EXT_EXTREF);
487121676Sharti			m->m_data += MBUF1_OFFSET;
488121676Sharti		} else
489254842Sandre			(void)hatm_mbuf1_free(NULL, c1, sc);
490116491Sharti	}
491116491Sharti
492116491Sharti	return (m);
493116491Sharti}
494116491Sharti
495116491Sharti/*
496116491Sharti * Interrupt because of receive buffer returned.
497116491Sharti */
498116491Shartistatic void
499116491Shartihe_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
500116491Sharti{
501116491Sharti	struct he_rbrqen *e;
502116491Sharti	uint32_t flags, tail;
503116491Sharti	u_int cid, len;
504116491Sharti	struct mbuf *m;
505116491Sharti
506116491Sharti	for (;;) {
507116491Sharti		tail = sc->hsp->group[group].rbrq_tail >> 3;
508116491Sharti
509116491Sharti		if (rq->head == tail)
510116491Sharti			break;
511116491Sharti
512116491Sharti		e = &rq->rbrq[rq->head];
513116491Sharti
514116491Sharti		flags = e->addr & HE_REGM_RBRQ_FLAGS;
515116491Sharti		if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
516121680Sharti			m = hatm_rx_buffer(sc, group, e->addr);
517116491Sharti		else
518116491Sharti			m = NULL;
519116491Sharti
520116491Sharti		cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
521116491Sharti		len = 4 * (e->len & HE_REGM_RBRQ_LEN);
522116491Sharti
523116491Sharti		hatm_rx(sc, cid, flags, m, len);
524116491Sharti
525116491Sharti		if (++rq->head == rq->size)
526116491Sharti			rq->head = 0;
527116491Sharti	}
528116491Sharti	WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
529116491Sharti}
530116491Sharti
531116491Shartivoid
532116491Shartihatm_intr(void *p)
533116491Sharti{
534116491Sharti	struct heirq *q = p;
535116491Sharti	struct hatm_softc *sc = q->sc;
536116491Sharti	u_int status;
537116491Sharti	u_int tail;
538116491Sharti
539116491Sharti	/* if we have a stray interrupt with a non-initialized card,
540116491Sharti	 * we cannot even lock before looking at the flag */
541148887Srwatson	if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING))
542116491Sharti		return;
543116491Sharti
544116491Sharti	mtx_lock(&sc->mtx);
545116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
546116491Sharti
547116491Sharti	tail = *q->tailp;
548116491Sharti	if (q->head == tail) {
549116491Sharti		/* workaround for tail pointer not updated bug (8.1.1) */
550116491Sharti		DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
551116491Sharti
552116491Sharti		/* read the tail pointer from the card */
553116491Sharti		tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
554116491Sharti		    HE_REGM_IRQ_BASE_TAIL;
555116491Sharti		BARRIER_R(sc);
556116491Sharti
557116491Sharti		sc->istats.bug_no_irq_upd++;
558116491Sharti	}
559116491Sharti
560116491Sharti	/* clear the interrupt */
561116491Sharti	WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
562116491Sharti	BARRIER_W(sc);
563116491Sharti
564116491Sharti	while (q->head != tail) {
565116491Sharti		status = q->irq[q->head];
566116491Sharti		q->irq[q->head] = HE_REGM_ITYPE_INVALID;
567116491Sharti		if (++q->head == (q->size - 1))
568116491Sharti			q->head = 0;
569116491Sharti
570116491Sharti		switch (status & HE_REGM_ITYPE) {
571116491Sharti
572116491Sharti		  case HE_REGM_ITYPE_TBRQ:
573116491Sharti			DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
574116491Sharti			sc->istats.itype_tbrq++;
575116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
576116491Sharti			break;
577116491Sharti
578116491Sharti		  case HE_REGM_ITYPE_TPD:
579116491Sharti			DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
580116491Sharti			sc->istats.itype_tpd++;
581116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
582116491Sharti			break;
583116491Sharti
584116491Sharti		  case HE_REGM_ITYPE_RBPS:
585116491Sharti			sc->istats.itype_rbps++;
586116491Sharti			switch (status & HE_REGM_IGROUP) {
587116491Sharti
588116491Sharti			  case 0:
589116491Sharti				he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
590116491Sharti				break;
591116491Sharti
592116491Sharti			  case 1:
593116491Sharti				he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
594116491Sharti				break;
595116491Sharti
596116491Sharti			  default:
597147256Sbrooks				if_printf(sc->ifp, "bad INTR RBPS%u\n",
598116491Sharti				    status & HE_REGM_IGROUP);
599116491Sharti				break;
600116491Sharti			}
601116491Sharti			break;
602116491Sharti
603116491Sharti		  case HE_REGM_ITYPE_RBPL:
604116491Sharti			sc->istats.itype_rbpl++;
605116491Sharti			switch (status & HE_REGM_IGROUP) {
606116491Sharti
607116491Sharti			  case 0:
608116491Sharti				he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
609116491Sharti				break;
610116491Sharti
611116491Sharti			  default:
612147256Sbrooks				if_printf(sc->ifp, "bad INTR RBPL%u\n",
613116491Sharti				    status & HE_REGM_IGROUP);
614116491Sharti				break;
615116491Sharti			}
616116491Sharti			break;
617116491Sharti
618116491Sharti		  case HE_REGM_ITYPE_RBRQ:
619116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
620116491Sharti			sc->istats.itype_rbrq++;
621116491Sharti			switch (status & HE_REGM_IGROUP) {
622116491Sharti
623116491Sharti			  case 0:
624116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
625116491Sharti				break;
626116491Sharti
627116491Sharti			  case 1:
628116491Sharti				if (sc->rbrq_1.size > 0) {
629116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
630116491Sharti					break;
631116491Sharti				}
632116491Sharti				/* FALLTHRU */
633116491Sharti
634116491Sharti			  default:
635147256Sbrooks				if_printf(sc->ifp, "bad INTR RBRQ%u\n",
636116491Sharti				    status & HE_REGM_IGROUP);
637116491Sharti				break;
638116491Sharti			}
639116491Sharti			break;
640116491Sharti
641116491Sharti		  case HE_REGM_ITYPE_RBRQT:
642116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
643116491Sharti			sc->istats.itype_rbrqt++;
644116491Sharti			switch (status & HE_REGM_IGROUP) {
645116491Sharti
646116491Sharti			  case 0:
647116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
648116491Sharti				break;
649116491Sharti
650116491Sharti			  case 1:
651116491Sharti				if (sc->rbrq_1.size > 0) {
652116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
653116491Sharti					break;
654116491Sharti				}
655116491Sharti				/* FALLTHRU */
656116491Sharti
657116491Sharti			  default:
658147256Sbrooks				if_printf(sc->ifp, "bad INTR RBRQT%u\n",
659116491Sharti				    status & HE_REGM_IGROUP);
660116491Sharti				break;
661116491Sharti			}
662116491Sharti			break;
663116491Sharti
664116491Sharti		  case HE_REGM_ITYPE_PHYS:
665116491Sharti			sc->istats.itype_phys++;
666116491Sharti			utopia_intr(&sc->utopia);
667116491Sharti			break;
668116491Sharti
669116491Sharti#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
670116491Sharti		  case HE_REGM_ITYPE_UNKNOWN:
671116491Sharti			sc->istats.itype_unknown++;
672147256Sbrooks			if_printf(sc->ifp, "bad interrupt\n");
673116491Sharti			break;
674116491Sharti#endif
675116491Sharti
676116491Sharti		  case HE_REGM_ITYPE_ERR:
677116491Sharti			sc->istats.itype_err++;
678116491Sharti			switch (status) {
679116491Sharti
680116491Sharti			  case HE_REGM_ITYPE_PERR:
681147256Sbrooks				if_printf(sc->ifp, "parity error\n");
682116491Sharti				break;
683116491Sharti
684116491Sharti			  case HE_REGM_ITYPE_ABORT:
685147256Sbrooks				if_printf(sc->ifp, "abort interrupt "
686116491Sharti				    "addr=0x%08x\n",
687116491Sharti				    READ4(sc, HE_REGO_ABORT_ADDR));
688116491Sharti				break;
689116491Sharti
690116491Sharti			  default:
691147256Sbrooks				if_printf(sc->ifp,
692116491Sharti				    "bad interrupt type %08x\n", status);
693116491Sharti				break;
694116491Sharti			}
695116491Sharti			break;
696116491Sharti
697116491Sharti		  case HE_REGM_ITYPE_INVALID:
698116491Sharti			/* this is the documented fix for the ISW bug 8.1.1
699116491Sharti			 * Note, that the documented fix is partly wrong:
700116491Sharti			 * the ISWs should be intialized to 0xf8 not 0xff */
701116491Sharti			sc->istats.bug_bad_isw++;
702116491Sharti			DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
703116491Sharti			he_intr_tbrq(sc, &sc->tbrq, 0);
704116491Sharti			he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
705116491Sharti			he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
706116491Sharti			he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
707116491Sharti			he_intr_rbrq(sc, &sc->rbrq_0, 0);
708116491Sharti			he_intr_rbrq(sc, &sc->rbrq_1, 1);
709116491Sharti			utopia_intr(&sc->utopia);
710116491Sharti			break;
711116491Sharti
712116491Sharti		  default:
713147256Sbrooks			if_printf(sc->ifp, "bad interrupt type %08x\n",
714116491Sharti			    status);
715116491Sharti			break;
716116491Sharti		}
717116491Sharti	}
718116491Sharti
719116491Sharti	/* write back head to clear queue */
720116491Sharti	WRITE4(sc, HE_REGO_IRQ_HEAD(0),
721116491Sharti	    ((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
722116491Sharti	    (q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
723116491Sharti	    (q->head << HE_REGS_IRQ_HEAD_HEAD));
724116491Sharti	BARRIER_W(sc);
725116491Sharti
726116491Sharti	/* workaround the back-to-back irq access problem (8.1.2) */
727116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
728116491Sharti	BARRIER_R(sc);
729116491Sharti
730116491Sharti	mtx_unlock(&sc->mtx);
731116491Sharti}
732