patch-2.4.25 linux-2.4.25/drivers/net/sgiseeq.c

Next file: linux-2.4.25/drivers/net/sk98lin/Makefile
Previous file: linux-2.4.25/drivers/net/sb1250-mac.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.24/drivers/net/sgiseeq.c linux-2.4.25/drivers/net/sgiseeq.c
@@ -5,51 +5,45 @@
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
 #include <linux/ioport.h>
+#include <linux/socket.h>
 #include <linux/in.h>
+#include <linux/route.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
 
+#include <asm/byteorder.h>
 #include <asm/io.h>
-#include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <linux/errno.h>
-#include <asm/byteorder.h>
-
-#include <linux/socket.h>
-#include <linux/route.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
-#include <asm/sgi/sgihpc.h>
-#include <asm/sgi/sgint23.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
 #include <asm/sgialib.h>
 
 #include "sgiseeq.h"
 
-static char *version =
-	"sgiseeq.c: David S. Miller (dm@engr.sgi.com)\n";
+static char *version = "sgiseeq.c: David S. Miller (dm@engr.sgi.com)\n";
 
 static char *sgiseeqstr = "SGI Seeq8003";
 
-/* If you want speed, you do something silly, it always has worked
- * for me.  So, with that in mind, I've decided to make this driver
- * look completely like a stupid Lance from a driver architecture
- * perspective.  Only difference is that here our "ring buffer" looks
- * and acts like a real Lance one does but is layed out like how the
- * HPC DMA and the Seeq want it to.  You'd be surprised how a stupid
- * idea like this can pay off in performance, not to mention making
- * this driver 2,000 times easier to write. ;-)
+/*
+ * If you want speed, you do something silly, it always has worked for me.  So,
+ * with that in mind, I've decided to make this driver look completely like a
+ * stupid Lance from a driver architecture perspective.  Only difference is that
+ * here our "ring buffer" looks and acts like a real Lance one does but is
+ * layed out like how the HPC DMA and the Seeq want it to.  You'd be surprised
+ * how a stupid idea like this can pay off in performance, not to mention
+ * making this driver 2,000 times easier to write. ;-)
  */
 
 /* Tune these if we tend to run out often etc. */
@@ -79,9 +73,10 @@
 	signed int buf_vaddr;
 };
 
-/* Warning: This structure is layed out in a certain way because
- *          HPC dma descriptors must be 8-byte aligned.  So don't
- *          touch this without some care.
+/*
+ * Warning: This structure is layed out in a certain way because HPC dma
+ *          descriptors must be 8-byte aligned.  So don't touch this without
+ *          some care.
  */
 struct sgiseeq_init_block { /* Note the name ;-) */
 	/* Ptrs to the descriptors in KSEG1 uncached space. */
@@ -96,8 +91,8 @@
 struct sgiseeq_private {
 	volatile struct sgiseeq_init_block srings;
 	char *name;
-	volatile struct hpc3_ethregs *hregs;
-	volatile struct sgiseeq_regs *sregs;
+	struct hpc3_ethregs *hregs;
+	struct sgiseeq_regs *sregs;
 
 	/* Ring entry counters. */
 	unsigned int rx_new, tx_new;
@@ -108,17 +103,23 @@
 	unsigned char mode;
 
 	struct net_device_stats stats;
+
+	struct net_device *next_module;
+	spinlock_t tx_lock;
 };
 
-static inline void hpc3_eth_reset(volatile struct hpc3_ethregs *hregs)
+/* A list of all installed seeq devices, for removing the driver module. */
+static struct net_device *root_sgiseeq_dev;
+
+static inline void hpc3_eth_reset(struct hpc3_ethregs *hregs)
 {
-	hregs->rx_reset = (HPC3_ERXRST_CRESET | HPC3_ERXRST_CLRIRQ);
+	hregs->rx_reset = HPC3_ERXRST_CRESET | HPC3_ERXRST_CLRIRQ;
 	udelay(20);
 	hregs->rx_reset = 0;
 }
 
-static inline void reset_hpc3_and_seeq(volatile struct hpc3_ethregs *hregs,
-				       volatile struct sgiseeq_regs *sregs)
+static inline void reset_hpc3_and_seeq(struct hpc3_ethregs *hregs,
+				       struct sgiseeq_regs *sregs)
 {
 	hregs->rx_ctrl = hregs->tx_ctrl = 0;
 	hpc3_eth_reset(hregs);
@@ -128,15 +129,15 @@
 		       SEEQ_RCMD_IDRIB | SEEQ_RCMD_ICRC)
 
 static inline void seeq_go(struct sgiseeq_private *sp,
-			   volatile struct hpc3_ethregs *hregs,
-			   volatile struct sgiseeq_regs *sregs)
+			   struct hpc3_ethregs *hregs,
+			   struct sgiseeq_regs *sregs)
 {
 	sregs->rstat = sp->mode | RSTAT_GO_BITS;
 	hregs->rx_ctrl = HPC3_ERXCTRL_ACTIVE;
 }
 
 static inline void seeq_load_eaddr(struct net_device *dev,
-				   volatile struct sgiseeq_regs *sregs)
+				   struct sgiseeq_regs *sregs)
 {
 	int i;
 
@@ -169,7 +170,7 @@
 
 	/* Setup tx ring. */
 	for(i = 0; i < SEEQ_TX_BUFFERS; i++) {
-		if(!ib->tx_desc[i].tdma.pbuf) {
+		if (!ib->tx_desc[i].tdma.pbuf) {
 			unsigned long buffer;
 
 			buffer = (unsigned long) kmalloc(PKT_BUF_SZ, GFP_KERNEL);
@@ -177,9 +178,8 @@
 				return -ENOMEM;
 			ib->tx_desc[i].buf_vaddr = KSEG1ADDR(buffer);
 			ib->tx_desc[i].tdma.pbuf = PHYSADDR(buffer);
-//			flush_cache_all();
 		}
-		ib->tx_desc[i].tdma.cntinfo = (TCNTINFO_INIT);
+		ib->tx_desc[i].tdma.cntinfo = TCNTINFO_INIT;
 	}
 
 	/* And now the rx ring. */
@@ -192,11 +192,10 @@
 				return -ENOMEM;
 			ib->rx_desc[i].buf_vaddr = KSEG1ADDR(buffer);
 			ib->rx_desc[i].rdma.pbuf = PHYSADDR(buffer);
-//			flush_cache_all();
 		}
-		ib->rx_desc[i].rdma.cntinfo = (RCNTINFO_INIT);
+		ib->rx_desc[i].rdma.cntinfo = RCNTINFO_INIT;
 	}
-	ib->rx_desc[i - 1].rdma.cntinfo |= (HPCDMA_EOR);
+	ib->rx_desc[i - 1].rdma.cntinfo |= HPCDMA_EOR;
 	return 0;
 }
 
@@ -209,10 +208,10 @@
 	static int once;
 	struct sgiseeq_rx_desc *r = gpriv->srings.rx_desc;
 	struct sgiseeq_tx_desc *t = gpriv->srings.tx_desc;
-	volatile struct hpc3_ethregs *hregs = gpriv->hregs;
+	struct hpc3_ethregs *hregs = gpriv->hregs;
 	int i;
 
-	if(once)
+	if (once)
 		return;
 	once++;
 	printk("RING DUMP:\n");
