📄 eni.c
字号:
/* * Looping a few times in here is probably far cheaper than * keeping track of TX completions all the time, so let's poll * a bit ... */ while (eni_in(MID_TX_RDPTR(eni_vcc->tx->index)) != eni_in(MID_TX_DESCRSTART(eni_vcc->tx->index))) schedule(); eni_free_mem(eni_dev,eni_vcc->tx->send,eni_vcc->tx->words << 2); eni_vcc->tx->send = 0; eni_dev->tx_bw += eni_vcc->tx->reserved; } eni_vcc->tx = NULL;}static int start_tx(struct atm_dev *dev){ struct eni_dev *eni_dev; int i; eni_dev = ENI_DEV(dev); eni_dev->lost = 0; eni_dev->tx_bw = ATM_OC3_PCR; eni_dev->tx_mult = DEFAULT_TX_MULT; init_waitqueue_head(&eni_dev->tx_wait); eni_dev->ubr = NULL; skb_queue_head_init(&eni_dev->tx_queue); eni_out(0,MID_DMA_WR_TX); for (i = 0; i < NR_CHAN; i++) { eni_dev->tx[i].send = 0; eni_dev->tx[i].index = i; } return 0;}/*--------------------------------- common ----------------------------------*/#if 0 /* may become useful again when tuning things */static void foo(void){printk(KERN_INFO "tx_complete=%d,dma_complete=%d,queued=%d,requeued=%d,sub=%d,\n" "backlogged=%d,rx_enqueued=%d,rx_dequeued=%d,putting=%d,pushed=%d\n", tx_complete,dma_complete,queued,requeued,submitted,backlogged, rx_enqueued,rx_dequeued,putting,pushed);if (eni_boards) printk(KERN_INFO "loss: %ld\n",ENI_DEV(eni_boards)->lost);}#endifstatic void bug_int(struct atm_dev *dev,unsigned long reason){ struct eni_dev *eni_dev; DPRINTK(">bug_int\n"); eni_dev = ENI_DEV(dev); if (reason & MID_DMA_ERR_ACK) printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " "error\n",dev->number); if (reason & MID_TX_IDENT_MISM) printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - ident " "mismatch\n",dev->number); if (reason & MID_TX_DMA_OVFL) printk(KERN_CRIT DEV_LABEL "(itf %d): driver error - DMA " "overflow\n",dev->number); EVENT("---dump ends here---\n",0,0); printk(KERN_NOTICE "---recent events---\n"); event_dump();}static void eni_int(int irq,void *dev_id,struct pt_regs *regs){ struct atm_dev *dev; struct eni_dev *eni_dev; u32 reason; DPRINTK(">eni_int\n"); dev = dev_id; eni_dev = ENI_DEV(dev); reason = eni_in(MID_ISA); DPRINTK(DEV_LABEL ": int 0x%lx\n",(unsigned long) reason); /* * Must handle these two right now, because reading ISA doesn't clear * them, so they re-occur and we never make it to the tasklet. Since * they're rare, we don't mind the occasional invocation of eni_tasklet * with eni_dev->events == 0. */ if (reason & MID_STAT_OVFL) { EVENT("stat overflow\n",0,0); eni_dev->lost += eni_in(MID_STAT) & MID_OVFL_TRASH; } if (reason & MID_SUNI_INT) { EVENT("SUNI int\n",0,0); dev->phy->interrupt(dev);#if 0 foo();#endif } spin_lock(&eni_dev->lock); eni_dev->events |= reason; spin_unlock(&eni_dev->lock); tasklet_schedule(&eni_dev->task);}static void eni_tasklet(unsigned long data){ struct atm_dev *dev = (struct atm_dev *) data; struct eni_dev *eni_dev = ENI_DEV(dev); unsigned long flags; u32 events; DPRINTK("eni_tasklet (dev %p)\n",dev); spin_lock_irqsave(&eni_dev->lock,flags); events = xchg(&eni_dev->events,0); spin_unlock_irqrestore(&eni_dev->lock,flags); if (events & MID_RX_DMA_COMPLETE) { EVENT("INT: RX DMA complete, starting dequeue_rx\n",0,0); dequeue_rx(dev); EVENT("dequeue_rx done, starting poll_rx\n",0,0); poll_rx(dev); EVENT("poll_rx done\n",0,0); /* poll_tx ? */ } if (events & MID_SERVICE) { EVENT("INT: service, starting get_service\n",0,0); get_service(dev); EVENT("get_service done, starting poll_rx\n",0,0); poll_rx(dev); EVENT("poll_rx done\n",0,0); } if (events & MID_TX_DMA_COMPLETE) { EVENT("INT: TX DMA COMPLETE\n",0,0); dequeue_tx(dev); } if (events & MID_TX_COMPLETE) { EVENT("INT: TX COMPLETE\n",0,0);tx_complete++; wake_up(&eni_dev->tx_wait); /* poll_rx ? */ } if (events & (MID_DMA_ERR_ACK | MID_TX_IDENT_MISM | MID_TX_DMA_OVFL)) { EVENT("bug interrupt\n",0,0); bug_int(dev,events); } poll_tx(dev);}/*--------------------------------- entries ---------------------------------*/static const char *media_name[] __devinitdata = { "MMF", "SMF", "MMF", "03?", /* 0- 3 */ "UTP", "05?", "06?", "07?", /* 4- 7 */ "TAXI","09?", "10?", "11?", /* 8-11 */ "12?", "13?", "14?", "15?", /* 12-15 */ "MMF", "SMF", "18?", "19?", /* 16-19 */ "UTP", "21?", "22?", "23?", /* 20-23 */ "24?", "25?", "26?", "27?", /* 24-27 */ "28?", "29?", "30?", "31?" /* 28-31 */};#define SET_SEPROM \ ({ if (!error && !pci_error) { \ pci_error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,tonga); \ udelay(10); /* 10 usecs */ \ } })#define GET_SEPROM \ ({ if (!error && !pci_error) { \ pci_error = pci_read_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL,&tonga); \ udelay(10); /* 10 usecs */ \ } })static int __devinit get_esi_asic(struct atm_dev *dev){ struct eni_dev *eni_dev; unsigned char tonga; int error,failed,pci_error; int address,i,j; eni_dev = ENI_DEV(dev); error = pci_error = 0; tonga = SEPROM_MAGIC | SEPROM_DATA | SEPROM_CLK; SET_SEPROM; for (i = 0; i < ESI_LEN && !error && !pci_error; i++) { /* start operation */ tonga |= SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; tonga &= ~SEPROM_DATA; SET_SEPROM; tonga &= ~SEPROM_CLK; SET_SEPROM; /* send address */ address = ((i+SEPROM_ESI_BASE) << 1)+1; for (j = 7; j >= 0; j--) { tonga = (address >> j) & 1 ? tonga | SEPROM_DATA : tonga & ~SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; tonga &= ~SEPROM_CLK; SET_SEPROM; } /* get ack */ tonga |= SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; GET_SEPROM; failed = tonga & SEPROM_DATA; tonga &= ~SEPROM_CLK; SET_SEPROM; tonga |= SEPROM_DATA; SET_SEPROM; if (failed) error = -EIO; else { dev->esi[i] = 0; for (j = 7; j >= 0; j--) { dev->esi[i] <<= 1; tonga |= SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; GET_SEPROM; if (tonga & SEPROM_DATA) dev->esi[i] |= 1; tonga &= ~SEPROM_CLK; SET_SEPROM; tonga |= SEPROM_DATA; SET_SEPROM; } /* get ack */ tonga |= SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; GET_SEPROM; if (!(tonga & SEPROM_DATA)) error = -EIO; tonga &= ~SEPROM_CLK; SET_SEPROM; tonga |= SEPROM_DATA; SET_SEPROM; } /* stop operation */ tonga &= ~SEPROM_DATA; SET_SEPROM; tonga |= SEPROM_CLK; SET_SEPROM; tonga |= SEPROM_DATA; SET_SEPROM; } if (pci_error) { printk(KERN_ERR DEV_LABEL "(itf %d): error reading ESI " "(0x%02x)\n",dev->number,pci_error); error = -EIO; } return error;}#undef SET_SEPROM#undef GET_SEPROMstatic int __devinit get_esi_fpga(struct atm_dev *dev,unsigned long base){ unsigned long mac_base; int i; mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom); for (i = 0; i < ESI_LEN; i++) dev->esi[i] = readb(mac_base+(i^3)); return 0;}static int __devinit eni_do_init(struct atm_dev *dev){ struct midway_eprom *eprom; struct eni_dev *eni_dev; struct pci_dev *pci_dev; unsigned long real_base,base; unsigned char revision; int error,i,last; DPRINTK(">eni_init\n"); dev->ci_range.vpi_bits = 0; dev->ci_range.vci_bits = NR_VCI_LD; dev->link_rate = ATM_OC3_PCR; eni_dev = ENI_DEV(dev); pci_dev = eni_dev->pci_dev; real_base = pci_resource_start(pci_dev, 0); eni_dev->irq = pci_dev->irq; error = pci_read_config_byte(pci_dev,PCI_REVISION_ID,&revision); if (error) { printk(KERN_ERR DEV_LABEL "(itf %d): init error 0x%02x\n", dev->number,error); return -EINVAL; } if ((error = pci_write_config_word(pci_dev,PCI_COMMAND, PCI_COMMAND_MEMORY | (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory " "(0x%02x)\n",dev->number,error); return -EIO; } printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,", dev->number,revision,real_base,eni_dev->irq); if (!(base = (unsigned long) ioremap_nocache(real_base,MAP_MAX_SIZE))) { printk("\n"); printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page " "mapping\n",dev->number); return error; } eni_dev->base_diff = real_base-base; /* id may not be present in ASIC Tonga boards - check this @@@ */ if (!eni_dev->asic) { eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct midway_eprom)); if (readl(&eprom->magic) != ENI155_MAGIC) { printk("\n"); printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad " "magic - expected 0x%x, got 0x%x\n",dev->number, ENI155_MAGIC,(unsigned) readl(&eprom->magic)); return -EINVAL; } } eni_dev->phy = base+PHY_BASE; eni_dev->reg = base+REG_BASE; eni_dev->ram = base+RAM_BASE; last = MAP_MAX_SIZE-RAM_BASE; for (i = last-RAM_INCREMENT; i >= 0; i -= RAM_INCREMENT) { writel(0x55555555,eni_dev->ram+i); if (readl(eni_dev->ram+i) != 0x55555555) last = i; else { writel(0xAAAAAAAA,eni_dev->ram+i); if (readl(eni_dev->ram+i) != 0xAAAAAAAA) last = i; else writel(i,eni_dev->ram+i); } } for (i = 0; i < last; i += RAM_INCREMENT) if (readl(eni_dev->ram+i) != i) break; eni_dev->mem = i; memset_io(eni_dev->ram,0,eni_dev->mem); /* TODO: should shrink allocation now */ printk("mem=%dkB (",eni_dev->mem >> 10); /* TODO: check for non-SUNI, check for TAXI ? */ if (!(eni_in(MID_RES_ID_MCON) & 0x200) != !eni_dev->asic) { printk(")\n"); printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n", dev->number,(unsigned) eni_in(MID_RES_ID_MCON)); return -EINVAL; } error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base); if (error) return error; for (i = 0; i < ESI_LEN; i++) printk("%s%02X",i ? "-" : "",dev->esi[i]); printk(")\n"); printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number, eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", media_name[eni_in(MID_RES_ID_MCON) & DAUGTHER_ID]); return suni_init(dev);}static int __devinit eni_start(struct atm_dev *dev){ struct eni_dev *eni_dev; unsigned long buf,buffer_mem; int error; DPRINTK(">eni_start\n"); eni_dev = ENI_DEV(dev); if (request_irq(eni_dev->irq,&eni_int,SA_SHIRQ,DEV_LABEL,dev)) { printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n", dev->number,eni_dev->irq); return -EAGAIN; } /* @@@ should release IRQ on error */ pci_set_master(eni_dev->pci_dev); if ((error = pci_write_config_word(eni_dev->pci_dev,PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | (eni_dev->asic ? PCI_COMMAND_PARITY | PCI_COMMAND_SERR : 0)))) { printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+" "master (0x%02x)\n",dev->number,error); return error; }#ifdef __sparc_v9__ /* copied from drivers/net/sunhme.c */ /* NOTE: Cache line size is in 32-bit word units. */ pci_write_config_byte(eni_dev->pci_dev, PCI_CACHE_LINE_SIZE, 0x10);#endif if ((error = pci_write_config_byte(eni_dev->pci_dev,PCI_TONGA_CTRL, END_SWAP_DMA))) { printk(KERN_ERR DEV_LABEL "(itf %d): can't set endian swap " "(0x%02x)\n",dev->number,error); return error; } /* determine addresses of internal tables */ eni_dev->vci = eni_dev->ram; eni_dev->rx_dma = eni_dev->ram+NR_VCI*16; eni_dev->tx_dma = eni_dev->rx_dma+NR_DMA_RX*8; eni_dev->service = eni_dev->tx_dma+NR_DMA_TX*8; buf = eni_dev->service+NR_SERVICE*4; DPRINTK("vci 0x%lx,rx 0x%lx, tx 0x%lx,srv 0x%lx,buf 0x%lx\n", eni_dev->vci,eni_dev->rx_dma,eni_dev->tx_dma, eni_dev->service,buf); spin_lock_init(&eni_dev->lock); tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev); eni_dev->events = 0; /* initialize memory management */ buffer_mem = eni_dev->mem-(buf-eni_dev->ram); eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2; eni_dev->free_list = (struct eni_free *) kmalloc( sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL); if (!eni_dev->free_list) { printk(KERN_ERR DEV_LABEL "(itf %d): couldn't get free page\n", dev->number); return -ENOMEM; } eni_dev->free_len = 0; eni_put_free(eni_dev,buf,buffer_mem); memset_io(eni_dev->vci,0,16*NR_VCI); /* clear VCI table */ /* * byte_addr free (k) * 0x00000000 512 VCI table * 0x00004000 496 RX DMA * 0x00005000 492 TX DMA * 0x00006000 488 service list * 0x00007000 484 buffers * 0x00080000 0 end (512kB) */ eni_out(0xffffffff,MID_IE); error = start_tx(dev); if (error) return error; error = start_rx(dev); if (error) return error; error = dev->phy->start(dev); if (error) return error; eni_out(eni_in(MID_MC_S) | (1 << MID_INT_SEL_SHIFT) | MID_TX_LOCK_MODE | MID_DMA_ENABLE | MID_TX_ENABLE | MID_RX_ENABLE, MID_MC_S); /* Tonga uses SBus INTReq1 */ (void) eni_in(MID_ISA); /* clear Midway interrupts */ return 0;}static void eni_close(struct atm_vcc *vcc){ DPRINTK(">eni_close\n"); if (!ENI_VCC(vcc)) return; clear_bit(ATM_VF_READY,&vcc->flags); close_rx(vcc); close_tx(vcc); DPRINTK("eni_close: done waiting\n"); /* deallocate memory */ kfree(ENI_VCC(vcc)); ENI_VCC(vcc) = NULL; clear_bit(ATM_VF_ADDR,&vcc->flags); /*foo();*/}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -