📄 eni.c
字号:
/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ #include <linux/module.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/errno.h>#include <linux/atm.h>#include <linux/atmdev.h>#include <linux/sonet.h>#include <linux/skbuff.h>#include <linux/time.h>#include <linux/sched.h> /* for xtime */#include <linux/delay.h>#include <linux/uio.h>#include <linux/init.h>#include <linux/atm_eni.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/atomic.h>#include <asm/uaccess.h>#include <asm/string.h>#include <asm/byteorder.h>#include "tonga.h"#include "midway.h"#include "suni.h"#include "eni.h"#if !defined(__i386__) && !defined(__x86_64__)#ifndef ioremap_nocache#define ioremap_nocache(X,Y) ioremap(X,Y)#endif #endif/* * TODO: * * Show stoppers * none * * Minor * - OAM support * - fix bugs listed below *//* * KNOWN BUGS: * * - may run into JK-JK bug and deadlock * - should allocate UBR channel first * - buffer space allocation algorithm is stupid * (RX: should be maxSDU+maxdelay*rate * TX: should be maxSDU+min(maxSDU,maxdelay*rate) ) * - doesn't support OAM cells * - eni_put_free may hang if not putting memory fragments that _complete_ * 2^n block (never happens in real life, though) * - keeps IRQ even if initialization fails */#if 0#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)#else#define DPRINTK(format,args...)#endif#ifndef CONFIG_ATM_ENI_TUNE_BURST#define CONFIG_ATM_ENI_BURST_TX_8W#define CONFIG_ATM_ENI_BURST_RX_4W#endif#ifndef CONFIG_ATM_ENI_DEBUG#define NULLCHECK(x)#define EVENT(s,a,b)static void event_dump(void){}#else/* * NULL pointer checking */#define NULLCHECK(x) \ if ((unsigned long) (x) < 0x30) \ printk(KERN_CRIT #x "==0x%lx\n",(unsigned long) (x))/* * Very extensive activity logging. Greatly improves bug detection speed but * costs a few Mbps if enabled. */#define EV 64static const char *ev[EV];static unsigned long ev_a[EV],ev_b[EV];static int ec = 0;static void EVENT(const char *s,unsigned long a,unsigned long b){ ev[ec] = s; ev_a[ec] = a; ev_b[ec] = b; ec = (ec+1) % EV;}static void event_dump(void){ int n,i; for (n = 0; n < EV; n++) { i = (ec+n) % EV; printk(KERN_NOTICE); printk(ev[i] ? ev[i] : "(null)",ev_a[i],ev_b[i]); }}#endif /* CONFIG_ATM_ENI_DEBUG *//* * NExx must not be equal at end * EExx may be equal at end * xxPJOK verify validity of pointer jumps * xxPMOK operating on a circular buffer of "c" words */#define NEPJOK(a0,a1,b) \ ((a0) < (a1) ? (b) <= (a0) || (b) > (a1) : (b) <= (a0) && (b) > (a1))#define EEPJOK(a0,a1,b) \ ((a0) < (a1) ? (b) < (a0) || (b) >= (a1) : (b) < (a0) && (b) >= (a1))#define NEPMOK(a0,d,b,c) NEPJOK(a0,(a0+d) & (c-1),b)#define EEPMOK(a0,d,b,c) EEPJOK(a0,(a0+d) & (c-1),b)static int tx_complete = 0,dma_complete = 0,queued = 0,requeued = 0, backlogged = 0,rx_enqueued = 0,rx_dequeued = 0,pushed = 0,submitted = 0, putting = 0;static struct atm_dev *eni_boards = NULL;static u32 *cpu_zeroes = NULL; /* aligned "magic" zeroes */static dma_addr_t zeroes;/* Read/write registers on card */#define eni_in(r) readl(eni_dev->reg+(r)*4)#define eni_out(v,r) writel((v),eni_dev->reg+(r)*4)/*-------------------------------- utilities --------------------------------*/static void dump_mem(struct eni_dev *eni_dev){ int i; for (i = 0; i < eni_dev->free_len; i++) printk(KERN_DEBUG " %d: 0x%lx %d\n",i, eni_dev->free_list[i].start, 1 << eni_dev->free_list[i].order);}static void dump(struct atm_dev *dev){ struct eni_dev *eni_dev; int i; eni_dev = ENI_DEV(dev); printk(KERN_NOTICE "Free memory\n"); dump_mem(eni_dev); printk(KERN_NOTICE "TX buffers\n"); for (i = 0; i < NR_CHAN; i++) if (eni_dev->tx[i].send) printk(KERN_NOTICE " TX %d @ 0x%lx: %ld\n",i, eni_dev->tx[i].send,eni_dev->tx[i].words*4); printk(KERN_NOTICE "RX buffers\n"); for (i = 0; i < 1024; i++) if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx) printk(KERN_NOTICE " RX %d @ 0x%lx: %ld\n",i, ENI_VCC(eni_dev->rx_map[i])->recv, ENI_VCC(eni_dev->rx_map[i])->words*4); printk(KERN_NOTICE "----\n");}static void eni_put_free(struct eni_dev *eni_dev,unsigned long start, unsigned long size){ struct eni_free *list; int len,order; DPRINTK("init 0x%lx+%ld(0x%lx)\n",start,size,size); start += eni_dev->base_diff; list = eni_dev->free_list; len = eni_dev->free_len; while (size) { if (len >= eni_dev->free_list_size) { printk(KERN_CRIT "eni_put_free overflow (0x%lx,%ld)\n", start,size); break; } for (order = 0; !((start | size) & (1 << order)); order++); if (MID_MIN_BUF_SIZE > (1 << order)) { printk(KERN_CRIT "eni_put_free: order %d too small\n", order); break; } list[len].start = start; list[len].order = order; len++; start += 1 << order; size -= 1 << order; } eni_dev->free_len = len; /*dump_mem(eni_dev);*/}static unsigned long eni_alloc_mem(struct eni_dev *eni_dev,unsigned long *size){ struct eni_free *list; unsigned long start; int len,i,order,best_order,index; list = eni_dev->free_list; len = eni_dev->free_len; if (*size < MID_MIN_BUF_SIZE) *size = MID_MIN_BUF_SIZE; if (*size > MID_MAX_BUF_SIZE) return 0; for (order = 0; (1 << order) < *size; order++); DPRINTK("trying: %ld->%d\n",*size,order); best_order = 65; /* we don't have more than 2^64 of anything ... */ index = 0; /* silence GCC */ for (i = 0; i < len; i++) if (list[i].order == order) { best_order = order; index = i; break; } else if (best_order > list[i].order && list[i].order > order) { best_order = list[i].order; index = i; } if (best_order == 65) return 0; start = list[index].start-eni_dev->base_diff; list[index] = list[--len]; eni_dev->free_len = len; *size = 1 << order; eni_put_free(eni_dev,start+*size,(1 << best_order)-*size); DPRINTK("%ld bytes (order %d) at 0x%lx\n",*size,order,start); memset_io(start,0,*size); /* never leak data */ /*dump_mem(eni_dev);*/ return start;}static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start, unsigned long size){ struct eni_free *list; int len,i,order; start += eni_dev->base_diff; list = eni_dev->free_list; len = eni_dev->free_len; for (order = -1; size; order++) size >>= 1; DPRINTK("eni_free_mem: 0x%lx+0x%lx (order %d)\n",start,size,order); for (i = 0; i < len; i++) if (list[i].start == (start^(1 << order)) && list[i].order == order) { DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i, list[i].start,start,1 << order,list[i].order,order); list[i] = list[--len]; start &= ~(unsigned long) (1 << order); order++; i = -1; continue; } if (len >= eni_dev->free_list_size) { printk(KERN_ALERT "eni_free_mem overflow (0x%lx,%d)\n",start, order); return; } list[len].start = start; list[len].order = order; eni_dev->free_len = len+1; /*dump_mem(eni_dev);*/}/*----------------------------------- RX ------------------------------------*/#define ENI_VCC_NOS ((struct atm_vcc *) 1)static void rx_ident_err(struct atm_vcc *vcc){ struct atm_dev *dev; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; dev = vcc->dev; eni_dev = ENI_DEV(dev); /* immediately halt adapter */ eni_out(eni_in(MID_MC_S) & ~(MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE),MID_MC_S); /* dump useful information */ eni_vcc = ENI_VCC(vcc); printk(KERN_ALERT DEV_LABEL "(itf %d): driver error - RX ident " "mismatch\n",dev->number); printk(KERN_ALERT " VCI %d, rxing %d, words %ld\n",vcc->vci, eni_vcc->rxing,eni_vcc->words); printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value " "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4)); printk(KERN_ALERT " last 0x%p, servicing %d\n",eni_vcc->last, eni_vcc->servicing); EVENT("---dump ends here---\n",0,0); printk(KERN_NOTICE "---recent events---\n"); event_dump(); ENI_DEV(dev)->fast = NULL; /* really stop it */ ENI_DEV(dev)->slow = NULL; skb_queue_head_init(&ENI_DEV(dev)->rx_queue);}static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, unsigned long skip,unsigned long size,unsigned long eff){ struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; u32 dma_rd,dma_wr; u32 dma[RX_DMA_BUF*2]; dma_addr_t paddr; unsigned long here; int i,j; eni_dev = ENI_DEV(vcc->dev); eni_vcc = ENI_VCC(vcc); paddr = 0; /* GCC, shut up */ if (skb) { paddr = pci_map_single(eni_dev->pci_dev,skb->data,skb->len, PCI_DMA_FROMDEVICE); ENI_PRV_PADDR(skb) = paddr; if (paddr & 3) printk(KERN_CRIT DEV_LABEL "(itf %d): VCI %d has " "mis-aligned RX data (0x%lx)\n",vcc->dev->number, vcc->vci,(unsigned long) paddr); ENI_PRV_SIZE(skb) = size+skip; /* PDU plus descriptor */ ATM_SKB(skb)->vcc = vcc; } j = 0; if ((eff && skip) || 1) { /* @@@ actually, skip is always == 1 ... */ here = (eni_vcc->descr+skip) & (eni_vcc->words-1); dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; j++; } here = (eni_vcc->descr+size+skip) & (eni_vcc->words-1); if (!eff) size += skip; else { unsigned long words; if (!size) { DPRINTK("strange things happen ...\n"); EVENT("strange things happen ... (skip=%ld,eff=%ld)\n", size,eff); } words = eff; if (paddr & 15) { unsigned long init; init = 4-((paddr & 15) >> 2); if (init > words) init = words; dma[j++] = MID_DT_WORD | (init << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; paddr += init << 2; words -= init; }#ifdef CONFIG_ATM_ENI_BURST_RX_16W /* may work with some PCI chipsets ... */ if (words & ~15) { dma[j++] = MID_DT_16W | ((words >> 4) << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; paddr += (words & ~15) << 2; words &= 15; }#endif#ifdef CONFIG_ATM_ENI_BURST_RX_8W /* works only with *some* PCI chipsets ... */ if (words & ~7) { dma[j++] = MID_DT_8W | ((words >> 3) << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; paddr += (words & ~7) << 2; words &= 7; }#endif#ifdef CONFIG_ATM_ENI_BURST_RX_4W /* recommended */ if (words & ~3) { dma[j++] = MID_DT_4W | ((words >> 2) << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; paddr += (words & ~3) << 2; words &= 3; }#endif#ifdef CONFIG_ATM_ENI_BURST_RX_2W /* probably useless if RX_4W, RX_8W, ... */ if (words & ~1) { dma[j++] = MID_DT_2W | ((words >> 1) << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; paddr += (words & ~1) << 2; words &= 1; }#endif if (words) { dma[j++] = MID_DT_WORD | (words << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT); dma[j++] = paddr; } } if (size != eff) { dma[j++] = (here << MID_DMA_COUNT_SHIFT) | (vcc->vci << MID_DMA_VCI_SHIFT) | MID_DT_JK; j++; } if (!j || j > 2*RX_DMA_BUF) { printk(KERN_CRIT DEV_LABEL "!j or j too big!!!\n"); goto trouble; } dma[j-2] |= MID_DMA_END; j = j >> 1; dma_wr = eni_in(MID_DMA_WR_RX); dma_rd = eni_in(MID_DMA_RD_RX); /* * Can I move the dma_wr pointer by 2j+1 positions without overwriting * data that hasn't been read (position of dma_rd) yet ? */ if (!NEPMOK(dma_wr,j+j+1,dma_rd,NR_DMA_RX)) { /* @@@ +1 is ugly */ printk(KERN_WARNING DEV_LABEL "(itf %d): RX DMA full\n", vcc->dev->number); goto trouble; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -