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