if_hatm_intr.c revision 121677
1300313Ssjg/*
2236769Sobrien * Copyright (c) 2001-2003
3236769Sobrien *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4236769Sobrien * 	All rights reserved.
5236769Sobrien * Author: Hartmut Brandt <harti@freebsd.org>
6236769Sobrien *
7236769Sobrien * Redistribution and use in source and binary forms, with or without
8236769Sobrien * modification, are permitted provided that the following conditions
9236769Sobrien * are met:
10236769Sobrien * 1. Redistributions of source code must retain the above copyright
11236769Sobrien *    notice, this list of conditions and the following disclaimer.
12236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13236769Sobrien *    notice, this list of conditions and the following disclaimer in the
14236769Sobrien *    documentation and/or other materials provided with the distribution.
15236769Sobrien *
16236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19236769Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26236769Sobrien * SUCH DAMAGE.
27236769Sobrien */
28236769Sobrien
29236769Sobrien#include <sys/cdefs.h>
30236769Sobrien__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_intr.c 121677 2003-10-29 13:21:38Z harti $");
31236769Sobrien
32236769Sobrien/*
33236769Sobrien * ForeHE driver.
34236769Sobrien *
35236769Sobrien * Interrupt handler.
36236769Sobrien */
37236769Sobrien
38236769Sobrien#include "opt_inet.h"
39236769Sobrien#include "opt_natm.h"
40236769Sobrien
41236769Sobrien#include <sys/types.h>
42236769Sobrien#include <sys/param.h>
43236769Sobrien#include <sys/systm.h>
44236769Sobrien#include <sys/malloc.h>
45236769Sobrien#include <sys/kernel.h>
46236769Sobrien#include <sys/bus.h>
47236769Sobrien#include <sys/errno.h>
48236769Sobrien#include <sys/conf.h>
49236769Sobrien#include <sys/module.h>
50236769Sobrien#include <sys/queue.h>
51236769Sobrien#include <sys/syslog.h>
52236769Sobrien#include <sys/condvar.h>
53236769Sobrien#include <sys/sysctl.h>
54236769Sobrien#include <vm/uma.h>
55236769Sobrien
56236769Sobrien#include <sys/sockio.h>
57236769Sobrien#include <sys/mbuf.h>
58236769Sobrien#include <sys/socket.h>
59236769Sobrien
60236769Sobrien#include <net/if.h>
61236769Sobrien#include <net/if_media.h>
62236769Sobrien#include <net/if_atm.h>
63236769Sobrien#include <net/route.h>
64236769Sobrien#include <netinet/in.h>
65236769Sobrien#include <netinet/if_atm.h>
66236769Sobrien
67236769Sobrien#include <machine/bus.h>
68236769Sobrien#include <machine/resource.h>
69236769Sobrien#include <sys/bus.h>
70236769Sobrien#include <sys/rman.h>
71236769Sobrien#include <dev/pci/pcireg.h>
72300313Ssjg#include <dev/pci/pcivar.h>
73236769Sobrien
74236769Sobrien#include <dev/utopia/utopia.h>
75236769Sobrien#include <dev/hatm/if_hatmconf.h>
76236769Sobrien#include <dev/hatm/if_hatmreg.h>
77236769Sobrien#include <dev/hatm/if_hatmvar.h>
78236769Sobrien
79300313SsjgCTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
80236769SobrienCTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
81236769SobrienCTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
82236769SobrienCTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
83236769SobrienCTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
84236769SobrienCTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
85236769Sobrien
86236769Sobrienstatic void hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group);
87236769Sobrien
88236769Sobrien/*
89236769Sobrien * Free an external mbuf to a list. We use atomic functions so that
90236769Sobrien * we don't need a mutex for the list.
91236769Sobrien */
92236769Sobrienstatic __inline void
93236769Sobrienhatm_ext_free(struct mbufx_free **list, struct mbufx_free *buf)
94236769Sobrien{
95236769Sobrien	for (;;) {
96236769Sobrien		buf->link = *list;
97236769Sobrien		if (atomic_cmpset_ptr(list, buf->link, buf))
98236769Sobrien			break;
99236769Sobrien	}
100236769Sobrien}
101236769Sobrien
102236769Sobrienstatic __inline struct mbufx_free *
103236769Sobrienhatm_ext_alloc(struct hatm_softc *sc, u_int g)
104236769Sobrien{
105236769Sobrien	struct mbufx_free *buf;
106236769Sobrien
107236769Sobrien	for (;;) {
108236769Sobrien		if ((buf = sc->mbuf_list[g]) == NULL)
109236769Sobrien			break;
110236769Sobrien		if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
111236769Sobrien			break;
112236769Sobrien	}
113236769Sobrien	if (buf == NULL) {
114236769Sobrien		hatm_mbuf_page_alloc(sc, g);
115236769Sobrien		for (;;) {
116236769Sobrien			if ((buf = sc->mbuf_list[g]) == NULL)
117236769Sobrien				break;
118236769Sobrien			if (atomic_cmpset_ptr(&sc->mbuf_list[g], buf, buf->link))
119236769Sobrien				break;
120236769Sobrien		}
121236769Sobrien	}
122236769Sobrien	return (buf);
123236769Sobrien}
124236769Sobrien
125236769Sobrien/*
126236769Sobrien * Either the queue treshold was crossed or a TPD with the INTR bit set
127236769Sobrien * was transmitted.
128236769Sobrien */
129236769Sobrienstatic void
130236769Sobrienhe_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
131236769Sobrien{
132236769Sobrien	uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
133236769Sobrien	u_int no;
134236769Sobrien
135236769Sobrien	while (q->head != (*tailp >> 2)) {
136236769Sobrien		no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
137236769Sobrien		    HE_REGS_TPD_ADDR;
138236769Sobrien		hatm_tx_complete(sc, TPD_ADDR(sc, no),
139236769Sobrien		    (q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
140236769Sobrien
141236769Sobrien		if (++q->head == q->size)
142236769Sobrien			q->head = 0;
143236769Sobrien	}
144236769Sobrien	WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
145236769Sobrien}
146236769Sobrien
147236769Sobrien/*
148236769Sobrien * DMA loader function for external mbuf page.
149236769Sobrien */
150236769Sobrienstatic void
151236769Sobrienhatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
152236769Sobrien    int error)
153236769Sobrien{
154236769Sobrien	if (error) {
155236769Sobrien		printf("%s: mapping error %d\n", __func__, error);
156236769Sobrien		return;
157236769Sobrien	}
158236769Sobrien	KASSERT(nsegs == 1,
159236769Sobrien	    ("too many segments for DMA: %d", nsegs));
160236769Sobrien	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
161236769Sobrien	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
162236769Sobrien
163236769Sobrien	*(uint32_t *)arg = segs[0].ds_addr;
164236769Sobrien}
165236769Sobrien
166236769Sobrien/*
167236769Sobrien * Allocate a page of external mbuf storage for the small pools.
168236769Sobrien * Create a DMA map and load it. Put all the chunks onto the right
169236769Sobrien * free list.
170236769Sobrien */
171236769Sobrienstatic void
172236769Sobrienhatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
173236769Sobrien{
174236769Sobrien	struct mbuf_page *pg;
175236769Sobrien	int err;
176236769Sobrien	u_int i;
177236769Sobrien
178236769Sobrien	if (sc->mbuf_npages == HE_CONFIG_MAX_MBUF_PAGES)
179236769Sobrien		return;
180236769Sobrien	if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
181236769Sobrien		return;
182236769Sobrien	bzero(pg->hdr.card, sizeof(pg->hdr.card));
183236769Sobrien
184236769Sobrien	err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
185236769Sobrien	if (err != 0) {
186236769Sobrien		if_printf(&sc->ifatm.ifnet, "%s -- bus_dmamap_create: %d\n",
187236769Sobrien		    __func__, err);
188236769Sobrien		free(pg, M_DEVBUF);
189236769Sobrien		return;
190236769Sobrien	}
191236769Sobrien	err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
192236769Sobrien	    hatm_extbuf_helper, &pg->hdr.phys, BUS_DMA_NOWAIT);
193236769Sobrien	if (err != 0) {
194236769Sobrien		if_printf(&sc->ifatm.ifnet, "%s -- mbuf mapping failed %d\n",
195236769Sobrien		    __func__, err);
196236769Sobrien		bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
197236769Sobrien		free(pg, M_DEVBUF);
198236769Sobrien		return;
199236769Sobrien	}
200236769Sobrien
201236769Sobrien	sc->mbuf_pages[sc->mbuf_npages] = pg;
202236769Sobrien
203236769Sobrien	if (group == 0) {
204236769Sobrien		struct mbuf0_chunk *c;
205236769Sobrien
206236769Sobrien		pg->hdr.nchunks = MBUF0_PER_PAGE;
207236769Sobrien		pg->hdr.chunksize = MBUF0_CHUNK;
208236769Sobrien		pg->hdr.hdroff = sizeof(c->storage);
209236769Sobrien		c = (struct mbuf0_chunk *)pg;
210236769Sobrien		for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
211236769Sobrien			c->hdr.pageno = sc->mbuf_npages;
212236769Sobrien			c->hdr.chunkno = i;
213236769Sobrien			hatm_ext_free(&sc->mbuf_list[0],
214236769Sobrien			    (struct mbufx_free *)c);
215236769Sobrien		}
216236769Sobrien	} else {
217236769Sobrien		struct mbuf1_chunk *c;
218236769Sobrien
219236769Sobrien		pg->hdr.nchunks = MBUF1_PER_PAGE;
220236769Sobrien		pg->hdr.chunksize = MBUF1_CHUNK;
221236769Sobrien		pg->hdr.hdroff = sizeof(c->storage);
222236769Sobrien		c = (struct mbuf1_chunk *)pg;
223236769Sobrien		for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
224236769Sobrien			c->hdr.pageno = sc->mbuf_npages;
225236769Sobrien			c->hdr.chunkno = i;
226236769Sobrien			hatm_ext_free(&sc->mbuf_list[1],
227236769Sobrien			    (struct mbufx_free *)c);
228236769Sobrien		}
229236769Sobrien	}
230236769Sobrien	sc->mbuf_npages++;
231236769Sobrien}
232236769Sobrien
233236769Sobrien/*
234236769Sobrien * Free an mbuf and put it onto the free list.
235236769Sobrien */
236236769Sobrienstatic void
237236769Sobrienhatm_mbuf0_free(void *buf, void *args)
238236769Sobrien{
239236769Sobrien	struct hatm_softc *sc = args;
240236769Sobrien	struct mbuf0_chunk *c = buf;
241236769Sobrien
242236769Sobrien	hatm_ext_free(&sc->mbuf_list[0], (struct mbufx_free *)c);
243236769Sobrien}
244236769Sobrienstatic void
245236769Sobrienhatm_mbuf1_free(void *buf, void *args)
246236769Sobrien{
247236769Sobrien	struct hatm_softc *sc = args;
248236769Sobrien	struct mbuf1_chunk *c = buf;
249236769Sobrien
250236769Sobrien	hatm_ext_free(&sc->mbuf_list[1], (struct mbufx_free *)c);
251236769Sobrien}
252236769Sobrien
253236769Sobrien/*
254236769Sobrien * Allocate an external mbuf storage
255296637Ssjg */
256236769Sobrienstatic int
257236769Sobrienhatm_mbuf_alloc(struct hatm_softc *sc, u_int group, uint32_t *phys,
258236769Sobrien    uint32_t *handle)
259236769Sobrien{
260236769Sobrien	struct mbufx_free *cf;
261236769Sobrien	struct mbuf_page *pg;
262236769Sobrien
263236769Sobrien	if (group == 0) {
264236769Sobrien		struct mbuf0_chunk *buf0;
265236769Sobrien
266236769Sobrien		if ((cf = hatm_ext_alloc(sc, 0)) == NULL)
267236769Sobrien			return (-1);
268236769Sobrien		buf0 = (struct mbuf0_chunk *)cf;
269236769Sobrien		pg = sc->mbuf_pages[buf0->hdr.pageno];
270236769Sobrien		MBUF_SET_BIT(pg->hdr.card, buf0->hdr.chunkno);
271236769Sobrien
272236769Sobrien		*handle = MBUF_MAKE_HANDLE(buf0->hdr.pageno, buf0->hdr.chunkno);
273236769Sobrien		*phys = pg->hdr.phys + buf0->hdr.chunkno * MBUF0_CHUNK +
274236769Sobrien		    MBUF0_OFFSET;
275236769Sobrien
276236769Sobrien	} else if (group == 1) {
277236769Sobrien		struct mbuf1_chunk *buf1;
278236769Sobrien
279236769Sobrien		if ((cf = hatm_ext_alloc(sc, 1)) == NULL)
280236769Sobrien			return (-1);
281236769Sobrien		buf1 = (struct mbuf1_chunk *)cf;
282236769Sobrien		pg = sc->mbuf_pages[buf1->hdr.pageno];
283236769Sobrien		MBUF_SET_BIT(pg->hdr.card, buf1->hdr.chunkno);
284236769Sobrien
285236769Sobrien		*handle = MBUF_MAKE_HANDLE(buf1->hdr.pageno, buf1->hdr.chunkno);
286236769Sobrien		*phys = pg->hdr.phys + buf1->hdr.chunkno * MBUF1_CHUNK +
287236769Sobrien		    MBUF1_OFFSET;
288236769Sobrien
289236769Sobrien	} else
290236769Sobrien		return (-1);
291236769Sobrien
292236769Sobrien	bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map, BUS_DMASYNC_PREREAD);
293236769Sobrien
294236769Sobrien	return (0);
295236769Sobrien}
296236769Sobrien
297236769Sobrienstatic void
298236769Sobrienhatm_mbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
299236769Sobrien{
300236769Sobrien	uint32_t *ptr = (uint32_t *)arg;
301236769Sobrien
302236769Sobrien	if (nsegs == 0) {
303236769Sobrien		printf("%s: error=%d\n", __func__, error);
304236769Sobrien		return;
305236769Sobrien	}
306236769Sobrien	KASSERT(nsegs == 1, ("too many segments for mbuf: %d", nsegs));
307236769Sobrien	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
308236769Sobrien	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
309236769Sobrien
310296637Ssjg	*ptr = segs[0].ds_addr;
311296637Ssjg}
312296637Ssjg
313296637Ssjg/*
314236769Sobrien * Receive buffer pool interrupt. This means the number of entries in the
315236769Sobrien * queue has dropped below the threshold. Try to supply new buffers.
316236769Sobrien */
317236769Sobrienstatic void
318236769Sobrienhe_intr_rbp(struct hatm_softc *sc, struct herbp *rbp, u_int large,
319236769Sobrien    u_int group)
320236769Sobrien{
321236769Sobrien	u_int ntail;
322236769Sobrien	struct mbuf *m;
323236769Sobrien	int error;
324236769Sobrien
325236769Sobrien	DBG(sc, INTR, ("%s buffer supply threshold crossed for group %u",
326296637Ssjg	   large ? "large" : "small", group));
327236769Sobrien
328236769Sobrien	rbp->head = (READ4(sc, HE_REGO_RBP_S(large, group)) >> HE_REGS_RBP_HEAD)
329236769Sobrien	    & (rbp->size - 1);
330236769Sobrien
331236769Sobrien	for (;;) {
332236769Sobrien		if ((ntail = rbp->tail + 1) == rbp->size)
333236769Sobrien			ntail = 0;
334236769Sobrien		if (ntail == rbp->head)
335236769Sobrien			break;
336236769Sobrien
337236769Sobrien		if (large) {
338236769Sobrien			/* allocate the MBUF */
339236769Sobrien			if ((m = m_getcl(M_DONTWAIT, MT_DATA,
340236769Sobrien			    M_PKTHDR)) == NULL) {
341236769Sobrien				if_printf(&sc->ifatm.ifnet,
342236769Sobrien				    "no mbuf clusters\n");
343236769Sobrien				break;
344236769Sobrien			}
345236769Sobrien			m->m_data += MBUFL_OFFSET;
346236769Sobrien
347236769Sobrien			if (sc->lbufs[sc->lbufs_next] != NULL)
348236769Sobrien				panic("hatm: lbufs full %u", sc->lbufs_next);
349236769Sobrien			sc->lbufs[sc->lbufs_next] = m;
350236769Sobrien
351236769Sobrien			if ((error = bus_dmamap_load(sc->mbuf_tag,
352296637Ssjg			    sc->rmaps[sc->lbufs_next],
353296637Ssjg			    m->m_data, rbp->bsize, hatm_mbuf_helper,
354296637Ssjg			    &rbp->rbp[rbp->tail].phys, BUS_DMA_NOWAIT)) != NULL)
355296637Ssjg				panic("hatm: mbuf mapping failed %d", error);
356236769Sobrien
357236769Sobrien			bus_dmamap_sync(sc->mbuf_tag,
358236769Sobrien			    sc->rmaps[sc->lbufs_next],
359236769Sobrien			    BUS_DMASYNC_PREREAD);
360236769Sobrien
361236769Sobrien			rbp->rbp[rbp->tail].handle = sc->lbufs_next |
362236769Sobrien			    MBUF_LARGE_FLAG;
363236769Sobrien
364236769Sobrien			if (++sc->lbufs_next == sc->lbufs_size)
365236769Sobrien				sc->lbufs_next = 0;
366236769Sobrien
367236769Sobrien		} else {
368236769Sobrien			m = NULL;
369236769Sobrien			if (hatm_mbuf_alloc(sc, group,
370236769Sobrien			    &rbp->rbp[rbp->tail].phys,
371236769Sobrien			    &rbp->rbp[rbp->tail].handle)) {
372236769Sobrien				m_freem(m);
373236769Sobrien				break;
374236769Sobrien			}
375236769Sobrien		}
376236769Sobrien		DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
377236769Sobrien		    rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
378236769Sobrien		rbp->rbp[rbp->tail].handle <<= HE_REGS_RBRQ_ADDR;
379236769Sobrien
380236769Sobrien		rbp->tail = ntail;
381236769Sobrien	}
382236769Sobrien	WRITE4(sc, HE_REGO_RBP_T(large, group),
383236769Sobrien	    (rbp->tail << HE_REGS_RBP_TAIL));
384236769Sobrien}
385236769Sobrien
386236769Sobrien/*
387236769Sobrien * Extract the buffer and hand it to the receive routine
388236769Sobrien */
389236769Sobrienstatic struct mbuf *
390236769Sobrienhatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
391236769Sobrien{
392236769Sobrien	u_int pageno;
393236769Sobrien	u_int chunkno;
394236769Sobrien	struct mbuf *m;
395236769Sobrien
396236769Sobrien	if (handle & MBUF_LARGE_FLAG) {
397236769Sobrien		/* large buffer - sync and unload */
398236769Sobrien		handle &= ~MBUF_LARGE_FLAG;
399236769Sobrien		DBG(sc, RX, ("RX large handle=%x", handle));
400236769Sobrien
401236769Sobrien		bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
402236769Sobrien		    BUS_DMASYNC_POSTREAD);
403236769Sobrien		bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
404236769Sobrien
405236769Sobrien		m = sc->lbufs[handle];
406296637Ssjg		sc->lbufs[handle] = NULL;
407296637Ssjg
408236769Sobrien		return (m);
409236769Sobrien	}
410236769Sobrien
411236769Sobrien	MBUF_PARSE_HANDLE(handle, pageno, chunkno);
412236769Sobrien	MBUF_CLR_BIT(sc->mbuf_pages[pageno]->hdr.card, chunkno);
413236769Sobrien
414236769Sobrien	DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
415236769Sobrien	    pageno, chunkno));
416236769Sobrien
417236769Sobrien	MGETHDR(m, M_DONTWAIT, MT_DATA);
418236769Sobrien
419236769Sobrien	if (group == 0) {
420236769Sobrien		struct mbuf0_chunk *c0;
421236769Sobrien
422236769Sobrien		c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
423236769Sobrien		KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
424236769Sobrien		    c0->hdr.pageno, pageno));
425236769Sobrien		KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
426236769Sobrien		    c0->hdr.chunkno, chunkno));
427236769Sobrien
428236769Sobrien		if (m != NULL) {
429236769Sobrien			m->m_ext.ref_cnt = &c0->hdr.ref_cnt;
430236769Sobrien			m_extadd(m, (void *)c0, MBUF0_SIZE,
431236769Sobrien			    hatm_mbuf0_free, sc, M_PKTHDR, EXT_EXTREF);
432236769Sobrien			m->m_data += MBUF0_OFFSET;
433236769Sobrien		} else
434236769Sobrien			hatm_mbuf0_free(c0, sc);
435236769Sobrien
436236769Sobrien	} else {
437236769Sobrien		struct mbuf1_chunk *c1;
438236769Sobrien
439236769Sobrien		c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
440236769Sobrien		KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
441236769Sobrien		    c1->hdr.pageno, pageno));
442236769Sobrien		KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
443236769Sobrien		    c1->hdr.chunkno, chunkno));
444236769Sobrien
445236769Sobrien		if (m != NULL) {
446236769Sobrien			m->m_ext.ref_cnt = &c1->hdr.ref_cnt;
447236769Sobrien			m_extadd(m, (void *)c1, MBUF1_SIZE,
448236769Sobrien			    hatm_mbuf1_free, sc, M_PKTHDR, EXT_EXTREF);
449236769Sobrien			m->m_data += MBUF1_OFFSET;
450236769Sobrien		} else
451236769Sobrien			hatm_mbuf1_free(c1, sc);
452236769Sobrien	}
453236769Sobrien
454236769Sobrien	return (m);
455236769Sobrien}
456236769Sobrien
457236769Sobrien/*
458236769Sobrien * Interrupt because of receive buffer returned.
459236769Sobrien */
460236769Sobrienstatic void
461236769Sobrienhe_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
462236769Sobrien{
463236769Sobrien	struct he_rbrqen *e;
464236769Sobrien	uint32_t flags, tail;
465236769Sobrien	u_int cid, len;
466236769Sobrien	struct mbuf *m;
467236769Sobrien
468236769Sobrien	for (;;) {
469236769Sobrien		tail = sc->hsp->group[group].rbrq_tail >> 3;
470236769Sobrien
471236769Sobrien		if (rq->head == tail)
472236769Sobrien			break;
473236769Sobrien
474236769Sobrien		e = &rq->rbrq[rq->head];
475236769Sobrien
476236769Sobrien		flags = e->addr & HE_REGM_RBRQ_FLAGS;
477236769Sobrien		if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
478236769Sobrien			m = hatm_rx_buffer(sc, group,
479236769Sobrien			    (e->addr & HE_REGM_RBRQ_ADDR) >> HE_REGS_RBRQ_ADDR);
480236769Sobrien		else
481236769Sobrien			m = NULL;
482236769Sobrien
483236769Sobrien		cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
484236769Sobrien		len = 4 * (e->len & HE_REGM_RBRQ_LEN);
485236769Sobrien
486236769Sobrien		hatm_rx(sc, cid, flags, m, len);
487236769Sobrien
488236769Sobrien		if (++rq->head == rq->size)
489236769Sobrien			rq->head = 0;
490236769Sobrien	}
491236769Sobrien	WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
492236769Sobrien}
493236769Sobrien
494236769Sobrienvoid
495236769Sobrienhatm_intr(void *p)
496236769Sobrien{
497236769Sobrien	struct heirq *q = p;
498236769Sobrien	struct hatm_softc *sc = q->sc;
499236769Sobrien	u_int status;
500236769Sobrien	u_int tail;
501236769Sobrien
502236769Sobrien	/* if we have a stray interrupt with a non-initialized card,
503236769Sobrien	 * we cannot even lock before looking at the flag */
504236769Sobrien	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING))
505236769Sobrien		return;
506236769Sobrien
507236769Sobrien	mtx_lock(&sc->mtx);
508236769Sobrien	(void)READ4(sc, HE_REGO_INT_FIFO);
509236769Sobrien
510236769Sobrien	tail = *q->tailp;
511236769Sobrien	if (q->head == tail) {
512236769Sobrien		/* workaround for tail pointer not updated bug (8.1.1) */
513236769Sobrien		DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
514236769Sobrien
515236769Sobrien		/* read the tail pointer from the card */
516236769Sobrien		tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
517236769Sobrien		    HE_REGM_IRQ_BASE_TAIL;
518236769Sobrien		BARRIER_R(sc);
519236769Sobrien
520236769Sobrien		sc->istats.bug_no_irq_upd++;
521236769Sobrien	}
522236769Sobrien
523236769Sobrien	/* clear the interrupt */
524236769Sobrien	WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
525236769Sobrien	BARRIER_W(sc);
526236769Sobrien
527236769Sobrien	while (q->head != tail) {
528236769Sobrien		status = q->irq[q->head];
529236769Sobrien		q->irq[q->head] = HE_REGM_ITYPE_INVALID;
530236769Sobrien		if (++q->head == (q->size - 1))
531236769Sobrien			q->head = 0;
532236769Sobrien
533236769Sobrien		switch (status & HE_REGM_ITYPE) {
534236769Sobrien
535236769Sobrien		  case HE_REGM_ITYPE_TBRQ:
536236769Sobrien			DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
537236769Sobrien			sc->istats.itype_tbrq++;
538236769Sobrien			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
539236769Sobrien			break;
540236769Sobrien
541236769Sobrien		  case HE_REGM_ITYPE_TPD:
542236769Sobrien			DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
543236769Sobrien			sc->istats.itype_tpd++;
544236769Sobrien			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
545236769Sobrien			break;
546236769Sobrien
547236769Sobrien		  case HE_REGM_ITYPE_RBPS:
548236769Sobrien			sc->istats.itype_rbps++;
549236769Sobrien			switch (status & HE_REGM_IGROUP) {
550236769Sobrien
551236769Sobrien			  case 0:
552236769Sobrien				he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
553236769Sobrien				break;
554236769Sobrien
555236769Sobrien			  case 1:
556236769Sobrien				he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
557236769Sobrien				break;
558236769Sobrien
559236769Sobrien			  default:
560236769Sobrien				if_printf(&sc->ifatm.ifnet, "bad INTR RBPS%u\n",
561236769Sobrien				    status & HE_REGM_IGROUP);
562236769Sobrien				break;
563236769Sobrien			}
564236769Sobrien			break;
565236769Sobrien
566236769Sobrien		  case HE_REGM_ITYPE_RBPL:
567236769Sobrien			sc->istats.itype_rbpl++;
568236769Sobrien			switch (status & HE_REGM_IGROUP) {
569236769Sobrien
570236769Sobrien			  case 0:
571236769Sobrien				he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
572236769Sobrien				break;
573236769Sobrien
574236769Sobrien			  default:
575236769Sobrien				if_printf(&sc->ifatm.ifnet, "bad INTR RBPL%u\n",
576236769Sobrien				    status & HE_REGM_IGROUP);
577236769Sobrien				break;
578236769Sobrien			}
579236769Sobrien			break;
580236769Sobrien
581236769Sobrien		  case HE_REGM_ITYPE_RBRQ:
582236769Sobrien			DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
583236769Sobrien			sc->istats.itype_rbrq++;
584236769Sobrien			switch (status & HE_REGM_IGROUP) {
585236769Sobrien
586236769Sobrien			  case 0:
587236769Sobrien				he_intr_rbrq(sc, &sc->rbrq_0, 0);
588236769Sobrien				break;
589236769Sobrien
590236769Sobrien			  case 1:
591236769Sobrien				if (sc->rbrq_1.size > 0) {
592236769Sobrien					he_intr_rbrq(sc, &sc->rbrq_1, 1);
593236769Sobrien					break;
594236769Sobrien				}
595236769Sobrien				/* FALLTHRU */
596236769Sobrien
597236769Sobrien			  default:
598236769Sobrien				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQ%u\n",
599236769Sobrien				    status & HE_REGM_IGROUP);
600236769Sobrien				break;
601236769Sobrien			}
602236769Sobrien			break;
603236769Sobrien
604236769Sobrien		  case HE_REGM_ITYPE_RBRQT:
605236769Sobrien			DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
606236769Sobrien			sc->istats.itype_rbrqt++;
607236769Sobrien			switch (status & HE_REGM_IGROUP) {
608236769Sobrien
609236769Sobrien			  case 0:
610236769Sobrien				he_intr_rbrq(sc, &sc->rbrq_0, 0);
611236769Sobrien				break;
612236769Sobrien
613236769Sobrien			  case 1:
614236769Sobrien				if (sc->rbrq_1.size > 0) {
615236769Sobrien					he_intr_rbrq(sc, &sc->rbrq_1, 1);
616236769Sobrien					break;
617236769Sobrien				}
618236769Sobrien				/* FALLTHRU */
619236769Sobrien
620236769Sobrien			  default:
621236769Sobrien				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQT%u\n",
622236769Sobrien				    status & HE_REGM_IGROUP);
623236769Sobrien				break;
624236769Sobrien			}
625236769Sobrien			break;
626236769Sobrien
627236769Sobrien		  case HE_REGM_ITYPE_PHYS:
628236769Sobrien			sc->istats.itype_phys++;
629236769Sobrien			utopia_intr(&sc->utopia);
630236769Sobrien			break;
631236769Sobrien
632236769Sobrien#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
633236769Sobrien		  case HE_REGM_ITYPE_UNKNOWN:
634236769Sobrien			sc->istats.itype_unknown++;
635236769Sobrien			if_printf(&sc->ifatm.ifnet, "bad interrupt\n");
636236769Sobrien			break;
637236769Sobrien#endif
638236769Sobrien
639236769Sobrien		  case HE_REGM_ITYPE_ERR:
640236769Sobrien			sc->istats.itype_err++;
641236769Sobrien			switch (status) {
642236769Sobrien
643236769Sobrien			  case HE_REGM_ITYPE_PERR:
644236769Sobrien				if_printf(&sc->ifatm.ifnet, "parity error\n");
645236769Sobrien				break;
646236769Sobrien
647236769Sobrien			  case HE_REGM_ITYPE_ABORT:
648236769Sobrien				if_printf(&sc->ifatm.ifnet, "abort interrupt "
649236769Sobrien				    "addr=0x%08x\n",
650236769Sobrien				    READ4(sc, HE_REGO_ABORT_ADDR));
651236769Sobrien				break;
652236769Sobrien
653236769Sobrien			  default:
654236769Sobrien				if_printf(&sc->ifatm.ifnet,
655236769Sobrien				    "bad interrupt type %08x\n", status);
656236769Sobrien				break;
657236769Sobrien			}
658236769Sobrien			break;
659236769Sobrien
660236769Sobrien		  case HE_REGM_ITYPE_INVALID:
661236769Sobrien			/* this is the documented fix for the ISW bug 8.1.1
662236769Sobrien			 * Note, that the documented fix is partly wrong:
663236769Sobrien			 * the ISWs should be intialized to 0xf8 not 0xff */
664236769Sobrien			sc->istats.bug_bad_isw++;
665236769Sobrien			DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
666236769Sobrien			he_intr_tbrq(sc, &sc->tbrq, 0);
667236769Sobrien			he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
668236769Sobrien			he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
669236769Sobrien			he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
670236769Sobrien			he_intr_rbrq(sc, &sc->rbrq_0, 0);
671236769Sobrien			he_intr_rbrq(sc, &sc->rbrq_1, 1);
672236769Sobrien			utopia_intr(&sc->utopia);
673236769Sobrien			break;
674236769Sobrien
675236769Sobrien		  default:
676236769Sobrien			if_printf(&sc->ifatm.ifnet, "bad interrupt type %08x\n",
677236769Sobrien			    status);
678236769Sobrien			break;
679236769Sobrien		}
680236769Sobrien	}
681236769Sobrien
682236769Sobrien	/* write back head to clear queue */
683236769Sobrien	WRITE4(sc, HE_REGO_IRQ_HEAD(0),
684236769Sobrien	    ((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
685236769Sobrien	    (q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
686236769Sobrien	    (q->head << HE_REGS_IRQ_HEAD_HEAD));
687236769Sobrien	BARRIER_W(sc);
688236769Sobrien
689236769Sobrien	/* workaround the back-to-back irq access problem (8.1.2) */
690236769Sobrien	(void)READ4(sc, HE_REGO_INT_FIFO);
691236769Sobrien	BARRIER_R(sc);
692236769Sobrien
693236769Sobrien	mtx_unlock(&sc->mtx);
694236769Sobrien}
695236769Sobrien