/* The user-configurable values.
These may be modified when a driver module is loaded.*/ staticint debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
Typical is a 64 element hash table based on the Ethernet CRC. */ staticconstint multicast_filter_limit = 32;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme. Settingto>1518effectivelydisablesthisfeature. Thischipcanreceiveintooffsetbuffers,sotheAlphadoesnot
need a copy-align. */ staticint rx_copybreak; staticint flowctrl=1;
/* media[] specifies the media type the NIC operates at. autosenseAutosensingactivemedia. 10mbps_hd10Mbpshalfduplex. 10mbps_fd10Mbpsfullduplex. 100mbps_hd100Mbpshalfduplex. 100mbps_fd100Mbpsfullduplex. 0Autosensingactivemedia. 110Mbpshalfduplex. 210Mbpsfullduplex. 3100Mbpshalfduplex. 4100Mbpsfullduplex.
*/ #define MAX_UNITS 8 staticchar *media[MAX_UNITS];
/* Operational parameters that are set at compile time. */
/* Keep the ring sizes a power of two for compile efficiency. Thecompilerwillconvert<unsigned>'%'<2^N>intoabitmask. MakingtheTxringtoolargedecreasestheeffectivenessofchannel bondingandpacketpriority,andmorethan128requiresmodifyingthe Txerrorrecovery.
Large receive rings merely waste memory. */ #define TX_RING_SIZE 32 #define TX_QUEUE_LEN (TX_RING_SIZE - 1) /* Limit ring entries actually used. */ #define RX_RING_SIZE 64 #define RX_BUDGET 32 #define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct netdev_desc) #define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct netdev_desc)
/* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (4*HZ) #define PKT_BUF_SZ 1536/* Size of each temporary Rx buffer.*/
/* Include files, designed to support most kernel versions 2.0.0 and later. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/init.h> #include <linux/bitops.h> #include <linux/uaccess.h> #include <asm/processor.h> /* Processor type for cache alignment. */ #include <asm/io.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/dma-mapping.h> #include <linux/crc32.h> #include <linux/ethtool.h> #include <linux/mii.h>
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Sundance Alta Ethernet driver");
MODULE_LICENSE("GPL");
module_param(debug, int, 0);
module_param(rx_copybreak, int, 0);
module_param_array(media, charp, NULL, 0);
module_param(flowctrl, int, 0);
MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
/* ST201 documentation states ASICCtrl is a 32bit register */
iowrite32 (reset_cmd | ioread32 (ioaddr), ioaddr); /* ST201 documentation states reset can take up to 1 ms */
countdown = 10 + 1; while (ioread32 (ioaddr) & (ResetBusy << 16)) { if (--countdown == 0) {
printk(KERN_WARNING "%s : reset not completed !!\n", dev->name); break;
}
udelay(100);
}
}
/* Fibre PHY? */ if (ioread32 (ioaddr + ASICCtrl) & 0x80) { /* Default 100Mbps Full */ if (np->an_enable) {
np->speed = 100;
np->mii_if.full_duplex = 1;
np->an_enable = 0;
}
} /* Reset PHY */
mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET);
mdelay (300); /* If flow control enabled, we need to advertise it.*/ if (np->flowctrl)
mdio_write (dev, np->phys[0], MII_ADVERTISE, np->mii_if.advertising | 0x0400);
mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); /* Force media type */ if (!np->an_enable) {
mii_ctl = 0;
mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
mii_ctl |= (np->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl);
printk (KERN_INFO "Override speed=%d, %s duplex\n",
np->speed, np->mii_if.full_duplex ? "Full" : "Half");
}
/* Perhaps move the reset here? */ /* Reset the chip to erase previous misconfiguration. */ if (netif_msg_hw(np))
printk("ASIC Control is %x.\n", ioread32(ioaddr + ASICCtrl));
sundance_reset(dev, 0x00ff << 16); if (netif_msg_hw(np))
printk("ASIC Control is now %x.\n", ioread32(ioaddr + ASICCtrl));
staticint change_mtu(struct net_device *dev, int new_mtu)
{ if (netif_running(dev)) return -EBUSY;
WRITE_ONCE(dev->mtu, new_mtu); return0;
}
#define eeprom_delay(ee_addr) ioread32(ee_addr) /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ staticint eeprom_read(void __iomem *ioaddr, int location)
{ int boguscnt = 10000; /* Typical 1900 ticks. */
iowrite16(0x0200 | (location & 0xff), ioaddr + EECtrl); do {
eeprom_delay(ioaddr + EECtrl); if (! (ioread16(ioaddr + EECtrl) & 0x8000)) { return ioread16(ioaddr + EEData);
}
} while (--boguscnt > 0); return0;
}
/* MII transceiver control section. ReadandwritetheMIIregistersusingsoftware-generatedserial MDIOprotocol.SeetheMIIspecificationsorDP83840Adatasheet fordetails.
Themaximumdataclockrateis2.5Mhz.Theminimumtimingisusually
met by back-to-back 33Mhz PCI cycles. */ #define mdio_delay() ioread8(mdio_addr)
/* Generate the preamble required for initial synchronization and
a few older transceivers. */ staticvoid mdio_sync(void __iomem *mdio_addr)
{ int bits = 32;
/* Establish sync by sending at least 32 logic ones. */ while (--bits >= 0) {
iowrite8(MDIO_WRITE1, mdio_addr);
mdio_delay();
iowrite8(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
mdio_delay();
}
}
staticint mdio_read(struct net_device *dev, int phy_id, int location)
{ struct netdev_private *np = netdev_priv(dev); void __iomem *mdio_addr = np->base + MIICtrl; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int i, retval = 0;
if (np->mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
iowrite8(dataval, mdio_addr);
mdio_delay();
iowrite8(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay();
} /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) {
iowrite8(MDIO_EnbIn, mdio_addr);
mdio_delay();
retval = (retval << 1) | ((ioread8(mdio_addr) & MDIO_Data) ? 1 : 0);
iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay();
} return (retval>>1) & 0xffff;
}
staticvoid mdio_write(struct net_device *dev, int phy_id, int location, int value)
{ struct netdev_private *np = netdev_priv(dev); void __iomem *mdio_addr = np->base + MIICtrl; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; int i;
if (np->mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
iowrite8(dataval, mdio_addr);
mdio_delay();
iowrite8(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay();
} /* Clear out extra bits. */ for (i = 2; i > 0; i--) {
iowrite8(MDIO_EnbIn, mdio_addr);
mdio_delay();
iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay();
}
}
staticint mdio_wait_link(struct net_device *dev, int wait)
{ int bmsr; int phy_id; struct netdev_private *np;
np = netdev_priv(dev);
phy_id = np->phys[0];
do {
bmsr = mdio_read(dev, phy_id, MII_BMSR); if (bmsr & 0x0004) return0;
mdelay(1);
} while (--wait > 0); return -1;
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ staticvoid init_ring(struct net_device *dev)
{ struct netdev_private *np = netdev_priv(dev); int i;
/* The interrupt handler cleans up after the Tx thread,
and schedule a Rx thread work */ static irqreturn_t intr_handler(int irq, void *dev_instance)
{ struct net_device *dev = (struct net_device *)dev_instance; struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->base; int hw_frame_id; int tx_cnt; int tx_status; int handled = 0; int i;
do { int intr_status = ioread16(ioaddr + IntrStatus);
iowrite16(intr_status, ioaddr + IntrStatus);
if (netif_msg_intr(np))
printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
dev->name, intr_status);
if (!(intr_status & DEFAULT_INTR)) break;
handled = 1;
if (intr_status & (IntrRxDMADone)) {
iowrite16(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
ioaddr + IntrEnable); if (np->budget < 0)
np->budget = RX_BUDGET;
tasklet_schedule(&np->rx_tasklet);
} if (intr_status & (IntrTxDone | IntrDrvRqst)) {
tx_status = ioread16 (ioaddr + TxStatus); for (tx_cnt=32; tx_status & 0x80; --tx_cnt) { if (netif_msg_tx_done(np))
printk
("%s: Transmit status is %2.2x.\n",
dev->name, tx_status); if (tx_status & 0x1e) { if (netif_msg_tx_err(np))
printk("%s: Transmit error status %4.4x.\n",
dev->name, tx_status);
dev->stats.tx_errors++; if (tx_status & 0x10)
dev->stats.tx_fifo_errors++; if (tx_status & 0x08)
dev->stats.collisions++; if (tx_status & 0x04)
dev->stats.tx_fifo_errors++; if (tx_status & 0x02)
dev->stats.tx_window_errors++;
/* **Thisresethasbeenverifiedon **DFE-580TXboards!phdm@macqel.be.
*/ if (tx_status & 0x10) { /* TxUnderrun */ /* Restart Tx FIFO and transmitter */
sundance_reset(dev, (NetworkReset|FIFOReset|TxReset) << 16); /* No need to reset the Tx pointer here */
} /* Restart the Tx. Need to make sure tx enabled */
i = 10; do {
iowrite16(ioread16(ioaddr + MACCtrl1) | TxEnable, ioaddr + MACCtrl1); if (ioread16(ioaddr + MACCtrl1) & TxEnabled) break;
mdelay(1);
} while (--i);
} /* Yup, this is a documentation bug. It cost me *hours*. */
iowrite16 (0, ioaddr + TxStatus); if (tx_cnt < 0) {
iowrite32(5000, ioaddr + DownCounter); break;
}
tx_status = ioread16 (ioaddr + TxStatus);
}
hw_frame_id = (tx_status >> 8) & 0xff;
} else {
hw_frame_id = ioread8(ioaddr + TxFrameId);
}
if (np->pci_dev->revision >= 0x14) {
spin_lock(&np->lock); for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { int entry = np->dirty_tx % TX_RING_SIZE; struct sk_buff *skb; int sw_frame_id;
sw_frame_id = (le32_to_cpu(
np->tx_ring[entry].status) >> 2) & 0xff; if (sw_frame_id == hw_frame_id &&
!(le32_to_cpu(np->tx_ring[entry].status)
& 0x00010000)) break; if (sw_frame_id == (hw_frame_id + 1) %
TX_RING_SIZE) break;
skb = np->tx_skbuff[entry]; /* Free the original skb. */
dma_unmap_single(&np->pci_dev->dev,
le32_to_cpu(np->tx_ring[entry].frag.addr),
skb->len, DMA_TO_DEVICE);
dev_consume_skb_irq(np->tx_skbuff[entry]);
np->tx_skbuff[entry] = NULL;
np->tx_ring[entry].frag.addr = 0;
np->tx_ring[entry].frag.length = 0;
}
spin_unlock(&np->lock);
} else {
spin_lock(&np->lock); for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { int entry = np->dirty_tx % TX_RING_SIZE; struct sk_buff *skb; if (!(le32_to_cpu(np->tx_ring[entry].status)
& 0x00010000)) break;
skb = np->tx_skbuff[entry]; /* Free the original skb. */
dma_unmap_single(&np->pci_dev->dev,
le32_to_cpu(np->tx_ring[entry].frag.addr),
skb->len, DMA_TO_DEVICE);
dev_consume_skb_irq(np->tx_skbuff[entry]);
np->tx_skbuff[entry] = NULL;
np->tx_ring[entry].frag.addr = 0;
np->tx_ring[entry].frag.length = 0;
}
spin_unlock(&np->lock);
}
if (netif_queue_stopped(dev) &&
np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { /* The ring is no longer full, clear busy flag. */
netif_wake_queue (dev);
} /* Abnormal error summary/uncommon events handlers. */ if (intr_status & (IntrPCIErr | LinkChange | StatsMax))
netdev_error(dev, intr_status);
} while (0); if (netif_msg_intr(np))
printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
dev->name, ioread16(ioaddr + IntrStatus)); return IRQ_RETVAL(handled);
}
staticvoid rx_poll(struct tasklet_struct *t)
{ struct netdev_private *np = from_tasklet(np, t, rx_tasklet); struct net_device *dev = np->ndev; int entry = np->cur_rx % RX_RING_SIZE; int boguscnt = np->budget; void __iomem *ioaddr = np->base; int received = 0;
/* If EOP is set on the next entry, it's a new packet. Send it up. */ while (1) { struct netdev_desc *desc = &(np->rx_ring[entry]);
u32 frame_status = le32_to_cpu(desc->status); int pkt_len;
if (--boguscnt < 0) { goto not_done;
} if (!(frame_status & DescOwn)) break;
pkt_len = frame_status & 0x1fff; /* Chip omits the CRC. */ if (netif_msg_rx_status(np))
printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n",
frame_status); if (frame_status & 0x001f4000) { /* There was a error. */ if (netif_msg_rx_err(np))
printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n",
frame_status);
dev->stats.rx_errors++; if (frame_status & 0x00100000)
dev->stats.rx_length_errors++; if (frame_status & 0x00010000)
dev->stats.rx_fifo_errors++; if (frame_status & 0x00060000)
dev->stats.rx_frame_errors++; if (frame_status & 0x00080000)
dev->stats.rx_crc_errors++; if (frame_status & 0x00100000) {
printk(KERN_WARNING "%s: Oversized Ethernet frame," " status %8.8x.\n",
dev->name, frame_status);
}
} else { struct sk_buff *skb; #ifndef final_version if (netif_msg_rx_status(np))
printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" ", bogus_cnt %d.\n",
pkt_len, boguscnt); #endif /* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak &&
(skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
dma_sync_single_for_cpu(&np->pci_dev->dev,
le32_to_cpu(desc->frag.addr),
np->rx_buf_sz, DMA_FROM_DEVICE);
skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len);
dma_sync_single_for_device(&np->pci_dev->dev,
le32_to_cpu(desc->frag.addr),
np->rx_buf_sz, DMA_FROM_DEVICE);
skb_put(skb, pkt_len);
} else {
dma_unmap_single(&np->pci_dev->dev,
le32_to_cpu(desc->frag.addr),
np->rx_buf_sz, DMA_FROM_DEVICE);
skb_put(skb = np->rx_skbuff[entry], pkt_len);
np->rx_skbuff[entry] = NULL;
}
skb->protocol = eth_type_trans(skb, dev); /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */
netif_rx(skb);
}
entry = (entry + 1) % RX_RING_SIZE;
received++;
}
np->cur_rx = entry;
refill_rx (dev);
np->budget -= received;
iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); return;
not_done:
np->cur_rx = entry;
refill_rx (dev); if (!received)
received = 1;
np->budget -= received; if (np->budget <= 0)
np->budget = RX_BUDGET;
tasklet_schedule(&np->rx_tasklet);
}
for (i = 2000; i > 0; i--) { if ((ioread16(ioaddr + ASIC_HI_WORD(ASICCtrl)) & ResetBusy) == 0) break;
mdelay(1);
}
#ifdef __i386__ if (netif_msg_hw(np)) {
printk(KERN_DEBUG " Tx ring at %8.8x:\n",
(int)(np->tx_ring_dma)); for (i = 0; i < TX_RING_SIZE; i++)
printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n",
i, np->tx_ring[i].status, np->tx_ring[i].frag.addr,
np->tx_ring[i].frag.length);
printk(KERN_DEBUG " Rx ring %8.8x:\n",
(int)(np->rx_ring_dma)); for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) {
printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
i, np->rx_ring[i].status, np->rx_ring[i].frag.addr,
np->rx_ring[i].frag.length);
}
} #endif/* __i386__ debugging only */
free_irq(np->pci_dev->irq, dev);
timer_delete_sync(&np->timer);
/* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) {
np->rx_ring[i].status = 0;
skb = np->rx_skbuff[i]; if (skb) {
dma_unmap_single(&np->pci_dev->dev,
le32_to_cpu(np->rx_ring[i].frag.addr),
np->rx_buf_sz, DMA_FROM_DEVICE);
dev_kfree_skb(skb);
np->rx_skbuff[i] = NULL;
}
np->rx_ring[i].frag.addr = cpu_to_le32(0xBADF00D0); /* poison */
} for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_ring[i].next_desc = 0;
skb = np->tx_skbuff[i]; if (skb) {
dma_unmap_single(&np->pci_dev->dev,
le32_to_cpu(np->tx_ring[i].frag.addr),
skb->len, DMA_TO_DEVICE);
dev_kfree_skb(skb);
np->tx_skbuff[i] = NULL;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.