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
77static void patm_feed_sbufs(struct patm_softc *sc);
78static void patm_feed_lbufs(struct patm_softc *sc);
79static void patm_feed_vbufs(struct patm_softc *sc);
80static void patm_intr_tsif(struct patm_softc *sc);
81static void patm_intr_raw(struct patm_softc *sc);
82
83#ifdef PATM_DEBUG
84static int patm_mbuf_cnt(u_int unit) __unused;
85#endif
86
87/*
88 * Write free buf Q
89 */
90static __inline void
91patm_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 */
106void
107patm_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 */
233static u_int
234patm_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 */
265static void
266patm_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 */
293static void
294patm_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 */
321static struct lmbuf *
322patm_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 */
356static void
357patm_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 */
382static void
383patm_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 */
424void
425patm_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 */
471static void
472patm_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 */
515void
516patm_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
528static int
529patm_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