@@ -248,9 +247,9 @@
 #define RDMACFG_INIT    (HPC3_ERXDCFG_FRXDC | HPC3_ERXDCFG_FEOP | HPC3_ERXDCFG_FIRQ)
 
 static int init_seeq(struct net_device *dev, struct sgiseeq_private *sp,
-		      volatile struct sgiseeq_regs *sregs)
+		     struct sgiseeq_regs *sregs)
 {
-	volatile struct hpc3_ethregs *hregs = sp->hregs;
+	struct hpc3_ethregs *hregs = sp->hregs;
 	int err;
 
 	reset_hpc3_and_seeq(hregs, sregs);
@@ -260,11 +259,11 @@
 
 	/* Setup to field the proper interrupt types. */
 	if (sp->is_edlc) {
-		sregs->tstat = (TSTAT_INIT_EDLC);
+		sregs->tstat = TSTAT_INIT_EDLC;
 		sregs->rw.wregs.control = sp->control;
 		sregs->rw.wregs.frame_gap = 0;
 	} else {
-		sregs->tstat = (TSTAT_INIT_SEEQ);
+		sregs->tstat = TSTAT_INIT_SEEQ;
 	}
 
 	hregs->rx_dconfig |= RDMACFG_INIT;
@@ -291,8 +290,8 @@
 }
 
 static inline void rx_maybe_restart(struct sgiseeq_private *sp,
-				    volatile struct hpc3_ethregs *hregs,
-				    volatile struct sgiseeq_regs *sregs)
+				    struct hpc3_ethregs *hregs,
+				    struct sgiseeq_regs *sregs)
 {
 	if (!(hregs->rx_ctrl & HPC3_ERXCTRL_ACTIVE)) {
 		hregs->rx_ndptr = PHYSADDR(&sp->srings.rx_desc[sp->rx_new]);
@@ -305,8 +304,8 @@
 				(rd) = &(sp)->srings.rx_desc[(sp)->rx_new])
 
 static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp,
-			      volatile struct hpc3_ethregs *hregs,
-			      volatile struct sgiseeq_regs *sregs)
+			      struct hpc3_ethregs *hregs,
+			      struct sgiseeq_regs *sregs)
 {
 	struct sgiseeq_rx_desc *rd;
 	struct sk_buff *skb = 0;
@@ -317,7 +316,7 @@
 
 	/* Service every received packet. */
 	for_each_rx(rd, sp) {
-		len = (PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3);
+		len = PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3;
 		pkt_pointer = (unsigned char *)(long)rd->buf_vaddr;
 		pkt_status = pkt_pointer[len + 2];
 
@@ -338,7 +337,7 @@
 				sp->stats.rx_packets++;
 				sp->stats.rx_bytes += len;
 			} else {
-				printk ("%s: Memory squeeze, deferring packet.\n",
+				printk (KERN_NOTICE "%s: Memory squeeze, deferring packet.\n",
 					dev->name);
 				sp->stats.rx_dropped++;
 			}
@@ -347,7 +346,7 @@
 		}
 
 		/* Return the entry to the ring pool. */
-		rd->rdma.cntinfo = (RCNTINFO_INIT);
+		rd->rdma.cntinfo = RCNTINFO_INIT;
 		sp->rx_new = NEXT_RX(sp->rx_new);
 	}
 	sp->srings.rx_desc[orig_end].rdma.cntinfo &= ~(HPCDMA_EOR);
@@ -356,7 +355,7 @@
 }
 
 static inline void tx_maybe_reset_collisions(struct sgiseeq_private *sp,
-					     volatile struct sgiseeq_regs *sregs)
+					     struct sgiseeq_regs *sregs)
 {
 	if (sp->is_edlc) {
 		sregs->rw.wregs.control = sp->control & ~(SEEQ_CTRL_XCNT);
@@ -365,7 +364,7 @@
 }
 
 static inline void kick_tx(struct sgiseeq_tx_desc *td,
-			   volatile struct hpc3_ethregs *hregs)
+			   struct hpc3_ethregs *hregs)
 {
 	/* If the HPC aint doin nothin, and there are more packets
 	 * with ETXD cleared and XIU set we must make very certain
@@ -383,8 +382,8 @@
 }
 
 static inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp,
-			      volatile struct hpc3_ethregs *hregs,
-			      volatile struct sgiseeq_regs *sregs)
+			      struct hpc3_ethregs *hregs,
+			      struct sgiseeq_regs *sregs)
 {
 	struct sgiseeq_tx_desc *td;
 	unsigned long status = hregs->tx_ctrl;
@@ -409,7 +408,7 @@
 		if (!(td->tdma.cntinfo & (HPCDMA_XIU)))
 			break;
 		if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) {
-			if(!(status & HPC3_ETXCTRL_ACTIVE)) {
+			if (!(status & HPC3_ETXCTRL_ACTIVE)) {
 				hregs->tx_ndptr = PHYSADDR(td);
 				hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;
 			}
@@ -426,8 +425,10 @@
 {
 	struct net_device *dev = (struct net_device *) dev_id;
 	struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv;
-	volatile struct hpc3_ethregs *hregs = sp->hregs;
-	volatile struct sgiseeq_regs *sregs = sp->sregs;
+	struct hpc3_ethregs *hregs = sp->hregs;
+	struct sgiseeq_regs *sregs = sp->sregs;
+
+	spin_lock(&sp->tx_lock);
 
 	/* Ack the IRQ and set software state. */
 	hregs->rx_reset = HPC3_ERXRST_CLRIRQ;
@@ -435,59 +436,47 @@
 	/* Always check for received packets. */
 	sgiseeq_rx(dev, sp, hregs, sregs);
 
-	/* Only check for tx acks iff we have something queued. */
+	/* Only check for tx acks if we have something queued. */
 	if (sp->tx_old != sp->tx_new)
 		sgiseeq_tx(dev, sp, hregs, sregs);
 
 	if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) {
 		netif_wake_queue(dev);
 	}
+	spin_unlock(&sp->tx_lock);
 }
 
 static int sgiseeq_open(struct net_device *dev)
 {
 	struct sgiseeq_private *sp = (struct sgiseeq_private *)dev->priv;
-	volatile struct sgiseeq_regs *sregs = sp->sregs;
-	unsigned long flags;
-	int err;
-
-	__save_and_cli(flags);
+	struct sgiseeq_regs *sregs = sp->sregs;
 
-	err = -EAGAIN;
-	if (request_irq(dev->irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) {
-		printk("Seeq8003: Can't get irq %d\n", dev->irq);
-		goto out;
-	}
-	err = init_seeq(dev, sp, sregs);
+	int err = init_seeq(dev, sp, sregs);
 	if (err)
-		goto out;
+		return err;
 
 	netif_start_queue(dev);
 
-out:
-	__restore_flags(flags);
-	return err;
+	return 0;
 }
 
 static int sgiseeq_close(struct net_device *dev)
 {
 	struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv;
-	volatile struct sgiseeq_regs *sregs = sp->sregs;
+	struct sgiseeq_regs *sregs = sp->sregs;
 
 	netif_stop_queue(dev);
 
 	/* Shutdown the Seeq. */
 	reset_hpc3_and_seeq(sp->hregs, sregs);
 
-	free_irq(dev->irq, dev);
-
 	return 0;
 }
 
 static inline int sgiseeq_reset(struct net_device *dev)
 {
 	struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv;
-	volatile struct sgiseeq_regs *sregs = sp->sregs;
+	struct sgiseeq_regs *sregs = sp->sregs;
 	int err;
 
 	err = init_seeq(dev, sp, sregs);
@@ -509,12 +498,12 @@
 static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv;
-	volatile struct hpc3_ethregs *hregs = sp->hregs;
+	struct hpc3_ethregs *hregs = sp->hregs;
 	unsigned long flags;
 	struct sgiseeq_tx_desc *td;
 	int skblen, len, entry;
 
-	save_and_cli(flags);
+	spin_lock_irqsave(&sp->tx_lock, flags);
 
 	/* Setup... */
 	skblen = skb->len;
@@ -530,22 +519,22 @@
 	 * 2) Do no allow the HPC to look at a new descriptor until
 	 *    we have completely set up it's state.  This means, do
 	 *    not clear HPCDMA_EOX in the current last descritptor
-	 *    until the one we are adding looks consistant and could
+	 *    until the one we are adding looks consistent and could
 	 *    be processes right now.
 	 * 3) The tx interrupt code must notice when we've added a new
 	 *    entry and the HPC got to the end of the chain before we
 	 *    added this new entry and restarted it.
 	 */
 	memcpy((char *)(long)td->buf_vaddr, skb->data, skblen);
-	if(len != skblen)
+	if (len != skblen)
 		memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen);
 	td->tdma.cntinfo = (len & HPCDMA_BCNT) |
-	                   (HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX);
+	                   HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX;
 	if (sp->tx_old != sp->tx_new) {
 		struct sgiseeq_tx_desc *backend;
 
 		backend = &sp->srings.tx_desc[PREV_TX(sp->tx_new)];
-		backend->tdma.cntinfo &= ~(HPCDMA_EOX);
+		backend->tdma.cntinfo &= ~HPCDMA_EOX;
 	}
 	sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */
 
