if_hatm_intr.c revision 122111
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 * Author: Hartmut Brandt <harti@freebsd.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_intr.c 122111 2003-11-05 11:15:47Z harti $");
31
32/*
33 * ForeHE driver.
34 *
35 * Interrupt handler.
36 */
37
38#include "opt_inet.h"
39#include "opt_natm.h"
40
41#include <sys/types.h>
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/malloc.h>
45#include <sys/kernel.h>
46#include <sys/bus.h>
47#include <sys/errno.h>
48#include <sys/conf.h>
49#include <sys/module.h>
50#include <sys/queue.h>
51#include <sys/syslog.h>
52#include <sys/condvar.h>
53#include <sys/sysctl.h>
54#include <vm/uma.h>
55
56#include <sys/sockio.h>
57#include <sys/mbuf.h>
58#include <sys/socket.h>
59
60#include <net/if.h>
61#include <net/if_media.h>
62#include <net/if_atm.h>
63#include <net/route.h>
64#include <netinet/in.h>
65#include <netinet/if_atm.h>
66
67#include <machine/bus.h>
68#include <machine/resource.h>
69#include <sys/bus.h>
70#include <sys/rman.h>
71#include <dev/pci/pcireg.h>
72#include <dev/pci/pcivar.h>
73
74#include <dev/utopia/utopia.h>
75#include <dev/hatm/if_hatmconf.h>
76#include <dev/hatm/if_hatmreg.h>
77#include <dev/hatm/if_hatmvar.h>
78
79CTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
80CTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
81CTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
82CTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
83CTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
84CTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
85
86CTASSERT(MBUF0_PER_PAGE <= 256);
87CTASSERT(MBUF1_PER_PAGE <= 256);
88
89static void hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group);
90
91/*
92 * Free an external mbuf to a list. We use atomic functions so that
93 * we don't need a mutex for the list.
94 *
95 * Note that in general this algorithm is not safe when multiple readers
96 * and writers are present. To cite from a mail from David Schultz
97 * <das@freebsd.org>:
98 *
99 *	It looks like this is subject to the ABA problem.  For instance,
100 *	suppose X, Y, and Z are the top things on the freelist and a
101 *	thread attempts to make an allocation.  You set buf to X and load
102 *	buf->link (Y) into a register.  Then the thread get preempted, and
103 *	another thread allocates both X and Y, then frees X.  When the
104 *	original thread gets the CPU again, X is still on top of the
105 *	freelist, so the atomic operation succeeds.  However, the atomic
106 *	op places Y on top of the freelist, even though Y is no longer
107 *	free.
108 *
109 * We are, however sure that we have only one thread that ever allocates
110 * buffers because the only place we're call from is the interrupt handler.
111 * Under these circumstances the code looks safe.
112 */
113__inline void
114hatm_ext_free(struct mbufx_free **list, struct mbufx_free *buf)
115{
116	for (;;) {
117		buf->link = *list;
118		if (atomic_cmpset_ptr(list, buf->link, buf))
119			break;
120	}
121}
122
123static __inline struct mbufx_free *
124hatm_ext_alloc(struct hatm_softc *sc, u_int g)
125{
126	struct mbufx_free *buf;
127
128	for (;;) {
129		if ((buf = sc->mbuf_list[g]) == NULL)
130			break;
131		if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
132			break;
133	}
134	if (buf == NULL) {
135		hatm_mbuf_page_alloc(sc, g);
136		for (;;) {
137			if ((buf = sc->mbuf_list[g]) == NULL)
138				break;
139			if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
140				break;
141		}
142	}
143	return (buf);
144}
145
146/*
147 * Either the queue treshold was crossed or a TPD with the INTR bit set
148 * was transmitted.
149 */
150static void
151he_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
152{
153	uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
154	u_int no;
155
156	while (q->head != (*tailp >> 2)) {
157		no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
158		    HE_REGS_TPD_ADDR;
159		hatm_tx_complete(sc, TPD_ADDR(sc, no),
160		    (q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
161
162		if (++q->head == q->size)
163			q->head = 0;
164	}
165	WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
166}
167
168/*
169 * DMA loader function for external mbuf page.
170 */
171static void
172hatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
173    int error)
174{
175	if (error) {
176		printf("%s: mapping error %d\n", __func__, error);
177		return;
178	}
179	KASSERT(nsegs == 1,
180	    ("too many segments for DMA: %d", nsegs));
181	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
182	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
183
184	*(uint32_t *)arg = segs[0].ds_addr;
185}
186
187/*
188 * Allocate a page of external mbuf storage for the small pools.
189 * Create a DMA map and load it. Put all the chunks onto the right
190 * free list.
191 */
192static void
193hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
194{
195	struct mbuf_page *pg;
196	int err;
197	u_int i;
198
199	if (sc->mbuf_npages == sc->mbuf_max_pages)
200		return;
201	if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
202		return;
203
204	err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
205	if (err != 0) {
206		if_printf(&sc->ifatm.ifnet, "%s -- bus_dmamap_create: %d\n",
207		    __func__, err);
208		free(pg, M_DEVBUF);
209		return;
210	}
211	err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
212	    hatm_extbuf_helper, &pg->hdr.phys, BUS_DMA_NOWAIT);
213	if (err != 0) {
214		if_printf(&sc->ifatm.ifnet, "%s -- mbuf mapping failed %d\n",
215		    __func__, err);
216		bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
217		free(pg, M_DEVBUF);
218		return;
219	}
220
221	sc->mbuf_pages[sc->mbuf_npages] = pg;
222
223	if (group == 0) {
224		struct mbuf0_chunk *c;
225
226		pg->hdr.pool = 0;
227		pg->hdr.nchunks = MBUF0_PER_PAGE;
228		pg->hdr.chunksize = MBUF0_CHUNK;
229		pg->hdr.hdroff = sizeof(c->storage);
230		c = (struct mbuf0_chunk *)pg;
231		for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
232			c->hdr.pageno = sc->mbuf_npages;
233			c->hdr.chunkno = i;
234			c->hdr.flags = 0;
235			hatm_ext_free(&sc->mbuf_list[0],
236			    (struct mbufx_free *)c);
237		}
238	} else {
239		struct mbuf1_chunk *c;
240
241		pg->hdr.pool = 1;
242		pg->hdr.nchunks = MBUF1_PER_PAGE;
243		pg->hdr.chunksize = MBUF1_CHUNK;
244		pg->hdr.hdroff = sizeof(c->storage);
245		c = (struct mbuf1_chunk *)pg;
246		for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
247			c->hdr.pageno = sc->mbuf_npages;
248			c->hdr.chunkno = i;
249			c->hdr.flags = 0;
250			hatm_ext_free(&sc->mbuf_list[1],
251			    (struct mbufx_free *)c);
252		}
253	}
254	sc->mbuf_npages++;
255}
256
257/*
258 * Free an mbuf and put it onto the free list.
259 */
260static void
261hatm_mbuf0_free(void *buf, void *args)
262{
263	struct hatm_softc *sc = args;
264	struct mbuf0_chunk *c = buf;
265
266	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
267	    ("freeing unused mbuf %x", c->hdr.flags));
268	c->hdr.flags &= ~MBUF_USED;
269	hatm_ext_free(&sc->mbuf_list[0], (struct mbufx_free *)c);
270}
271static void
272hatm_mbuf1_free(void *buf, void *args)
273{
274	struct hatm_softc *sc = args;
275	struct mbuf1_chunk *c = buf;
276
277	KASSERT((c->hdr.flags & (MBUF_USED | MBUF_CARD)) == MBUF_USED,
278	    ("freeing unused mbuf %x", c->hdr.flags));
279	c->hdr.flags &= ~MBUF_USED;
280	hatm_ext_free(&sc->mbuf_list[1], (struct mbufx_free *)c);
281}
282
283static void
284hatm_mbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
285{
286	uint32_t *ptr = (uint32_t *)arg;
287
288	if (nsegs == 0) {
289		printf("%s: error=%d\n", __func__, error);
290		return;
291	}
292	KASSERT(nsegs == 1, ("too many segments for mbuf: %d", nsegs));
293	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
294	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
295
296	*ptr = segs[0].ds_addr;
297}
298
299/*
300 * Receive buffer pool interrupt. This means the number of entries in the
301 * queue has dropped below the threshold. Try to supply new buffers.
302 */
303static void
304he_intr_rbp(struct hatm_softc *sc, struct herbp *rbp, u_int large,
305    u_int group)
306{
307	u_int ntail;
308	struct mbuf *m;
309	int error;
310	struct mbufx_free *cf;
311	struct mbuf_page *pg;
312	struct mbuf0_chunk *buf0;
313	struct mbuf1_chunk *buf1;
314
315	DBG(sc, INTR, ("%s buffer supply threshold crossed for group %u",
316	   large ? "large" : "small", group));
317
318	rbp->head = (READ4(sc, HE_REGO_RBP_S(large, group)) >> HE_REGS_RBP_HEAD)
319	    & (rbp->size - 1);
320
321	for (;;) {
322		if ((ntail = rbp->tail + 1) == rbp->size)
323			ntail = 0;
324		if (ntail == rbp->head)
325			break;
326		m = NULL;
327
328		if (large) {
329			/* allocate the MBUF */
330			if ((m = m_getcl(M_DONTWAIT, MT_DATA,
331			    M_PKTHDR)) == NULL) {
332				if_printf(&sc->ifatm.ifnet,
333				    "no mbuf clusters\n");
334				break;
335			}
336			m->m_data += MBUFL_OFFSET;
337
338			if (sc->lbufs[sc->lbufs_next] != NULL)
339				panic("hatm: lbufs full %u", sc->lbufs_next);
340			sc->lbufs[sc->lbufs_next] = m;
341
342			if ((error = bus_dmamap_load(sc->mbuf_tag,
343			    sc->rmaps[sc->lbufs_next],
344			    m->m_data, rbp->bsize, hatm_mbuf_helper,
345			    &rbp->rbp[rbp->tail].phys, BUS_DMA_NOWAIT)) != NULL)
346				panic("hatm: mbuf mapping failed %d", error);
347
348			bus_dmamap_sync(sc->mbuf_tag,
349			    sc->rmaps[sc->lbufs_next],
350			    BUS_DMASYNC_PREREAD);
351
352			rbp->rbp[rbp->tail].handle =
353			    MBUF_MAKE_LHANDLE(sc->lbufs_next);
354
355			if (++sc->lbufs_next == sc->lbufs_size)
356				sc->lbufs_next = 0;
357
358		} else if (group == 0) {
359			/*
360			 * Allocate small buffer in group 0
361			 */
362			if ((cf = hatm_ext_alloc(sc, 0)) == NULL)
363				break;
364			buf0 = (struct mbuf0_chunk *)cf;
365			pg = sc->mbuf_pages[buf0->hdr.pageno];
366			buf0->hdr.flags |= MBUF_CARD;
367			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
368			    buf0->hdr.chunkno * MBUF0_CHUNK + MBUF0_OFFSET;
369			rbp->rbp[rbp->tail].handle =
370			    MBUF_MAKE_HANDLE(buf0->hdr.pageno,
371			    buf0->hdr.chunkno);
372
373			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
374			    BUS_DMASYNC_PREREAD);
375
376		} else if (group == 1) {
377			/*
378			 * Allocate small buffer in group 1
379			 */
380			if ((cf = hatm_ext_alloc(sc, 1)) == NULL)
381				break;
382			buf1 = (struct mbuf1_chunk *)cf;
383			pg = sc->mbuf_pages[buf1->hdr.pageno];
384			buf1->hdr.flags |= MBUF_CARD;
385			rbp->rbp[rbp->tail].phys = pg->hdr.phys +
386			    buf1->hdr.chunkno * MBUF1_CHUNK + MBUF1_OFFSET;
387			rbp->rbp[rbp->tail].handle =
388			    MBUF_MAKE_HANDLE(buf1->hdr.pageno,
389			    buf1->hdr.chunkno);
390
391			bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map,
392			    BUS_DMASYNC_PREREAD);
393
394		} else
395			/* ups */
396			break;
397
398		DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
399		    rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
400
401		rbp->tail = ntail;
402	}
403	WRITE4(sc, HE_REGO_RBP_T(large, group),
404	    (rbp->tail << HE_REGS_RBP_TAIL));
405}
406
407/*
408 * Extract the buffer and hand it to the receive routine
409 */
410static struct mbuf *
411hatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
412{
413	u_int pageno;
414	u_int chunkno;
415	struct mbuf *m;
416
417	if (handle & MBUF_LARGE_FLAG) {
418		/* large buffer - sync and unload */
419		MBUF_PARSE_LHANDLE(handle, handle);
420		DBG(sc, RX, ("RX large handle=%x", handle));
421
422		bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
423		    BUS_DMASYNC_POSTREAD);
424		bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
425
426		m = sc->lbufs[handle];
427		sc->lbufs[handle] = NULL;
428
429		return (m);
430	}
431
432	MBUF_PARSE_HANDLE(handle, pageno, chunkno);
433
434	DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
435	    pageno, chunkno));
436
437	MGETHDR(m, M_DONTWAIT, MT_DATA);
438
439	if (group == 0) {
440		struct mbuf0_chunk *c0;
441
442		c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
443		KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
444		    c0->hdr.pageno, pageno));
445		KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
446		    c0->hdr.chunkno, chunkno));
447		KASSERT(c0->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
448		    pageno, chunkno));
449		KASSERT(!(c0->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
450		    pageno, chunkno));
451
452		c0->hdr.flags |= MBUF_USED;
453		c0->hdr.flags &= ~MBUF_CARD;
454
455		if (m != NULL) {
456			m->m_ext.ref_cnt = &c0->hdr.ref_cnt;
457			m_extadd(m, (void *)c0, MBUF0_SIZE,
458			    hatm_mbuf0_free, sc, M_PKTHDR, EXT_EXTREF);
459			m->m_data += MBUF0_OFFSET;
460		} else
461			hatm_mbuf0_free(c0, sc);
462
463	} else {
464		struct mbuf1_chunk *c1;
465
466		c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
467		KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
468		    c1->hdr.pageno, pageno));
469		KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
470		    c1->hdr.chunkno, chunkno));
471		KASSERT(c1->hdr.flags & MBUF_CARD, ("mbuf not on card %u/%u",
472		    pageno, chunkno));
473		KASSERT(!(c1->hdr.flags & MBUF_USED), ("used mbuf %u/%u",
474		    pageno, chunkno));
475
476		c1->hdr.flags |= MBUF_USED;
477		c1->hdr.flags &= ~MBUF_CARD;
478
479		if (m != NULL) {
480			m->m_ext.ref_cnt = &c1->hdr.ref_cnt;
481			m_extadd(m, (void *)c1, MBUF1_SIZE,
482			    hatm_mbuf1_free, sc, M_PKTHDR, EXT_EXTREF);
483			m->m_data += MBUF1_OFFSET;
484		} else
485			hatm_mbuf1_free(c1, sc);
486	}
487
488	return (m);
489}
490
491/*
492 * Interrupt because of receive buffer returned.
493 */
494static void
495he_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
496{
497	struct he_rbrqen *e;
498	uint32_t flags, tail;
499	u_int cid, len;
500	struct mbuf *m;
501
502	for (;;) {
503		tail = sc->hsp->group[group].rbrq_tail >> 3;
504
505		if (rq->head == tail)
506			break;
507
508		e = &rq->rbrq[rq->head];
509
510		flags = e->addr & HE_REGM_RBRQ_FLAGS;
511		if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
512			m = hatm_rx_buffer(sc, group, e->addr);
513		else
514			m = NULL;
515
516		cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
517		len = 4 * (e->len & HE_REGM_RBRQ_LEN);
518
519		hatm_rx(sc, cid, flags, m, len);
520
521		if (++rq->head == rq->size)
522			rq->head = 0;
523	}
524	WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
525}
526
527void
528hatm_intr(void *p)
529{
530	struct heirq *q = p;
531	struct hatm_softc *sc = q->sc;
532	u_int status;
533	u_int tail;
534
535	/* if we have a stray interrupt with a non-initialized card,
536	 * we cannot even lock before looking at the flag */
537	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING))
538		return;
539
540	mtx_lock(&sc->mtx);
541	(void)READ4(sc, HE_REGO_INT_FIFO);
542
543	tail = *q->tailp;
544	if (q->head == tail) {
545		/* workaround for tail pointer not updated bug (8.1.1) */
546		DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
547
548		/* read the tail pointer from the card */
549		tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
550		    HE_REGM_IRQ_BASE_TAIL;
551		BARRIER_R(sc);
552
553		sc->istats.bug_no_irq_upd++;
554	}
555
556	/* clear the interrupt */
557	WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
558	BARRIER_W(sc);
559
560	while (q->head != tail) {
561		status = q->irq[q->head];
562		q->irq[q->head] = HE_REGM_ITYPE_INVALID;
563		if (++q->head == (q->size - 1))
564			q->head = 0;
565
566		switch (status & HE_REGM_ITYPE) {
567
568		  case HE_REGM_ITYPE_TBRQ:
569			DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
570			sc->istats.itype_tbrq++;
571			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
572			break;
573
574		  case HE_REGM_ITYPE_TPD:
575			DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
576			sc->istats.itype_tpd++;
577			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
578			break;
579
580		  case HE_REGM_ITYPE_RBPS:
581			sc->istats.itype_rbps++;
582			switch (status & HE_REGM_IGROUP) {
583
584			  case 0:
585				he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
586				break;
587
588			  case 1:
589				he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
590				break;
591
592			  default:
593				if_printf(&sc->ifatm.ifnet, "bad INTR RBPS%u\n",
594				    status & HE_REGM_IGROUP);
595				break;
596			}
597			break;
598
599		  case HE_REGM_ITYPE_RBPL:
600			sc->istats.itype_rbpl++;
601			switch (status & HE_REGM_IGROUP) {
602
603			  case 0:
604				he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
605				break;
606
607			  default:
608				if_printf(&sc->ifatm.ifnet, "bad INTR RBPL%u\n",
609				    status & HE_REGM_IGROUP);
610				break;
611			}
612			break;
613
614		  case HE_REGM_ITYPE_RBRQ:
615			DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
616			sc->istats.itype_rbrq++;
617			switch (status & HE_REGM_IGROUP) {
618
619			  case 0:
620				he_intr_rbrq(sc, &sc->rbrq_0, 0);
621				break;
622
623			  case 1:
624				if (sc->rbrq_1.size > 0) {
625					he_intr_rbrq(sc, &sc->rbrq_1, 1);
626					break;
627				}
628				/* FALLTHRU */
629
630			  default:
631				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQ%u\n",
632				    status & HE_REGM_IGROUP);
633				break;
634			}
635			break;
636
637		  case HE_REGM_ITYPE_RBRQT:
638			DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
639			sc->istats.itype_rbrqt++;
640			switch (status & HE_REGM_IGROUP) {
641
642			  case 0:
643				he_intr_rbrq(sc, &sc->rbrq_0, 0);
644				break;
645
646			  case 1:
647				if (sc->rbrq_1.size > 0) {
648					he_intr_rbrq(sc, &sc->rbrq_1, 1);
649					break;
650				}
651				/* FALLTHRU */
652
653			  default:
654				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQT%u\n",
655				    status & HE_REGM_IGROUP);
656				break;
657			}
658			break;
659
660		  case HE_REGM_ITYPE_PHYS:
661			sc->istats.itype_phys++;
662			utopia_intr(&sc->utopia);
663			break;
664
665#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
666		  case HE_REGM_ITYPE_UNKNOWN:
667			sc->istats.itype_unknown++;
668			if_printf(&sc->ifatm.ifnet, "bad interrupt\n");
669			break;
670#endif
671
672		  case HE_REGM_ITYPE_ERR:
673			sc->istats.itype_err++;
674			switch (status) {
675
676			  case HE_REGM_ITYPE_PERR:
677				if_printf(&sc->ifatm.ifnet, "parity error\n");
678				break;
679
680			  case HE_REGM_ITYPE_ABORT:
681				if_printf(&sc->ifatm.ifnet, "abort interrupt "
682				    "addr=0x%08x\n",
683				    READ4(sc, HE_REGO_ABORT_ADDR));
684				break;
685
686			  default:
687				if_printf(&sc->ifatm.ifnet,
688				    "bad interrupt type %08x\n", status);
689				break;
690			}
691			break;
692
693		  case HE_REGM_ITYPE_INVALID:
694			/* this is the documented fix for the ISW bug 8.1.1
695			 * Note, that the documented fix is partly wrong:
696			 * the ISWs should be intialized to 0xf8 not 0xff */
697			sc->istats.bug_bad_isw++;
698			DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
699			he_intr_tbrq(sc, &sc->tbrq, 0);
700			he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
701			he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
702			he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
703			he_intr_rbrq(sc, &sc->rbrq_0, 0);
704			he_intr_rbrq(sc, &sc->rbrq_1, 1);
705			utopia_intr(&sc->utopia);
706			break;
707
708		  default:
709			if_printf(&sc->ifatm.ifnet, "bad interrupt type %08x\n",
710			    status);
711			break;
712		}
713	}
714
715	/* write back head to clear queue */
716	WRITE4(sc, HE_REGO_IRQ_HEAD(0),
717	    ((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
718	    (q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
719	    (q->head << HE_REGS_IRQ_HEAD_HEAD));
720	BARRIER_W(sc);
721
722	/* workaround the back-to-back irq access problem (8.1.2) */
723	(void)READ4(sc, HE_REGO_INT_FIFO);
724	BARRIER_R(sc);
725
726	mtx_unlock(&sc->mtx);
727}
728