if_hatm_tx.c revision 117382
1/*
2 * Copyright (c) 2001-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 * ForeHE driver.
30 *
31 * Transmission.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_tx.c 117382 2003-07-10 13:55:09Z harti $");
36
37#include "opt_inet.h"
38#include "opt_natm.h"
39
40#include <sys/types.h>
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/bus.h>
46#include <sys/errno.h>
47#include <sys/conf.h>
48#include <sys/module.h>
49#include <sys/queue.h>
50#include <sys/syslog.h>
51#include <sys/condvar.h>
52#include <sys/sysctl.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#ifdef ENABLE_BPF
64#include <net/bpf.h>
65#endif
66#include <netinet/in.h>
67#include <netinet/if_atm.h>
68
69#include <machine/bus.h>
70#include <machine/resource.h>
71#include <sys/bus.h>
72#include <sys/rman.h>
73#include <pci/pcireg.h>
74#include <pci/pcivar.h>
75
76#include <dev/utopia/utopia.h>
77#include <dev/hatm/if_hatmconf.h>
78#include <dev/hatm/if_hatmreg.h>
79#include <dev/hatm/if_hatmvar.h>
80
81/*
82 * Allocate a new TPD, zero the TPD part. Cannot return NULL if
83 * flag is 0. The TPD is removed from the free list and its used
84 * bit is set.
85 */
86static struct tpd *
87hatm_alloc_tpd(struct hatm_softc *sc, u_int flags)
88{
89	struct tpd *t;
90
91	/* if we allocate a transmit TPD check for the reserve */
92	if (flags & M_NOWAIT) {
93		if (sc->tpd_nfree <= HE_CONFIG_TPD_RESERVE)
94			return (NULL);
95	} else {
96		if (sc->tpd_nfree == 0)
97			return (NULL);
98	}
99
100	/* make it beeing used */
101	t = SLIST_FIRST(&sc->tpd_free);
102	KASSERT(t != NULL, ("tpd botch"));
103	SLIST_REMOVE_HEAD(&sc->tpd_free, link);
104	TPD_SET_USED(sc, t->no);
105	sc->tpd_nfree--;
106
107	/* initialize */
108	t->mbuf = NULL;
109	t->cid = 0;
110	bzero(&t->tpd, sizeof(t->tpd));
111	t->tpd.addr = t->no << HE_REGS_TPD_ADDR;
112
113	return (t);
114}
115
116/*
117 * Free a TPD. If the mbuf pointer in that TPD is not zero, it is assumed, that
118 * the DMA map of this TPD was used to load this mbuf. The map is unloaded
119 * and the mbuf is freed. The TPD is put back onto the free list and
120 * its used bit is cleared.
121 */
122static void
123hatm_free_tpd(struct hatm_softc *sc, struct tpd *tpd)
124{
125	if (tpd->mbuf != NULL) {
126		bus_dmamap_unload(sc->tx_tag, tpd->map);
127		m_freem(tpd->mbuf);
128		tpd->mbuf = NULL;
129	}
130
131	/* insert TPD into free list */
132	SLIST_INSERT_HEAD(&sc->tpd_free, tpd, link);
133	TPD_CLR_USED(sc, tpd->no);
134	sc->tpd_nfree++;
135}
136
137/*
138 * Queue a number of TPD. If there is not enough space none of the TPDs
139 * is queued and an error code is returned.
140 */
141static int
142hatm_queue_tpds(struct hatm_softc *sc, u_int count, struct tpd **list,
143    u_int cid)
144{
145	u_int space;
146	u_int i;
147
148	if (count >= sc->tpdrq.size) {
149		sc->istats.tdprq_full++;
150		return (EBUSY);
151	}
152
153	if (sc->tpdrq.tail < sc->tpdrq.head)
154		space = sc->tpdrq.head - sc->tpdrq.tail;
155	else
156		space = sc->tpdrq.head - sc->tpdrq.tail +  sc->tpdrq.size;
157
158	if (space <= count) {
159		sc->tpdrq.head =
160		    (READ4(sc, HE_REGO_TPDRQ_H) >> HE_REGS_TPDRQ_H_H) &
161		    (sc->tpdrq.size - 1);
162
163		if (sc->tpdrq.tail < sc->tpdrq.head)
164			space = sc->tpdrq.head - sc->tpdrq.tail;
165		else
166			space = sc->tpdrq.head - sc->tpdrq.tail +
167			    sc->tpdrq.size;
168
169		if (space <= count) {
170			if_printf(&sc->ifatm.ifnet, "TPDRQ full\n");
171			sc->istats.tdprq_full++;
172			return (EBUSY);
173		}
174	}
175
176	/* we are going to write to the TPD queue space */
177	bus_dmamap_sync(sc->tpdrq.mem.tag, sc->tpdrq.mem.map,
178	    BUS_DMASYNC_PREWRITE);
179
180	/* put the entries into the TPD space */
181	for (i = 0; i < count; i++) {
182		/* we are going to 'write' the TPD to the device */
183		bus_dmamap_sync(sc->tpds.tag, sc->tpds.map,
184		    BUS_DMASYNC_PREWRITE);
185
186		sc->tpdrq.tpdrq[sc->tpdrq.tail].tpd =
187		    sc->tpds.paddr + HE_TPD_SIZE * list[i]->no;
188		sc->tpdrq.tpdrq[sc->tpdrq.tail].cid = cid;
189
190		if (++sc->tpdrq.tail == sc->tpdrq.size)
191			sc->tpdrq.tail = 0;
192	}
193
194	/* update tail pointer */
195	WRITE4(sc, HE_REGO_TPDRQ_T, (sc->tpdrq.tail << HE_REGS_TPDRQ_T_T));
196
197	return (0);
198}
199
200/*
201 * Helper struct for communication with the DMA load helper.
202 */
203struct load_txbuf_arg {
204	struct hatm_softc *sc;
205	struct tpd *first;
206	struct mbuf *mbuf;
207	struct hevcc *vcc;
208	int error;
209	u_int pti;
210	u_int vpi, vci;
211};
212
213/*
214 * Loader callback for the mbuf. This function allocates the TPDs and
215 * fills them. It puts the dmamap and and the mbuf pointer into the last
216 * TPD and then tries to queue all the TPDs. If anything fails, all TPDs
217 * allocated by this function are freed and the error flag is set in the
218 * argument structure. The first TPD must then be freed by the caller.
219 */
220static void
221hatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg,
222    bus_size_t mapsize, int error)
223{
224	struct load_txbuf_arg *arg = uarg;
225	u_int tpds_needed, i, n, tpd_cnt;
226	int need_intr;
227	struct tpd *tpd;
228	struct tpd *tpd_list[HE_CONFIG_MAX_TPD_PER_PACKET];
229
230	if (error != 0) {
231		DBG(arg->sc, DMA, ("%s -- error=%d plen=%d\n",
232		    __func__, error, arg->mbuf->m_pkthdr.len));
233		return;
234	}
235
236	/* ensure, we have enough TPDs (remember, we already have one) */
237	tpds_needed = (nseg + 2) / 3;
238	if (HE_CONFIG_TPD_RESERVE + tpds_needed - 1 > arg->sc->tpd_nfree) {
239		if_printf(&arg->sc->ifatm.ifnet, "%s -- out of TPDs (need %d, "
240		    "have %u)\n", __func__, tpds_needed - 1,
241		    arg->sc->tpd_nfree + 1);
242		arg->error = 1;
243		return;
244	}
245
246	/*
247	 * Check for the maximum number of TPDs on the connection.
248	 */
249	need_intr = 0;
250	if (arg->sc->max_tpd > 0) {
251		if (arg->vcc->ntpds + tpds_needed > arg->sc->max_tpd) {
252			arg->sc->istats.flow_closed++;
253			arg->vcc->vflags |= HE_VCC_FLOW_CTRL;
254#ifdef notyet
255			atm_message(&arg->sc->ifatm.ifnet, ATM_MSG_FLOW_CONTROL,
256			   (1 << 24) | (arg->vpi << 16) | arg->vci);
257#endif
258			arg->error = 1;
259			return;
260		}
261		if (arg->vcc->ntpds + tpds_needed >
262		    (9 * arg->sc->max_tpd) / 10)
263			need_intr = 1;
264	}
265
266	tpd = arg->first;
267	tpd_cnt = 0;
268	tpd_list[tpd_cnt++] = tpd;
269	for (i = n = 0; i < nseg; i++, n++) {
270		if (n == 3) {
271			if ((tpd = hatm_alloc_tpd(arg->sc, M_NOWAIT)) == NULL)
272				/* may not fail (see check above) */
273				panic("%s: out of TPDs", __func__);
274			tpd->cid = arg->first->cid;
275			tpd->tpd.addr |= arg->pti;
276			tpd_list[tpd_cnt++] = tpd;
277			n = 0;
278		}
279		KASSERT(segs[i].ds_addr <= 0xffffffffLU,
280		    ("phys addr too large %lx", (u_long)segs[i].ds_addr));
281
282		DBG(arg->sc, DMA, ("DMA loaded: %lx/%lu",
283		    (u_long)segs[i].ds_addr, (u_long)segs[i].ds_len));
284
285		tpd->tpd.bufs[n].addr = segs[i].ds_addr;
286		tpd->tpd.bufs[n].len = segs[i].ds_len;
287
288		DBG(arg->sc, TX, ("seg[%u]=tpd[%u,%u]=%x/%u", i,
289		    tpd_cnt, n, tpd->tpd.bufs[n].addr, tpd->tpd.bufs[n].len));
290
291		if (i == nseg - 1)
292			tpd->tpd.bufs[n].len |= HE_REGM_TPD_LST;
293	}
294
295	/*
296	 * Swap the MAP in the first and the last TPD and set the mbuf
297	 * pointer into the last TPD. We use the map in the last TPD, because
298	 * the map must stay valid until the last TPD is processed by the card.
299	 */
300	if (tpd_cnt > 1) {
301		bus_dmamap_t tmp;
302
303		tmp = arg->first->map;
304		arg->first->map = tpd_list[tpd_cnt - 1]->map;
305		tpd_list[tpd_cnt - 1]->map = tmp;
306	}
307	tpd_list[tpd_cnt - 1]->mbuf = arg->mbuf;
308
309	if (need_intr)
310		tpd_list[tpd_cnt - 1]->tpd.addr |= HE_REGM_TPD_INTR;
311
312	/* queue the TPDs */
313	if (hatm_queue_tpds(arg->sc, tpd_cnt, tpd_list, arg->first->cid)) {
314		/* free all, except the first TPD */
315		for (i = 1; i < tpd_cnt; i++)
316			hatm_free_tpd(arg->sc, tpd_list[i]);
317		arg->error = 1;
318		return;
319	}
320	arg->vcc->ntpds += tpd_cnt;
321}
322
323
324/*
325 * Start output on the interface
326 *
327 * For raw aal we process only the first cell in the mbuf chain! XXX
328 */
329void
330hatm_start(struct ifnet *ifp)
331{
332	struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
333	struct mbuf *m;
334	struct atm_pseudohdr *aph;
335	u_int cid;
336	struct tpd *tpd;
337	struct load_txbuf_arg arg;
338	u_int len;
339	int error;
340
341	if (!(ifp->if_flags & IFF_RUNNING))
342		return;
343	mtx_lock(&sc->mtx);
344	arg.sc = sc;
345
346	while (1) {
347		IF_DEQUEUE(&ifp->if_snd, m);
348		if (m == NULL)
349			break;
350
351		if (m->m_len < sizeof(*aph))
352			if ((m = m_pullup(m, sizeof(*aph))) == NULL)
353				continue;
354
355		aph = mtod(m, struct atm_pseudohdr *);
356		arg.vci = ATM_PH_VCI(aph);
357		arg.vpi = ATM_PH_VPI(aph);
358		m_adj(m, sizeof(*aph));
359
360		if ((len = m->m_pkthdr.len) == 0) {
361			m_freem(m);
362			continue;
363		}
364
365		if ((arg.vpi & ~HE_VPI_MASK) || (arg.vci & ~HE_VCI_MASK) ||
366		    (arg.vci == 0)) {
367			m_freem(m);
368			continue;
369		}
370		cid = HE_CID(arg.vpi, arg.vci);
371		arg.vcc = sc->vccs[cid];
372
373		if (arg.vcc == NULL || !(arg.vcc->vflags & HE_VCC_OPEN)) {
374			m_freem(m);
375			continue;
376		}
377		if (arg.vcc->vflags & HE_VCC_FLOW_CTRL) {
378			m_freem(m);
379			sc->istats.flow_drop++;
380			continue;
381		}
382
383		arg.pti = 0;
384		if (arg.vcc->param.aal == ATMIO_AAL_RAW) {
385			if (len < 52) {
386				m_freem(m);
387				continue;
388			}
389			if (len > 52) {
390				m_adj(m, -((int)(len - 52)));
391				len = 52;
392			}
393			if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL)
394				continue;
395
396			/* ignore header except payload type and CLP */
397			arg.pti = mtod(m, u_char *)[3] & 0xf;
398			arg.pti = ((arg.pti & 0xe) << 2) | ((arg.pti & 1) << 1);
399			m_adj(m, 4);
400			len -= 4;
401		}
402
403#ifdef ENABLE_BPF
404		if (!(arg.vcc->param.flags & ATMIO_FLAG_NG) &&
405		    (arg.vcc->param.flags & ATM_PH_AAL5) &&
406		    (arg.vcc->param.flags & ATM_PH_LLCSNAP))
407		 	BPF_MTAP(ifp, m);
408#endif
409
410		/* Now load a DMA map with the packet. Allocate the first
411		 * TPD to get a map. Additional TPDs may be allocated by the
412		 * callback. */
413		if ((tpd = hatm_alloc_tpd(sc, M_NOWAIT)) == NULL) {
414			m_freem(m);
415			sc->ifatm.ifnet.if_oerrors++;
416			continue;
417		}
418		tpd->cid = cid;
419		tpd->tpd.addr |= arg.pti;
420		arg.first = tpd;
421		arg.error = 0;
422		arg.mbuf = m;
423
424		error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
425		    hatm_load_txbuf, &arg, BUS_DMA_NOWAIT);
426
427		if (error == EFBIG) {
428			/* try to defragment the packet */
429			sc->istats.defrag++;
430			m = m_defrag(m, M_DONTWAIT);
431			if (m == NULL) {
432				sc->ifatm.ifnet.if_oerrors++;
433				continue;
434			}
435			arg.mbuf = m;
436			error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
437			    hatm_load_txbuf, &arg, BUS_DMA_NOWAIT);
438		}
439
440		if (error != 0) {
441			if_printf(&sc->ifatm.ifnet, "mbuf loaded error=%d\n",
442			    error);
443			hatm_free_tpd(sc, tpd);
444			sc->ifatm.ifnet.if_oerrors++;
445			continue;
446		}
447		if (arg.error) {
448			hatm_free_tpd(sc, tpd);
449			sc->ifatm.ifnet.if_oerrors++;
450			continue;
451		}
452		arg.vcc->opackets++;
453		arg.vcc->obytes += len;
454		sc->ifatm.ifnet.if_opackets++;
455	}
456	mtx_unlock(&sc->mtx);
457}
458
459void
460hatm_tx_complete(struct hatm_softc *sc, struct tpd *tpd, uint32_t flags)
461{
462	struct hevcc *vcc = sc->vccs[tpd->cid];
463
464	DBG(sc, TX, ("tx_complete cid=%#x flags=%#x", tpd->cid, flags));
465
466	if (vcc == NULL)
467		return;
468	if ((flags & HE_REGM_TBRQ_EOS) && (vcc->vflags & HE_VCC_TX_CLOSING)) {
469		vcc->vflags &= ~HE_VCC_TX_CLOSING;
470		if (vcc->vflags & HE_VCC_ASYNC) {
471			hatm_tx_vcc_closed(sc, tpd->cid);
472			if (!(vcc->vflags & HE_VCC_OPEN)) {
473				hatm_vcc_closed(sc, tpd->cid);
474				vcc = NULL;
475			}
476		} else
477			cv_signal(&sc->vcc_cv);
478	}
479	hatm_free_tpd(sc, tpd);
480
481	if (vcc == NULL)
482		return;
483
484	vcc->ntpds--;
485
486	if ((vcc->vflags & HE_VCC_FLOW_CTRL) &&
487	    vcc->ntpds <= HE_CONFIG_TPD_FLOW_ENB) {
488		vcc->vflags &= ~HE_VCC_FLOW_CTRL;
489#ifdef notyet
490		atm_message(&sc->ifatm.ifnet, ATM_MSG_FLOW_CONTROL,
491		   (0 << 24) | (HE_VPI(tpd->cid) << 16) | HE_VCI(tpd->cid));
492#endif
493	}
494}
495
496/*
497 * Convert CPS to Rate for a rate group
498 */
499static u_int
500cps_to_rate(struct hatm_softc *sc, uint32_t cps)
501{
502	u_int clk = sc->he622 ? HE_622_CLOCK : HE_155_CLOCK;
503	u_int period, rate;
504
505	/* how many double ticks between two cells */
506	period = (clk + 2 * cps - 1) / (2 * cps);
507	rate = hatm_cps2atmf(period);
508	if (hatm_atmf2cps(rate) < period)
509		rate++;
510
511	return (rate);
512}
513
514/*
515 * Check whether the VCC is really closed on the hardware and available for
516 * open. Check that we have enough resources. If this function returns ok,
517 * a later actual open must succeed. Assume, that we are locked between this
518 * function and the next one, so that nothing does change. For CBR this
519 * assigns the rate group and set the rate group's parameter.
520 */
521int
522hatm_tx_vcc_can_open(struct hatm_softc *sc, u_int cid, struct hevcc *vcc)
523{
524	uint32_t v, line_rate;
525	u_int rc, idx, free_idx;
526	struct atmio_tparam *t = &vcc->param.tparam;
527
528	/* verify that connection is closed */
529#if 0
530	v = READ_TSR(sc, cid, 4);
531	if(!(v & HE_REGM_TSR4_SESS_END)) {
532		if_printf(&sc->ifatm.ifnet, "cid=%#x not closed (TSR4)\n", cid);
533		return (EBUSY);
534	}
535#endif
536	v = READ_TSR(sc, cid, 0);
537	if((v & HE_REGM_TSR0_CONN_STATE) != 0) {
538		if_printf(&sc->ifatm.ifnet, "cid=%#x not closed (TSR0=%#x)\n",
539		    cid, v);
540		return (EBUSY);
541	}
542
543	/* check traffic parameters */
544	line_rate = sc->he622 ? ATM_RATE_622M : ATM_RATE_155M;
545	switch (vcc->param.traffic) {
546
547	  case ATMIO_TRAFFIC_UBR:
548		if (t->pcr == 0 || t->pcr > line_rate)
549			t->pcr = line_rate;
550		if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
551		    t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
552		    t->cdf != 0)
553			return (EINVAL);
554		break;
555
556	  case ATMIO_TRAFFIC_CBR:
557		/*
558		 * Compute rate group index
559		 */
560		if (t->pcr < 10)
561			t->pcr = 10;
562		if (sc->cbr_bw + t->pcr > line_rate)
563			return (EINVAL);
564		if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
565		    t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
566		    t->cdf != 0)
567			return (EINVAL);
568
569		rc = cps_to_rate(sc, t->pcr);
570		free_idx = HE_REGN_CS_STPER;
571		for (idx = 0; idx < HE_REGN_CS_STPER; idx++) {
572			if (sc->rate_ctrl[idx].refcnt == 0) {
573				if (free_idx == HE_REGN_CS_STPER)
574					free_idx = idx;
575			} else {
576				if (sc->rate_ctrl[idx].rate == rc)
577					break;
578			}
579		}
580		if (idx == HE_REGN_CS_STPER) {
581			if ((idx = free_idx) == HE_REGN_CS_STPER)
582				return (EBUSY);
583			sc->rate_ctrl[idx].rate = rc;
584			WRITE_MBOX4(sc, HE_REGO_CS_STPER(idx), rc);
585		}
586		vcc->rc = idx;
587		break;
588
589	  case ATMIO_TRAFFIC_ABR:
590		if (t->pcr > line_rate)
591			t->pcr = line_rate;
592		if (t->mcr > line_rate)
593			t->mcr = line_rate;
594		if (t->icr > line_rate)
595			t->icr = line_rate;
596		if (t->tbe == 0 || t->tbe >= 1 << 24 || t->nrm > 7 ||
597		    t->trm > 7 || t->adtf >= 1 << 10 || t->rif > 15 ||
598		    t->rdf > 15 || t->cdf > 7)
599			return (EINVAL);
600		break;
601
602	  default:
603		return (EINVAL);
604	}
605	return (0);
606}
607
608#define NRM_CODE2VAL(CODE) (2 * (1 << (CODE)))
609
610/*
611 * Actually open the transmit VCC
612 */
613void
614hatm_tx_vcc_open(struct hatm_softc *sc, u_int cid)
615{
616	struct hevcc *vcc = sc->vccs[cid];
617	uint32_t tsr0, tsr4, atmf, crm;
618	const struct atmio_tparam *t = &vcc->param.tparam;
619
620	if (vcc->param.aal == ATMIO_AAL_5) {
621		tsr0 = HE_REGM_TSR0_AAL_5 << HE_REGS_TSR0_AAL;
622		tsr4 = HE_REGM_TSR4_AAL_5 << HE_REGS_TSR4_AAL;
623	} else {
624		tsr0 = HE_REGM_TSR0_AAL_0 << HE_REGS_TSR0_AAL;
625		tsr4 = HE_REGM_TSR4_AAL_0 << HE_REGS_TSR4_AAL;
626	}
627	tsr4 |= 1;
628
629	switch (vcc->param.traffic) {
630
631	  case ATMIO_TRAFFIC_UBR:
632		atmf = hatm_cps2atmf(t->pcr);
633
634		tsr0 |= HE_REGM_TSR0_TRAFFIC_UBR << HE_REGS_TSR0_TRAFFIC;
635		tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
636
637		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
638		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
639		WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
640		WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
641		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
642		WRITE_TSR(sc, cid, 3, 0xf, 0);
643		WRITE_TSR(sc, cid, 5, 0xf, 0);
644		WRITE_TSR(sc, cid, 6, 0xf, 0);
645		WRITE_TSR(sc, cid, 7, 0xf, 0);
646		WRITE_TSR(sc, cid, 8, 0xf, 0);
647		WRITE_TSR(sc, cid, 10, 0xf, 0);
648		WRITE_TSR(sc, cid, 11, 0xf, 0);
649		WRITE_TSR(sc, cid, 12, 0xf, 0);
650		WRITE_TSR(sc, cid, 13, 0xf, 0);
651		WRITE_TSR(sc, cid, 14, 0xf, 0);
652		break;
653
654	  case ATMIO_TRAFFIC_CBR:
655		atmf = hatm_cps2atmf(t->pcr);
656		sc->rate_ctrl[vcc->rc].refcnt++;
657
658		tsr0 |= HE_REGM_TSR0_TRAFFIC_CBR << HE_REGS_TSR0_TRAFFIC;
659		tsr0 |= vcc->rc;
660
661		WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
662		WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
663		WRITE_TSR(sc, cid, 3, 0xf, 0);
664		WRITE_TSR(sc, cid, 5, 0xf, 0);
665		WRITE_TSR(sc, cid, 6, 0xf, 0);
666		WRITE_TSR(sc, cid, 7, 0xf, 0);
667		WRITE_TSR(sc, cid, 8, 0xf, 0);
668		WRITE_TSR(sc, cid, 10, 0xf, 0);
669		WRITE_TSR(sc, cid, 11, 0xf, 0);
670		WRITE_TSR(sc, cid, 12, 0xf, 0);
671		WRITE_TSR(sc, cid, 13, 0xf, 0);
672		WRITE_TSR(sc, cid, 14, 0xf, 0);
673		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
674		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
675		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
676
677		sc->cbr_bw += t->pcr;
678		break;
679
680	  case ATMIO_TRAFFIC_ABR:
681		if ((crm = t->tbe / NRM_CODE2VAL(t->nrm)) > 0xffff)
682			crm = 0xffff;
683
684		tsr0 |= HE_REGM_TSR0_TRAFFIC_ABR << HE_REGS_TSR0_TRAFFIC;
685		tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
686
687		WRITE_TSR(sc, cid, 0, 0xf, tsr0);
688		WRITE_TSR(sc, cid, 4, 0xf, tsr4);
689
690		WRITE_TSR(sc, cid, 1, 0xf,
691		    ((hatm_cps2atmf(t->pcr) << HE_REGS_TSR1_PCR) |
692		     (hatm_cps2atmf(t->mcr) << HE_REGS_TSR1_MCR)));
693		WRITE_TSR(sc, cid, 2, 0xf,
694		    (hatm_cps2atmf(t->icr) << HE_REGS_TSR2_ACR));
695		WRITE_TSR(sc, cid, 3, 0xf,
696		    ((NRM_CODE2VAL(t->nrm) - 1) << HE_REGS_TSR3_NRM) |
697		    (crm << HE_REGS_TSR3_CRM));
698
699		WRITE_TSR(sc, cid, 5, 0xf, 0);
700		WRITE_TSR(sc, cid, 6, 0xf, 0);
701		WRITE_TSR(sc, cid, 7, 0xf, 0);
702		WRITE_TSR(sc, cid, 8, 0xf, 0);
703		WRITE_TSR(sc, cid, 10, 0xf, 0);
704		WRITE_TSR(sc, cid, 12, 0xf, 0);
705		WRITE_TSR(sc, cid, 14, 0xf, 0);
706		WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
707
708		WRITE_TSR(sc, cid, 11, 0xf,
709		    (hatm_cps2atmf(t->icr) << HE_REGS_TSR11_ICR) |
710		    (t->trm << HE_REGS_TSR11_TRM) |
711		    (t->nrm << HE_REGS_TSR11_NRM) |
712		    (t->adtf << HE_REGS_TSR11_ADTF));
713
714		WRITE_TSR(sc, cid, 13, 0xf,
715		    (t->rdf << HE_REGS_TSR13_RDF) |
716		    (t->rif << HE_REGS_TSR13_RIF) |
717		    (t->cdf << HE_REGS_TSR13_CDF) |
718		    (crm << HE_REGS_TSR13_CRM));
719
720		break;
721
722	  default:
723		return;
724	}
725
726	vcc->vflags |= HE_VCC_TX_OPEN;
727}
728
729/*
730 * Close the TX side of a VCC. Set the CLOSING flag.
731 */
732void
733hatm_tx_vcc_close(struct hatm_softc *sc, u_int cid)
734{
735	struct hevcc *vcc = sc->vccs[cid];
736	struct tpd *tpd_list[1];
737	u_int i, pcr = 0;
738
739	WRITE_TSR(sc, cid, 4, 0x8, HE_REGM_TSR4_FLUSH);
740
741	switch (vcc->param.traffic) {
742
743	  case ATMIO_TRAFFIC_CBR:
744		WRITE_TSR(sc, cid, 14, 0x8, HE_REGM_TSR14_CBR_DELETE);
745		break;
746
747	  case ATMIO_TRAFFIC_ABR:
748		WRITE_TSR(sc, cid, 14, 0x4, HE_REGM_TSR14_ABR_CLOSE);
749		pcr = vcc->param.tparam.pcr;
750		/* FALL THROUGH */
751
752	  case ATMIO_TRAFFIC_UBR:
753		WRITE_TSR(sc, cid, 1, 0xf,
754		    hatm_cps2atmf(HE_CONFIG_FLUSH_RATE) << HE_REGS_TSR1_MCR |
755		    hatm_cps2atmf(pcr) << HE_REGS_TSR1_PCR);
756		break;
757	}
758
759	tpd_list[0] = hatm_alloc_tpd(sc, 0);
760	tpd_list[0]->tpd.addr |= HE_REGM_TPD_EOS | HE_REGM_TPD_INTR;
761	tpd_list[0]->cid = cid;
762
763	vcc->vflags |= HE_VCC_TX_CLOSING;
764	vcc->vflags &= ~HE_VCC_TX_OPEN;
765
766	i = 0;
767	while (hatm_queue_tpds(sc, 1, tpd_list, cid) != 0) {
768		if (++i == 1000)
769			panic("TPDRQ permanently full");
770		DELAY(1000);
771	}
772}
773
774void
775hatm_tx_vcc_closed(struct hatm_softc *sc, u_int cid)
776{
777	if (sc->vccs[cid]->param.traffic == ATMIO_TRAFFIC_CBR) {
778		sc->cbr_bw -= sc->vccs[cid]->param.tparam.pcr;
779		sc->rate_ctrl[sc->vccs[cid]->rc].refcnt--;
780	}
781}
782