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: stable/10/sys/dev/patm/if_patm_intr.c 243857 2012-12-04 09:32:43Z glebius $");
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
patm_fbq_write(struct patm_softc * sc,u_int queue,uint32_t h0,uint32_t p0,uint32_t h1,uint32_t p1)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
patm_intr(void * p)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
patm_feed_cnt(struct patm_softc * sc,u_int q)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
patm_feed_sbufs(struct patm_softc * sc)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
patm_feed_vbufs(struct patm_softc * sc)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 *
patm_lmbuf_alloc(struct patm_softc * sc)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
patm_feed_lbufs(struct patm_softc * sc)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
patm_intr_tsif(struct patm_softc * sc)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
patm_intr_rsq(struct patm_softc * sc)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
patm_intr_raw(struct patm_softc * sc)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
patm_lbuf_free(struct patm_softc * sc,struct lmbuf * b)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
patm_mbuf_cnt(u_int unit)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
554