epic100.c
来自「linux 内核源代码」· C语言 代码 · 共 1,609 行 · 第 1/3 页
C
1,609 行
/* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD (5 << 6)#define EE_READ64_CMD (6 << 6)#define EE_READ256_CMD (6 << 8)#define EE_ERASE_CMD (7 << 6)static void epic_disable_int(struct net_device *dev, struct epic_private *ep){ long ioaddr = dev->base_addr; outl(0x00000000, ioaddr + INTMASK);}static inline void __epic_pci_commit(long ioaddr){#ifndef USE_IO_OPS inl(ioaddr + INTMASK);#endif}static inline void epic_napi_irq_off(struct net_device *dev, struct epic_private *ep){ long ioaddr = dev->base_addr; outl(ep->irq_mask & ~EpicNapiEvent, ioaddr + INTMASK); __epic_pci_commit(ioaddr);}static inline void epic_napi_irq_on(struct net_device *dev, struct epic_private *ep){ long ioaddr = dev->base_addr; /* No need to commit possible posted write */ outl(ep->irq_mask | EpicNapiEvent, ioaddr + INTMASK);}static int __devinit read_eeprom(long ioaddr, int location){ int i; int retval = 0; long ee_addr = ioaddr + EECTL; int read_cmd = location | (inl(ee_addr) & 0x40 ? EE_READ64_CMD : EE_READ256_CMD); outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); /* Shift the read command bits out. */ for (i = 12; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0; outl(EE_ENB | dataval, ee_addr); eeprom_delay(); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(); } outl(EE_ENB, ee_addr); for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); outl(EE_ENB, ee_addr); eeprom_delay(); } /* Terminate the EEPROM access. */ outl(EE_ENB & ~EE_CS, ee_addr); return retval;}#define MII_READOP 1#define MII_WRITEOP 2static int mdio_read(struct net_device *dev, int phy_id, int location){ long ioaddr = dev->base_addr; int read_cmd = (phy_id << 9) | (location << 4) | MII_READOP; int i; outl(read_cmd, ioaddr + MIICtrl); /* Typical operation takes 25 loops. */ for (i = 400; i > 0; i--) { barrier(); if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) { /* Work around read failure bug. */ if (phy_id == 1 && location < 6 && inw(ioaddr + MIIData) == 0xffff) { outl(read_cmd, ioaddr + MIICtrl); continue; } return inw(ioaddr + MIIData); } } return 0xffff;}static void mdio_write(struct net_device *dev, int phy_id, int loc, int value){ long ioaddr = dev->base_addr; int i; outw(value, ioaddr + MIIData); outl((phy_id << 9) | (loc << 4) | MII_WRITEOP, ioaddr + MIICtrl); for (i = 10000; i > 0; i--) { barrier(); if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0) break; } return;}static int epic_open(struct net_device *dev){ struct epic_private *ep = dev->priv; long ioaddr = dev->base_addr; int i; int retval; /* Soft reset the chip. */ outl(0x4001, ioaddr + GENCTL); napi_enable(&ep->napi); if ((retval = request_irq(dev->irq, &epic_interrupt, IRQF_SHARED, dev->name, dev))) { napi_disable(&ep->napi); return retval; } epic_init_ring(dev); outl(0x4000, ioaddr + GENCTL); /* This magic is documented in SMSC app note 7.15 */ for (i = 16; i > 0; i--) outl(0x0008, ioaddr + TEST1); /* Pull the chip out of low-power mode, enable interrupts, and set for PCI read multiple. The MIIcfg setting and strange write order are required by the details of which bits are reset and the transceiver wiring on the Ositech CardBus card. */#if 0 outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);#endif if (ep->chip_flags & MII_PWRDWN) outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);#if defined(__powerpc__) || defined(__sparc__) /* Big endian */ outl(0x4432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); inl(ioaddr + GENCTL); outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#else outl(0x4412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); inl(ioaddr + GENCTL); outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#endif udelay(20); /* Looks like EPII needs that if you want reliable RX init. FIXME: pci posting bug? */ for (i = 0; i < 3; i++) outl(cpu_to_le16(((u16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4); ep->tx_threshold = TX_FIFO_THRESH; outl(ep->tx_threshold, ioaddr + TxThresh); if (media2miictl[dev->if_port & 15]) { if (ep->mii_phy_cnt) mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]); if (dev->if_port == 1) { if (debug > 1) printk(KERN_INFO "%s: Using the 10base2 transceiver, MII " "status %4.4x.\n", dev->name, mdio_read(dev, ep->phys[0], MII_BMSR)); } } else { int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA); if (mii_lpa != 0xffff) { if ((mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == LPA_10FULL) ep->mii.full_duplex = 1; else if (! (mii_lpa & LPA_LPACK)) mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); if (debug > 1) printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d" " register read of %4.4x.\n", dev->name, ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa); } } outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); outl(ep->rx_ring_dma, ioaddr + PRxCDAR); outl(ep->tx_ring_dma, ioaddr + PTxCDAR); /* Start the chip's Rx process. */ set_rx_mode(dev); outl(StartRx | RxQueued, ioaddr + COMMAND); netif_start_queue(dev); /* Enable interrupts by setting the interrupt mask. */ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) | CntFull | TxUnderrun | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK); if (debug > 1) printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x " "%s-duplex.\n", dev->name, ioaddr, dev->irq, (int)inl(ioaddr + GENCTL), ep->mii.full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&ep->timer); ep->timer.expires = jiffies + 3*HZ; ep->timer.data = (unsigned long)dev; ep->timer.function = &epic_timer; /* timer handler */ add_timer(&ep->timer); return 0;}/* Reset the chip to recover from a PCI transaction error. This may occur at interrupt time. */static void epic_pause(struct net_device *dev){ long ioaddr = dev->base_addr; struct epic_private *ep = dev->priv; netif_stop_queue (dev); /* Disable interrupts by clearing the interrupt mask. */ outl(0x00000000, ioaddr + INTMASK); /* Stop the chip's Tx and Rx DMA processes. */ outw(StopRx | StopTxDMA | StopRxDMA, ioaddr + COMMAND); /* Update the error counts. */ if (inw(ioaddr + COMMAND) != 0xffff) { ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); } /* Remove the packets on the Rx queue. */ epic_rx(dev, RX_RING_SIZE);}static void epic_restart(struct net_device *dev){ long ioaddr = dev->base_addr; struct epic_private *ep = dev->priv; int i; /* Soft reset the chip. */ outl(0x4001, ioaddr + GENCTL); printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n", dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx); udelay(1); /* This magic is documented in SMSC app note 7.15 */ for (i = 16; i > 0; i--) outl(0x0008, ioaddr + TEST1);#if defined(__powerpc__) || defined(__sparc__) /* Big endian */ outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#else outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);#endif outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg); if (ep->chip_flags & MII_PWRDWN) outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); for (i = 0; i < 3; i++) outl(cpu_to_le16(((u16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4); ep->tx_threshold = TX_FIFO_THRESH; outl(ep->tx_threshold, ioaddr + TxThresh); outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); outl(ep->rx_ring_dma + (ep->cur_rx%RX_RING_SIZE)* sizeof(struct epic_rx_desc), ioaddr + PRxCDAR); outl(ep->tx_ring_dma + (ep->dirty_tx%TX_RING_SIZE)* sizeof(struct epic_tx_desc), ioaddr + PTxCDAR); /* Start the chip's Rx process. */ set_rx_mode(dev); outl(StartRx | RxQueued, ioaddr + COMMAND); /* Enable interrupts by setting the interrupt mask. */ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) | CntFull | TxUnderrun | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK); printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x" " interrupt %4.4x.\n", dev->name, (int)inl(ioaddr + COMMAND), (int)inl(ioaddr + GENCTL), (int)inl(ioaddr + INTSTAT)); return;}static void check_media(struct net_device *dev){ struct epic_private *ep = dev->priv; long ioaddr = dev->base_addr; int mii_lpa = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], MII_LPA) : 0; int negotiated = mii_lpa & ep->mii.advertising; int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; if (ep->mii.force_media) return; if (mii_lpa == 0xffff) /* Bogus read */ return; if (ep->mii.full_duplex != duplex) { ep->mii.full_duplex = duplex; printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" " partner capability of %4.4x.\n", dev->name, ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa); outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); }}static void epic_timer(unsigned long data){ struct net_device *dev = (struct net_device *)data; struct epic_private *ep = dev->priv; long ioaddr = dev->base_addr; int next_tick = 5*HZ; if (debug > 3) { printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n", dev->name, (int)inl(ioaddr + TxSTAT)); printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x " "IntStatus %4.4x RxStatus %4.4x.\n", dev->name, (int)inl(ioaddr + INTMASK), (int)inl(ioaddr + INTSTAT), (int)inl(ioaddr + RxSTAT)); } check_media(dev); ep->timer.expires = jiffies + next_tick; add_timer(&ep->timer);}static void epic_tx_timeout(struct net_device *dev){ struct epic_private *ep = dev->priv; long ioaddr = dev->base_addr; if (debug > 0) { printk(KERN_WARNING "%s: Transmit timeout using MII device, " "Tx status %4.4x.\n", dev->name, (int)inw(ioaddr + TxSTAT)); if (debug > 1) { printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n", dev->name, ep->dirty_tx, ep->cur_tx); } } if (inw(ioaddr + TxSTAT) & 0x10) { /* Tx FIFO underflow. */ ep->stats.tx_fifo_errors++; outl(RestartTx, ioaddr + COMMAND); } else { epic_restart(dev); outl(TxQueued, dev->base_addr + COMMAND); } dev->trans_start = jiffies; ep->stats.tx_errors++; if (!ep->tx_full) netif_wake_queue(dev);}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static void epic_init_ring(struct net_device *dev){ struct epic_private *ep = dev->priv; int i; ep->tx_full = 0; ep->dirty_tx = ep->cur_tx = 0; ep->cur_rx = ep->dirty_rx = 0; ep->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); /* Initialize all Rx descriptors. */ for (i = 0; i < RX_RING_SIZE; i++) { ep->rx_ring[i].rxstatus = 0; ep->rx_ring[i].buflength = cpu_to_le32(ep->rx_buf_sz); ep->rx_ring[i].next = ep->rx_ring_dma + (i+1)*sizeof(struct epic_rx_desc); ep->rx_skbuff[i] = NULL; } /* Mark the last entry as wrapping the ring. */ ep->rx_ring[i-1].next = ep->rx_ring_dma; /* Fill in the Rx buffers. Handle allocation failure gracefully. */ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb = dev_alloc_skb(ep->rx_buf_sz); ep->rx_skbuff[i] = skb; if (skb == NULL) break; skb_reserve(skb, 2); /* 16 byte align the IP header. */ ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev, skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE); ep->rx_ring[i].rxstatus = cpu_to_le32(DescOwn); } ep->dirty_rx = (unsigned int)(i - RX_RING_SIZE); /* The Tx buffer descriptor is filled in as needed, but we do need to clear the ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { ep->tx_skbuff[i] = NULL; ep->tx_ring[i].txstatus = 0x0000; ep->tx_ring[i].next = ep->tx_ring_dma + (i+1)*sizeof(struct epic_tx_desc); } ep->tx_ring[i-1].next = ep->tx_ring_dma; return;}static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct epic_private *ep = dev->priv; int entry, free_count; u32 ctrl_word; unsigned long flags; if (skb_padto(skb, ETH_ZLEN)) return 0; /* Caution: the write order is important here, set the field with the "ownership" bit last. */ /* Calculate the next Tx descriptor entry. */ spin_lock_irqsave(&ep->lock, flags); free_count = ep->cur_tx - ep->dirty_tx; entry = ep->cur_tx % TX_RING_SIZE; ep->tx_skbuff[entry] = skb; ep->tx_ring[entry].bufaddr = pci_map_single(ep->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); if (free_count < TX_QUEUE_LEN/2) {/* Typical path */ ctrl_word = cpu_to_le32(0x100000); /* No interrupt */ } else if (free_count == TX_QUEUE_LEN/2) { ctrl_word = cpu_to_le32(0x140000); /* Tx-done intr. */ } else if (free_count < TX_QUEUE_LEN - 1) { ctrl_word = cpu_to_le32(0x100000); /* No Tx-done intr. */ } else { /* Leave room for an additional entry. */ ctrl_word = cpu_to_le32(0x140000); /* Tx-done intr. */ ep->tx_full = 1; } ep->tx_ring[entry].buflength = ctrl_word | cpu_to_le32(skb->len); ep->tx_ring[entry].txstatus = ((skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN) << 16) | cpu_to_le32(DescOwn); ep->cur_tx++; if (ep->tx_full) netif_stop_queue(dev); spin_unlock_irqrestore(&ep->lock, flags); /* Trigger an immediate transmit demand. */ outl(TxQueued, dev->base_addr + COMMAND); dev->trans_start = jiffies; if (debug > 4) printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, " "flag %2.2x Tx status %8.8x.\n", dev->name, (int)skb->len, entry, ctrl_word, (int)inl(dev->base_addr + TxSTAT)); return 0;}static void epic_tx_error(struct net_device *dev, struct epic_private *ep, int status){ struct net_device_stats *stats = &ep->stats;#ifndef final_version /* There was an major error, log it. */ if (debug > 1) printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", dev->name, status);#endif stats->tx_errors++; if (status & 0x1050) stats->tx_aborted_errors++; if (status & 0x0008) stats->tx_carrier_errors++; if (status & 0x0040) stats->tx_window_errors++; if (status & 0x0010) stats->tx_fifo_errors++;}static void epic_tx(struct net_device *dev, struct epic_private *ep){ unsigned int dirty_tx, cur_tx; /* * Note: if this lock becomes a problem we can narrow the locked * region at the cost of occasionally grabbing the lock more times. */ cur_tx = ep->cur_tx; for (dirty_tx = ep->dirty_tx; cur_tx - dirty_tx > 0; dirty_tx++) { struct sk_buff *skb; int entry = dirty_tx % TX_RING_SIZE; int txstatus = le32_to_cpu(ep->tx_ring[entry].txstatus); if (txstatus & DescOwn) break; /* It still hasn't been Txed */ if (likely(txstatus & 0x0001)) { ep->stats.collisions += (txstatus >> 8) & 15; ep->stats.tx_packets++; ep->stats.tx_bytes += ep->tx_skbuff[entry]->len; } else epic_tx_error(dev, ep, txstatus); /* Free the original skb. */ skb = ep->tx_skbuff[entry]; pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); ep->tx_skbuff[entry] = NULL; }#ifndef final_version if (cur_tx - dirty_tx > TX_RING_SIZE) { printk(KERN_WARNING "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", dev->name, dirty_tx, cur_tx, ep->tx_full); dirty_tx += TX_RING_SIZE; }#endif ep->dirty_tx = dirty_tx; if (ep->tx_full && cur_tx - dirty_tx < TX_QUEUE_LEN - 4) { /* The ring is no longer full, allow new TX entries. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?