xref: /freebsd-11-stable/sys/dev/patm/if_patm_intr.c (revision 3b8f08459569bf0faa21473e5cec2491e95c9349)
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 
77 static void patm_feed_sbufs(struct patm_softc *sc);
78 static void patm_feed_lbufs(struct patm_softc *sc);
79 static void patm_feed_vbufs(struct patm_softc *sc);
80 static void patm_intr_tsif(struct patm_softc *sc);
81 static void patm_intr_raw(struct patm_softc *sc);
82 
83 #ifdef PATM_DEBUG
84 static int patm_mbuf_cnt(u_int unit) __unused;
85 #endif
86 
87 /*
88  * Write free buf Q
89  */
90 static __inline void
patm_fbq_write(struct patm_softc * sc,u_int queue,uint32_t h0,uint32_t p0,uint32_t h1,uint32_t p1)91 patm_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  */
106 void
patm_intr(void * p)107 patm_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  */
233 static u_int
patm_feed_cnt(struct patm_softc * sc,u_int q)234 patm_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  */
265 static void
patm_feed_sbufs(struct patm_softc * sc)266 patm_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  */
293 static void
patm_feed_vbufs(struct patm_softc * sc)294 patm_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  */
321 static struct lmbuf *
patm_lmbuf_alloc(struct patm_softc * sc)322 patm_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  */
356 static void
patm_feed_lbufs(struct patm_softc * sc)357 patm_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  */
382 static void
patm_intr_tsif(struct patm_softc * sc)383 patm_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  */
424 void
patm_intr_rsq(struct patm_softc * sc)425 patm_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  */
471 static void
patm_intr_raw(struct patm_softc * sc)472 patm_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  */
515 void
patm_lbuf_free(struct patm_softc * sc,struct lmbuf * b)516 patm_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
528 static int
patm_mbuf_cnt(u_int unit)529 patm_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