162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/atm.h> 1362306a36Sopenharmony_ci#include <linux/atmdev.h> 1462306a36Sopenharmony_ci#include <linux/sonet.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/time.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/uio.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/atm_eni.h> 2162306a36Sopenharmony_ci#include <linux/bitops.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <asm/io.h> 2462306a36Sopenharmony_ci#include <linux/atomic.h> 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <asm/string.h> 2762306a36Sopenharmony_ci#include <asm/byteorder.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "tonga.h" 3062306a36Sopenharmony_ci#include "midway.h" 3162306a36Sopenharmony_ci#include "suni.h" 3262306a36Sopenharmony_ci#include "eni.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * TODO: 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Show stoppers 3862306a36Sopenharmony_ci * none 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Minor 4162306a36Sopenharmony_ci * - OAM support 4262306a36Sopenharmony_ci * - fix bugs listed below 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * KNOWN BUGS: 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * - may run into JK-JK bug and deadlock 4962306a36Sopenharmony_ci * - should allocate UBR channel first 5062306a36Sopenharmony_ci * - buffer space allocation algorithm is stupid 5162306a36Sopenharmony_ci * (RX: should be maxSDU+maxdelay*rate 5262306a36Sopenharmony_ci * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) 5362306a36Sopenharmony_ci * - doesn't support OAM cells 5462306a36Sopenharmony_ci * - eni_put_free may hang if not putting memory fragments that _complete_ 5562306a36Sopenharmony_ci * 2^n block (never happens in real life, though) 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#if 0 6062306a36Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 6162306a36Sopenharmony_ci#else 6262306a36Sopenharmony_ci#define DPRINTK(format,args...) 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST 6762306a36Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_TX_8W 6862306a36Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_RX_4W 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_DEBUG 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define NULLCHECK(x) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define EVENT(s,a,b) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void event_dump(void) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#else 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * NULL pointer checking 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define NULLCHECK(x) \ 9362306a36Sopenharmony_ci if ((unsigned long) (x) < 0x30) \ 9462306a36Sopenharmony_ci printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x)) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Very extensive activity logging. Greatly improves bug detection speed but 9862306a36Sopenharmony_ci * costs a few Mbps if enabled. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define EV 64 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic const char *ev[EV]; 10462306a36Sopenharmony_cistatic unsigned long ev_a[EV],ev_b[EV]; 10562306a36Sopenharmony_cistatic int ec = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void EVENT(const char *s,unsigned long a,unsigned long b) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci ev[ec] = s; 11162306a36Sopenharmony_ci ev_a[ec] = a; 11262306a36Sopenharmony_ci ev_b[ec] = b; 11362306a36Sopenharmony_ci ec = (ec+1) % EV; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void event_dump(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int n,i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for (n = 0; n < EV; n++) { 12262306a36Sopenharmony_ci i = (ec+n) % EV; 12362306a36Sopenharmony_ci printk(KERN_NOTICE); 12462306a36Sopenharmony_ci printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#endif /* CONFIG_ATM_ENI_DEBUG */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * NExx must not be equal at end 13462306a36Sopenharmony_ci * EExx may be equal at end 13562306a36Sopenharmony_ci * xxPJOK verify validity of pointer jumps 13662306a36Sopenharmony_ci * xxPMOK operating on a circular buffer of "c" words 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define NEPJOK(a0,a1,b) \ 14062306a36Sopenharmony_ci ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1)) 14162306a36Sopenharmony_ci#define EEPJOK(a0,a1,b) \ 14262306a36Sopenharmony_ci ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1)) 14362306a36Sopenharmony_ci#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b) 14462306a36Sopenharmony_ci#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0, 14862306a36Sopenharmony_ci backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0, 14962306a36Sopenharmony_ci putting = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct atm_dev *eni_boards = NULL; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* Read/write registers on card */ 15462306a36Sopenharmony_ci#define eni_in(r) readl(eni_dev->reg+(r)*4) 15562306a36Sopenharmony_ci#define eni_out(v,r) writel((v),eni_dev->reg+(r)*4) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/*-------------------------------- utilities --------------------------------*/ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void dump_mem(struct eni_dev *eni_dev) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int i; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci for (i = 0; i < eni_dev->free_len; i++) 16662306a36Sopenharmony_ci printk(KERN_DEBUG " %d: %p %d\n",i, 16762306a36Sopenharmony_ci eni_dev->free_list[i].start, 16862306a36Sopenharmony_ci 1 << eni_dev->free_list[i].order); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void dump(struct atm_dev *dev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct eni_dev *eni_dev; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci int i; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 17962306a36Sopenharmony_ci printk(KERN_NOTICE "Free memory\n"); 18062306a36Sopenharmony_ci dump_mem(eni_dev); 18162306a36Sopenharmony_ci printk(KERN_NOTICE "TX buffers\n"); 18262306a36Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) 18362306a36Sopenharmony_ci if (eni_dev->tx[i].send) 18462306a36Sopenharmony_ci printk(KERN_NOTICE " TX %d @ %p: %ld\n",i, 18562306a36Sopenharmony_ci eni_dev->tx[i].send,eni_dev->tx[i].words*4); 18662306a36Sopenharmony_ci printk(KERN_NOTICE "RX buffers\n"); 18762306a36Sopenharmony_ci for (i = 0; i < 1024; i++) 18862306a36Sopenharmony_ci if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx) 18962306a36Sopenharmony_ci printk(KERN_NOTICE " RX %d @ %p: %ld\n",i, 19062306a36Sopenharmony_ci ENI_VCC(eni_dev->rx_map[i])->recv, 19162306a36Sopenharmony_ci ENI_VCC(eni_dev->rx_map[i])->words*4); 19262306a36Sopenharmony_ci printk(KERN_NOTICE "----\n"); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void eni_put_free(struct eni_dev *eni_dev, void __iomem *start, 19762306a36Sopenharmony_ci unsigned long size) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct eni_free *list; 20062306a36Sopenharmony_ci int len,order; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size); 20362306a36Sopenharmony_ci start += eni_dev->base_diff; 20462306a36Sopenharmony_ci list = eni_dev->free_list; 20562306a36Sopenharmony_ci len = eni_dev->free_len; 20662306a36Sopenharmony_ci while (size) { 20762306a36Sopenharmony_ci if (len >= eni_dev->free_list_size) { 20862306a36Sopenharmony_ci printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n", 20962306a36Sopenharmony_ci start,size); 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++); 21362306a36Sopenharmony_ci if (MID_MIN_BUF_SIZE > (1 << order)) { 21462306a36Sopenharmony_ci printk(KERN_CRIT "eni_put_free: order %d too small\n", 21562306a36Sopenharmony_ci order); 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci list[len].start = (void __iomem *) start; 21962306a36Sopenharmony_ci list[len].order = order; 22062306a36Sopenharmony_ci len++; 22162306a36Sopenharmony_ci start += 1 << order; 22262306a36Sopenharmony_ci size -= 1 << order; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci eni_dev->free_len = len; 22562306a36Sopenharmony_ci /*dump_mem(eni_dev);*/ 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct eni_free *list; 23262306a36Sopenharmony_ci void __iomem *start; 23362306a36Sopenharmony_ci int len,i,order,best_order,index; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci list = eni_dev->free_list; 23662306a36Sopenharmony_ci len = eni_dev->free_len; 23762306a36Sopenharmony_ci if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE; 23862306a36Sopenharmony_ci if (*size > MID_MAX_BUF_SIZE) return NULL; 23962306a36Sopenharmony_ci for (order = 0; (1 << order) < *size; order++) 24062306a36Sopenharmony_ci ; 24162306a36Sopenharmony_ci DPRINTK("trying: %ld->%d\n",*size,order); 24262306a36Sopenharmony_ci best_order = 65; /* we don't have more than 2^64 of anything ... */ 24362306a36Sopenharmony_ci index = 0; /* silence GCC */ 24462306a36Sopenharmony_ci for (i = 0; i < len; i++) 24562306a36Sopenharmony_ci if (list[i].order == order) { 24662306a36Sopenharmony_ci best_order = order; 24762306a36Sopenharmony_ci index = i; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci else if (best_order > list[i].order && list[i].order > order) { 25162306a36Sopenharmony_ci best_order = list[i].order; 25262306a36Sopenharmony_ci index = i; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if (best_order == 65) return NULL; 25562306a36Sopenharmony_ci start = list[index].start-eni_dev->base_diff; 25662306a36Sopenharmony_ci list[index] = list[--len]; 25762306a36Sopenharmony_ci eni_dev->free_len = len; 25862306a36Sopenharmony_ci *size = 1 << order; 25962306a36Sopenharmony_ci eni_put_free(eni_dev,start+*size,(1 << best_order)-*size); 26062306a36Sopenharmony_ci DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start); 26162306a36Sopenharmony_ci memset_io(start,0,*size); /* never leak data */ 26262306a36Sopenharmony_ci /*dump_mem(eni_dev);*/ 26362306a36Sopenharmony_ci return start; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start, 26862306a36Sopenharmony_ci unsigned long size) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct eni_free *list; 27162306a36Sopenharmony_ci int len,i,order; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci start += eni_dev->base_diff; 27462306a36Sopenharmony_ci list = eni_dev->free_list; 27562306a36Sopenharmony_ci len = eni_dev->free_len; 27662306a36Sopenharmony_ci for (order = -1; size; order++) size >>= 1; 27762306a36Sopenharmony_ci DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order); 27862306a36Sopenharmony_ci for (i = 0; i < len; i++) 27962306a36Sopenharmony_ci if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) && 28062306a36Sopenharmony_ci list[i].order == order) { 28162306a36Sopenharmony_ci DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i, 28262306a36Sopenharmony_ci list[i].start,start,1 << order,list[i].order,order); 28362306a36Sopenharmony_ci list[i] = list[--len]; 28462306a36Sopenharmony_ci start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order)); 28562306a36Sopenharmony_ci order++; 28662306a36Sopenharmony_ci i = -1; 28762306a36Sopenharmony_ci continue; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (len >= eni_dev->free_list_size) { 29062306a36Sopenharmony_ci printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start, 29162306a36Sopenharmony_ci order); 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci list[len].start = start; 29562306a36Sopenharmony_ci list[len].order = order; 29662306a36Sopenharmony_ci eni_dev->free_len = len+1; 29762306a36Sopenharmony_ci /*dump_mem(eni_dev);*/ 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/*----------------------------------- RX ------------------------------------*/ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci#define ENI_VCC_NOS ((struct atm_vcc *) 1) 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void rx_ident_err(struct atm_vcc *vcc) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct atm_dev *dev; 31062306a36Sopenharmony_ci struct eni_dev *eni_dev; 31162306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci dev = vcc->dev; 31462306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 31562306a36Sopenharmony_ci /* immediately halt adapter */ 31662306a36Sopenharmony_ci eni_out(eni_in(MID_MC_S) & 31762306a36Sopenharmony_ci ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S); 31862306a36Sopenharmony_ci /* dump useful information */ 31962306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 32062306a36Sopenharmony_ci printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident " 32162306a36Sopenharmony_ci "mismatch\n",dev->number); 32262306a36Sopenharmony_ci printk(KERN_ALERT " VCI %d, rxing %d, words %ld\n",vcc->vci, 32362306a36Sopenharmony_ci eni_vcc->rxing,eni_vcc->words); 32462306a36Sopenharmony_ci printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value " 32562306a36Sopenharmony_ci "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, 32662306a36Sopenharmony_ci (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4)); 32762306a36Sopenharmony_ci printk(KERN_ALERT " last %p, servicing %d\n",eni_vcc->last, 32862306a36Sopenharmony_ci eni_vcc->servicing); 32962306a36Sopenharmony_ci EVENT("---dump ends here---\n",0,0); 33062306a36Sopenharmony_ci printk(KERN_NOTICE "---recent events---\n"); 33162306a36Sopenharmony_ci event_dump(); 33262306a36Sopenharmony_ci ENI_DEV(dev)->fast = NULL; /* really stop it */ 33362306a36Sopenharmony_ci ENI_DEV(dev)->slow = NULL; 33462306a36Sopenharmony_ci skb_queue_head_init(&ENI_DEV(dev)->rx_queue); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, 33962306a36Sopenharmony_ci unsigned long skip,unsigned long size,unsigned long eff) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct eni_dev *eni_dev; 34262306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 34362306a36Sopenharmony_ci u32 dma_rd,dma_wr; 34462306a36Sopenharmony_ci u32 dma[RX_DMA_BUF*2]; 34562306a36Sopenharmony_ci dma_addr_t paddr; 34662306a36Sopenharmony_ci unsigned long here; 34762306a36Sopenharmony_ci int i,j; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 35062306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 35162306a36Sopenharmony_ci paddr = 0; /* GCC, shut up */ 35262306a36Sopenharmony_ci if (skb) { 35362306a36Sopenharmony_ci paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, 35462306a36Sopenharmony_ci DMA_FROM_DEVICE); 35562306a36Sopenharmony_ci if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) 35662306a36Sopenharmony_ci goto dma_map_error; 35762306a36Sopenharmony_ci ENI_PRV_PADDR(skb) = paddr; 35862306a36Sopenharmony_ci if (paddr & 3) 35962306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has " 36062306a36Sopenharmony_ci "mis-aligned RX data (0x%lx)\n",vcc->dev->number, 36162306a36Sopenharmony_ci vcc->vci,(unsigned long) paddr); 36262306a36Sopenharmony_ci ENI_PRV_SIZE(skb) = size+skip; 36362306a36Sopenharmony_ci /* PDU plus descriptor */ 36462306a36Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci j = 0; 36762306a36Sopenharmony_ci if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */ 36862306a36Sopenharmony_ci here = (eni_vcc->descr+skip) & (eni_vcc->words-1); 36962306a36Sopenharmony_ci dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci 37062306a36Sopenharmony_ci << MID_DMA_VCI_SHIFT) | MID_DT_JK; 37162306a36Sopenharmony_ci dma[j++] = 0; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1); 37462306a36Sopenharmony_ci if (!eff) size += skip; 37562306a36Sopenharmony_ci else { 37662306a36Sopenharmony_ci unsigned long words; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (!size) { 37962306a36Sopenharmony_ci DPRINTK("strange things happen ...\n"); 38062306a36Sopenharmony_ci EVENT("strange things happen ... (skip=%ld,eff=%ld)\n", 38162306a36Sopenharmony_ci size,eff); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci words = eff; 38462306a36Sopenharmony_ci if (paddr & 15) { 38562306a36Sopenharmony_ci unsigned long init; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci init = 4-((paddr & 15) >> 2); 38862306a36Sopenharmony_ci if (init > words) init = words; 38962306a36Sopenharmony_ci dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | 39062306a36Sopenharmony_ci (vcc->vci << MID_DMA_VCI_SHIFT); 39162306a36Sopenharmony_ci dma[j++] = paddr; 39262306a36Sopenharmony_ci paddr += init << 2; 39362306a36Sopenharmony_ci words -= init; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ 39662306a36Sopenharmony_ci if (words & ~15) { 39762306a36Sopenharmony_ci dma[j++] = MID_DT_16W | ((words >> 4) << 39862306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 39962306a36Sopenharmony_ci MID_DMA_VCI_SHIFT); 40062306a36Sopenharmony_ci dma[j++] = paddr; 40162306a36Sopenharmony_ci paddr += (words & ~15) << 2; 40262306a36Sopenharmony_ci words &= 15; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci#endif 40562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ 40662306a36Sopenharmony_ci if (words & ~7) { 40762306a36Sopenharmony_ci dma[j++] = MID_DT_8W | ((words >> 3) << 40862306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 40962306a36Sopenharmony_ci MID_DMA_VCI_SHIFT); 41062306a36Sopenharmony_ci dma[j++] = paddr; 41162306a36Sopenharmony_ci paddr += (words & ~7) << 2; 41262306a36Sopenharmony_ci words &= 7; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci#endif 41562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ 41662306a36Sopenharmony_ci if (words & ~3) { 41762306a36Sopenharmony_ci dma[j++] = MID_DT_4W | ((words >> 2) << 41862306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 41962306a36Sopenharmony_ci MID_DMA_VCI_SHIFT); 42062306a36Sopenharmony_ci dma[j++] = paddr; 42162306a36Sopenharmony_ci paddr += (words & ~3) << 2; 42262306a36Sopenharmony_ci words &= 3; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci#endif 42562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ 42662306a36Sopenharmony_ci if (words & ~1) { 42762306a36Sopenharmony_ci dma[j++] = MID_DT_2W | ((words >> 1) << 42862306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 42962306a36Sopenharmony_ci MID_DMA_VCI_SHIFT); 43062306a36Sopenharmony_ci dma[j++] = paddr; 43162306a36Sopenharmony_ci paddr += (words & ~1) << 2; 43262306a36Sopenharmony_ci words &= 1; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci#endif 43562306a36Sopenharmony_ci if (words) { 43662306a36Sopenharmony_ci dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) 43762306a36Sopenharmony_ci | (vcc->vci << MID_DMA_VCI_SHIFT); 43862306a36Sopenharmony_ci dma[j++] = paddr; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci if (size != eff) { 44262306a36Sopenharmony_ci dma[j++] = (here << MID_DMA_COUNT_SHIFT) | 44362306a36Sopenharmony_ci (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; 44462306a36Sopenharmony_ci dma[j++] = 0; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci if (!j || j > 2*RX_DMA_BUF) { 44762306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n"); 44862306a36Sopenharmony_ci goto trouble; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci dma[j-2] |= MID_DMA_END; 45162306a36Sopenharmony_ci j = j >> 1; 45262306a36Sopenharmony_ci dma_wr = eni_in(MID_DMA_WR_RX); 45362306a36Sopenharmony_ci dma_rd = eni_in(MID_DMA_RD_RX); 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Can I move the dma_wr pointer by 2j+1 positions without overwriting 45662306a36Sopenharmony_ci * data that hasn't been read (position of dma_rd) yet ? 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */ 45962306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n", 46062306a36Sopenharmony_ci vcc->dev->number); 46162306a36Sopenharmony_ci goto trouble; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci for (i = 0; i < j; i++) { 46462306a36Sopenharmony_ci writel(dma[i*2],eni_dev->rx_dma+dma_wr*8); 46562306a36Sopenharmony_ci writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4); 46662306a36Sopenharmony_ci dma_wr = (dma_wr+1) & (NR_DMA_RX-1); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci if (skb) { 46962306a36Sopenharmony_ci ENI_PRV_POS(skb) = eni_vcc->descr+size+1; 47062306a36Sopenharmony_ci skb_queue_tail(&eni_dev->rx_queue,skb); 47162306a36Sopenharmony_ci eni_vcc->last = skb; 47262306a36Sopenharmony_ci rx_enqueued++; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci eni_vcc->descr = here; 47562306a36Sopenharmony_ci eni_out(dma_wr,MID_DMA_WR_RX); 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_citrouble: 47962306a36Sopenharmony_ci if (paddr) 48062306a36Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len, 48162306a36Sopenharmony_ci DMA_FROM_DEVICE); 48262306a36Sopenharmony_cidma_map_error: 48362306a36Sopenharmony_ci if (skb) dev_kfree_skb_irq(skb); 48462306a36Sopenharmony_ci return -1; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void discard(struct atm_vcc *vcc,unsigned long size) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 49362306a36Sopenharmony_ci EVENT("discard (size=%ld)\n",size,0); 49462306a36Sopenharmony_ci while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0); 49562306a36Sopenharmony_ci /* could do a full fallback, but that might be more expensive */ 49662306a36Sopenharmony_ci if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1; 49762306a36Sopenharmony_ci else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/* 50262306a36Sopenharmony_ci * TODO: should check whether direct copies (without DMA setup, dequeuing on 50362306a36Sopenharmony_ci * interrupt, etc.) aren't much faster for AAL0 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int rx_aal0(struct atm_vcc *vcc) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 50962306a36Sopenharmony_ci unsigned long descr; 51062306a36Sopenharmony_ci unsigned long length; 51162306a36Sopenharmony_ci struct sk_buff *skb; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci DPRINTK(">rx_aal0\n"); 51462306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 51562306a36Sopenharmony_ci descr = readl(eni_vcc->recv+eni_vcc->descr*4); 51662306a36Sopenharmony_ci if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { 51762306a36Sopenharmony_ci rx_ident_err(vcc); 51862306a36Sopenharmony_ci return 1; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci if (descr & MID_RED_T) { 52162306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", 52262306a36Sopenharmony_ci vcc->dev->number); 52362306a36Sopenharmony_ci length = 0; 52462306a36Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci else { 52762306a36Sopenharmony_ci length = ATM_CELL_SIZE-1; /* no HEC */ 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL; 53062306a36Sopenharmony_ci if (!skb) { 53162306a36Sopenharmony_ci discard(vcc,length >> 2); 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci skb_put(skb,length); 53562306a36Sopenharmony_ci skb->tstamp = eni_vcc->timestamp; 53662306a36Sopenharmony_ci DPRINTK("got len %ld\n",length); 53762306a36Sopenharmony_ci if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; 53862306a36Sopenharmony_ci eni_vcc->rxing++; 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int rx_aal5(struct atm_vcc *vcc) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 54662306a36Sopenharmony_ci unsigned long descr; 54762306a36Sopenharmony_ci unsigned long size,eff,length; 54862306a36Sopenharmony_ci struct sk_buff *skb; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci EVENT("rx_aal5\n",0,0); 55162306a36Sopenharmony_ci DPRINTK(">rx_aal5\n"); 55262306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 55362306a36Sopenharmony_ci descr = readl(eni_vcc->recv+eni_vcc->descr*4); 55462306a36Sopenharmony_ci if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { 55562306a36Sopenharmony_ci rx_ident_err(vcc); 55662306a36Sopenharmony_ci return 1; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci if (descr & (MID_RED_T | MID_RED_CRC_ERR)) { 55962306a36Sopenharmony_ci if (descr & MID_RED_T) { 56062306a36Sopenharmony_ci EVENT("empty cell (descr=0x%lx)\n",descr,0); 56162306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", 56262306a36Sopenharmony_ci vcc->dev->number); 56362306a36Sopenharmony_ci size = 0; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci else { 56662306a36Sopenharmony_ci static unsigned long silence = 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (time_after(jiffies, silence) || silence == 0) { 56962306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): " 57062306a36Sopenharmony_ci "discarding PDU(s) with CRC error\n", 57162306a36Sopenharmony_ci vcc->dev->number); 57262306a36Sopenharmony_ci silence = (jiffies+2*HZ)|1; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); 57562306a36Sopenharmony_ci EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr, 57662306a36Sopenharmony_ci size); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci eff = length = 0; 57962306a36Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci else { 58262306a36Sopenharmony_ci size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); 58362306a36Sopenharmony_ci DPRINTK("size=%ld\n",size); 58462306a36Sopenharmony_ci length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) & 58562306a36Sopenharmony_ci (eni_vcc->words-1)))*4) & 0xffff; 58662306a36Sopenharmony_ci /* -trailer(2)+header(1) */ 58762306a36Sopenharmony_ci if (length && length <= (size << 2)-8 && length <= 58862306a36Sopenharmony_ci ATM_MAX_AAL5_PDU) eff = (length+3) >> 2; 58962306a36Sopenharmony_ci else { /* ^ trailer length (8) */ 59062306a36Sopenharmony_ci EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr, 59162306a36Sopenharmony_ci length); 59262306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU " 59362306a36Sopenharmony_ci "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n", 59462306a36Sopenharmony_ci vcc->dev->number,vcc->vci,length,size << 2,descr); 59562306a36Sopenharmony_ci length = eff = 0; 59662306a36Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL; 60062306a36Sopenharmony_ci if (!skb) { 60162306a36Sopenharmony_ci discard(vcc,size); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci skb_put(skb,length); 60562306a36Sopenharmony_ci DPRINTK("got len %ld\n",length); 60662306a36Sopenharmony_ci if (do_rx_dma(vcc,skb,1,size,eff)) return 1; 60762306a36Sopenharmony_ci eni_vcc->rxing++; 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic inline int rx_vcc(struct atm_vcc *vcc) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci void __iomem *vci_dsc; 61562306a36Sopenharmony_ci unsigned long tmp; 61662306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 61962306a36Sopenharmony_ci vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16; 62062306a36Sopenharmony_ci EVENT("rx_vcc(1)\n",0,0); 62162306a36Sopenharmony_ci while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >> 62262306a36Sopenharmony_ci MID_VCI_DESCR_SHIFT)) { 62362306a36Sopenharmony_ci EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n", 62462306a36Sopenharmony_ci eni_vcc->descr,tmp); 62562306a36Sopenharmony_ci DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, 62662306a36Sopenharmony_ci (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> 62762306a36Sopenharmony_ci MID_VCI_DESCR_SHIFT)); 62862306a36Sopenharmony_ci if (ENI_VCC(vcc)->rx(vcc)) return 1; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci /* clear IN_SERVICE flag */ 63162306a36Sopenharmony_ci writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); 63262306a36Sopenharmony_ci /* 63362306a36Sopenharmony_ci * If new data has arrived between evaluating the while condition and 63462306a36Sopenharmony_ci * clearing IN_SERVICE, we wouldn't be notified until additional data 63562306a36Sopenharmony_ci * follows. So we have to loop again to be sure. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ci EVENT("rx_vcc(3)\n",0,0); 63862306a36Sopenharmony_ci while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) 63962306a36Sopenharmony_ci >> MID_VCI_DESCR_SHIFT)) { 64062306a36Sopenharmony_ci EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n", 64162306a36Sopenharmony_ci eni_vcc->descr,tmp); 64262306a36Sopenharmony_ci DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, 64362306a36Sopenharmony_ci (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> 64462306a36Sopenharmony_ci MID_VCI_DESCR_SHIFT)); 64562306a36Sopenharmony_ci if (ENI_VCC(vcc)->rx(vcc)) return 1; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic void poll_rx(struct atm_dev *dev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct eni_dev *eni_dev; 65462306a36Sopenharmony_ci struct atm_vcc *curr; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 65762306a36Sopenharmony_ci while ((curr = eni_dev->fast)) { 65862306a36Sopenharmony_ci EVENT("poll_rx.fast\n",0,0); 65962306a36Sopenharmony_ci if (rx_vcc(curr)) return; 66062306a36Sopenharmony_ci eni_dev->fast = ENI_VCC(curr)->next; 66162306a36Sopenharmony_ci ENI_VCC(curr)->next = ENI_VCC_NOS; 66262306a36Sopenharmony_ci barrier(); 66362306a36Sopenharmony_ci ENI_VCC(curr)->servicing--; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci while ((curr = eni_dev->slow)) { 66662306a36Sopenharmony_ci EVENT("poll_rx.slow\n",0,0); 66762306a36Sopenharmony_ci if (rx_vcc(curr)) return; 66862306a36Sopenharmony_ci eni_dev->slow = ENI_VCC(curr)->next; 66962306a36Sopenharmony_ci ENI_VCC(curr)->next = ENI_VCC_NOS; 67062306a36Sopenharmony_ci barrier(); 67162306a36Sopenharmony_ci ENI_VCC(curr)->servicing--; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void get_service(struct atm_dev *dev) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct eni_dev *eni_dev; 67962306a36Sopenharmony_ci struct atm_vcc *vcc; 68062306a36Sopenharmony_ci unsigned long vci; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci DPRINTK(">get_service\n"); 68362306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 68462306a36Sopenharmony_ci while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) { 68562306a36Sopenharmony_ci vci = readl(eni_dev->service+eni_dev->serv_read*4); 68662306a36Sopenharmony_ci eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1); 68762306a36Sopenharmony_ci vcc = eni_dev->rx_map[vci & 1023]; 68862306a36Sopenharmony_ci if (!vcc) { 68962306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not " 69062306a36Sopenharmony_ci "found\n",dev->number,vci); 69162306a36Sopenharmony_ci continue; /* nasty but we try to go on anyway */ 69262306a36Sopenharmony_ci /* @@@ nope, doesn't work */ 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci EVENT("getting from service\n",0,0); 69562306a36Sopenharmony_ci if (ENI_VCC(vcc)->next != ENI_VCC_NOS) { 69662306a36Sopenharmony_ci EVENT("double service\n",0,0); 69762306a36Sopenharmony_ci DPRINTK("Grr, servicing VCC %ld twice\n",vci); 69862306a36Sopenharmony_ci continue; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci ENI_VCC(vcc)->timestamp = ktime_get_real(); 70162306a36Sopenharmony_ci ENI_VCC(vcc)->next = NULL; 70262306a36Sopenharmony_ci if (vcc->qos.rxtp.traffic_class == ATM_CBR) { 70362306a36Sopenharmony_ci if (eni_dev->fast) 70462306a36Sopenharmony_ci ENI_VCC(eni_dev->last_fast)->next = vcc; 70562306a36Sopenharmony_ci else eni_dev->fast = vcc; 70662306a36Sopenharmony_ci eni_dev->last_fast = vcc; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci else { 70962306a36Sopenharmony_ci if (eni_dev->slow) 71062306a36Sopenharmony_ci ENI_VCC(eni_dev->last_slow)->next = vcc; 71162306a36Sopenharmony_ci else eni_dev->slow = vcc; 71262306a36Sopenharmony_ci eni_dev->last_slow = vcc; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci putting++; 71562306a36Sopenharmony_ci ENI_VCC(vcc)->servicing++; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void dequeue_rx(struct atm_dev *dev) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct eni_dev *eni_dev; 72362306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 72462306a36Sopenharmony_ci struct atm_vcc *vcc; 72562306a36Sopenharmony_ci struct sk_buff *skb; 72662306a36Sopenharmony_ci void __iomem *vci_dsc; 72762306a36Sopenharmony_ci int first; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 73062306a36Sopenharmony_ci first = 1; 73162306a36Sopenharmony_ci while (1) { 73262306a36Sopenharmony_ci skb = skb_dequeue(&eni_dev->rx_queue); 73362306a36Sopenharmony_ci if (!skb) { 73462306a36Sopenharmony_ci if (first) { 73562306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): RX but not " 73662306a36Sopenharmony_ci "rxing\n",dev->number); 73762306a36Sopenharmony_ci EVENT("nothing to dequeue\n",0,0); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), 74262306a36Sopenharmony_ci ENI_PRV_POS(skb)); 74362306a36Sopenharmony_ci rx_dequeued++; 74462306a36Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 74562306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 74662306a36Sopenharmony_ci first = 0; 74762306a36Sopenharmony_ci vci_dsc = eni_dev->vci+vcc->vci*16; 74862306a36Sopenharmony_ci if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), 74962306a36Sopenharmony_ci (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT, 75062306a36Sopenharmony_ci eni_vcc->words)) { 75162306a36Sopenharmony_ci EVENT("requeuing\n",0,0); 75262306a36Sopenharmony_ci skb_queue_head(&eni_dev->rx_queue,skb); 75362306a36Sopenharmony_ci break; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci eni_vcc->rxing--; 75662306a36Sopenharmony_ci eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1); 75762306a36Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, 75862306a36Sopenharmony_ci DMA_TO_DEVICE); 75962306a36Sopenharmony_ci if (!skb->len) dev_kfree_skb_irq(skb); 76062306a36Sopenharmony_ci else { 76162306a36Sopenharmony_ci EVENT("pushing (len=%ld)\n",skb->len,0); 76262306a36Sopenharmony_ci if (vcc->qos.aal == ATM_AAL0) 76362306a36Sopenharmony_ci *(unsigned long *) skb->data = 76462306a36Sopenharmony_ci ntohl(*(unsigned long *) skb->data); 76562306a36Sopenharmony_ci memset(skb->cb,0,sizeof(struct eni_skb_prv)); 76662306a36Sopenharmony_ci vcc->push(vcc,skb); 76762306a36Sopenharmony_ci pushed++; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci atomic_inc(&vcc->stats->rx); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci wake_up(&eni_dev->rx_wait); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int open_rx_first(struct atm_vcc *vcc) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci struct eni_dev *eni_dev; 77862306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 77962306a36Sopenharmony_ci unsigned long size; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci DPRINTK("open_rx_first\n"); 78262306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 78362306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 78462306a36Sopenharmony_ci eni_vcc->rx = NULL; 78562306a36Sopenharmony_ci if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0; 78662306a36Sopenharmony_ci size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100; 78762306a36Sopenharmony_ci if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= 78862306a36Sopenharmony_ci MID_MAX_BUF_SIZE) 78962306a36Sopenharmony_ci size = MID_MAX_BUF_SIZE; 79062306a36Sopenharmony_ci eni_vcc->recv = eni_alloc_mem(eni_dev,&size); 79162306a36Sopenharmony_ci DPRINTK("rx at 0x%lx\n",eni_vcc->recv); 79262306a36Sopenharmony_ci eni_vcc->words = size >> 2; 79362306a36Sopenharmony_ci if (!eni_vcc->recv) return -ENOBUFS; 79462306a36Sopenharmony_ci eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0; 79562306a36Sopenharmony_ci eni_vcc->descr = 0; 79662306a36Sopenharmony_ci eni_vcc->rx_pos = 0; 79762306a36Sopenharmony_ci eni_vcc->rxing = 0; 79862306a36Sopenharmony_ci eni_vcc->servicing = 0; 79962306a36Sopenharmony_ci eni_vcc->next = ENI_VCC_NOS; 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int open_rx_second(struct atm_vcc *vcc) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci void __iomem *here; 80762306a36Sopenharmony_ci struct eni_dev *eni_dev; 80862306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 80962306a36Sopenharmony_ci unsigned long size; 81062306a36Sopenharmony_ci int order; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci DPRINTK("open_rx_second\n"); 81362306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 81462306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 81562306a36Sopenharmony_ci if (!eni_vcc->rx) return 0; 81662306a36Sopenharmony_ci /* set up VCI descriptor */ 81762306a36Sopenharmony_ci here = eni_dev->vci+vcc->vci*16; 81862306a36Sopenharmony_ci DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4); 81962306a36Sopenharmony_ci size = eni_vcc->words >> 8; 82062306a36Sopenharmony_ci for (order = -1; size; order++) size >>= 1; 82162306a36Sopenharmony_ci writel(0,here+4); /* descr, read = 0 */ 82262306a36Sopenharmony_ci writel(0,here+8); /* write, state, count = 0 */ 82362306a36Sopenharmony_ci if (eni_dev->rx_map[vcc->vci]) 82462306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already " 82562306a36Sopenharmony_ci "in use\n",vcc->dev->number,vcc->vci); 82662306a36Sopenharmony_ci eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */ 82762306a36Sopenharmony_ci writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) << 82862306a36Sopenharmony_ci MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE | 82962306a36Sopenharmony_ci (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) << 83062306a36Sopenharmony_ci MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here); 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic void close_rx(struct atm_vcc *vcc) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait,current); 83862306a36Sopenharmony_ci void __iomem *here; 83962306a36Sopenharmony_ci struct eni_dev *eni_dev; 84062306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 84362306a36Sopenharmony_ci if (!eni_vcc->rx) return; 84462306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 84562306a36Sopenharmony_ci if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) { 84662306a36Sopenharmony_ci here = eni_dev->vci+vcc->vci*16; 84762306a36Sopenharmony_ci /* block receiver */ 84862306a36Sopenharmony_ci writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << 84962306a36Sopenharmony_ci MID_VCI_MODE_SHIFT),here); 85062306a36Sopenharmony_ci /* wait for receiver to become idle */ 85162306a36Sopenharmony_ci udelay(27); 85262306a36Sopenharmony_ci /* discard pending cell */ 85362306a36Sopenharmony_ci writel(readl(here) & ~MID_VCI_IN_SERVICE,here); 85462306a36Sopenharmony_ci /* don't accept any new ones */ 85562306a36Sopenharmony_ci eni_dev->rx_map[vcc->vci] = NULL; 85662306a36Sopenharmony_ci /* wait for RX queue to drain */ 85762306a36Sopenharmony_ci DPRINTK("eni_close: waiting for RX ...\n"); 85862306a36Sopenharmony_ci EVENT("RX closing\n",0,0); 85962306a36Sopenharmony_ci add_wait_queue(&eni_dev->rx_wait,&wait); 86062306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 86162306a36Sopenharmony_ci barrier(); 86262306a36Sopenharmony_ci for (;;) { 86362306a36Sopenharmony_ci /* transition service->rx: rxing++, servicing-- */ 86462306a36Sopenharmony_ci if (!eni_vcc->servicing) { 86562306a36Sopenharmony_ci barrier(); 86662306a36Sopenharmony_ci if (!eni_vcc->rxing) break; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing, 86962306a36Sopenharmony_ci eni_vcc->servicing); 87062306a36Sopenharmony_ci printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing, 87162306a36Sopenharmony_ci eni_vcc->rxing); 87262306a36Sopenharmony_ci schedule(); 87362306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci for (;;) { 87662306a36Sopenharmony_ci int at_end; 87762306a36Sopenharmony_ci u32 tmp; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci tasklet_disable(&eni_dev->task); 88062306a36Sopenharmony_ci tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ; 88162306a36Sopenharmony_ci at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT; 88262306a36Sopenharmony_ci tasklet_enable(&eni_dev->task); 88362306a36Sopenharmony_ci if (at_end) break; 88462306a36Sopenharmony_ci EVENT("drain discard (host 0x%lx, nic 0x%lx)\n", 88562306a36Sopenharmony_ci eni_vcc->rx_pos,tmp); 88662306a36Sopenharmony_ci printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n", 88762306a36Sopenharmony_ci eni_vcc->rx_pos,tmp); 88862306a36Sopenharmony_ci schedule(); 88962306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 89262306a36Sopenharmony_ci remove_wait_queue(&eni_dev->rx_wait,&wait); 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2); 89562306a36Sopenharmony_ci eni_vcc->rx = NULL; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int start_rx(struct atm_dev *dev) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct eni_dev *eni_dev; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 90462306a36Sopenharmony_ci eni_dev->rx_map = (struct atm_vcc **) get_zeroed_page(GFP_KERNEL); 90562306a36Sopenharmony_ci if (!eni_dev->rx_map) { 90662306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", 90762306a36Sopenharmony_ci dev->number); 90862306a36Sopenharmony_ci free_page((unsigned long) eni_dev->free_list); 90962306a36Sopenharmony_ci return -ENOMEM; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci eni_dev->rx_mult = DEFAULT_RX_MULT; 91262306a36Sopenharmony_ci eni_dev->fast = eni_dev->last_fast = NULL; 91362306a36Sopenharmony_ci eni_dev->slow = eni_dev->last_slow = NULL; 91462306a36Sopenharmony_ci init_waitqueue_head(&eni_dev->rx_wait); 91562306a36Sopenharmony_ci skb_queue_head_init(&eni_dev->rx_queue); 91662306a36Sopenharmony_ci eni_dev->serv_read = eni_in(MID_SERV_WRITE); 91762306a36Sopenharmony_ci eni_out(0,MID_DMA_WR_RX); 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci/*----------------------------------- TX ------------------------------------*/ 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cienum enq_res { enq_ok,enq_next,enq_jam }; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr, 92962306a36Sopenharmony_ci u32 size) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci u32 init,words; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size); 93462306a36Sopenharmony_ci EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size); 93562306a36Sopenharmony_ci#if 0 /* don't complain anymore */ 93662306a36Sopenharmony_ci if (paddr & 3) 93762306a36Sopenharmony_ci printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); 93862306a36Sopenharmony_ci if (size & 3) 93962306a36Sopenharmony_ci printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size); 94062306a36Sopenharmony_ci#endif 94162306a36Sopenharmony_ci if (paddr & 3) { 94262306a36Sopenharmony_ci init = 4-(paddr & 3); 94362306a36Sopenharmony_ci if (init > size || size < 7) init = size; 94462306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d/%d bytes\n", 94562306a36Sopenharmony_ci (unsigned long) paddr,init,size); 94662306a36Sopenharmony_ci dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | 94762306a36Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 94862306a36Sopenharmony_ci dma[(*j)++] = paddr; 94962306a36Sopenharmony_ci paddr += init; 95062306a36Sopenharmony_ci size -= init; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci words = size >> 2; 95362306a36Sopenharmony_ci size &= 3; 95462306a36Sopenharmony_ci if (words && (paddr & 31)) { 95562306a36Sopenharmony_ci init = 8-((paddr & 31) >> 2); 95662306a36Sopenharmony_ci if (init > words) init = words; 95762306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d/%d words\n", 95862306a36Sopenharmony_ci (unsigned long) paddr,init,words); 95962306a36Sopenharmony_ci dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | 96062306a36Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 96162306a36Sopenharmony_ci dma[(*j)++] = paddr; 96262306a36Sopenharmony_ci paddr += init << 2; 96362306a36Sopenharmony_ci words -= init; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ 96662306a36Sopenharmony_ci if (words & ~15) { 96762306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*16/%d words\n", 96862306a36Sopenharmony_ci (unsigned long) paddr,words >> 4,words); 96962306a36Sopenharmony_ci dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) 97062306a36Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 97162306a36Sopenharmony_ci dma[(*j)++] = paddr; 97262306a36Sopenharmony_ci paddr += (words & ~15) << 2; 97362306a36Sopenharmony_ci words &= 15; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci#endif 97662306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ 97762306a36Sopenharmony_ci if (words & ~7) { 97862306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*8/%d words\n", 97962306a36Sopenharmony_ci (unsigned long) paddr,words >> 3,words); 98062306a36Sopenharmony_ci dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) 98162306a36Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 98262306a36Sopenharmony_ci dma[(*j)++] = paddr; 98362306a36Sopenharmony_ci paddr += (words & ~7) << 2; 98462306a36Sopenharmony_ci words &= 7; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci#endif 98762306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ 98862306a36Sopenharmony_ci if (words & ~3) { 98962306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*4/%d words\n", 99062306a36Sopenharmony_ci (unsigned long) paddr,words >> 2,words); 99162306a36Sopenharmony_ci dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) 99262306a36Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 99362306a36Sopenharmony_ci dma[(*j)++] = paddr; 99462306a36Sopenharmony_ci paddr += (words & ~3) << 2; 99562306a36Sopenharmony_ci words &= 3; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci#endif 99862306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ 99962306a36Sopenharmony_ci if (words & ~1) { 100062306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*2/%d words\n", 100162306a36Sopenharmony_ci (unsigned long) paddr,words >> 1,words); 100262306a36Sopenharmony_ci dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) 100362306a36Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 100462306a36Sopenharmony_ci dma[(*j)++] = paddr; 100562306a36Sopenharmony_ci paddr += (words & ~1) << 2; 100662306a36Sopenharmony_ci words &= 1; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci#endif 100962306a36Sopenharmony_ci if (words) { 101062306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr, 101162306a36Sopenharmony_ci words); 101262306a36Sopenharmony_ci dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | 101362306a36Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 101462306a36Sopenharmony_ci dma[(*j)++] = paddr; 101562306a36Sopenharmony_ci paddr += words << 2; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci if (size) { 101862306a36Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr, 101962306a36Sopenharmony_ci size); 102062306a36Sopenharmony_ci dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | 102162306a36Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 102262306a36Sopenharmony_ci dma[(*j)++] = paddr; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic enum enq_res do_tx(struct sk_buff *skb) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct atm_vcc *vcc; 103062306a36Sopenharmony_ci struct eni_dev *eni_dev; 103162306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 103262306a36Sopenharmony_ci struct eni_tx *tx; 103362306a36Sopenharmony_ci dma_addr_t paddr; 103462306a36Sopenharmony_ci u32 dma_rd,dma_wr; 103562306a36Sopenharmony_ci u32 size; /* in words */ 103662306a36Sopenharmony_ci int aal5,dma_size,i,j; 103762306a36Sopenharmony_ci unsigned char skb_data3; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci DPRINTK(">do_tx\n"); 104062306a36Sopenharmony_ci NULLCHECK(skb); 104162306a36Sopenharmony_ci EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); 104262306a36Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 104362306a36Sopenharmony_ci NULLCHECK(vcc); 104462306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 104562306a36Sopenharmony_ci NULLCHECK(eni_dev); 104662306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 104762306a36Sopenharmony_ci tx = eni_vcc->tx; 104862306a36Sopenharmony_ci NULLCHECK(tx); 104962306a36Sopenharmony_ci#if 0 /* Enable this for testing with the "align" program */ 105062306a36Sopenharmony_ci { 105162306a36Sopenharmony_ci unsigned int hack = *((char *) skb->data)-'0'; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (hack < 8) { 105462306a36Sopenharmony_ci skb->data += hack; 105562306a36Sopenharmony_ci skb->len -= hack; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci#endif 105962306a36Sopenharmony_ci#if 0 /* should work now */ 106062306a36Sopenharmony_ci if ((unsigned long) skb->data & 3) 106162306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned " 106262306a36Sopenharmony_ci "TX data\n",vcc->dev->number,vcc->vci); 106362306a36Sopenharmony_ci#endif 106462306a36Sopenharmony_ci /* 106562306a36Sopenharmony_ci * Potential future IP speedup: make hard_header big enough to put 106662306a36Sopenharmony_ci * segmentation descriptor directly into PDU. Saves: 4 slave writes, 106762306a36Sopenharmony_ci * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-) 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci aal5 = vcc->qos.aal == ATM_AAL5; 107162306a36Sopenharmony_ci /* check space in buffer */ 107262306a36Sopenharmony_ci if (!aal5) 107362306a36Sopenharmony_ci size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE; 107462306a36Sopenharmony_ci /* cell without HEC plus segmentation header (includes 107562306a36Sopenharmony_ci four-byte cell header) */ 107662306a36Sopenharmony_ci else { 107762306a36Sopenharmony_ci size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1; 107862306a36Sopenharmony_ci /* add AAL5 trailer */ 107962306a36Sopenharmony_ci size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE; 108062306a36Sopenharmony_ci /* add segmentation header */ 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci /* 108362306a36Sopenharmony_ci * Can I move tx_pos by size bytes without getting closer than TX_GAP 108462306a36Sopenharmony_ci * to the read pointer ? TX_GAP means to leave some space for what 108562306a36Sopenharmony_ci * the manual calls "too close". 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ci if (!NEPMOK(tx->tx_pos,size+TX_GAP, 108862306a36Sopenharmony_ci eni_in(MID_TX_RDPTR(tx->index)),tx->words)) { 108962306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n", 109062306a36Sopenharmony_ci vcc->dev->number,size); 109162306a36Sopenharmony_ci return enq_next; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci /* check DMA */ 109462306a36Sopenharmony_ci dma_wr = eni_in(MID_DMA_WR_TX); 109562306a36Sopenharmony_ci dma_rd = eni_in(MID_DMA_RD_TX); 109662306a36Sopenharmony_ci dma_size = 3; /* JK for descriptor and final fill, plus final size 109762306a36Sopenharmony_ci mis-alignment fix */ 109862306a36Sopenharmony_ciDPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags); 109962306a36Sopenharmony_ci if (!skb_shinfo(skb)->nr_frags) dma_size += 5; 110062306a36Sopenharmony_ci else dma_size += 5*(skb_shinfo(skb)->nr_frags+1); 110162306a36Sopenharmony_ci if (dma_size > TX_DMA_BUF) { 110262306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries " 110362306a36Sopenharmony_ci "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF); 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos); 110662306a36Sopenharmony_ci if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) < 110762306a36Sopenharmony_ci dma_size) { 110862306a36Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n", 110962306a36Sopenharmony_ci vcc->dev->number); 111062306a36Sopenharmony_ci return enq_jam; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci skb_data3 = skb->data[3]; 111362306a36Sopenharmony_ci paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, 111462306a36Sopenharmony_ci DMA_TO_DEVICE); 111562306a36Sopenharmony_ci if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) 111662306a36Sopenharmony_ci return enq_next; 111762306a36Sopenharmony_ci ENI_PRV_PADDR(skb) = paddr; 111862306a36Sopenharmony_ci /* prepare DMA queue entries */ 111962306a36Sopenharmony_ci j = 0; 112062306a36Sopenharmony_ci eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) << 112162306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | 112262306a36Sopenharmony_ci MID_DT_JK; 112362306a36Sopenharmony_ci j++; 112462306a36Sopenharmony_ci if (!skb_shinfo(skb)->nr_frags) 112562306a36Sopenharmony_ci if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len); 112662306a36Sopenharmony_ci else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4); 112762306a36Sopenharmony_ci else { 112862306a36Sopenharmony_ciDPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */ 112962306a36Sopenharmony_ci for (i = -1; i < skb_shinfo(skb)->nr_frags; i++) 113062306a36Sopenharmony_ci if (i == -1) 113162306a36Sopenharmony_ci put_dma(tx->index,eni_dev->dma,&j,(unsigned long) 113262306a36Sopenharmony_ci skb->data, 113362306a36Sopenharmony_ci skb_headlen(skb)); 113462306a36Sopenharmony_ci else 113562306a36Sopenharmony_ci put_dma(tx->index,eni_dev->dma,&j,(unsigned long) 113662306a36Sopenharmony_ci skb_frag_page(&skb_shinfo(skb)->frags[i]) + 113762306a36Sopenharmony_ci skb_frag_off(&skb_shinfo(skb)->frags[i]), 113862306a36Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[i])); 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci if (skb->len & 3) { 114162306a36Sopenharmony_ci put_dma(tx->index, eni_dev->dma, &j, eni_dev->zero.dma, 114262306a36Sopenharmony_ci 4 - (skb->len & 3)); 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ 114562306a36Sopenharmony_ci eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) << 114662306a36Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | 114762306a36Sopenharmony_ci MID_DMA_END | MID_DT_JK; 114862306a36Sopenharmony_ci j++; 114962306a36Sopenharmony_ci DPRINTK("DMA at end: %d\n",j); 115062306a36Sopenharmony_ci /* store frame */ 115162306a36Sopenharmony_ci writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) | 115262306a36Sopenharmony_ci (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) | 115362306a36Sopenharmony_ci (tx->resolution << MID_SEG_RATE_SHIFT) | 115462306a36Sopenharmony_ci (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4); 115562306a36Sopenharmony_ci/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/ 115662306a36Sopenharmony_ci writel((vcc->vci << MID_SEG_VCI_SHIFT) | 115762306a36Sopenharmony_ci (aal5 ? 0 : (skb_data3 & 0xf)) | 115862306a36Sopenharmony_ci (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0), 115962306a36Sopenharmony_ci tx->send+((tx->tx_pos+1) & (tx->words-1))*4); 116062306a36Sopenharmony_ci DPRINTK("size: %d, len:%d\n",size,skb->len); 116162306a36Sopenharmony_ci if (aal5) 116262306a36Sopenharmony_ci writel(skb->len,tx->send+ 116362306a36Sopenharmony_ci ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4); 116462306a36Sopenharmony_ci j = j >> 1; 116562306a36Sopenharmony_ci for (i = 0; i < j; i++) { 116662306a36Sopenharmony_ci writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8); 116762306a36Sopenharmony_ci writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4); 116862306a36Sopenharmony_ci dma_wr = (dma_wr+1) & (NR_DMA_TX-1); 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci ENI_PRV_POS(skb) = tx->tx_pos; 117162306a36Sopenharmony_ci ENI_PRV_SIZE(skb) = size; 117262306a36Sopenharmony_ci ENI_VCC(vcc)->txing += size; 117362306a36Sopenharmony_ci tx->tx_pos = (tx->tx_pos+size) & (tx->words-1); 117462306a36Sopenharmony_ci DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos); 117562306a36Sopenharmony_ci eni_out(dma_wr,MID_DMA_WR_TX); 117662306a36Sopenharmony_ci skb_queue_tail(&eni_dev->tx_queue,skb); 117762306a36Sopenharmony_ci queued++; 117862306a36Sopenharmony_ci return enq_ok; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic void poll_tx(struct atm_dev *dev) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct eni_tx *tx; 118562306a36Sopenharmony_ci struct sk_buff *skb; 118662306a36Sopenharmony_ci enum enq_res res; 118762306a36Sopenharmony_ci int i; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci DPRINTK(">poll_tx\n"); 119062306a36Sopenharmony_ci for (i = NR_CHAN-1; i >= 0; i--) { 119162306a36Sopenharmony_ci tx = &ENI_DEV(dev)->tx[i]; 119262306a36Sopenharmony_ci if (tx->send) 119362306a36Sopenharmony_ci while ((skb = skb_dequeue(&tx->backlog))) { 119462306a36Sopenharmony_ci res = do_tx(skb); 119562306a36Sopenharmony_ci if (res == enq_ok) continue; 119662306a36Sopenharmony_ci DPRINTK("re-queuing TX PDU\n"); 119762306a36Sopenharmony_ci skb_queue_head(&tx->backlog,skb); 119862306a36Sopenharmony_ci requeued++; 119962306a36Sopenharmony_ci if (res == enq_jam) return; 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic void dequeue_tx(struct atm_dev *dev) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci struct eni_dev *eni_dev; 120962306a36Sopenharmony_ci struct atm_vcc *vcc; 121062306a36Sopenharmony_ci struct sk_buff *skb; 121162306a36Sopenharmony_ci struct eni_tx *tx; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci NULLCHECK(dev); 121462306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 121562306a36Sopenharmony_ci NULLCHECK(eni_dev); 121662306a36Sopenharmony_ci while ((skb = skb_dequeue(&eni_dev->tx_queue))) { 121762306a36Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 121862306a36Sopenharmony_ci NULLCHECK(vcc); 121962306a36Sopenharmony_ci tx = ENI_VCC(vcc)->tx; 122062306a36Sopenharmony_ci NULLCHECK(ENI_VCC(vcc)->tx); 122162306a36Sopenharmony_ci DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), 122262306a36Sopenharmony_ci (unsigned) eni_in(MID_TX_DESCRSTART(tx->index))); 122362306a36Sopenharmony_ci if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == 122462306a36Sopenharmony_ci eni_in(MID_TX_DESCRSTART(tx->index))) { 122562306a36Sopenharmony_ci skb_queue_head(&eni_dev->tx_queue,skb); 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); 122962306a36Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, 123062306a36Sopenharmony_ci DMA_TO_DEVICE); 123162306a36Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 123262306a36Sopenharmony_ci else dev_kfree_skb_irq(skb); 123362306a36Sopenharmony_ci atomic_inc(&vcc->stats->tx); 123462306a36Sopenharmony_ci wake_up(&eni_dev->tx_wait); 123562306a36Sopenharmony_ci dma_complete++; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci int i; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci for (i = !ubr; i < NR_CHAN; i++) 124562306a36Sopenharmony_ci if (!eni_dev->tx[i].send) return eni_dev->tx+i; 124662306a36Sopenharmony_ci return NULL; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cistatic int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, 125162306a36Sopenharmony_ci int *res,int unlimited) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci static const int pre_div[] = { 4,16,128,2048 }; 125462306a36Sopenharmony_ci /* 2^(((x+2)^2-(x+2))/2+1) */ 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (unlimited) *pre = *res = 0; 125762306a36Sopenharmony_ci else { 125862306a36Sopenharmony_ci if (*pcr > 0) { 125962306a36Sopenharmony_ci int div; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci for (*pre = 0; *pre < 3; (*pre)++) 126262306a36Sopenharmony_ci if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; 126362306a36Sopenharmony_ci div = pre_div[*pre]**pcr; 126462306a36Sopenharmony_ci DPRINTK("min div %d\n",div); 126562306a36Sopenharmony_ci *res = TS_CLOCK/div-1; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci else { 126862306a36Sopenharmony_ci int div; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if (!*pcr) *pcr = eni_dev->tx_bw+reserved; 127162306a36Sopenharmony_ci for (*pre = 3; *pre >= 0; (*pre)--) 127262306a36Sopenharmony_ci if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; 127362306a36Sopenharmony_ci if (*pre < 3) (*pre)++; /* else fail later */ 127462306a36Sopenharmony_ci div = pre_div[*pre]*-*pcr; 127562306a36Sopenharmony_ci DPRINTK("max div %d\n",div); 127662306a36Sopenharmony_ci *res = DIV_ROUND_UP(TS_CLOCK, div)-1; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci if (*res < 0) *res = 0; 127962306a36Sopenharmony_ci if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); 128262306a36Sopenharmony_ci DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); 128362306a36Sopenharmony_ci return 0; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, 128862306a36Sopenharmony_ci int set_rsv,int set_shp) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(vcc->dev); 129162306a36Sopenharmony_ci struct eni_vcc *eni_vcc = ENI_VCC(vcc); 129262306a36Sopenharmony_ci struct eni_tx *tx; 129362306a36Sopenharmony_ci unsigned long size; 129462306a36Sopenharmony_ci void __iomem *mem; 129562306a36Sopenharmony_ci int rate,ubr,unlimited,new_tx; 129662306a36Sopenharmony_ci int pre,res,order; 129762306a36Sopenharmony_ci int error; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci rate = atm_pcr_goal(txtp); 130062306a36Sopenharmony_ci ubr = txtp->traffic_class == ATM_UBR; 130162306a36Sopenharmony_ci unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || 130262306a36Sopenharmony_ci rate >= ATM_OC3_PCR); 130362306a36Sopenharmony_ci if (!unlimited) { 130462306a36Sopenharmony_ci size = txtp->max_sdu*eni_dev->tx_mult/100; 130562306a36Sopenharmony_ci if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= 130662306a36Sopenharmony_ci MID_MAX_BUF_SIZE) 130762306a36Sopenharmony_ci size = MID_MAX_BUF_SIZE; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci else { 131062306a36Sopenharmony_ci if (eni_dev->ubr) { 131162306a36Sopenharmony_ci eni_vcc->tx = eni_dev->ubr; 131262306a36Sopenharmony_ci txtp->pcr = ATM_OC3_PCR; 131362306a36Sopenharmony_ci return 0; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci size = UBR_BUFFER; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci new_tx = !eni_vcc->tx; 131862306a36Sopenharmony_ci mem = NULL; /* for gcc */ 131962306a36Sopenharmony_ci if (!new_tx) tx = eni_vcc->tx; 132062306a36Sopenharmony_ci else { 132162306a36Sopenharmony_ci mem = eni_alloc_mem(eni_dev,&size); 132262306a36Sopenharmony_ci if (!mem) return -ENOBUFS; 132362306a36Sopenharmony_ci tx = alloc_tx(eni_dev,unlimited); 132462306a36Sopenharmony_ci if (!tx) { 132562306a36Sopenharmony_ci eni_free_mem(eni_dev,mem,size); 132662306a36Sopenharmony_ci return -EBUSY; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci DPRINTK("got chan %d\n",tx->index); 132962306a36Sopenharmony_ci tx->reserved = tx->shaping = 0; 133062306a36Sopenharmony_ci tx->send = mem; 133162306a36Sopenharmony_ci tx->words = size >> 2; 133262306a36Sopenharmony_ci skb_queue_head_init(&tx->backlog); 133362306a36Sopenharmony_ci for (order = 0; size > (1 << (order+10)); order++); 133462306a36Sopenharmony_ci eni_out((order << MID_SIZE_SHIFT) | 133562306a36Sopenharmony_ci ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)), 133662306a36Sopenharmony_ci MID_TX_PLACE(tx->index)); 133762306a36Sopenharmony_ci tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) & 133862306a36Sopenharmony_ci MID_DESCR_START; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); 134162306a36Sopenharmony_ci if (!error && txtp->min_pcr > rate) error = -EINVAL; 134262306a36Sopenharmony_ci if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && 134362306a36Sopenharmony_ci txtp->max_pcr < rate) error = -EINVAL; 134462306a36Sopenharmony_ci if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) 134562306a36Sopenharmony_ci error = -EINVAL; 134662306a36Sopenharmony_ci if (!error && set_rsv && !set_shp && rate < tx->shaping) 134762306a36Sopenharmony_ci error = -EINVAL; 134862306a36Sopenharmony_ci if (!error && !set_rsv && rate > tx->reserved && !ubr) 134962306a36Sopenharmony_ci error = -EINVAL; 135062306a36Sopenharmony_ci if (error) { 135162306a36Sopenharmony_ci if (new_tx) { 135262306a36Sopenharmony_ci tx->send = NULL; 135362306a36Sopenharmony_ci eni_free_mem(eni_dev,mem,size); 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci return error; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci txtp->pcr = rate; 135862306a36Sopenharmony_ci if (set_rsv && !ubr) { 135962306a36Sopenharmony_ci eni_dev->tx_bw += tx->reserved; 136062306a36Sopenharmony_ci tx->reserved = rate; 136162306a36Sopenharmony_ci eni_dev->tx_bw -= rate; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci if (set_shp || (unlimited && new_tx)) { 136462306a36Sopenharmony_ci if (unlimited && new_tx) eni_dev->ubr = tx; 136562306a36Sopenharmony_ci tx->prescaler = pre; 136662306a36Sopenharmony_ci tx->resolution = res; 136762306a36Sopenharmony_ci tx->shaping = rate; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci if (set_shp) eni_vcc->tx = tx; 137062306a36Sopenharmony_ci DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic int open_tx_first(struct atm_vcc *vcc) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci ENI_VCC(vcc)->tx = NULL; 137862306a36Sopenharmony_ci if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; 137962306a36Sopenharmony_ci ENI_VCC(vcc)->txing = 0; 138062306a36Sopenharmony_ci return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1); 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic int open_tx_second(struct atm_vcc *vcc) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci return 0; /* nothing to do */ 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void close_tx(struct atm_vcc *vcc) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait,current); 139362306a36Sopenharmony_ci struct eni_dev *eni_dev; 139462306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 139762306a36Sopenharmony_ci if (!eni_vcc->tx) return; 139862306a36Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 139962306a36Sopenharmony_ci /* wait for TX queue to drain */ 140062306a36Sopenharmony_ci DPRINTK("eni_close: waiting for TX ...\n"); 140162306a36Sopenharmony_ci add_wait_queue(&eni_dev->tx_wait,&wait); 140262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 140362306a36Sopenharmony_ci for (;;) { 140462306a36Sopenharmony_ci int txing; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci tasklet_disable(&eni_dev->task); 140762306a36Sopenharmony_ci txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing; 140862306a36Sopenharmony_ci tasklet_enable(&eni_dev->task); 140962306a36Sopenharmony_ci if (!txing) break; 141062306a36Sopenharmony_ci DPRINTK("%d TX left\n",eni_vcc->txing); 141162306a36Sopenharmony_ci schedule(); 141262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 141562306a36Sopenharmony_ci remove_wait_queue(&eni_dev->tx_wait,&wait); 141662306a36Sopenharmony_ci if (eni_vcc->tx != eni_dev->ubr) { 141762306a36Sopenharmony_ci /* 141862306a36Sopenharmony_ci * Looping a few times in here is probably far cheaper than 141962306a36Sopenharmony_ci * keeping track of TX completions all the time, so let's poll 142062306a36Sopenharmony_ci * a bit ... 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_ci while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) != 142362306a36Sopenharmony_ci eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index))) 142462306a36Sopenharmony_ci schedule(); 142562306a36Sopenharmony_ci eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2); 142662306a36Sopenharmony_ci eni_vcc->tx->send = NULL; 142762306a36Sopenharmony_ci eni_dev->tx_bw += eni_vcc->tx->reserved; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci eni_vcc->tx = NULL; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic int start_tx(struct atm_dev *dev) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci struct eni_dev *eni_dev; 143662306a36Sopenharmony_ci int i; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 143962306a36Sopenharmony_ci eni_dev->lost = 0; 144062306a36Sopenharmony_ci eni_dev->tx_bw = ATM_OC3_PCR; 144162306a36Sopenharmony_ci eni_dev->tx_mult = DEFAULT_TX_MULT; 144262306a36Sopenharmony_ci init_waitqueue_head(&eni_dev->tx_wait); 144362306a36Sopenharmony_ci eni_dev->ubr = NULL; 144462306a36Sopenharmony_ci skb_queue_head_init(&eni_dev->tx_queue); 144562306a36Sopenharmony_ci eni_out(0,MID_DMA_WR_TX); 144662306a36Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) { 144762306a36Sopenharmony_ci eni_dev->tx[i].send = NULL; 144862306a36Sopenharmony_ci eni_dev->tx[i].index = i; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci return 0; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci/*--------------------------------- common ----------------------------------*/ 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci#if 0 /* may become useful again when tuning things */ 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_cistatic void foo(void) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ciprintk(KERN_INFO 146262306a36Sopenharmony_ci "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n" 146362306a36Sopenharmony_ci "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n", 146462306a36Sopenharmony_ci tx_complete,dma_complete,queued,requeued,submitted,backlogged, 146562306a36Sopenharmony_ci rx_enqueued,rx_dequeued,putting,pushed); 146662306a36Sopenharmony_ciif (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost); 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci#endif 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic void bug_int(struct atm_dev *dev,unsigned long reason) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci DPRINTK(">bug_int\n"); 147562306a36Sopenharmony_ci if (reason & MID_DMA_ERR_ACK) 147662306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " 147762306a36Sopenharmony_ci "error\n",dev->number); 147862306a36Sopenharmony_ci if (reason & MID_TX_IDENT_MISM) 147962306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident " 148062306a36Sopenharmony_ci "mismatch\n",dev->number); 148162306a36Sopenharmony_ci if (reason & MID_TX_DMA_OVFL) 148262306a36Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " 148362306a36Sopenharmony_ci "overflow\n",dev->number); 148462306a36Sopenharmony_ci EVENT("---dump ends here---\n",0,0); 148562306a36Sopenharmony_ci printk(KERN_NOTICE "---recent events---\n"); 148662306a36Sopenharmony_ci event_dump(); 148762306a36Sopenharmony_ci} 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic irqreturn_t eni_int(int irq,void *dev_id) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci struct atm_dev *dev; 149362306a36Sopenharmony_ci struct eni_dev *eni_dev; 149462306a36Sopenharmony_ci u32 reason; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci DPRINTK(">eni_int\n"); 149762306a36Sopenharmony_ci dev = dev_id; 149862306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 149962306a36Sopenharmony_ci reason = eni_in(MID_ISA); 150062306a36Sopenharmony_ci DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason); 150162306a36Sopenharmony_ci /* 150262306a36Sopenharmony_ci * Must handle these two right now, because reading ISA doesn't clear 150362306a36Sopenharmony_ci * them, so they re-occur and we never make it to the tasklet. Since 150462306a36Sopenharmony_ci * they're rare, we don't mind the occasional invocation of eni_tasklet 150562306a36Sopenharmony_ci * with eni_dev->events == 0. 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_ci if (reason & MID_STAT_OVFL) { 150862306a36Sopenharmony_ci EVENT("stat overflow\n",0,0); 150962306a36Sopenharmony_ci eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci if (reason & MID_SUNI_INT) { 151262306a36Sopenharmony_ci EVENT("SUNI int\n",0,0); 151362306a36Sopenharmony_ci dev->phy->interrupt(dev); 151462306a36Sopenharmony_ci#if 0 151562306a36Sopenharmony_ci foo(); 151662306a36Sopenharmony_ci#endif 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci spin_lock(&eni_dev->lock); 151962306a36Sopenharmony_ci eni_dev->events |= reason; 152062306a36Sopenharmony_ci spin_unlock(&eni_dev->lock); 152162306a36Sopenharmony_ci tasklet_schedule(&eni_dev->task); 152262306a36Sopenharmony_ci return IRQ_HANDLED; 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic void eni_tasklet(unsigned long data) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci struct atm_dev *dev = (struct atm_dev *) data; 152962306a36Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 153062306a36Sopenharmony_ci unsigned long flags; 153162306a36Sopenharmony_ci u32 events; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci DPRINTK("eni_tasklet (dev %p)\n",dev); 153462306a36Sopenharmony_ci spin_lock_irqsave(&eni_dev->lock,flags); 153562306a36Sopenharmony_ci events = xchg(&eni_dev->events,0); 153662306a36Sopenharmony_ci spin_unlock_irqrestore(&eni_dev->lock,flags); 153762306a36Sopenharmony_ci if (events & MID_RX_DMA_COMPLETE) { 153862306a36Sopenharmony_ci EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0); 153962306a36Sopenharmony_ci dequeue_rx(dev); 154062306a36Sopenharmony_ci EVENT("dequeue_rx done, starting poll_rx\n",0,0); 154162306a36Sopenharmony_ci poll_rx(dev); 154262306a36Sopenharmony_ci EVENT("poll_rx done\n",0,0); 154362306a36Sopenharmony_ci /* poll_tx ? */ 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci if (events & MID_SERVICE) { 154662306a36Sopenharmony_ci EVENT("INT: service, starting get_service\n",0,0); 154762306a36Sopenharmony_ci get_service(dev); 154862306a36Sopenharmony_ci EVENT("get_service done, starting poll_rx\n",0,0); 154962306a36Sopenharmony_ci poll_rx(dev); 155062306a36Sopenharmony_ci EVENT("poll_rx done\n",0,0); 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci if (events & MID_TX_DMA_COMPLETE) { 155362306a36Sopenharmony_ci EVENT("INT: TX DMA COMPLETE\n",0,0); 155462306a36Sopenharmony_ci dequeue_tx(dev); 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci if (events & MID_TX_COMPLETE) { 155762306a36Sopenharmony_ci EVENT("INT: TX COMPLETE\n",0,0); 155862306a36Sopenharmony_ci tx_complete++; 155962306a36Sopenharmony_ci wake_up(&eni_dev->tx_wait); 156062306a36Sopenharmony_ci /* poll_rx ? */ 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { 156362306a36Sopenharmony_ci EVENT("bug interrupt\n",0,0); 156462306a36Sopenharmony_ci bug_int(dev,events); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci poll_tx(dev); 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci/*--------------------------------- entries ---------------------------------*/ 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic char * const media_name[] = { 157462306a36Sopenharmony_ci "MMF", "SMF", "MMF", "03?", /* 0- 3 */ 157562306a36Sopenharmony_ci "UTP", "05?", "06?", "07?", /* 4- 7 */ 157662306a36Sopenharmony_ci "TAXI","09?", "10?", "11?", /* 8-11 */ 157762306a36Sopenharmony_ci "12?", "13?", "14?", "15?", /* 12-15 */ 157862306a36Sopenharmony_ci "MMF", "SMF", "18?", "19?", /* 16-19 */ 157962306a36Sopenharmony_ci "UTP", "21?", "22?", "23?", /* 20-23 */ 158062306a36Sopenharmony_ci "24?", "25?", "26?", "27?", /* 24-27 */ 158162306a36Sopenharmony_ci "28?", "29?", "30?", "31?" /* 28-31 */ 158262306a36Sopenharmony_ci}; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci#define SET_SEPROM \ 158662306a36Sopenharmony_ci ({ if (!error && !pci_error) { \ 158762306a36Sopenharmony_ci pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ 158862306a36Sopenharmony_ci udelay(10); /* 10 usecs */ \ 158962306a36Sopenharmony_ci } }) 159062306a36Sopenharmony_ci#define GET_SEPROM \ 159162306a36Sopenharmony_ci ({ if (!error && !pci_error) { \ 159262306a36Sopenharmony_ci pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ 159362306a36Sopenharmony_ci udelay(10); /* 10 usecs */ \ 159462306a36Sopenharmony_ci } }) 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic int get_esi_asic(struct atm_dev *dev) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct eni_dev *eni_dev; 160062306a36Sopenharmony_ci unsigned char tonga; 160162306a36Sopenharmony_ci int error,failed,pci_error; 160262306a36Sopenharmony_ci int address,i,j; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 160562306a36Sopenharmony_ci error = pci_error = 0; 160662306a36Sopenharmony_ci tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK; 160762306a36Sopenharmony_ci SET_SEPROM; 160862306a36Sopenharmony_ci for (i = 0; i < ESI_LEN && !error && !pci_error; i++) { 160962306a36Sopenharmony_ci /* start operation */ 161062306a36Sopenharmony_ci tonga |= SEPROM_DATA; 161162306a36Sopenharmony_ci SET_SEPROM; 161262306a36Sopenharmony_ci tonga |= SEPROM_CLK; 161362306a36Sopenharmony_ci SET_SEPROM; 161462306a36Sopenharmony_ci tonga &= ~SEPROM_DATA; 161562306a36Sopenharmony_ci SET_SEPROM; 161662306a36Sopenharmony_ci tonga &= ~SEPROM_CLK; 161762306a36Sopenharmony_ci SET_SEPROM; 161862306a36Sopenharmony_ci /* send address */ 161962306a36Sopenharmony_ci address = ((i+SEPROM_ESI_BASE) << 1)+1; 162062306a36Sopenharmony_ci for (j = 7; j >= 0; j--) { 162162306a36Sopenharmony_ci tonga = (address >> j) & 1 ? tonga | SEPROM_DATA : 162262306a36Sopenharmony_ci tonga & ~SEPROM_DATA; 162362306a36Sopenharmony_ci SET_SEPROM; 162462306a36Sopenharmony_ci tonga |= SEPROM_CLK; 162562306a36Sopenharmony_ci SET_SEPROM; 162662306a36Sopenharmony_ci tonga &= ~SEPROM_CLK; 162762306a36Sopenharmony_ci SET_SEPROM; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci /* get ack */ 163062306a36Sopenharmony_ci tonga |= SEPROM_DATA; 163162306a36Sopenharmony_ci SET_SEPROM; 163262306a36Sopenharmony_ci tonga |= SEPROM_CLK; 163362306a36Sopenharmony_ci SET_SEPROM; 163462306a36Sopenharmony_ci GET_SEPROM; 163562306a36Sopenharmony_ci failed = tonga & SEPROM_DATA; 163662306a36Sopenharmony_ci tonga &= ~SEPROM_CLK; 163762306a36Sopenharmony_ci SET_SEPROM; 163862306a36Sopenharmony_ci tonga |= SEPROM_DATA; 163962306a36Sopenharmony_ci SET_SEPROM; 164062306a36Sopenharmony_ci if (failed) error = -EIO; 164162306a36Sopenharmony_ci else { 164262306a36Sopenharmony_ci dev->esi[i] = 0; 164362306a36Sopenharmony_ci for (j = 7; j >= 0; j--) { 164462306a36Sopenharmony_ci dev->esi[i] <<= 1; 164562306a36Sopenharmony_ci tonga |= SEPROM_DATA; 164662306a36Sopenharmony_ci SET_SEPROM; 164762306a36Sopenharmony_ci tonga |= SEPROM_CLK; 164862306a36Sopenharmony_ci SET_SEPROM; 164962306a36Sopenharmony_ci GET_SEPROM; 165062306a36Sopenharmony_ci if (tonga & SEPROM_DATA) dev->esi[i] |= 1; 165162306a36Sopenharmony_ci tonga &= ~SEPROM_CLK; 165262306a36Sopenharmony_ci SET_SEPROM; 165362306a36Sopenharmony_ci tonga |= SEPROM_DATA; 165462306a36Sopenharmony_ci SET_SEPROM; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci /* get ack */ 165762306a36Sopenharmony_ci tonga |= SEPROM_DATA; 165862306a36Sopenharmony_ci SET_SEPROM; 165962306a36Sopenharmony_ci tonga |= SEPROM_CLK; 166062306a36Sopenharmony_ci SET_SEPROM; 166162306a36Sopenharmony_ci GET_SEPROM; 166262306a36Sopenharmony_ci if (!(tonga & SEPROM_DATA)) error = -EIO; 166362306a36Sopenharmony_ci tonga &= ~SEPROM_CLK; 166462306a36Sopenharmony_ci SET_SEPROM; 166562306a36Sopenharmony_ci tonga |= SEPROM_DATA; 166662306a36Sopenharmony_ci SET_SEPROM; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci /* stop operation */ 166962306a36Sopenharmony_ci tonga &= ~SEPROM_DATA; 167062306a36Sopenharmony_ci SET_SEPROM; 167162306a36Sopenharmony_ci tonga |= SEPROM_CLK; 167262306a36Sopenharmony_ci SET_SEPROM; 167362306a36Sopenharmony_ci tonga |= SEPROM_DATA; 167462306a36Sopenharmony_ci SET_SEPROM; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci if (pci_error) { 167762306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI " 167862306a36Sopenharmony_ci "(0x%02x)\n",dev->number,pci_error); 167962306a36Sopenharmony_ci error = -EIO; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci return error; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci#undef SET_SEPROM 168662306a36Sopenharmony_ci#undef GET_SEPROM 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic int get_esi_fpga(struct atm_dev *dev, void __iomem *base) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci void __iomem *mac_base; 169262306a36Sopenharmony_ci int i; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom); 169562306a36Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3)); 169662306a36Sopenharmony_ci return 0; 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic int eni_do_init(struct atm_dev *dev) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct midway_eprom __iomem *eprom; 170362306a36Sopenharmony_ci struct eni_dev *eni_dev; 170462306a36Sopenharmony_ci struct pci_dev *pci_dev; 170562306a36Sopenharmony_ci unsigned long real_base; 170662306a36Sopenharmony_ci void __iomem *base; 170762306a36Sopenharmony_ci int error,i,last; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci DPRINTK(">eni_init\n"); 171062306a36Sopenharmony_ci dev->ci_range.vpi_bits = 0; 171162306a36Sopenharmony_ci dev->ci_range.vci_bits = NR_VCI_LD; 171262306a36Sopenharmony_ci dev->link_rate = ATM_OC3_PCR; 171362306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 171462306a36Sopenharmony_ci pci_dev = eni_dev->pci_dev; 171562306a36Sopenharmony_ci real_base = pci_resource_start(pci_dev, 0); 171662306a36Sopenharmony_ci eni_dev->irq = pci_dev->irq; 171762306a36Sopenharmony_ci if ((error = pci_write_config_word(pci_dev,PCI_COMMAND, 171862306a36Sopenharmony_ci PCI_COMMAND_MEMORY | 171962306a36Sopenharmony_ci (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { 172062306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory " 172162306a36Sopenharmony_ci "(0x%02x)\n",dev->number,error); 172262306a36Sopenharmony_ci return -EIO; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,", 172562306a36Sopenharmony_ci dev->number,pci_dev->revision,real_base,eni_dev->irq); 172662306a36Sopenharmony_ci if (!(base = ioremap(real_base,MAP_MAX_SIZE))) { 172762306a36Sopenharmony_ci printk("\n"); 172862306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page " 172962306a36Sopenharmony_ci "mapping\n",dev->number); 173062306a36Sopenharmony_ci return -ENOMEM; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci eni_dev->ioaddr = base; 173362306a36Sopenharmony_ci eni_dev->base_diff = real_base - (unsigned long) base; 173462306a36Sopenharmony_ci /* id may not be present in ASIC Tonga boards - check this @@@ */ 173562306a36Sopenharmony_ci if (!eni_dev->asic) { 173662306a36Sopenharmony_ci eprom = (base+EPROM_SIZE-sizeof(struct midway_eprom)); 173762306a36Sopenharmony_ci if (readl(&eprom->magic) != ENI155_MAGIC) { 173862306a36Sopenharmony_ci printk("\n"); 173962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL 174062306a36Sopenharmony_ci "(itf %d): bad magic - expected 0x%x, got 0x%x\n", 174162306a36Sopenharmony_ci dev->number, ENI155_MAGIC, 174262306a36Sopenharmony_ci (unsigned)readl(&eprom->magic)); 174362306a36Sopenharmony_ci error = -EINVAL; 174462306a36Sopenharmony_ci goto unmap; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci eni_dev->phy = base+PHY_BASE; 174862306a36Sopenharmony_ci eni_dev->reg = base+REG_BASE; 174962306a36Sopenharmony_ci eni_dev->ram = base+RAM_BASE; 175062306a36Sopenharmony_ci last = MAP_MAX_SIZE-RAM_BASE; 175162306a36Sopenharmony_ci for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) { 175262306a36Sopenharmony_ci writel(0x55555555,eni_dev->ram+i); 175362306a36Sopenharmony_ci if (readl(eni_dev->ram+i) != 0x55555555) last = i; 175462306a36Sopenharmony_ci else { 175562306a36Sopenharmony_ci writel(0xAAAAAAAA,eni_dev->ram+i); 175662306a36Sopenharmony_ci if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; 175762306a36Sopenharmony_ci else writel(i,eni_dev->ram+i); 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci for (i = 0; i < last; i += RAM_INCREMENT) 176162306a36Sopenharmony_ci if (readl(eni_dev->ram+i) != i) break; 176262306a36Sopenharmony_ci eni_dev->mem = i; 176362306a36Sopenharmony_ci memset_io(eni_dev->ram,0,eni_dev->mem); 176462306a36Sopenharmony_ci /* TODO: should shrink allocation now */ 176562306a36Sopenharmony_ci printk("mem=%dkB (",eni_dev->mem >> 10); 176662306a36Sopenharmony_ci /* TODO: check for non-SUNI, check for TAXI ? */ 176762306a36Sopenharmony_ci if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { 176862306a36Sopenharmony_ci printk(")\n"); 176962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n", 177062306a36Sopenharmony_ci dev->number,(unsigned) eni_in(MID_RES_ID_MCON)); 177162306a36Sopenharmony_ci error = -EINVAL; 177262306a36Sopenharmony_ci goto unmap; 177362306a36Sopenharmony_ci } 177462306a36Sopenharmony_ci error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base); 177562306a36Sopenharmony_ci if (error) 177662306a36Sopenharmony_ci goto unmap; 177762306a36Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 177862306a36Sopenharmony_ci printk("%s%02X",i ? "-" : "",dev->esi[i]); 177962306a36Sopenharmony_ci printk(")\n"); 178062306a36Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number, 178162306a36Sopenharmony_ci eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", 178262306a36Sopenharmony_ci media_name[eni_in(MID_RES_ID_MCON) & DAUGHTER_ID]); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci error = suni_init(dev); 178562306a36Sopenharmony_ci if (error) 178662306a36Sopenharmony_ci goto unmap; 178762306a36Sopenharmony_ciout: 178862306a36Sopenharmony_ci return error; 178962306a36Sopenharmony_ciunmap: 179062306a36Sopenharmony_ci iounmap(base); 179162306a36Sopenharmony_ci goto out; 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_cistatic void eni_do_release(struct atm_dev *dev) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci struct eni_dev *ed = ENI_DEV(dev); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci dev->phy->stop(dev); 179962306a36Sopenharmony_ci dev->phy = NULL; 180062306a36Sopenharmony_ci iounmap(ed->ioaddr); 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic int eni_start(struct atm_dev *dev) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct eni_dev *eni_dev; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci void __iomem *buf; 180862306a36Sopenharmony_ci unsigned long buffer_mem; 180962306a36Sopenharmony_ci int error; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci DPRINTK(">eni_start\n"); 181262306a36Sopenharmony_ci eni_dev = ENI_DEV(dev); 181362306a36Sopenharmony_ci if (request_irq(eni_dev->irq,&eni_int,IRQF_SHARED,DEV_LABEL,dev)) { 181462306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n", 181562306a36Sopenharmony_ci dev->number,eni_dev->irq); 181662306a36Sopenharmony_ci error = -EAGAIN; 181762306a36Sopenharmony_ci goto out; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci pci_set_master(eni_dev->pci_dev); 182062306a36Sopenharmony_ci if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND, 182162306a36Sopenharmony_ci PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | 182262306a36Sopenharmony_ci (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { 182362306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+" 182462306a36Sopenharmony_ci "master (0x%02x)\n",dev->number,error); 182562306a36Sopenharmony_ci goto free_irq; 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, 182862306a36Sopenharmony_ci END_SWAP_DMA))) { 182962306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " 183062306a36Sopenharmony_ci "(0x%02x)\n",dev->number,error); 183162306a36Sopenharmony_ci goto free_irq; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci /* determine addresses of internal tables */ 183462306a36Sopenharmony_ci eni_dev->vci = eni_dev->ram; 183562306a36Sopenharmony_ci eni_dev->rx_dma = eni_dev->ram+NR_VCI*16; 183662306a36Sopenharmony_ci eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8; 183762306a36Sopenharmony_ci eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8; 183862306a36Sopenharmony_ci buf = eni_dev->service+NR_SERVICE*4; 183962306a36Sopenharmony_ci DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n", 184062306a36Sopenharmony_ci eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma, 184162306a36Sopenharmony_ci eni_dev->service,buf); 184262306a36Sopenharmony_ci spin_lock_init(&eni_dev->lock); 184362306a36Sopenharmony_ci tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev); 184462306a36Sopenharmony_ci eni_dev->events = 0; 184562306a36Sopenharmony_ci /* initialize memory management */ 184662306a36Sopenharmony_ci buffer_mem = eni_dev->mem - (buf - eni_dev->ram); 184762306a36Sopenharmony_ci eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2; 184862306a36Sopenharmony_ci eni_dev->free_list = kmalloc_array(eni_dev->free_list_size + 1, 184962306a36Sopenharmony_ci sizeof(*eni_dev->free_list), 185062306a36Sopenharmony_ci GFP_KERNEL); 185162306a36Sopenharmony_ci if (!eni_dev->free_list) { 185262306a36Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", 185362306a36Sopenharmony_ci dev->number); 185462306a36Sopenharmony_ci error = -ENOMEM; 185562306a36Sopenharmony_ci goto free_irq; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci eni_dev->free_len = 0; 185862306a36Sopenharmony_ci eni_put_free(eni_dev,buf,buffer_mem); 185962306a36Sopenharmony_ci memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */ 186062306a36Sopenharmony_ci /* 186162306a36Sopenharmony_ci * byte_addr free (k) 186262306a36Sopenharmony_ci * 0x00000000 512 VCI table 186362306a36Sopenharmony_ci * 0x00004000 496 RX DMA 186462306a36Sopenharmony_ci * 0x00005000 492 TX DMA 186562306a36Sopenharmony_ci * 0x00006000 488 service list 186662306a36Sopenharmony_ci * 0x00007000 484 buffers 186762306a36Sopenharmony_ci * 0x00080000 0 end (512kB) 186862306a36Sopenharmony_ci */ 186962306a36Sopenharmony_ci eni_out(0xffffffff,MID_IE); 187062306a36Sopenharmony_ci error = start_tx(dev); 187162306a36Sopenharmony_ci if (error) goto free_list; 187262306a36Sopenharmony_ci error = start_rx(dev); 187362306a36Sopenharmony_ci if (error) goto free_list; 187462306a36Sopenharmony_ci error = dev->phy->start(dev); 187562306a36Sopenharmony_ci if (error) goto free_list; 187662306a36Sopenharmony_ci eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | 187762306a36Sopenharmony_ci MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, 187862306a36Sopenharmony_ci MID_MC_S); 187962306a36Sopenharmony_ci /* Tonga uses SBus INTReq1 */ 188062306a36Sopenharmony_ci (void) eni_in(MID_ISA); /* clear Midway interrupts */ 188162306a36Sopenharmony_ci return 0; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_cifree_list: 188462306a36Sopenharmony_ci kfree(eni_dev->free_list); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_cifree_irq: 188762306a36Sopenharmony_ci free_irq(eni_dev->irq, dev); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ciout: 189062306a36Sopenharmony_ci return error; 189162306a36Sopenharmony_ci} 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cistatic void eni_close(struct atm_vcc *vcc) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci DPRINTK(">eni_close\n"); 189762306a36Sopenharmony_ci if (!ENI_VCC(vcc)) return; 189862306a36Sopenharmony_ci clear_bit(ATM_VF_READY,&vcc->flags); 189962306a36Sopenharmony_ci close_rx(vcc); 190062306a36Sopenharmony_ci close_tx(vcc); 190162306a36Sopenharmony_ci DPRINTK("eni_close: done waiting\n"); 190262306a36Sopenharmony_ci /* deallocate memory */ 190362306a36Sopenharmony_ci kfree(ENI_VCC(vcc)); 190462306a36Sopenharmony_ci vcc->dev_data = NULL; 190562306a36Sopenharmony_ci clear_bit(ATM_VF_ADDR,&vcc->flags); 190662306a36Sopenharmony_ci /*foo();*/ 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_cistatic int eni_open(struct atm_vcc *vcc) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 191362306a36Sopenharmony_ci int error; 191462306a36Sopenharmony_ci short vpi = vcc->vpi; 191562306a36Sopenharmony_ci int vci = vcc->vci; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci DPRINTK(">eni_open\n"); 191862306a36Sopenharmony_ci EVENT("eni_open\n",0,0); 191962306a36Sopenharmony_ci if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) 192062306a36Sopenharmony_ci vcc->dev_data = NULL; 192162306a36Sopenharmony_ci if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) 192262306a36Sopenharmony_ci set_bit(ATM_VF_ADDR,&vcc->flags); 192362306a36Sopenharmony_ci if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5) 192462306a36Sopenharmony_ci return -EINVAL; 192562306a36Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi, 192662306a36Sopenharmony_ci vcc->vci); 192762306a36Sopenharmony_ci if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) { 192862306a36Sopenharmony_ci eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL); 192962306a36Sopenharmony_ci if (!eni_vcc) return -ENOMEM; 193062306a36Sopenharmony_ci vcc->dev_data = eni_vcc; 193162306a36Sopenharmony_ci eni_vcc->tx = NULL; /* for eni_close after open_rx */ 193262306a36Sopenharmony_ci if ((error = open_rx_first(vcc))) { 193362306a36Sopenharmony_ci eni_close(vcc); 193462306a36Sopenharmony_ci return error; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci if ((error = open_tx_first(vcc))) { 193762306a36Sopenharmony_ci eni_close(vcc); 193862306a36Sopenharmony_ci return error; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0; 194262306a36Sopenharmony_ci if ((error = open_rx_second(vcc))) { 194362306a36Sopenharmony_ci eni_close(vcc); 194462306a36Sopenharmony_ci return error; 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci if ((error = open_tx_second(vcc))) { 194762306a36Sopenharmony_ci eni_close(vcc); 194862306a36Sopenharmony_ci return error; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci set_bit(ATM_VF_READY,&vcc->flags); 195162306a36Sopenharmony_ci /* should power down SUNI while !ref_count @@@ */ 195262306a36Sopenharmony_ci return 0; 195362306a36Sopenharmony_ci} 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_cistatic int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) 195762306a36Sopenharmony_ci{ 195862306a36Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(vcc->dev); 195962306a36Sopenharmony_ci struct eni_tx *tx = ENI_VCC(vcc)->tx; 196062306a36Sopenharmony_ci struct sk_buff *skb; 196162306a36Sopenharmony_ci int error,rate,rsv,shp; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci if (qos->txtp.traffic_class == ATM_NONE) return 0; 196462306a36Sopenharmony_ci if (tx == eni_dev->ubr) return -EBADFD; 196562306a36Sopenharmony_ci rate = atm_pcr_goal(&qos->txtp); 196662306a36Sopenharmony_ci if (rate < 0) rate = -rate; 196762306a36Sopenharmony_ci rsv = shp = 0; 196862306a36Sopenharmony_ci if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1; 196962306a36Sopenharmony_ci if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1; 197062306a36Sopenharmony_ci if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1; 197162306a36Sopenharmony_ci if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1; 197262306a36Sopenharmony_ci if (!rsv && !shp) return 0; 197362306a36Sopenharmony_ci error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp); 197462306a36Sopenharmony_ci if (error) return error; 197562306a36Sopenharmony_ci if (shp && !(flgs & ATM_MF_IMMED)) return 0; 197662306a36Sopenharmony_ci /* 197762306a36Sopenharmony_ci * Walk through the send buffer and patch the rate information in all 197862306a36Sopenharmony_ci * segmentation buffer descriptors of this VCC. 197962306a36Sopenharmony_ci */ 198062306a36Sopenharmony_ci tasklet_disable(&eni_dev->task); 198162306a36Sopenharmony_ci skb_queue_walk(&eni_dev->tx_queue, skb) { 198262306a36Sopenharmony_ci void __iomem *dsc; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci if (ATM_SKB(skb)->vcc != vcc) continue; 198562306a36Sopenharmony_ci dsc = tx->send+ENI_PRV_POS(skb)*4; 198662306a36Sopenharmony_ci writel((readl(dsc) & ~(MID_SEG_RATE | MID_SEG_PR)) | 198762306a36Sopenharmony_ci (tx->prescaler << MID_SEG_PR_SHIFT) | 198862306a36Sopenharmony_ci (tx->resolution << MID_SEG_RATE_SHIFT), dsc); 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci tasklet_enable(&eni_dev->task); 199162306a36Sopenharmony_ci return 0; 199262306a36Sopenharmony_ci} 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (cmd == ENI_MEMDUMP) { 200062306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 200162306a36Sopenharmony_ci printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " 200262306a36Sopenharmony_ci "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); 200362306a36Sopenharmony_ci dump(dev); 200462306a36Sopenharmony_ci return 0; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci if (cmd == ENI_SETMULT) { 200762306a36Sopenharmony_ci struct eni_multipliers mult; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 201062306a36Sopenharmony_ci if (copy_from_user(&mult, arg, 201162306a36Sopenharmony_ci sizeof(struct eni_multipliers))) 201262306a36Sopenharmony_ci return -EFAULT; 201362306a36Sopenharmony_ci if ((mult.tx && mult.tx <= 100) || (mult.rx &&mult.rx <= 100) || 201462306a36Sopenharmony_ci mult.tx > 65536 || mult.rx > 65536) 201562306a36Sopenharmony_ci return -EINVAL; 201662306a36Sopenharmony_ci if (mult.tx) eni_dev->tx_mult = mult.tx; 201762306a36Sopenharmony_ci if (mult.rx) eni_dev->rx_mult = mult.rx; 201862306a36Sopenharmony_ci return 0; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci if (cmd == ATM_SETCIRANGE) { 202162306a36Sopenharmony_ci struct atm_cirange ci; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci if (copy_from_user(&ci, arg,sizeof(struct atm_cirange))) 202462306a36Sopenharmony_ci return -EFAULT; 202562306a36Sopenharmony_ci if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) && 202662306a36Sopenharmony_ci (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX)) 202762306a36Sopenharmony_ci return 0; 202862306a36Sopenharmony_ci return -EINVAL; 202962306a36Sopenharmony_ci } 203062306a36Sopenharmony_ci if (!dev->phy->ioctl) return -ENOIOCTLCMD; 203162306a36Sopenharmony_ci return dev->phy->ioctl(dev,cmd,arg); 203262306a36Sopenharmony_ci} 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_cistatic int eni_send(struct atm_vcc *vcc,struct sk_buff *skb) 203562306a36Sopenharmony_ci{ 203662306a36Sopenharmony_ci enum enq_res res; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci DPRINTK(">eni_send\n"); 203962306a36Sopenharmony_ci if (!ENI_VCC(vcc)->tx) { 204062306a36Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 204162306a36Sopenharmony_ci else dev_kfree_skb(skb); 204262306a36Sopenharmony_ci return -EINVAL; 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci if (!skb) { 204562306a36Sopenharmony_ci printk(KERN_CRIT "!skb in eni_send ?\n"); 204662306a36Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 204762306a36Sopenharmony_ci return -EINVAL; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci if (vcc->qos.aal == ATM_AAL0) { 205062306a36Sopenharmony_ci if (skb->len != ATM_CELL_SIZE-1) { 205162306a36Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 205262306a36Sopenharmony_ci else dev_kfree_skb(skb); 205362306a36Sopenharmony_ci return -EINVAL; 205462306a36Sopenharmony_ci } 205562306a36Sopenharmony_ci *(u32 *) skb->data = htonl(*(u32 *) skb->data); 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci submitted++; 205862306a36Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 205962306a36Sopenharmony_ci tasklet_disable_in_atomic(&ENI_DEV(vcc->dev)->task); 206062306a36Sopenharmony_ci res = do_tx(skb); 206162306a36Sopenharmony_ci tasklet_enable(&ENI_DEV(vcc->dev)->task); 206262306a36Sopenharmony_ci if (res == enq_ok) return 0; 206362306a36Sopenharmony_ci skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb); 206462306a36Sopenharmony_ci backlogged++; 206562306a36Sopenharmony_ci tasklet_schedule(&ENI_DEV(vcc->dev)->task); 206662306a36Sopenharmony_ci return 0; 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic void eni_phy_put(struct atm_dev *dev,unsigned char value, 207062306a36Sopenharmony_ci unsigned long addr) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci writel(value,ENI_DEV(dev)->phy+addr*4); 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_cistatic unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci return readl(ENI_DEV(dev)->phy+addr*4); 208062306a36Sopenharmony_ci} 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_cistatic int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) 208462306a36Sopenharmony_ci{ 208562306a36Sopenharmony_ci struct sock *s; 208662306a36Sopenharmony_ci static const char *signal[] = { "LOST","unknown","okay" }; 208762306a36Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 208862306a36Sopenharmony_ci struct atm_vcc *vcc; 208962306a36Sopenharmony_ci int left,i; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci left = *pos; 209262306a36Sopenharmony_ci if (!left) 209362306a36Sopenharmony_ci return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " 209462306a36Sopenharmony_ci "%d cps remaining\n",dev->number,signal[(int) dev->signal], 209562306a36Sopenharmony_ci eni_dev->mem >> 10,eni_dev->tx_bw); 209662306a36Sopenharmony_ci if (!--left) 209762306a36Sopenharmony_ci return sprintf(page,"%4sBursts: TX" 209862306a36Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \ 209962306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \ 210062306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \ 210162306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_2W) 210262306a36Sopenharmony_ci " none" 210362306a36Sopenharmony_ci#endif 210462306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W 210562306a36Sopenharmony_ci " 16W" 210662306a36Sopenharmony_ci#endif 210762306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W 210862306a36Sopenharmony_ci " 8W" 210962306a36Sopenharmony_ci#endif 211062306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W 211162306a36Sopenharmony_ci " 4W" 211262306a36Sopenharmony_ci#endif 211362306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W 211462306a36Sopenharmony_ci " 2W" 211562306a36Sopenharmony_ci#endif 211662306a36Sopenharmony_ci ", RX" 211762306a36Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \ 211862306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \ 211962306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \ 212062306a36Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_2W) 212162306a36Sopenharmony_ci " none" 212262306a36Sopenharmony_ci#endif 212362306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W 212462306a36Sopenharmony_ci " 16W" 212562306a36Sopenharmony_ci#endif 212662306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W 212762306a36Sopenharmony_ci " 8W" 212862306a36Sopenharmony_ci#endif 212962306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W 213062306a36Sopenharmony_ci " 4W" 213162306a36Sopenharmony_ci#endif 213262306a36Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W 213362306a36Sopenharmony_ci " 2W" 213462306a36Sopenharmony_ci#endif 213562306a36Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST 213662306a36Sopenharmony_ci " (default)" 213762306a36Sopenharmony_ci#endif 213862306a36Sopenharmony_ci "\n",""); 213962306a36Sopenharmony_ci if (!--left) 214062306a36Sopenharmony_ci return sprintf(page,"%4sBuffer multipliers: tx %d%%, rx %d%%\n", 214162306a36Sopenharmony_ci "",eni_dev->tx_mult,eni_dev->rx_mult); 214262306a36Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) { 214362306a36Sopenharmony_ci struct eni_tx *tx = eni_dev->tx+i; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci if (!tx->send) continue; 214662306a36Sopenharmony_ci if (!--left) { 214762306a36Sopenharmony_ci return sprintf(page, "tx[%d]: 0x%lx-0x%lx " 214862306a36Sopenharmony_ci "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i, 214962306a36Sopenharmony_ci (unsigned long) (tx->send - eni_dev->ram), 215062306a36Sopenharmony_ci tx->send-eni_dev->ram+tx->words*4-1,tx->words*4, 215162306a36Sopenharmony_ci tx->reserved,tx->shaping, 215262306a36Sopenharmony_ci tx == eni_dev->ubr ? " (UBR)" : ""); 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci if (--left) continue; 215562306a36Sopenharmony_ci return sprintf(page,"%10sbacklog %u packets\n","", 215662306a36Sopenharmony_ci skb_queue_len(&tx->backlog)); 215762306a36Sopenharmony_ci } 215862306a36Sopenharmony_ci read_lock(&vcc_sklist_lock); 215962306a36Sopenharmony_ci for(i = 0; i < VCC_HTABLE_SIZE; ++i) { 216062306a36Sopenharmony_ci struct hlist_head *head = &vcc_hash[i]; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci sk_for_each(s, head) { 216362306a36Sopenharmony_ci struct eni_vcc *eni_vcc; 216462306a36Sopenharmony_ci int length; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci vcc = atm_sk(s); 216762306a36Sopenharmony_ci if (vcc->dev != dev) 216862306a36Sopenharmony_ci continue; 216962306a36Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 217062306a36Sopenharmony_ci if (--left) continue; 217162306a36Sopenharmony_ci length = sprintf(page,"vcc %4d: ",vcc->vci); 217262306a36Sopenharmony_ci if (eni_vcc->rx) { 217362306a36Sopenharmony_ci length += sprintf(page+length, "0x%lx-0x%lx " 217462306a36Sopenharmony_ci "(%6ld bytes)", 217562306a36Sopenharmony_ci (unsigned long) (eni_vcc->recv - eni_dev->ram), 217662306a36Sopenharmony_ci eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1, 217762306a36Sopenharmony_ci eni_vcc->words*4); 217862306a36Sopenharmony_ci if (eni_vcc->tx) length += sprintf(page+length,", "); 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci if (eni_vcc->tx) 218162306a36Sopenharmony_ci length += sprintf(page+length,"tx[%d], txing %d bytes", 218262306a36Sopenharmony_ci eni_vcc->tx->index,eni_vcc->txing); 218362306a36Sopenharmony_ci page[length] = '\n'; 218462306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 218562306a36Sopenharmony_ci return length+1; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci read_unlock(&vcc_sklist_lock); 218962306a36Sopenharmony_ci for (i = 0; i < eni_dev->free_len; i++) { 219062306a36Sopenharmony_ci struct eni_free *fe = eni_dev->free_list+i; 219162306a36Sopenharmony_ci unsigned long offset; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (--left) continue; 219462306a36Sopenharmony_ci offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; 219562306a36Sopenharmony_ci return sprintf(page,"free %p-%p (%6d bytes)\n", 219662306a36Sopenharmony_ci fe->start-offset,fe->start-offset+(1 << fe->order)-1, 219762306a36Sopenharmony_ci 1 << fe->order); 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci return 0; 220062306a36Sopenharmony_ci} 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_cistatic const struct atmdev_ops ops = { 220462306a36Sopenharmony_ci .open = eni_open, 220562306a36Sopenharmony_ci .close = eni_close, 220662306a36Sopenharmony_ci .ioctl = eni_ioctl, 220762306a36Sopenharmony_ci .send = eni_send, 220862306a36Sopenharmony_ci .phy_put = eni_phy_put, 220962306a36Sopenharmony_ci .phy_get = eni_phy_get, 221062306a36Sopenharmony_ci .change_qos = eni_change_qos, 221162306a36Sopenharmony_ci .proc_read = eni_proc_read 221262306a36Sopenharmony_ci}; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_cistatic int eni_init_one(struct pci_dev *pci_dev, 221662306a36Sopenharmony_ci const struct pci_device_id *ent) 221762306a36Sopenharmony_ci{ 221862306a36Sopenharmony_ci struct atm_dev *dev; 221962306a36Sopenharmony_ci struct eni_dev *eni_dev; 222062306a36Sopenharmony_ci struct eni_zero *zero; 222162306a36Sopenharmony_ci int rc; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci rc = pci_enable_device(pci_dev); 222462306a36Sopenharmony_ci if (rc < 0) 222562306a36Sopenharmony_ci goto out; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); 222862306a36Sopenharmony_ci if (rc < 0) 222962306a36Sopenharmony_ci goto err_disable; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci rc = -ENOMEM; 223262306a36Sopenharmony_ci eni_dev = kmalloc(sizeof(struct eni_dev), GFP_KERNEL); 223362306a36Sopenharmony_ci if (!eni_dev) 223462306a36Sopenharmony_ci goto err_disable; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci zero = &eni_dev->zero; 223762306a36Sopenharmony_ci zero->addr = dma_alloc_coherent(&pci_dev->dev, 223862306a36Sopenharmony_ci ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL); 223962306a36Sopenharmony_ci if (!zero->addr) 224062306a36Sopenharmony_ci goto err_kfree; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL); 224362306a36Sopenharmony_ci if (!dev) 224462306a36Sopenharmony_ci goto err_free_consistent; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci dev->dev_data = eni_dev; 224762306a36Sopenharmony_ci pci_set_drvdata(pci_dev, dev); 224862306a36Sopenharmony_ci eni_dev->pci_dev = pci_dev; 224962306a36Sopenharmony_ci eni_dev->asic = ent->driver_data; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci rc = eni_do_init(dev); 225262306a36Sopenharmony_ci if (rc < 0) 225362306a36Sopenharmony_ci goto err_unregister; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci rc = eni_start(dev); 225662306a36Sopenharmony_ci if (rc < 0) 225762306a36Sopenharmony_ci goto err_eni_release; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci eni_dev->more = eni_boards; 226062306a36Sopenharmony_ci eni_boards = dev; 226162306a36Sopenharmony_ciout: 226262306a36Sopenharmony_ci return rc; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_cierr_eni_release: 226562306a36Sopenharmony_ci dev->phy = NULL; 226662306a36Sopenharmony_ci iounmap(ENI_DEV(dev)->ioaddr); 226762306a36Sopenharmony_cierr_unregister: 226862306a36Sopenharmony_ci atm_dev_deregister(dev); 226962306a36Sopenharmony_cierr_free_consistent: 227062306a36Sopenharmony_ci dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); 227162306a36Sopenharmony_cierr_kfree: 227262306a36Sopenharmony_ci kfree(eni_dev); 227362306a36Sopenharmony_cierr_disable: 227462306a36Sopenharmony_ci pci_disable_device(pci_dev); 227562306a36Sopenharmony_ci goto out; 227662306a36Sopenharmony_ci} 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistatic const struct pci_device_id eni_pci_tbl[] = { 228062306a36Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_FPGA), 0 /* FPGA */ }, 228162306a36Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_ASIC), 1 /* ASIC */ }, 228262306a36Sopenharmony_ci { 0, } 228362306a36Sopenharmony_ci}; 228462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci,eni_pci_tbl); 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cistatic void eni_remove_one(struct pci_dev *pdev) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci struct atm_dev *dev = pci_get_drvdata(pdev); 229062306a36Sopenharmony_ci struct eni_dev *ed = ENI_DEV(dev); 229162306a36Sopenharmony_ci struct eni_zero *zero = &ed->zero; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci eni_do_release(dev); 229462306a36Sopenharmony_ci atm_dev_deregister(dev); 229562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); 229662306a36Sopenharmony_ci kfree(ed); 229762306a36Sopenharmony_ci pci_disable_device(pdev); 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic struct pci_driver eni_driver = { 230262306a36Sopenharmony_ci .name = DEV_LABEL, 230362306a36Sopenharmony_ci .id_table = eni_pci_tbl, 230462306a36Sopenharmony_ci .probe = eni_init_one, 230562306a36Sopenharmony_ci .remove = eni_remove_one, 230662306a36Sopenharmony_ci}; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_cistatic int __init eni_init(void) 231062306a36Sopenharmony_ci{ 231162306a36Sopenharmony_ci struct sk_buff *skb; /* dummy for sizeof */ 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct eni_skb_prv)); 231462306a36Sopenharmony_ci return pci_register_driver(&eni_driver); 231562306a36Sopenharmony_ci} 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cimodule_init(eni_init); 231962306a36Sopenharmony_ci/* @@@ since exit routine not defined, this module can not be unloaded */ 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2322