The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/patm/if_patm_intr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/8.3/sys/dev/patm/if_patm_intr.c 203786 2010-02-11 18:34:06Z mjacob $");
   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 
   76 static void patm_feed_sbufs(struct patm_softc *sc);
   77 static void patm_feed_lbufs(struct patm_softc *sc);
   78 static void patm_feed_vbufs(struct patm_softc *sc);
   79 static void patm_intr_tsif(struct patm_softc *sc);
   80 static void patm_intr_raw(struct patm_softc *sc);
   81 
   82 #ifdef PATM_DEBUG
   83 static int patm_mbuf_cnt(u_int unit) __unused;
   84 #endif
   85 
   86 /*
   87  * Write free buf Q
   88  */
   89 static __inline void
   90 patm_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  */
  105 void
  106 patm_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  */
  232 static u_int
  233 patm_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  */
  264 static void
  265 patm_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  */
  292 static void
  293 patm_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  */
  320 static struct lmbuf *
  321 patm_lmbuf_alloc(struct patm_softc *sc)
  322 {
  323         int error;
  324         struct mbuf *m;
  325         struct lmbuf *b;
  326 
  327         m = m_getcl(M_DONTWAIT, 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  */
  355 static void
  356 patm_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  */
  381 static void
  382 patm_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  */
  423 void
  424 patm_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  */
  470 static void
  471 patm_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  */
  514 void
  515 patm_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
  527 static int
  528 patm_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

Cache object: 430b2fe77dbfe057df03e18e595d494d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.