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