if_patm.c revision 126816
1133936Sobrien/* 279968Sobrien * Copyright (c) 2003 379968Sobrien * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4133936Sobrien * All rights reserved. 579968Sobrien * 679968Sobrien * Redistribution and use in source and binary forms, with or without 779968Sobrien * modification, are permitted provided that the following conditions 879968Sobrien * are met: 979968Sobrien * 1. Redistributions of source code must retain the above copyright 1079968Sobrien * notice, this list of conditions and the following disclaimer. 1179968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1279968Sobrien * notice, this list of conditions and the following disclaimer in the 1379968Sobrien * documentation and/or other materials provided with the distribution. 1479968Sobrien * 1579968Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1679968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1779968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1879968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1979968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2079968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2179968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2279968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2379968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2479968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2579968Sobrien * SUCH DAMAGE. 2679968Sobrien * 2779968Sobrien * Author: Hartmut Brandt <harti@freebsd.org> 2879968Sobrien * 2979968Sobrien * Driver for IDT77252 based cards like ProSum's. 3079968Sobrien */ 3179968Sobrien 3279968Sobrien#include <sys/cdefs.h> 3379968Sobrien__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm.c 126816 2004-03-10 17:03:27Z bms $"); 3479968Sobrien 3579968Sobrien#include "opt_inet.h" 3679968Sobrien#include "opt_natm.h" 3779968Sobrien 3879968Sobrien#include <sys/types.h> 3979968Sobrien#include <sys/param.h> 4079968Sobrien#include <sys/systm.h> 4179968Sobrien#include <sys/malloc.h> 4279968Sobrien#include <sys/kernel.h> 4379968Sobrien#include <sys/bus.h> 4479968Sobrien#include <sys/errno.h> 4579968Sobrien#include <sys/conf.h> 4679968Sobrien#include <sys/module.h> 4779968Sobrien#include <sys/lock.h> 4879968Sobrien#include <sys/mutex.h> 4979968Sobrien#include <sys/sysctl.h> 5079968Sobrien#include <sys/queue.h> 51133936Sobrien#include <sys/condvar.h> 5279968Sobrien#include <sys/endian.h> 5379968Sobrien#include <vm/uma.h> 5479968Sobrien 5579968Sobrien#include <sys/sockio.h> 5679968Sobrien#include <sys/mbuf.h> 5779968Sobrien#include <sys/socket.h> 5879968Sobrien 5979968Sobrien#include <net/if.h> 6079968Sobrien#include <net/if_media.h> 6179968Sobrien#include <net/if_atm.h> 6279968Sobrien#include <net/route.h> 6379968Sobrien#include <netinet/in.h> 6479968Sobrien#include <netinet/if_atm.h> 6579968Sobrien 6679968Sobrien#include <machine/bus.h> 6779968Sobrien#include <machine/resource.h> 6879968Sobrien#include <sys/bus.h> 6979968Sobrien#include <sys/rman.h> 7079968Sobrien#include <sys/mbpool.h> 7179968Sobrien 7279968Sobrien#include <dev/utopia/utopia.h> 7379968Sobrien#include <dev/patm/idt77252reg.h> 7479968Sobrien#include <dev/patm/if_patmvar.h> 7579968Sobrien 7679968Sobrienstatic void patm_tst_init(struct patm_softc *sc); 7779968Sobrienstatic void patm_scd_init(struct patm_softc *sc); 7879968Sobrien 7979968Sobrien/* 8079968Sobrien * Start the card. This assumes the mutex to be held 8179968Sobrien */ 8279968Sobrienvoid 8379968Sobrienpatm_initialize(struct patm_softc *sc) 8479968Sobrien{ 8579968Sobrien uint32_t cfg; 8679968Sobrien u_int i; 8779968Sobrien 8879968Sobrien patm_debug(sc, ATTACH, "configuring..."); 8979968Sobrien 9079968Sobrien /* clear SRAM */ 9179968Sobrien for (i = 0; i < sc->mmap->sram * 1024; i += 4) 9279968Sobrien patm_sram_write4(sc, i, 0, 0, 0, 0); 9379968Sobrien patm_scd_init(sc); 9479968Sobrien 9579968Sobrien /* configuration register. Setting NOIDLE makes the timing wrong! */ 9679968Sobrien cfg = IDT_CFG_TXFIFO9 | IDT_CFG_RXQ512 | PATM_CFG_VPI | 9779968Sobrien /* IDT_CFG_NOIDLE | */ sc->mmap->rxtab; 98108746Sobrien if (!(sc->flags & PATM_UNASS)) 99108746Sobrien cfg |= IDT_CFG_IDLECLP; 100133936Sobrien patm_nor_write(sc, IDT_NOR_CFG, cfg); 101108746Sobrien 102108746Sobrien /* clean all the status queues and the Raw handle */ 103108746Sobrien memset(sc->tsq, 0, sc->sq_size); 104108746Sobrien 105108746Sobrien /* initialize RSQ */ 106108746Sobrien patm_debug(sc, ATTACH, "RSQ %llx", (unsigned long long)sc->rsq_phy); 107108746Sobrien patm_nor_write(sc, IDT_NOR_RSQB, sc->rsq_phy); 108108746Sobrien patm_nor_write(sc, IDT_NOR_RSQT, sc->rsq_phy); 109108746Sobrien patm_nor_write(sc, IDT_NOR_RSQH, 0); 110108746Sobrien sc->rsq_last = PATM_RSQ_SIZE - 1; 111108746Sobrien 112108746Sobrien /* initialize TSTB */ 113108746Sobrien patm_nor_write(sc, IDT_NOR_TSTB, sc->mmap->tst1base << 2); 114108746Sobrien patm_tst_init(sc); 115108746Sobrien 116108746Sobrien /* initialize TSQ */ 117108746Sobrien for (i = 0; i < IDT_TSQ_SIZE; i++) 118108746Sobrien sc->tsq[i].stamp = htole32(IDT_TSQE_EMPTY); 119108746Sobrien patm_nor_write(sc, IDT_NOR_TSQB, sc->tsq_phy); 120108746Sobrien patm_nor_write(sc, IDT_NOR_TSQH, 0); 12179968Sobrien patm_nor_write(sc, IDT_NOR_TSQT, 0); 12279968Sobrien sc->tsq_next = sc->tsq; 12392282Sobrien 12492282Sobrien /* GP */ 12592282Sobrien#if BYTE_ORDER == BIG_ENDIAN && 0 12692282Sobrien patm_nor_write(sc, IDT_NOR_GP, IDT_GP_BIGE); 12792282Sobrien#else 12879968Sobrien patm_nor_write(sc, IDT_NOR_GP, 0); 12979968Sobrien#endif 13079968Sobrien 13179968Sobrien /* VPM */ 13279968Sobrien patm_nor_write(sc, IDT_NOR_VPM, 0); 13392282Sobrien 13479968Sobrien /* RxFIFO */ 13579968Sobrien patm_nor_write(sc, IDT_NOR_RXFD, 13679968Sobrien IDT_RXFD(sc->mmap->rxfifo_addr, sc->mmap->rxfifo_code)); 13779968Sobrien patm_nor_write(sc, IDT_NOR_RXFT, 0); 13879968Sobrien patm_nor_write(sc, IDT_NOR_RXFH, 0); 13979968Sobrien 14079968Sobrien /* RAWHND */ 14179968Sobrien patm_debug(sc, ATTACH, "RWH %llx", 14279968Sobrien (unsigned long long)sc->rawhnd_phy); 14379968Sobrien patm_nor_write(sc, IDT_NOR_RAWHND, sc->rawhnd_phy); 14479968Sobrien 14579968Sobrien /* ABRSTD */ 14679968Sobrien patm_nor_write(sc, IDT_NOR_ABRSTD, 14779968Sobrien IDT_ABRSTD(sc->mmap->abrstd_addr, sc->mmap->abrstd_code)); 14879968Sobrien for (i = 0; i < sc->mmap->abrstd_size; i++) 14979968Sobrien patm_sram_write(sc, sc->mmap->abrstd_addr + i, 0); 15079968Sobrien patm_nor_write(sc, IDT_NOR_ABRRQ, 0); 15179968Sobrien patm_nor_write(sc, IDT_NOR_VBRRQ, 0); 15279968Sobrien 15379968Sobrien /* rate tables */ 15479968Sobrien if (sc->flags & PATM_25M) { 15579968Sobrien for (i = 0; i < patm_rtables_size; i++) 15679968Sobrien patm_sram_write(sc, sc->mmap->rtables + i, 15779968Sobrien patm_rtables25[i]); 15879968Sobrien } else { 15979968Sobrien for (i = 0; i < patm_rtables_size; i++) 16079968Sobrien patm_sram_write(sc, sc->mmap->rtables + i, 16179968Sobrien patm_rtables155[i]); 16279968Sobrien } 16379968Sobrien patm_nor_write(sc, IDT_NOR_RTBL, sc->mmap->rtables << 2); 16479968Sobrien 16579968Sobrien /* Maximum deficit */ 16679968Sobrien patm_nor_write(sc, IDT_NOR_MXDFCT, 32 | IDT_MDFCT_LCI | IDT_MDFCT_LNI); 16779968Sobrien 16879968Sobrien /* Free buffer queues */ 169110242Sobrien patm_nor_write(sc, IDT_NOR_FBQP0, 0); 170110242Sobrien patm_nor_write(sc, IDT_NOR_FBQP1, 0); 17179968Sobrien patm_nor_write(sc, IDT_NOR_FBQP2, 0); 17279968Sobrien patm_nor_write(sc, IDT_NOR_FBQP3, 0); 17379968Sobrien 17479968Sobrien patm_nor_write(sc, IDT_NOR_FBQWP0, 0); 17579968Sobrien patm_nor_write(sc, IDT_NOR_FBQWP1, 0); 17679968Sobrien patm_nor_write(sc, IDT_NOR_FBQWP2, 0); 17779968Sobrien patm_nor_write(sc, IDT_NOR_FBQWP3, 0); 17879968Sobrien 17979968Sobrien patm_nor_write(sc, IDT_NOR_FBQS0, 18079968Sobrien (SMBUF_THRESHOLD << 28) | 181110242Sobrien (SMBUF_NI_THRESH << 24) | 182110242Sobrien (SMBUF_CI_THRESH << 20) | 183110242Sobrien SMBUF_CELLS); 18479968Sobrien patm_nor_write(sc, IDT_NOR_FBQS1, 18579968Sobrien (LMBUF_THRESHOLD << 28) | 18679968Sobrien (LMBUF_NI_THRESH << 24) | 18779968Sobrien (LMBUF_CI_THRESH << 20) | 18879968Sobrien LMBUF_CELLS); 18979968Sobrien patm_nor_write(sc, IDT_NOR_FBQS2, 19079968Sobrien (VMBUF_THRESHOLD << 28) | VMBUF_CELLS); 19179968Sobrien patm_nor_write(sc, IDT_NOR_FBQS3, 0); 19279968Sobrien 19379968Sobrien /* make SCD0 for UBR0 */ 19479968Sobrien if ((sc->scd0 = patm_scd_alloc(sc)) == NULL) { 19579968Sobrien patm_printf(sc, "cannot create UBR0 SCD\n"); 19679968Sobrien patm_reset(sc); 19779968Sobrien return; 19879968Sobrien } 19979968Sobrien sc->scd0->q.ifq_maxlen = PATM_DLFT_MAXQ; 20079968Sobrien 20179968Sobrien patm_scd_setup(sc, sc->scd0); 20279968Sobrien patm_tct_setup(sc, sc->scd0, NULL); 20379968Sobrien 20479968Sobrien patm_debug(sc, ATTACH, "go..."); 20579968Sobrien 20679968Sobrien sc->utopia.flags &= ~UTP_FL_POLL_CARRIER; 20779968Sobrien sc->ifatm.ifnet.if_flags |= IFF_RUNNING; 20879968Sobrien 20979968Sobrien /* enable interrupts, Tx and Rx paths */ 21079968Sobrien cfg |= IDT_CFG_RXPTH | IDT_CFG_RXIIMM | IDT_CFG_RAWIE | IDT_CFG_RQFIE | 21179968Sobrien IDT_CFG_TIMOIE | IDT_CFG_FBIE | IDT_CFG_TXENB | IDT_CFG_TXINT | 21279968Sobrien IDT_CFG_TXUIE | IDT_CFG_TXSFI | IDT_CFG_PHYIE; 21379968Sobrien patm_nor_write(sc, IDT_NOR_CFG, cfg); 21479968Sobrien 21579968Sobrien for (i = 0; i < sc->mmap->max_conn; i++) 21679968Sobrien if (sc->vccs[i] != NULL) 21779968Sobrien patm_load_vc(sc, sc->vccs[i], 1); 21879968Sobrien 21979968Sobrien ATMEV_SEND_IFSTATE_CHANGED(&sc->ifatm, 22079968Sobrien sc->utopia.carrier == UTP_CARR_OK); 22179968Sobrien} 22279968Sobrien 22379968Sobrien/* 22479968Sobrien * External callable start function 22579968Sobrien */ 22679968Sobrienvoid 22779968Sobrienpatm_init(void *p) 22879968Sobrien{ 22979968Sobrien struct patm_softc *sc = p; 23079968Sobrien 23179968Sobrien mtx_lock(&sc->mtx); 23279968Sobrien patm_stop(sc); 23379968Sobrien patm_initialize(sc); 23479968Sobrien mtx_unlock(&sc->mtx); 23579968Sobrien} 23679968Sobrien 23779968Sobrien/* 23879968Sobrien * Stop the interface 23979968Sobrien */ 24079968Sobrienvoid 24179968Sobrienpatm_stop(struct patm_softc *sc) 24279968Sobrien{ 24379968Sobrien u_int i; 24479968Sobrien struct mbuf *m; 24579968Sobrien struct patm_txmap *map; 24679968Sobrien struct patm_scd *scd; 24779968Sobrien 24879968Sobrien sc->ifatm.ifnet.if_flags &= ~IFF_RUNNING; 24979968Sobrien sc->utopia.flags |= UTP_FL_POLL_CARRIER; 25079968Sobrien 25179968Sobrien patm_reset(sc); 25279968Sobrien 25379968Sobrien mtx_lock(&sc->tst_lock); 25479968Sobrien i = sc->tst_state; 25592282Sobrien sc->tst_state = 0; 25692282Sobrien callout_stop(&sc->tst_callout); 25792282Sobrien mtx_unlock(&sc->tst_lock); 25879968Sobrien 25979968Sobrien if (i != 0) { 26079968Sobrien /* this means we are just entering or leaving the timeout. 26179968Sobrien * wait a little bit. Doing this correctly would be more 26292282Sobrien * involved */ 26379968Sobrien DELAY(1000); 26492282Sobrien } 26579968Sobrien 26679968Sobrien /* 26779968Sobrien * Give any waiters on closing a VCC a chance. They will stop 26879968Sobrien * to wait if they see that IFF_RUNNING disappeared. 26979968Sobrien */ 27079968Sobrien cv_broadcast(&sc->vcc_cv); 27179968Sobrien 27292282Sobrien /* free large buffers */ 27379968Sobrien patm_debug(sc, ATTACH, "freeing large buffers..."); 27479968Sobrien for (i = 0; i < sc->lbuf_max; i++) 27579968Sobrien if (sc->lbufs[i].m != NULL) 27679968Sobrien patm_lbuf_free(sc, &sc->lbufs[i]); 27779968Sobrien 27879968Sobrien /* free small buffers that are on the card */ 27979968Sobrien patm_debug(sc, ATTACH, "freeing small buffers..."); 28079968Sobrien mbp_card_free(sc->sbuf_pool); 28192282Sobrien 28279968Sobrien /* free aal0 buffers that are on the card */ 28379968Sobrien patm_debug(sc, ATTACH, "freeing aal0 buffers..."); 28479968Sobrien mbp_card_free(sc->vbuf_pool); 28579968Sobrien 28679968Sobrien /* freeing partial receive chains and reset vcc state */ 28779968Sobrien for (i = 0; i < sc->mmap->max_conn; i++) { 28879968Sobrien if (sc->vccs[i] != NULL) { 28979968Sobrien if (sc->vccs[i]->chain != NULL) { 29079968Sobrien m_freem(sc->vccs[i]->chain); 29179968Sobrien sc->vccs[i]->chain = NULL; 29279968Sobrien sc->vccs[i]->last = NULL; 29379968Sobrien } 29479968Sobrien 29579968Sobrien if (sc->vccs[i]->vflags & (PATM_VCC_RX_CLOSING | 29679968Sobrien PATM_VCC_TX_CLOSING)) { 29779968Sobrien uma_zfree(sc->vcc_zone, sc->vccs[i]); 29879968Sobrien sc->vccs[i] = NULL; 29979968Sobrien } else { 30079968Sobrien /* keep */ 30179968Sobrien sc->vccs[i]->vflags &= ~PATM_VCC_OPEN; 30279968Sobrien sc->vccs[i]->cps = 0; 30379968Sobrien sc->vccs[i]->scd = NULL; 30479968Sobrien } 30579968Sobrien } 30679968Sobrien } 30779968Sobrien 30879968Sobrien /* stop all active SCDs */ 30979968Sobrien while ((scd = LIST_FIRST(&sc->scd_list)) != NULL) { 31079968Sobrien /* free queue packets */ 31179968Sobrien for (;;) { 31292282Sobrien _IF_DEQUEUE(&scd->q, m); 31379968Sobrien if (m == NULL) 31479968Sobrien break; 31579968Sobrien m_freem(m); 31679968Sobrien } 31779968Sobrien 31879968Sobrien /* free transmitting packets */ 31979968Sobrien for (i = 0; i < IDT_TSQE_TAG_SPACE; i++) { 32079968Sobrien if ((m = scd->on_card[i]) != NULL) { 32179968Sobrien scd->on_card[i] = 0; 32279968Sobrien map = m->m_pkthdr.header; 32379968Sobrien 32479968Sobrien bus_dmamap_unload(sc->tx_tag, map->map); 32579968Sobrien SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link); 32679968Sobrien m_freem(m); 32779968Sobrien } 32879968Sobrien } 32979968Sobrien patm_scd_free(sc, scd); 33079968Sobrien } 33179968Sobrien sc->scd0 = NULL; 33279968Sobrien 333108746Sobrien sc->flags &= ~PATM_CLR; 33479968Sobrien 33579968Sobrien /* reset raw cell queue */ 33679968Sobrien sc->rawh = NULL; 337108746Sobrien 33879968Sobrien ATMEV_SEND_IFSTATE_CHANGED(&sc->ifatm, 33979968Sobrien sc->utopia.carrier == UTP_CARR_OK); 34079968Sobrien} 34179968Sobrien 34279968Sobrien/* 34379968Sobrien * Stop the card and reset it 34479968Sobrien */ 34579968Sobrienvoid 34679968Sobrienpatm_reset(struct patm_softc *sc) 34779968Sobrien{ 34879968Sobrien 34979968Sobrien patm_debug(sc, ATTACH, "resetting..."); 35079968Sobrien 35179968Sobrien patm_nor_write(sc, IDT_NOR_CFG, IDT_CFG_SWRST); 35279968Sobrien DELAY(200); 35379968Sobrien patm_nor_write(sc, IDT_NOR_CFG, 0); 35479968Sobrien DELAY(200); 35579968Sobrien 35679968Sobrien patm_nor_write(sc, IDT_NOR_RSQH, 0); 35779968Sobrien patm_nor_write(sc, IDT_NOR_TSQH, 0); 35879968Sobrien 35979968Sobrien patm_nor_write(sc, IDT_NOR_GP, IDT_GP_PHY_RST); 36079968Sobrien DELAY(50); 36179968Sobrien patm_nor_write(sc, IDT_NOR_GP, IDT_GP_EEDO | IDT_GP_EECS); 36279968Sobrien DELAY(50); 36379968Sobrien} 36479968Sobrien 36579968Sobrien/* 36679968Sobrien * Initialize the soft TST to contain only ABR scheduling and 36779968Sobrien * write it to SRAM 36879968Sobrien */ 36979968Sobrienstatic void 37079968Sobrienpatm_tst_init(struct patm_softc *sc) 37179968Sobrien{ 37279968Sobrien u_int i; 37379968Sobrien u_int base, idle; 37479968Sobrien 37579968Sobrien base = sc->mmap->tst1base; 37679968Sobrien idle = sc->mmap->tst1base + sc->mmap->tst_size; 37779968Sobrien 37879968Sobrien /* soft */ 37979968Sobrien for (i = 0; i < sc->mmap->tst_size - 1; i++) 38079968Sobrien sc->tst_soft[i] = IDT_TST_VBR; 38179968Sobrien 38279968Sobrien sc->tst_state = 0; 38379968Sobrien sc->tst_jump[0] = base + sc->mmap->tst_size - 1; 38479968Sobrien sc->tst_jump[1] = idle + sc->mmap->tst_size - 1; 38579968Sobrien sc->tst_base[0] = base; 38679968Sobrien sc->tst_base[1] = idle; 38779968Sobrien 38879968Sobrien /* TST1 */ 38979968Sobrien for (i = 0; i < sc->mmap->tst_size - 1; i++) 39079968Sobrien patm_sram_write(sc, base + i, IDT_TST_VBR); 39179968Sobrien patm_sram_write(sc, sc->tst_jump[0], IDT_TST_BR | (base << 2)); 39279968Sobrien 39379968Sobrien /* TST2 */ 39479968Sobrien for (i = 0; i < sc->mmap->tst_size - 1; i++) 39579968Sobrien patm_sram_write(sc, idle + i, IDT_TST_VBR); 39679968Sobrien patm_sram_write(sc, sc->tst_jump[1], IDT_TST_BR | (idle << 2)); 39779968Sobrien 39879968Sobrien sc->tst_free = sc->mmap->tst_size - 1; 39979968Sobrien sc->tst_reserve = sc->tst_free * PATM_TST_RESERVE / 100; 40079968Sobrien sc->bwrem = sc->ifatm.mib.pcr; 40179968Sobrien} 40279968Sobrien 40379968Sobrien/* 40479968Sobrien * Initialize the SCDs. This is done by building a list of all free 40579968Sobrien * SCDs in SRAM. The first word of each potential SCD is used as a 40679968Sobrien * link to the next free SCD. The list is rooted in softc. 407110242Sobrien */ 408110242Sobrienstatic void 409110242Sobrienpatm_scd_init(struct patm_softc *sc) 410110242Sobrien{ 411110242Sobrien u_int s; /* SRAM address of current SCD */ 412110242Sobrien 413110242Sobrien sc->scd_free = 0; 414110242Sobrien for (s = sc->mmap->scd_base; s + 12 <= sc->mmap->tst1base; s += 12) { 415110242Sobrien patm_sram_write(sc, s, sc->scd_free); 41679968Sobrien sc->scd_free = s; 41779968Sobrien } 41879968Sobrien} 41979968Sobrien 42079968Sobrien/* 42179968Sobrien * allocate an SCQ 42279968Sobrien */ 42379968Sobrienstruct patm_scd * 42479968Sobrienpatm_scd_alloc(struct patm_softc *sc) 42579968Sobrien{ 42679968Sobrien u_int sram, next; /* SRAM address of this and next SCD */ 42779968Sobrien int error; 42879968Sobrien void *p; 42979968Sobrien struct patm_scd *scd; 43079968Sobrien bus_dmamap_t map; 43179968Sobrien bus_addr_t phy; 43279968Sobrien 43379968Sobrien /* get an SCD from the free list */ 43479968Sobrien if ((sram = sc->scd_free) == 0) 43579968Sobrien return (NULL); 43679968Sobrien next = patm_sram_read(sc, sram); 43779968Sobrien 43879968Sobrien /* allocate memory for the queue and our host stuff */ 43979968Sobrien error = bus_dmamem_alloc(sc->scd_tag, &p, BUS_DMA_NOWAIT, &map); 44079968Sobrien if (error != 0) 44179968Sobrien return (NULL); 44279968Sobrien phy = 0x3ff; 44379968Sobrien error = bus_dmamap_load(sc->scd_tag, map, p, sizeof(scd->scq), 44479968Sobrien patm_load_callback, &phy, BUS_DMA_NOWAIT); 44579968Sobrien if (error != 0) { 44679968Sobrien bus_dmamem_free(sc->scd_tag, p, map); 44779968Sobrien return (NULL); 44879968Sobrien } 44979968Sobrien KASSERT((phy & 0x1ff) == 0, ("SCD not aligned %lx", (u_long)phy)); 45079968Sobrien 45179968Sobrien scd = p; 45279968Sobrien bzero(scd, sizeof(*scd)); 45379968Sobrien 45479968Sobrien scd->sram = sram; 45579968Sobrien scd->phy = phy; 45679968Sobrien scd->map = map; 45779968Sobrien scd->space = IDT_SCQ_SIZE; 45879968Sobrien scd->last_tag = IDT_TSQE_TAG_SPACE - 1; 45979968Sobrien scd->q.ifq_maxlen = PATM_TX_IFQLEN; 46079968Sobrien 46179968Sobrien /* remove the scd from the free list */ 46279968Sobrien sc->scd_free = next; 46379968Sobrien LIST_INSERT_HEAD(&sc->scd_list, scd, link); 46479968Sobrien 46579968Sobrien return (scd); 46679968Sobrien} 46779968Sobrien 46879968Sobrien/* 46979968Sobrien * Free an SCD 47079968Sobrien */ 47179968Sobrienvoid 47279968Sobrienpatm_scd_free(struct patm_softc *sc, struct patm_scd *scd) 47379968Sobrien{ 47479968Sobrien 47579968Sobrien LIST_REMOVE(scd, link); 47679968Sobrien 47779968Sobrien /* clear SCD and insert link word */ 47879968Sobrien patm_sram_write4(sc, scd->sram, sc->scd_free, 0, 0, 0); 47979968Sobrien patm_sram_write4(sc, scd->sram, 0, 0, 0, 0); 48079968Sobrien patm_sram_write4(sc, scd->sram, 0, 0, 0, 0); 48179968Sobrien 48279968Sobrien /* put on free list */ 48379968Sobrien sc->scd_free = scd->sram; 48479968Sobrien 48579968Sobrien /* free memory */ 48679968Sobrien bus_dmamap_unload(sc->scd_tag, scd->map); 48792282Sobrien bus_dmamem_free(sc->scd_tag, scd, scd->map); 48892282Sobrien} 48992282Sobrien 49092282Sobrien/* 49192282Sobrien * DMA loading helper function. This function handles the loading of 49279968Sobrien * all one segment DMA maps. The argument is a pointer to a bus_addr_t 49379968Sobrien * which must contain the desired alignment of the address as a bitmap. 49492282Sobrien */ 49579968Sobrienvoid 49679968Sobrienpatm_load_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 49779968Sobrien{ 49879968Sobrien bus_addr_t *phy = arg; 49979968Sobrien 50079968Sobrien if (error) 50179968Sobrien return; 50279968Sobrien 50379968Sobrien KASSERT(nsegs == 1, 50479968Sobrien ("too many segments for DMA: %d", nsegs)); 50579968Sobrien KASSERT(segs[0].ds_addr <= 0xffffffffUL, 50679968Sobrien ("phys addr too large %lx", (u_long)segs[0].ds_addr)); 50779968Sobrien KASSERT((segs[0].ds_addr & *phy) == 0, 50879968Sobrien ("bad alignment %lx:%lx", (u_long)segs[0].ds_addr, (u_long)*phy)); 50979968Sobrien 51079968Sobrien *phy = segs[0].ds_addr; 51179968Sobrien} 51279968Sobrien