if_patm_tx.c revision 117632
1117632Sharti/* 2117632Sharti * Copyright (c) 2003 3117632Sharti * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4117632Sharti * All rights reserved. 5117632Sharti * 6117632Sharti * Redistribution and use in source and binary forms, with or without 7117632Sharti * modification, are permitted provided that the following conditions 8117632Sharti * are met: 9117632Sharti * 1. Redistributions of source code must retain the above copyright 10117632Sharti * notice, this list of conditions and the following disclaimer. 11117632Sharti * 2. Redistributions in binary form must reproduce the above copyright 12117632Sharti * notice, this list of conditions and the following disclaimer in the 13117632Sharti * documentation and/or other materials provided with the distribution. 14117632Sharti * 15117632Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16117632Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17117632Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18117632Sharti * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19117632Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20117632Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21117632Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22117632Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23117632Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24117632Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25117632Sharti * SUCH DAMAGE. 26117632Sharti * 27117632Sharti * The TST allocation algorithm is from the IDT driver which is: 28117632Sharti * 29117632Sharti * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. 30117632Sharti * All rights reserved. 31117632Sharti * 32117632Sharti * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely 33117632Sharti * All rights reserved. 34117632Sharti * 35117632Sharti * Author: Hartmut Brandt <harti@freebsd.org> 36117632Sharti * 37117632Sharti * Driver for IDT77252 based cards like ProSum's. 38117632Sharti */ 39117632Sharti#include <sys/cdefs.h> 40117632Sharti__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_tx.c 117632 2003-07-15 11:57:24Z harti $"); 41117632Sharti 42117632Sharti#include "opt_inet.h" 43117632Sharti#include "opt_natm.h" 44117632Sharti 45117632Sharti#include <sys/types.h> 46117632Sharti#include <sys/param.h> 47117632Sharti#include <sys/systm.h> 48117632Sharti#include <sys/malloc.h> 49117632Sharti#include <sys/kernel.h> 50117632Sharti#include <sys/bus.h> 51117632Sharti#include <sys/errno.h> 52117632Sharti#include <sys/conf.h> 53117632Sharti#include <sys/module.h> 54117632Sharti#include <sys/lock.h> 55117632Sharti#include <sys/mutex.h> 56117632Sharti#include <sys/sysctl.h> 57117632Sharti#include <sys/queue.h> 58117632Sharti#include <sys/condvar.h> 59117632Sharti#include <sys/endian.h> 60117632Sharti#include <vm/uma.h> 61117632Sharti 62117632Sharti#include <sys/sockio.h> 63117632Sharti#include <sys/mbuf.h> 64117632Sharti#include <sys/socket.h> 65117632Sharti 66117632Sharti#include <net/if.h> 67117632Sharti#include <net/if_media.h> 68117632Sharti#include <net/if_atm.h> 69117632Sharti#include <net/route.h> 70117632Sharti#ifdef ENABLE_BPF 71117632Sharti#include <net/bpf.h> 72117632Sharti#endif 73117632Sharti#include <netinet/in.h> 74117632Sharti#include <netinet/if_atm.h> 75117632Sharti 76117632Sharti#include <machine/bus.h> 77117632Sharti#include <machine/resource.h> 78117632Sharti#include <sys/bus.h> 79117632Sharti#include <sys/rman.h> 80117632Sharti#include <sys/mbpool.h> 81117632Sharti 82117632Sharti#include <dev/utopia/utopia.h> 83117632Sharti#include <dev/patm/idt77252reg.h> 84117632Sharti#include <dev/patm/if_patmvar.h> 85117632Sharti 86117632Shartistatic struct mbuf *patm_tx_pad(struct patm_softc *sc, struct mbuf *m0); 87117632Shartistatic void patm_launch(struct patm_softc *sc, struct patm_scd *scd); 88117632Sharti 89117632Shartistatic struct patm_txmap *patm_txmap_get(struct patm_softc *); 90117632Shartistatic void patm_load_txbuf(void *, bus_dma_segment_t *, int, 91117632Sharti bus_size_t, int); 92117632Sharti 93117632Shartistatic void patm_tst_alloc(struct patm_softc *sc, struct patm_vcc *vcc); 94117632Shartistatic void patm_tst_free(struct patm_softc *sc, struct patm_vcc *vcc); 95117632Shartistatic void patm_tst_timer(void *p); 96117632Shartistatic void patm_tst_update(struct patm_softc *); 97117632Sharti 98117632Shartistatic void patm_tct_start(struct patm_softc *sc, struct patm_vcc *); 99117632Sharti 100117632Shartistatic const char *dump_scd(struct patm_softc *sc, struct patm_scd *scd) 101117632Sharti __unused; 102117632Shartistatic void patm_tct_print(struct patm_softc *sc, u_int cid) __unused; 103117632Sharti 104117632Sharti/* 105117632Sharti * Structure for communication with the loader function for transmission 106117632Sharti */ 107117632Shartistruct txarg { 108117632Sharti struct patm_softc *sc; 109117632Sharti struct patm_scd *scd; /* scheduling channel */ 110117632Sharti struct patm_vcc *vcc; /* the VCC of this PDU */ 111117632Sharti struct mbuf *mbuf; 112117632Sharti u_int hdr; /* cell header */ 113117632Sharti}; 114117632Sharti 115117632Shartistatic __inline u_int 116117632Sharticbr2slots(struct patm_softc *sc, struct patm_vcc *vcc) 117117632Sharti{ 118117632Sharti /* compute the number of slots we need, make sure to get at least 119117632Sharti * the specified PCR */ 120117632Sharti return ((u_int)(((uint64_t)(sc->mmap->tst_size - 1) * 121117632Sharti vcc->vcc.tparam.pcr + sc->ifatm.mib.pcr - 1) / sc->ifatm.mib.pcr)); 122117632Sharti} 123117632Sharti 124117632Shartistatic __inline u_int 125117632Shartislots2cr(struct patm_softc *sc, u_int slots) 126117632Sharti{ 127117632Sharti return ((slots * sc->ifatm.mib.pcr + sc->mmap->tst_size - 2) / 128117632Sharti (sc->mmap->tst_size - 1)); 129117632Sharti} 130117632Sharti 131117632Sharti/* check if we can open this one */ 132117632Shartiint 133117632Shartipatm_tx_vcc_can_open(struct patm_softc *sc, struct patm_vcc *vcc) 134117632Sharti{ 135117632Sharti 136117632Sharti /* check resources */ 137117632Sharti switch (vcc->vcc.traffic) { 138117632Sharti 139117632Sharti case ATMIO_TRAFFIC_CBR: 140117632Sharti { 141117632Sharti u_int slots = cbr2slots(sc, vcc); 142117632Sharti 143117632Sharti if (slots > sc->tst_free + sc->tst_reserve) 144117632Sharti return (EINVAL); 145117632Sharti break; 146117632Sharti } 147117632Sharti 148117632Sharti case ATMIO_TRAFFIC_VBR: 149117632Sharti if (vcc->vcc.tparam.scr > sc->bwrem) 150117632Sharti return (EINVAL); 151117632Sharti if (vcc->vcc.tparam.pcr > sc->ifatm.mib.pcr) 152117632Sharti return (EINVAL); 153117632Sharti if (vcc->vcc.tparam.scr > vcc->vcc.tparam.pcr || 154117632Sharti vcc->vcc.tparam.mbs == 0) 155117632Sharti return (EINVAL); 156117632Sharti break; 157117632Sharti 158117632Sharti case ATMIO_TRAFFIC_ABR: 159117632Sharti if (vcc->vcc.tparam.tbe == 0 || 160117632Sharti vcc->vcc.tparam.nrm == 0) 161117632Sharti /* needed to compute CRM */ 162117632Sharti return (EINVAL); 163117632Sharti if (vcc->vcc.tparam.pcr > sc->ifatm.mib.pcr || 164117632Sharti vcc->vcc.tparam.icr > vcc->vcc.tparam.pcr || 165117632Sharti vcc->vcc.tparam.mcr > vcc->vcc.tparam.icr) 166117632Sharti return (EINVAL); 167117632Sharti if (vcc->vcc.tparam.mcr > sc->bwrem || 168117632Sharti vcc->vcc.tparam.icr > sc->bwrem) 169117632Sharti return (EINVAL); 170117632Sharti break; 171117632Sharti } 172117632Sharti 173117632Sharti return (0); 174117632Sharti} 175117632Sharti 176117632Sharti#define NEXT_TAG(T) do { \ 177117632Sharti (T) = ((T) + 1) % IDT_TSQE_TAG_SPACE; \ 178117632Sharti } while (0) 179117632Sharti 180117632Sharti/* 181117632Sharti * open it 182117632Sharti */ 183117632Shartivoid 184117632Shartipatm_tx_vcc_open(struct patm_softc *sc, struct patm_vcc *vcc) 185117632Sharti{ 186117632Sharti struct patm_scd *scd; 187117632Sharti 188117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_UBR) { 189117632Sharti /* we use UBR0 */ 190117632Sharti vcc->scd = sc->scd0; 191117632Sharti vcc->vflags |= PATM_VCC_TX_OPEN; 192117632Sharti return; 193117632Sharti } 194117632Sharti 195117632Sharti /* get an SCD */ 196117632Sharti scd = patm_scd_alloc(sc); 197117632Sharti if (scd == NULL) { 198117632Sharti /* should not happen */ 199117632Sharti patm_printf(sc, "out of SCDs\n"); 200117632Sharti return; 201117632Sharti } 202117632Sharti vcc->scd = scd; 203117632Sharti patm_scd_setup(sc, scd); 204117632Sharti patm_tct_setup(sc, scd, vcc); 205117632Sharti 206117632Sharti if (vcc->vcc.traffic != ATMIO_TRAFFIC_CBR) 207117632Sharti patm_tct_start(sc, vcc); 208117632Sharti 209117632Sharti vcc->vflags |= PATM_VCC_TX_OPEN; 210117632Sharti} 211117632Sharti 212117632Sharti/* 213117632Sharti * close the given vcc for transmission 214117632Sharti */ 215117632Shartivoid 216117632Shartipatm_tx_vcc_close(struct patm_softc *sc, struct patm_vcc *vcc) 217117632Sharti{ 218117632Sharti struct patm_scd *scd; 219117632Sharti struct mbuf *m; 220117632Sharti 221117632Sharti vcc->vflags |= PATM_VCC_TX_CLOSING; 222117632Sharti 223117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_UBR) { 224117632Sharti /* let the queue PDUs go out */ 225117632Sharti vcc->scd = NULL; 226117632Sharti vcc->vflags &= ~(PATM_VCC_TX_OPEN | PATM_VCC_TX_CLOSING); 227117632Sharti return; 228117632Sharti } 229117632Sharti scd = vcc->scd; 230117632Sharti 231117632Sharti /* empty the waitq */ 232117632Sharti for (;;) { 233117632Sharti _IF_DEQUEUE(&scd->q, m); 234117632Sharti if (m == NULL) 235117632Sharti break; 236117632Sharti m_freem(m); 237117632Sharti } 238117632Sharti 239117632Sharti if (scd->num_on_card == 0) { 240117632Sharti /* we are idle */ 241117632Sharti vcc->vflags &= ~PATM_VCC_TX_OPEN; 242117632Sharti 243117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_CBR) 244117632Sharti patm_tst_free(sc, vcc); 245117632Sharti 246117632Sharti patm_sram_write4(sc, scd->sram + 0, 0, 0, 0, 0); 247117632Sharti patm_sram_write4(sc, scd->sram + 4, 0, 0, 0, 0); 248117632Sharti patm_scd_free(sc, scd); 249117632Sharti 250117632Sharti vcc->scd = NULL; 251117632Sharti vcc->vflags &= ~PATM_VCC_TX_CLOSING; 252117632Sharti 253117632Sharti return; 254117632Sharti } 255117632Sharti 256117632Sharti /* speed up transmission */ 257117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_UIER(vcc->cid, 0xff)); 258117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_ULACR(vcc->cid, 0xff)); 259117632Sharti 260117632Sharti /* wait for the interrupt to drop the number to 0 */ 261117632Sharti patm_debug(sc, VCC, "%u buffers still on card", scd->num_on_card); 262117632Sharti} 263117632Sharti 264117632Sharti/* transmission side finally closed */ 265117632Shartivoid 266117632Shartipatm_tx_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc) 267117632Sharti{ 268117632Sharti 269117632Sharti patm_debug(sc, VCC, "%u.%u TX closed", vcc->vcc.vpi, vcc->vcc.vci); 270117632Sharti 271117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_VBR) 272117632Sharti sc->bwrem += vcc->vcc.tparam.scr; 273117632Sharti} 274117632Sharti 275117632Sharti/* 276117632Sharti * Pull off packets from the interface queue and try to transmit them. 277117632Sharti * If the transmission fails because of a full transmit channel, we drop 278117632Sharti * packets for CBR and queue them for other channels up to limit. 279117632Sharti * This limit should depend on the CDVT for VBR and ABR, but it doesn't. 280117632Sharti */ 281117632Shartivoid 282117632Shartipatm_start(struct ifnet *ifp) 283117632Sharti{ 284117632Sharti struct patm_softc *sc = (struct patm_softc *)ifp->if_softc; 285117632Sharti struct mbuf *m; 286117632Sharti struct atm_pseudohdr *aph; 287117632Sharti u_int vpi, vci, cid; 288117632Sharti struct patm_vcc *vcc; 289117632Sharti 290117632Sharti mtx_lock(&sc->mtx); 291117632Sharti if (!(ifp->if_flags & IFF_RUNNING)) { 292117632Sharti mtx_unlock(&sc->mtx); 293117632Sharti return; 294117632Sharti } 295117632Sharti 296117632Sharti while (1) { 297117632Sharti /* get a new mbuf */ 298117632Sharti IF_DEQUEUE(&ifp->if_snd, m); 299117632Sharti if (m == NULL) 300117632Sharti break; 301117632Sharti 302117632Sharti /* split of pseudo header */ 303117632Sharti if (m->m_len < sizeof(*aph) && 304117632Sharti (m = m_pullup(m, sizeof(*aph))) == NULL) { 305117632Sharti sc->ifatm.ifnet.if_oerrors++; 306117632Sharti continue; 307117632Sharti } 308117632Sharti 309117632Sharti aph = mtod(m, struct atm_pseudohdr *); 310117632Sharti vci = ATM_PH_VCI(aph); 311117632Sharti vpi = ATM_PH_VPI(aph); 312117632Sharti m_adj(m, sizeof(*aph)); 313117632Sharti 314117632Sharti /* reject empty packets */ 315117632Sharti if (m->m_pkthdr.len == 0) { 316117632Sharti m_freem(m); 317117632Sharti sc->ifatm.ifnet.if_oerrors++; 318117632Sharti continue; 319117632Sharti } 320117632Sharti 321117632Sharti /* check whether this is a legal vcc */ 322117632Sharti if (!LEGAL_VPI(sc, vpi) || !LEGAL_VCI(sc, vci) || vci == 0) { 323117632Sharti m_freem(m); 324117632Sharti sc->ifatm.ifnet.if_oerrors++; 325117632Sharti continue; 326117632Sharti } 327117632Sharti cid = PATM_CID(sc, vpi, vci); 328117632Sharti vcc = sc->vccs[cid]; 329117632Sharti if (vcc == NULL) { 330117632Sharti m_freem(m); 331117632Sharti sc->ifatm.ifnet.if_oerrors++; 332117632Sharti continue; 333117632Sharti } 334117632Sharti 335117632Sharti /* must be multiple of 48 if not AAL5 */ 336117632Sharti if (vcc->vcc.aal == ATMIO_AAL_0 || 337117632Sharti vcc->vcc.aal == ATMIO_AAL_34) { 338117632Sharti /* XXX AAL3/4 format? */ 339117632Sharti if (m->m_pkthdr.len % 48 != 0 && 340117632Sharti (m = patm_tx_pad(sc, m)) == NULL) { 341117632Sharti sc->ifatm.ifnet.if_oerrors++; 342117632Sharti continue; 343117632Sharti } 344117632Sharti } else if (vcc->vcc.aal == ATMIO_AAL_RAW) { 345117632Sharti switch (vcc->vflags & PATM_RAW_FORMAT) { 346117632Sharti 347117632Sharti default: 348117632Sharti case PATM_RAW_CELL: 349117632Sharti if (m->m_pkthdr.len != 53) { 350117632Sharti sc->ifatm.ifnet.if_oerrors++; 351117632Sharti m_freem(m); 352117632Sharti continue; 353117632Sharti } 354117632Sharti break; 355117632Sharti 356117632Sharti case PATM_RAW_NOHEC: 357117632Sharti if (m->m_pkthdr.len != 52) { 358117632Sharti sc->ifatm.ifnet.if_oerrors++; 359117632Sharti m_freem(m); 360117632Sharti continue; 361117632Sharti } 362117632Sharti break; 363117632Sharti 364117632Sharti case PATM_RAW_CS: 365117632Sharti if (m->m_pkthdr.len != 64) { 366117632Sharti sc->ifatm.ifnet.if_oerrors++; 367117632Sharti m_freem(m); 368117632Sharti continue; 369117632Sharti } 370117632Sharti break; 371117632Sharti } 372117632Sharti } 373117632Sharti 374117632Sharti /* save data */ 375117632Sharti m->m_pkthdr.header = vcc; 376117632Sharti 377117632Sharti /* try to put it on the channels queue */ 378117632Sharti if (_IF_QFULL(&vcc->scd->q)) { 379117632Sharti sc->ifatm.ifnet.if_oerrors++; 380117632Sharti sc->stats.tx_qfull++; 381117632Sharti m_freem(m); 382117632Sharti continue; 383117632Sharti } 384117632Sharti _IF_ENQUEUE(&vcc->scd->q, m); 385117632Sharti 386117632Sharti#ifdef ENABLE_BPF 387117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NG) && 388117632Sharti (vcc->vcc.flags & ATM_PH_AAL5) && 389117632Sharti (vcc->vcc.flags & ATM_PH_LLCSNAP)) 390117632Sharti BPF_MTAP(ifp, m); 391117632Sharti#endif 392117632Sharti 393117632Sharti /* kick the channel to life */ 394117632Sharti patm_launch(sc, vcc->scd); 395117632Sharti 396117632Sharti } 397117632Sharti mtx_unlock(&sc->mtx); 398117632Sharti} 399117632Sharti 400117632Sharti/* 401117632Sharti * Pad non-AAL5 packet to a multiple of 48-byte. 402117632Sharti * We assume AAL0 only. We have still to decide on the format of AAL3/4. 403117632Sharti */ 404117632Shartistatic struct mbuf * 405117632Shartipatm_tx_pad(struct patm_softc *sc, struct mbuf *m0) 406117632Sharti{ 407117632Sharti struct mbuf *last, *m; 408117632Sharti u_int plen, pad, space; 409117632Sharti 410117632Sharti plen = m_length(m0, &last); 411117632Sharti if (plen != m0->m_pkthdr.len) { 412117632Sharti patm_printf(sc, "%s: mbuf length mismatch %d %u\n", __func__, 413117632Sharti m0->m_pkthdr.len, plen); 414117632Sharti m0->m_pkthdr.len = plen; 415117632Sharti if (plen == 0) { 416117632Sharti m_freem(m0); 417117632Sharti sc->ifatm.ifnet.if_oerrors++; 418117632Sharti return (NULL); 419117632Sharti } 420117632Sharti if (plen % 48 == 0) 421117632Sharti return (m0); 422117632Sharti } 423117632Sharti pad = 48 - plen % 48; 424117632Sharti if (M_WRITABLE(last)) { 425117632Sharti if (M_TRAILINGSPACE(last) >= pad) { 426117632Sharti bzero(last->m_data + last->m_len, pad); 427117632Sharti last->m_len += pad; 428117632Sharti return (m0); 429117632Sharti } 430117632Sharti space = M_LEADINGSPACE(last); 431117632Sharti if (space + M_TRAILINGSPACE(last) >= pad) { 432117632Sharti bcopy(last->m_data, last->m_data + space, last->m_len); 433117632Sharti last->m_data -= space; 434117632Sharti bzero(last->m_data + last->m_len, pad); 435117632Sharti last->m_len += pad; 436117632Sharti return (m0); 437117632Sharti } 438117632Sharti } 439117632Sharti MGET(m, M_DONTWAIT, MT_DATA); 440117632Sharti if (m == 0) { 441117632Sharti m_freem(m0); 442117632Sharti sc->ifatm.ifnet.if_oerrors++; 443117632Sharti return (NULL); 444117632Sharti } 445117632Sharti bzero(mtod(m, u_char *), pad); 446117632Sharti m->m_len = pad; 447117632Sharti last->m_next = m; 448117632Sharti 449117632Sharti return (m0); 450117632Sharti} 451117632Sharti 452117632Sharti/* 453117632Sharti * Try to put as many packets from the channels queue onto the channel 454117632Sharti */ 455117632Shartistatic void 456117632Shartipatm_launch(struct patm_softc *sc, struct patm_scd *scd) 457117632Sharti{ 458117632Sharti struct txarg a; 459117632Sharti struct mbuf *m, *tmp; 460117632Sharti u_int segs; 461117632Sharti struct patm_txmap *map; 462117632Sharti int error; 463117632Sharti 464117632Sharti a.sc = sc; 465117632Sharti a.scd = scd; 466117632Sharti 467117632Sharti /* limit the number of outstanding packets to the tag space */ 468117632Sharti while (scd->num_on_card < IDT_TSQE_TAG_SPACE) { 469117632Sharti /* get the next packet */ 470117632Sharti _IF_DEQUEUE(&scd->q, m); 471117632Sharti if (m == NULL) 472117632Sharti break; 473117632Sharti 474117632Sharti a.vcc = m->m_pkthdr.header; 475117632Sharti 476117632Sharti /* we must know the number of segments beforehand - count 477117632Sharti * this may actually give a wrong number of segments for 478117632Sharti * AAL_RAW where we still need to remove the cell header */ 479117632Sharti segs = 0; 480117632Sharti for (tmp = m; tmp != NULL; tmp = tmp->m_next) 481117632Sharti if (tmp->m_len != 0) 482117632Sharti segs++; 483117632Sharti 484117632Sharti /* check whether there is space in the queue */ 485117632Sharti if (segs >= scd->space) { 486117632Sharti /* put back */ 487117632Sharti _IF_PREPEND(&scd->q, m); 488117632Sharti sc->stats.tx_out_of_tbds++; 489117632Sharti break; 490117632Sharti } 491117632Sharti 492117632Sharti /* get a DMA map */ 493117632Sharti if ((map = patm_txmap_get(sc)) == NULL) { 494117632Sharti _IF_PREPEND(&scd->q, m); 495117632Sharti sc->stats.tx_out_of_maps++; 496117632Sharti break; 497117632Sharti } 498117632Sharti 499117632Sharti /* load the map */ 500117632Sharti m->m_pkthdr.header = map; 501117632Sharti a.mbuf = m; 502117632Sharti 503117632Sharti /* handle AAL_RAW */ 504117632Sharti if (a.vcc->vcc.aal == ATMIO_AAL_RAW) { 505117632Sharti u_char hdr[4]; 506117632Sharti 507117632Sharti m_copydata(m, 0, 4, hdr); 508117632Sharti a.hdr = (hdr[0] << 24) | (hdr[1] << 16) | 509117632Sharti (hdr[2] << 8) | hdr[3]; 510117632Sharti 511117632Sharti switch (a.vcc->vflags & PATM_RAW_FORMAT) { 512117632Sharti 513117632Sharti default: 514117632Sharti case PATM_RAW_CELL: 515117632Sharti m_adj(m, 5); 516117632Sharti break; 517117632Sharti 518117632Sharti case PATM_RAW_NOHEC: 519117632Sharti m_adj(m, 4); 520117632Sharti break; 521117632Sharti 522117632Sharti case PATM_RAW_CS: 523117632Sharti m_adj(m, 16); 524117632Sharti break; 525117632Sharti } 526117632Sharti } else 527117632Sharti a.hdr = IDT_TBD_HDR(a.vcc->vcc.vpi, a.vcc->vcc.vci, 528117632Sharti 0, 0); 529117632Sharti 530117632Sharti error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m, 531117632Sharti patm_load_txbuf, &a, BUS_DMA_NOWAIT); 532117632Sharti if (error == EFBIG) { 533117632Sharti if ((m = m_defrag(m, M_DONTWAIT)) == NULL) { 534117632Sharti sc->ifatm.ifnet.if_oerrors++; 535117632Sharti continue; 536117632Sharti } 537117632Sharti error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m, 538117632Sharti patm_load_txbuf, &a, BUS_DMA_NOWAIT); 539117632Sharti } 540117632Sharti if (error != 0) { 541117632Sharti sc->stats.tx_load_err++; 542117632Sharti sc->ifatm.ifnet.if_oerrors++; 543117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 544117632Sharti m_freem(m); 545117632Sharti continue; 546117632Sharti } 547117632Sharti 548117632Sharti sc->ifatm.ifnet.if_opackets++; 549117632Sharti } 550117632Sharti} 551117632Sharti 552117632Sharti/* 553117632Sharti * Load the DMA segments into the scheduling channel 554117632Sharti */ 555117632Shartistatic void 556117632Shartipatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg, 557117632Sharti bus_size_t mapsize, int error) 558117632Sharti{ 559117632Sharti struct txarg *a= uarg; 560117632Sharti struct patm_scd *scd = a->scd; 561117632Sharti u_int w1, w3, cnt; 562117632Sharti struct idt_tbd *tbd = NULL; 563117632Sharti u_int rest = mapsize; 564117632Sharti 565117632Sharti if (error != 0) 566117632Sharti return; 567117632Sharti 568117632Sharti cnt = 0; 569117632Sharti while (nseg > 0) { 570117632Sharti if (segs->ds_len == 0) { 571117632Sharti /* transmit buffer length must be > 0 */ 572117632Sharti nseg--; 573117632Sharti segs++; 574117632Sharti continue; 575117632Sharti } 576117632Sharti /* rest after this buffer */ 577117632Sharti rest -= segs->ds_len; 578117632Sharti 579117632Sharti /* put together status word */ 580117632Sharti w1 = 0; 581117632Sharti if (rest < 48 /* && a->vcc->vcc.aal != ATMIO_AAL_5 */) 582117632Sharti /* last cell is in this buffer */ 583117632Sharti w1 |= IDT_TBD_EPDU; 584117632Sharti 585117632Sharti if (a->vcc->vcc.aal == ATMIO_AAL_5) 586117632Sharti w1 |= IDT_TBD_AAL5; 587117632Sharti else if (a->vcc->vcc.aal == ATMIO_AAL_34) 588117632Sharti w1 |= IDT_TBD_AAL34; 589117632Sharti else 590117632Sharti w1 |= IDT_TBD_AAL0; 591117632Sharti 592117632Sharti w1 |= segs->ds_len; 593117632Sharti 594117632Sharti /* AAL5 PDU length (unpadded) */ 595117632Sharti if (a->vcc->vcc.aal == ATMIO_AAL_5) 596117632Sharti w3 = mapsize; 597117632Sharti else 598117632Sharti w3 = 0; 599117632Sharti 600117632Sharti if (rest == 0) 601117632Sharti w1 |= IDT_TBD_TSIF | IDT_TBD_GTSI | 602117632Sharti (scd->tag << IDT_TBD_TAG_SHIFT); 603117632Sharti 604117632Sharti tbd = &scd->scq[scd->tail]; 605117632Sharti 606117632Sharti tbd->flags = htole32(w1); 607117632Sharti tbd->addr = htole32(segs->ds_addr); 608117632Sharti tbd->aal5 = htole32(w3); 609117632Sharti tbd->hdr = htole32(a->hdr); 610117632Sharti 611117632Sharti patm_debug(a->sc, TX, "TBD(%u): %08x %08x %08x %08x", 612117632Sharti scd->tail, w1, segs->ds_addr, w3, a->hdr); 613117632Sharti 614117632Sharti /* got to next entry */ 615117632Sharti if (++scd->tail == IDT_SCQ_SIZE) 616117632Sharti scd->tail = 0; 617117632Sharti cnt++; 618117632Sharti nseg--; 619117632Sharti segs++; 620117632Sharti } 621117632Sharti scd->space -= cnt; 622117632Sharti scd->num_on_card++; 623117632Sharti 624117632Sharti KASSERT(rest == 0, ("bad mbuf")); 625117632Sharti KASSERT(cnt > 0, ("no segs")); 626117632Sharti KASSERT(scd->space > 0, ("scq full")); 627117632Sharti 628117632Sharti KASSERT(scd->on_card[scd->tag] == NULL, 629117632Sharti ("scd on_card wedged %u%s", scd->tag, dump_scd(a->sc, scd))); 630117632Sharti scd->on_card[scd->tag] = a->mbuf; 631117632Sharti a->mbuf->m_pkthdr.csum_data = cnt; 632117632Sharti 633117632Sharti NEXT_TAG(scd->tag); 634117632Sharti 635117632Sharti patm_debug(a->sc, TX, "SCD tail %u (%lx:%lx)", scd->tail, 636117632Sharti (u_long)scd->phy, (u_long)scd->phy + (scd->tail << IDT_TBD_SHIFT)); 637117632Sharti patm_sram_write(a->sc, scd->sram, 638117632Sharti scd->phy + (scd->tail << IDT_TBD_SHIFT)); 639117632Sharti 640117632Sharti if (patm_sram_read(a->sc, a->vcc->cid * 8 + 3) & IDT_TCT_IDLE) { 641117632Sharti /* 642117632Sharti * if the connection is idle start it. We cannot rely 643117632Sharti * on a flag set by patm_tx_idle() here, because sometimes 644117632Sharti * the card seems to place an idle TSI into the TSQ but 645117632Sharti * forgets to raise an interrupt. 646117632Sharti */ 647117632Sharti patm_nor_write(a->sc, IDT_NOR_TCMDQ, 648117632Sharti IDT_TCMDQ_START(a->vcc->cid)); 649117632Sharti } 650117632Sharti} 651117632Sharti 652117632Sharti/* 653117632Sharti * packet transmitted 654117632Sharti */ 655117632Shartivoid 656117632Shartipatm_tx(struct patm_softc *sc, u_int stamp, u_int status) 657117632Sharti{ 658117632Sharti u_int cid, tag, last; 659117632Sharti struct mbuf *m; 660117632Sharti struct patm_vcc *vcc; 661117632Sharti struct patm_scd *scd; 662117632Sharti struct patm_txmap *map; 663117632Sharti 664117632Sharti /* get the connection */ 665117632Sharti cid = PATM_CID(sc, IDT_TBD_VPI(status), IDT_TBD_VCI(status)); 666117632Sharti if ((vcc = sc->vccs[cid]) == NULL) { 667117632Sharti /* closed UBR connection */ 668117632Sharti return; 669117632Sharti } 670117632Sharti scd = vcc->scd; 671117632Sharti 672117632Sharti tag = IDT_TSQE_TAG(stamp); 673117632Sharti 674117632Sharti last = scd->last_tag; 675117632Sharti if (tag == last) { 676117632Sharti patm_printf(sc, "same tag %u\n", tag); 677117632Sharti return; 678117632Sharti } 679117632Sharti 680117632Sharti /* Errata 12 requests us to free all entries up to the one 681117632Sharti * with the given tag. */ 682117632Sharti do { 683117632Sharti /* next tag to try */ 684117632Sharti NEXT_TAG(last); 685117632Sharti 686117632Sharti m = scd->on_card[last]; 687117632Sharti KASSERT(m != NULL, ("%stag=%u", dump_scd(sc, scd), tag)); 688117632Sharti scd->on_card[last] = NULL; 689117632Sharti patm_debug(sc, TX, "ok tag=%x", last); 690117632Sharti 691117632Sharti map = m->m_pkthdr.header; 692117632Sharti scd->space += m->m_pkthdr.csum_data; 693117632Sharti 694117632Sharti bus_dmamap_sync(sc->tx_tag, map->map, 695117632Sharti BUS_DMASYNC_POSTWRITE); 696117632Sharti bus_dmamap_unload(sc->tx_tag, map->map); 697117632Sharti m_freem(m); 698117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 699117632Sharti scd->num_on_card--; 700117632Sharti 701117632Sharti if (vcc->vflags & PATM_VCC_TX_CLOSING) { 702117632Sharti if (scd->num_on_card == 0) { 703117632Sharti /* done with this VCC */ 704117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_CBR) 705117632Sharti patm_tst_free(sc, vcc); 706117632Sharti 707117632Sharti patm_sram_write4(sc, scd->sram + 0, 0, 0, 0, 0); 708117632Sharti patm_sram_write4(sc, scd->sram + 4, 0, 0, 0, 0); 709117632Sharti patm_scd_free(sc, scd); 710117632Sharti 711117632Sharti vcc->scd = NULL; 712117632Sharti vcc->vflags &= ~PATM_VCC_TX_CLOSING; 713117632Sharti 714117632Sharti if (vcc->vflags & PATM_VCC_ASYNC) { 715117632Sharti patm_tx_vcc_closed(sc, vcc); 716117632Sharti if (!(vcc->vflags & PATM_VCC_OPEN)) 717117632Sharti patm_vcc_closed(sc, vcc); 718117632Sharti } else 719117632Sharti cv_signal(&sc->vcc_cv); 720117632Sharti return; 721117632Sharti } 722117632Sharti patm_debug(sc, VCC, "%u buffers still on card", 723117632Sharti scd->num_on_card); 724117632Sharti 725117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) { 726117632Sharti /* insist on speeding up transmission for ABR */ 727117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 728117632Sharti IDT_TCMDQ_UIER(vcc->cid, 0xff)); 729117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 730117632Sharti IDT_TCMDQ_ULACR(vcc->cid, 0xff)); 731117632Sharti } 732117632Sharti } 733117632Sharti 734117632Sharti } while (last != tag); 735117632Sharti scd->last_tag = tag; 736117632Sharti 737117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) { 738117632Sharti u_int acri, cps; 739117632Sharti 740117632Sharti acri = (patm_sram_read(sc, 8 * cid + 2) >> IDT_TCT_ACRI_SHIFT) 741117632Sharti & 0x3fff; 742117632Sharti cps = sc->ifatm.mib.pcr * 32 / 743117632Sharti ((1 << (acri >> 10)) * (acri & 0x3ff)); 744117632Sharti 745117632Sharti if (cps != vcc->cps) { 746117632Sharti /* send message */ 747117632Sharti patm_debug(sc, VCC, "ACRI=%04x CPS=%u", acri, cps); 748117632Sharti vcc->cps = cps; 749117632Sharti } 750117632Sharti } 751117632Sharti 752117632Sharti patm_launch(sc, scd); 753117632Sharti} 754117632Sharti 755117632Sharti/* 756117632Sharti * VBR/ABR connection went idle 757117632Sharti * Either restart it or set the idle flag. 758117632Sharti */ 759117632Shartivoid 760117632Shartipatm_tx_idle(struct patm_softc *sc, u_int cid) 761117632Sharti{ 762117632Sharti struct patm_vcc *vcc; 763117632Sharti 764117632Sharti patm_debug(sc, VCC, "idle %u", cid); 765117632Sharti 766117632Sharti if ((vcc = sc->vccs[cid]) != NULL && 767117632Sharti (vcc->vflags & (PATM_VCC_TX_OPEN | PATM_VCC_TX_CLOSING)) != 0 && 768117632Sharti vcc->scd != NULL && (vcc->scd->num_on_card != 0 || 769117632Sharti _IF_QLEN(&vcc->scd->q) != 0)) { 770117632Sharti /* 771117632Sharti * If there is any packet outstanding in the SCD re-activate 772117632Sharti * the channel and kick it. 773117632Sharti */ 774117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 775117632Sharti IDT_TCMDQ_START(vcc->cid)); 776117632Sharti 777117632Sharti patm_launch(sc, vcc->scd); 778117632Sharti } 779117632Sharti} 780117632Sharti 781117632Sharti/* 782117632Sharti * Convert a (24bit) rate to the atm-forum form 783117632Sharti * Our rate is never larger than 19 bit. 784117632Sharti */ 785117632Shartistatic u_int 786117632Sharticps2atmf(u_int cps) 787117632Sharti{ 788117632Sharti u_int e; 789117632Sharti 790117632Sharti if (cps == 0) 791117632Sharti return (0); 792117632Sharti cps <<= 9; 793117632Sharti e = 0; 794117632Sharti while (cps > (1024 - 1)) { 795117632Sharti e++; 796117632Sharti cps >>= 1; 797117632Sharti } 798117632Sharti return ((1 << 14) | (e << 9) | (cps & 0x1ff)); 799117632Sharti} 800117632Sharti 801117632Sharti/* 802117632Sharti * Do a binary search on the log2rate table to convert the rate 803117632Sharti * to its log form. This assumes that the ATM-Forum form is monotonically 804117632Sharti * increasing with the plain cell rate. 805117632Sharti */ 806117632Shartistatic u_int 807117632Shartirate2log(struct patm_softc *sc, u_int rate) 808117632Sharti{ 809117632Sharti const uint32_t *tbl; 810117632Sharti u_int lower, upper, mid, done, val, afr; 811117632Sharti 812117632Sharti afr = cps2atmf(rate); 813117632Sharti 814117632Sharti if (sc->flags & PATM_25M) 815117632Sharti tbl = patm_rtables25; 816117632Sharti else 817117632Sharti tbl = patm_rtables155; 818117632Sharti 819117632Sharti lower = 0; 820117632Sharti upper = 255; 821117632Sharti done = 0; 822117632Sharti while (!done) { 823117632Sharti mid = (lower + upper) / 2; 824117632Sharti val = tbl[mid] >> 17; 825117632Sharti if (val == afr || upper == lower) 826117632Sharti break; 827117632Sharti if (afr > val) 828117632Sharti lower = mid + 1; 829117632Sharti else 830117632Sharti upper = mid - 1; 831117632Sharti } 832117632Sharti if (val > afr && mid > 0) 833117632Sharti mid--; 834117632Sharti return (mid); 835117632Sharti} 836117632Sharti 837117632Sharti/* 838117632Sharti * Return the table index for an increase table. The increase table 839117632Sharti * must be selected not by the RIF itself, but by PCR/2^RIF. Each table 840117632Sharti * represents an additive increase of a cell rate that can be computed 841117632Sharti * from the first table entry (the value in this entry will not be clamped 842117632Sharti * by the link rate). 843117632Sharti */ 844117632Shartistatic u_int 845117632Shartiget_air_table(struct patm_softc *sc, u_int rif, u_int pcr) 846117632Sharti{ 847117632Sharti const uint32_t *tbl; 848117632Sharti u_int increase, base, lair0, ret, t, cps; 849117632Sharti 850117632Sharti#define GET_ENTRY(TAB, IDX) (0xffff & ((IDX & 1) ? \ 851117632Sharti (tbl[512 + (IDX / 2) + 128 * (TAB)] >> 16) : \ 852117632Sharti (tbl[512 + (IDX / 2) + 128 * (TAB)]))) 853117632Sharti 854117632Sharti#define MANT_BITS 10 855117632Sharti#define FRAC_BITS 16 856117632Sharti 857117632Sharti#define DIFF_TO_FP(D) (((D) & ((1 << MANT_BITS) - 1)) << ((D) >> MANT_BITS)) 858117632Sharti#define AFR_TO_INT(A) ((1 << (((A) >> 9) & 0x1f)) * \ 859117632Sharti (512 + ((A) & 0x1ff)) / 512 * ((A) >> 14)) 860117632Sharti 861117632Sharti if (sc->flags & PATM_25M) 862117632Sharti tbl = patm_rtables25; 863117632Sharti else 864117632Sharti tbl = patm_rtables155; 865117632Sharti if (rif >= patm_rtables_ntab) 866117632Sharti rif = patm_rtables_ntab - 1; 867117632Sharti increase = pcr >> rif; 868117632Sharti 869117632Sharti ret = 0; 870117632Sharti for (t = 0; t < patm_rtables_ntab; t++) { 871117632Sharti /* get base rate of this table */ 872117632Sharti base = GET_ENTRY(t, 0); 873117632Sharti /* convert this to fixed point */ 874117632Sharti lair0 = DIFF_TO_FP(base) >> FRAC_BITS; 875117632Sharti 876117632Sharti /* get the CPS from the log2rate table */ 877117632Sharti cps = AFR_TO_INT(tbl[lair0] >> 17) - 10; 878117632Sharti 879117632Sharti if (increase >= cps) 880117632Sharti break; 881117632Sharti 882117632Sharti ret = t; 883117632Sharti } 884117632Sharti return (ret + 4); 885117632Sharti} 886117632Sharti 887117632Sharti/* 888117632Sharti * Setup the TCT 889117632Sharti */ 890117632Shartivoid 891117632Shartipatm_tct_setup(struct patm_softc *sc, struct patm_scd *scd, 892117632Sharti struct patm_vcc *vcc) 893117632Sharti{ 894117632Sharti uint32_t tct[8]; 895117632Sharti u_int sram; 896117632Sharti u_int mbs, token; 897117632Sharti u_int tmp, crm, rdf, cdf, air, mcr; 898117632Sharti 899117632Sharti bzero(tct, sizeof(tct)); 900117632Sharti if (vcc == NULL) { 901117632Sharti /* special case for UBR0 */ 902117632Sharti sram = 0; 903117632Sharti tct[0] = IDT_TCT_UBR | scd->sram; 904117632Sharti tct[7] = IDT_TCT_UBR_FLG; 905117632Sharti 906117632Sharti } else { 907117632Sharti sram = vcc->cid * 8; 908117632Sharti switch (vcc->vcc.traffic) { 909117632Sharti 910117632Sharti case ATMIO_TRAFFIC_CBR: 911117632Sharti patm_tst_alloc(sc, vcc); 912117632Sharti tct[0] = IDT_TCT_CBR | scd->sram; 913117632Sharti /* must account for what was really allocated */ 914117632Sharti break; 915117632Sharti 916117632Sharti case ATMIO_TRAFFIC_VBR: 917117632Sharti /* compute parameters for the TCT */ 918117632Sharti scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr); 919117632Sharti scd->lacr = rate2log(sc, vcc->vcc.tparam.scr); 920117632Sharti 921117632Sharti /* get the 16-bit fraction of SCR/PCR 922117632Sharti * both a 24 bit. Do it the simple way. */ 923117632Sharti token = (uint64_t)(vcc->vcc.tparam.scr << 16) / 924117632Sharti vcc->vcc.tparam.pcr; 925117632Sharti 926117632Sharti patm_debug(sc, VCC, "VBR: init_er=%u lacr=%u " 927117632Sharti "token=0x%04x\n", scd->init_er, scd->lacr, token); 928117632Sharti 929117632Sharti tct[0] = IDT_TCT_VBR | scd->sram; 930117632Sharti tct[2] = IDT_TCT_TSIF; 931117632Sharti tct[3] = IDT_TCT_IDLE | IDT_TCT_HALT; 932117632Sharti tct[4] = IDT_TCT_MAXIDLE; 933117632Sharti tct[5] = 0x01000000; 934117632Sharti if ((mbs = vcc->vcc.tparam.mbs) > 0xff) 935117632Sharti mbs = 0xff; 936117632Sharti tct[6] = (mbs << 16) | token; 937117632Sharti sc->bwrem -= vcc->vcc.tparam.scr; 938117632Sharti break; 939117632Sharti 940117632Sharti case ATMIO_TRAFFIC_ABR: 941117632Sharti scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr); 942117632Sharti scd->lacr = rate2log(sc, vcc->vcc.tparam.icr); 943117632Sharti mcr = rate2log(sc, vcc->vcc.tparam.mcr); 944117632Sharti 945117632Sharti /* compute CRM */ 946117632Sharti tmp = vcc->vcc.tparam.tbe / vcc->vcc.tparam.nrm; 947117632Sharti if (tmp * vcc->vcc.tparam.nrm < vcc->vcc.tparam.tbe) 948117632Sharti tmp++; 949117632Sharti for (crm = 1; tmp > (1 << crm); crm++) 950117632Sharti ; 951117632Sharti if (crm > 0x7) 952117632Sharti crm = 7; 953117632Sharti 954117632Sharti air = get_air_table(sc, vcc->vcc.tparam.rif, 955117632Sharti vcc->vcc.tparam.pcr); 956117632Sharti 957117632Sharti if ((rdf = vcc->vcc.tparam.rdf) >= patm_rtables_ntab) 958117632Sharti rdf = patm_rtables_ntab - 1; 959117632Sharti rdf += patm_rtables_ntab + 4; 960117632Sharti 961117632Sharti if ((cdf = vcc->vcc.tparam.cdf) >= patm_rtables_ntab) 962117632Sharti cdf = patm_rtables_ntab - 1; 963117632Sharti cdf += patm_rtables_ntab + 4; 964117632Sharti 965117632Sharti patm_debug(sc, VCC, "ABR: init_er=%u lacr=%u mcr=%u " 966117632Sharti "crm=%u air=%u rdf=%u cdf=%u\n", scd->init_er, 967117632Sharti scd->lacr, mcr, crm, air, rdf, cdf); 968117632Sharti 969117632Sharti tct[0] = IDT_TCT_ABR | scd->sram; 970117632Sharti tct[1] = crm << IDT_TCT_CRM_SHIFT; 971117632Sharti tct[3] = IDT_TCT_HALT | IDT_TCT_IDLE | 972117632Sharti (4 << IDT_TCT_NAGE_SHIFT); 973117632Sharti tct[4] = mcr << IDT_TCT_LMCR_SHIFT; 974117632Sharti tct[5] = (cdf << IDT_TCT_CDF_SHIFT) | 975117632Sharti (rdf << IDT_TCT_RDF_SHIFT) | 976117632Sharti (air << IDT_TCT_AIR_SHIFT); 977117632Sharti 978117632Sharti sc->bwrem -= vcc->vcc.tparam.mcr; 979117632Sharti break; 980117632Sharti } 981117632Sharti } 982117632Sharti 983117632Sharti patm_sram_write4(sc, sram + 0, tct[0], tct[1], tct[2], tct[3]); 984117632Sharti patm_sram_write4(sc, sram + 4, tct[4], tct[5], tct[6], tct[7]); 985117632Sharti 986117632Sharti patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x %08x %08x %08x %08x", 987117632Sharti sram / 8, patm_sram_read(sc, sram + 0), 988117632Sharti patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2), 989117632Sharti patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4), 990117632Sharti patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6), 991117632Sharti patm_sram_read(sc, sram + 7)); 992117632Sharti} 993117632Sharti 994117632Sharti/* 995117632Sharti * Start a channel 996117632Sharti */ 997117632Shartistatic void 998117632Shartipatm_tct_start(struct patm_softc *sc, struct patm_vcc *vcc) 999117632Sharti{ 1000117632Sharti 1001117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_UIER(vcc->cid, 1002117632Sharti vcc->scd->init_er)); 1003117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_SLACR(vcc->cid, 1004117632Sharti vcc->scd->lacr)); 1005117632Sharti} 1006117632Sharti 1007117632Shartistatic void 1008117632Shartipatm_tct_print(struct patm_softc *sc, u_int cid) 1009117632Sharti{ 1010117632Sharti#ifdef PATM_DEBUG 1011117632Sharti u_int sram = cid * 8; 1012117632Sharti#endif 1013117632Sharti 1014117632Sharti patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x %08x %08x %08x %08x", 1015117632Sharti sram / 8, patm_sram_read(sc, sram + 0), 1016117632Sharti patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2), 1017117632Sharti patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4), 1018117632Sharti patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6), 1019117632Sharti patm_sram_read(sc, sram + 7)); 1020117632Sharti} 1021117632Sharti 1022117632Sharti/* 1023117632Sharti * Setup the SCD 1024117632Sharti */ 1025117632Shartivoid 1026117632Shartipatm_scd_setup(struct patm_softc *sc, struct patm_scd *scd) 1027117632Sharti{ 1028117632Sharti patm_sram_write4(sc, scd->sram + 0, 1029117632Sharti scd->phy, 0, 0xffffffff, 0); 1030117632Sharti patm_sram_write4(sc, scd->sram + 4, 1031117632Sharti 0, 0, 0, 0); 1032117632Sharti 1033117632Sharti patm_debug(sc, VCC, "SCD(%x): %08x %08x %08x %08x %08x %08x %08x %08x", 1034117632Sharti scd->sram, 1035117632Sharti patm_sram_read(sc, scd->sram + 0), 1036117632Sharti patm_sram_read(sc, scd->sram + 1), 1037117632Sharti patm_sram_read(sc, scd->sram + 2), 1038117632Sharti patm_sram_read(sc, scd->sram + 3), 1039117632Sharti patm_sram_read(sc, scd->sram + 4), 1040117632Sharti patm_sram_read(sc, scd->sram + 5), 1041117632Sharti patm_sram_read(sc, scd->sram + 6), 1042117632Sharti patm_sram_read(sc, scd->sram + 7)); 1043117632Sharti} 1044117632Sharti 1045117632Sharti/* 1046117632Sharti * Grow the TX map table if possible 1047117632Sharti */ 1048117632Shartistatic void 1049117632Shartipatm_txmaps_grow(struct patm_softc *sc) 1050117632Sharti{ 1051117632Sharti u_int i; 1052117632Sharti struct patm_txmap *map; 1053117632Sharti int err; 1054117632Sharti 1055117632Sharti if (sc->tx_nmaps >= sc->tx_maxmaps) 1056117632Sharti return; 1057117632Sharti 1058117632Sharti for (i = sc->tx_nmaps; i < sc->tx_nmaps + PATM_CFG_TXMAPS_STEP; i++) { 1059117632Sharti map = uma_zalloc(sc->tx_mapzone, M_NOWAIT); 1060117632Sharti err = bus_dmamap_create(sc->tx_tag, 0, &map->map); 1061117632Sharti if (err) { 1062117632Sharti uma_zfree(sc->tx_mapzone, map); 1063117632Sharti break; 1064117632Sharti } 1065117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 1066117632Sharti } 1067117632Sharti 1068117632Sharti sc->tx_nmaps = i; 1069117632Sharti} 1070117632Sharti 1071117632Sharti/* 1072117632Sharti * Allocate a transmission map 1073117632Sharti */ 1074117632Shartistatic struct patm_txmap * 1075117632Shartipatm_txmap_get(struct patm_softc *sc) 1076117632Sharti{ 1077117632Sharti struct patm_txmap *map; 1078117632Sharti 1079117632Sharti if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL) { 1080117632Sharti patm_txmaps_grow(sc); 1081117632Sharti if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL) 1082117632Sharti return (NULL); 1083117632Sharti } 1084117632Sharti SLIST_REMOVE_HEAD(&sc->tx_maps_free, link); 1085117632Sharti return (map); 1086117632Sharti} 1087117632Sharti 1088117632Sharti/* 1089117632Sharti * Look whether we are in the process of updating the TST on the chip. 1090117632Sharti * If we are set the flag that we need another update. 1091117632Sharti * If we are not start the update. 1092117632Sharti */ 1093117632Shartistatic __inline void 1094117632Shartipatm_tst_start(struct patm_softc *sc) 1095117632Sharti{ 1096117632Sharti 1097117632Sharti if (!(sc->tst_state & TST_PENDING)) { 1098117632Sharti sc->tst_state |= TST_PENDING; 1099117632Sharti if (!(sc->tst_state & TST_WAIT)) { 1100117632Sharti /* timer not running */ 1101117632Sharti patm_tst_update(sc); 1102117632Sharti } 1103117632Sharti } 1104117632Sharti} 1105117632Sharti 1106117632Sharti/* 1107117632Sharti * Allocate TST entries to a CBR connection 1108117632Sharti */ 1109117632Shartistatic void 1110117632Shartipatm_tst_alloc(struct patm_softc *sc, struct patm_vcc *vcc) 1111117632Sharti{ 1112117632Sharti u_int slots; 1113117632Sharti u_int qptr, pptr; 1114117632Sharti u_int qmax, pmax; 1115117632Sharti u_int pspc, last; 1116117632Sharti 1117117632Sharti mtx_lock(&sc->tst_lock); 1118117632Sharti 1119117632Sharti /* compute the number of slots we need, make sure to get at least 1120117632Sharti * the specified PCR */ 1121117632Sharti slots = cbr2slots(sc, vcc); 1122117632Sharti vcc->scd->slots = slots; 1123117632Sharti sc->bwrem -= slots2cr(sc, slots); 1124117632Sharti 1125117632Sharti patm_debug(sc, TST, "tst_alloc: cbr=%u link=%u tst=%u slots=%u", 1126117632Sharti vcc->vcc.tparam.pcr, sc->ifatm.mib.pcr, sc->mmap->tst_size, slots); 1127117632Sharti 1128117632Sharti qmax = sc->mmap->tst_size - 1; 1129117632Sharti pmax = qmax << 8; 1130117632Sharti 1131117632Sharti pspc = pmax / slots; 1132117632Sharti 1133117632Sharti pptr = pspc >> 1; /* starting point */ 1134117632Sharti qptr = pptr >> 8; 1135117632Sharti 1136117632Sharti last = qptr; 1137117632Sharti 1138117632Sharti while (slots > 0) { 1139117632Sharti if (qptr >= qmax) 1140117632Sharti qptr -= qmax; 1141117632Sharti if (sc->tst_soft[qptr] != IDT_TST_VBR) { 1142117632Sharti /* used - try next */ 1143117632Sharti qptr++; 1144117632Sharti continue; 1145117632Sharti } 1146117632Sharti patm_debug(sc, TST, "slot[%u] = %u.%u diff=%d", qptr, 1147117632Sharti vcc->vcc.vpi, vcc->vcc.vci, (int)qptr - (int)last); 1148117632Sharti last = qptr; 1149117632Sharti 1150117632Sharti sc->tst_soft[qptr] = IDT_TST_CBR | vcc->cid | TST_BOTH; 1151117632Sharti sc->tst_free--; 1152117632Sharti 1153117632Sharti if ((pptr += pspc) >= pmax) 1154117632Sharti pptr -= pmax; 1155117632Sharti qptr = pptr >> 8; 1156117632Sharti 1157117632Sharti slots--; 1158117632Sharti } 1159117632Sharti patm_tst_start(sc); 1160117632Sharti mtx_unlock(&sc->tst_lock); 1161117632Sharti} 1162117632Sharti 1163117632Sharti/* 1164117632Sharti * Free a CBR connection's TST entries 1165117632Sharti */ 1166117632Shartistatic void 1167117632Shartipatm_tst_free(struct patm_softc *sc, struct patm_vcc *vcc) 1168117632Sharti{ 1169117632Sharti u_int i; 1170117632Sharti 1171117632Sharti mtx_lock(&sc->tst_lock); 1172117632Sharti for (i = 0; i < sc->mmap->tst_size - 1; i++) { 1173117632Sharti if ((sc->tst_soft[i] & IDT_TST_MASK) == vcc->cid) { 1174117632Sharti sc->tst_soft[i] = IDT_TST_VBR | TST_BOTH; 1175117632Sharti sc->tst_free++; 1176117632Sharti } 1177117632Sharti } 1178117632Sharti sc->bwrem += slots2cr(sc, vcc->scd->slots); 1179117632Sharti patm_tst_start(sc); 1180117632Sharti mtx_unlock(&sc->tst_lock); 1181117632Sharti} 1182117632Sharti 1183117632Sharti/* 1184117632Sharti * Write the soft TST into the idle incore TST and start the wait timer. 1185117632Sharti * We assume that we hold the tst lock. 1186117632Sharti */ 1187117632Shartistatic void 1188117632Shartipatm_tst_update(struct patm_softc *sc) 1189117632Sharti{ 1190117632Sharti u_int flag; /* flag to clear from soft TST */ 1191117632Sharti u_int idle; /* the idle TST */ 1192117632Sharti u_int act; /* the active TST */ 1193117632Sharti u_int i; 1194117632Sharti 1195117632Sharti if (sc->tst_state & TST_ACT1) { 1196117632Sharti act = 1; 1197117632Sharti idle = 0; 1198117632Sharti flag = TST_CH0; 1199117632Sharti } else { 1200117632Sharti act = 0; 1201117632Sharti idle = 1; 1202117632Sharti flag = TST_CH1; 1203117632Sharti } 1204117632Sharti /* update the idle one */ 1205117632Sharti for (i = 0; i < sc->mmap->tst_size - 1; i++) 1206117632Sharti if (sc->tst_soft[i] & flag) { 1207117632Sharti patm_sram_write(sc, sc->tst_base[idle] + i, 1208117632Sharti sc->tst_soft[i] & ~TST_BOTH); 1209117632Sharti sc->tst_soft[i] &= ~flag; 1210117632Sharti } 1211117632Sharti /* the used one jump to the idle one */ 1212117632Sharti patm_sram_write(sc, sc->tst_jump[act], 1213117632Sharti IDT_TST_BR | (sc->tst_base[idle] << 2)); 1214117632Sharti 1215117632Sharti /* wait for the chip to jump */ 1216117632Sharti sc->tst_state &= ~TST_PENDING; 1217117632Sharti sc->tst_state |= TST_WAIT; 1218117632Sharti 1219117632Sharti callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc); 1220117632Sharti} 1221117632Sharti 1222117632Sharti/* 1223117632Sharti * Timer for TST updates 1224117632Sharti */ 1225117632Shartistatic void 1226117632Shartipatm_tst_timer(void *p) 1227117632Sharti{ 1228117632Sharti struct patm_softc *sc = p; 1229117632Sharti u_int act; /* active TST */ 1230117632Sharti u_int now; /* current place in TST */ 1231117632Sharti 1232117632Sharti mtx_lock(&sc->tst_lock); 1233117632Sharti 1234117632Sharti if (sc->tst_state & TST_WAIT) { 1235117632Sharti /* ignore the PENDING state while we are waiting for 1236117632Sharti * the chip to switch tables. Once the switch is done, 1237117632Sharti * we will again lock at PENDING */ 1238117632Sharti act = (sc->tst_state & TST_ACT1) ? 1 : 0; 1239117632Sharti now = patm_nor_read(sc, IDT_NOR_NOW) >> 2; 1240117632Sharti if (now >= sc->tst_base[act] && now <= sc->tst_jump[act]) { 1241117632Sharti /* not yet */ 1242117632Sharti callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc); 1243117632Sharti goto done; 1244117632Sharti } 1245117632Sharti sc->tst_state &= ~TST_WAIT; 1246117632Sharti /* change back jump */ 1247117632Sharti patm_sram_write(sc, sc->tst_jump[act], 1248117632Sharti IDT_TST_BR | (sc->tst_base[act] << 2)); 1249117632Sharti 1250117632Sharti /* switch */ 1251117632Sharti sc->tst_state ^= TST_ACT1; 1252117632Sharti } 1253117632Sharti 1254117632Sharti if (sc->tst_state & TST_PENDING) 1255117632Sharti /* we got another update request while the timer was running. */ 1256117632Sharti patm_tst_update(sc); 1257117632Sharti 1258117632Sharti done: 1259117632Sharti mtx_unlock(&sc->tst_lock); 1260117632Sharti} 1261117632Sharti 1262117632Shartistatic const char * 1263117632Shartidump_scd(struct patm_softc *sc, struct patm_scd *scd) 1264117632Sharti{ 1265117632Sharti u_int i; 1266117632Sharti 1267117632Sharti for (i = 0; i < IDT_TSQE_TAG_SPACE; i++) 1268117632Sharti printf("on_card[%u] = %p\n", i, scd->on_card[i]); 1269117632Sharti printf("space=%u tag=%u num_on_card=%u last_tag=%u\n", 1270117632Sharti scd->space, scd->tag, scd->num_on_card, scd->last_tag); 1271117632Sharti 1272117632Sharti return (""); 1273117632Sharti} 1274