/* atmxram.c */ /* Manager for APE 25 "Control / Data" local memory */ /* Copyright (c) 1996 Robert Geist and James Westall * Clemson University Dept. of Computer Science * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "atmdd.h" #ifdef DUMMY_V #define printk printf #endif /**/ /* Print the layout of X Ram */ void atm_xmap() { printk("VP_TABLE_BASE - %08x \n", 0); printk("VP_TABLE_SIZE - %08x \n", 512); printk("LCI_TABLE_BASE - %08x \n", (VP_TABLE_BASE + VP_TABLE_SIZE)); printk("LCI_TABLE_SIZE - %08x \n", (2 * LCI_COUNT)); printk("TCB_TABLE_BASE - %08x \n", (LCI_TABLE_BASE + LCI_TABLE_SIZE)); printk("TCB_TABLE_SIZE - %08x \n", (64 * TCB_COUNT) ); printk("R1CB_TABLE_BASE - %08x \n", (TCB_TABLE_BASE + TCB_TABLE_SIZE)); printk("R1CB_TABLE_SIZE - %08x \n", (64 * R1CB_COUNT) ); printk("R5CB_TABLE_BASE - %08x \n", (R1CB_TABLE_BASE + R1CB_TABLE_SIZE)); printk("R5CB_TABLE_SIZE - %08x \n", (64 * R5CB_COUNT) ); printk("ROCB_TABLE_BASE - %08x \n", (R5CB_TABLE_BASE + R5CB_TABLE_SIZE)); printk("ROCB_TABLE_SIZE - %08x \n", (64 * ROCB_COUNT) ); printk("T1RQ_TABLE_BASE - %08x \n", (ROCB_TABLE_BASE + ROCB_TABLE_SIZE)); printk("T1RQ_TABLE_SIZE - %08x \n", (8 * T1RQ_COUNT)); printk("T5RQ_TABLE_BASE - %08x \n", (T1RQ_TABLE_BASE + T1RQ_TABLE_SIZE)); printk("T5RQ_TABLE_SIZE - %08x \n", (8 * T5RQ_COUNT)); printk("LC_TABLE_BASE - %08x \n", (T5RQ_TABLE_BASE + T5RQ_TABLE_SIZE)); printk("LC_TABLE_SIZE - %08x \n", LC_TABLE_SIZE); printk("LC_TABLE_END - %08x \n", (LC_TABLE_BASE + LC_TABLE_SIZE)); } #ifndef DUMMY_V /**/ /* Initialize Xram */ void atm_initxram( struct pddtype *ape) { int i; unsigned short val; /* Begin by zeroing the whole thing out... just to be sure */ /* we always start with a consistent state. */ for (i = 0; i < XRAM_SIZE / 2; i++) { WT_XRAM(ape, 2 * i, 0); } /* Set up the VP table base register */ val = VP_TABLE_BASE >> 9; WT_CNTLREG(ape, VP_TABLE_BR, val); /* Establish a VP table entry for VP 0..Spse we wish */ /* 32 possible VCI numbers and put them all on VP 0. */ /* We use a "Bits indicator" of 0b100000 or 0x20 */ /* Since the LCI table is 64 byte aligned we can use its */ /* complete address as the "pointer". */ WT_XRAM(ape, VP_TABLE_BASE, LCI_TABLE_BASE | LCI_COUNT); /* Set up the LCI table base register */ val = LCI_TABLE_BASE >> 16; WT_CNTLREG(ape, LCI_TABLE_BR, val); val = LCI_TABLE_BASE & 0xffff; WT_CNTLREG(ape, LCI_TABLE_TOP, val); val = (LCI_TABLE_BASE + LCI_TABLE_SIZE - 2) & 0xffff; WT_CNTLREG(ape, LCI_TABLE_BOT, val); /* Set up the transmit cell buffer list */ val = TCB_TABLE_BASE >> 6; WT_CNTLREG(ape, TCB_TOP, val); WT_CNTLREG(ape, TCB_RD_PTR, val); WT_CNTLREG(ape, TCB_WR_PTR, val); val = TCB_TABLE_BASE + TCB_TABLE_SIZE - 64; val >>= 6; WT_CNTLREG(ape, TCB_BOT, val); /* Set up the recv 1 cell buffer list */ val = R1CB_TABLE_BASE >> 6; WT_CNTLREG(ape, R1CB_TOP, val); WT_CNTLREG(ape, R1CB_RD_PTR, val); WT_CNTLREG(ape, R1CB_WR_PTR, val); val = R1CB_TABLE_BASE + R1CB_TABLE_SIZE - 64; val >>= 6; WT_CNTLREG(ape, R1CB_BOT, val); /* Set up the recv 5 cell buffer list */ val = R5CB_TABLE_BASE >> 6; WT_CNTLREG(ape, R5CB_TOP, val); WT_CNTLREG(ape, R5CB_RD_PTR, val); WT_CNTLREG(ape, R5CB_WR_PTR, val); val = R5CB_TABLE_BASE + R5CB_TABLE_SIZE - 64; val >>= 6; WT_CNTLREG(ape, R5CB_BOT, val); /* Set up the recv OAM cell buffer list */ val = ROCB_TABLE_BASE >> 6; WT_CNTLREG(ape, ROCB_TOP, val); WT_CNTLREG(ape, ROCB_RD_PTR, val); WT_CNTLREG(ape, ROCB_WR_PTR, val); val = ROCB_TABLE_BASE + ROCB_TABLE_SIZE - 64; val >>= 6; WT_CNTLREG(ape, ROCB_BOT, val); /* Set up the transmit request queues */ val = T1RQ_TABLE_BASE >> 16; WT_CNTLREG(ape, T1RQ_BASE, val); val = (T1RQ_TABLE_BASE & 0xffff) >> 3; WT_CNTLREG(ape, T1RQ_TOP, val); WT_CNTLREG(ape, T1RQ_WR_PTR, val); WT_CNTLREG(ape, T1RQ_RD_PTR, val); val = T1RQ_TABLE_BASE + T1RQ_TABLE_SIZE - 8; val = (val & 0xffff) >> 3; WT_CNTLREG(ape, T1RQ_BOT, val); val = T5RQ_TABLE_BASE >> 16; WT_CNTLREG(ape, T5RQ_BASE, val); val = (T5RQ_TABLE_BASE & 0xffff) >> 3; WT_CNTLREG(ape, T5RQ_TOP, val); WT_CNTLREG(ape, T5RQ_WR_PTR, val); WT_CNTLREG(ape, T5RQ_RD_PTR, val); val = T5RQ_TABLE_BASE + T5RQ_TABLE_SIZE - 8; val = (val & 0xffff) >> 3; WT_CNTLREG(ape, T5RQ_BOT, val); } /**/ /* Create a LC descriptor for an AAL 5 Channel */ struct lc5type lc; /* Skeleton LC descriptor */ int atm_initlc5( struct pddtype *ape, int vpi, int vci) { int i; int found; unsigned short lci; unsigned short lcndx; unsigned int lcloc; unsigned short lciloc; unsigned short vptloc; unsigned short vptval; unsigned short *lcptr; unsigned short oldlc; /* Use the VPI to access the VP table and see if there is an */ /* LCI table pointer there.. If not this VP cant be used. */ vptloc = VP_TABLE_BASE + vpi << 1; vptval = RD_XRAM(ape, vptloc); /* No lcitable pointer -> out of luck. */ if (vptval == 0) { printk("atm_initlc5: %d is an invalid vpi \n", vpi); return(-1); } /* VCI's are also limited to the number of bits specified */ /* during initialization. */ if (vci > VCI_MASK) { printk("atm_initlc5: %d is an invalid vci \n", vci); return(-1); } /* This code is not very general... It assumes all LC's */ /* are associated with VP 0. Trying to any other VP */ /* will DEFINITELY break it. */ #if 0 /* This is the original LC allocation code.. it assumed */ /* a one-to-one correspondence between possible vcis */ /* and LC's.. Thus vci "n" always used LC "n". */ lciloc = LCI_TABLE_BASE + ((vci & VCI_MASK) << 1); lcloc = LC_TABLE_BASE + ((vci & VCI_MASK) << 7); lci = lcloc >> 7; lcndx = lci - FIRST_LCID; printk("Before: lcndx = %x lci = %x lciloc = %x lcloc = %x \n", lcndx, lci, lciloc, lcloc); #endif #if 1 /* The new LC allocation code takes advantage of the indirection */ /* afforded by the hardware. The LCI table is in one-to-one */ /* correspondence with the vci, but an LCI entry can point to */ /* any LC block. */ /* Try to find an available LC block.. If the vci space is larger */ /* than the number of LC's this search may not succeed. */ found = 0; for (i = 1; i < LC_COUNT; i++) { if (ape->lc_to_vci[i] == 0) { lcndx = i; found = 1; break; } } if (!found) { return(-1); } /* The value lcndx is the offset of the selected LC from the */ /* start of the LC table in units of LC size (128 bytes) */ /* The value lci is the offset of the LC from the start of */ /* XRAM in units of LC size (128 bytes) */ /* Thus FIRST_LCID is the address of the start of the */ /* LC table divided by 128. */ lciloc = LCI_TABLE_BASE + ((vci & VCI_MASK) << 1); lci = lcndx + FIRST_LCID; lcloc = LC_TABLE_BASE + (lcndx << 7); /* printk("After: lcndx = %x lci = %x lciloc = %x lcloc = %x \n", lcndx, lci, lciloc, lcloc); */ #endif /* No transmit buffers currently allocated to this LC */ ape->tbcount[lcndx] = 0; ape->tx_backlog[lcndx] = 0; skb_queue_head_init(&ape->tx_queue[lcndx]); /* Set up the mapping between lcndx and vci */ ape->lc_to_vci[lcndx] = vci; ape->vci_to_lc[vci] = lcndx; #if 0 printk("LC location for vci %d is %x \n", vci, lcloc); /* See if the requested lc is already in use. */ oldlc = RD_XRAM(ape, lciloc); if (oldlc != 0) { printk("atm_initlc5: vci %d is already in use \n", vci); return(-1); } #endif /* Fill in a "default" configuration */ memset(&lc, 0, sizeof(lc)); lc.maxsdu = RB_SIZE; lc.aalstate = 0; lc.rrl = 0; if (vci > 32) lc.rrl = 1 << 2; /* Specify receive list */ lc.lcflags = 0; lc.crd = 1; lc.psr = 1; /* Peak sustainable rate */ lc.bts = 4096; /* Burst tolerance */ /* lc.tflags = vci & 7; TMQ set to seven; */ /* Here we (try to) set vcis 0-31 to TMQ 0 high priority */ /* and 32-63 to TMQ 4 (low priority.) */ lc.tflags = (vci >> 5 ) << 2; /* lc.tflags = 0; */ lc.chdr1 = (vpi << 4) | (vci >> 12); lc.chdr2 = (vci << 4); /* Copy the configuration to XRAM */ lcptr = (unsigned short *)&lc; for (i = 0; i < (sizeof(lc) / 2); i++) { WT_XRAM(ape, lcloc + 2 * i, *lcptr); lcptr += 1; } WT_XRAM(ape, lciloc, lci); ape->lcstatus[lci - FIRST_LCID] = 1; for (i = 1; i < LC_COUNT; i++) { if (ape->lc_to_vci[i] != 0) { ape->max_lc_ndx = i; } } printk("max_lc_ndx = %d \n", ape->max_lc_ndx); return(lci); } int atm_freelc5( struct pddtype *ape, int vpi, int vci) { int i; unsigned short lci; unsigned int lcloc; unsigned short lciloc; unsigned short lcndx; unsigned short vptloc; unsigned short vptval; unsigned short *lcptr; unsigned short oldlc; struct sk_buff *skb; /* Use the VPI to access the VP table and see if there is an */ /* LCI table pointer there.. If not this VP cant be used. */ vptloc = VP_TABLE_BASE + vpi << 1; vptval = RD_XRAM(ape, vptloc); /* No lcitable pointer -> out of luck. */ if (vptval == 0) { printk("atm_freelc5: %d is an invalid vpi \n", vpi); return(-1); } /* VCI's are also limited to the number of bits specified */ /* during initialization. */ if (vci > VCI_MASK) { printk("atm_freelc5: %d is an invalid vci \n", vci); return(-1); } /* This code is not very general... It assumes all LC's */ /* are associated with VP 0. Trying to use any other VP */ /* will DEFINITELY break it. */ lciloc = LCI_TABLE_BASE + ((vci & VCI_MASK) << 1); lcndx = ape->vci_to_lc[vci]; #if 0 lcloc = LC_TABLE_BASE + ((vci & VCI_MASK) << 7); lci = lcloc >> 7; #endif lci = lcndx + FIRST_LCID; lcloc = LC_TABLE_BASE + (lcndx << 7); /* See if the requested lc is already in use. */ oldlc = RD_XRAM(ape, lciloc); if (oldlc == 0) { /* printk("atm_freelc5: vci %d is not in use \n", vci); */ return(-1); } WT_XRAM(ape, lciloc, 0); #ifdef FULL_STACK { int hi; int lo; int aalstate; struct rbhtype *rbh; /* Here we extract the RBH of the last buffer that was */ /* being worked upon by the ape25 */ hi = RD_XRAM(ape, lcloc + 2); lo = RD_XRAM(ape, lcloc + 4); rbh = (struct rbhtype *)phys_to_virt(((hi << 16) + lo)); #if 0 printk("freelc: %04x %04x %0x8 %0x8 \n", hi, lo, ape->erfl, rbh); printk("freelc: Last index was %d \n", rbh - ape->rbpool->rbhs); #endif /* Now we extract the status */ lo = RD_XRAM(ape, lcloc + 16); #if 0 printk("freelc: Status at close was %04x \n", lo); #endif aalstate = lo >> 8; /* Finally we conjecture that if the status is not "idle" */ /* Then we need to return the RBH and the SKB */ if (aalstate) { /* if (rbh->skb != 0) dev_kfree_skb(rbh->skb); */ atm_freerbh(ape, rbh); /* Free the rbh. */ } } #endif #if 0 /* Zero the LC table. */ for (i = 0; i < 64; i++) { WT_XRAM(ape, lcloc + 2 * i, 0); } #endif /* Set the status to out of use. */ ape->lcstatus[lci - FIRST_LCID] = 0; ape->vci_to_lc[vci] = 0; ape->lc_to_vci[lci - FIRST_LCID] = 0; #if 0 while ((skb = skb_dequeue(&ape->tx_queue[lci - FIRST_LCID])) != 0) dev_kfree_skb(skb); ape->tbcount[lci - FIRST_LCID] = 0; ape->tx_backlog[lci - FIRST_LCID] = 0; skb_queue_head_init(&ape->tx_queue[lci - FIRST_LCID]); #endif return(0); } /**/ /* Determine if an LC is established. */ int atm_testvci( struct pddtype *ape, int vci) /* The vci number */ { unsigned short lciloc; unsigned int lcloc; unsigned short maxsdu; unsigned short lcnum; /* First see if there is an LC number in the LCI table */ lciloc = LCI_TABLE_BASE + ((vci & VCI_MASK) << 1); lcnum = RD_XRAM(ape, lciloc); if (lcnum == 0) return(-1); /* Now see if the LC structure seems filled in */ lcloc = LC_TABLE_BASE + ((vci & VCI_MASK) << 7); maxsdu = RD_XRAM(ape, lcloc); if (maxsdu == 0) return(-2); return(lcnum); } #else main() { atm_xmap(); } #endif