cregit-Linux how code gets into the kernel

Release 4.17 drivers/atm/iphase.c

Directory: drivers/atm
/******************************************************************************
         iphase.c: Device driver for Interphase ATM PCI adapter cards 
                    Author: Peter Wang  <pwang@iphase.com>            
                   Some fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                   Interphase Corporation  <www.iphase.com>           
                               Version: 1.0                           
*******************************************************************************
      
      This software may be used and distributed according to the terms
      of the GNU General Public License (GPL), incorporated herein by reference.
      Drivers based on this skeleton fall under the GPL and must retain
      the authorship (implicit copyright) notice.

      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.
      
      Modified from an incomplete driver for Interphase 5575 1KVC 1M card which 
      was originally written by Monalisa Agrawal at UNH. Now this driver 
      supports a variety of varients of Interphase ATM PCI (i)Chip adapter 
      card family (See www.iphase.com/products/ClassSheet.cfm?ClassID=ATM) 
      in terms of PHY type, the size of control memory and the size of 
      packet memory. The following are the change log and history:
     
          Bugfix the Mona's UBR driver.
          Modify the basic memory allocation and dma logic.
          Port the driver to the latest kernel from 2.0.46.
          Complete the ABR logic of the driver, and added the ABR work-
              around for the hardware anormalies.
          Add the CBR support.
          Add the flow control logic to the driver to allow rate-limit VC.
          Add 4K VC support to the board with 512K control memory.
          Add the support of all the variants of the Interphase ATM PCI 
          (i)Chip adapter cards including x575 (155M OC3 and UTP155), x525
          (25M UTP25) and x531 (DS3 and E3).
          Add SMP support.

      Support and updates available at: ftp://ftp.iphase.com/pub/atm

*******************************************************************************/

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/mm.h>  
#include <linux/pci.h>  
#include <linux/errno.h>  
#include <linux/atm.h>  
#include <linux/atmdev.h>  
#include <linux/sonet.h>  
#include <linux/skbuff.h>  
#include <linux/time.h>  
#include <linux/delay.h>  
#include <linux/uio.h>  
#include <linux/init.h>  
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <asm/io.h>  
#include <linux/atomic.h>
#include <linux/uaccess.h>  
#include <asm/string.h>  
#include <asm/byteorder.h>  
#include <linux/vmalloc.h>
#include <linux/jiffies.h>
#include "iphase.h"		  
#include "suni.h"		  

#define swap_byte_order(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8))


#define PRIV(dev) ((struct suni_priv *) dev->phy_data)

static unsigned char ia_phy_get(struct atm_dev *dev, unsigned long addr);
static void desc_dbg(IADEV *iadev);


static IADEV *ia_dev[8];

static struct atm_dev *_ia_dev[8];

static int iadev_count;
static void ia_led_timer(struct timer_list *unused);
static DEFINE_TIMER(ia_timer, ia_led_timer);


static int IA_TX_BUF = DFL_TX_BUFFERS, IA_TX_BUF_SZ = DFL_TX_BUF_SZ;


static int IA_RX_BUF = DFL_RX_BUFFERS, IA_RX_BUF_SZ = DFL_RX_BUF_SZ;

static uint IADebugFlag = /* IF_IADBG_ERR | IF_IADBG_CBR| IF_IADBG_INIT_ADAPTER
            |IF_IADBG_ABR | IF_IADBG_EVENT*/ 0; 

module_param(IA_TX_BUF, int, 0);
module_param(IA_TX_BUF_SZ, int, 0);
module_param(IA_RX_BUF, int, 0);
module_param(IA_RX_BUF_SZ, int, 0);
module_param(IADebugFlag, uint, 0644);

MODULE_LICENSE("GPL");

/**************************** IA_LIB **********************************/


static void ia_init_rtn_q (IARTN_Q *que) { que->next = NULL; que->tail = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)22100.00%1100.00%
Total22100.00%1100.00%