@@ -558,14 +547,14 @@
 
 	if (!TX_BUFFS_AVAIL(sp))
 		netif_stop_queue(dev);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&sp->tx_lock, flags);
 
 	return 0;
 }
 
 static void timeout(struct net_device *dev)
 {
-	printk("%s: transmit timed out, resetting\n", dev->name);
+	printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name);
 	sgiseeq_reset(dev);
 
 	dev->trans_start = jiffies;
@@ -608,49 +597,56 @@
 	buf[i].rdma.pnext = PHYSADDR(&buf[0]);
 }
 
-static char onboard_eth_addr[6];
-
 #define ALIGNED(x)  ((((unsigned long)(x)) + 0xf) & ~(0xf))
 
-int sgiseeq_init(struct net_device *dev, struct sgiseeq_regs *sregs,
-		 struct hpc3_ethregs *hregs, int irq)
+int sgiseeq_init(struct hpc3_regs* regs, int irq)
 {
-	static unsigned version_printed;
-	int i;
+	struct net_device *dev;
 	struct sgiseeq_private *sp;
+	int err, i;
 
-	dev->priv = (struct sgiseeq_private *) get_free_page(GFP_KERNEL);
-	if (dev->priv == NULL)
-		return -ENOMEM;
-
-	if (!version_printed++)
-		printk(version);
-
-	printk("%s: SGI Seeq8003 ", dev->name);
+	dev = alloc_etherdev(0);
+	if (!dev) {
+		printk(KERN_ERR "Sgiseeq: Etherdev alloc failed, aborting.\n");
+		err = -ENOMEM;
+		goto err_out;
+	}
+	/* Make private data page aligned */
+	sp = (struct sgiseeq_private *) get_zeroed_page(GFP_KERNEL); 	 
+	if (!sp) {
+		printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n");
+		err = -ENOMEM;
+		goto err_out_free_dev;
+	}
+
+	if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) {
+		printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq);
+		err = -EAGAIN;
+		goto err_out_free_page;
+	}
+
+#define EADDR_NVOFS     250
+	for (i = 0; i < 3; i++) {
+		unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i);
 
-	for (i = 0; i < 6; i++)
-		printk("%2.2x%c",
-		       dev->dev_addr[i] = onboard_eth_addr[i],
-		       i == 5 ? ' ': ':');
-
-	printk("\n");
+		dev->dev_addr[2 * i]     = tmp >> 8;
+		dev->dev_addr[2 * i + 1] = tmp & 0xff;
+	}
 
-	sp = (struct sgiseeq_private *) dev->priv;
 #ifdef DEBUG
 	gpriv = sp;
 	gdev = dev;
 #endif
-	memset((char *)dev->priv, 0, sizeof(struct sgiseeq_private));
-	sp->sregs = sregs;
-	sp->hregs = hregs;
+	sp->sregs = (struct sgiseeq_regs *) &hpc3c0->eth_ext[0];
+	sp->hregs = &hpc3c0->ethregs;
 	sp->name = sgiseeqstr;
 
 	sp->srings.rx_desc = (struct sgiseeq_rx_desc *)
-	                     (KSEG1ADDR(ALIGNED(&sp->srings.rxvector[0])));
+	                     KSEG1ADDR(ALIGNED(&sp->srings.rxvector[0]));
 	dma_cache_wback_inv((unsigned long)&sp->srings.rxvector,
 	                    sizeof(sp->srings.rxvector));
 	sp->srings.tx_desc = (struct sgiseeq_tx_desc *)
-	                     (KSEG1ADDR(ALIGNED(&sp->srings.txvector[0])));
+	                     KSEG1ADDR(ALIGNED(&sp->srings.txvector[0]));
 	dma_cache_wback_inv((unsigned long)&sp->srings.txvector,
 	                    sizeof(sp->srings.txvector));
 
@@ -659,71 +655,78 @@
 	setup_tx_ring(sp->srings.tx_desc, SEEQ_TX_BUFFERS);
 
 	/* Reset the chip. */
-	hpc3_eth_reset((volatile struct hpc3_ethregs *) hregs);
+	hpc3_eth_reset(sp->hregs);
 
