18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/atm.h> 138c2ecf20Sopenharmony_ci#include <linux/atmdev.h> 148c2ecf20Sopenharmony_ci#include <linux/sonet.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/uio.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/atm_eni.h> 218c2ecf20Sopenharmony_ci#include <linux/bitops.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <asm/io.h> 248c2ecf20Sopenharmony_ci#include <linux/atomic.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci#include <asm/string.h> 278c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "tonga.h" 308c2ecf20Sopenharmony_ci#include "midway.h" 318c2ecf20Sopenharmony_ci#include "suni.h" 328c2ecf20Sopenharmony_ci#include "eni.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * TODO: 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Show stoppers 388c2ecf20Sopenharmony_ci * none 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Minor 418c2ecf20Sopenharmony_ci * - OAM support 428c2ecf20Sopenharmony_ci * - fix bugs listed below 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * KNOWN BUGS: 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * - may run into JK-JK bug and deadlock 498c2ecf20Sopenharmony_ci * - should allocate UBR channel first 508c2ecf20Sopenharmony_ci * - buffer space allocation algorithm is stupid 518c2ecf20Sopenharmony_ci * (RX: should be maxSDU+maxdelay*rate 528c2ecf20Sopenharmony_ci * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) 538c2ecf20Sopenharmony_ci * - doesn't support OAM cells 548c2ecf20Sopenharmony_ci * - eni_put_free may hang if not putting memory fragments that _complete_ 558c2ecf20Sopenharmony_ci * 2^n block (never happens in real life, though) 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#if 0 608c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) 618c2ecf20Sopenharmony_ci#else 628c2ecf20Sopenharmony_ci#define DPRINTK(format,args...) 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST 678c2ecf20Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_TX_8W 688c2ecf20Sopenharmony_ci#define CONFIG_ATM_ENI_BURST_RX_4W 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#ifndef CONFIG_ATM_ENI_DEBUG 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define NULLCHECK(x) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define EVENT(s,a,b) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void event_dump(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#else 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * NULL pointer checking 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define NULLCHECK(x) \ 938c2ecf20Sopenharmony_ci if ((unsigned long) (x) < 0x30) \ 948c2ecf20Sopenharmony_ci printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x)) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* 978c2ecf20Sopenharmony_ci * Very extensive activity logging. Greatly improves bug detection speed but 988c2ecf20Sopenharmony_ci * costs a few Mbps if enabled. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define EV 64 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic const char *ev[EV]; 1048c2ecf20Sopenharmony_cistatic unsigned long ev_a[EV],ev_b[EV]; 1058c2ecf20Sopenharmony_cistatic int ec = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void EVENT(const char *s,unsigned long a,unsigned long b) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci ev[ec] = s; 1118c2ecf20Sopenharmony_ci ev_a[ec] = a; 1128c2ecf20Sopenharmony_ci ev_b[ec] = b; 1138c2ecf20Sopenharmony_ci ec = (ec+1) % EV; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void event_dump(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int n,i; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci for (n = 0; n < EV; n++) { 1228c2ecf20Sopenharmony_ci i = (ec+n) % EV; 1238c2ecf20Sopenharmony_ci printk(KERN_NOTICE); 1248c2ecf20Sopenharmony_ci printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#endif /* CONFIG_ATM_ENI_DEBUG */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* 1338c2ecf20Sopenharmony_ci * NExx must not be equal at end 1348c2ecf20Sopenharmony_ci * EExx may be equal at end 1358c2ecf20Sopenharmony_ci * xxPJOK verify validity of pointer jumps 1368c2ecf20Sopenharmony_ci * xxPMOK operating on a circular buffer of "c" words 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define NEPJOK(a0,a1,b) \ 1408c2ecf20Sopenharmony_ci ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1)) 1418c2ecf20Sopenharmony_ci#define EEPJOK(a0,a1,b) \ 1428c2ecf20Sopenharmony_ci ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1)) 1438c2ecf20Sopenharmony_ci#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b) 1448c2ecf20Sopenharmony_ci#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0, 1488c2ecf20Sopenharmony_ci backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0, 1498c2ecf20Sopenharmony_ci putting = 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic struct atm_dev *eni_boards = NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Read/write registers on card */ 1548c2ecf20Sopenharmony_ci#define eni_in(r) readl(eni_dev->reg+(r)*4) 1558c2ecf20Sopenharmony_ci#define eni_out(v,r) writel((v),eni_dev->reg+(r)*4) 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/*-------------------------------- utilities --------------------------------*/ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void dump_mem(struct eni_dev *eni_dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int i; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0; i < eni_dev->free_len; i++) 1668c2ecf20Sopenharmony_ci printk(KERN_DEBUG " %d: %p %d\n",i, 1678c2ecf20Sopenharmony_ci eni_dev->free_list[i].start, 1688c2ecf20Sopenharmony_ci 1 << eni_dev->free_list[i].order); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void dump(struct atm_dev *dev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci int i; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 1798c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Free memory\n"); 1808c2ecf20Sopenharmony_ci dump_mem(eni_dev); 1818c2ecf20Sopenharmony_ci printk(KERN_NOTICE "TX buffers\n"); 1828c2ecf20Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) 1838c2ecf20Sopenharmony_ci if (eni_dev->tx[i].send) 1848c2ecf20Sopenharmony_ci printk(KERN_NOTICE " TX %d @ %p: %ld\n",i, 1858c2ecf20Sopenharmony_ci eni_dev->tx[i].send,eni_dev->tx[i].words*4); 1868c2ecf20Sopenharmony_ci printk(KERN_NOTICE "RX buffers\n"); 1878c2ecf20Sopenharmony_ci for (i = 0; i < 1024; i++) 1888c2ecf20Sopenharmony_ci if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx) 1898c2ecf20Sopenharmony_ci printk(KERN_NOTICE " RX %d @ %p: %ld\n",i, 1908c2ecf20Sopenharmony_ci ENI_VCC(eni_dev->rx_map[i])->recv, 1918c2ecf20Sopenharmony_ci ENI_VCC(eni_dev->rx_map[i])->words*4); 1928c2ecf20Sopenharmony_ci printk(KERN_NOTICE "----\n"); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void eni_put_free(struct eni_dev *eni_dev, void __iomem *start, 1978c2ecf20Sopenharmony_ci unsigned long size) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct eni_free *list; 2008c2ecf20Sopenharmony_ci int len,order; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size); 2038c2ecf20Sopenharmony_ci start += eni_dev->base_diff; 2048c2ecf20Sopenharmony_ci list = eni_dev->free_list; 2058c2ecf20Sopenharmony_ci len = eni_dev->free_len; 2068c2ecf20Sopenharmony_ci while (size) { 2078c2ecf20Sopenharmony_ci if (len >= eni_dev->free_list_size) { 2088c2ecf20Sopenharmony_ci printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n", 2098c2ecf20Sopenharmony_ci start,size); 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++); 2138c2ecf20Sopenharmony_ci if (MID_MIN_BUF_SIZE > (1 << order)) { 2148c2ecf20Sopenharmony_ci printk(KERN_CRIT "eni_put_free: order %d too small\n", 2158c2ecf20Sopenharmony_ci order); 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci list[len].start = (void __iomem *) start; 2198c2ecf20Sopenharmony_ci list[len].order = order; 2208c2ecf20Sopenharmony_ci len++; 2218c2ecf20Sopenharmony_ci start += 1 << order; 2228c2ecf20Sopenharmony_ci size -= 1 << order; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci eni_dev->free_len = len; 2258c2ecf20Sopenharmony_ci /*dump_mem(eni_dev);*/ 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct eni_free *list; 2328c2ecf20Sopenharmony_ci void __iomem *start; 2338c2ecf20Sopenharmony_ci int len,i,order,best_order,index; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci list = eni_dev->free_list; 2368c2ecf20Sopenharmony_ci len = eni_dev->free_len; 2378c2ecf20Sopenharmony_ci if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE; 2388c2ecf20Sopenharmony_ci if (*size > MID_MAX_BUF_SIZE) return NULL; 2398c2ecf20Sopenharmony_ci for (order = 0; (1 << order) < *size; order++) 2408c2ecf20Sopenharmony_ci ; 2418c2ecf20Sopenharmony_ci DPRINTK("trying: %ld->%d\n",*size,order); 2428c2ecf20Sopenharmony_ci best_order = 65; /* we don't have more than 2^64 of anything ... */ 2438c2ecf20Sopenharmony_ci index = 0; /* silence GCC */ 2448c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 2458c2ecf20Sopenharmony_ci if (list[i].order == order) { 2468c2ecf20Sopenharmony_ci best_order = order; 2478c2ecf20Sopenharmony_ci index = i; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci else if (best_order > list[i].order && list[i].order > order) { 2518c2ecf20Sopenharmony_ci best_order = list[i].order; 2528c2ecf20Sopenharmony_ci index = i; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci if (best_order == 65) return NULL; 2558c2ecf20Sopenharmony_ci start = list[index].start-eni_dev->base_diff; 2568c2ecf20Sopenharmony_ci list[index] = list[--len]; 2578c2ecf20Sopenharmony_ci eni_dev->free_len = len; 2588c2ecf20Sopenharmony_ci *size = 1 << order; 2598c2ecf20Sopenharmony_ci eni_put_free(eni_dev,start+*size,(1 << best_order)-*size); 2608c2ecf20Sopenharmony_ci DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start); 2618c2ecf20Sopenharmony_ci memset_io(start,0,*size); /* never leak data */ 2628c2ecf20Sopenharmony_ci /*dump_mem(eni_dev);*/ 2638c2ecf20Sopenharmony_ci return start; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start, 2688c2ecf20Sopenharmony_ci unsigned long size) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct eni_free *list; 2718c2ecf20Sopenharmony_ci int len,i,order; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci start += eni_dev->base_diff; 2748c2ecf20Sopenharmony_ci list = eni_dev->free_list; 2758c2ecf20Sopenharmony_ci len = eni_dev->free_len; 2768c2ecf20Sopenharmony_ci for (order = -1; size; order++) size >>= 1; 2778c2ecf20Sopenharmony_ci DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order); 2788c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 2798c2ecf20Sopenharmony_ci if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) && 2808c2ecf20Sopenharmony_ci list[i].order == order) { 2818c2ecf20Sopenharmony_ci DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i, 2828c2ecf20Sopenharmony_ci list[i].start,start,1 << order,list[i].order,order); 2838c2ecf20Sopenharmony_ci list[i] = list[--len]; 2848c2ecf20Sopenharmony_ci start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order)); 2858c2ecf20Sopenharmony_ci order++; 2868c2ecf20Sopenharmony_ci i = -1; 2878c2ecf20Sopenharmony_ci continue; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (len >= eni_dev->free_list_size) { 2908c2ecf20Sopenharmony_ci printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start, 2918c2ecf20Sopenharmony_ci order); 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci list[len].start = start; 2958c2ecf20Sopenharmony_ci list[len].order = order; 2968c2ecf20Sopenharmony_ci eni_dev->free_len = len+1; 2978c2ecf20Sopenharmony_ci /*dump_mem(eni_dev);*/ 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/*----------------------------------- RX ------------------------------------*/ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci#define ENI_VCC_NOS ((struct atm_vcc *) 1) 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void rx_ident_err(struct atm_vcc *vcc) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct atm_dev *dev; 3108c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 3118c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dev = vcc->dev; 3148c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 3158c2ecf20Sopenharmony_ci /* immediately halt adapter */ 3168c2ecf20Sopenharmony_ci eni_out(eni_in(MID_MC_S) & 3178c2ecf20Sopenharmony_ci ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S); 3188c2ecf20Sopenharmony_ci /* dump useful information */ 3198c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 3208c2ecf20Sopenharmony_ci printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident " 3218c2ecf20Sopenharmony_ci "mismatch\n",dev->number); 3228c2ecf20Sopenharmony_ci printk(KERN_ALERT " VCI %d, rxing %d, words %ld\n",vcc->vci, 3238c2ecf20Sopenharmony_ci eni_vcc->rxing,eni_vcc->words); 3248c2ecf20Sopenharmony_ci printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value " 3258c2ecf20Sopenharmony_ci "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, 3268c2ecf20Sopenharmony_ci (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4)); 3278c2ecf20Sopenharmony_ci printk(KERN_ALERT " last %p, servicing %d\n",eni_vcc->last, 3288c2ecf20Sopenharmony_ci eni_vcc->servicing); 3298c2ecf20Sopenharmony_ci EVENT("---dump ends here---\n",0,0); 3308c2ecf20Sopenharmony_ci printk(KERN_NOTICE "---recent events---\n"); 3318c2ecf20Sopenharmony_ci event_dump(); 3328c2ecf20Sopenharmony_ci ENI_DEV(dev)->fast = NULL; /* really stop it */ 3338c2ecf20Sopenharmony_ci ENI_DEV(dev)->slow = NULL; 3348c2ecf20Sopenharmony_ci skb_queue_head_init(&ENI_DEV(dev)->rx_queue); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, 3398c2ecf20Sopenharmony_ci unsigned long skip,unsigned long size,unsigned long eff) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 3428c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 3438c2ecf20Sopenharmony_ci u32 dma_rd,dma_wr; 3448c2ecf20Sopenharmony_ci u32 dma[RX_DMA_BUF*2]; 3458c2ecf20Sopenharmony_ci dma_addr_t paddr; 3468c2ecf20Sopenharmony_ci unsigned long here; 3478c2ecf20Sopenharmony_ci int i,j; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 3508c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 3518c2ecf20Sopenharmony_ci paddr = 0; /* GCC, shut up */ 3528c2ecf20Sopenharmony_ci if (skb) { 3538c2ecf20Sopenharmony_ci paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, 3548c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3558c2ecf20Sopenharmony_ci if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) 3568c2ecf20Sopenharmony_ci goto dma_map_error; 3578c2ecf20Sopenharmony_ci ENI_PRV_PADDR(skb) = paddr; 3588c2ecf20Sopenharmony_ci if (paddr & 3) 3598c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has " 3608c2ecf20Sopenharmony_ci "mis-aligned RX data (0x%lx)\n",vcc->dev->number, 3618c2ecf20Sopenharmony_ci vcc->vci,(unsigned long) paddr); 3628c2ecf20Sopenharmony_ci ENI_PRV_SIZE(skb) = size+skip; 3638c2ecf20Sopenharmony_ci /* PDU plus descriptor */ 3648c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci j = 0; 3678c2ecf20Sopenharmony_ci if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */ 3688c2ecf20Sopenharmony_ci here = (eni_vcc->descr+skip) & (eni_vcc->words-1); 3698c2ecf20Sopenharmony_ci dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci 3708c2ecf20Sopenharmony_ci << MID_DMA_VCI_SHIFT) | MID_DT_JK; 3718c2ecf20Sopenharmony_ci dma[j++] = 0; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1); 3748c2ecf20Sopenharmony_ci if (!eff) size += skip; 3758c2ecf20Sopenharmony_ci else { 3768c2ecf20Sopenharmony_ci unsigned long words; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!size) { 3798c2ecf20Sopenharmony_ci DPRINTK("strange things happen ...\n"); 3808c2ecf20Sopenharmony_ci EVENT("strange things happen ... (skip=%ld,eff=%ld)\n", 3818c2ecf20Sopenharmony_ci size,eff); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci words = eff; 3848c2ecf20Sopenharmony_ci if (paddr & 15) { 3858c2ecf20Sopenharmony_ci unsigned long init; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci init = 4-((paddr & 15) >> 2); 3888c2ecf20Sopenharmony_ci if (init > words) init = words; 3898c2ecf20Sopenharmony_ci dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | 3908c2ecf20Sopenharmony_ci (vcc->vci << MID_DMA_VCI_SHIFT); 3918c2ecf20Sopenharmony_ci dma[j++] = paddr; 3928c2ecf20Sopenharmony_ci paddr += init << 2; 3938c2ecf20Sopenharmony_ci words -= init; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ 3968c2ecf20Sopenharmony_ci if (words & ~15) { 3978c2ecf20Sopenharmony_ci dma[j++] = MID_DT_16W | ((words >> 4) << 3988c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 3998c2ecf20Sopenharmony_ci MID_DMA_VCI_SHIFT); 4008c2ecf20Sopenharmony_ci dma[j++] = paddr; 4018c2ecf20Sopenharmony_ci paddr += (words & ~15) << 2; 4028c2ecf20Sopenharmony_ci words &= 15; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci#endif 4058c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ 4068c2ecf20Sopenharmony_ci if (words & ~7) { 4078c2ecf20Sopenharmony_ci dma[j++] = MID_DT_8W | ((words >> 3) << 4088c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 4098c2ecf20Sopenharmony_ci MID_DMA_VCI_SHIFT); 4108c2ecf20Sopenharmony_ci dma[j++] = paddr; 4118c2ecf20Sopenharmony_ci paddr += (words & ~7) << 2; 4128c2ecf20Sopenharmony_ci words &= 7; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ 4168c2ecf20Sopenharmony_ci if (words & ~3) { 4178c2ecf20Sopenharmony_ci dma[j++] = MID_DT_4W | ((words >> 2) << 4188c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 4198c2ecf20Sopenharmony_ci MID_DMA_VCI_SHIFT); 4208c2ecf20Sopenharmony_ci dma[j++] = paddr; 4218c2ecf20Sopenharmony_ci paddr += (words & ~3) << 2; 4228c2ecf20Sopenharmony_ci words &= 3; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci#endif 4258c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ 4268c2ecf20Sopenharmony_ci if (words & ~1) { 4278c2ecf20Sopenharmony_ci dma[j++] = MID_DT_2W | ((words >> 1) << 4288c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (vcc->vci << 4298c2ecf20Sopenharmony_ci MID_DMA_VCI_SHIFT); 4308c2ecf20Sopenharmony_ci dma[j++] = paddr; 4318c2ecf20Sopenharmony_ci paddr += (words & ~1) << 2; 4328c2ecf20Sopenharmony_ci words &= 1; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci#endif 4358c2ecf20Sopenharmony_ci if (words) { 4368c2ecf20Sopenharmony_ci dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) 4378c2ecf20Sopenharmony_ci | (vcc->vci << MID_DMA_VCI_SHIFT); 4388c2ecf20Sopenharmony_ci dma[j++] = paddr; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci if (size != eff) { 4428c2ecf20Sopenharmony_ci dma[j++] = (here << MID_DMA_COUNT_SHIFT) | 4438c2ecf20Sopenharmony_ci (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; 4448c2ecf20Sopenharmony_ci dma[j++] = 0; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci if (!j || j > 2*RX_DMA_BUF) { 4478c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n"); 4488c2ecf20Sopenharmony_ci goto trouble; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci dma[j-2] |= MID_DMA_END; 4518c2ecf20Sopenharmony_ci j = j >> 1; 4528c2ecf20Sopenharmony_ci dma_wr = eni_in(MID_DMA_WR_RX); 4538c2ecf20Sopenharmony_ci dma_rd = eni_in(MID_DMA_RD_RX); 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * Can I move the dma_wr pointer by 2j+1 positions without overwriting 4568c2ecf20Sopenharmony_ci * data that hasn't been read (position of dma_rd) yet ? 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */ 4598c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n", 4608c2ecf20Sopenharmony_ci vcc->dev->number); 4618c2ecf20Sopenharmony_ci goto trouble; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci for (i = 0; i < j; i++) { 4648c2ecf20Sopenharmony_ci writel(dma[i*2],eni_dev->rx_dma+dma_wr*8); 4658c2ecf20Sopenharmony_ci writel(dma[i*2+1],eni_dev->rx_dma+dma_wr*8+4); 4668c2ecf20Sopenharmony_ci dma_wr = (dma_wr+1) & (NR_DMA_RX-1); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci if (skb) { 4698c2ecf20Sopenharmony_ci ENI_PRV_POS(skb) = eni_vcc->descr+size+1; 4708c2ecf20Sopenharmony_ci skb_queue_tail(&eni_dev->rx_queue,skb); 4718c2ecf20Sopenharmony_ci eni_vcc->last = skb; 4728c2ecf20Sopenharmony_ci rx_enqueued++; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci eni_vcc->descr = here; 4758c2ecf20Sopenharmony_ci eni_out(dma_wr,MID_DMA_WR_RX); 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_citrouble: 4798c2ecf20Sopenharmony_ci if (paddr) 4808c2ecf20Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len, 4818c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4828c2ecf20Sopenharmony_cidma_map_error: 4838c2ecf20Sopenharmony_ci if (skb) dev_kfree_skb_irq(skb); 4848c2ecf20Sopenharmony_ci return -1; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic void discard(struct atm_vcc *vcc,unsigned long size) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 4938c2ecf20Sopenharmony_ci EVENT("discard (size=%ld)\n",size,0); 4948c2ecf20Sopenharmony_ci while (do_rx_dma(vcc,NULL,1,size,0)) EVENT("BUSY LOOP",0,0); 4958c2ecf20Sopenharmony_ci /* could do a full fallback, but that might be more expensive */ 4968c2ecf20Sopenharmony_ci if (eni_vcc->rxing) ENI_PRV_POS(eni_vcc->last) += size+1; 4978c2ecf20Sopenharmony_ci else eni_vcc->rx_pos = (eni_vcc->rx_pos+size+1) & (eni_vcc->words-1); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* 5028c2ecf20Sopenharmony_ci * TODO: should check whether direct copies (without DMA setup, dequeuing on 5038c2ecf20Sopenharmony_ci * interrupt, etc.) aren't much faster for AAL0 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int rx_aal0(struct atm_vcc *vcc) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 5098c2ecf20Sopenharmony_ci unsigned long descr; 5108c2ecf20Sopenharmony_ci unsigned long length; 5118c2ecf20Sopenharmony_ci struct sk_buff *skb; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci DPRINTK(">rx_aal0\n"); 5148c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 5158c2ecf20Sopenharmony_ci descr = readl(eni_vcc->recv+eni_vcc->descr*4); 5168c2ecf20Sopenharmony_ci if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { 5178c2ecf20Sopenharmony_ci rx_ident_err(vcc); 5188c2ecf20Sopenharmony_ci return 1; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci if (descr & MID_RED_T) { 5218c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", 5228c2ecf20Sopenharmony_ci vcc->dev->number); 5238c2ecf20Sopenharmony_ci length = 0; 5248c2ecf20Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci else { 5278c2ecf20Sopenharmony_ci length = ATM_CELL_SIZE-1; /* no HEC */ 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci skb = length ? atm_alloc_charge(vcc,length,GFP_ATOMIC) : NULL; 5308c2ecf20Sopenharmony_ci if (!skb) { 5318c2ecf20Sopenharmony_ci discard(vcc,length >> 2); 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci skb_put(skb,length); 5358c2ecf20Sopenharmony_ci skb->tstamp = eni_vcc->timestamp; 5368c2ecf20Sopenharmony_ci DPRINTK("got len %ld\n",length); 5378c2ecf20Sopenharmony_ci if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; 5388c2ecf20Sopenharmony_ci eni_vcc->rxing++; 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int rx_aal5(struct atm_vcc *vcc) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 5468c2ecf20Sopenharmony_ci unsigned long descr; 5478c2ecf20Sopenharmony_ci unsigned long size,eff,length; 5488c2ecf20Sopenharmony_ci struct sk_buff *skb; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci EVENT("rx_aal5\n",0,0); 5518c2ecf20Sopenharmony_ci DPRINTK(">rx_aal5\n"); 5528c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 5538c2ecf20Sopenharmony_ci descr = readl(eni_vcc->recv+eni_vcc->descr*4); 5548c2ecf20Sopenharmony_ci if ((descr & MID_RED_IDEN) != (MID_RED_RX_ID << MID_RED_SHIFT)) { 5558c2ecf20Sopenharmony_ci rx_ident_err(vcc); 5568c2ecf20Sopenharmony_ci return 1; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci if (descr & (MID_RED_T | MID_RED_CRC_ERR)) { 5598c2ecf20Sopenharmony_ci if (descr & MID_RED_T) { 5608c2ecf20Sopenharmony_ci EVENT("empty cell (descr=0x%lx)\n",descr,0); 5618c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): trashing empty cell\n", 5628c2ecf20Sopenharmony_ci vcc->dev->number); 5638c2ecf20Sopenharmony_ci size = 0; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci else { 5668c2ecf20Sopenharmony_ci static unsigned long silence = 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (time_after(jiffies, silence) || silence == 0) { 5698c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): " 5708c2ecf20Sopenharmony_ci "discarding PDU(s) with CRC error\n", 5718c2ecf20Sopenharmony_ci vcc->dev->number); 5728c2ecf20Sopenharmony_ci silence = (jiffies+2*HZ)|1; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); 5758c2ecf20Sopenharmony_ci EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr, 5768c2ecf20Sopenharmony_ci size); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci eff = length = 0; 5798c2ecf20Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci else { 5828c2ecf20Sopenharmony_ci size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2); 5838c2ecf20Sopenharmony_ci DPRINTK("size=%ld\n",size); 5848c2ecf20Sopenharmony_ci length = readl(eni_vcc->recv+(((eni_vcc->descr+size-1) & 5858c2ecf20Sopenharmony_ci (eni_vcc->words-1)))*4) & 0xffff; 5868c2ecf20Sopenharmony_ci /* -trailer(2)+header(1) */ 5878c2ecf20Sopenharmony_ci if (length && length <= (size << 2)-8 && length <= 5888c2ecf20Sopenharmony_ci ATM_MAX_AAL5_PDU) eff = (length+3) >> 2; 5898c2ecf20Sopenharmony_ci else { /* ^ trailer length (8) */ 5908c2ecf20Sopenharmony_ci EVENT("bad PDU (descr=0x08%lx,length=%ld)\n",descr, 5918c2ecf20Sopenharmony_ci length); 5928c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): bad AAL5 PDU " 5938c2ecf20Sopenharmony_ci "(VCI=%d,length=%ld,size=%ld (descr 0x%lx))\n", 5948c2ecf20Sopenharmony_ci vcc->dev->number,vcc->vci,length,size << 2,descr); 5958c2ecf20Sopenharmony_ci length = eff = 0; 5968c2ecf20Sopenharmony_ci atomic_inc(&vcc->stats->rx_err); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci skb = eff ? atm_alloc_charge(vcc,eff << 2,GFP_ATOMIC) : NULL; 6008c2ecf20Sopenharmony_ci if (!skb) { 6018c2ecf20Sopenharmony_ci discard(vcc,size); 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci skb_put(skb,length); 6058c2ecf20Sopenharmony_ci DPRINTK("got len %ld\n",length); 6068c2ecf20Sopenharmony_ci if (do_rx_dma(vcc,skb,1,size,eff)) return 1; 6078c2ecf20Sopenharmony_ci eni_vcc->rxing++; 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic inline int rx_vcc(struct atm_vcc *vcc) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci void __iomem *vci_dsc; 6158c2ecf20Sopenharmony_ci unsigned long tmp; 6168c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 6198c2ecf20Sopenharmony_ci vci_dsc = ENI_DEV(vcc->dev)->vci+vcc->vci*16; 6208c2ecf20Sopenharmony_ci EVENT("rx_vcc(1)\n",0,0); 6218c2ecf20Sopenharmony_ci while (eni_vcc->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) >> 6228c2ecf20Sopenharmony_ci MID_VCI_DESCR_SHIFT)) { 6238c2ecf20Sopenharmony_ci EVENT("rx_vcc(2: host dsc=0x%lx, nic dsc=0x%lx)\n", 6248c2ecf20Sopenharmony_ci eni_vcc->descr,tmp); 6258c2ecf20Sopenharmony_ci DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, 6268c2ecf20Sopenharmony_ci (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> 6278c2ecf20Sopenharmony_ci MID_VCI_DESCR_SHIFT)); 6288c2ecf20Sopenharmony_ci if (ENI_VCC(vcc)->rx(vcc)) return 1; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci /* clear IN_SERVICE flag */ 6318c2ecf20Sopenharmony_ci writel(readl(vci_dsc) & ~MID_VCI_IN_SERVICE,vci_dsc); 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * If new data has arrived between evaluating the while condition and 6348c2ecf20Sopenharmony_ci * clearing IN_SERVICE, we wouldn't be notified until additional data 6358c2ecf20Sopenharmony_ci * follows. So we have to loop again to be sure. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci EVENT("rx_vcc(3)\n",0,0); 6388c2ecf20Sopenharmony_ci while (ENI_VCC(vcc)->descr != (tmp = (readl(vci_dsc+4) & MID_VCI_DESCR) 6398c2ecf20Sopenharmony_ci >> MID_VCI_DESCR_SHIFT)) { 6408c2ecf20Sopenharmony_ci EVENT("rx_vcc(4: host dsc=0x%lx, nic dsc=0x%lx)\n", 6418c2ecf20Sopenharmony_ci eni_vcc->descr,tmp); 6428c2ecf20Sopenharmony_ci DPRINTK("CB_DESCR %ld REG_DESCR %d\n",ENI_VCC(vcc)->descr, 6438c2ecf20Sopenharmony_ci (((unsigned) readl(vci_dsc+4) & MID_VCI_DESCR) >> 6448c2ecf20Sopenharmony_ci MID_VCI_DESCR_SHIFT)); 6458c2ecf20Sopenharmony_ci if (ENI_VCC(vcc)->rx(vcc)) return 1; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void poll_rx(struct atm_dev *dev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 6548c2ecf20Sopenharmony_ci struct atm_vcc *curr; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 6578c2ecf20Sopenharmony_ci while ((curr = eni_dev->fast)) { 6588c2ecf20Sopenharmony_ci EVENT("poll_rx.fast\n",0,0); 6598c2ecf20Sopenharmony_ci if (rx_vcc(curr)) return; 6608c2ecf20Sopenharmony_ci eni_dev->fast = ENI_VCC(curr)->next; 6618c2ecf20Sopenharmony_ci ENI_VCC(curr)->next = ENI_VCC_NOS; 6628c2ecf20Sopenharmony_ci barrier(); 6638c2ecf20Sopenharmony_ci ENI_VCC(curr)->servicing--; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci while ((curr = eni_dev->slow)) { 6668c2ecf20Sopenharmony_ci EVENT("poll_rx.slow\n",0,0); 6678c2ecf20Sopenharmony_ci if (rx_vcc(curr)) return; 6688c2ecf20Sopenharmony_ci eni_dev->slow = ENI_VCC(curr)->next; 6698c2ecf20Sopenharmony_ci ENI_VCC(curr)->next = ENI_VCC_NOS; 6708c2ecf20Sopenharmony_ci barrier(); 6718c2ecf20Sopenharmony_ci ENI_VCC(curr)->servicing--; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void get_service(struct atm_dev *dev) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 6798c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 6808c2ecf20Sopenharmony_ci unsigned long vci; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci DPRINTK(">get_service\n"); 6838c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 6848c2ecf20Sopenharmony_ci while (eni_in(MID_SERV_WRITE) != eni_dev->serv_read) { 6858c2ecf20Sopenharmony_ci vci = readl(eni_dev->service+eni_dev->serv_read*4); 6868c2ecf20Sopenharmony_ci eni_dev->serv_read = (eni_dev->serv_read+1) & (NR_SERVICE-1); 6878c2ecf20Sopenharmony_ci vcc = eni_dev->rx_map[vci & 1023]; 6888c2ecf20Sopenharmony_ci if (!vcc) { 6898c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %ld not " 6908c2ecf20Sopenharmony_ci "found\n",dev->number,vci); 6918c2ecf20Sopenharmony_ci continue; /* nasty but we try to go on anyway */ 6928c2ecf20Sopenharmony_ci /* @@@ nope, doesn't work */ 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci EVENT("getting from service\n",0,0); 6958c2ecf20Sopenharmony_ci if (ENI_VCC(vcc)->next != ENI_VCC_NOS) { 6968c2ecf20Sopenharmony_ci EVENT("double service\n",0,0); 6978c2ecf20Sopenharmony_ci DPRINTK("Grr, servicing VCC %ld twice\n",vci); 6988c2ecf20Sopenharmony_ci continue; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci ENI_VCC(vcc)->timestamp = ktime_get_real(); 7018c2ecf20Sopenharmony_ci ENI_VCC(vcc)->next = NULL; 7028c2ecf20Sopenharmony_ci if (vcc->qos.rxtp.traffic_class == ATM_CBR) { 7038c2ecf20Sopenharmony_ci if (eni_dev->fast) 7048c2ecf20Sopenharmony_ci ENI_VCC(eni_dev->last_fast)->next = vcc; 7058c2ecf20Sopenharmony_ci else eni_dev->fast = vcc; 7068c2ecf20Sopenharmony_ci eni_dev->last_fast = vcc; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci else { 7098c2ecf20Sopenharmony_ci if (eni_dev->slow) 7108c2ecf20Sopenharmony_ci ENI_VCC(eni_dev->last_slow)->next = vcc; 7118c2ecf20Sopenharmony_ci else eni_dev->slow = vcc; 7128c2ecf20Sopenharmony_ci eni_dev->last_slow = vcc; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci putting++; 7158c2ecf20Sopenharmony_ci ENI_VCC(vcc)->servicing++; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic void dequeue_rx(struct atm_dev *dev) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 7238c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 7248c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 7258c2ecf20Sopenharmony_ci struct sk_buff *skb; 7268c2ecf20Sopenharmony_ci void __iomem *vci_dsc; 7278c2ecf20Sopenharmony_ci int first; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 7308c2ecf20Sopenharmony_ci first = 1; 7318c2ecf20Sopenharmony_ci while (1) { 7328c2ecf20Sopenharmony_ci skb = skb_dequeue(&eni_dev->rx_queue); 7338c2ecf20Sopenharmony_ci if (!skb) { 7348c2ecf20Sopenharmony_ci if (first) { 7358c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): RX but not " 7368c2ecf20Sopenharmony_ci "rxing\n",dev->number); 7378c2ecf20Sopenharmony_ci EVENT("nothing to dequeue\n",0,0); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci EVENT("dequeued (size=%ld,pos=0x%lx)\n",ENI_PRV_SIZE(skb), 7428c2ecf20Sopenharmony_ci ENI_PRV_POS(skb)); 7438c2ecf20Sopenharmony_ci rx_dequeued++; 7448c2ecf20Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 7458c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 7468c2ecf20Sopenharmony_ci first = 0; 7478c2ecf20Sopenharmony_ci vci_dsc = eni_dev->vci+vcc->vci*16; 7488c2ecf20Sopenharmony_ci if (!EEPMOK(eni_vcc->rx_pos,ENI_PRV_SIZE(skb), 7498c2ecf20Sopenharmony_ci (readl(vci_dsc+4) & MID_VCI_READ) >> MID_VCI_READ_SHIFT, 7508c2ecf20Sopenharmony_ci eni_vcc->words)) { 7518c2ecf20Sopenharmony_ci EVENT("requeuing\n",0,0); 7528c2ecf20Sopenharmony_ci skb_queue_head(&eni_dev->rx_queue,skb); 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci eni_vcc->rxing--; 7568c2ecf20Sopenharmony_ci eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1); 7578c2ecf20Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, 7588c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7598c2ecf20Sopenharmony_ci if (!skb->len) dev_kfree_skb_irq(skb); 7608c2ecf20Sopenharmony_ci else { 7618c2ecf20Sopenharmony_ci EVENT("pushing (len=%ld)\n",skb->len,0); 7628c2ecf20Sopenharmony_ci if (vcc->qos.aal == ATM_AAL0) 7638c2ecf20Sopenharmony_ci *(unsigned long *) skb->data = 7648c2ecf20Sopenharmony_ci ntohl(*(unsigned long *) skb->data); 7658c2ecf20Sopenharmony_ci memset(skb->cb,0,sizeof(struct eni_skb_prv)); 7668c2ecf20Sopenharmony_ci vcc->push(vcc,skb); 7678c2ecf20Sopenharmony_ci pushed++; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci atomic_inc(&vcc->stats->rx); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci wake_up(&eni_dev->rx_wait); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int open_rx_first(struct atm_vcc *vcc) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 7788c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 7798c2ecf20Sopenharmony_ci unsigned long size; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci DPRINTK("open_rx_first\n"); 7828c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 7838c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 7848c2ecf20Sopenharmony_ci eni_vcc->rx = NULL; 7858c2ecf20Sopenharmony_ci if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0; 7868c2ecf20Sopenharmony_ci size = vcc->qos.rxtp.max_sdu*eni_dev->rx_mult/100; 7878c2ecf20Sopenharmony_ci if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <= 7888c2ecf20Sopenharmony_ci MID_MAX_BUF_SIZE) 7898c2ecf20Sopenharmony_ci size = MID_MAX_BUF_SIZE; 7908c2ecf20Sopenharmony_ci eni_vcc->recv = eni_alloc_mem(eni_dev,&size); 7918c2ecf20Sopenharmony_ci DPRINTK("rx at 0x%lx\n",eni_vcc->recv); 7928c2ecf20Sopenharmony_ci eni_vcc->words = size >> 2; 7938c2ecf20Sopenharmony_ci if (!eni_vcc->recv) return -ENOBUFS; 7948c2ecf20Sopenharmony_ci eni_vcc->rx = vcc->qos.aal == ATM_AAL5 ? rx_aal5 : rx_aal0; 7958c2ecf20Sopenharmony_ci eni_vcc->descr = 0; 7968c2ecf20Sopenharmony_ci eni_vcc->rx_pos = 0; 7978c2ecf20Sopenharmony_ci eni_vcc->rxing = 0; 7988c2ecf20Sopenharmony_ci eni_vcc->servicing = 0; 7998c2ecf20Sopenharmony_ci eni_vcc->next = ENI_VCC_NOS; 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int open_rx_second(struct atm_vcc *vcc) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci void __iomem *here; 8078c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 8088c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 8098c2ecf20Sopenharmony_ci unsigned long size; 8108c2ecf20Sopenharmony_ci int order; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci DPRINTK("open_rx_second\n"); 8138c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 8148c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 8158c2ecf20Sopenharmony_ci if (!eni_vcc->rx) return 0; 8168c2ecf20Sopenharmony_ci /* set up VCI descriptor */ 8178c2ecf20Sopenharmony_ci here = eni_dev->vci+vcc->vci*16; 8188c2ecf20Sopenharmony_ci DPRINTK("loc 0x%x\n",(unsigned) (eni_vcc->recv-eni_dev->ram)/4); 8198c2ecf20Sopenharmony_ci size = eni_vcc->words >> 8; 8208c2ecf20Sopenharmony_ci for (order = -1; size; order++) size >>= 1; 8218c2ecf20Sopenharmony_ci writel(0,here+4); /* descr, read = 0 */ 8228c2ecf20Sopenharmony_ci writel(0,here+8); /* write, state, count = 0 */ 8238c2ecf20Sopenharmony_ci if (eni_dev->rx_map[vcc->vci]) 8248c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): BUG - VCI %d already " 8258c2ecf20Sopenharmony_ci "in use\n",vcc->dev->number,vcc->vci); 8268c2ecf20Sopenharmony_ci eni_dev->rx_map[vcc->vci] = vcc; /* now it counts */ 8278c2ecf20Sopenharmony_ci writel(((vcc->qos.aal != ATM_AAL5 ? MID_MODE_RAW : MID_MODE_AAL5) << 8288c2ecf20Sopenharmony_ci MID_VCI_MODE_SHIFT) | MID_VCI_PTI_MODE | 8298c2ecf20Sopenharmony_ci (((eni_vcc->recv-eni_dev->ram) >> (MID_LOC_SKIP+2)) << 8308c2ecf20Sopenharmony_ci MID_VCI_LOCATION_SHIFT) | (order << MID_VCI_SIZE_SHIFT),here); 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic void close_rx(struct atm_vcc *vcc) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait,current); 8388c2ecf20Sopenharmony_ci void __iomem *here; 8398c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 8408c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 8438c2ecf20Sopenharmony_ci if (!eni_vcc->rx) return; 8448c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 8458c2ecf20Sopenharmony_ci if (vcc->vpi != ATM_VPI_UNSPEC && vcc->vci != ATM_VCI_UNSPEC) { 8468c2ecf20Sopenharmony_ci here = eni_dev->vci+vcc->vci*16; 8478c2ecf20Sopenharmony_ci /* block receiver */ 8488c2ecf20Sopenharmony_ci writel((readl(here) & ~MID_VCI_MODE) | (MID_MODE_TRASH << 8498c2ecf20Sopenharmony_ci MID_VCI_MODE_SHIFT),here); 8508c2ecf20Sopenharmony_ci /* wait for receiver to become idle */ 8518c2ecf20Sopenharmony_ci udelay(27); 8528c2ecf20Sopenharmony_ci /* discard pending cell */ 8538c2ecf20Sopenharmony_ci writel(readl(here) & ~MID_VCI_IN_SERVICE,here); 8548c2ecf20Sopenharmony_ci /* don't accept any new ones */ 8558c2ecf20Sopenharmony_ci eni_dev->rx_map[vcc->vci] = NULL; 8568c2ecf20Sopenharmony_ci /* wait for RX queue to drain */ 8578c2ecf20Sopenharmony_ci DPRINTK("eni_close: waiting for RX ...\n"); 8588c2ecf20Sopenharmony_ci EVENT("RX closing\n",0,0); 8598c2ecf20Sopenharmony_ci add_wait_queue(&eni_dev->rx_wait,&wait); 8608c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 8618c2ecf20Sopenharmony_ci barrier(); 8628c2ecf20Sopenharmony_ci for (;;) { 8638c2ecf20Sopenharmony_ci /* transition service->rx: rxing++, servicing-- */ 8648c2ecf20Sopenharmony_ci if (!eni_vcc->servicing) { 8658c2ecf20Sopenharmony_ci barrier(); 8668c2ecf20Sopenharmony_ci if (!eni_vcc->rxing) break; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci EVENT("drain PDUs (rx %ld, serv %ld)\n",eni_vcc->rxing, 8698c2ecf20Sopenharmony_ci eni_vcc->servicing); 8708c2ecf20Sopenharmony_ci printk(KERN_INFO "%d+%d RX left\n",eni_vcc->servicing, 8718c2ecf20Sopenharmony_ci eni_vcc->rxing); 8728c2ecf20Sopenharmony_ci schedule(); 8738c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci for (;;) { 8768c2ecf20Sopenharmony_ci int at_end; 8778c2ecf20Sopenharmony_ci u32 tmp; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci tasklet_disable(&eni_dev->task); 8808c2ecf20Sopenharmony_ci tmp = readl(eni_dev->vci+vcc->vci*16+4) & MID_VCI_READ; 8818c2ecf20Sopenharmony_ci at_end = eni_vcc->rx_pos == tmp >> MID_VCI_READ_SHIFT; 8828c2ecf20Sopenharmony_ci tasklet_enable(&eni_dev->task); 8838c2ecf20Sopenharmony_ci if (at_end) break; 8848c2ecf20Sopenharmony_ci EVENT("drain discard (host 0x%lx, nic 0x%lx)\n", 8858c2ecf20Sopenharmony_ci eni_vcc->rx_pos,tmp); 8868c2ecf20Sopenharmony_ci printk(KERN_INFO "draining RX: host 0x%lx, nic 0x%x\n", 8878c2ecf20Sopenharmony_ci eni_vcc->rx_pos,tmp); 8888c2ecf20Sopenharmony_ci schedule(); 8898c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 8928c2ecf20Sopenharmony_ci remove_wait_queue(&eni_dev->rx_wait,&wait); 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci eni_free_mem(eni_dev,eni_vcc->recv,eni_vcc->words << 2); 8958c2ecf20Sopenharmony_ci eni_vcc->rx = NULL; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic int start_rx(struct atm_dev *dev) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 9048c2ecf20Sopenharmony_ci eni_dev->rx_map = (struct atm_vcc **) get_zeroed_page(GFP_KERNEL); 9058c2ecf20Sopenharmony_ci if (!eni_dev->rx_map) { 9068c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", 9078c2ecf20Sopenharmony_ci dev->number); 9088c2ecf20Sopenharmony_ci free_page((unsigned long) eni_dev->free_list); 9098c2ecf20Sopenharmony_ci return -ENOMEM; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci eni_dev->rx_mult = DEFAULT_RX_MULT; 9128c2ecf20Sopenharmony_ci eni_dev->fast = eni_dev->last_fast = NULL; 9138c2ecf20Sopenharmony_ci eni_dev->slow = eni_dev->last_slow = NULL; 9148c2ecf20Sopenharmony_ci init_waitqueue_head(&eni_dev->rx_wait); 9158c2ecf20Sopenharmony_ci skb_queue_head_init(&eni_dev->rx_queue); 9168c2ecf20Sopenharmony_ci eni_dev->serv_read = eni_in(MID_SERV_WRITE); 9178c2ecf20Sopenharmony_ci eni_out(0,MID_DMA_WR_RX); 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/*----------------------------------- TX ------------------------------------*/ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cienum enq_res { enq_ok,enq_next,enq_jam }; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic inline void put_dma(int chan,u32 *dma,int *j,dma_addr_t paddr, 9298c2ecf20Sopenharmony_ci u32 size) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci u32 init,words; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci DPRINTK("put_dma: 0x%lx+0x%x\n",(unsigned long) paddr,size); 9348c2ecf20Sopenharmony_ci EVENT("put_dma: 0x%lx+0x%lx\n",(unsigned long) paddr,size); 9358c2ecf20Sopenharmony_ci#if 0 /* don't complain anymore */ 9368c2ecf20Sopenharmony_ci if (paddr & 3) 9378c2ecf20Sopenharmony_ci printk(KERN_ERR "put_dma: unaligned addr (0x%lx)\n",paddr); 9388c2ecf20Sopenharmony_ci if (size & 3) 9398c2ecf20Sopenharmony_ci printk(KERN_ERR "put_dma: unaligned size (0x%lx)\n",size); 9408c2ecf20Sopenharmony_ci#endif 9418c2ecf20Sopenharmony_ci if (paddr & 3) { 9428c2ecf20Sopenharmony_ci init = 4-(paddr & 3); 9438c2ecf20Sopenharmony_ci if (init > size || size < 7) init = size; 9448c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d/%d bytes\n", 9458c2ecf20Sopenharmony_ci (unsigned long) paddr,init,size); 9468c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_BYTE | (init << MID_DMA_COUNT_SHIFT) | 9478c2ecf20Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 9488c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 9498c2ecf20Sopenharmony_ci paddr += init; 9508c2ecf20Sopenharmony_ci size -= init; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci words = size >> 2; 9538c2ecf20Sopenharmony_ci size &= 3; 9548c2ecf20Sopenharmony_ci if (words && (paddr & 31)) { 9558c2ecf20Sopenharmony_ci init = 8-((paddr & 31) >> 2); 9568c2ecf20Sopenharmony_ci if (init > words) init = words; 9578c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d/%d words\n", 9588c2ecf20Sopenharmony_ci (unsigned long) paddr,init,words); 9598c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | 9608c2ecf20Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 9618c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 9628c2ecf20Sopenharmony_ci paddr += init << 2; 9638c2ecf20Sopenharmony_ci words -= init; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W /* may work with some PCI chipsets ... */ 9668c2ecf20Sopenharmony_ci if (words & ~15) { 9678c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*16/%d words\n", 9688c2ecf20Sopenharmony_ci (unsigned long) paddr,words >> 4,words); 9698c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) 9708c2ecf20Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 9718c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 9728c2ecf20Sopenharmony_ci paddr += (words & ~15) << 2; 9738c2ecf20Sopenharmony_ci words &= 15; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci#endif 9768c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W /* recommended */ 9778c2ecf20Sopenharmony_ci if (words & ~7) { 9788c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*8/%d words\n", 9798c2ecf20Sopenharmony_ci (unsigned long) paddr,words >> 3,words); 9808c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) 9818c2ecf20Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 9828c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 9838c2ecf20Sopenharmony_ci paddr += (words & ~7) << 2; 9848c2ecf20Sopenharmony_ci words &= 7; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci#endif 9878c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W /* probably useless if TX_8W or TX_16W */ 9888c2ecf20Sopenharmony_ci if (words & ~3) { 9898c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*4/%d words\n", 9908c2ecf20Sopenharmony_ci (unsigned long) paddr,words >> 2,words); 9918c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) 9928c2ecf20Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 9938c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 9948c2ecf20Sopenharmony_ci paddr += (words & ~3) << 2; 9958c2ecf20Sopenharmony_ci words &= 3; 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci#endif 9988c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W /* probably useless if TX_4W, TX_8W, ... */ 9998c2ecf20Sopenharmony_ci if (words & ~1) { 10008c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d*2/%d words\n", 10018c2ecf20Sopenharmony_ci (unsigned long) paddr,words >> 1,words); 10028c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) 10038c2ecf20Sopenharmony_ci | (chan << MID_DMA_CHAN_SHIFT); 10048c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 10058c2ecf20Sopenharmony_ci paddr += (words & ~1) << 2; 10068c2ecf20Sopenharmony_ci words &= 1; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci#endif 10098c2ecf20Sopenharmony_ci if (words) { 10108c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d words\n",(unsigned long) paddr, 10118c2ecf20Sopenharmony_ci words); 10128c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | 10138c2ecf20Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 10148c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 10158c2ecf20Sopenharmony_ci paddr += words << 2; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci if (size) { 10188c2ecf20Sopenharmony_ci DPRINTK("put_dma: %lx DMA: %d bytes\n",(unsigned long) paddr, 10198c2ecf20Sopenharmony_ci size); 10208c2ecf20Sopenharmony_ci dma[(*j)++] = MID_DT_BYTE | (size << MID_DMA_COUNT_SHIFT) | 10218c2ecf20Sopenharmony_ci (chan << MID_DMA_CHAN_SHIFT); 10228c2ecf20Sopenharmony_ci dma[(*j)++] = paddr; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic enum enq_res do_tx(struct sk_buff *skb) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 10308c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 10318c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 10328c2ecf20Sopenharmony_ci struct eni_tx *tx; 10338c2ecf20Sopenharmony_ci dma_addr_t paddr; 10348c2ecf20Sopenharmony_ci u32 dma_rd,dma_wr; 10358c2ecf20Sopenharmony_ci u32 size; /* in words */ 10368c2ecf20Sopenharmony_ci int aal5,dma_size,i,j; 10378c2ecf20Sopenharmony_ci unsigned char skb_data3; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci DPRINTK(">do_tx\n"); 10408c2ecf20Sopenharmony_ci NULLCHECK(skb); 10418c2ecf20Sopenharmony_ci EVENT("do_tx: skb=0x%lx, %ld bytes\n",(unsigned long) skb,skb->len); 10428c2ecf20Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 10438c2ecf20Sopenharmony_ci NULLCHECK(vcc); 10448c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 10458c2ecf20Sopenharmony_ci NULLCHECK(eni_dev); 10468c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 10478c2ecf20Sopenharmony_ci tx = eni_vcc->tx; 10488c2ecf20Sopenharmony_ci NULLCHECK(tx); 10498c2ecf20Sopenharmony_ci#if 0 /* Enable this for testing with the "align" program */ 10508c2ecf20Sopenharmony_ci { 10518c2ecf20Sopenharmony_ci unsigned int hack = *((char *) skb->data)-'0'; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (hack < 8) { 10548c2ecf20Sopenharmony_ci skb->data += hack; 10558c2ecf20Sopenharmony_ci skb->len -= hack; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci#endif 10598c2ecf20Sopenharmony_ci#if 0 /* should work now */ 10608c2ecf20Sopenharmony_ci if ((unsigned long) skb->data & 3) 10618c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): VCI %d has mis-aligned " 10628c2ecf20Sopenharmony_ci "TX data\n",vcc->dev->number,vcc->vci); 10638c2ecf20Sopenharmony_ci#endif 10648c2ecf20Sopenharmony_ci /* 10658c2ecf20Sopenharmony_ci * Potential future IP speedup: make hard_header big enough to put 10668c2ecf20Sopenharmony_ci * segmentation descriptor directly into PDU. Saves: 4 slave writes, 10678c2ecf20Sopenharmony_ci * 1 DMA xfer & 2 DMA'ed bytes (protocol layering is for wimps :-) 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci aal5 = vcc->qos.aal == ATM_AAL5; 10718c2ecf20Sopenharmony_ci /* check space in buffer */ 10728c2ecf20Sopenharmony_ci if (!aal5) 10738c2ecf20Sopenharmony_ci size = (ATM_CELL_PAYLOAD >> 2)+TX_DESCR_SIZE; 10748c2ecf20Sopenharmony_ci /* cell without HEC plus segmentation header (includes 10758c2ecf20Sopenharmony_ci four-byte cell header) */ 10768c2ecf20Sopenharmony_ci else { 10778c2ecf20Sopenharmony_ci size = skb->len+4*AAL5_TRAILER+ATM_CELL_PAYLOAD-1; 10788c2ecf20Sopenharmony_ci /* add AAL5 trailer */ 10798c2ecf20Sopenharmony_ci size = ((size-(size % ATM_CELL_PAYLOAD)) >> 2)+TX_DESCR_SIZE; 10808c2ecf20Sopenharmony_ci /* add segmentation header */ 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * Can I move tx_pos by size bytes without getting closer than TX_GAP 10848c2ecf20Sopenharmony_ci * to the read pointer ? TX_GAP means to leave some space for what 10858c2ecf20Sopenharmony_ci * the manual calls "too close". 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ci if (!NEPMOK(tx->tx_pos,size+TX_GAP, 10888c2ecf20Sopenharmony_ci eni_in(MID_TX_RDPTR(tx->index)),tx->words)) { 10898c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): TX full (size %d)\n", 10908c2ecf20Sopenharmony_ci vcc->dev->number,size); 10918c2ecf20Sopenharmony_ci return enq_next; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci /* check DMA */ 10948c2ecf20Sopenharmony_ci dma_wr = eni_in(MID_DMA_WR_TX); 10958c2ecf20Sopenharmony_ci dma_rd = eni_in(MID_DMA_RD_TX); 10968c2ecf20Sopenharmony_ci dma_size = 3; /* JK for descriptor and final fill, plus final size 10978c2ecf20Sopenharmony_ci mis-alignment fix */ 10988c2ecf20Sopenharmony_ciDPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags); 10998c2ecf20Sopenharmony_ci if (!skb_shinfo(skb)->nr_frags) dma_size += 5; 11008c2ecf20Sopenharmony_ci else dma_size += 5*(skb_shinfo(skb)->nr_frags+1); 11018c2ecf20Sopenharmony_ci if (dma_size > TX_DMA_BUF) { 11028c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): needs %d DMA entries " 11038c2ecf20Sopenharmony_ci "(got only %d)\n",vcc->dev->number,dma_size,TX_DMA_BUF); 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci DPRINTK("dma_wr is %d, tx_pos is %ld\n",dma_wr,tx->tx_pos); 11068c2ecf20Sopenharmony_ci if (dma_wr != dma_rd && ((dma_rd+NR_DMA_TX-dma_wr) & (NR_DMA_TX-1)) < 11078c2ecf20Sopenharmony_ci dma_size) { 11088c2ecf20Sopenharmony_ci printk(KERN_WARNING DEV_LABEL "(itf %d): TX DMA full\n", 11098c2ecf20Sopenharmony_ci vcc->dev->number); 11108c2ecf20Sopenharmony_ci return enq_jam; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci skb_data3 = skb->data[3]; 11138c2ecf20Sopenharmony_ci paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, 11148c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 11158c2ecf20Sopenharmony_ci if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) 11168c2ecf20Sopenharmony_ci return enq_next; 11178c2ecf20Sopenharmony_ci ENI_PRV_PADDR(skb) = paddr; 11188c2ecf20Sopenharmony_ci /* prepare DMA queue entries */ 11198c2ecf20Sopenharmony_ci j = 0; 11208c2ecf20Sopenharmony_ci eni_dev->dma[j++] = (((tx->tx_pos+TX_DESCR_SIZE) & (tx->words-1)) << 11218c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | 11228c2ecf20Sopenharmony_ci MID_DT_JK; 11238c2ecf20Sopenharmony_ci j++; 11248c2ecf20Sopenharmony_ci if (!skb_shinfo(skb)->nr_frags) 11258c2ecf20Sopenharmony_ci if (aal5) put_dma(tx->index,eni_dev->dma,&j,paddr,skb->len); 11268c2ecf20Sopenharmony_ci else put_dma(tx->index,eni_dev->dma,&j,paddr+4,skb->len-4); 11278c2ecf20Sopenharmony_ci else { 11288c2ecf20Sopenharmony_ciDPRINTK("doing direct send\n"); /* @@@ well, this doesn't work anyway */ 11298c2ecf20Sopenharmony_ci for (i = -1; i < skb_shinfo(skb)->nr_frags; i++) 11308c2ecf20Sopenharmony_ci if (i == -1) 11318c2ecf20Sopenharmony_ci put_dma(tx->index,eni_dev->dma,&j,(unsigned long) 11328c2ecf20Sopenharmony_ci skb->data, 11338c2ecf20Sopenharmony_ci skb_headlen(skb)); 11348c2ecf20Sopenharmony_ci else 11358c2ecf20Sopenharmony_ci put_dma(tx->index,eni_dev->dma,&j,(unsigned long) 11368c2ecf20Sopenharmony_ci skb_frag_page(&skb_shinfo(skb)->frags[i]) + 11378c2ecf20Sopenharmony_ci skb_frag_off(&skb_shinfo(skb)->frags[i]), 11388c2ecf20Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[i])); 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci if (skb->len & 3) { 11418c2ecf20Sopenharmony_ci put_dma(tx->index, eni_dev->dma, &j, eni_dev->zero.dma, 11428c2ecf20Sopenharmony_ci 4 - (skb->len & 3)); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci /* JK for AAL5 trailer - AAL0 doesn't need it, but who cares ... */ 11458c2ecf20Sopenharmony_ci eni_dev->dma[j++] = (((tx->tx_pos+size) & (tx->words-1)) << 11468c2ecf20Sopenharmony_ci MID_DMA_COUNT_SHIFT) | (tx->index << MID_DMA_CHAN_SHIFT) | 11478c2ecf20Sopenharmony_ci MID_DMA_END | MID_DT_JK; 11488c2ecf20Sopenharmony_ci j++; 11498c2ecf20Sopenharmony_ci DPRINTK("DMA at end: %d\n",j); 11508c2ecf20Sopenharmony_ci /* store frame */ 11518c2ecf20Sopenharmony_ci writel((MID_SEG_TX_ID << MID_SEG_ID_SHIFT) | 11528c2ecf20Sopenharmony_ci (aal5 ? MID_SEG_AAL5 : 0) | (tx->prescaler << MID_SEG_PR_SHIFT) | 11538c2ecf20Sopenharmony_ci (tx->resolution << MID_SEG_RATE_SHIFT) | 11548c2ecf20Sopenharmony_ci (size/(ATM_CELL_PAYLOAD/4)),tx->send+tx->tx_pos*4); 11558c2ecf20Sopenharmony_ci/*printk("dsc = 0x%08lx\n",(unsigned long) readl(tx->send+tx->tx_pos*4));*/ 11568c2ecf20Sopenharmony_ci writel((vcc->vci << MID_SEG_VCI_SHIFT) | 11578c2ecf20Sopenharmony_ci (aal5 ? 0 : (skb_data3 & 0xf)) | 11588c2ecf20Sopenharmony_ci (ATM_SKB(skb)->atm_options & ATM_ATMOPT_CLP ? MID_SEG_CLP : 0), 11598c2ecf20Sopenharmony_ci tx->send+((tx->tx_pos+1) & (tx->words-1))*4); 11608c2ecf20Sopenharmony_ci DPRINTK("size: %d, len:%d\n",size,skb->len); 11618c2ecf20Sopenharmony_ci if (aal5) 11628c2ecf20Sopenharmony_ci writel(skb->len,tx->send+ 11638c2ecf20Sopenharmony_ci ((tx->tx_pos+size-AAL5_TRAILER) & (tx->words-1))*4); 11648c2ecf20Sopenharmony_ci j = j >> 1; 11658c2ecf20Sopenharmony_ci for (i = 0; i < j; i++) { 11668c2ecf20Sopenharmony_ci writel(eni_dev->dma[i*2],eni_dev->tx_dma+dma_wr*8); 11678c2ecf20Sopenharmony_ci writel(eni_dev->dma[i*2+1],eni_dev->tx_dma+dma_wr*8+4); 11688c2ecf20Sopenharmony_ci dma_wr = (dma_wr+1) & (NR_DMA_TX-1); 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci ENI_PRV_POS(skb) = tx->tx_pos; 11718c2ecf20Sopenharmony_ci ENI_PRV_SIZE(skb) = size; 11728c2ecf20Sopenharmony_ci ENI_VCC(vcc)->txing += size; 11738c2ecf20Sopenharmony_ci tx->tx_pos = (tx->tx_pos+size) & (tx->words-1); 11748c2ecf20Sopenharmony_ci DPRINTK("dma_wr set to %d, tx_pos is now %ld\n",dma_wr,tx->tx_pos); 11758c2ecf20Sopenharmony_ci eni_out(dma_wr,MID_DMA_WR_TX); 11768c2ecf20Sopenharmony_ci skb_queue_tail(&eni_dev->tx_queue,skb); 11778c2ecf20Sopenharmony_ci queued++; 11788c2ecf20Sopenharmony_ci return enq_ok; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cistatic void poll_tx(struct atm_dev *dev) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct eni_tx *tx; 11858c2ecf20Sopenharmony_ci struct sk_buff *skb; 11868c2ecf20Sopenharmony_ci enum enq_res res; 11878c2ecf20Sopenharmony_ci int i; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci DPRINTK(">poll_tx\n"); 11908c2ecf20Sopenharmony_ci for (i = NR_CHAN-1; i >= 0; i--) { 11918c2ecf20Sopenharmony_ci tx = &ENI_DEV(dev)->tx[i]; 11928c2ecf20Sopenharmony_ci if (tx->send) 11938c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&tx->backlog))) { 11948c2ecf20Sopenharmony_ci res = do_tx(skb); 11958c2ecf20Sopenharmony_ci if (res == enq_ok) continue; 11968c2ecf20Sopenharmony_ci DPRINTK("re-queuing TX PDU\n"); 11978c2ecf20Sopenharmony_ci skb_queue_head(&tx->backlog,skb); 11988c2ecf20Sopenharmony_ci requeued++; 11998c2ecf20Sopenharmony_ci if (res == enq_jam) return; 12008c2ecf20Sopenharmony_ci break; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic void dequeue_tx(struct atm_dev *dev) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 12098c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 12108c2ecf20Sopenharmony_ci struct sk_buff *skb; 12118c2ecf20Sopenharmony_ci struct eni_tx *tx; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci NULLCHECK(dev); 12148c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 12158c2ecf20Sopenharmony_ci NULLCHECK(eni_dev); 12168c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&eni_dev->tx_queue))) { 12178c2ecf20Sopenharmony_ci vcc = ATM_SKB(skb)->vcc; 12188c2ecf20Sopenharmony_ci NULLCHECK(vcc); 12198c2ecf20Sopenharmony_ci tx = ENI_VCC(vcc)->tx; 12208c2ecf20Sopenharmony_ci NULLCHECK(ENI_VCC(vcc)->tx); 12218c2ecf20Sopenharmony_ci DPRINTK("dequeue_tx: next 0x%lx curr 0x%x\n",ENI_PRV_POS(skb), 12228c2ecf20Sopenharmony_ci (unsigned) eni_in(MID_TX_DESCRSTART(tx->index))); 12238c2ecf20Sopenharmony_ci if (ENI_VCC(vcc)->txing < tx->words && ENI_PRV_POS(skb) == 12248c2ecf20Sopenharmony_ci eni_in(MID_TX_DESCRSTART(tx->index))) { 12258c2ecf20Sopenharmony_ci skb_queue_head(&eni_dev->tx_queue,skb); 12268c2ecf20Sopenharmony_ci break; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); 12298c2ecf20Sopenharmony_ci dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, 12308c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12318c2ecf20Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 12328c2ecf20Sopenharmony_ci else dev_kfree_skb_irq(skb); 12338c2ecf20Sopenharmony_ci atomic_inc(&vcc->stats->tx); 12348c2ecf20Sopenharmony_ci wake_up(&eni_dev->tx_wait); 12358c2ecf20Sopenharmony_ci dma_complete++; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic struct eni_tx *alloc_tx(struct eni_dev *eni_dev,int ubr) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci int i; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci for (i = !ubr; i < NR_CHAN; i++) 12458c2ecf20Sopenharmony_ci if (!eni_dev->tx[i].send) return eni_dev->tx+i; 12468c2ecf20Sopenharmony_ci return NULL; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic int comp_tx(struct eni_dev *eni_dev,int *pcr,int reserved,int *pre, 12518c2ecf20Sopenharmony_ci int *res,int unlimited) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci static const int pre_div[] = { 4,16,128,2048 }; 12548c2ecf20Sopenharmony_ci /* 2^(((x+2)^2-(x+2))/2+1) */ 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (unlimited) *pre = *res = 0; 12578c2ecf20Sopenharmony_ci else { 12588c2ecf20Sopenharmony_ci if (*pcr > 0) { 12598c2ecf20Sopenharmony_ci int div; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci for (*pre = 0; *pre < 3; (*pre)++) 12628c2ecf20Sopenharmony_ci if (TS_CLOCK/pre_div[*pre]/64 <= *pcr) break; 12638c2ecf20Sopenharmony_ci div = pre_div[*pre]**pcr; 12648c2ecf20Sopenharmony_ci DPRINTK("min div %d\n",div); 12658c2ecf20Sopenharmony_ci *res = TS_CLOCK/div-1; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci else { 12688c2ecf20Sopenharmony_ci int div; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (!*pcr) *pcr = eni_dev->tx_bw+reserved; 12718c2ecf20Sopenharmony_ci for (*pre = 3; *pre >= 0; (*pre)--) 12728c2ecf20Sopenharmony_ci if (TS_CLOCK/pre_div[*pre]/64 > -*pcr) break; 12738c2ecf20Sopenharmony_ci if (*pre < 3) (*pre)++; /* else fail later */ 12748c2ecf20Sopenharmony_ci div = pre_div[*pre]*-*pcr; 12758c2ecf20Sopenharmony_ci DPRINTK("max div %d\n",div); 12768c2ecf20Sopenharmony_ci *res = DIV_ROUND_UP(TS_CLOCK, div)-1; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci if (*res < 0) *res = 0; 12798c2ecf20Sopenharmony_ci if (*res > MID_SEG_MAX_RATE) *res = MID_SEG_MAX_RATE; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci *pcr = TS_CLOCK/pre_div[*pre]/(*res+1); 12828c2ecf20Sopenharmony_ci DPRINTK("out pcr: %d (%d:%d)\n",*pcr,*pre,*res); 12838c2ecf20Sopenharmony_ci return 0; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, 12888c2ecf20Sopenharmony_ci int set_rsv,int set_shp) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(vcc->dev); 12918c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc = ENI_VCC(vcc); 12928c2ecf20Sopenharmony_ci struct eni_tx *tx; 12938c2ecf20Sopenharmony_ci unsigned long size; 12948c2ecf20Sopenharmony_ci void __iomem *mem; 12958c2ecf20Sopenharmony_ci int rate,ubr,unlimited,new_tx; 12968c2ecf20Sopenharmony_ci int pre,res,order; 12978c2ecf20Sopenharmony_ci int error; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci rate = atm_pcr_goal(txtp); 13008c2ecf20Sopenharmony_ci ubr = txtp->traffic_class == ATM_UBR; 13018c2ecf20Sopenharmony_ci unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || 13028c2ecf20Sopenharmony_ci rate >= ATM_OC3_PCR); 13038c2ecf20Sopenharmony_ci if (!unlimited) { 13048c2ecf20Sopenharmony_ci size = txtp->max_sdu*eni_dev->tx_mult/100; 13058c2ecf20Sopenharmony_ci if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <= 13068c2ecf20Sopenharmony_ci MID_MAX_BUF_SIZE) 13078c2ecf20Sopenharmony_ci size = MID_MAX_BUF_SIZE; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci else { 13108c2ecf20Sopenharmony_ci if (eni_dev->ubr) { 13118c2ecf20Sopenharmony_ci eni_vcc->tx = eni_dev->ubr; 13128c2ecf20Sopenharmony_ci txtp->pcr = ATM_OC3_PCR; 13138c2ecf20Sopenharmony_ci return 0; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci size = UBR_BUFFER; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci new_tx = !eni_vcc->tx; 13188c2ecf20Sopenharmony_ci mem = NULL; /* for gcc */ 13198c2ecf20Sopenharmony_ci if (!new_tx) tx = eni_vcc->tx; 13208c2ecf20Sopenharmony_ci else { 13218c2ecf20Sopenharmony_ci mem = eni_alloc_mem(eni_dev,&size); 13228c2ecf20Sopenharmony_ci if (!mem) return -ENOBUFS; 13238c2ecf20Sopenharmony_ci tx = alloc_tx(eni_dev,unlimited); 13248c2ecf20Sopenharmony_ci if (!tx) { 13258c2ecf20Sopenharmony_ci eni_free_mem(eni_dev,mem,size); 13268c2ecf20Sopenharmony_ci return -EBUSY; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci DPRINTK("got chan %d\n",tx->index); 13298c2ecf20Sopenharmony_ci tx->reserved = tx->shaping = 0; 13308c2ecf20Sopenharmony_ci tx->send = mem; 13318c2ecf20Sopenharmony_ci tx->words = size >> 2; 13328c2ecf20Sopenharmony_ci skb_queue_head_init(&tx->backlog); 13338c2ecf20Sopenharmony_ci for (order = 0; size > (1 << (order+10)); order++); 13348c2ecf20Sopenharmony_ci eni_out((order << MID_SIZE_SHIFT) | 13358c2ecf20Sopenharmony_ci ((tx->send-eni_dev->ram) >> (MID_LOC_SKIP+2)), 13368c2ecf20Sopenharmony_ci MID_TX_PLACE(tx->index)); 13378c2ecf20Sopenharmony_ci tx->tx_pos = eni_in(MID_TX_DESCRSTART(tx->index)) & 13388c2ecf20Sopenharmony_ci MID_DESCR_START; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci error = comp_tx(eni_dev,&rate,tx->reserved,&pre,&res,unlimited); 13418c2ecf20Sopenharmony_ci if (!error && txtp->min_pcr > rate) error = -EINVAL; 13428c2ecf20Sopenharmony_ci if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && 13438c2ecf20Sopenharmony_ci txtp->max_pcr < rate) error = -EINVAL; 13448c2ecf20Sopenharmony_ci if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) 13458c2ecf20Sopenharmony_ci error = -EINVAL; 13468c2ecf20Sopenharmony_ci if (!error && set_rsv && !set_shp && rate < tx->shaping) 13478c2ecf20Sopenharmony_ci error = -EINVAL; 13488c2ecf20Sopenharmony_ci if (!error && !set_rsv && rate > tx->reserved && !ubr) 13498c2ecf20Sopenharmony_ci error = -EINVAL; 13508c2ecf20Sopenharmony_ci if (error) { 13518c2ecf20Sopenharmony_ci if (new_tx) { 13528c2ecf20Sopenharmony_ci tx->send = NULL; 13538c2ecf20Sopenharmony_ci eni_free_mem(eni_dev,mem,size); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci return error; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci txtp->pcr = rate; 13588c2ecf20Sopenharmony_ci if (set_rsv && !ubr) { 13598c2ecf20Sopenharmony_ci eni_dev->tx_bw += tx->reserved; 13608c2ecf20Sopenharmony_ci tx->reserved = rate; 13618c2ecf20Sopenharmony_ci eni_dev->tx_bw -= rate; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci if (set_shp || (unlimited && new_tx)) { 13648c2ecf20Sopenharmony_ci if (unlimited && new_tx) eni_dev->ubr = tx; 13658c2ecf20Sopenharmony_ci tx->prescaler = pre; 13668c2ecf20Sopenharmony_ci tx->resolution = res; 13678c2ecf20Sopenharmony_ci tx->shaping = rate; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci if (set_shp) eni_vcc->tx = tx; 13708c2ecf20Sopenharmony_ci DPRINTK("rsv %d shp %d\n",tx->reserved,tx->shaping); 13718c2ecf20Sopenharmony_ci return 0; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic int open_tx_first(struct atm_vcc *vcc) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci ENI_VCC(vcc)->tx = NULL; 13788c2ecf20Sopenharmony_ci if (vcc->qos.txtp.traffic_class == ATM_NONE) return 0; 13798c2ecf20Sopenharmony_ci ENI_VCC(vcc)->txing = 0; 13808c2ecf20Sopenharmony_ci return reserve_or_set_tx(vcc,&vcc->qos.txtp,1,1); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int open_tx_second(struct atm_vcc *vcc) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci return 0; /* nothing to do */ 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void close_tx(struct atm_vcc *vcc) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait,current); 13938c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 13948c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 13978c2ecf20Sopenharmony_ci if (!eni_vcc->tx) return; 13988c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(vcc->dev); 13998c2ecf20Sopenharmony_ci /* wait for TX queue to drain */ 14008c2ecf20Sopenharmony_ci DPRINTK("eni_close: waiting for TX ...\n"); 14018c2ecf20Sopenharmony_ci add_wait_queue(&eni_dev->tx_wait,&wait); 14028c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 14038c2ecf20Sopenharmony_ci for (;;) { 14048c2ecf20Sopenharmony_ci int txing; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci tasklet_disable(&eni_dev->task); 14078c2ecf20Sopenharmony_ci txing = skb_peek(&eni_vcc->tx->backlog) || eni_vcc->txing; 14088c2ecf20Sopenharmony_ci tasklet_enable(&eni_dev->task); 14098c2ecf20Sopenharmony_ci if (!txing) break; 14108c2ecf20Sopenharmony_ci DPRINTK("%d TX left\n",eni_vcc->txing); 14118c2ecf20Sopenharmony_ci schedule(); 14128c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 14158c2ecf20Sopenharmony_ci remove_wait_queue(&eni_dev->tx_wait,&wait); 14168c2ecf20Sopenharmony_ci if (eni_vcc->tx != eni_dev->ubr) { 14178c2ecf20Sopenharmony_ci /* 14188c2ecf20Sopenharmony_ci * Looping a few times in here is probably far cheaper than 14198c2ecf20Sopenharmony_ci * keeping track of TX completions all the time, so let's poll 14208c2ecf20Sopenharmony_ci * a bit ... 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ci while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) != 14238c2ecf20Sopenharmony_ci eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index))) 14248c2ecf20Sopenharmony_ci schedule(); 14258c2ecf20Sopenharmony_ci eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2); 14268c2ecf20Sopenharmony_ci eni_vcc->tx->send = NULL; 14278c2ecf20Sopenharmony_ci eni_dev->tx_bw += eni_vcc->tx->reserved; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci eni_vcc->tx = NULL; 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic int start_tx(struct atm_dev *dev) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 14368c2ecf20Sopenharmony_ci int i; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 14398c2ecf20Sopenharmony_ci eni_dev->lost = 0; 14408c2ecf20Sopenharmony_ci eni_dev->tx_bw = ATM_OC3_PCR; 14418c2ecf20Sopenharmony_ci eni_dev->tx_mult = DEFAULT_TX_MULT; 14428c2ecf20Sopenharmony_ci init_waitqueue_head(&eni_dev->tx_wait); 14438c2ecf20Sopenharmony_ci eni_dev->ubr = NULL; 14448c2ecf20Sopenharmony_ci skb_queue_head_init(&eni_dev->tx_queue); 14458c2ecf20Sopenharmony_ci eni_out(0,MID_DMA_WR_TX); 14468c2ecf20Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) { 14478c2ecf20Sopenharmony_ci eni_dev->tx[i].send = NULL; 14488c2ecf20Sopenharmony_ci eni_dev->tx[i].index = i; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci/*--------------------------------- common ----------------------------------*/ 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci#if 0 /* may become useful again when tuning things */ 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic void foo(void) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ciprintk(KERN_INFO 14628c2ecf20Sopenharmony_ci "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n" 14638c2ecf20Sopenharmony_ci "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n", 14648c2ecf20Sopenharmony_ci tx_complete,dma_complete,queued,requeued,submitted,backlogged, 14658c2ecf20Sopenharmony_ci rx_enqueued,rx_dequeued,putting,pushed); 14668c2ecf20Sopenharmony_ciif (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost); 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci#endif 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic void bug_int(struct atm_dev *dev,unsigned long reason) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci DPRINTK(">bug_int\n"); 14758c2ecf20Sopenharmony_ci if (reason & MID_DMA_ERR_ACK) 14768c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " 14778c2ecf20Sopenharmony_ci "error\n",dev->number); 14788c2ecf20Sopenharmony_ci if (reason & MID_TX_IDENT_MISM) 14798c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident " 14808c2ecf20Sopenharmony_ci "mismatch\n",dev->number); 14818c2ecf20Sopenharmony_ci if (reason & MID_TX_DMA_OVFL) 14828c2ecf20Sopenharmony_ci printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " 14838c2ecf20Sopenharmony_ci "overflow\n",dev->number); 14848c2ecf20Sopenharmony_ci EVENT("---dump ends here---\n",0,0); 14858c2ecf20Sopenharmony_ci printk(KERN_NOTICE "---recent events---\n"); 14868c2ecf20Sopenharmony_ci event_dump(); 14878c2ecf20Sopenharmony_ci} 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic irqreturn_t eni_int(int irq,void *dev_id) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci struct atm_dev *dev; 14938c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 14948c2ecf20Sopenharmony_ci u32 reason; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci DPRINTK(">eni_int\n"); 14978c2ecf20Sopenharmony_ci dev = dev_id; 14988c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 14998c2ecf20Sopenharmony_ci reason = eni_in(MID_ISA); 15008c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason); 15018c2ecf20Sopenharmony_ci /* 15028c2ecf20Sopenharmony_ci * Must handle these two right now, because reading ISA doesn't clear 15038c2ecf20Sopenharmony_ci * them, so they re-occur and we never make it to the tasklet. Since 15048c2ecf20Sopenharmony_ci * they're rare, we don't mind the occasional invocation of eni_tasklet 15058c2ecf20Sopenharmony_ci * with eni_dev->events == 0. 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_ci if (reason & MID_STAT_OVFL) { 15088c2ecf20Sopenharmony_ci EVENT("stat overflow\n",0,0); 15098c2ecf20Sopenharmony_ci eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci if (reason & MID_SUNI_INT) { 15128c2ecf20Sopenharmony_ci EVENT("SUNI int\n",0,0); 15138c2ecf20Sopenharmony_ci dev->phy->interrupt(dev); 15148c2ecf20Sopenharmony_ci#if 0 15158c2ecf20Sopenharmony_ci foo(); 15168c2ecf20Sopenharmony_ci#endif 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci spin_lock(&eni_dev->lock); 15198c2ecf20Sopenharmony_ci eni_dev->events |= reason; 15208c2ecf20Sopenharmony_ci spin_unlock(&eni_dev->lock); 15218c2ecf20Sopenharmony_ci tasklet_schedule(&eni_dev->task); 15228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_cistatic void eni_tasklet(unsigned long data) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci struct atm_dev *dev = (struct atm_dev *) data; 15298c2ecf20Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 15308c2ecf20Sopenharmony_ci unsigned long flags; 15318c2ecf20Sopenharmony_ci u32 events; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci DPRINTK("eni_tasklet (dev %p)\n",dev); 15348c2ecf20Sopenharmony_ci spin_lock_irqsave(&eni_dev->lock,flags); 15358c2ecf20Sopenharmony_ci events = xchg(&eni_dev->events,0); 15368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&eni_dev->lock,flags); 15378c2ecf20Sopenharmony_ci if (events & MID_RX_DMA_COMPLETE) { 15388c2ecf20Sopenharmony_ci EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0); 15398c2ecf20Sopenharmony_ci dequeue_rx(dev); 15408c2ecf20Sopenharmony_ci EVENT("dequeue_rx done, starting poll_rx\n",0,0); 15418c2ecf20Sopenharmony_ci poll_rx(dev); 15428c2ecf20Sopenharmony_ci EVENT("poll_rx done\n",0,0); 15438c2ecf20Sopenharmony_ci /* poll_tx ? */ 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci if (events & MID_SERVICE) { 15468c2ecf20Sopenharmony_ci EVENT("INT: service, starting get_service\n",0,0); 15478c2ecf20Sopenharmony_ci get_service(dev); 15488c2ecf20Sopenharmony_ci EVENT("get_service done, starting poll_rx\n",0,0); 15498c2ecf20Sopenharmony_ci poll_rx(dev); 15508c2ecf20Sopenharmony_ci EVENT("poll_rx done\n",0,0); 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci if (events & MID_TX_DMA_COMPLETE) { 15538c2ecf20Sopenharmony_ci EVENT("INT: TX DMA COMPLETE\n",0,0); 15548c2ecf20Sopenharmony_ci dequeue_tx(dev); 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci if (events & MID_TX_COMPLETE) { 15578c2ecf20Sopenharmony_ci EVENT("INT: TX COMPLETE\n",0,0); 15588c2ecf20Sopenharmony_ci tx_complete++; 15598c2ecf20Sopenharmony_ci wake_up(&eni_dev->tx_wait); 15608c2ecf20Sopenharmony_ci /* poll_rx ? */ 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { 15638c2ecf20Sopenharmony_ci EVENT("bug interrupt\n",0,0); 15648c2ecf20Sopenharmony_ci bug_int(dev,events); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci poll_tx(dev); 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci/*--------------------------------- entries ---------------------------------*/ 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic char * const media_name[] = { 15748c2ecf20Sopenharmony_ci "MMF", "SMF", "MMF", "03?", /* 0- 3 */ 15758c2ecf20Sopenharmony_ci "UTP", "05?", "06?", "07?", /* 4- 7 */ 15768c2ecf20Sopenharmony_ci "TAXI","09?", "10?", "11?", /* 8-11 */ 15778c2ecf20Sopenharmony_ci "12?", "13?", "14?", "15?", /* 12-15 */ 15788c2ecf20Sopenharmony_ci "MMF", "SMF", "18?", "19?", /* 16-19 */ 15798c2ecf20Sopenharmony_ci "UTP", "21?", "22?", "23?", /* 20-23 */ 15808c2ecf20Sopenharmony_ci "24?", "25?", "26?", "27?", /* 24-27 */ 15818c2ecf20Sopenharmony_ci "28?", "29?", "30?", "31?" /* 28-31 */ 15828c2ecf20Sopenharmony_ci}; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci#define SET_SEPROM \ 15868c2ecf20Sopenharmony_ci ({ if (!error && !pci_error) { \ 15878c2ecf20Sopenharmony_ci pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ 15888c2ecf20Sopenharmony_ci udelay(10); /* 10 usecs */ \ 15898c2ecf20Sopenharmony_ci } }) 15908c2ecf20Sopenharmony_ci#define GET_SEPROM \ 15918c2ecf20Sopenharmony_ci ({ if (!error && !pci_error) { \ 15928c2ecf20Sopenharmony_ci pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ 15938c2ecf20Sopenharmony_ci udelay(10); /* 10 usecs */ \ 15948c2ecf20Sopenharmony_ci } }) 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic int get_esi_asic(struct atm_dev *dev) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 16008c2ecf20Sopenharmony_ci unsigned char tonga; 16018c2ecf20Sopenharmony_ci int error,failed,pci_error; 16028c2ecf20Sopenharmony_ci int address,i,j; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 16058c2ecf20Sopenharmony_ci error = pci_error = 0; 16068c2ecf20Sopenharmony_ci tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK; 16078c2ecf20Sopenharmony_ci SET_SEPROM; 16088c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN && !error && !pci_error; i++) { 16098c2ecf20Sopenharmony_ci /* start operation */ 16108c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16118c2ecf20Sopenharmony_ci SET_SEPROM; 16128c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16138c2ecf20Sopenharmony_ci SET_SEPROM; 16148c2ecf20Sopenharmony_ci tonga &= ~SEPROM_DATA; 16158c2ecf20Sopenharmony_ci SET_SEPROM; 16168c2ecf20Sopenharmony_ci tonga &= ~SEPROM_CLK; 16178c2ecf20Sopenharmony_ci SET_SEPROM; 16188c2ecf20Sopenharmony_ci /* send address */ 16198c2ecf20Sopenharmony_ci address = ((i+SEPROM_ESI_BASE) << 1)+1; 16208c2ecf20Sopenharmony_ci for (j = 7; j >= 0; j--) { 16218c2ecf20Sopenharmony_ci tonga = (address >> j) & 1 ? tonga | SEPROM_DATA : 16228c2ecf20Sopenharmony_ci tonga & ~SEPROM_DATA; 16238c2ecf20Sopenharmony_ci SET_SEPROM; 16248c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16258c2ecf20Sopenharmony_ci SET_SEPROM; 16268c2ecf20Sopenharmony_ci tonga &= ~SEPROM_CLK; 16278c2ecf20Sopenharmony_ci SET_SEPROM; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci /* get ack */ 16308c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16318c2ecf20Sopenharmony_ci SET_SEPROM; 16328c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16338c2ecf20Sopenharmony_ci SET_SEPROM; 16348c2ecf20Sopenharmony_ci GET_SEPROM; 16358c2ecf20Sopenharmony_ci failed = tonga & SEPROM_DATA; 16368c2ecf20Sopenharmony_ci tonga &= ~SEPROM_CLK; 16378c2ecf20Sopenharmony_ci SET_SEPROM; 16388c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16398c2ecf20Sopenharmony_ci SET_SEPROM; 16408c2ecf20Sopenharmony_ci if (failed) error = -EIO; 16418c2ecf20Sopenharmony_ci else { 16428c2ecf20Sopenharmony_ci dev->esi[i] = 0; 16438c2ecf20Sopenharmony_ci for (j = 7; j >= 0; j--) { 16448c2ecf20Sopenharmony_ci dev->esi[i] <<= 1; 16458c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16468c2ecf20Sopenharmony_ci SET_SEPROM; 16478c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16488c2ecf20Sopenharmony_ci SET_SEPROM; 16498c2ecf20Sopenharmony_ci GET_SEPROM; 16508c2ecf20Sopenharmony_ci if (tonga & SEPROM_DATA) dev->esi[i] |= 1; 16518c2ecf20Sopenharmony_ci tonga &= ~SEPROM_CLK; 16528c2ecf20Sopenharmony_ci SET_SEPROM; 16538c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16548c2ecf20Sopenharmony_ci SET_SEPROM; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci /* get ack */ 16578c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16588c2ecf20Sopenharmony_ci SET_SEPROM; 16598c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16608c2ecf20Sopenharmony_ci SET_SEPROM; 16618c2ecf20Sopenharmony_ci GET_SEPROM; 16628c2ecf20Sopenharmony_ci if (!(tonga & SEPROM_DATA)) error = -EIO; 16638c2ecf20Sopenharmony_ci tonga &= ~SEPROM_CLK; 16648c2ecf20Sopenharmony_ci SET_SEPROM; 16658c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16668c2ecf20Sopenharmony_ci SET_SEPROM; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci /* stop operation */ 16698c2ecf20Sopenharmony_ci tonga &= ~SEPROM_DATA; 16708c2ecf20Sopenharmony_ci SET_SEPROM; 16718c2ecf20Sopenharmony_ci tonga |= SEPROM_CLK; 16728c2ecf20Sopenharmony_ci SET_SEPROM; 16738c2ecf20Sopenharmony_ci tonga |= SEPROM_DATA; 16748c2ecf20Sopenharmony_ci SET_SEPROM; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci if (pci_error) { 16778c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI " 16788c2ecf20Sopenharmony_ci "(0x%02x)\n",dev->number,pci_error); 16798c2ecf20Sopenharmony_ci error = -EIO; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci return error; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci#undef SET_SEPROM 16868c2ecf20Sopenharmony_ci#undef GET_SEPROM 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cistatic int get_esi_fpga(struct atm_dev *dev, void __iomem *base) 16908c2ecf20Sopenharmony_ci{ 16918c2ecf20Sopenharmony_ci void __iomem *mac_base; 16928c2ecf20Sopenharmony_ci int i; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom); 16958c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3)); 16968c2ecf20Sopenharmony_ci return 0; 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic int eni_do_init(struct atm_dev *dev) 17018c2ecf20Sopenharmony_ci{ 17028c2ecf20Sopenharmony_ci struct midway_eprom __iomem *eprom; 17038c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 17048c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 17058c2ecf20Sopenharmony_ci unsigned long real_base; 17068c2ecf20Sopenharmony_ci void __iomem *base; 17078c2ecf20Sopenharmony_ci int error,i,last; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci DPRINTK(">eni_init\n"); 17108c2ecf20Sopenharmony_ci dev->ci_range.vpi_bits = 0; 17118c2ecf20Sopenharmony_ci dev->ci_range.vci_bits = NR_VCI_LD; 17128c2ecf20Sopenharmony_ci dev->link_rate = ATM_OC3_PCR; 17138c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 17148c2ecf20Sopenharmony_ci pci_dev = eni_dev->pci_dev; 17158c2ecf20Sopenharmony_ci real_base = pci_resource_start(pci_dev, 0); 17168c2ecf20Sopenharmony_ci eni_dev->irq = pci_dev->irq; 17178c2ecf20Sopenharmony_ci if ((error = pci_write_config_word(pci_dev,PCI_COMMAND, 17188c2ecf20Sopenharmony_ci PCI_COMMAND_MEMORY | 17198c2ecf20Sopenharmony_ci (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { 17208c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory " 17218c2ecf20Sopenharmony_ci "(0x%02x)\n",dev->number,error); 17228c2ecf20Sopenharmony_ci return -EIO; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,", 17258c2ecf20Sopenharmony_ci dev->number,pci_dev->revision,real_base,eni_dev->irq); 17268c2ecf20Sopenharmony_ci if (!(base = ioremap(real_base,MAP_MAX_SIZE))) { 17278c2ecf20Sopenharmony_ci printk("\n"); 17288c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page " 17298c2ecf20Sopenharmony_ci "mapping\n",dev->number); 17308c2ecf20Sopenharmony_ci return -ENOMEM; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci eni_dev->ioaddr = base; 17338c2ecf20Sopenharmony_ci eni_dev->base_diff = real_base - (unsigned long) base; 17348c2ecf20Sopenharmony_ci /* id may not be present in ASIC Tonga boards - check this @@@ */ 17358c2ecf20Sopenharmony_ci if (!eni_dev->asic) { 17368c2ecf20Sopenharmony_ci eprom = (base+EPROM_SIZE-sizeof(struct midway_eprom)); 17378c2ecf20Sopenharmony_ci if (readl(&eprom->magic) != ENI155_MAGIC) { 17388c2ecf20Sopenharmony_ci printk("\n"); 17398c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL 17408c2ecf20Sopenharmony_ci "(itf %d): bad magic - expected 0x%x, got 0x%x\n", 17418c2ecf20Sopenharmony_ci dev->number, ENI155_MAGIC, 17428c2ecf20Sopenharmony_ci (unsigned)readl(&eprom->magic)); 17438c2ecf20Sopenharmony_ci error = -EINVAL; 17448c2ecf20Sopenharmony_ci goto unmap; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci eni_dev->phy = base+PHY_BASE; 17488c2ecf20Sopenharmony_ci eni_dev->reg = base+REG_BASE; 17498c2ecf20Sopenharmony_ci eni_dev->ram = base+RAM_BASE; 17508c2ecf20Sopenharmony_ci last = MAP_MAX_SIZE-RAM_BASE; 17518c2ecf20Sopenharmony_ci for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) { 17528c2ecf20Sopenharmony_ci writel(0x55555555,eni_dev->ram+i); 17538c2ecf20Sopenharmony_ci if (readl(eni_dev->ram+i) != 0x55555555) last = i; 17548c2ecf20Sopenharmony_ci else { 17558c2ecf20Sopenharmony_ci writel(0xAAAAAAAA,eni_dev->ram+i); 17568c2ecf20Sopenharmony_ci if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; 17578c2ecf20Sopenharmony_ci else writel(i,eni_dev->ram+i); 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci for (i = 0; i < last; i += RAM_INCREMENT) 17618c2ecf20Sopenharmony_ci if (readl(eni_dev->ram+i) != i) break; 17628c2ecf20Sopenharmony_ci eni_dev->mem = i; 17638c2ecf20Sopenharmony_ci memset_io(eni_dev->ram,0,eni_dev->mem); 17648c2ecf20Sopenharmony_ci /* TODO: should shrink allocation now */ 17658c2ecf20Sopenharmony_ci printk("mem=%dkB (",eni_dev->mem >> 10); 17668c2ecf20Sopenharmony_ci /* TODO: check for non-SUNI, check for TAXI ? */ 17678c2ecf20Sopenharmony_ci if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { 17688c2ecf20Sopenharmony_ci printk(")\n"); 17698c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n", 17708c2ecf20Sopenharmony_ci dev->number,(unsigned) eni_in(MID_RES_ID_MCON)); 17718c2ecf20Sopenharmony_ci error = -EINVAL; 17728c2ecf20Sopenharmony_ci goto unmap; 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base); 17758c2ecf20Sopenharmony_ci if (error) 17768c2ecf20Sopenharmony_ci goto unmap; 17778c2ecf20Sopenharmony_ci for (i = 0; i < ESI_LEN; i++) 17788c2ecf20Sopenharmony_ci printk("%s%02X",i ? "-" : "",dev->esi[i]); 17798c2ecf20Sopenharmony_ci printk(")\n"); 17808c2ecf20Sopenharmony_ci printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number, 17818c2ecf20Sopenharmony_ci eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", 17828c2ecf20Sopenharmony_ci media_name[eni_in(MID_RES_ID_MCON) & DAUGHTER_ID]); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci error = suni_init(dev); 17858c2ecf20Sopenharmony_ci if (error) 17868c2ecf20Sopenharmony_ci goto unmap; 17878c2ecf20Sopenharmony_ciout: 17888c2ecf20Sopenharmony_ci return error; 17898c2ecf20Sopenharmony_ciunmap: 17908c2ecf20Sopenharmony_ci iounmap(base); 17918c2ecf20Sopenharmony_ci goto out; 17928c2ecf20Sopenharmony_ci} 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_cistatic void eni_do_release(struct atm_dev *dev) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci struct eni_dev *ed = ENI_DEV(dev); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci dev->phy->stop(dev); 17998c2ecf20Sopenharmony_ci dev->phy = NULL; 18008c2ecf20Sopenharmony_ci iounmap(ed->ioaddr); 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_cistatic int eni_start(struct atm_dev *dev) 18048c2ecf20Sopenharmony_ci{ 18058c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci void __iomem *buf; 18088c2ecf20Sopenharmony_ci unsigned long buffer_mem; 18098c2ecf20Sopenharmony_ci int error; 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci DPRINTK(">eni_start\n"); 18128c2ecf20Sopenharmony_ci eni_dev = ENI_DEV(dev); 18138c2ecf20Sopenharmony_ci if (request_irq(eni_dev->irq,&eni_int,IRQF_SHARED,DEV_LABEL,dev)) { 18148c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n", 18158c2ecf20Sopenharmony_ci dev->number,eni_dev->irq); 18168c2ecf20Sopenharmony_ci error = -EAGAIN; 18178c2ecf20Sopenharmony_ci goto out; 18188c2ecf20Sopenharmony_ci } 18198c2ecf20Sopenharmony_ci pci_set_master(eni_dev->pci_dev); 18208c2ecf20Sopenharmony_ci if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND, 18218c2ecf20Sopenharmony_ci PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | 18228c2ecf20Sopenharmony_ci (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { 18238c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+" 18248c2ecf20Sopenharmony_ci "master (0x%02x)\n",dev->number,error); 18258c2ecf20Sopenharmony_ci goto free_irq; 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, 18288c2ecf20Sopenharmony_ci END_SWAP_DMA))) { 18298c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " 18308c2ecf20Sopenharmony_ci "(0x%02x)\n",dev->number,error); 18318c2ecf20Sopenharmony_ci goto free_irq; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci /* determine addresses of internal tables */ 18348c2ecf20Sopenharmony_ci eni_dev->vci = eni_dev->ram; 18358c2ecf20Sopenharmony_ci eni_dev->rx_dma = eni_dev->ram+NR_VCI*16; 18368c2ecf20Sopenharmony_ci eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8; 18378c2ecf20Sopenharmony_ci eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8; 18388c2ecf20Sopenharmony_ci buf = eni_dev->service+NR_SERVICE*4; 18398c2ecf20Sopenharmony_ci DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n", 18408c2ecf20Sopenharmony_ci eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma, 18418c2ecf20Sopenharmony_ci eni_dev->service,buf); 18428c2ecf20Sopenharmony_ci spin_lock_init(&eni_dev->lock); 18438c2ecf20Sopenharmony_ci tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev); 18448c2ecf20Sopenharmony_ci eni_dev->events = 0; 18458c2ecf20Sopenharmony_ci /* initialize memory management */ 18468c2ecf20Sopenharmony_ci buffer_mem = eni_dev->mem - (buf - eni_dev->ram); 18478c2ecf20Sopenharmony_ci eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2; 18488c2ecf20Sopenharmony_ci eni_dev->free_list = kmalloc_array(eni_dev->free_list_size + 1, 18498c2ecf20Sopenharmony_ci sizeof(*eni_dev->free_list), 18508c2ecf20Sopenharmony_ci GFP_KERNEL); 18518c2ecf20Sopenharmony_ci if (!eni_dev->free_list) { 18528c2ecf20Sopenharmony_ci printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", 18538c2ecf20Sopenharmony_ci dev->number); 18548c2ecf20Sopenharmony_ci error = -ENOMEM; 18558c2ecf20Sopenharmony_ci goto free_irq; 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci eni_dev->free_len = 0; 18588c2ecf20Sopenharmony_ci eni_put_free(eni_dev,buf,buffer_mem); 18598c2ecf20Sopenharmony_ci memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */ 18608c2ecf20Sopenharmony_ci /* 18618c2ecf20Sopenharmony_ci * byte_addr free (k) 18628c2ecf20Sopenharmony_ci * 0x00000000 512 VCI table 18638c2ecf20Sopenharmony_ci * 0x00004000 496 RX DMA 18648c2ecf20Sopenharmony_ci * 0x00005000 492 TX DMA 18658c2ecf20Sopenharmony_ci * 0x00006000 488 service list 18668c2ecf20Sopenharmony_ci * 0x00007000 484 buffers 18678c2ecf20Sopenharmony_ci * 0x00080000 0 end (512kB) 18688c2ecf20Sopenharmony_ci */ 18698c2ecf20Sopenharmony_ci eni_out(0xffffffff,MID_IE); 18708c2ecf20Sopenharmony_ci error = start_tx(dev); 18718c2ecf20Sopenharmony_ci if (error) goto free_list; 18728c2ecf20Sopenharmony_ci error = start_rx(dev); 18738c2ecf20Sopenharmony_ci if (error) goto free_list; 18748c2ecf20Sopenharmony_ci error = dev->phy->start(dev); 18758c2ecf20Sopenharmony_ci if (error) goto free_list; 18768c2ecf20Sopenharmony_ci eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | 18778c2ecf20Sopenharmony_ci MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, 18788c2ecf20Sopenharmony_ci MID_MC_S); 18798c2ecf20Sopenharmony_ci /* Tonga uses SBus INTReq1 */ 18808c2ecf20Sopenharmony_ci (void) eni_in(MID_ISA); /* clear Midway interrupts */ 18818c2ecf20Sopenharmony_ci return 0; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cifree_list: 18848c2ecf20Sopenharmony_ci kfree(eni_dev->free_list); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_cifree_irq: 18878c2ecf20Sopenharmony_ci free_irq(eni_dev->irq, dev); 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ciout: 18908c2ecf20Sopenharmony_ci return error; 18918c2ecf20Sopenharmony_ci} 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic void eni_close(struct atm_vcc *vcc) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci DPRINTK(">eni_close\n"); 18978c2ecf20Sopenharmony_ci if (!ENI_VCC(vcc)) return; 18988c2ecf20Sopenharmony_ci clear_bit(ATM_VF_READY,&vcc->flags); 18998c2ecf20Sopenharmony_ci close_rx(vcc); 19008c2ecf20Sopenharmony_ci close_tx(vcc); 19018c2ecf20Sopenharmony_ci DPRINTK("eni_close: done waiting\n"); 19028c2ecf20Sopenharmony_ci /* deallocate memory */ 19038c2ecf20Sopenharmony_ci kfree(ENI_VCC(vcc)); 19048c2ecf20Sopenharmony_ci vcc->dev_data = NULL; 19058c2ecf20Sopenharmony_ci clear_bit(ATM_VF_ADDR,&vcc->flags); 19068c2ecf20Sopenharmony_ci /*foo();*/ 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic int eni_open(struct atm_vcc *vcc) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 19138c2ecf20Sopenharmony_ci int error; 19148c2ecf20Sopenharmony_ci short vpi = vcc->vpi; 19158c2ecf20Sopenharmony_ci int vci = vcc->vci; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci DPRINTK(">eni_open\n"); 19188c2ecf20Sopenharmony_ci EVENT("eni_open\n",0,0); 19198c2ecf20Sopenharmony_ci if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) 19208c2ecf20Sopenharmony_ci vcc->dev_data = NULL; 19218c2ecf20Sopenharmony_ci if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC) 19228c2ecf20Sopenharmony_ci set_bit(ATM_VF_ADDR,&vcc->flags); 19238c2ecf20Sopenharmony_ci if (vcc->qos.aal != ATM_AAL0 && vcc->qos.aal != ATM_AAL5) 19248c2ecf20Sopenharmony_ci return -EINVAL; 19258c2ecf20Sopenharmony_ci DPRINTK(DEV_LABEL "(itf %d): open %d.%d\n",vcc->dev->number,vcc->vpi, 19268c2ecf20Sopenharmony_ci vcc->vci); 19278c2ecf20Sopenharmony_ci if (!test_bit(ATM_VF_PARTIAL,&vcc->flags)) { 19288c2ecf20Sopenharmony_ci eni_vcc = kmalloc(sizeof(struct eni_vcc),GFP_KERNEL); 19298c2ecf20Sopenharmony_ci if (!eni_vcc) return -ENOMEM; 19308c2ecf20Sopenharmony_ci vcc->dev_data = eni_vcc; 19318c2ecf20Sopenharmony_ci eni_vcc->tx = NULL; /* for eni_close after open_rx */ 19328c2ecf20Sopenharmony_ci if ((error = open_rx_first(vcc))) { 19338c2ecf20Sopenharmony_ci eni_close(vcc); 19348c2ecf20Sopenharmony_ci return error; 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci if ((error = open_tx_first(vcc))) { 19378c2ecf20Sopenharmony_ci eni_close(vcc); 19388c2ecf20Sopenharmony_ci return error; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci if (vci == ATM_VPI_UNSPEC || vpi == ATM_VCI_UNSPEC) return 0; 19428c2ecf20Sopenharmony_ci if ((error = open_rx_second(vcc))) { 19438c2ecf20Sopenharmony_ci eni_close(vcc); 19448c2ecf20Sopenharmony_ci return error; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci if ((error = open_tx_second(vcc))) { 19478c2ecf20Sopenharmony_ci eni_close(vcc); 19488c2ecf20Sopenharmony_ci return error; 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci set_bit(ATM_VF_READY,&vcc->flags); 19518c2ecf20Sopenharmony_ci /* should power down SUNI while !ref_count @@@ */ 19528c2ecf20Sopenharmony_ci return 0; 19538c2ecf20Sopenharmony_ci} 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_cistatic int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) 19578c2ecf20Sopenharmony_ci{ 19588c2ecf20Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(vcc->dev); 19598c2ecf20Sopenharmony_ci struct eni_tx *tx = ENI_VCC(vcc)->tx; 19608c2ecf20Sopenharmony_ci struct sk_buff *skb; 19618c2ecf20Sopenharmony_ci int error,rate,rsv,shp; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (qos->txtp.traffic_class == ATM_NONE) return 0; 19648c2ecf20Sopenharmony_ci if (tx == eni_dev->ubr) return -EBADFD; 19658c2ecf20Sopenharmony_ci rate = atm_pcr_goal(&qos->txtp); 19668c2ecf20Sopenharmony_ci if (rate < 0) rate = -rate; 19678c2ecf20Sopenharmony_ci rsv = shp = 0; 19688c2ecf20Sopenharmony_ci if ((flgs & ATM_MF_DEC_RSV) && rate && rate < tx->reserved) rsv = 1; 19698c2ecf20Sopenharmony_ci if ((flgs & ATM_MF_INC_RSV) && (!rate || rate > tx->reserved)) rsv = 1; 19708c2ecf20Sopenharmony_ci if ((flgs & ATM_MF_DEC_SHP) && rate && rate < tx->shaping) shp = 1; 19718c2ecf20Sopenharmony_ci if ((flgs & ATM_MF_INC_SHP) && (!rate || rate > tx->shaping)) shp = 1; 19728c2ecf20Sopenharmony_ci if (!rsv && !shp) return 0; 19738c2ecf20Sopenharmony_ci error = reserve_or_set_tx(vcc,&qos->txtp,rsv,shp); 19748c2ecf20Sopenharmony_ci if (error) return error; 19758c2ecf20Sopenharmony_ci if (shp && !(flgs & ATM_MF_IMMED)) return 0; 19768c2ecf20Sopenharmony_ci /* 19778c2ecf20Sopenharmony_ci * Walk through the send buffer and patch the rate information in all 19788c2ecf20Sopenharmony_ci * segmentation buffer descriptors of this VCC. 19798c2ecf20Sopenharmony_ci */ 19808c2ecf20Sopenharmony_ci tasklet_disable(&eni_dev->task); 19818c2ecf20Sopenharmony_ci skb_queue_walk(&eni_dev->tx_queue, skb) { 19828c2ecf20Sopenharmony_ci void __iomem *dsc; 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci if (ATM_SKB(skb)->vcc != vcc) continue; 19858c2ecf20Sopenharmony_ci dsc = tx->send+ENI_PRV_POS(skb)*4; 19868c2ecf20Sopenharmony_ci writel((readl(dsc) & ~(MID_SEG_RATE | MID_SEG_PR)) | 19878c2ecf20Sopenharmony_ci (tx->prescaler << MID_SEG_PR_SHIFT) | 19888c2ecf20Sopenharmony_ci (tx->resolution << MID_SEG_RATE_SHIFT), dsc); 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci tasklet_enable(&eni_dev->task); 19918c2ecf20Sopenharmony_ci return 0; 19928c2ecf20Sopenharmony_ci} 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cistatic int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci if (cmd == ENI_MEMDUMP) { 20008c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 20018c2ecf20Sopenharmony_ci printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " 20028c2ecf20Sopenharmony_ci "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); 20038c2ecf20Sopenharmony_ci dump(dev); 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci if (cmd == ENI_SETMULT) { 20078c2ecf20Sopenharmony_ci struct eni_multipliers mult; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) return -EPERM; 20108c2ecf20Sopenharmony_ci if (copy_from_user(&mult, arg, 20118c2ecf20Sopenharmony_ci sizeof(struct eni_multipliers))) 20128c2ecf20Sopenharmony_ci return -EFAULT; 20138c2ecf20Sopenharmony_ci if ((mult.tx && mult.tx <= 100) || (mult.rx &&mult.rx <= 100) || 20148c2ecf20Sopenharmony_ci mult.tx > 65536 || mult.rx > 65536) 20158c2ecf20Sopenharmony_ci return -EINVAL; 20168c2ecf20Sopenharmony_ci if (mult.tx) eni_dev->tx_mult = mult.tx; 20178c2ecf20Sopenharmony_ci if (mult.rx) eni_dev->rx_mult = mult.rx; 20188c2ecf20Sopenharmony_ci return 0; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci if (cmd == ATM_SETCIRANGE) { 20218c2ecf20Sopenharmony_ci struct atm_cirange ci; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci if (copy_from_user(&ci, arg,sizeof(struct atm_cirange))) 20248c2ecf20Sopenharmony_ci return -EFAULT; 20258c2ecf20Sopenharmony_ci if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) && 20268c2ecf20Sopenharmony_ci (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX)) 20278c2ecf20Sopenharmony_ci return 0; 20288c2ecf20Sopenharmony_ci return -EINVAL; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci if (!dev->phy->ioctl) return -ENOIOCTLCMD; 20318c2ecf20Sopenharmony_ci return dev->phy->ioctl(dev,cmd,arg); 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_cistatic int eni_send(struct atm_vcc *vcc,struct sk_buff *skb) 20358c2ecf20Sopenharmony_ci{ 20368c2ecf20Sopenharmony_ci enum enq_res res; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci DPRINTK(">eni_send\n"); 20398c2ecf20Sopenharmony_ci if (!ENI_VCC(vcc)->tx) { 20408c2ecf20Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 20418c2ecf20Sopenharmony_ci else dev_kfree_skb(skb); 20428c2ecf20Sopenharmony_ci return -EINVAL; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci if (!skb) { 20458c2ecf20Sopenharmony_ci printk(KERN_CRIT "!skb in eni_send ?\n"); 20468c2ecf20Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 20478c2ecf20Sopenharmony_ci return -EINVAL; 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci if (vcc->qos.aal == ATM_AAL0) { 20508c2ecf20Sopenharmony_ci if (skb->len != ATM_CELL_SIZE-1) { 20518c2ecf20Sopenharmony_ci if (vcc->pop) vcc->pop(vcc,skb); 20528c2ecf20Sopenharmony_ci else dev_kfree_skb(skb); 20538c2ecf20Sopenharmony_ci return -EINVAL; 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci *(u32 *) skb->data = htonl(*(u32 *) skb->data); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci submitted++; 20588c2ecf20Sopenharmony_ci ATM_SKB(skb)->vcc = vcc; 20598c2ecf20Sopenharmony_ci tasklet_disable(&ENI_DEV(vcc->dev)->task); 20608c2ecf20Sopenharmony_ci res = do_tx(skb); 20618c2ecf20Sopenharmony_ci tasklet_enable(&ENI_DEV(vcc->dev)->task); 20628c2ecf20Sopenharmony_ci if (res == enq_ok) return 0; 20638c2ecf20Sopenharmony_ci skb_queue_tail(&ENI_VCC(vcc)->tx->backlog,skb); 20648c2ecf20Sopenharmony_ci backlogged++; 20658c2ecf20Sopenharmony_ci tasklet_schedule(&ENI_DEV(vcc->dev)->task); 20668c2ecf20Sopenharmony_ci return 0; 20678c2ecf20Sopenharmony_ci} 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_cistatic void eni_phy_put(struct atm_dev *dev,unsigned char value, 20708c2ecf20Sopenharmony_ci unsigned long addr) 20718c2ecf20Sopenharmony_ci{ 20728c2ecf20Sopenharmony_ci writel(value,ENI_DEV(dev)->phy+addr*4); 20738c2ecf20Sopenharmony_ci} 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_cistatic unsigned char eni_phy_get(struct atm_dev *dev,unsigned long addr) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci return readl(ENI_DEV(dev)->phy+addr*4); 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_cistatic int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci struct sock *s; 20868c2ecf20Sopenharmony_ci static const char *signal[] = { "LOST","unknown","okay" }; 20878c2ecf20Sopenharmony_ci struct eni_dev *eni_dev = ENI_DEV(dev); 20888c2ecf20Sopenharmony_ci struct atm_vcc *vcc; 20898c2ecf20Sopenharmony_ci int left,i; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci left = *pos; 20928c2ecf20Sopenharmony_ci if (!left) 20938c2ecf20Sopenharmony_ci return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " 20948c2ecf20Sopenharmony_ci "%d cps remaining\n",dev->number,signal[(int) dev->signal], 20958c2ecf20Sopenharmony_ci eni_dev->mem >> 10,eni_dev->tx_bw); 20968c2ecf20Sopenharmony_ci if (!--left) 20978c2ecf20Sopenharmony_ci return sprintf(page,"%4sBursts: TX" 20988c2ecf20Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_TX_16W) && \ 20998c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_8W) && \ 21008c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_4W) && \ 21018c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_TX_2W) 21028c2ecf20Sopenharmony_ci " none" 21038c2ecf20Sopenharmony_ci#endif 21048c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_16W 21058c2ecf20Sopenharmony_ci " 16W" 21068c2ecf20Sopenharmony_ci#endif 21078c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_8W 21088c2ecf20Sopenharmony_ci " 8W" 21098c2ecf20Sopenharmony_ci#endif 21108c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_4W 21118c2ecf20Sopenharmony_ci " 4W" 21128c2ecf20Sopenharmony_ci#endif 21138c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_TX_2W 21148c2ecf20Sopenharmony_ci " 2W" 21158c2ecf20Sopenharmony_ci#endif 21168c2ecf20Sopenharmony_ci ", RX" 21178c2ecf20Sopenharmony_ci#if !defined(CONFIG_ATM_ENI_BURST_RX_16W) && \ 21188c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_8W) && \ 21198c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_4W) && \ 21208c2ecf20Sopenharmony_ci !defined(CONFIG_ATM_ENI_BURST_RX_2W) 21218c2ecf20Sopenharmony_ci " none" 21228c2ecf20Sopenharmony_ci#endif 21238c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_16W 21248c2ecf20Sopenharmony_ci " 16W" 21258c2ecf20Sopenharmony_ci#endif 21268c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_8W 21278c2ecf20Sopenharmony_ci " 8W" 21288c2ecf20Sopenharmony_ci#endif 21298c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_4W 21308c2ecf20Sopenharmony_ci " 4W" 21318c2ecf20Sopenharmony_ci#endif 21328c2ecf20Sopenharmony_ci#ifdef CONFIG_ATM_ENI_BURST_RX_2W 21338c2ecf20Sopenharmony_ci " 2W" 21348c2ecf20Sopenharmony_ci#endif 21358c2ecf20Sopenharmony_ci#ifndef CONFIG_ATM_ENI_TUNE_BURST 21368c2ecf20Sopenharmony_ci " (default)" 21378c2ecf20Sopenharmony_ci#endif 21388c2ecf20Sopenharmony_ci "\n",""); 21398c2ecf20Sopenharmony_ci if (!--left) 21408c2ecf20Sopenharmony_ci return sprintf(page,"%4sBuffer multipliers: tx %d%%, rx %d%%\n", 21418c2ecf20Sopenharmony_ci "",eni_dev->tx_mult,eni_dev->rx_mult); 21428c2ecf20Sopenharmony_ci for (i = 0; i < NR_CHAN; i++) { 21438c2ecf20Sopenharmony_ci struct eni_tx *tx = eni_dev->tx+i; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci if (!tx->send) continue; 21468c2ecf20Sopenharmony_ci if (!--left) { 21478c2ecf20Sopenharmony_ci return sprintf(page, "tx[%d]: 0x%lx-0x%lx " 21488c2ecf20Sopenharmony_ci "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i, 21498c2ecf20Sopenharmony_ci (unsigned long) (tx->send - eni_dev->ram), 21508c2ecf20Sopenharmony_ci tx->send-eni_dev->ram+tx->words*4-1,tx->words*4, 21518c2ecf20Sopenharmony_ci tx->reserved,tx->shaping, 21528c2ecf20Sopenharmony_ci tx == eni_dev->ubr ? " (UBR)" : ""); 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci if (--left) continue; 21558c2ecf20Sopenharmony_ci return sprintf(page,"%10sbacklog %u packets\n","", 21568c2ecf20Sopenharmony_ci skb_queue_len(&tx->backlog)); 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci read_lock(&vcc_sklist_lock); 21598c2ecf20Sopenharmony_ci for(i = 0; i < VCC_HTABLE_SIZE; ++i) { 21608c2ecf20Sopenharmony_ci struct hlist_head *head = &vcc_hash[i]; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci sk_for_each(s, head) { 21638c2ecf20Sopenharmony_ci struct eni_vcc *eni_vcc; 21648c2ecf20Sopenharmony_ci int length; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci vcc = atm_sk(s); 21678c2ecf20Sopenharmony_ci if (vcc->dev != dev) 21688c2ecf20Sopenharmony_ci continue; 21698c2ecf20Sopenharmony_ci eni_vcc = ENI_VCC(vcc); 21708c2ecf20Sopenharmony_ci if (--left) continue; 21718c2ecf20Sopenharmony_ci length = sprintf(page,"vcc %4d: ",vcc->vci); 21728c2ecf20Sopenharmony_ci if (eni_vcc->rx) { 21738c2ecf20Sopenharmony_ci length += sprintf(page+length, "0x%lx-0x%lx " 21748c2ecf20Sopenharmony_ci "(%6ld bytes)", 21758c2ecf20Sopenharmony_ci (unsigned long) (eni_vcc->recv - eni_dev->ram), 21768c2ecf20Sopenharmony_ci eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1, 21778c2ecf20Sopenharmony_ci eni_vcc->words*4); 21788c2ecf20Sopenharmony_ci if (eni_vcc->tx) length += sprintf(page+length,", "); 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci if (eni_vcc->tx) 21818c2ecf20Sopenharmony_ci length += sprintf(page+length,"tx[%d], txing %d bytes", 21828c2ecf20Sopenharmony_ci eni_vcc->tx->index,eni_vcc->txing); 21838c2ecf20Sopenharmony_ci page[length] = '\n'; 21848c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 21858c2ecf20Sopenharmony_ci return length+1; 21868c2ecf20Sopenharmony_ci } 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci read_unlock(&vcc_sklist_lock); 21898c2ecf20Sopenharmony_ci for (i = 0; i < eni_dev->free_len; i++) { 21908c2ecf20Sopenharmony_ci struct eni_free *fe = eni_dev->free_list+i; 21918c2ecf20Sopenharmony_ci unsigned long offset; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci if (--left) continue; 21948c2ecf20Sopenharmony_ci offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; 21958c2ecf20Sopenharmony_ci return sprintf(page,"free %p-%p (%6d bytes)\n", 21968c2ecf20Sopenharmony_ci fe->start-offset,fe->start-offset+(1 << fe->order)-1, 21978c2ecf20Sopenharmony_ci 1 << fe->order); 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci return 0; 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_cistatic const struct atmdev_ops ops = { 22048c2ecf20Sopenharmony_ci .open = eni_open, 22058c2ecf20Sopenharmony_ci .close = eni_close, 22068c2ecf20Sopenharmony_ci .ioctl = eni_ioctl, 22078c2ecf20Sopenharmony_ci .send = eni_send, 22088c2ecf20Sopenharmony_ci .phy_put = eni_phy_put, 22098c2ecf20Sopenharmony_ci .phy_get = eni_phy_get, 22108c2ecf20Sopenharmony_ci .change_qos = eni_change_qos, 22118c2ecf20Sopenharmony_ci .proc_read = eni_proc_read 22128c2ecf20Sopenharmony_ci}; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_cistatic int eni_init_one(struct pci_dev *pci_dev, 22168c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 22178c2ecf20Sopenharmony_ci{ 22188c2ecf20Sopenharmony_ci struct atm_dev *dev; 22198c2ecf20Sopenharmony_ci struct eni_dev *eni_dev; 22208c2ecf20Sopenharmony_ci struct eni_zero *zero; 22218c2ecf20Sopenharmony_ci int rc; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci rc = pci_enable_device(pci_dev); 22248c2ecf20Sopenharmony_ci if (rc < 0) 22258c2ecf20Sopenharmony_ci goto out; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); 22288c2ecf20Sopenharmony_ci if (rc < 0) 22298c2ecf20Sopenharmony_ci goto err_disable; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci rc = -ENOMEM; 22328c2ecf20Sopenharmony_ci eni_dev = kmalloc(sizeof(struct eni_dev), GFP_KERNEL); 22338c2ecf20Sopenharmony_ci if (!eni_dev) 22348c2ecf20Sopenharmony_ci goto err_disable; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci zero = &eni_dev->zero; 22378c2ecf20Sopenharmony_ci zero->addr = dma_alloc_coherent(&pci_dev->dev, 22388c2ecf20Sopenharmony_ci ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL); 22398c2ecf20Sopenharmony_ci if (!zero->addr) 22408c2ecf20Sopenharmony_ci goto err_kfree; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &ops, -1, NULL); 22438c2ecf20Sopenharmony_ci if (!dev) 22448c2ecf20Sopenharmony_ci goto err_free_consistent; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci dev->dev_data = eni_dev; 22478c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, dev); 22488c2ecf20Sopenharmony_ci eni_dev->pci_dev = pci_dev; 22498c2ecf20Sopenharmony_ci eni_dev->asic = ent->driver_data; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci rc = eni_do_init(dev); 22528c2ecf20Sopenharmony_ci if (rc < 0) 22538c2ecf20Sopenharmony_ci goto err_unregister; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci rc = eni_start(dev); 22568c2ecf20Sopenharmony_ci if (rc < 0) 22578c2ecf20Sopenharmony_ci goto err_eni_release; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci eni_dev->more = eni_boards; 22608c2ecf20Sopenharmony_ci eni_boards = dev; 22618c2ecf20Sopenharmony_ciout: 22628c2ecf20Sopenharmony_ci return rc; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_cierr_eni_release: 22658c2ecf20Sopenharmony_ci dev->phy = NULL; 22668c2ecf20Sopenharmony_ci iounmap(ENI_DEV(dev)->ioaddr); 22678c2ecf20Sopenharmony_cierr_unregister: 22688c2ecf20Sopenharmony_ci atm_dev_deregister(dev); 22698c2ecf20Sopenharmony_cierr_free_consistent: 22708c2ecf20Sopenharmony_ci dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); 22718c2ecf20Sopenharmony_cierr_kfree: 22728c2ecf20Sopenharmony_ci kfree(eni_dev); 22738c2ecf20Sopenharmony_cierr_disable: 22748c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 22758c2ecf20Sopenharmony_ci goto out; 22768c2ecf20Sopenharmony_ci} 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_cistatic const struct pci_device_id eni_pci_tbl[] = { 22808c2ecf20Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_FPGA), 0 /* FPGA */ }, 22818c2ecf20Sopenharmony_ci { PCI_VDEVICE(EF, PCI_DEVICE_ID_EF_ATM_ASIC), 1 /* ASIC */ }, 22828c2ecf20Sopenharmony_ci { 0, } 22838c2ecf20Sopenharmony_ci}; 22848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci,eni_pci_tbl); 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic void eni_remove_one(struct pci_dev *pdev) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct atm_dev *dev = pci_get_drvdata(pdev); 22908c2ecf20Sopenharmony_ci struct eni_dev *ed = ENI_DEV(dev); 22918c2ecf20Sopenharmony_ci struct eni_zero *zero = &ed->zero; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci eni_do_release(dev); 22948c2ecf20Sopenharmony_ci atm_dev_deregister(dev); 22958c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); 22968c2ecf20Sopenharmony_ci kfree(ed); 22978c2ecf20Sopenharmony_ci pci_disable_device(pdev); 22988c2ecf20Sopenharmony_ci} 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_cistatic struct pci_driver eni_driver = { 23028c2ecf20Sopenharmony_ci .name = DEV_LABEL, 23038c2ecf20Sopenharmony_ci .id_table = eni_pci_tbl, 23048c2ecf20Sopenharmony_ci .probe = eni_init_one, 23058c2ecf20Sopenharmony_ci .remove = eni_remove_one, 23068c2ecf20Sopenharmony_ci}; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_cistatic int __init eni_init(void) 23108c2ecf20Sopenharmony_ci{ 23118c2ecf20Sopenharmony_ci struct sk_buff *skb; /* dummy for sizeof */ 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct eni_skb_prv)); 23148c2ecf20Sopenharmony_ci return pci_register_driver(&eni_driver); 23158c2ecf20Sopenharmony_ci} 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_cimodule_init(eni_init); 23198c2ecf20Sopenharmony_ci/* @@@ since exit routine not defined, this module can not be unloaded */ 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2322