if_hatm_intr.c revision 147256
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: head/sys/dev/hatm/if_hatm_intr.c 147256 2005-06-10 16:49:24Z brooks $");
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>
61116491Sharti#include <net/if_media.h>
62116491Sharti#include <net/if_atm.h>
63116491Sharti#include <net/route.h>
64116491Sharti#include <netinet/in.h>
65116491Sharti#include <netinet/if_atm.h>
66116491Sharti
67116491Sharti#include <machine/bus.h>
68116491Sharti#include <machine/resource.h>
69116491Sharti#include <sys/bus.h>
70116491Sharti#include <sys/rman.h>
71119280Simp#include <dev/pci/pcireg.h>
72119280Simp#include <dev/pci/pcivar.h>
73116491Sharti
74116491Sharti#include <dev/utopia/utopia.h>
75116491Sharti#include <dev/hatm/if_hatmconf.h>
76116491Sharti#include <dev/hatm/if_hatmreg.h>
77116491Sharti#include <dev/hatm/if_hatmvar.h>
78116491Sharti
79116491ShartiCTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
80116491ShartiCTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
81116491ShartiCTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
82116491ShartiCTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
83116491ShartiCTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
84116491ShartiCTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
85116491Sharti
86121729ShartiCTASSERT(MBUF0_PER_PAGE <= 256);
87121729ShartiCTASSERT(MBUF1_PER_PAGE <= 256);
88121729Sharti
89121675Shartistatic void hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group);
90121675Sharti
91116491Sharti/*
92121675Sharti * Free an external mbuf to a list. We use atomic functions so that
93121675Sharti * we don't need a mutex for the list.
94121744Sharti *
95121744Sharti * Note that in general this algorithm is not safe when multiple readers
96121744Sharti * and writers are present. To cite from a mail from David Schultz
97121744Sharti * <das@freebsd.org>:
98121744Sharti *
99121744Sharti *	It looks like this is subject to the ABA problem.  For instance,
100121744Sharti *	suppose X, Y, and Z are the top things on the freelist and a
101121744Sharti *	thread attempts to make an allocation.  You set buf to X and load
102121744Sharti *	buf->link (Y) into a register.  Then the thread get preempted, and
103121744Sharti *	another thread allocates both X and Y, then frees X.  When the
104121744Sharti *	original thread gets the CPU again, X is still on top of the
105121744Sharti *	freelist, so the atomic operation succeeds.  However, the atomic
106121744Sharti *	op places Y on top of the freelist, even though Y is no longer
107121744Sharti *	free.
108121744Sharti *
109121744Sharti * We are, however sure that we have only one thread that ever allocates
110121744Sharti * buffers because the only place we're call from is the interrupt handler.
111121744Sharti * Under these circumstances the code looks safe.
112121675Sharti */
113121729Sharti__inline void
114121675Shartihatm_ext_free(struct mbufx_free **list, struct mbufx_free *buf)
115121675Sharti{
116121675Sharti	for (;;) {
117121675Sharti		buf->link = *list;
118121675Sharti		if (atomic_cmpset_ptr(list, buf->link, buf))
119121675Sharti			break;
120121675Sharti	}
121121675Sharti}
122121675Sharti
123121675Shartistatic __inline struct mbufx_free *
124121675Shartihatm_ext_alloc(struct hatm_softc *sc, u_int g)
125121675Sharti{
126121675Sharti	struct mbufx_free *buf;
127121675Sharti
128121675Sharti	for (;;) {
129121675Sharti		if ((buf = sc->mbuf_list[g]) == NULL)
130121675Sharti			break;
131121675Sharti		if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
132121675Sharti			break;
133121675Sharti	}
134121675Sharti	if (buf == NULL) {
135121675Sharti		hatm_mbuf_page_alloc(sc, g);
136121675Sharti		for (;;) {
137121675Sharti			if ((buf = sc->mbuf_list[g]) == NULL)
138121675Sharti				break;
139121675Sharti			if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
140121675Sharti				break;
141121675Sharti		}
142121675Sharti	}
143121675Sharti	return (buf);
144121675Sharti}
145121675Sharti
146121675Sharti/*
147116491Sharti * Either the queue treshold was crossed or a TPD with the INTR bit set
148116491Sharti * was transmitted.
149116491Sharti */
150116491Shartistatic void
151116491Shartihe_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
152116491Sharti{
153116491Sharti	uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
154116491Sharti	u_int no;
155116491Sharti
156116491Sharti	while (q->head != (*tailp >> 2)) {
157116491Sharti		no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
158116491Sharti		    HE_REGS_TPD_ADDR;
159116491Sharti		hatm_tx_complete(sc, TPD_ADDR(sc, no),
160116491Sharti		    (q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
161116491Sharti
162116491Sharti		if (++q->head == q->size)
163116491Sharti			q->head = 0;
164116491Sharti	}
165116491Sharti	WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
166116491Sharti}
167116491Sharti
168116491Sharti/*
169116491Sharti * DMA loader function for external mbuf page.
170116491Sharti */
171116491Shartistatic void
172116491Shartihatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
173116491Sharti    int error)
174116491Sharti{
175116491Sharti	if (error) {
176116491Sharti		printf("%s: mapping error %d\n", __func__, error);
177116491Sharti		return;
178116491Sharti	}
179116491Sharti	KASSERT(nsegs == 1,
180116491Sharti	    ("too many segments for DMA: %d", nsegs));
181116491Sharti	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
182116491Sharti	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
183116491Sharti
184116491Sharti	*(uint32_t *)arg = segs[0].ds_addr;
185116491Sharti}
186116491Sharti
187116491Sharti/*
188116491Sharti * Allocate a page of external mbuf storage for the small pools.
189116491Sharti * Create a DMA map and load it. Put all the chunks onto the right
190116491Sharti * free list.
191116491Sharti */
192116491Shartistatic void
193116491Shartihatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
194116491Sharti{
195116491Sharti	struct mbuf_page *pg;
196116491Sharti	int err;
197116491Sharti	u_int i;
198116491Sharti
199121686Sharti	if (sc->mbuf_npages == sc->mbuf_max_pages)
200116491Sharti		return;
201116491Sharti	if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
202116491Sharti		return;
203116491Sharti
204116491Sharti	err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
205116491Sharti	if (err != 0) {
206147256Sbrooks		if_printf(sc->ifp, "%s -- bus_dmamap_create: %d\n",
207116491Sharti		    __func__, err);
208116491Sharti		free(pg, M_DEVBUF);
209116491Sharti		return;
210116491Sharti	}
211116491Sharti	err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
212117382Sharti	    hatm_extbuf_helper, &pg->hdr.phys, BUS_DMA_NOWAIT);
213116491Sharti	if (err != 0) {
214147256Sbrooks		if_printf(sc->ifp, "%s -- mbuf mapping failed %d\n",
215116491Sharti		    __func__, err);
216116491Sharti		bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
217116491Sharti		free(pg, M_DEVBUF);
218116491Sharti		return;
219116491Sharti	}
220116491Sharti
221116491Sharti	sc->mbuf_pages[sc->mbuf_npages] = pg;
222116491Sharti
223116491Sharti	if (group == 0) {
224116491Sharti		struct mbuf0_chunk *c;
225116491Sharti
226121729Sharti		pg->hdr.pool = 0;
227116491Sharti		pg->hdr.nchunks = MBUF0_PER_PAGE;
228116491Sharti		pg->hdr.chunksize = MBUF0_CHUNK;
229116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
230116491Sharti		c = (struct mbuf0_chunk *)pg;
231116491Sharti		for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
232116491Sharti			c->hdr.pageno = sc->mbuf_npages;
233116491Sharti			c->hdr.chunkno = i;
234122111Sharti			c->hdr.flags = 0;
235121675Sharti			hatm_ext_free(&sc->mbuf_list[0],
236121675Sharti			    (struct mbufx_free *)c);
237116491Sharti		}
238116491Sharti	} else {
239116491Sharti		struct mbuf1_chunk *c;
240116491Sharti
241121729Sharti		pg->hdr.pool = 1;
242116491Sharti		pg->hdr.nchunks = MBUF1_PER_PAGE;
243116491Sharti		pg->hdr.chunksize = MBUF1_CHUNK;
244116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
245116491Sharti		c = (struct mbuf1_chunk *)pg;
246116491Sharti		for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
247116491Sharti			c->hdr.pageno = sc->mbuf_npages;
248116491Sharti			c->hdr.chunkno = i;
249122111Sharti			c->hdr.flags = 0;
250121675Sharti			hatm_ext_free(&sc->mbuf_list[1],
251121675Sharti			    (struct mbufx_free *)c);
252116491Sharti		}
253116491Sharti	}
254116491Sharti	sc->mbuf_npages++;
255116491Sharti}
256116491Sharti
257116491Sharti/*
258116491Sharti * Free an mbuf and put it onto the free list.
259116491Sharti */
260116491Shartistatic void
261116491Shartihatm_mbuf0_free(void *buf, void *args)
262116491Sharti{
263116491Sharti	struct hatm_softc *sc = args;
264116491Sharti	struct mbuf0_chunk *c = buf;
265116491Sharti
266121729Sharti	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
267121729Sharti	    ("freeing unused mbuf %x", c->hdr.flags));
268121729Sharti	c->hdr.flags &= ~MBUF_USED;
269121675Sharti	hatm_ext_free(&sc->mbuf_list[0], (struct mbufx_free *)c);
270116491Sharti}
271116491Shartistatic void
272116491Shartihatm_mbuf1_free(void *buf, void *args)
273116491Sharti{
274116491Sharti	struct hatm_softc *sc = args;
275116491Sharti	struct mbuf1_chunk *c = buf;
276116491Sharti
277121729Sharti	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
278121729Sharti	    ("freeing unused mbuf %x", c->hdr.flags));
279121729Sharti	c->hdr.flags &= ~MBUF_USED;
280121675Sharti	hatm_ext_free(&sc->mbuf_list[1], (struct mbufx_free *)c);
281116491Sharti}
282116491Sharti
283116491Shartistatic void
284116491Shartihatm_mbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
285116491Sharti{
286116491Sharti	uint32_t *ptr = (uint32_t *)arg;
287116491Sharti
288116491Sharti	if (nsegs == 0) {
289116491Sharti		printf("%s: error=%d\n", __func__, error);
290116491Sharti		return;
291116491Sharti	}
292116491Sharti	KASSERT(nsegs == 1, ("too many segments for mbuf: %d", nsegs));
293116491Sharti	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
294116491Sharti	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
295116491Sharti
296116491Sharti	*ptr = segs[0].ds_addr;
297116491Sharti}
298116491Sharti
299116491Sharti/*
300116491Sharti * Receive buffer pool interrupt. This means the number of entries in the
301116491Sharti * queue has dropped below the threshold. Try to supply new buffers.
302116491Sharti */
303116491Shartistatic void
304116491Shartihe_intr_rbp(struct hatm_softc *sc, struct herbp *rbp, u_int large,
305116491Sharti    u_int group)
306116491Sharti{
307121617Sharti	u_int ntail;
308116491Sharti	struct mbuf *m;
309116491Sharti	int error;
310121680Sharti	struct mbufx_free *cf;
311121680Sharti	struct mbuf_page *pg;
312121680Sharti	struct mbuf0_chunk *buf0;
313121680Sharti	struct mbuf1_chunk *buf1;
314116491Sharti
315116491Sharti	DBG(sc, INTR, ("%s buffer supply threshold crossed for group %u",
316116491Sharti	   large ? "large" : "small", group));
317116491Sharti
318116491Sharti	rbp->head = (READ4(sc, HE_REGO_RBP_S(large, group)) >> HE_REGS_RBP_HEAD)
319116491Sharti	    & (rbp->size - 1);
320116491Sharti
321116491Sharti	for (;;) {
322116491Sharti		if ((ntail = rbp->tail + 1) == rbp->size)
323116491Sharti			ntail = 0;
324116491Sharti		if (ntail == rbp->head)
325116491Sharti			break;
326121680Sharti		m = NULL;
327116491Sharti
328116491Sharti		if (large) {
329121676Sharti			/* allocate the MBUF */
330116491Sharti			if ((m = m_getcl(M_DONTWAIT, MT_DATA,
331116491Sharti			    M_PKTHDR)) == NULL) {
332147256Sbrooks				if_printf(sc->ifp,
333116491Sharti				    "no mbuf clusters\n");
334116491Sharti				break;
335116491Sharti			}
336116491Sharti			m->m_data += MBUFL_OFFSET;
337116491Sharti
338116491Sharti			if (sc->lbufs[sc->lbufs_next] != NULL)
339116491Sharti				panic("hatm: lbufs full %u", sc->lbufs_next);
340116491Sharti			sc->lbufs[sc->lbufs_next] = m;
341116491Sharti
342116491Sharti			if ((error = bus_dmamap_load(sc->mbuf_tag,
343116491Sharti			    sc->rmaps[sc->lbufs_next],
344116491Sharti			    m->m_data, rbp->bsize, hatm_mbuf_helper,
345123810Salfred			    &rbp->rbp[rbp->tail].phys, BUS_DMA_NOWAIT)) != 0)
346116491Sharti				panic("hatm: mbuf mapping failed %d", error);
347116491Sharti
348116491Sharti			bus_dmamap_sync(sc->mbuf_tag,
349116491Sharti			    sc->rmaps[sc->lbufs_next],
350116491Sharti			    BUS_DMASYNC_PREREAD);
351116491Sharti
352121680Sharti			rbp->rbp[rbp->tail].handle =
353121680Sharti			    MBUF_MAKE_LHANDLE(sc->lbufs_next);
354116491Sharti
355116491Sharti			if (++sc->lbufs_next == sc->lbufs_size)
356116491Sharti				sc->lbufs_next = 0;
357116491Sharti
358121680Sharti		} else if (group == 0) {
359121680Sharti			/*
360121680Sharti			 * Allocate small buffer in group 0
361121680Sharti			 */
362121680Sharti			if ((cf = hatm_ext_alloc(sc, 0)) == NULL)
363116491Sharti				break;
364121680Sharti			buf0 = (struct mbuf0_chunk *)cf;
365121680Sharti			pg = sc->mbuf_pages[buf0->hdr.pageno];
366121729Sharti			buf0->hdr.flags |= MBUF_CARD;
367121680Sharti			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
368121680Sharti			    buf0->hdr.chunkno * MBUF0_CHUNK + MBUF0_OFFSET;
369121680Sharti			rbp->rbp[rbp->tail].handle =
370121680Sharti			    MBUF_MAKE_HANDLE(buf0->hdr.pageno,
371121680Sharti			    buf0->hdr.chunkno);
372121680Sharti
373121680Sharti			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
374121680Sharti			    BUS_DMASYNC_PREREAD);
375121680Sharti
376121680Sharti		} else if (group == 1) {
377121680Sharti			/*
378121680Sharti			 * Allocate small buffer in group 1
379121680Sharti			 */
380121680Sharti			if ((cf = hatm_ext_alloc(sc, 1)) == NULL)
381121680Sharti				break;
382121680Sharti			buf1 = (struct mbuf1_chunk *)cf;
383121680Sharti			pg = sc->mbuf_pages[buf1->hdr.pageno];
384121729Sharti			buf1->hdr.flags |= MBUF_CARD;
385121680Sharti			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
386121680Sharti			    buf1->hdr.chunkno * MBUF1_CHUNK + MBUF1_OFFSET;
387121680Sharti			rbp->rbp[rbp->tail].handle =
388121680Sharti			    MBUF_MAKE_HANDLE(buf1->hdr.pageno,
389121680Sharti			    buf1->hdr.chunkno);
390121680Sharti
391121680Sharti			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
392121680Sharti			    BUS_DMASYNC_PREREAD);
393121680Sharti
394121680Sharti		} else
395121680Sharti			/* ups */
396121680Sharti			break;
397121680Sharti
398116491Sharti		DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
399116491Sharti		    rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
400116491Sharti
401116491Sharti		rbp->tail = ntail;
402116491Sharti	}
403121617Sharti	WRITE4(sc, HE_REGO_RBP_T(large, group),
404121617Sharti	    (rbp->tail << HE_REGS_RBP_TAIL));
405116491Sharti}
406116491Sharti
407116491Sharti/*
408116491Sharti * Extract the buffer and hand it to the receive routine
409116491Sharti */
410116491Shartistatic struct mbuf *
411116491Shartihatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
412116491Sharti{
413116491Sharti	u_int pageno;
414116491Sharti	u_int chunkno;
415116491Sharti	struct mbuf *m;
416116491Sharti
417116491Sharti	if (handle & MBUF_LARGE_FLAG) {
418116491Sharti		/* large buffer - sync and unload */
419121680Sharti		MBUF_PARSE_LHANDLE(handle, handle);
420116491Sharti		DBG(sc, RX, ("RX large handle=%x", handle));
421116491Sharti
422116491Sharti		bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
423116491Sharti		    BUS_DMASYNC_POSTREAD);
424116491Sharti		bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
425116491Sharti
426116491Sharti		m = sc->lbufs[handle];
427116491Sharti		sc->lbufs[handle] = NULL;
428116491Sharti
429116491Sharti		return (m);
430116491Sharti	}
431116491Sharti
432116491Sharti	MBUF_PARSE_HANDLE(handle, pageno, chunkno);
433116491Sharti
434116491Sharti	DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
435116491Sharti	    pageno, chunkno));
436116491Sharti
437121676Sharti	MGETHDR(m, M_DONTWAIT, MT_DATA);
438121676Sharti
439116491Sharti	if (group == 0) {
440116491Sharti		struct mbuf0_chunk *c0;
441116491Sharti
442116491Sharti		c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
443116491Sharti		KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
444116491Sharti		    c0->hdr.pageno, pageno));
445116491Sharti		KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
446116491Sharti		    c0->hdr.chunkno, chunkno));
447121729Sharti		KASSERT(c0->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
448121729Sharti		    pageno, chunkno));
449121729Sharti		KASSERT(!(c0->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
450121729Sharti		    pageno, chunkno));
451116491Sharti
452121729Sharti		c0->hdr.flags |= MBUF_USED;
453121729Sharti		c0->hdr.flags &= ~MBUF_CARD;
454121729Sharti
455121676Sharti		if (m != NULL) {
456121677Sharti			m->m_ext.ref_cnt = &c0->hdr.ref_cnt;
457121676Sharti			m_extadd(m, (void *)c0, MBUF0_SIZE,
458121677Sharti			    hatm_mbuf0_free, sc, M_PKTHDR, EXT_EXTREF);
459121676Sharti			m->m_data += MBUF0_OFFSET;
460121676Sharti		} else
461121676Sharti			hatm_mbuf0_free(c0, sc);
462116491Sharti
463116491Sharti	} else {
464116491Sharti		struct mbuf1_chunk *c1;
465116491Sharti
466116491Sharti		c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
467116491Sharti		KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
468116491Sharti		    c1->hdr.pageno, pageno));
469116491Sharti		KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
470116491Sharti		    c1->hdr.chunkno, chunkno));
471121729Sharti		KASSERT(c1->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
472121729Sharti		    pageno, chunkno));
473121729Sharti		KASSERT(!(c1->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
474121729Sharti		    pageno, chunkno));
475116491Sharti
476121729Sharti		c1->hdr.flags |= MBUF_USED;
477121729Sharti		c1->hdr.flags &= ~MBUF_CARD;
478121729Sharti
479121676Sharti		if (m != NULL) {
480121677Sharti			m->m_ext.ref_cnt = &c1->hdr.ref_cnt;
481121676Sharti			m_extadd(m, (void *)c1, MBUF1_SIZE,
482121677Sharti			    hatm_mbuf1_free, sc, M_PKTHDR, EXT_EXTREF);
483121676Sharti			m->m_data += MBUF1_OFFSET;
484121676Sharti		} else
485121676Sharti			hatm_mbuf1_free(c1, sc);
486116491Sharti	}
487116491Sharti
488116491Sharti	return (m);
489116491Sharti}
490116491Sharti
491116491Sharti/*
492116491Sharti * Interrupt because of receive buffer returned.
493116491Sharti */
494116491Shartistatic void
495116491Shartihe_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
496116491Sharti{
497116491Sharti	struct he_rbrqen *e;
498116491Sharti	uint32_t flags, tail;
499116491Sharti	u_int cid, len;
500116491Sharti	struct mbuf *m;
501116491Sharti
502116491Sharti	for (;;) {
503116491Sharti		tail = sc->hsp->group[group].rbrq_tail >> 3;
504116491Sharti
505116491Sharti		if (rq->head == tail)
506116491Sharti			break;
507116491Sharti
508116491Sharti		e = &rq->rbrq[rq->head];
509116491Sharti
510116491Sharti		flags = e->addr & HE_REGM_RBRQ_FLAGS;
511116491Sharti		if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
512121680Sharti			m = hatm_rx_buffer(sc, group, e->addr);
513116491Sharti		else
514116491Sharti			m = NULL;
515116491Sharti
516116491Sharti		cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
517116491Sharti		len = 4 * (e->len & HE_REGM_RBRQ_LEN);
518116491Sharti
519116491Sharti		hatm_rx(sc, cid, flags, m, len);
520116491Sharti
521116491Sharti		if (++rq->head == rq->size)
522116491Sharti			rq->head = 0;
523116491Sharti	}
524116491Sharti	WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
525116491Sharti}
526116491Sharti
527116491Shartivoid
528116491Shartihatm_intr(void *p)
529116491Sharti{
530116491Sharti	struct heirq *q = p;
531116491Sharti	struct hatm_softc *sc = q->sc;
532116491Sharti	u_int status;
533116491Sharti	u_int tail;
534116491Sharti
535116491Sharti	/* if we have a stray interrupt with a non-initialized card,
536116491Sharti	 * we cannot even lock before looking at the flag */
537147256Sbrooks	if (!(sc->ifp->if_flags & IFF_RUNNING))
538116491Sharti		return;
539116491Sharti
540116491Sharti	mtx_lock(&sc->mtx);
541116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
542116491Sharti
543116491Sharti	tail = *q->tailp;
544116491Sharti	if (q->head == tail) {
545116491Sharti		/* workaround for tail pointer not updated bug (8.1.1) */
546116491Sharti		DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
547116491Sharti
548116491Sharti		/* read the tail pointer from the card */
549116491Sharti		tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
550116491Sharti		    HE_REGM_IRQ_BASE_TAIL;
551116491Sharti		BARRIER_R(sc);
552116491Sharti
553116491Sharti		sc->istats.bug_no_irq_upd++;
554116491Sharti	}
555116491Sharti
556116491Sharti	/* clear the interrupt */
557116491Sharti	WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
558116491Sharti	BARRIER_W(sc);
559116491Sharti
560116491Sharti	while (q->head != tail) {
561116491Sharti		status = q->irq[q->head];
562116491Sharti		q->irq[q->head] = HE_REGM_ITYPE_INVALID;
563116491Sharti		if (++q->head == (q->size - 1))
564116491Sharti			q->head = 0;
565116491Sharti
566116491Sharti		switch (status & HE_REGM_ITYPE) {
567116491Sharti
568116491Sharti		  case HE_REGM_ITYPE_TBRQ:
569116491Sharti			DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
570116491Sharti			sc->istats.itype_tbrq++;
571116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
572116491Sharti			break;
573116491Sharti
574116491Sharti		  case HE_REGM_ITYPE_TPD:
575116491Sharti			DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
576116491Sharti			sc->istats.itype_tpd++;
577116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
578116491Sharti			break;
579116491Sharti
580116491Sharti		  case HE_REGM_ITYPE_RBPS:
581116491Sharti			sc->istats.itype_rbps++;
582116491Sharti			switch (status & HE_REGM_IGROUP) {
583116491Sharti
584116491Sharti			  case 0:
585116491Sharti				he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
586116491Sharti				break;
587116491Sharti
588116491Sharti			  case 1:
589116491Sharti				he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
590116491Sharti				break;
591116491Sharti
592116491Sharti			  default:
593147256Sbrooks				if_printf(sc->ifp, "bad INTR RBPS%u\n",
594116491Sharti				    status & HE_REGM_IGROUP);
595116491Sharti				break;
596116491Sharti			}
597116491Sharti			break;
598116491Sharti
599116491Sharti		  case HE_REGM_ITYPE_RBPL:
600116491Sharti			sc->istats.itype_rbpl++;
601116491Sharti			switch (status & HE_REGM_IGROUP) {
602116491Sharti
603116491Sharti			  case 0:
604116491Sharti				he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
605116491Sharti				break;
606116491Sharti
607116491Sharti			  default:
608147256Sbrooks				if_printf(sc->ifp, "bad INTR RBPL%u\n",
609116491Sharti				    status & HE_REGM_IGROUP);
610116491Sharti				break;
611116491Sharti			}
612116491Sharti			break;
613116491Sharti
614116491Sharti		  case HE_REGM_ITYPE_RBRQ:
615116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
616116491Sharti			sc->istats.itype_rbrq++;
617116491Sharti			switch (status & HE_REGM_IGROUP) {
618116491Sharti
619116491Sharti			  case 0:
620116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
621116491Sharti				break;
622116491Sharti
623116491Sharti			  case 1:
624116491Sharti				if (sc->rbrq_1.size > 0) {
625116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
626116491Sharti					break;
627116491Sharti				}
628116491Sharti				/* FALLTHRU */
629116491Sharti
630116491Sharti			  default:
631147256Sbrooks				if_printf(sc->ifp, "bad INTR RBRQ%u\n",
632116491Sharti				    status & HE_REGM_IGROUP);
633116491Sharti				break;
634116491Sharti			}
635116491Sharti			break;
636116491Sharti
637116491Sharti		  case HE_REGM_ITYPE_RBRQT:
638116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
639116491Sharti			sc->istats.itype_rbrqt++;
640116491Sharti			switch (status & HE_REGM_IGROUP) {
641116491Sharti
642116491Sharti			  case 0:
643116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
644116491Sharti				break;
645116491Sharti
646116491Sharti			  case 1:
647116491Sharti				if (sc->rbrq_1.size > 0) {
648116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
649116491Sharti					break;
650116491Sharti				}
651116491Sharti				/* FALLTHRU */
652116491Sharti
653116491Sharti			  default:
654147256Sbrooks				if_printf(sc->ifp, "bad INTR RBRQT%u\n",
655116491Sharti				    status & HE_REGM_IGROUP);
656116491Sharti				break;
657116491Sharti			}
658116491Sharti			break;
659116491Sharti
660116491Sharti		  case HE_REGM_ITYPE_PHYS:
661116491Sharti			sc->istats.itype_phys++;
662116491Sharti			utopia_intr(&sc->utopia);
663116491Sharti			break;
664116491Sharti
665116491Sharti#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
666116491Sharti		  case HE_REGM_ITYPE_UNKNOWN:
667116491Sharti			sc->istats.itype_unknown++;
668147256Sbrooks			if_printf(sc->ifp, "bad interrupt\n");
669116491Sharti			break;
670116491Sharti#endif
671116491Sharti
672116491Sharti		  case HE_REGM_ITYPE_ERR:
673116491Sharti			sc->istats.itype_err++;
674116491Sharti			switch (status) {
675116491Sharti
676116491Sharti			  case HE_REGM_ITYPE_PERR:
677147256Sbrooks				if_printf(sc->ifp, "parity error\n");
678116491Sharti				break;
679116491Sharti
680116491Sharti			  case HE_REGM_ITYPE_ABORT:
681147256Sbrooks				if_printf(sc->ifp, "abort interrupt "
682116491Sharti				    "addr=0x%08x\n",
683116491Sharti				    READ4(sc, HE_REGO_ABORT_ADDR));
684116491Sharti				break;
685116491Sharti
686116491Sharti			  default:
687147256Sbrooks				if_printf(sc->ifp,
688116491Sharti				    "bad interrupt type %08x\n", status);
689116491Sharti				break;
690116491Sharti			}
691116491Sharti			break;
692116491Sharti
693116491Sharti		  case HE_REGM_ITYPE_INVALID:
694116491Sharti			/* this is the documented fix for the ISW bug 8.1.1
695116491Sharti			 * Note, that the documented fix is partly wrong:
696116491Sharti			 * the ISWs should be intialized to 0xf8 not 0xff */
697116491Sharti			sc->istats.bug_bad_isw++;
698116491Sharti			DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
699116491Sharti			he_intr_tbrq(sc, &sc->tbrq, 0);
700116491Sharti			he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
701116491Sharti			he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
702116491Sharti			he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
703116491Sharti			he_intr_rbrq(sc, &sc->rbrq_0, 0);
704116491Sharti			he_intr_rbrq(sc, &sc->rbrq_1, 1);
705116491Sharti			utopia_intr(&sc->utopia);
706116491Sharti			break;
707116491Sharti
708116491Sharti		  default:
709147256Sbrooks			if_printf(sc->ifp, "bad interrupt type %08x\n",
710116491Sharti			    status);
711116491Sharti			break;
712116491Sharti		}
713116491Sharti	}
714116491Sharti
715116491Sharti	/* write back head to clear queue */
716116491Sharti	WRITE4(sc, HE_REGO_IRQ_HEAD(0),
717116491Sharti	    ((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
718116491Sharti	    (q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
719116491Sharti	    (q->head << HE_REGS_IRQ_HEAD_HEAD));
720116491Sharti	BARRIER_W(sc);
721116491Sharti
722116491Sharti	/* workaround the back-to-back irq access problem (8.1.2) */
723116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
724116491Sharti	BARRIER_R(sc);
725116491Sharti
726116491Sharti	mtx_unlock(&sc->mtx);
727116491Sharti}
728