]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/patm/if_patm_intr.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / patm / if_patm_intr.c
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
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_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  */
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