diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Config.in | 3 | ||||
-rw-r--r-- | drivers/net/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/Space.c | 4 | ||||
-rw-r--r-- | drivers/net/ioc3-eth.c | 873 |
4 files changed, 884 insertions, 0 deletions
diff --git a/drivers/net/Config.in b/drivers/net/Config.in index b091fc5f0..5dfea0a88 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -56,6 +56,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then tristate ' MIPS JAZZ onboard SONIC Ethernet support' CONFIG_MIPS_JAZZ_SONIC fi + if [ "$CONFIG_SGI_IP27" = "y" ]; then + bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH + fi bool ' 3COM cards' CONFIG_NET_VENDOR_3COM if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then tristate ' 3c501 support' CONFIG_EL1 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4589d0ee3..4c40cdcac 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -903,6 +903,10 @@ else endif endif +ifeq ($(CONFIG_SGI_IOC3_ETH),y) +L_OBJS += ioc3-eth.o +endif + ifeq ($(CONFIG_BAGETLANCE),y) L_OBJS += bagetlance.o else diff --git a/drivers/net/Space.c b/drivers/net/Space.c index a10d519f5..31182b810 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -83,6 +83,7 @@ extern int ni5010_probe(struct net_device *); extern int ni52_probe(struct net_device *); extern int ni65_probe(struct net_device *); extern int sonic_probe(struct net_device *); +extern int ioc3_probe(struct net_device *); extern int SK_init(struct net_device *); extern int seeq8005_probe(struct net_device *); extern int tc59x_probe(struct net_device *); @@ -487,6 +488,9 @@ struct devprobe mips_probes[] __initdata = { #ifdef CONFIG_MIPS_JAZZ_SONIC {sonic_probe, 0}, #endif +#ifdef CONFIG_SGI_IOC3_ETH + {ioc3_probe, 0}, +#endif #ifdef CONFIG_DECLANCE /* DECstation on-board controller */ {dec_lance_probe, 0}, /* and maybe TURBOchannel option boards */ #endif diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c new file mode 100644 index 000000000..6d4585b71 --- /dev/null +++ b/drivers/net/ioc3-eth.c @@ -0,0 +1,873 @@ +/* $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_set_multicast_list() should use the hash filter when possible. + * - 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. + * - 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 int ioc3_close(struct net_device *dev); + +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 8 +#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); +nic[7] = 0x42; + + 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 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 - sizeof(struct ioc3_erxbuf)); + 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_DMA|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 - + sizeof(struct ioc3_erxbuf)); + w0 = rxb->w0; + } + ip->rx_pi = n_entry; + ip->rx_ci = rx_entry; + + return; +} + +static 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; +} + +/* 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; + + dev->interrupt = 1; + 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 (dev->tbusy && (TX_BUFFS_AVAIL(ip) >= 0)) { + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + + __cli(); + ioc3->eier = eier; + dev->interrupt = 0; + + 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) +{ + 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, GFP_DMA); + if (!skb) + continue; + + p->rx_skbs[i] = skb; + skb->dev = dev; + + /* Because we reserve afterwards. */ + skb_put(skb, (1664 + RX_OFFSET)); + rxr[i] = (0xa5UL << 56) | + ((unsigned long) skb->data & 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->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 = (4 << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN | EMCR_TXEN + | EMCR_RXDMAEN | EMCR_RXEN; + ioc3->eier = EISR_RXTIMERINT | EISR_TXEXPLICIT; /* Interrupts ... */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + 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 (test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + int tickssofar = jiffies - dev->trans_start; + + if (tickssofar >= 40) { + printk("%s: transmit timed out.\n", dev->name); + dev->tbusy = 0; + dev->trans_start = jiffies; + } + return 1; + } + + 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)) + dev->tbusy = 0; + + return 0; +} + +static int +ioc3_close(struct net_device *dev) +{ + struct ioc3_private *ip = dev->priv; + struct ioc3 *ioc3 = ip->regs; + + dev->start = 0; + dev->tbusy = 1; + + 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"); +} + +/* + * Given a multicast ethernet address, this routine calculates the + * address's bit index in the logical address filter mask + */ +#define CRC_MASK 0xEDB88320 + +static int +ioc3_lafhash(u_char *addr, int len) +{ + unsigned int crc; + unsigned char byte; + int bits; + unsigned int temp = 0; + + 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; +} +#endif + +static void ioc3_set_multicast_list(struct net_device *dev) +{ + struct ioc3_private *p; + struct ioc3 *ioc3; + u32 emcr; + + p = dev->priv; + ioc3 = p->regs; + + emcr = ioc3->emcr & ~EMCR_PROMISC; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + emcr |= EMCR_PROMISC; + ioc3->emcr = emcr; + } else if ((dev->mc_count > 128) || (dev->flags & IFF_ALLMULTI)) { + /* Too many for hashing to make sense, skip computing all the + hashes and just accept all packets. */ + ioc3->ehar_h = ioc3->ehar_l = 0xffffffff; + } else { + /* This is cheating but at least it works for now ... */ + if (dev->mc_count) { + printk(KERN_DEBUG "%s: Yikes, implement " + "ioc3_set_multicast_list for real.\n", + dev->name); + ioc3->ehar_h = ioc3->ehar_l = 0xffffffff; + } else + ioc3->ehar_h = ioc3->ehar_l = 0; + } +} + +#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 */ |