static void ia_enque_head_rtn_q (IARTN_Q *que, IARTN_Q * data) { data->next = NULL; if (que->next == NULL) que->next = que->tail = data; else { data->next = que->next; que->next = data; } return; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)56100.00%1100.00%
Total56100.00%1100.00%


static int ia_enque_rtn_q (IARTN_Q *que, struct desc_tbl_t data) { IARTN_Q *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return -ENOMEM; entry->data = data; entry->next = NULL; if (que->next == NULL) que->next = que->tail = entry; else { que->tail->next = entry; que->tail = que->tail->next; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8693.48%125.00%
Linus Torvalds44.35%125.00%
Alan Cox11.09%125.00%
Tillmann Heidsieck11.09%125.00%
Total92100.00%4100.00%


static IARTN_Q * ia_deque_rtn_q (IARTN_Q *que) { IARTN_Q *tmpdata; if (que->next == NULL) return NULL; tmpdata = que->next; if ( que->next == que->tail) que->next = que->tail = NULL; else que->next = que->next->next; return tmpdata; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)66100.00%1100.00%
Total66100.00%1100.00%


static void ia_hack_tcq(IADEV *dev) { u_short desc1; u_short tcq_wr; struct ia_vcc *iavcc_r = NULL; tcq_wr = readl(dev->seg_reg+TCQ_WR_PTR) & 0xffff; while (dev->host_tcq_wr != tcq_wr) { desc1 = *(u_short *)(dev->seg_ram + dev->host_tcq_wr); if (!desc1) ; else if (!dev->desc_tbl[desc1 -1].timestamp) { IF_ABR(printk(" Desc %d is reset at %ld\n", desc1 -1, jiffies);) *(u_short *) (dev->seg_ram + dev->host_tcq_wr) = 0; } else if (dev->desc_tbl[desc1 -1].timestamp) { if (!(iavcc_r = dev->desc_tbl[desc1 -1].iavcc)) { printk("IA: Fatal err in get_desc\n"); continue; } iavcc_r->vc_desc_cnt--; dev->desc_tbl[desc1 -1].timestamp = 0; IF_EVENT(printk("ia_hack: return_q skb = 0x%p desc = %d\n", dev->desc_tbl[desc1 -1].txskb, desc1);) if (iavcc_r->pcr < dev->rate_limit) { IA_SKB_STATE (dev->desc_tbl[desc1-1].txskb) |= IA_TX_DONE; if (ia_enque_rtn_q(&dev->tx_return_q, dev->desc_tbl[desc1 -1]) < 0) printk("ia_hack_tcq: No memory available\n"); } dev->desc_tbl[desc1 -1].iavcc = NULL; dev->desc_tbl[desc1 -1].txskb = NULL; } dev->host_tcq_wr += 2; if (dev->host_tcq_wr > dev->ffL.tcq_ed) dev->host_tcq_wr = dev->ffL.tcq_st; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)28599.30%150.00%
David Howells20.70%150.00%
Total287100.00%2100.00%

/* ia_hack_tcq */
static u16 get_desc (IADEV *dev, struct ia_vcc *iavcc) { u_short desc_num, i; struct sk_buff *skb; struct ia_vcc *iavcc_r = NULL; unsigned long delta; static unsigned long timer = 0; int ltimeout; ia_hack_tcq (dev); if((time_after(jiffies,timer+50)) || ((dev->ffL.tcq_rd==dev->host_tcq_wr))) { timer = jiffies; i=0; while (i < dev->num_tx_desc) { if (!dev->desc_tbl[i].timestamp) { i++; continue; } ltimeout = dev->desc_tbl[i].iavcc->ltimeout; delta = jiffies - dev->desc_tbl[i].timestamp; if (delta >= ltimeout) { IF_ABR(printk("RECOVER run!! desc_tbl %d = %d delta = %ld, time = %ld\n", i,dev->desc_tbl[i].timestamp, delta, jiffies);) if (dev->ffL.tcq_rd == dev->ffL.tcq_st) dev->ffL.tcq_rd = dev->ffL.tcq_ed; else dev->ffL.tcq_rd -= 2; *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd) = i+1; if (!(skb = dev->desc_tbl[i].txskb) || !(iavcc_r = dev->desc_tbl[i].iavcc)) printk("Fatal err, desc table vcc or skb is NULL\n"); else iavcc_r->vc_desc_cnt--; dev->desc_tbl[i].timestamp = 0; dev->desc_tbl[i].iavcc = NULL; dev->desc_tbl[i].txskb = NULL; } i++; } /* while */ } if (dev->ffL.tcq_rd == dev->host_tcq_wr) return 0xFFFF; /* Get the next available descriptor number from TCQ */ desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd); while (!desc_num || (dev->desc_tbl[desc_num -1]).timestamp) { dev->ffL.tcq_rd += 2; if (dev->ffL.tcq_rd > dev->ffL.tcq_ed) dev->ffL.tcq_rd = dev->ffL.tcq_st; if (dev->ffL.tcq_rd == dev->host_tcq_wr) return 0xFFFF; desc_num = *(u_short *)(dev->seg_ram + dev->ffL.tcq_rd); } /* get system time */ dev->desc_tbl[desc_num -1].timestamp = jiffies; return desc_num; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)42498.60%133.33%
Julia Lawall51.16%133.33%
Alan Cox10.23%133.33%
Total430100.00%3100.00%


static void clear_lockup (struct atm_vcc *vcc, IADEV *dev) { u_char foundLockUp; vcstatus_t *vcstatus; u_short *shd_tbl; u_short tempCellSlot, tempFract; struct main_vc *abr_vc = (struct main_vc *)dev->MAIN_VC_TABLE_ADDR; struct ext_vc *eabr_vc = (struct ext_vc *)dev->EXT_VC_TABLE_ADDR; u_int i; if (vcc->qos.txtp.traffic_class == ATM_ABR) { vcstatus = (vcstatus_t *) &(dev->testTable[vcc->vci]->vc_status); vcstatus->cnt++; foundLockUp = 0; if( vcstatus->cnt == 0x05 ) { abr_vc += vcc->vci; eabr_vc += vcc->vci; if( eabr_vc->last_desc ) { if( (abr_vc->status & 0x07) == ABR_STATE /* 0x2 */ ) { /* Wait for 10 Micro sec */ udelay(10); if ((eabr_vc->last_desc)&&((abr_vc->status & 0x07)==ABR_STATE)) foundLockUp = 1; } else { tempCellSlot = abr_vc->last_cell_slot; tempFract = abr_vc->fraction; if((tempCellSlot == dev->testTable[vcc->vci]->lastTime) && (tempFract == dev->testTable[vcc->vci]->fract)) foundLockUp = 1; dev->testTable[vcc->vci]->lastTime = tempCellSlot; dev->testTable[vcc->vci]->fract = tempFract; } } /* last descriptor */ vcstatus->cnt = 0; } /* vcstatus->cnt */ if (foundLockUp) { IF_ABR(printk("LOCK UP found\n");) writew(0xFFFD, dev->seg_reg+MODE_REG_0); /* Wait for 10 Micro sec */ udelay(10); abr_vc->status &= 0xFFF8; abr_vc->status |= 0x0001; /* state is idle */ shd_tbl = (u_short *)dev->ABR_SCHED_TABLE_ADDR; for( i = 0; ((i < dev->num_vc) && (shd_tbl[i])); i++ ); if (i < dev->num_vc) shd_tbl[i] = vcc->vci; else IF_ERR(printk("ABR Seg. may not continue on VC %x\n",vcc->vci);) writew(T_ONLINE, dev->seg_reg+MODE_REG_0); writew(~(TRANSMIT_DONE|TCQ_NOT_EMPTY), dev->seg_reg+SEG_MASK_REG); writew(TRANSMIT_DONE, dev->seg_reg+SEG_INTR_STATUS_REG); vcstatus->cnt = 0; } /* foundLockUp */ } /* if an ABR VC */ }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)414100.00%1100.00%
Total414100.00%1100.00%

