if_hatm_intr.c revision 119280
1116491Sharti/*
2116491Sharti * Copyright (c) 2001-2003
3116491Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4116491Sharti * 	All rights reserved.
5116491Sharti *
6116491Sharti * Redistribution and use in source and binary forms, with or without
7116491Sharti * modification, are permitted provided that the following conditions
8116491Sharti * are met:
9116491Sharti * 1. Redistributions of source code must retain the above copyright
10116491Sharti *    notice, this list of conditions and the following disclaimer.
11116491Sharti * 2. Redistributions in binary form must reproduce the above copyright
12116491Sharti *    notice, this list of conditions and the following disclaimer in the
13116491Sharti *    documentation and/or other materials provided with the distribution.
14116491Sharti *
15116491Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16116491Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17116491Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18116491Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19116491Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20116491Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21116491Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22116491Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23116491Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24116491Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25116491Sharti * SUCH DAMAGE.
26116491Sharti *
27116491Sharti * Author: Hartmut Brandt <harti@freebsd.org>
28116491Sharti *
29116491Sharti * ForeHE driver.
30116491Sharti *
31116491Sharti * Interrupt handler.
32116491Sharti */
33116491Sharti
34116519Sharti#include <sys/cdefs.h>
35116519Sharti__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_intr.c 119280 2003-08-22 06:00:27Z imp $");
36116519Sharti
37116491Sharti#include "opt_inet.h"
38116491Sharti#include "opt_natm.h"
39116491Sharti
40116491Sharti#include <sys/types.h>
41116491Sharti#include <sys/param.h>
42116491Sharti#include <sys/systm.h>
43116491Sharti#include <sys/malloc.h>
44116491Sharti#include <sys/kernel.h>
45116491Sharti#include <sys/bus.h>
46116491Sharti#include <sys/errno.h>
47116491Sharti#include <sys/conf.h>
48116491Sharti#include <sys/module.h>
49116491Sharti#include <sys/queue.h>
50116491Sharti#include <sys/syslog.h>
51116491Sharti#include <sys/condvar.h>
52116491Sharti#include <sys/sysctl.h>
53116491Sharti#include <vm/uma.h>
54116491Sharti
55116491Sharti#include <sys/sockio.h>
56116491Sharti#include <sys/mbuf.h>
57116491Sharti#include <sys/socket.h>
58116491Sharti
59116491Sharti#include <net/if.h>
60116491Sharti#include <net/if_media.h>
61116491Sharti#include <net/if_atm.h>
62116491Sharti#include <net/route.h>
63116491Sharti#include <netinet/in.h>
64116491Sharti#include <netinet/if_atm.h>
65116491Sharti
66116491Sharti#include <machine/bus.h>
67116491Sharti#include <machine/resource.h>
68116491Sharti#include <sys/bus.h>
69116491Sharti#include <sys/rman.h>
70119280Simp#include <dev/pci/pcireg.h>
71119280Simp#include <dev/pci/pcivar.h>
72116491Sharti
73116491Sharti#include <dev/utopia/utopia.h>
74116491Sharti#include <dev/hatm/if_hatmconf.h>
75116491Sharti#include <dev/hatm/if_hatmreg.h>
76116491Sharti#include <dev/hatm/if_hatmvar.h>
77116491Sharti
78116491ShartiCTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
79116491ShartiCTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
80116491ShartiCTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
81116491ShartiCTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
82116491ShartiCTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
83116491ShartiCTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
84116491Sharti
85116491Sharti/*
86116491Sharti * Either the queue treshold was crossed or a TPD with the INTR bit set
87116491Sharti * was transmitted.
88116491Sharti */
89116491Shartistatic void
90116491Shartihe_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
91116491Sharti{
92116491Sharti	uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
93116491Sharti	u_int no;
94116491Sharti
95116491Sharti	while (q->head != (*tailp >> 2)) {
96116491Sharti		no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
97116491Sharti		    HE_REGS_TPD_ADDR;
98116491Sharti		hatm_tx_complete(sc, TPD_ADDR(sc, no),
99116491Sharti		    (q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
100116491Sharti
101116491Sharti		if (++q->head == q->size)
102116491Sharti			q->head = 0;
103116491Sharti	}
104116491Sharti	WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
105116491Sharti}
106116491Sharti
107116491Sharti/*
108116491Sharti * DMA loader function for external mbuf page.
109116491Sharti */
110116491Shartistatic void
111116491Shartihatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
112116491Sharti    int error)
113116491Sharti{
114116491Sharti	if (error) {
115116491Sharti		printf("%s: mapping error %d\n", __func__, error);
116116491Sharti		return;
117116491Sharti	}
118116491Sharti	KASSERT(nsegs == 1,
119116491Sharti	    ("too many segments for DMA: %d", nsegs));
120116491Sharti	KASSERT(segs[0].ds_addr <= 0xffffffffLU,
121116491Sharti	    ("phys addr too large %lx", (u_long)segs[0].ds_addr));
122116491Sharti
123116491Sharti	*(uint32_t *)arg = segs[0].ds_addr;
124116491Sharti}
125116491Sharti
126116491Sharti/*
127116491Sharti * Allocate a page of external mbuf storage for the small pools.
128116491Sharti * Create a DMA map and load it. Put all the chunks onto the right
129116491Sharti * free list.
130116491Sharti */
131116491Shartistatic void
132116491Shartihatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
133116491Sharti{
134116491Sharti	struct mbuf_page *pg;
135116491Sharti	int err;
136116491Sharti	u_int i;
137116491Sharti
138116491Sharti	if (sc->mbuf_npages == HE_CONFIG_MAX_MBUF_PAGES)
139116491Sharti		return;
140116491Sharti	if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
141116491Sharti		return;
142116491Sharti	bzero(pg->hdr.card, sizeof(pg->hdr.card));
143116491Sharti	bzero(pg->hdr.used, sizeof(pg->hdr.used));
144116491Sharti
145116491Sharti	err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
146116491Sharti	if (err != 0) {
147116491Sharti		if_printf(&sc->ifatm.ifnet, "%s -- bus_dmamap_create: %d\n",
148116491Sharti		    __func__, err);
149116491Sharti		free(pg, M_DEVBUF);
150116491Sharti		return;
151116491Sharti	}
152116491Sharti	err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
153117382Sharti	    hatm_extbuf_helper, &pg->hdr.phys, BUS_DMA_NOWAIT);
154116491Sharti	if (err != 0) {
155116491Sharti		if_printf(&sc->ifatm.ifnet, "%s -- mbuf mapping failed %d\n",
156116491Sharti		    __func__, err);
157116491Sharti		bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
158116491Sharti		free(pg, M_DEVBUF);
159116491Sharti		return;
160116491Sharti	}
161116491Sharti
162116491Sharti	sc->mbuf_pages[sc->mbuf_npages] = pg;
163116491Sharti
164116491Sharti	if (group == 0) {
165116491Sharti		struct mbuf0_chunk *c;
166116491Sharti
167116491Sharti		pg->hdr.nchunks = MBUF0_PER_PAGE;
168116491Sharti		pg->hdr.chunksize = MBUF0_CHUNK;
169116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
170116491Sharti		c = (struct mbuf0_chunk *)pg;
171116491Sharti		for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
172116491Sharti			c->hdr.pageno = sc->mbuf_npages;
173116491Sharti			c->hdr.chunkno = i;
174116491Sharti			SLIST_INSERT_HEAD(&sc->mbuf0_list,
175116491Sharti			    (struct mbufx_free *)c, link);
176116491Sharti		}
177116491Sharti	} else {
178116491Sharti		struct mbuf1_chunk *c;
179116491Sharti
180116491Sharti		pg->hdr.nchunks = MBUF1_PER_PAGE;
181116491Sharti		pg->hdr.chunksize = MBUF1_CHUNK;
182116491Sharti		pg->hdr.hdroff = sizeof(c->storage);
183116491Sharti		c = (struct mbuf1_chunk *)pg;
184116491Sharti		for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
185116491Sharti			c->hdr.pageno = sc->mbuf_npages;
186116491Sharti			c->hdr.chunkno = i;
187116491Sharti			SLIST_INSERT_HEAD(&sc->mbuf1_list,
188116491Sharti			    (struct mbufx_free *)c, link);
189116491Sharti		}
190116491Sharti	}
191116491Sharti	sc->mbuf_npages++;
192116491Sharti}
193116491Sharti
194116491Sharti/*
195116491Sharti * Free an mbuf and put it onto the free list.
196116491Sharti */
197116491Shartistatic void
198116491Shartihatm_mbuf0_free(void *buf, void *args)
199116491Sharti{
200116491Sharti	struct hatm_softc *sc = args;
201116491Sharti	struct mbuf0_chunk *c = buf;
202116491Sharti
203116491Sharti	mtx_lock(&sc->mbuf0_mtx);
204116491Sharti	SLIST_INSERT_HEAD(&sc->mbuf0_list, (struct mbufx_free *)c, link);
205116491Sharti	MBUF_CLR_BIT(sc->mbuf_pages[c->hdr.pageno]->hdr.used, c->hdr.chunkno);
206116491Sharti	mtx_unlock(&sc->mbuf0_mtx);
207116491Sharti}
208116491Shartistatic void
209116491Shartihatm_mbuf1_free(void *buf, void *args)
210116491Sharti{
211116491Sharti	struct hatm_softc *sc = args;
212116491Sharti	struct mbuf1_chunk *c = buf;
213116491Sharti
214116491Sharti	mtx_lock(&sc->mbuf1_mtx);
215116491Sharti	SLIST_INSERT_HEAD(&sc->mbuf1_list, (struct mbufx_free *)c, link);
216116491Sharti	MBUF_CLR_BIT(sc->mbuf_pages[c->hdr.pageno]->hdr.used, c->hdr.chunkno);
217116491Sharti	mtx_unlock(&sc->mbuf1_mtx);
218116491Sharti}
219116491Sharti
220116491Sharti/*
221116491Sharti * Allocate an external mbuf storage
222116491Sharti */
223116491Shartistatic int
224116491Shartihatm_mbuf_alloc(struct hatm_softc *sc, u_int group, struct mbuf *m,
225116491Sharti    uint32_t *phys, uint32_t *handle)
226116491Sharti{
227116491Sharti	struct mbufx_free *cf;
228116491Sharti	struct mbuf_page *pg;
229116491Sharti
230116491Sharti	if (group == 0) {
231116491Sharti		struct mbuf0_chunk *buf0;
232116491Sharti
233116491Sharti		mtx_lock(&sc->mbuf0_mtx);
234116491Sharti		if ((cf = SLIST_FIRST(&sc->mbuf0_list)) == NULL) {
235116491Sharti			hatm_mbuf_page_alloc(sc, group);
236116491Sharti			if ((cf = SLIST_FIRST(&sc->mbuf0_list)) == NULL) {
237116491Sharti				mtx_unlock(&sc->mbuf0_mtx);
238116491Sharti				return (0);
239116491Sharti			}
240116491Sharti		}
241116491Sharti		SLIST_REMOVE_HEAD(&sc->mbuf0_list, link);
242116491Sharti		buf0 = (struct mbuf0_chunk *)cf;
243116491Sharti		pg = sc->mbuf_pages[buf0->hdr.pageno];
244116491Sharti		MBUF_SET_BIT(pg->hdr.card, buf0->hdr.chunkno);
245116491Sharti		mtx_unlock(&sc->mbuf0_mtx);
246116491Sharti
247116491Sharti		m_extadd(m, (caddr_t)buf0, MBUF0_SIZE, hatm_mbuf0_free, sc,
248116491Sharti		    M_PKTHDR, EXT_NET_DRV);
249116491Sharti		m->m_data += MBUF0_OFFSET;
250116491Sharti		buf0->hdr.mbuf = m;
251116491Sharti
252116491Sharti		*handle = MBUF_MAKE_HANDLE(buf0->hdr.pageno, buf0->hdr.chunkno);
253116491Sharti
254116491Sharti	} else if (group == 1) {
255116491Sharti		struct mbuf1_chunk *buf1;
256116491Sharti
257116491Sharti		mtx_lock(&sc->mbuf1_mtx);
258116491Sharti		if ((cf = SLIST_FIRST(&sc->mbuf1_list)) == NULL) {
259116491Sharti			hatm_mbuf_page_alloc(sc, group);
260116491Sharti			if ((cf = SLIST_FIRST(&sc->mbuf1_list)) == NULL) {
261116491Sharti				mtx_unlock(&sc->mbuf1_mtx);
262116491Sharti				return (0);
263116491Sharti			}
264116491Sharti		}
265116491Sharti		SLIST_REMOVE_HEAD(&sc->mbuf1_list, link);
266116491Sharti		buf1 = (struct mbuf1_chunk *)cf;
267116491Sharti		pg = sc->mbuf_pages[buf1->hdr.pageno];
268116491Sharti		MBUF_SET_BIT(pg->hdr.card, buf1->hdr.chunkno);
269116491Sharti		mtx_unlock(&sc->mbuf1_mtx);
270116491Sharti
271116491Sharti		m_extadd(m, (caddr_t)buf1, MBUF1_SIZE, hatm_mbuf1_free, sc,
272116491Sharti		    M_PKTHDR, EXT_NET_DRV);
273116491Sharti		m->m_data += MBUF1_OFFSET;
274116491Sharti		buf1->hdr.mbuf = m;
275116491Sharti
276116491Sharti		*handle = MBUF_MAKE_HANDLE(buf1->hdr.pageno, buf1->hdr.chunkno);
277116491Sharti
278116491Sharti	} else
279116491Sharti		return (-1);
280116491Sharti
281116491Sharti	*phys = pg->hdr.phys + (mtod(m, char *) - (char *)pg);
282116491Sharti	bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map, BUS_DMASYNC_PREREAD);
283116491Sharti
284116491Sharti	return (0);
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{
311116491Sharti	u_int ntail, upd;
312116491Sharti	struct mbuf *m;
313116491Sharti	int error;
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	upd = 0;
322116491Sharti	for (;;) {
323116491Sharti		if ((ntail = rbp->tail + 1) == rbp->size)
324116491Sharti			ntail = 0;
325116491Sharti		if (ntail == rbp->head)
326116491Sharti			break;
327116491Sharti
328116491Sharti		/* allocate the MBUF */
329116491Sharti		if (large) {
330116491Sharti			if ((m = m_getcl(M_DONTWAIT, MT_DATA,
331116491Sharti			    M_PKTHDR)) == NULL) {
332116491Sharti				if_printf(&sc->ifatm.ifnet,
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,
345117382Sharti			    &rbp->rbp[rbp->tail].phys, BUS_DMA_NOWAIT)) != NULL)
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
352116491Sharti			rbp->rbp[rbp->tail].handle = sc->lbufs_next |
353116491Sharti			    MBUF_LARGE_FLAG;
354116491Sharti
355116491Sharti			if (++sc->lbufs_next == sc->lbufs_size)
356116491Sharti				sc->lbufs_next = 0;
357116491Sharti
358116491Sharti		} else {
359116491Sharti			MGETHDR(m, M_DONTWAIT, MT_DATA);
360116491Sharti			if (m == NULL) {
361116491Sharti				if_printf(&sc->ifatm.ifnet, "no mbufs\n");
362116491Sharti				break;
363116491Sharti			}
364116491Sharti			if (hatm_mbuf_alloc(sc, group, m,
365116491Sharti			    &rbp->rbp[rbp->tail].phys,
366116491Sharti			    &rbp->rbp[rbp->tail].handle)) {
367116491Sharti				m_freem(m);
368116491Sharti				break;
369116491Sharti			}
370116491Sharti		}
371116491Sharti		DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
372116491Sharti		    rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
373116491Sharti		rbp->rbp[rbp->tail].handle <<= HE_REGS_RBRQ_ADDR;
374116491Sharti
375116491Sharti		rbp->tail = ntail;
376116491Sharti		upd++;
377116491Sharti	}
378116491Sharti	if (upd) {
379116491Sharti		WRITE4(sc, HE_REGO_RBP_T(large, group),
380116491Sharti		    (rbp->tail << HE_REGS_RBP_TAIL));
381116491Sharti	}
382116491Sharti}
383116491Sharti
384116491Sharti/*
385116491Sharti * Extract the buffer and hand it to the receive routine
386116491Sharti */
387116491Shartistatic struct mbuf *
388116491Shartihatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
389116491Sharti{
390116491Sharti	u_int pageno;
391116491Sharti	u_int chunkno;
392116491Sharti	struct mbuf *m;
393116491Sharti
394116491Sharti	if (handle & MBUF_LARGE_FLAG) {
395116491Sharti		/* large buffer - sync and unload */
396116491Sharti		handle &= ~MBUF_LARGE_FLAG;
397116491Sharti		DBG(sc, RX, ("RX large handle=%x", handle));
398116491Sharti
399116491Sharti		bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
400116491Sharti		    BUS_DMASYNC_POSTREAD);
401116491Sharti		bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
402116491Sharti
403116491Sharti		m = sc->lbufs[handle];
404116491Sharti		sc->lbufs[handle] = NULL;
405116491Sharti
406116491Sharti		return (m);
407116491Sharti	}
408116491Sharti
409116491Sharti	MBUF_PARSE_HANDLE(handle, pageno, chunkno);
410116491Sharti
411116491Sharti	DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
412116491Sharti	    pageno, chunkno));
413116491Sharti
414116491Sharti	if (group == 0) {
415116491Sharti		struct mbuf0_chunk *c0;
416116491Sharti
417116491Sharti		c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
418116491Sharti		KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
419116491Sharti		    c0->hdr.pageno, pageno));
420116491Sharti		KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
421116491Sharti		    c0->hdr.chunkno, chunkno));
422116491Sharti
423116491Sharti		m = c0->hdr.mbuf;
424116491Sharti
425116491Sharti	} else {
426116491Sharti		struct mbuf1_chunk *c1;
427116491Sharti
428116491Sharti		c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
429116491Sharti		KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
430116491Sharti		    c1->hdr.pageno, pageno));
431116491Sharti		KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
432116491Sharti		    c1->hdr.chunkno, chunkno));
433116491Sharti
434116491Sharti		m = c1->hdr.mbuf;
435116491Sharti	}
436116491Sharti	MBUF_CLR_BIT(sc->mbuf_pages[pageno]->hdr.card, chunkno);
437116491Sharti	MBUF_SET_BIT(sc->mbuf_pages[pageno]->hdr.used, chunkno);
438116491Sharti
439116491Sharti	bus_dmamap_sync(sc->mbuf_tag, sc->mbuf_pages[pageno]->hdr.map,
440116491Sharti	    BUS_DMASYNC_POSTREAD);
441116491Sharti
442116491Sharti	return (m);
443116491Sharti}
444116491Sharti
445116491Sharti/*
446116491Sharti * Interrupt because of receive buffer returned.
447116491Sharti */
448116491Shartistatic void
449116491Shartihe_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
450116491Sharti{
451116491Sharti	struct he_rbrqen *e;
452116491Sharti	uint32_t flags, tail;
453116491Sharti	u_int cid, len;
454116491Sharti	struct mbuf *m;
455116491Sharti
456116491Sharti	for (;;) {
457116491Sharti		tail = sc->hsp->group[group].rbrq_tail >> 3;
458116491Sharti
459116491Sharti		if (rq->head == tail)
460116491Sharti			break;
461116491Sharti
462116491Sharti		e = &rq->rbrq[rq->head];
463116491Sharti
464116491Sharti		flags = e->addr & HE_REGM_RBRQ_FLAGS;
465116491Sharti		if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
466116491Sharti			m = hatm_rx_buffer(sc, group,
467116491Sharti			    (e->addr & HE_REGM_RBRQ_ADDR) >> HE_REGS_RBRQ_ADDR);
468116491Sharti		else
469116491Sharti			m = NULL;
470116491Sharti
471116491Sharti		cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
472116491Sharti		len = 4 * (e->len & HE_REGM_RBRQ_LEN);
473116491Sharti
474116491Sharti		hatm_rx(sc, cid, flags, m, len);
475116491Sharti
476116491Sharti		if (++rq->head == rq->size)
477116491Sharti			rq->head = 0;
478116491Sharti	}
479116491Sharti	WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
480116491Sharti}
481116491Sharti
482116491Shartivoid
483116491Shartihatm_intr(void *p)
484116491Sharti{
485116491Sharti	struct heirq *q = p;
486116491Sharti	struct hatm_softc *sc = q->sc;
487116491Sharti	u_int status;
488116491Sharti	u_int tail;
489116491Sharti
490116491Sharti	/* if we have a stray interrupt with a non-initialized card,
491116491Sharti	 * we cannot even lock before looking at the flag */
492116491Sharti	if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING))
493116491Sharti		return;
494116491Sharti
495116491Sharti	mtx_lock(&sc->mtx);
496116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
497116491Sharti
498116491Sharti	tail = *q->tailp;
499116491Sharti	if (q->head == tail) {
500116491Sharti		/* workaround for tail pointer not updated bug (8.1.1) */
501116491Sharti		DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
502116491Sharti
503116491Sharti		/* read the tail pointer from the card */
504116491Sharti		tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
505116491Sharti		    HE_REGM_IRQ_BASE_TAIL;
506116491Sharti		BARRIER_R(sc);
507116491Sharti
508116491Sharti		sc->istats.bug_no_irq_upd++;
509116491Sharti	}
510116491Sharti
511116491Sharti	/* clear the interrupt */
512116491Sharti	WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
513116491Sharti	BARRIER_W(sc);
514116491Sharti
515116491Sharti	while (q->head != tail) {
516116491Sharti		status = q->irq[q->head];
517116491Sharti		q->irq[q->head] = HE_REGM_ITYPE_INVALID;
518116491Sharti		if (++q->head == (q->size - 1))
519116491Sharti			q->head = 0;
520116491Sharti
521116491Sharti		switch (status & HE_REGM_ITYPE) {
522116491Sharti
523116491Sharti		  case HE_REGM_ITYPE_TBRQ:
524116491Sharti			DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
525116491Sharti			sc->istats.itype_tbrq++;
526116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
527116491Sharti			break;
528116491Sharti
529116491Sharti		  case HE_REGM_ITYPE_TPD:
530116491Sharti			DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
531116491Sharti			sc->istats.itype_tpd++;
532116491Sharti			he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
533116491Sharti			break;
534116491Sharti
535116491Sharti		  case HE_REGM_ITYPE_RBPS:
536116491Sharti			sc->istats.itype_rbps++;
537116491Sharti			switch (status & HE_REGM_IGROUP) {
538116491Sharti
539116491Sharti			  case 0:
540116491Sharti				he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
541116491Sharti				break;
542116491Sharti
543116491Sharti			  case 1:
544116491Sharti				he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
545116491Sharti				break;
546116491Sharti
547116491Sharti			  default:
548116491Sharti				if_printf(&sc->ifatm.ifnet, "bad INTR RBPS%u\n",
549116491Sharti				    status & HE_REGM_IGROUP);
550116491Sharti				break;
551116491Sharti			}
552116491Sharti			break;
553116491Sharti
554116491Sharti		  case HE_REGM_ITYPE_RBPL:
555116491Sharti			sc->istats.itype_rbpl++;
556116491Sharti			switch (status & HE_REGM_IGROUP) {
557116491Sharti
558116491Sharti			  case 0:
559116491Sharti				he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
560116491Sharti				break;
561116491Sharti
562116491Sharti			  default:
563116491Sharti				if_printf(&sc->ifatm.ifnet, "bad INTR RBPL%u\n",
564116491Sharti				    status & HE_REGM_IGROUP);
565116491Sharti				break;
566116491Sharti			}
567116491Sharti			break;
568116491Sharti
569116491Sharti		  case HE_REGM_ITYPE_RBRQ:
570116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
571116491Sharti			sc->istats.itype_rbrq++;
572116491Sharti			switch (status & HE_REGM_IGROUP) {
573116491Sharti
574116491Sharti			  case 0:
575116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
576116491Sharti				break;
577116491Sharti
578116491Sharti			  case 1:
579116491Sharti				if (sc->rbrq_1.size > 0) {
580116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
581116491Sharti					break;
582116491Sharti				}
583116491Sharti				/* FALLTHRU */
584116491Sharti
585116491Sharti			  default:
586116491Sharti				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQ%u\n",
587116491Sharti				    status & HE_REGM_IGROUP);
588116491Sharti				break;
589116491Sharti			}
590116491Sharti			break;
591116491Sharti
592116491Sharti		  case HE_REGM_ITYPE_RBRQT:
593116491Sharti			DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
594116491Sharti			sc->istats.itype_rbrqt++;
595116491Sharti			switch (status & HE_REGM_IGROUP) {
596116491Sharti
597116491Sharti			  case 0:
598116491Sharti				he_intr_rbrq(sc, &sc->rbrq_0, 0);
599116491Sharti				break;
600116491Sharti
601116491Sharti			  case 1:
602116491Sharti				if (sc->rbrq_1.size > 0) {
603116491Sharti					he_intr_rbrq(sc, &sc->rbrq_1, 1);
604116491Sharti					break;
605116491Sharti				}
606116491Sharti				/* FALLTHRU */
607116491Sharti
608116491Sharti			  default:
609116491Sharti				if_printf(&sc->ifatm.ifnet, "bad INTR RBRQT%u\n",
610116491Sharti				    status & HE_REGM_IGROUP);
611116491Sharti				break;
612116491Sharti			}
613116491Sharti			break;
614116491Sharti
615116491Sharti		  case HE_REGM_ITYPE_PHYS:
616116491Sharti			sc->istats.itype_phys++;
617116491Sharti			utopia_intr(&sc->utopia);
618116491Sharti			break;
619116491Sharti
620116491Sharti#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
621116491Sharti		  case HE_REGM_ITYPE_UNKNOWN:
622116491Sharti			sc->istats.itype_unknown++;
623116491Sharti			if_printf(&sc->ifatm.ifnet, "bad interrupt\n");
624116491Sharti			break;
625116491Sharti#endif
626116491Sharti
627116491Sharti		  case HE_REGM_ITYPE_ERR:
628116491Sharti			sc->istats.itype_err++;
629116491Sharti			switch (status) {
630116491Sharti
631116491Sharti			  case HE_REGM_ITYPE_PERR:
632116491Sharti				if_printf(&sc->ifatm.ifnet, "parity error\n");
633116491Sharti				break;
634116491Sharti
635116491Sharti			  case HE_REGM_ITYPE_ABORT:
636116491Sharti				if_printf(&sc->ifatm.ifnet, "abort interrupt "
637116491Sharti				    "addr=0x%08x\n",
638116491Sharti				    READ4(sc, HE_REGO_ABORT_ADDR));
639116491Sharti				break;
640116491Sharti
641116491Sharti			  default:
642116491Sharti				if_printf(&sc->ifatm.ifnet,
643116491Sharti				    "bad interrupt type %08x\n", status);
644116491Sharti				break;
645116491Sharti			}
646116491Sharti			break;
647116491Sharti
648116491Sharti		  case HE_REGM_ITYPE_INVALID:
649116491Sharti			/* this is the documented fix for the ISW bug 8.1.1
650116491Sharti			 * Note, that the documented fix is partly wrong:
651116491Sharti			 * the ISWs should be intialized to 0xf8 not 0xff */
652116491Sharti			sc->istats.bug_bad_isw++;
653116491Sharti			DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
654116491Sharti			he_intr_tbrq(sc, &sc->tbrq, 0);
655116491Sharti			he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
656116491Sharti			he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
657116491Sharti			he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
658116491Sharti			he_intr_rbrq(sc, &sc->rbrq_0, 0);
659116491Sharti			he_intr_rbrq(sc, &sc->rbrq_1, 1);
660116491Sharti			utopia_intr(&sc->utopia);
661116491Sharti			break;
662116491Sharti
663116491Sharti		  default:
664116491Sharti			if_printf(&sc->ifatm.ifnet, "bad interrupt type %08x\n",
665116491Sharti			    status);
666116491Sharti			break;
667116491Sharti		}
668116491Sharti	}
669116491Sharti
670116491Sharti	/* write back head to clear queue */
671116491Sharti	WRITE4(sc, HE_REGO_IRQ_HEAD(0),
672116491Sharti	    ((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
673116491Sharti	    (q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
674116491Sharti	    (q->head << HE_REGS_IRQ_HEAD_HEAD));
675116491Sharti	BARRIER_W(sc);
676116491Sharti
677116491Sharti	/* workaround the back-to-back irq access problem (8.1.2) */
678116491Sharti	(void)READ4(sc, HE_REGO_INT_FIFO);
679116491Sharti	BARRIER_R(sc);
680116491Sharti
681116491Sharti	mtx_unlock(&sc->mtx);
682116491Sharti}
683