if_patm_tx.c revision 118158
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 118158 2003-07-29 13:21:57Z 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; 424117872Sharti m0->m_pkthdr.len += pad; 425117632Sharti if (M_WRITABLE(last)) { 426117632Sharti if (M_TRAILINGSPACE(last) >= pad) { 427117632Sharti bzero(last->m_data + last->m_len, pad); 428117632Sharti last->m_len += pad; 429117632Sharti return (m0); 430117632Sharti } 431117632Sharti space = M_LEADINGSPACE(last); 432117632Sharti if (space + M_TRAILINGSPACE(last) >= pad) { 433117632Sharti bcopy(last->m_data, last->m_data + space, last->m_len); 434117632Sharti last->m_data -= space; 435117632Sharti bzero(last->m_data + last->m_len, pad); 436117632Sharti last->m_len += pad; 437117632Sharti return (m0); 438117632Sharti } 439117632Sharti } 440117632Sharti MGET(m, M_DONTWAIT, MT_DATA); 441117632Sharti if (m == 0) { 442117632Sharti m_freem(m0); 443117632Sharti sc->ifatm.ifnet.if_oerrors++; 444117632Sharti return (NULL); 445117632Sharti } 446117632Sharti bzero(mtod(m, u_char *), pad); 447117632Sharti m->m_len = pad; 448117632Sharti last->m_next = m; 449117632Sharti 450117632Sharti return (m0); 451117632Sharti} 452117632Sharti 453117632Sharti/* 454117632Sharti * Try to put as many packets from the channels queue onto the channel 455117632Sharti */ 456117632Shartistatic void 457117632Shartipatm_launch(struct patm_softc *sc, struct patm_scd *scd) 458117632Sharti{ 459117632Sharti struct txarg a; 460117632Sharti struct mbuf *m, *tmp; 461117632Sharti u_int segs; 462117632Sharti struct patm_txmap *map; 463117632Sharti int error; 464117632Sharti 465117632Sharti a.sc = sc; 466117632Sharti a.scd = scd; 467117632Sharti 468117632Sharti /* limit the number of outstanding packets to the tag space */ 469117632Sharti while (scd->num_on_card < IDT_TSQE_TAG_SPACE) { 470117632Sharti /* get the next packet */ 471117632Sharti _IF_DEQUEUE(&scd->q, m); 472117632Sharti if (m == NULL) 473117632Sharti break; 474117632Sharti 475117632Sharti a.vcc = m->m_pkthdr.header; 476117632Sharti 477117632Sharti /* we must know the number of segments beforehand - count 478117632Sharti * this may actually give a wrong number of segments for 479117632Sharti * AAL_RAW where we still need to remove the cell header */ 480117632Sharti segs = 0; 481117632Sharti for (tmp = m; tmp != NULL; tmp = tmp->m_next) 482117632Sharti if (tmp->m_len != 0) 483117632Sharti segs++; 484117632Sharti 485117632Sharti /* check whether there is space in the queue */ 486117632Sharti if (segs >= scd->space) { 487117632Sharti /* put back */ 488117632Sharti _IF_PREPEND(&scd->q, m); 489117632Sharti sc->stats.tx_out_of_tbds++; 490117632Sharti break; 491117632Sharti } 492117632Sharti 493117632Sharti /* get a DMA map */ 494117632Sharti if ((map = patm_txmap_get(sc)) == NULL) { 495117632Sharti _IF_PREPEND(&scd->q, m); 496117632Sharti sc->stats.tx_out_of_maps++; 497117632Sharti break; 498117632Sharti } 499117632Sharti 500117632Sharti /* load the map */ 501117632Sharti m->m_pkthdr.header = map; 502117632Sharti a.mbuf = m; 503117632Sharti 504117632Sharti /* handle AAL_RAW */ 505117632Sharti if (a.vcc->vcc.aal == ATMIO_AAL_RAW) { 506117632Sharti u_char hdr[4]; 507117632Sharti 508117632Sharti m_copydata(m, 0, 4, hdr); 509117632Sharti a.hdr = (hdr[0] << 24) | (hdr[1] << 16) | 510117632Sharti (hdr[2] << 8) | hdr[3]; 511117632Sharti 512117632Sharti switch (a.vcc->vflags & PATM_RAW_FORMAT) { 513117632Sharti 514117632Sharti default: 515117632Sharti case PATM_RAW_CELL: 516117632Sharti m_adj(m, 5); 517117632Sharti break; 518117632Sharti 519117632Sharti case PATM_RAW_NOHEC: 520117632Sharti m_adj(m, 4); 521117632Sharti break; 522117632Sharti 523117632Sharti case PATM_RAW_CS: 524117632Sharti m_adj(m, 16); 525117632Sharti break; 526117632Sharti } 527117632Sharti } else 528117632Sharti a.hdr = IDT_TBD_HDR(a.vcc->vcc.vpi, a.vcc->vcc.vci, 529117632Sharti 0, 0); 530117632Sharti 531117632Sharti error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m, 532117632Sharti patm_load_txbuf, &a, BUS_DMA_NOWAIT); 533117632Sharti if (error == EFBIG) { 534117632Sharti if ((m = m_defrag(m, M_DONTWAIT)) == NULL) { 535117632Sharti sc->ifatm.ifnet.if_oerrors++; 536117632Sharti continue; 537117632Sharti } 538117632Sharti error = bus_dmamap_load_mbuf(sc->tx_tag, map->map, m, 539117632Sharti patm_load_txbuf, &a, BUS_DMA_NOWAIT); 540117632Sharti } 541117632Sharti if (error != 0) { 542117632Sharti sc->stats.tx_load_err++; 543117632Sharti sc->ifatm.ifnet.if_oerrors++; 544117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 545117632Sharti m_freem(m); 546117632Sharti continue; 547117632Sharti } 548117632Sharti 549117632Sharti sc->ifatm.ifnet.if_opackets++; 550117632Sharti } 551117632Sharti} 552117632Sharti 553117632Sharti/* 554117632Sharti * Load the DMA segments into the scheduling channel 555117632Sharti */ 556117632Shartistatic void 557117632Shartipatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg, 558117632Sharti bus_size_t mapsize, int error) 559117632Sharti{ 560117632Sharti struct txarg *a= uarg; 561117632Sharti struct patm_scd *scd = a->scd; 562117632Sharti u_int w1, w3, cnt; 563117632Sharti struct idt_tbd *tbd = NULL; 564117632Sharti u_int rest = mapsize; 565117632Sharti 566117632Sharti if (error != 0) 567117632Sharti return; 568117632Sharti 569117632Sharti cnt = 0; 570117632Sharti while (nseg > 0) { 571117632Sharti if (segs->ds_len == 0) { 572117632Sharti /* transmit buffer length must be > 0 */ 573117632Sharti nseg--; 574117632Sharti segs++; 575117632Sharti continue; 576117632Sharti } 577117632Sharti /* rest after this buffer */ 578117632Sharti rest -= segs->ds_len; 579117632Sharti 580117632Sharti /* put together status word */ 581117632Sharti w1 = 0; 582117632Sharti if (rest < 48 /* && a->vcc->vcc.aal != ATMIO_AAL_5 */) 583117632Sharti /* last cell is in this buffer */ 584117632Sharti w1 |= IDT_TBD_EPDU; 585117632Sharti 586117632Sharti if (a->vcc->vcc.aal == ATMIO_AAL_5) 587117632Sharti w1 |= IDT_TBD_AAL5; 588117632Sharti else if (a->vcc->vcc.aal == ATMIO_AAL_34) 589117632Sharti w1 |= IDT_TBD_AAL34; 590117632Sharti else 591117632Sharti w1 |= IDT_TBD_AAL0; 592117632Sharti 593117632Sharti w1 |= segs->ds_len; 594117632Sharti 595117632Sharti /* AAL5 PDU length (unpadded) */ 596117632Sharti if (a->vcc->vcc.aal == ATMIO_AAL_5) 597117632Sharti w3 = mapsize; 598117632Sharti else 599117632Sharti w3 = 0; 600117632Sharti 601117632Sharti if (rest == 0) 602117632Sharti w1 |= IDT_TBD_TSIF | IDT_TBD_GTSI | 603117632Sharti (scd->tag << IDT_TBD_TAG_SHIFT); 604117632Sharti 605117632Sharti tbd = &scd->scq[scd->tail]; 606117632Sharti 607117632Sharti tbd->flags = htole32(w1); 608117632Sharti tbd->addr = htole32(segs->ds_addr); 609117632Sharti tbd->aal5 = htole32(w3); 610117632Sharti tbd->hdr = htole32(a->hdr); 611117632Sharti 612117632Sharti patm_debug(a->sc, TX, "TBD(%u): %08x %08x %08x %08x", 613117632Sharti scd->tail, w1, segs->ds_addr, w3, a->hdr); 614117632Sharti 615117632Sharti /* got to next entry */ 616117632Sharti if (++scd->tail == IDT_SCQ_SIZE) 617117632Sharti scd->tail = 0; 618117632Sharti cnt++; 619117632Sharti nseg--; 620117632Sharti segs++; 621117632Sharti } 622117632Sharti scd->space -= cnt; 623117632Sharti scd->num_on_card++; 624117632Sharti 625117632Sharti KASSERT(rest == 0, ("bad mbuf")); 626117632Sharti KASSERT(cnt > 0, ("no segs")); 627117632Sharti KASSERT(scd->space > 0, ("scq full")); 628117632Sharti 629117632Sharti KASSERT(scd->on_card[scd->tag] == NULL, 630117632Sharti ("scd on_card wedged %u%s", scd->tag, dump_scd(a->sc, scd))); 631117632Sharti scd->on_card[scd->tag] = a->mbuf; 632117632Sharti a->mbuf->m_pkthdr.csum_data = cnt; 633117632Sharti 634117632Sharti NEXT_TAG(scd->tag); 635117632Sharti 636117632Sharti patm_debug(a->sc, TX, "SCD tail %u (%lx:%lx)", scd->tail, 637117632Sharti (u_long)scd->phy, (u_long)scd->phy + (scd->tail << IDT_TBD_SHIFT)); 638117632Sharti patm_sram_write(a->sc, scd->sram, 639117632Sharti scd->phy + (scd->tail << IDT_TBD_SHIFT)); 640117632Sharti 641117632Sharti if (patm_sram_read(a->sc, a->vcc->cid * 8 + 3) & IDT_TCT_IDLE) { 642117632Sharti /* 643117632Sharti * if the connection is idle start it. We cannot rely 644117632Sharti * on a flag set by patm_tx_idle() here, because sometimes 645117632Sharti * the card seems to place an idle TSI into the TSQ but 646117632Sharti * forgets to raise an interrupt. 647117632Sharti */ 648117632Sharti patm_nor_write(a->sc, IDT_NOR_TCMDQ, 649117632Sharti IDT_TCMDQ_START(a->vcc->cid)); 650117632Sharti } 651117632Sharti} 652117632Sharti 653117632Sharti/* 654117632Sharti * packet transmitted 655117632Sharti */ 656117632Shartivoid 657117632Shartipatm_tx(struct patm_softc *sc, u_int stamp, u_int status) 658117632Sharti{ 659117632Sharti u_int cid, tag, last; 660117632Sharti struct mbuf *m; 661117632Sharti struct patm_vcc *vcc; 662117632Sharti struct patm_scd *scd; 663117632Sharti struct patm_txmap *map; 664117632Sharti 665117632Sharti /* get the connection */ 666117632Sharti cid = PATM_CID(sc, IDT_TBD_VPI(status), IDT_TBD_VCI(status)); 667117632Sharti if ((vcc = sc->vccs[cid]) == NULL) { 668117632Sharti /* closed UBR connection */ 669117632Sharti return; 670117632Sharti } 671117632Sharti scd = vcc->scd; 672117632Sharti 673117632Sharti tag = IDT_TSQE_TAG(stamp); 674117632Sharti 675117632Sharti last = scd->last_tag; 676117632Sharti if (tag == last) { 677117632Sharti patm_printf(sc, "same tag %u\n", tag); 678117632Sharti return; 679117632Sharti } 680117632Sharti 681117632Sharti /* Errata 12 requests us to free all entries up to the one 682117632Sharti * with the given tag. */ 683117632Sharti do { 684117632Sharti /* next tag to try */ 685117632Sharti NEXT_TAG(last); 686117632Sharti 687117632Sharti m = scd->on_card[last]; 688117632Sharti KASSERT(m != NULL, ("%stag=%u", dump_scd(sc, scd), tag)); 689117632Sharti scd->on_card[last] = NULL; 690117632Sharti patm_debug(sc, TX, "ok tag=%x", last); 691117632Sharti 692117632Sharti map = m->m_pkthdr.header; 693117632Sharti scd->space += m->m_pkthdr.csum_data; 694117632Sharti 695117632Sharti bus_dmamap_sync(sc->tx_tag, map->map, 696117632Sharti BUS_DMASYNC_POSTWRITE); 697117632Sharti bus_dmamap_unload(sc->tx_tag, map->map); 698117632Sharti m_freem(m); 699117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 700117632Sharti scd->num_on_card--; 701117632Sharti 702117632Sharti if (vcc->vflags & PATM_VCC_TX_CLOSING) { 703117632Sharti if (scd->num_on_card == 0) { 704117632Sharti /* done with this VCC */ 705117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_CBR) 706117632Sharti patm_tst_free(sc, vcc); 707117632Sharti 708117632Sharti patm_sram_write4(sc, scd->sram + 0, 0, 0, 0, 0); 709117632Sharti patm_sram_write4(sc, scd->sram + 4, 0, 0, 0, 0); 710117632Sharti patm_scd_free(sc, scd); 711117632Sharti 712117632Sharti vcc->scd = NULL; 713117632Sharti vcc->vflags &= ~PATM_VCC_TX_CLOSING; 714117632Sharti 715117632Sharti if (vcc->vflags & PATM_VCC_ASYNC) { 716117632Sharti patm_tx_vcc_closed(sc, vcc); 717117632Sharti if (!(vcc->vflags & PATM_VCC_OPEN)) 718117632Sharti patm_vcc_closed(sc, vcc); 719117632Sharti } else 720117632Sharti cv_signal(&sc->vcc_cv); 721117632Sharti return; 722117632Sharti } 723117632Sharti patm_debug(sc, VCC, "%u buffers still on card", 724117632Sharti scd->num_on_card); 725117632Sharti 726117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) { 727117632Sharti /* insist on speeding up transmission for ABR */ 728117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 729117632Sharti IDT_TCMDQ_UIER(vcc->cid, 0xff)); 730117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 731117632Sharti IDT_TCMDQ_ULACR(vcc->cid, 0xff)); 732117632Sharti } 733117632Sharti } 734117632Sharti 735117632Sharti } while (last != tag); 736117632Sharti scd->last_tag = tag; 737117632Sharti 738117632Sharti if (vcc->vcc.traffic == ATMIO_TRAFFIC_ABR) { 739117632Sharti u_int acri, cps; 740117632Sharti 741117632Sharti acri = (patm_sram_read(sc, 8 * cid + 2) >> IDT_TCT_ACRI_SHIFT) 742117632Sharti & 0x3fff; 743117632Sharti cps = sc->ifatm.mib.pcr * 32 / 744117632Sharti ((1 << (acri >> 10)) * (acri & 0x3ff)); 745117632Sharti 746117632Sharti if (cps != vcc->cps) { 747117632Sharti patm_debug(sc, VCC, "ACRI=%04x CPS=%u", acri, cps); 748118158Sharti ATMEV_SEND_ACR_CHANGED(&sc->ifatm, vcc->vcc.vpi, 749118158Sharti vcc->vcc.vci, cps); 750117632Sharti vcc->cps = cps; 751117632Sharti } 752117632Sharti } 753117632Sharti 754117632Sharti patm_launch(sc, scd); 755117632Sharti} 756117632Sharti 757117632Sharti/* 758117632Sharti * VBR/ABR connection went idle 759117632Sharti * Either restart it or set the idle flag. 760117632Sharti */ 761117632Shartivoid 762117632Shartipatm_tx_idle(struct patm_softc *sc, u_int cid) 763117632Sharti{ 764117632Sharti struct patm_vcc *vcc; 765117632Sharti 766117632Sharti patm_debug(sc, VCC, "idle %u", cid); 767117632Sharti 768117632Sharti if ((vcc = sc->vccs[cid]) != NULL && 769117632Sharti (vcc->vflags & (PATM_VCC_TX_OPEN | PATM_VCC_TX_CLOSING)) != 0 && 770117632Sharti vcc->scd != NULL && (vcc->scd->num_on_card != 0 || 771117632Sharti _IF_QLEN(&vcc->scd->q) != 0)) { 772117632Sharti /* 773117632Sharti * If there is any packet outstanding in the SCD re-activate 774117632Sharti * the channel and kick it. 775117632Sharti */ 776117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, 777117632Sharti IDT_TCMDQ_START(vcc->cid)); 778117632Sharti 779117632Sharti patm_launch(sc, vcc->scd); 780117632Sharti } 781117632Sharti} 782117632Sharti 783117632Sharti/* 784117632Sharti * Convert a (24bit) rate to the atm-forum form 785117632Sharti * Our rate is never larger than 19 bit. 786117632Sharti */ 787117632Shartistatic u_int 788117632Sharticps2atmf(u_int cps) 789117632Sharti{ 790117632Sharti u_int e; 791117632Sharti 792117632Sharti if (cps == 0) 793117632Sharti return (0); 794117632Sharti cps <<= 9; 795117632Sharti e = 0; 796117632Sharti while (cps > (1024 - 1)) { 797117632Sharti e++; 798117632Sharti cps >>= 1; 799117632Sharti } 800117632Sharti return ((1 << 14) | (e << 9) | (cps & 0x1ff)); 801117632Sharti} 802117632Sharti 803117632Sharti/* 804117632Sharti * Do a binary search on the log2rate table to convert the rate 805117632Sharti * to its log form. This assumes that the ATM-Forum form is monotonically 806117632Sharti * increasing with the plain cell rate. 807117632Sharti */ 808117632Shartistatic u_int 809117632Shartirate2log(struct patm_softc *sc, u_int rate) 810117632Sharti{ 811117632Sharti const uint32_t *tbl; 812117632Sharti u_int lower, upper, mid, done, val, afr; 813117632Sharti 814117632Sharti afr = cps2atmf(rate); 815117632Sharti 816117632Sharti if (sc->flags & PATM_25M) 817117632Sharti tbl = patm_rtables25; 818117632Sharti else 819117632Sharti tbl = patm_rtables155; 820117632Sharti 821117632Sharti lower = 0; 822117632Sharti upper = 255; 823117632Sharti done = 0; 824117632Sharti while (!done) { 825117632Sharti mid = (lower + upper) / 2; 826117632Sharti val = tbl[mid] >> 17; 827117632Sharti if (val == afr || upper == lower) 828117632Sharti break; 829117632Sharti if (afr > val) 830117632Sharti lower = mid + 1; 831117632Sharti else 832117632Sharti upper = mid - 1; 833117632Sharti } 834117632Sharti if (val > afr && mid > 0) 835117632Sharti mid--; 836117632Sharti return (mid); 837117632Sharti} 838117632Sharti 839117632Sharti/* 840117632Sharti * Return the table index for an increase table. The increase table 841117632Sharti * must be selected not by the RIF itself, but by PCR/2^RIF. Each table 842117632Sharti * represents an additive increase of a cell rate that can be computed 843117632Sharti * from the first table entry (the value in this entry will not be clamped 844117632Sharti * by the link rate). 845117632Sharti */ 846117632Shartistatic u_int 847117632Shartiget_air_table(struct patm_softc *sc, u_int rif, u_int pcr) 848117632Sharti{ 849117632Sharti const uint32_t *tbl; 850117632Sharti u_int increase, base, lair0, ret, t, cps; 851117632Sharti 852117632Sharti#define GET_ENTRY(TAB, IDX) (0xffff & ((IDX & 1) ? \ 853117632Sharti (tbl[512 + (IDX / 2) + 128 * (TAB)] >> 16) : \ 854117632Sharti (tbl[512 + (IDX / 2) + 128 * (TAB)]))) 855117632Sharti 856117632Sharti#define MANT_BITS 10 857117632Sharti#define FRAC_BITS 16 858117632Sharti 859117632Sharti#define DIFF_TO_FP(D) (((D) & ((1 << MANT_BITS) - 1)) << ((D) >> MANT_BITS)) 860117632Sharti#define AFR_TO_INT(A) ((1 << (((A) >> 9) & 0x1f)) * \ 861117632Sharti (512 + ((A) & 0x1ff)) / 512 * ((A) >> 14)) 862117632Sharti 863117632Sharti if (sc->flags & PATM_25M) 864117632Sharti tbl = patm_rtables25; 865117632Sharti else 866117632Sharti tbl = patm_rtables155; 867117632Sharti if (rif >= patm_rtables_ntab) 868117632Sharti rif = patm_rtables_ntab - 1; 869117632Sharti increase = pcr >> rif; 870117632Sharti 871117632Sharti ret = 0; 872117632Sharti for (t = 0; t < patm_rtables_ntab; t++) { 873117632Sharti /* get base rate of this table */ 874117632Sharti base = GET_ENTRY(t, 0); 875117632Sharti /* convert this to fixed point */ 876117632Sharti lair0 = DIFF_TO_FP(base) >> FRAC_BITS; 877117632Sharti 878117632Sharti /* get the CPS from the log2rate table */ 879117632Sharti cps = AFR_TO_INT(tbl[lair0] >> 17) - 10; 880117632Sharti 881117632Sharti if (increase >= cps) 882117632Sharti break; 883117632Sharti 884117632Sharti ret = t; 885117632Sharti } 886117632Sharti return (ret + 4); 887117632Sharti} 888117632Sharti 889117632Sharti/* 890117632Sharti * Setup the TCT 891117632Sharti */ 892117632Shartivoid 893117632Shartipatm_tct_setup(struct patm_softc *sc, struct patm_scd *scd, 894117632Sharti struct patm_vcc *vcc) 895117632Sharti{ 896117632Sharti uint32_t tct[8]; 897117632Sharti u_int sram; 898117632Sharti u_int mbs, token; 899117632Sharti u_int tmp, crm, rdf, cdf, air, mcr; 900117632Sharti 901117632Sharti bzero(tct, sizeof(tct)); 902117632Sharti if (vcc == NULL) { 903117632Sharti /* special case for UBR0 */ 904117632Sharti sram = 0; 905117632Sharti tct[0] = IDT_TCT_UBR | scd->sram; 906117632Sharti tct[7] = IDT_TCT_UBR_FLG; 907117632Sharti 908117632Sharti } else { 909117632Sharti sram = vcc->cid * 8; 910117632Sharti switch (vcc->vcc.traffic) { 911117632Sharti 912117632Sharti case ATMIO_TRAFFIC_CBR: 913117632Sharti patm_tst_alloc(sc, vcc); 914117632Sharti tct[0] = IDT_TCT_CBR | scd->sram; 915117632Sharti /* must account for what was really allocated */ 916117632Sharti break; 917117632Sharti 918117632Sharti case ATMIO_TRAFFIC_VBR: 919117632Sharti /* compute parameters for the TCT */ 920117632Sharti scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr); 921117632Sharti scd->lacr = rate2log(sc, vcc->vcc.tparam.scr); 922117632Sharti 923117632Sharti /* get the 16-bit fraction of SCR/PCR 924117632Sharti * both a 24 bit. Do it the simple way. */ 925117632Sharti token = (uint64_t)(vcc->vcc.tparam.scr << 16) / 926117632Sharti vcc->vcc.tparam.pcr; 927117632Sharti 928117632Sharti patm_debug(sc, VCC, "VBR: init_er=%u lacr=%u " 929117632Sharti "token=0x%04x\n", scd->init_er, scd->lacr, token); 930117632Sharti 931117632Sharti tct[0] = IDT_TCT_VBR | scd->sram; 932117632Sharti tct[2] = IDT_TCT_TSIF; 933117632Sharti tct[3] = IDT_TCT_IDLE | IDT_TCT_HALT; 934117632Sharti tct[4] = IDT_TCT_MAXIDLE; 935117632Sharti tct[5] = 0x01000000; 936117632Sharti if ((mbs = vcc->vcc.tparam.mbs) > 0xff) 937117632Sharti mbs = 0xff; 938117632Sharti tct[6] = (mbs << 16) | token; 939117632Sharti sc->bwrem -= vcc->vcc.tparam.scr; 940117632Sharti break; 941117632Sharti 942117632Sharti case ATMIO_TRAFFIC_ABR: 943117632Sharti scd->init_er = rate2log(sc, vcc->vcc.tparam.pcr); 944117632Sharti scd->lacr = rate2log(sc, vcc->vcc.tparam.icr); 945117632Sharti mcr = rate2log(sc, vcc->vcc.tparam.mcr); 946117632Sharti 947117632Sharti /* compute CRM */ 948117632Sharti tmp = vcc->vcc.tparam.tbe / vcc->vcc.tparam.nrm; 949117632Sharti if (tmp * vcc->vcc.tparam.nrm < vcc->vcc.tparam.tbe) 950117632Sharti tmp++; 951117632Sharti for (crm = 1; tmp > (1 << crm); crm++) 952117632Sharti ; 953117632Sharti if (crm > 0x7) 954117632Sharti crm = 7; 955117632Sharti 956117632Sharti air = get_air_table(sc, vcc->vcc.tparam.rif, 957117632Sharti vcc->vcc.tparam.pcr); 958117632Sharti 959117632Sharti if ((rdf = vcc->vcc.tparam.rdf) >= patm_rtables_ntab) 960117632Sharti rdf = patm_rtables_ntab - 1; 961117632Sharti rdf += patm_rtables_ntab + 4; 962117632Sharti 963117632Sharti if ((cdf = vcc->vcc.tparam.cdf) >= patm_rtables_ntab) 964117632Sharti cdf = patm_rtables_ntab - 1; 965117632Sharti cdf += patm_rtables_ntab + 4; 966117632Sharti 967117632Sharti patm_debug(sc, VCC, "ABR: init_er=%u lacr=%u mcr=%u " 968117632Sharti "crm=%u air=%u rdf=%u cdf=%u\n", scd->init_er, 969117632Sharti scd->lacr, mcr, crm, air, rdf, cdf); 970117632Sharti 971117632Sharti tct[0] = IDT_TCT_ABR | scd->sram; 972117632Sharti tct[1] = crm << IDT_TCT_CRM_SHIFT; 973117632Sharti tct[3] = IDT_TCT_HALT | IDT_TCT_IDLE | 974117632Sharti (4 << IDT_TCT_NAGE_SHIFT); 975117632Sharti tct[4] = mcr << IDT_TCT_LMCR_SHIFT; 976117632Sharti tct[5] = (cdf << IDT_TCT_CDF_SHIFT) | 977117632Sharti (rdf << IDT_TCT_RDF_SHIFT) | 978117632Sharti (air << IDT_TCT_AIR_SHIFT); 979117632Sharti 980117632Sharti sc->bwrem -= vcc->vcc.tparam.mcr; 981117632Sharti break; 982117632Sharti } 983117632Sharti } 984117632Sharti 985117632Sharti patm_sram_write4(sc, sram + 0, tct[0], tct[1], tct[2], tct[3]); 986117632Sharti patm_sram_write4(sc, sram + 4, tct[4], tct[5], tct[6], tct[7]); 987117632Sharti 988117632Sharti patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x %08x %08x %08x %08x", 989117632Sharti sram / 8, patm_sram_read(sc, sram + 0), 990117632Sharti patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2), 991117632Sharti patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4), 992117632Sharti patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6), 993117632Sharti patm_sram_read(sc, sram + 7)); 994117632Sharti} 995117632Sharti 996117632Sharti/* 997117632Sharti * Start a channel 998117632Sharti */ 999117632Shartistatic void 1000117632Shartipatm_tct_start(struct patm_softc *sc, struct patm_vcc *vcc) 1001117632Sharti{ 1002117632Sharti 1003117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_UIER(vcc->cid, 1004117632Sharti vcc->scd->init_er)); 1005117632Sharti patm_nor_write(sc, IDT_NOR_TCMDQ, IDT_TCMDQ_SLACR(vcc->cid, 1006117632Sharti vcc->scd->lacr)); 1007117632Sharti} 1008117632Sharti 1009117632Shartistatic void 1010117632Shartipatm_tct_print(struct patm_softc *sc, u_int cid) 1011117632Sharti{ 1012117632Sharti#ifdef PATM_DEBUG 1013117632Sharti u_int sram = cid * 8; 1014117632Sharti#endif 1015117632Sharti 1016117632Sharti patm_debug(sc, VCC, "TCT[%u]: %08x %08x %08x %08x %08x %08x %08x %08x", 1017117632Sharti sram / 8, patm_sram_read(sc, sram + 0), 1018117632Sharti patm_sram_read(sc, sram + 1), patm_sram_read(sc, sram + 2), 1019117632Sharti patm_sram_read(sc, sram + 3), patm_sram_read(sc, sram + 4), 1020117632Sharti patm_sram_read(sc, sram + 5), patm_sram_read(sc, sram + 6), 1021117632Sharti patm_sram_read(sc, sram + 7)); 1022117632Sharti} 1023117632Sharti 1024117632Sharti/* 1025117632Sharti * Setup the SCD 1026117632Sharti */ 1027117632Shartivoid 1028117632Shartipatm_scd_setup(struct patm_softc *sc, struct patm_scd *scd) 1029117632Sharti{ 1030117632Sharti patm_sram_write4(sc, scd->sram + 0, 1031117632Sharti scd->phy, 0, 0xffffffff, 0); 1032117632Sharti patm_sram_write4(sc, scd->sram + 4, 1033117632Sharti 0, 0, 0, 0); 1034117632Sharti 1035117632Sharti patm_debug(sc, VCC, "SCD(%x): %08x %08x %08x %08x %08x %08x %08x %08x", 1036117632Sharti scd->sram, 1037117632Sharti patm_sram_read(sc, scd->sram + 0), 1038117632Sharti patm_sram_read(sc, scd->sram + 1), 1039117632Sharti patm_sram_read(sc, scd->sram + 2), 1040117632Sharti patm_sram_read(sc, scd->sram + 3), 1041117632Sharti patm_sram_read(sc, scd->sram + 4), 1042117632Sharti patm_sram_read(sc, scd->sram + 5), 1043117632Sharti patm_sram_read(sc, scd->sram + 6), 1044117632Sharti patm_sram_read(sc, scd->sram + 7)); 1045117632Sharti} 1046117632Sharti 1047117632Sharti/* 1048117632Sharti * Grow the TX map table if possible 1049117632Sharti */ 1050117632Shartistatic void 1051117632Shartipatm_txmaps_grow(struct patm_softc *sc) 1052117632Sharti{ 1053117632Sharti u_int i; 1054117632Sharti struct patm_txmap *map; 1055117632Sharti int err; 1056117632Sharti 1057117632Sharti if (sc->tx_nmaps >= sc->tx_maxmaps) 1058117632Sharti return; 1059117632Sharti 1060117632Sharti for (i = sc->tx_nmaps; i < sc->tx_nmaps + PATM_CFG_TXMAPS_STEP; i++) { 1061117632Sharti map = uma_zalloc(sc->tx_mapzone, M_NOWAIT); 1062117632Sharti err = bus_dmamap_create(sc->tx_tag, 0, &map->map); 1063117632Sharti if (err) { 1064117632Sharti uma_zfree(sc->tx_mapzone, map); 1065117632Sharti break; 1066117632Sharti } 1067117632Sharti SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 1068117632Sharti } 1069117632Sharti 1070117632Sharti sc->tx_nmaps = i; 1071117632Sharti} 1072117632Sharti 1073117632Sharti/* 1074117632Sharti * Allocate a transmission map 1075117632Sharti */ 1076117632Shartistatic struct patm_txmap * 1077117632Shartipatm_txmap_get(struct patm_softc *sc) 1078117632Sharti{ 1079117632Sharti struct patm_txmap *map; 1080117632Sharti 1081117632Sharti if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL) { 1082117632Sharti patm_txmaps_grow(sc); 1083117632Sharti if ((map = SLIST_FIRST(&sc->tx_maps_free)) == NULL) 1084117632Sharti return (NULL); 1085117632Sharti } 1086117632Sharti SLIST_REMOVE_HEAD(&sc->tx_maps_free, link); 1087117632Sharti return (map); 1088117632Sharti} 1089117632Sharti 1090117632Sharti/* 1091117632Sharti * Look whether we are in the process of updating the TST on the chip. 1092117632Sharti * If we are set the flag that we need another update. 1093117632Sharti * If we are not start the update. 1094117632Sharti */ 1095117632Shartistatic __inline void 1096117632Shartipatm_tst_start(struct patm_softc *sc) 1097117632Sharti{ 1098117632Sharti 1099117632Sharti if (!(sc->tst_state & TST_PENDING)) { 1100117632Sharti sc->tst_state |= TST_PENDING; 1101117632Sharti if (!(sc->tst_state & TST_WAIT)) { 1102117632Sharti /* timer not running */ 1103117632Sharti patm_tst_update(sc); 1104117632Sharti } 1105117632Sharti } 1106117632Sharti} 1107117632Sharti 1108117632Sharti/* 1109117632Sharti * Allocate TST entries to a CBR connection 1110117632Sharti */ 1111117632Shartistatic void 1112117632Shartipatm_tst_alloc(struct patm_softc *sc, struct patm_vcc *vcc) 1113117632Sharti{ 1114117632Sharti u_int slots; 1115117632Sharti u_int qptr, pptr; 1116117632Sharti u_int qmax, pmax; 1117117632Sharti u_int pspc, last; 1118117632Sharti 1119117632Sharti mtx_lock(&sc->tst_lock); 1120117632Sharti 1121117632Sharti /* compute the number of slots we need, make sure to get at least 1122117632Sharti * the specified PCR */ 1123117632Sharti slots = cbr2slots(sc, vcc); 1124117632Sharti vcc->scd->slots = slots; 1125117632Sharti sc->bwrem -= slots2cr(sc, slots); 1126117632Sharti 1127117632Sharti patm_debug(sc, TST, "tst_alloc: cbr=%u link=%u tst=%u slots=%u", 1128117632Sharti vcc->vcc.tparam.pcr, sc->ifatm.mib.pcr, sc->mmap->tst_size, slots); 1129117632Sharti 1130117632Sharti qmax = sc->mmap->tst_size - 1; 1131117632Sharti pmax = qmax << 8; 1132117632Sharti 1133117632Sharti pspc = pmax / slots; 1134117632Sharti 1135117632Sharti pptr = pspc >> 1; /* starting point */ 1136117632Sharti qptr = pptr >> 8; 1137117632Sharti 1138117632Sharti last = qptr; 1139117632Sharti 1140117632Sharti while (slots > 0) { 1141117632Sharti if (qptr >= qmax) 1142117632Sharti qptr -= qmax; 1143117632Sharti if (sc->tst_soft[qptr] != IDT_TST_VBR) { 1144117632Sharti /* used - try next */ 1145117632Sharti qptr++; 1146117632Sharti continue; 1147117632Sharti } 1148117632Sharti patm_debug(sc, TST, "slot[%u] = %u.%u diff=%d", qptr, 1149117632Sharti vcc->vcc.vpi, vcc->vcc.vci, (int)qptr - (int)last); 1150117632Sharti last = qptr; 1151117632Sharti 1152117632Sharti sc->tst_soft[qptr] = IDT_TST_CBR | vcc->cid | TST_BOTH; 1153117632Sharti sc->tst_free--; 1154117632Sharti 1155117632Sharti if ((pptr += pspc) >= pmax) 1156117632Sharti pptr -= pmax; 1157117632Sharti qptr = pptr >> 8; 1158117632Sharti 1159117632Sharti slots--; 1160117632Sharti } 1161117632Sharti patm_tst_start(sc); 1162117632Sharti mtx_unlock(&sc->tst_lock); 1163117632Sharti} 1164117632Sharti 1165117632Sharti/* 1166117632Sharti * Free a CBR connection's TST entries 1167117632Sharti */ 1168117632Shartistatic void 1169117632Shartipatm_tst_free(struct patm_softc *sc, struct patm_vcc *vcc) 1170117632Sharti{ 1171117632Sharti u_int i; 1172117632Sharti 1173117632Sharti mtx_lock(&sc->tst_lock); 1174117632Sharti for (i = 0; i < sc->mmap->tst_size - 1; i++) { 1175117632Sharti if ((sc->tst_soft[i] & IDT_TST_MASK) == vcc->cid) { 1176117632Sharti sc->tst_soft[i] = IDT_TST_VBR | TST_BOTH; 1177117632Sharti sc->tst_free++; 1178117632Sharti } 1179117632Sharti } 1180117632Sharti sc->bwrem += slots2cr(sc, vcc->scd->slots); 1181117632Sharti patm_tst_start(sc); 1182117632Sharti mtx_unlock(&sc->tst_lock); 1183117632Sharti} 1184117632Sharti 1185117632Sharti/* 1186117632Sharti * Write the soft TST into the idle incore TST and start the wait timer. 1187117632Sharti * We assume that we hold the tst lock. 1188117632Sharti */ 1189117632Shartistatic void 1190117632Shartipatm_tst_update(struct patm_softc *sc) 1191117632Sharti{ 1192117632Sharti u_int flag; /* flag to clear from soft TST */ 1193117632Sharti u_int idle; /* the idle TST */ 1194117632Sharti u_int act; /* the active TST */ 1195117632Sharti u_int i; 1196117632Sharti 1197117632Sharti if (sc->tst_state & TST_ACT1) { 1198117632Sharti act = 1; 1199117632Sharti idle = 0; 1200117632Sharti flag = TST_CH0; 1201117632Sharti } else { 1202117632Sharti act = 0; 1203117632Sharti idle = 1; 1204117632Sharti flag = TST_CH1; 1205117632Sharti } 1206117632Sharti /* update the idle one */ 1207117632Sharti for (i = 0; i < sc->mmap->tst_size - 1; i++) 1208117632Sharti if (sc->tst_soft[i] & flag) { 1209117632Sharti patm_sram_write(sc, sc->tst_base[idle] + i, 1210117632Sharti sc->tst_soft[i] & ~TST_BOTH); 1211117632Sharti sc->tst_soft[i] &= ~flag; 1212117632Sharti } 1213117632Sharti /* the used one jump to the idle one */ 1214117632Sharti patm_sram_write(sc, sc->tst_jump[act], 1215117632Sharti IDT_TST_BR | (sc->tst_base[idle] << 2)); 1216117632Sharti 1217117632Sharti /* wait for the chip to jump */ 1218117632Sharti sc->tst_state &= ~TST_PENDING; 1219117632Sharti sc->tst_state |= TST_WAIT; 1220117632Sharti 1221117632Sharti callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc); 1222117632Sharti} 1223117632Sharti 1224117632Sharti/* 1225117632Sharti * Timer for TST updates 1226117632Sharti */ 1227117632Shartistatic void 1228117632Shartipatm_tst_timer(void *p) 1229117632Sharti{ 1230117632Sharti struct patm_softc *sc = p; 1231117632Sharti u_int act; /* active TST */ 1232117632Sharti u_int now; /* current place in TST */ 1233117632Sharti 1234117632Sharti mtx_lock(&sc->tst_lock); 1235117632Sharti 1236117632Sharti if (sc->tst_state & TST_WAIT) { 1237117632Sharti /* ignore the PENDING state while we are waiting for 1238117632Sharti * the chip to switch tables. Once the switch is done, 1239117632Sharti * we will again lock at PENDING */ 1240117632Sharti act = (sc->tst_state & TST_ACT1) ? 1 : 0; 1241117632Sharti now = patm_nor_read(sc, IDT_NOR_NOW) >> 2; 1242117632Sharti if (now >= sc->tst_base[act] && now <= sc->tst_jump[act]) { 1243117632Sharti /* not yet */ 1244117632Sharti callout_reset(&sc->tst_callout, 1, patm_tst_timer, sc); 1245117632Sharti goto done; 1246117632Sharti } 1247117632Sharti sc->tst_state &= ~TST_WAIT; 1248117632Sharti /* change back jump */ 1249117632Sharti patm_sram_write(sc, sc->tst_jump[act], 1250117632Sharti IDT_TST_BR | (sc->tst_base[act] << 2)); 1251117632Sharti 1252117632Sharti /* switch */ 1253117632Sharti sc->tst_state ^= TST_ACT1; 1254117632Sharti } 1255117632Sharti 1256117632Sharti if (sc->tst_state & TST_PENDING) 1257117632Sharti /* we got another update request while the timer was running. */ 1258117632Sharti patm_tst_update(sc); 1259117632Sharti 1260117632Sharti done: 1261117632Sharti mtx_unlock(&sc->tst_lock); 1262117632Sharti} 1263117632Sharti 1264117632Shartistatic const char * 1265117632Shartidump_scd(struct patm_softc *sc, struct patm_scd *scd) 1266117632Sharti{ 1267117632Sharti u_int i; 1268117632Sharti 1269117632Sharti for (i = 0; i < IDT_TSQE_TAG_SPACE; i++) 1270117632Sharti printf("on_card[%u] = %p\n", i, scd->on_card[i]); 1271117632Sharti printf("space=%u tag=%u num_on_card=%u last_tag=%u\n", 1272117632Sharti scd->space, scd->tag, scd->num_on_card, scd->last_tag); 1273117632Sharti 1274117632Sharti return (""); 1275117632Sharti} 1276