/* ** Conversion of 24-bit cellrate (cells/sec) to 16-bit floating point format. ** ** +----+----+------------------+-------------------------------+ ** | R | NZ | 5-bit exponent | 9-bit mantissa | ** +----+----+------------------+-------------------------------+ ** ** R = reserved (written as 0) ** NZ = 0 if 0 cells/sec; 1 otherwise ** ** if NZ = 1, rate = 1.mmmmmmmmm x 2^(eeeee) cells/sec */
static u16 cellrate_to_float(u32 cr) { #define NZ 0x4000 #define M_BITS 9 /* Number of bits in mantissa */ #define E_BITS 5 /* Number of bits in exponent */ #define M_MASK 0x1ff #define E_MASK 0x1f u16 flot; u32 tmp = cr & 0x00ffffff; int i = 0; if (cr == 0) return 0; while (tmp != 1) { tmp >>= 1; i++; } if (i == M_BITS) flot = NZ | (i << M_BITS) | (cr & M_MASK); else if (i < M_BITS) flot = NZ | (i << M_BITS) | ((cr << (M_BITS - i)) & M_MASK); else flot = NZ | (i << M_BITS) | ((cr >> (i - M_BITS)) & M_MASK); return flot; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)151100.00%1100.00%
Total151100.00%1100.00%

#if 0 /* ** Conversion of 16-bit floating point format to 24-bit cellrate (cells/sec). */ static u32 float_to_cellrate(u16 rate) { u32 exp, mantissa, cps; if ((rate & NZ) == 0) return 0; exp = (rate >> M_BITS) & E_MASK; mantissa = rate & M_MASK; if (exp == 0) return 1; cps = (1 << M_BITS) | mantissa; if (exp == M_BITS) cps = cps; else if (exp > M_BITS) cps <<= (exp - M_BITS); else cps >>= (M_BITS - exp); return cps; } #endif
static void init_abr_vc (IADEV *dev, srv_cls_param_t *srv_p) { srv_p->class_type = ATM_ABR; srv_p->pcr = dev->LineRate; srv_p->mcr = 0; srv_p->icr = 0x055cb7; srv_p->tbe = 0xffffff; srv_p->frtt = 0x3a; srv_p->rif = 0xf; srv_p->rdf = 0xb; srv_p->nrm = 0x4; srv_p->trm = 0x7; srv_p->cdf = 0x3; srv_p->adtf = 50; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)88100.00%1100.00%
Total88100.00%1100.00%


static int ia_open_abr_vc(IADEV *dev, srv_cls_param_t *srv_p, struct atm_vcc *vcc, u8 flag) { f_vc_abr_entry *f_abr_vc; r_vc_abr_entry *r_abr_vc; u32 icr; u8 trm, nrm, crm; u16 adtf, air, *ptr16; f_abr_vc =(f_vc_abr_entry *)dev->MAIN_VC_TABLE_ADDR; f_abr_vc += vcc->vci; switch (flag) { case 1: /* FFRED initialization */ #if 0 /* sanity check */ if (srv_p->pcr == 0) return INVALID_PCR; if (srv_p->pcr > dev->LineRate) srv_p->pcr = dev->LineRate; if ((srv_p->mcr + dev->sum_mcr) > dev->LineRate) return MCR_UNAVAILABLE; if (srv_p->mcr > srv_p->pcr) return INVALID_MCR; if (!(srv_p->icr)) srv_p->icr = srv_p->pcr; if ((srv_p->icr < srv_p->mcr) || (srv_p->icr > srv_p->pcr)) return INVALID_ICR; if ((srv_p->tbe < MIN_TBE) || (srv_p->tbe > MAX_TBE)) return INVALID_TBE; if ((srv_p->frtt < MIN_FRTT) || (srv_p->frtt > MAX_FRTT)) return INVALID_FRTT; if (srv_p->nrm > MAX_NRM) return INVALID_NRM; if (srv_p->trm > MAX_TRM) return INVALID_TRM; if (srv_p->adtf > MAX_ADTF) return INVALID_ADTF; else if (srv_p->adtf == 0) srv_p->adtf = 1; if (srv_p->cdf > MAX_CDF) return INVALID_CDF; if (srv_p->rif > MAX_RIF) return INVALID_RIF; if (srv_p->rdf > MAX_RDF) return INVALID_RDF; #endif memset ((caddr_t)f_abr_vc, 0, sizeof(*f_abr_vc)); f_abr_vc->f_vc_type = ABR; nrm = 2 << srv_p->nrm; /* (2 ** (srv_p->nrm +1)) */ /* i.e 2**n = 2 << (n-1) */ f_abr_vc->f_nrm = nrm << 8 | nrm; trm = 100000/(2 << (16 - srv_p->trm)); if ( trm == 0) trm = 1; f_abr_vc->f_nrmexp =(((srv_p->nrm +1) & 0x0f) << 12)|(MRM << 8) | trm; crm = srv_p->tbe / nrm; if (crm == 0) crm = 1; f_abr_vc->f_crm = crm & 0xff; f_abr_vc->f_pcr = cellrate_to_float(srv_p->pcr); icr = min( srv_p->icr, (srv_p->tbe > srv_p->frtt) ? ((srv_p->tbe/srv_p->frtt)*1000000) : (1000000/(srv_p->frtt/srv_p->tbe))); f_abr_vc->f_icr = cellrate_to_float(icr); adtf = (10000 * srv_p->adtf)/8192; if (adtf == 0) adtf = 1; f_abr_vc->f_cdf = ((7 - srv_p->cdf) << 12 | adtf) & 0xfff; f_abr_vc->f_mcr = cellrate_to_float(srv_p->mcr); f_abr_vc->f_acr = f_abr_vc->f_icr; f_abr_vc->f_status = 0x0042; break; case 0: /* RFRED initialization */ ptr16 = (u_short *)(dev->reass_ram + REASS_TABLE*dev->memSize); *(ptr16 + vcc->vci) = NO_AAL5_PKT | REASS_ABR; r_abr_vc = (r_vc_abr_entry*)(dev->reass_ram+ABR_VC_TABLE*dev->memSize); r_abr_vc += vcc->vci; r_abr_vc->r_status_rdf = (15 - srv_p->rdf) & 0x000f; air = srv_p->pcr << (15 - srv_p->rif); if (air == 0) air = 1; r_abr_vc->r_air = cellrate_to_float(air); dev->testTable[vcc->vci]->vc_status = VC_ACTIVE | VC_ABR; dev->sum_mcr += srv_p->mcr; dev->n_abr++; break; default: break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)47599.37%133.33%
Linus Torvalds20.42%133.33%
Adrian Bunk10.21%133.33%
Total478100.00%3100.00%


static int ia_cbr_setup (IADEV *dev, struct atm_vcc *vcc) { u32 rateLow=0, rateHigh, rate; int entries; struct ia_vcc *ia_vcc; int idealSlot =0, testSlot, toBeAssigned, inc; u32 spacing; u16 *SchedTbl, *TstSchedTbl; u16 cbrVC, vcIndex; u32 fracSlot = 0; u32 sp_mod = 0; u32 sp_mod2 = 0; /* IpAdjustTrafficParams */ if (vcc->qos.txtp.max_pcr <= 0) { IF_ERR(printk("PCR for CBR not defined\n");) return -1; } rate = vcc->qos.txtp.max_pcr; entries = rate / dev->Granularity; IF_CBR(printk("CBR: CBR entries=0x%x for rate=0x%x & Gran=0x%x\n", entries, rate, dev->Granularity);) if (entries < 1) IF_CBR(printk("CBR: Bandwidth smaller than granularity of CBR table\n");) rateLow = entries * dev->Granularity; rateHigh = (entries + 1) * dev->Granularity; if (3*(rate - rateLow) > (rateHigh - rate)) entries++; if (entries > dev->CbrRemEntries) { IF_CBR(printk("CBR: Not enough bandwidth to support this PCR.\n");) IF_CBR(printk("Entries = 0x%x, CbrRemEntries = 0x%x.\n", entries, dev->CbrRemEntries);) return -EBUSY; } ia_vcc = INPH_IA_VCC(vcc); ia_vcc->NumCbrEntry = entries; dev->sum_mcr += entries * dev->Granularity; /* IaFFrednInsertCbrSched */ // Starting at an arbitrary location, place the entries into the table // as smoothly as possible cbrVC = 0; spacing = dev->CbrTotEntries / entries; sp_mod = dev->CbrTotEntries % entries; // get modulo toBeAssigned = entries; fracSlot = 0; vcIndex = vcc->vci; IF_CBR(printk("Vci=0x%x,Spacing=0x%x,Sp_mod=0x%x\n",vcIndex,spacing,sp_mod);) while (toBeAssigned) { // If this is the first time, start the table loading for this connection // as close to entryPoint as possible. if (toBeAssigned == entries) { idealSlot = dev->CbrEntryPt; dev->CbrEntryPt += 2; // Adding 2 helps to prevent clumping if (dev->CbrEntryPt >= dev->CbrTotEntries) dev->CbrEntryPt -= dev->CbrTotEntries;// Wrap if necessary } else { idealSlot += (u32)(spacing + fracSlot); // Point to the next location // in the table that would be smoothest fracSlot = ((sp_mod + sp_mod2) / entries); // get new integer part sp_mod2 = ((sp_mod + sp_mod2) % entries); // calc new fractional part } if (idealSlot >= (int)dev->CbrTotEntries) idealSlot -= dev->CbrTotEntries; // Continuously check around this ideal value until a null // location is encountered. SchedTbl = (u16*)(dev->seg_ram+CBR_SCHED_TABLE*dev->memSize); inc = 0; testSlot = idealSlot; TstSchedTbl = (u16*)(SchedTbl+testSlot); //set index and read in value IF_CBR(printk("CBR Testslot 0x%x AT Location 0x%p, NumToAssign=%d\n", testSlot, TstSchedTbl,toBeAssigned);) memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); while (cbrVC) // If another VC at this location, we have to keep looking { inc++; testSlot = idealSlot - inc; if (testSlot < 0) { // Wrap if necessary testSlot += dev->CbrTotEntries; IF_CBR(printk("Testslot Wrap. STable Start=0x%p,Testslot=%d\n", SchedTbl,testSlot);) } TstSchedTbl = (u16 *)(SchedTbl + testSlot); // set table index memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); if (!cbrVC) break; testSlot = idealSlot + inc; if (testSlot >= (int)dev->CbrTotEntries) { // Wrap if necessary testSlot -= dev->CbrTotEntries; IF_CBR(printk("TotCbrEntries=%d",dev->CbrTotEntries);) IF_CBR(printk(" Testslot=0x%x ToBeAssgned=%d\n", testSlot, toBeAssigned);) } // set table index and read in value TstSchedTbl = (u16*)(SchedTbl + testSlot); IF_CBR(printk("Reading CBR Tbl from 0x%p, CbrVal=0x%x Iteration %d\n", TstSchedTbl,cbrVC,inc);) memcpy((caddr_t)&cbrVC,(caddr_t)TstSchedTbl,sizeof(cbrVC)); } /* while */ // Move this VCI number into this location of the CBR Sched table. memcpy((caddr_t)TstSchedTbl, (caddr_t)&vcIndex, sizeof(*TstSchedTbl)); dev->CbrRemEntries--; toBeAssigned--; } /* while */ /* IaFFrednCbrEnable */ dev->NumEnabledCBR++; if (dev->NumEnabledCBR == 1) { writew((CBR_EN | UBR_EN | ABR_EN