patch-2.3.48 linux/drivers/net/ioc3-eth.c
Next file: linux/drivers/net/irda/nsc-ircc.c
Previous file: linux/drivers/net/hamradio/6pack.c
Back to the patch index
Back to the overall index
- Lines: 906
- Date:
Thu Feb 24 22:49:19 2000
- Orig file:
v2.3.47/linux/drivers/net/ioc3-eth.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.47/linux/drivers/net/ioc3-eth.c linux/drivers/net/ioc3-eth.c
@@ -0,0 +1,905 @@
+/* $Id$
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Driver for SGI's IOC3 based Ethernet cards as found in the PCI card.
+ *
+ * Copyright (C) 1999, 2000 Ralf Baechle
+ * Copyright (C) 1995, 1999, 2000 by Silicon Graphics, Inc.
+ *
+ * Reporting bugs:
+ *
+ * If you find problems with this drivers, then if possible do the
+ * following. Hook up a terminal to the MSC port, send an NMI to the CPUs
+ * by typing ^Tnmi (where ^T stands for <CTRL>-T). You'll see something
+ * like:
+ * 1A 000:
+ * 1A 000: *** NMI while in Kernel and no NMI vector installed on node 0
+ * 1A 000: *** Error EPC: 0xffffffff800265e4 (0xffffffff800265e4)
+ * 1A 000: *** Press ENTER to continue.
+ *
+ * Next enter the command ``lw i:0x86000f0 0x18'' and include this
+ * commands output which will look like below with your bugreport.
+ *
+ * 1A 000: POD MSC Dex> lw i:0x86000f0 0x18
+ * 1A 000: 92000000086000f0: 0021f28c 00000000 00000000 00000000
+ * 1A 000: 9200000008600100: a5000000 01cde000 00000000 000004e0
+ * 1A 000: 9200000008600110: 00000650 00000000 00110b15 00000000
+ * 1A 000: 9200000008600120: 006d0005 77bbca0a a5000000 01ce0000
+ * 1A 000: 9200000008600130: 80000500 00000500 00002538 05690008
+ * 1A 000: 9200000008600140: 00000000 00000000 000003e1 0000786d
+ *
+ * To do:
+ *
+ * - ioc3_close() should attempt to shutdown the adapter somewhat more
+ * gracefully.
+ * - Free rings and buffers when closing or before re-initializing rings.
+ * - Handle allocation failures in ioc3_alloc_skb() more gracefully.
+ * - Handle allocation failures in ioc3_init_rings().
+ * - Maybe implement private_ioctl().
+ * - Use prefetching for large packets. What is a good lower limit for
+ * prefetching?
+ * - We're probably allocating a bit too much memory.
+ * - Workarounds for various PHYs.
+ * - Proper autonegotiation.
+ * - What exactly is net_device_stats.tx_dropped supposed to count?
+ * - Use hardware checksums.
+ * - Convert to using the PCI infrastructure / IOC3 meta driver.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/sn/types.h>
+#include <asm/sn/sn0/addrs.h>
+#include <asm/sn/sn0/hubni.h>
+#include <asm/sn/sn0/hubio.h>
+#include <asm/sn/klconfig.h>
+#include <asm/ioc3.h>
+#include <asm/sn/sn0/ip27.h>
+#include <asm/pci/bridge.h>
+
+/* 32 RX buffers. This is tunable in the range of 16 <= x < 512. */
+#define RX_BUFFS 32
+
+static void ioc3_set_multicast_list(struct net_device *dev);
+static int ioc3_open(struct net_device *dev);
+static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void ioc3_timeout(struct net_device *dev);
+static int ioc3_close(struct net_device *dev);
+static inline unsigned int ioc3_hash(const unsigned char *addr);
+
+static const char ioc3_str[] = "IOC3 Ethernet";
+
+/* Private per NIC data of the driver. */
+struct ioc3_private {
+ struct ioc3 *regs;
+ int phy;
+ unsigned long rxr; /* pointer to receiver ring */
+ struct ioc3_etxd *txr;
+ struct sk_buff *rx_skbs[512];
+ struct sk_buff *tx_skbs[128];
+ struct net_device_stats stats;
+ int rx_ci; /* RX consumer index */
+ int rx_pi; /* RX producer index */
+ int tx_ci; /* TX consumer index */
+ int tx_pi; /* TX producer index */
+};
+
+/* We use this to acquire receive skb's that we can DMA directly into. */
+#define ALIGNED_RX_SKB_ADDR(addr) \
+ ((((unsigned long)(addr) + (128 - 1)) & ~(128 - 1)) - (unsigned long)(addr))
+
+#define ioc3_alloc_skb(__length, __gfp_flags) \
+({ struct sk_buff *__skb; \
+ __skb = alloc_skb((__length) + 128, (__gfp_flags)); \
+ if (__skb) { \
+ int __offset = ALIGNED_RX_SKB_ADDR(__skb->data); \
+ if(__offset) \
+ skb_reserve(__skb, __offset); \
+ } \
+ __skb; \
+})
+
+/* BEWARE: The IOC3 documentation documents the size of rx buffers as
+ 1644 while it's actually 1664. This one was nasty to track down ... */
+#define RX_OFFSET 10
+#define RX_BUF_ALLOC_SIZE (1664 + RX_OFFSET + 128)
+
+/* DMA barrier to separate cached and uncached accesses. */
+#define BARRIER() \
+ __asm__("sync" ::: "memory")
+
+/* This El Cheapo implementatin of TX_BUFFS_AVAIL may leave on entry unused.
+ Since the TX ring has 128 entries which is fairly large we don't care and
+ use this, more efficient implementation. */
+#define TX_BUFFS_AVAIL(ip) \
+({ \
+ struct ioc3_private *_ip = (ip); \
+ ((512 + _ip->tx_ci + 1) - _ip->tx_pi) & 511; \
+})
+//#undef TX_BUFFS_AVAIL
+//#define TX_BUFFS_AVAIL(ip) (1)
+
+
+#define IOC3_SIZE 0x100000
+
+#define ioc3_r(reg) \
+({ \
+ u32 __res; \
+ __res = ioc3->reg; \
+ __res; \
+})
+
+#define ioc3_w(reg,val) \
+do { \
+ (ioc3->reg = (val)); \
+} while(0)
+
+static inline u32
+mcr_pack(u32 pulse, u32 sample)
+{
+ return (pulse << 10) | (sample << 2);
+}
+
+static int
+nic_wait(struct ioc3 *ioc3)
+{
+ u32 mcr;
+
+ do {
+ mcr = ioc3_r(mcr);
+ } while (!(mcr & 2));
+
+ return mcr & 1;
+}
+
+static int
+nic_reset(struct ioc3 *ioc3)
+{
+ int presence;
+
+ ioc3_w(mcr, mcr_pack(500, 65));
+ presence = nic_wait(ioc3);
+
+ ioc3_w(mcr, mcr_pack(0, 500));
+ nic_wait(ioc3);
+
+ return presence;
+}
+
+/*
+ * Read a byte from an iButton device
+ */
+static u32
+nic_read_byte(struct ioc3 *ioc3)
+{
+ u32 result = 0;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ ioc3_w(mcr, mcr_pack(6, 13));
+ result = (result >> 1) | (nic_wait(ioc3) << 7);
+
+ ioc3_w(mcr, mcr_pack(0, 100));
+ nic_wait(ioc3);
+ }
+
+ return result;
+}
+
+/*
+ * Write a byte to an iButton device
+ */
+static void
+nic_write_byte(struct ioc3 *ioc3, int byte)
+{
+ int i, bit;
+
+ for (i = 8; i; i--) {
+ bit = byte & 1;
+ byte >>= 1;
+
+ if (bit)
+ ioc3_w(mcr, mcr_pack(6, 110));
+ else
+ ioc3_w(mcr, mcr_pack(80, 30));
+ nic_wait(ioc3);
+ }
+}
+
+static void nic_show_regnr(struct ioc3 *ioc3)
+{
+ const char *type;
+ u8 regnr[8];
+ int i;
+
+ nic_write_byte(ioc3, 0x33);
+ for (i = 0; i < 8; i++)
+ regnr[i] = nic_read_byte(ioc3);
+
+ switch(regnr[0]) {
+ case 0x01: type = "DS1990A"; break;
+ case 0x91: type = "DS1981U"; break;
+ default: type = "unknown"; break;
+ }
+
+ printk("Found %s NIC, registration number "
+ "%02x:%02x:%02x:%02x:%02x:%02x, CRC %02x.\n", type,
+ regnr[1], regnr[2], regnr[3], regnr[4], regnr[5], regnr[6],
+ regnr[7]);
+}
+
+/*
+ * Read the NIC (Number-In-a-Can) device.
+ */
+static void ioc3_get_eaddr(struct net_device *dev, struct ioc3 *ioc3)
+{
+ u8 nic[14];
+ int i;
+
+ ioc3_w(gpcr_s, (1 << 21));
+
+ nic_reset(ioc3);
+ nic_show_regnr(ioc3);
+
+ nic_reset(ioc3);
+ nic_write_byte(ioc3, 0xcc);
+ nic_write_byte(ioc3, 0xf0);
+ nic_write_byte(ioc3, 0x00);
+ nic_write_byte(ioc3, 0x00);
+
+ for (i = 13; i >= 0; i--)
+ nic[i] = nic_read_byte(ioc3);
+
+ printk("Ethernet address is ");
+ for (i = 2; i < 8; i++) {
+ dev->dev_addr[i - 2] = nic[i];
+ printk("%02x", nic[i]);
+ if (i < 7)
+ printk(":");
+ }
+ printk(".\n");
+}
+
+static u16 mii_read(struct ioc3 *ioc3, int phy, int reg)
+{
+ while (ioc3->micr & MICR_BUSY);
+ ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG;
+ while (ioc3->micr & MICR_BUSY);
+
+ return ioc3->midr & MIDR_DATA_MASK;
+}
+
+static void mii_write(struct ioc3 *ioc3, int phy, int reg, u16 data)
+{
+ while (ioc3->micr & MICR_BUSY);
+ ioc3->midr = data;
+ ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg;
+ while (ioc3->micr & MICR_BUSY);
+}
+
+static struct net_device_stats *ioc3_get_stats(struct net_device *dev)
+{
+ struct ioc3_private *ip = (struct ioc3_private *) dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+
+ ip->stats.collisions += (ioc3->etcdc & ETCDC_COLLCNT_MASK);
+ return &ip->stats;
+}
+
+static inline void
+ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
+{
+ struct sk_buff *skb, *new_skb;
+ int rx_entry, n_entry, len;
+ struct ioc3_erxbuf *rxb;
+ unsigned long *rxr;
+ u32 w0, err;
+
+ rxr = (unsigned long *) ip->rxr; /* Ring base */
+ rx_entry = ip->rx_ci; /* RX consume index */
+ n_entry = ip->rx_pi;
+
+ skb = ip->rx_skbs[rx_entry];
+ rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET);
+ w0 = rxb->w0;
+
+ while (w0 & ERXBUF_V) {
+ ioc3->eisr = EISR_RXTIMERINT; /* Ack */
+ ioc3->eisr; /* Flush */
+
+ err = rxb->err; /* It's valid ... */
+ if (err & ERXBUF_GOODPKT) {
+ len = (w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff;
+ skb_trim(skb, len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ new_skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+ if (!new_skb) {
+ /* Ouch, drop packet and just recycle packet
+ to keep the ring filled. */
+ ip->stats.rx_dropped++;
+ new_skb = skb;
+ goto next;
+ }
+
+ new_skb->dev = dev;
+
+ /* Because we reserve afterwards. */
+ skb_put(new_skb, (1664 + RX_OFFSET));
+ rxb = (struct ioc3_erxbuf *) new_skb->data;
+ skb_reserve(new_skb, RX_OFFSET);
+
+ ip->stats.rx_packets++; /* Statistics */
+ ip->stats.rx_bytes += len;
+
+ goto next;
+ }
+ if (err & (ERXBUF_CRCERR | ERXBUF_FRAMERR | ERXBUF_CODERR |
+ ERXBUF_INVPREAMB | ERXBUF_BADPKT | ERXBUF_CARRIER)) {
+ /* We don't send the skbuf to the network layer, so
+ just recycle it. */
+ new_skb = skb;
+
+ if (err & ERXBUF_CRCERR) /* Statistics */
+ ip->stats.rx_crc_errors++;
+ if (err & ERXBUF_FRAMERR)
+ ip->stats.rx_frame_errors++;
+ ip->stats.rx_errors++;
+ }
+
+next:
+ ip->rx_skbs[n_entry] = new_skb;
+ rxr[n_entry] = (0xa5UL << 56) |
+ ((unsigned long) rxb & TO_PHYS_MASK);
+ rxb->w0 = 0; /* Clear valid flag */
+ n_entry = (n_entry + 1) & 511; /* Update erpir */
+ ioc3->erpir = (n_entry << 3) | ERPIR_ARM;
+
+ /* Now go on to the next ring entry. */
+ rx_entry = (rx_entry + 1) & 511;
+ skb = ip->rx_skbs[rx_entry];
+ rxb = (struct ioc3_erxbuf *) (skb->data - RX_OFFSET);
+ w0 = rxb->w0;
+ }
+ ip->rx_pi = n_entry;
+ ip->rx_ci = rx_entry;
+
+ return;
+}
+
+static inline void
+ioc3_tx(struct ioc3_private *ip, struct ioc3 *ioc3)
+{
+ int tx_entry, o_entry;
+ struct sk_buff *skb;
+ u32 etcir;
+
+ etcir = ioc3->etcir;
+ tx_entry = (etcir >> 7) & 127;
+ o_entry = ip->tx_ci;
+
+ while (o_entry != tx_entry) {
+ ioc3->eisr = EISR_TXEXPLICIT; /* Ack */
+ ioc3->eisr; /* Flush */
+
+ skb = ip->tx_skbs[o_entry];
+ ip->stats.tx_packets++;
+ ip->stats.tx_bytes += skb->len;
+ dev_kfree_skb(skb);
+ ip->tx_skbs[o_entry] = NULL;
+
+ o_entry = (o_entry + 1) & 127; /* Next */
+
+ etcir = ioc3->etcir; /* More pkts sent? */
+ tx_entry = (etcir >> 7) & 127;
+ }
+ ip->tx_ci = o_entry;
+}
+
+/*
+ * Deal with fatal IOC3 errors. For now let's panic. This condition might
+ * be caused by a hard or software problems, so we should try to recover
+ * more gracefully if this ever happens.
+ */
+static void
+ioc3_error(struct net_device *dev, struct ioc3_private *ip,
+ struct ioc3 *ioc3, u32 eisr)
+{
+ if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
+ if (eisr & EISR_RXMEMERR) {
+ panic("%s: RX PCI error.\n", dev->name);
+ }
+ if (eisr & EISR_TXMEMERR) {
+ panic("%s: TX PCI error.\n", dev->name);
+ }
+ }
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)_dev;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+ u32 eisr, eier;
+
+ ip = dev->priv;
+
+ eier = ioc3->eier; /* Disable eth ints */
+ ioc3->eier = 0;
+ eisr = ioc3->eisr;
+ __sti();
+
+ if (eisr & EISR_RXTIMERINT) {
+ ioc3_rx(dev, ip, ioc3);
+ }
+ if (eisr & EISR_TXEXPLICIT) {
+ ioc3_tx(ip, ioc3);
+ }
+ if (eisr & (EISR_RXMEMERR | EISR_TXMEMERR)) {
+ ioc3_error(dev, ip, ioc3, eisr);
+ }
+
+ if ((TX_BUFFS_AVAIL(ip) >= 0) && netif_queue_stopped(dev)) {
+ netif_wake_queue(dev);
+ }
+
+ __cli();
+ ioc3->eier = eier;
+
+ return;
+}
+
+int
+ioc3_eth_init(struct net_device *dev, struct ioc3_private *p, struct ioc3 *ioc3)
+{
+ u16 word, mii0, mii_status, mii2, mii3, mii4;
+ u32 vendor, model, rev;
+ int i, phy;
+
+ ioc3->emcr = EMCR_RST; /* Reset */
+ ioc3->emcr; /* flush WB */
+ udelay(4); /* Give it time ... */
+ ioc3->emcr = 0;
+
+ phy = -1;
+ for (i = 0; i < 32; i++) {
+ word = mii_read(ioc3, i, 2);
+ if ((word != 0xffff) & (word != 0x0000)) {
+ phy = i;
+ break; /* Found a PHY */
+ }
+ }
+ if (phy == -1) {
+ printk("Didn't find a PHY, goodbye.\n");
+ return -ENODEV;
+ }
+ p->phy = phy;
+
+ mii0 = mii_read(ioc3, phy, 0);
+ mii_status = mii_read(ioc3, phy, 1);
+ mii2 = mii_read(ioc3, phy, 2);
+ mii3 = mii_read(ioc3, phy, 3);
+ mii4 = mii_read(ioc3, phy, 4);
+ vendor = (mii2 << 12) | (mii3 >> 4);
+ model = (mii3 >> 4) & 0x3f;
+ rev = mii3 & 0xf;
+ printk("Ok, using PHY %d, vendor 0x%x, model %d, rev %d.\n",
+ phy, vendor, model, rev);
+ printk(KERN_INFO "%s: MII transceiver found at MDIO address "
+ "%d, config %4.4x status %4.4x.\n",
+ dev->name, phy, mii0, mii_status);
+
+ /* Autonegotiate 100mbit and fullduplex. */
+ mii_write(ioc3, phy, 0, mii0 | 0x3100);
+ mdelay(1000);
+ mii_status = mii_read(ioc3, phy, 1);
+
+ return 0; /* XXX */
+}
+
+/* To do: For reinit of the ring we have to cleanup old skbs first ... */
+static void
+ioc3_init_rings(struct net_device *dev, struct ioc3_private *p,
+ struct ioc3 *ioc3)
+{
+ struct ioc3_erxbuf *rxb;
+ unsigned long *rxr;
+ unsigned long ring;
+ int i;
+
+ /* Allocate and initialize rx ring. 4kb = 512 entries */
+ p->rxr = get_free_page(GFP_KERNEL);
+ rxr = (unsigned long *) p->rxr;
+
+ /* Now the rx buffers. The RX ring may be larger but we only
+ allocate 16 buffers for now. Need to tune this for performance
+ and memory later. */
+ for (i = 0; i < RX_BUFFS; i++) {
+ struct sk_buff *skb;
+
+ skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, 0);
+ if (!skb) {
+ show_free_areas();
+ continue;
+ }
+
+ p->rx_skbs[i] = skb;
+ skb->dev = dev;
+
+ /* Because we reserve afterwards. */
+ skb_put(skb, (1664 + RX_OFFSET));
+ rxb = (struct ioc3_erxbuf *) skb->data;
+ rxb->w0 = 0; /* Clear valid bit */
+ rxr[i] = (0xa5UL << 56) | ((unsigned long) rxb & TO_PHYS_MASK);
+ skb_reserve(skb, RX_OFFSET);
+ }
+
+ /* Now the rx ring base, consume & produce registers. */
+ ring = (0xa5UL << 56) | (p->rxr & TO_PHYS_MASK);
+ ioc3->erbr_h = ring >> 32;
+ ioc3->erbr_l = ring & 0xffffffff;
+ p->rx_ci = 0;
+ ioc3->ercir = (p->rx_ci << 3);
+ p->rx_pi = RX_BUFFS;
+ ioc3->erpir = (p->rx_pi << 3) | ERPIR_ARM;
+
+ /* Allocate and initialize tx rings. 16kb = 128 bufs. */
+ p->txr = (struct ioc3_etxd *)__get_free_pages(GFP_KERNEL, 2);
+ ring = (0xa5UL << 56) | ((unsigned long)p->txr & TO_PHYS_MASK);
+
+ /* Now the tx ring base, consume & produce registers. */
+ ioc3->etbr_h = ring >> 32;
+ ioc3->etbr_l = ring & 0xffffffff;
+ p->tx_pi = 0;
+ ioc3->etpir = (p->tx_pi << 7);
+ p->tx_ci = 0;
+ ioc3->etcir = (p->tx_pi << 7);
+ ioc3->etcir; /* Flush */
+}
+
+void
+ioc3_ssram_disc(struct ioc3_private *ip)
+{
+ struct ioc3 *ioc3 = ip->regs;
+ volatile u32 *ssram0 = &ioc3->ssram[0x0000];
+ volatile u32 *ssram1 = &ioc3->ssram[0x4000];
+ unsigned int pattern = 0x5555;
+
+ /* Assume the larger size SSRAM and enable parity checking */
+ ioc3->emcr |= (EMCR_BUFSIZ | EMCR_RAMPAR);
+
+ *ssram0 = pattern;
+ *ssram1 = ~pattern & IOC3_SSRAM_DM;
+
+ if ((*ssram0 & IOC3_SSRAM_DM) != pattern ||
+ (*ssram1 & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) {
+ /* set ssram size to 64 KB */
+ ioc3->emcr &= ~EMCR_BUFSIZ;
+ printk("IOC3 SSRAM has 64 kbyte.\n");
+ } else {
+ //ei->ei_ssram_bits = EMCR_BUFSIZ | EMCR_RAMPAR;
+ printk("IOC3 SSRAM has 64 kbyte.\n");
+ }
+}
+
+static void ioc3_probe1(struct net_device *dev, struct ioc3 *ioc3)
+{
+ struct ioc3_private *p;
+
+ dev = init_etherdev(dev, 0);
+ p = (struct ioc3_private *) kmalloc(sizeof(*p), GFP_KERNEL);
+ memset(p, 0, sizeof(*p));
+ dev->priv = p;
+ dev->irq = IOC3_ETH_INT;
+
+ p->regs = ioc3;
+
+ ioc3_eth_init(dev, p, ioc3);
+ ioc3_ssram_disc(p);
+ ioc3_get_eaddr(dev, ioc3);
+ ioc3_init_rings(dev, p, ioc3);
+
+ /* Misc registers */
+ ioc3->erbar = 0;
+ ioc3->etcsr = (17<<ETCSR_IPGR2_SHIFT) | (11<<ETCSR_IPGR1_SHIFT) | 21;
+ ioc3->etcdc; /* Clear on read */
+ ioc3->ercsr = 15; /* RX low watermark */
+ ioc3->ertr = 0; /* Interrupt immediately */
+ ioc3->emar_h = (dev->dev_addr[5] << 8) | dev->dev_addr[4];
+ ioc3->emar_l = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) |
+ (dev->dev_addr[1] << 8) | dev->dev_addr[0];
+ ioc3->ehar_h = ioc3->ehar_l = 0;
+ ioc3->ersr = 42; /* XXX should be random */
+ //ioc3->erpir = ERPIR_ARM;
+
+ /* The IOC3-specific entries in the device structure. */
+ dev->open = &ioc3_open;
+ dev->hard_start_xmit = &ioc3_start_xmit;
+ dev->tx_timeout = ioc3_timeout;
+ dev->watchdog_timeo = (400 * HZ) / 1000;
+ dev->stop = &ioc3_close;
+ dev->get_stats = &ioc3_get_stats;
+ dev->set_multicast_list = &ioc3_set_multicast_list;
+}
+
+int
+ioc3_probe(struct net_device *dev)
+{
+ static int initialized;
+ struct ioc3 *ioc3;
+ nasid_t nid;
+
+ if (initialized) /* Only initialize once ... */
+ return 0;
+ initialized++;
+
+ nid = get_nasid();
+ ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
+ ioc3_probe1(dev, ioc3);
+
+ return 0;
+}
+
+static int
+ioc3_open(struct net_device *dev)
+{
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
+ printk("%s: Can't get irq %d\n", dev->name, dev->irq);
+ restore_flags(flags);
+
+ return -EAGAIN;
+ }
+
+ //ioc3_eth_init(dev, p, ioc3);
+
+ ioc3->emcr = ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
+ EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
+ ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT | /* Interrupts ... */
+ EISR_RXMEMERR | EISR_TXMEMERR;
+
+ netif_wake_queue(dev);
+ restore_flags(flags);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned long data;
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+ unsigned int len;
+ struct ioc3_etxd *desc;
+ int produce;
+
+ if (!TX_BUFFS_AVAIL(ip)) {
+ return 1;
+ }
+
+ data = (unsigned long) skb->data;
+ len = skb->len;
+
+ produce = ip->tx_pi;
+ desc = &ip->txr[produce];
+
+ if (len <= 104) {
+ /* Short packet, let's copy it directly into the ring. */
+ memcpy(desc->data, skb->data, skb->len);
+ if (len < ETH_ZLEN) {
+ /* Very short packet, pad with zeros at the end. */
+ memset(desc->data + len, 0, ETH_ZLEN - len);
+ len = ETH_ZLEN;
+ }
+ desc->cmd = len | ETXD_INTWHENDONE | ETXD_D0V;
+ desc->bufcnt = len;
+ } if ((data ^ (data + len)) & 0x4000) {
+ unsigned long b2, s1, s2;
+
+ b2 = (data | 0x3fffUL) + 1UL;
+ s1 = b2 - data;
+ s2 = data + len - b2;
+
+ desc->cmd = len | ETXD_INTWHENDONE | ETXD_B1V | ETXD_B2V;
+ desc->bufcnt = (s1 << ETXD_B1CNT_SHIFT) |
+ (s2 << ETXD_B2CNT_SHIFT);
+ desc->p1 = (0xa5UL << 56) | (data & TO_PHYS_MASK);
+ desc->p2 = (0xa5UL << 56) | (data & TO_PHYS_MASK);
+ } else {
+ /* Normal sized packet that doesn't cross a page boundary. */
+ desc->cmd = len | ETXD_INTWHENDONE | ETXD_B1V;
+ desc->bufcnt = len << ETXD_B1CNT_SHIFT;
+ desc->p1 = (0xa5UL << 56) | (data & TO_PHYS_MASK);
+ }
+
+ BARRIER();
+
+ dev->trans_start = jiffies;
+ ip->tx_skbs[produce] = skb; /* Remember skb */
+ produce = (produce + 1) & 127;
+ ip->tx_pi = produce;
+ ioc3->etpir = produce << 7; /* Fire ... */
+
+ if (TX_BUFFS_AVAIL(ip))
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static void ioc3_timeout(struct net_device *dev)
+{
+ printk("%s: transmit timed out, resetting\n", dev->name);
+ /* XXX should reset device here. */
+
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+}
+
+static int
+ioc3_close(struct net_device *dev)
+{
+ struct ioc3_private *ip = dev->priv;
+ struct ioc3 *ioc3 = ip->regs;
+
+ netif_stop_queue(dev);
+
+ ioc3->emcr = 0; /* Shutup */
+ ioc3->eier = 0; /* Disable interrupts */
+ ioc3->eier; /* Flush */
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#if 0
+/* Initialize the NIC */
+static int
+ioc3_init(struct ioc3_private *p, struct pci_dev *dev)
+{
+#if 0
+ unsigned long base;
+ struct ioc3 *ioc3;
+
+ pci_set_master(dev);
+ base = dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
+ printk("Base address at %08lx\n", base);
+
+ p->regs = ioc3 = ioremap (base, IOC3_SIZE);
+ printk("Remapped base address to %08lx\n", (unsigned long) regs);
+
+ read_nic(ioc3);
+
+ return 0;
+#endif
+ panic(__FUNCTION__" has been called.\n");
+}
+#endif
+
+/*
+ * Given a multicast ethernet address, this routine calculates the
+ * address's bit index in the logical address filter mask
+ */
+#define CRC_MASK 0xEDB88320
+
+static inline unsigned int
+ioc3_hash(const unsigned char *addr)
+{
+ unsigned int temp = 0;
+ unsigned char byte;
+ unsigned int crc;
+ int bits, len;
+
+ len = ETH_ALEN;
+ for (crc = ~0; --len >= 0; addr++) {
+ byte = *addr;
+ for (bits = 8; --bits >= 0; ) {
+ if ((byte ^ crc) & 1)
+ crc = (crc >> 1) ^ CRC_MASK;
+ else
+ crc >>= 1;
+ byte >>= 1;
+ }
+ }
+
+ crc &= 0x3f; /* bit reverse lowest 6 bits for hash index */
+ for (bits = 6; --bits >= 0; ) {
+ temp <<= 1;
+ temp |= (crc & 0x1);
+ crc >>= 1;
+ }
+
+ return temp;
+}
+
+static void ioc3_set_multicast_list(struct net_device *dev)
+{
+ struct dev_mc_list *dmi = dev->mc_list;
+ char *addr = dmi->dmi_addr;
+ struct ioc3_private *p;
+ struct ioc3 *ioc3;
+ u64 ehar = 0;
+ int i;
+
+ p = dev->priv;
+ ioc3 = p->regs;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ /* Unconditionally log net taps. */
+ printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+ ioc3->emcr |= EMCR_PROMISC;
+ ioc3->emcr;
+ } else {
+ ioc3->emcr &= ~EMCR_PROMISC; /* Clear promiscuous. */
+ ioc3->emcr;
+
+ if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
+ /* Too many for hashing to make sense or we want all
+ multicast packets anyway, so skip computing all the
+ hashes and just accept all packets. */
+ ioc3->ehar_h = 0xffffffff;
+ ioc3->ehar_l = 0xffffffff;
+ } else {
+ for (i = 0; i < dev->mc_count; i++) {
+ dmi = dmi->next;
+
+ if (!(*addr & 1))
+ continue;
+
+ ehar |= (1 << ioc3_hash(addr));
+ }
+ ioc3->ehar_h = ehar >> 32;
+ ioc3->ehar_l = ehar & 0xffffffff;
+ }
+ }
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");
+MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
+
+int
+init_module(void)
+{
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_find_device(PCI_VENDOR_ID_SGI, 0x0003, dev))) {
+ ioc3_init(&p, dev);
+ }
+
+ return -EBUSY;
+}
+
+void
+cleanup_module(void)
+{
+ printk("cleanup_module called\n");
+}
+#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)