-	sp->is_edlc = !(sregs->rw.rregs.collision_tx[0] & 0xff);
-	if (sp->is_edlc) {
-		sp->control = (SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT |
-			       SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT |
-			       SEEQ_CTRL_ENCARR);
+	sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff);
+	if (sp->is_edlc)
+		sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT |
+			      SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT |
+			      SEEQ_CTRL_ENCARR;
+
+	dev->open		= sgiseeq_open;
+	dev->stop		= sgiseeq_close;
+	dev->hard_start_xmit	= sgiseeq_start_xmit;
+	dev->tx_timeout		= timeout;
+	dev->watchdog_timeo	= (200 * HZ) / 1000;
+	dev->get_stats		= sgiseeq_get_stats;
+	dev->set_multicast_list	= sgiseeq_set_multicast;
+	dev->irq		= irq;
+	dev->dma		= 0;
+	dev->priv		= sp;
+
+	if (register_netdev(dev)) {
+		printk(KERN_ERR "Sgiseeq: Cannot register net device, "
+		       "aborting.\n");
+		err = -ENODEV;
+		goto err_out_free_irq;
 	}
 
-	dev->open                 = sgiseeq_open;
-	dev->stop                 = sgiseeq_close;
-	dev->hard_start_xmit      = sgiseeq_start_xmit;
-	dev->tx_timeout           = timeout;
-	dev->watchdog_timeo       = (200 * HZ) / 1000;
-	dev->get_stats            = sgiseeq_get_stats;
-	dev->set_multicast_list   = sgiseeq_set_multicast;
-	dev->irq                  = irq;
-	dev->dma                  = 0;
-	ether_setup(dev);
+	printk(KERN_INFO "%s: SGI Seeq8003 ", dev->name);
+	for (i = 0; i < 6; i++)
+		printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':');
+
+	sp->next_module = root_sgiseeq_dev;
+	root_sgiseeq_dev = dev;
 
 	return 0;
-}
 
-static inline unsigned char str2hexnum(unsigned char c)
-{
-	if (c >= '0' && c <= '9')
-		return c - '0';
-	if (c >= 'a' && c <= 'f')
-		return c - 'a' + 10;
-	return 0; /* foo */
+err_out_free_irq:
+	free_irq(irq, dev);
+err_out_free_page:
+	free_page((unsigned long) sp);
+err_out_free_dev:
+	kfree(dev);
+
+err_out:
+	return err;
 }
 
-static inline void str2eaddr(unsigned char *ea, unsigned char *str)
+static int __init sgiseeq_probe(void)
 {
-	int i;
-
-	for (i = 0; i < 6; i++) {
-		unsigned char num;
+	printk(version);
 
-		if(*str == ':')
-			str++;
-		num = str2hexnum(*str++) << 4;
-		num |= (str2hexnum(*str++));
-		ea[i] = num;
-	}
+	/* On board adapter on 1st HPC is always present */
+	return sgiseeq_init(hpc3c0, SGI_ENET_IRQ);
 }
 
-int sgiseeq_probe(struct net_device *dev)
+static void __exit sgiseeq_exit(void)
 {
-	static int initialized;
-	char *ep;
-
-	if (initialized)	/* Already initialized? */
-		return 1;
-	initialized++;
+	struct net_device *next, *dev;
+	struct sgiseeq_private *sp;
+	int irq;
 
-	/* First get the ethernet address of the onboard interface from ARCS.
-	 * This is fragile; PROM doesn't like running from cache.
-	 * On MIPS64 it crashes for some other, yet unknown reason ...
-	 */
-	ep = ArcGetEnvironmentVariable("eaddr");
-	str2eaddr(onboard_eth_addr, ep);
-	return sgiseeq_init(dev,
-			    (struct sgiseeq_regs *) (KSEG1ADDR(0x1fbd4000)),
-			    &hpc3c0->ethregs, SGI_ENET_IRQ);
+	for (dev = root_sgiseeq_dev; dev; dev = next) {
+		sp = (struct sgiseeq_private *) dev->priv;
+		next = sp->next_module;
+		irq = dev->irq;
+		unregister_netdev(dev);
+		free_irq(irq, dev);
+		free_page((unsigned long) dev->priv);
+		kfree(dev);
+	}
 }
 
+module_init(sgiseeq_probe);
+module_exit(sgiseeq_exit);
+
 MODULE_LICENSE("GPL");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)