1/*- 2 * Copyright (c) 2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * Author: Hartmut Brandt <harti@freebsd.org> 28 * 29 * Driver for IDT77252 based cards like ProSum's. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include "opt_inet.h" 36#include "opt_natm.h" 37 38#include <sys/types.h> 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/malloc.h> 42#include <sys/kernel.h> 43#include <sys/bus.h> 44#include <sys/errno.h> 45#include <sys/conf.h> 46#include <sys/module.h> 47#include <sys/lock.h> 48#include <sys/mutex.h> 49#include <sys/sysctl.h> 50#include <sys/queue.h> 51#include <sys/condvar.h> 52#include <sys/endian.h> 53#include <vm/uma.h> 54 55#include <sys/sockio.h> 56#include <sys/mbuf.h> 57#include <sys/socket.h> 58 59#include <net/if.h> 60#include <net/if_var.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 <sys/mbpool.h> 72 73#include <dev/utopia/utopia.h> 74#include <dev/patm/idt77252reg.h> 75#include <dev/patm/if_patmvar.h> 76 77static void patm_feed_sbufs(struct patm_softc *sc); 78static void patm_feed_lbufs(struct patm_softc *sc); 79static void patm_feed_vbufs(struct patm_softc *sc); 80static void patm_intr_tsif(struct patm_softc *sc); 81static void patm_intr_raw(struct patm_softc *sc); 82 83#ifdef PATM_DEBUG 84static int patm_mbuf_cnt(u_int unit) __unused; 85#endif 86 87/* 88 * Write free buf Q 89 */ 90static __inline void 91patm_fbq_write(struct patm_softc *sc, u_int queue, uint32_t h0, 92 uint32_t p0, uint32_t h1, uint32_t p1) 93{ 94 patm_debug(sc, FREEQ, "supplying(%u,%#x,%#x,%#x,%#x)", 95 queue, h0, p0, h1, p1); 96 patm_nor_write(sc, IDT_NOR_D0, h0); 97 patm_nor_write(sc, IDT_NOR_D1, p0); 98 patm_nor_write(sc, IDT_NOR_D2, h1); 99 patm_nor_write(sc, IDT_NOR_D3, p1); 100 patm_cmd_exec(sc, IDT_CMD_WFBQ | queue); 101} 102 103/* 104 * Interrupt 105 */ 106void 107patm_intr(void *p) 108{ 109 struct patm_softc *sc = p; 110 uint32_t stat, cfg; 111 u_int cnt; 112 const uint32_t ints = IDT_STAT_TSIF | IDT_STAT_TXICP | IDT_STAT_TSQF | 113 IDT_STAT_TMROF | IDT_STAT_PHYI | IDT_STAT_RSQF | IDT_STAT_EPDU | 114 IDT_STAT_RAWCF | IDT_STAT_RSQAF; 115 const uint32_t fbqa = IDT_STAT_FBQ3A | IDT_STAT_FBQ2A | 116 IDT_STAT_FBQ1A | IDT_STAT_FBQ0A; 117 118 mtx_lock(&sc->mtx); 119 120 stat = patm_nor_read(sc, IDT_NOR_STAT); 121 patm_nor_write(sc, IDT_NOR_STAT, stat & (ints | fbqa)); 122 123 if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 124 /* if we are stopped ack all interrupts and handle PHYI */ 125 if (stat & IDT_STAT_PHYI) { 126 patm_debug(sc, INTR, "PHYI (stopped)"); 127 utopia_intr(&sc->utopia); 128 } 129 mtx_unlock(&sc->mtx); 130 return; 131 } 132 133 patm_debug(sc, INTR, "stat=%08x", stat); 134 135 /* 136 * If the buffer queues are empty try to fill them. If this fails 137 * disable the interrupt. Otherwise enable the interrupt. 138 */ 139 if (stat & fbqa) { 140 cfg = patm_nor_read(sc, IDT_NOR_CFG); 141 if (stat & IDT_STAT_FBQ0A) 142 patm_feed_sbufs(sc); 143 if (stat & IDT_STAT_FBQ1A) 144 patm_feed_lbufs(sc); 145 if (stat & IDT_STAT_FBQ2A) { 146 /* 147 * Workaround for missing interrupt on AAL0. Check the 148 * receive status queue if the FBQ2 is not full. 149 */ 150 patm_intr_rsq(sc); 151 patm_feed_vbufs(sc); 152 } 153 if ((patm_nor_read(sc, IDT_NOR_STAT) & fbqa) && 154 (cfg & IDT_CFG_FBIE)) { 155 /* failed */ 156 patm_nor_write(sc, IDT_NOR_CFG, cfg & ~IDT_CFG_FBIE); 157 patm_printf(sc, "out of buffers -- intr disabled\n"); 158 } else if (!(cfg & IDT_CFG_FBIE)) { 159 patm_printf(sc, "bufQ intr re-enabled\n"); 160 patm_nor_write(sc, IDT_NOR_CFG, cfg | IDT_CFG_FBIE); 161 } 162 patm_nor_write(sc, IDT_NOR_STAT, fbqa); 163 } 164 165 cnt = 0; 166 while ((stat & ints) != 0) { 167 if (++cnt == 200) { 168 patm_printf(sc, "%s: excessive interrupts\n", __func__); 169 patm_stop(sc); 170 break; 171 } 172 if (stat & IDT_STAT_TSIF) { 173 patm_debug(sc, INTR, "TSIF"); 174 patm_intr_tsif(sc); 175 } 176 if (stat & IDT_STAT_TXICP) { 177 patm_printf(sc, "incomplete PDU transmitted\n"); 178 } 179 if (stat & IDT_STAT_TSQF) { 180 patm_printf(sc, "TSQF\n"); 181 patm_intr_tsif(sc); 182 } 183 if (stat & IDT_STAT_TMROF) { 184 patm_debug(sc, INTR, "TMROF"); 185 patm_intr_tsif(sc); 186 } 187 if (stat & IDT_STAT_PHYI) { 188 patm_debug(sc, INTR, "PHYI"); 189 utopia_intr(&sc->utopia); 190 } 191 if (stat & IDT_STAT_RSQF) { 192 patm_printf(sc, "RSQF\n"); 193 patm_intr_rsq(sc); 194 } 195 if (stat & IDT_STAT_EPDU) { 196 patm_debug(sc, INTR, "EPDU"); 197 patm_intr_rsq(sc); 198 } 199 if (stat & IDT_STAT_RAWCF) { 200 patm_debug(sc, INTR, "RAWCF"); 201 patm_intr_raw(sc); 202 } 203 if (stat & IDT_STAT_RSQAF) { 204 patm_debug(sc, INTR, "RSQAF"); 205 patm_intr_rsq(sc); 206 } else if (IDT_STAT_FRAC2(stat) != 0xf) { 207 /* 208 * Workaround for missing interrupt on AAL0. Check the 209 * receive status queue if the FBQ2 is not full. 210 */ 211 patm_intr_rsq(sc); 212 } 213 214 stat = patm_nor_read(sc, IDT_NOR_STAT); 215 patm_nor_write(sc, IDT_NOR_STAT, ints & stat); 216 patm_debug(sc, INTR, "stat=%08x", stat); 217 } 218 219 mtx_unlock(&sc->mtx); 220 221 patm_debug(sc, INTR, "... exit"); 222} 223 224/* 225 * Compute the amount of buffers to feed into a given free buffer queue 226 * 227 * Feeding buffers is actually not so easy as it seems. We cannot use the 228 * fraction fields in the status registers, because they round down, i.e. 229 * if we have 34 buffers in the queue, it will show 1. If we now feed 230 * 512 - 1 * 32 buffers, we lose two buffers. The only reliable way to know 231 * how many buffers are in the queue are the FBQP registers. 232 */ 233static u_int 234patm_feed_cnt(struct patm_softc *sc, u_int q) 235{ 236 u_int w, r, reg; 237 u_int feed; 238 int free; 239 240 /* get the FBQ read and write pointers */ 241 reg = patm_nor_read(sc, IDT_NOR_FBQP0 + 4 * q); 242 r = (reg & 0x7ff) >> 1; 243 w = ((reg >> 16) & 0x7ff) >> 1; 244 /* compute amount of free buffers */ 245 if ((free = w - r) < 0) 246 free += 0x400; 247 KASSERT(free <= 512, ("bad FBQP 0x%x", reg)); 248 feed = 512 - free; 249 250 /* can only feed pairs of buffers */ 251 feed &= ~1; 252 253 if (feed > 0) 254 feed -= 2; 255 256 patm_debug(sc, FREEQ, "feeding %u buffers into queue %u", feed, q); 257 258 return (feed); 259} 260 261/* 262 * Feed small buffers into buffer queue 0 263 * 264 */ 265static void 266patm_feed_sbufs(struct patm_softc *sc) 267{ 268 u_int feed; 269 bus_addr_t p0, p1; 270 void *v0; 271 uint32_t h0, h1; 272 273 feed = patm_feed_cnt(sc, 0); 274 275 while (feed > 0) { 276 if ((v0 = mbp_alloc(sc->sbuf_pool, &p0, &h0)) == NULL) 277 break; 278 if (mbp_alloc(sc->sbuf_pool, &p1, &h1) == NULL) { 279 mbp_free(sc->sbuf_pool, v0); 280 break; 281 } 282 patm_fbq_write(sc, 0, 283 h0 | MBUF_SHANDLE, (p0 + SMBUF_OFFSET), 284 h1 | MBUF_SHANDLE, (p1 + SMBUF_OFFSET)); 285 286 feed -= 2; 287 } 288} 289 290/* 291 * Feed small buffers into buffer queue 0 292 */ 293static void 294patm_feed_vbufs(struct patm_softc *sc) 295{ 296 u_int feed; 297 bus_addr_t p0, p1; 298 void *v0; 299 uint32_t h0, h1; 300 301 feed = patm_feed_cnt(sc, 2); 302 303 while (feed > 0) { 304 if ((v0 = mbp_alloc(sc->vbuf_pool, &p0, &h0)) == NULL) 305 break; 306 if (mbp_alloc(sc->vbuf_pool, &p1, &h1) == NULL) { 307 mbp_free(sc->vbuf_pool, v0); 308 break; 309 } 310 patm_fbq_write(sc, 2, 311 h0 | MBUF_VHANDLE, (p0 + VMBUF_OFFSET), 312 h1 | MBUF_VHANDLE, (p1 + VMBUF_OFFSET)); 313 314 feed -= 2; 315 } 316} 317 318/* 319 * Allocate a large buffer 320 */ 321static struct lmbuf * 322patm_lmbuf_alloc(struct patm_softc *sc) 323{ 324 int error; 325 struct mbuf *m; 326 struct lmbuf *b; 327 328 m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 329 if (m == NULL) 330 return (NULL); 331 m->m_data += LMBUF_OFFSET; 332 333 if ((b = SLIST_FIRST(&sc->lbuf_free_list)) == NULL) { 334 m_freem(m); 335 return (NULL); 336 } 337 338 b->phy = 0; /* alignment */ 339 error = bus_dmamap_load(sc->lbuf_tag, b->map, m->m_data, LMBUF_SIZE, 340 patm_load_callback, &b->phy, BUS_DMA_NOWAIT); 341 if (error) { 342 patm_printf(sc, "%s -- bus_dmamap_load: %d\n", __func__, error); 343 m_free(m); 344 return (NULL); 345 } 346 347 SLIST_REMOVE_HEAD(&sc->lbuf_free_list, link); 348 b->m = m; 349 350 return (b); 351} 352 353/* 354 * Feed large buffers into buffer queue 1 355 */ 356static void 357patm_feed_lbufs(struct patm_softc *sc) 358{ 359 u_int feed; 360 struct lmbuf *b0, *b1; 361 362 feed = patm_feed_cnt(sc, 1); 363 364 while (feed > 0) { 365 if ((b0 = patm_lmbuf_alloc(sc)) == NULL) 366 break; 367 if ((b1 = patm_lmbuf_alloc(sc)) == NULL) { 368 patm_lbuf_free(sc, b0); 369 break; 370 } 371 patm_fbq_write(sc, 1, 372 LMBUF_HANDLE | b0->handle, b0->phy, 373 LMBUF_HANDLE | b1->handle, b1->phy); 374 375 feed -= 2; 376 } 377} 378 379/* 380 * Handle transmit status interrupt 381 */ 382static void 383patm_intr_tsif(struct patm_softc *sc) 384{ 385 struct idt_tsqe *tsqe = sc->tsq_next; 386 struct idt_tsqe *prev = NULL; 387 uint32_t stamp; 388 389 stamp = le32toh(tsqe->stamp); 390 if (stamp & IDT_TSQE_EMPTY) 391 return; 392 393 do { 394 switch (IDT_TSQE_TYPE(stamp)) { 395 396 case IDT_TSQE_TBD: 397 patm_tx(sc, stamp, le32toh(tsqe->stat)); 398 break; 399 400 case IDT_TSQE_IDLE: 401 patm_tx_idle(sc, le32toh(tsqe->stat)); 402 break; 403 } 404 405 /* recycle */ 406 tsqe->stat = 0; 407 tsqe->stamp = htole32(IDT_TSQE_EMPTY); 408 409 /* save pointer to this entry and advance */ 410 prev = tsqe; 411 if (++tsqe == &sc->tsq[IDT_TSQ_SIZE]) 412 tsqe = &sc->tsq[0]; 413 414 stamp = le32toh(tsqe->stamp); 415 } while (!(stamp & IDT_TSQE_EMPTY)); 416 417 sc->tsq_next = tsqe; 418 patm_nor_write(sc, IDT_NOR_TSQH, ((prev - sc->tsq) << IDT_TSQE_SHIFT)); 419} 420 421/* 422 * Handle receive interrupt 423 */ 424void 425patm_intr_rsq(struct patm_softc *sc) 426{ 427 struct idt_rsqe *rsqe; 428 u_int stat; 429 430 if (sc->rsq_last + 1 == PATM_RSQ_SIZE) 431 rsqe = &sc->rsq[0]; 432 else 433 rsqe = &sc->rsq[sc->rsq_last + 1]; 434 stat = le32toh(rsqe->stat); 435 if (!(stat & IDT_RSQE_VALID)) 436 return; 437 438 while (stat & IDT_RSQE_VALID) { 439 patm_rx(sc, rsqe); 440 441 /* recycle RSQE */ 442 rsqe->cid = 0; 443 rsqe->handle = 0; 444 rsqe->crc = 0; 445 rsqe->stat = 0; 446 447 /* save pointer to this entry and advance */ 448 if (++sc->rsq_last == PATM_RSQ_SIZE) 449 sc->rsq_last = 0; 450 if (++rsqe == &sc->rsq[PATM_RSQ_SIZE]) 451 rsqe = sc->rsq; 452 453 stat = le32toh(rsqe->stat); 454 } 455 456 patm_nor_write(sc, IDT_NOR_RSQH, sc->rsq_phy | (sc->rsq_last << 2)); 457 458 patm_feed_sbufs(sc); 459 patm_feed_lbufs(sc); 460 patm_feed_vbufs(sc); 461} 462 463/* 464 * Handle raw cell receive. 465 * 466 * Note that the description on page 3-8 is wrong. The RAWHND contains not 467 * the same value as RAWCT. RAWCT points to the next address the chip is 468 * going to write to whike RAWHND points to the last cell's address the chip 469 * has written to. 470 */ 471static void 472patm_intr_raw(struct patm_softc *sc) 473{ 474 uint32_t tail; 475 uint32_t h, *cell; 476 477#ifdef notyet 478 bus_dma_sync_size(sc->sq_tag, sc->sq_map, IDT_TSQ_SIZE * IDT_TSQE_SIZE + 479 PATM_RSQ_SIZE * IDT_RSQE_SIZE, sizeof(*sc->rawhnd), 480 BUS_DMASYNC_POSTREAD); 481#endif 482 /* first turn */ 483 if (sc->rawh == NULL) { 484 sc->rawh = &sc->lbufs[le32toh(sc->rawhnd->handle) & MBUF_HMASK]; 485 } 486 tail = le32toh(sc->rawhnd->tail); 487 if (tail == sc->rawh->phy) 488 /* not really a raw interrupt */ 489 return; 490 491 while (tail + 64 != sc->rawh->phy + sc->rawi * 64) { 492#ifdef notyet 493 bus_dmamap_sync_size(sc->lbuf_tag, sc->rawh->map, 494 sc->rawi * 64, 64, BUS_DMASYNC_POSTREAD); 495#endif 496 cell = (uint32_t *)(mtod(sc->rawh->m, u_char *) + 497 sc->rawi * 64); 498 if (sc->rawi == (LMBUF_SIZE / 64) - 1) { 499 /* chain */ 500 h = le32toh(cell[1]); 501 patm_lbuf_free(sc, sc->rawh); 502 sc->rawh = &sc->lbufs[h & MBUF_HMASK]; 503 sc->rawi = 0; 504 continue; 505 } 506 507 patm_rx_raw(sc, (u_char *)cell); 508 sc->rawi++; 509 } 510} 511 512/* 513 * Free a large mbuf. This is called by us. 514 */ 515void 516patm_lbuf_free(struct patm_softc *sc, struct lmbuf *b) 517{ 518 519 bus_dmamap_unload(sc->lbuf_tag, b->map); 520 if (b->m != NULL) { 521 m_free(b->m); 522 b->m = NULL; 523 } 524 SLIST_INSERT_HEAD(&sc->lbuf_free_list, b, link); 525} 526 527#ifdef PATM_DEBUG 528static int 529patm_mbuf_cnt(u_int unit) 530{ 531 devclass_t dc; 532 struct patm_softc *sc; 533 u_int used, card, free; 534 535 dc = devclass_find("patm"); 536 if (dc == NULL) { 537 printf("%s: can't find devclass\n", __func__); 538 return (0); 539 } 540 sc = devclass_get_softc(dc, unit); 541 if (sc == NULL) { 542 printf("%s: invalid unit number: %d\n", __func__, unit); 543 return (0); 544 } 545 546 mbp_count(sc->sbuf_pool, &used, &card, &free); 547 printf("sbufs: %u on card, %u used, %u free\n", card, used, free); 548 549 mbp_count(sc->vbuf_pool, &used, &card, &free); 550 printf("aal0 bufs: %u on card, %u used, %u free\n", card, used, free); 551 552 return (0); 553} 554#endif 555