Arctic ethernet

David Gibson david at gibson.dropbear.id.au
Mon Dec 16 13:11:45 EST 2002


...and another driver for Arctic-2, this time for Ethernet (which is
actually on a debug board, no on the Arctic-2 itself).  It's based on
a RTL8019 chip and this driver was based on the ariadne2 driver.

diff -urN /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/Config.in linux-bartholomew/drivers/net/Config.in
--- /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/Config.in	2002-12-04 10:44:50.000000000 +1100
+++ linux-bartholomew/drivers/net/Config.in	2002-12-12 16:50:02.000000000 +1100
@@ -43,6 +43,9 @@
       if [ "$CONFIG_BEECH" = "y" ]; then
          tristate '  Beech onboard CS8900A Ethernet support' CONFIG_CS89x0
       fi
+      if [ "$CONFIG_ARCTIC2" = "y" ]; then
+	 tristate '  Arctic-II debug sled ethernet support' CONFIG_ARCTIC_ENET
+      fi
       if [ "$CONFIG_XILINX_OCP" = "y" ]; then
    	 tristate '  Xilinx on-chip ethernet' CONFIG_XILINX_ENET
       fi
diff -urN /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/Makefile linux-bartholomew/drivers/net/Makefile
--- /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/Makefile	2002-09-27 09:11:02.000000000 +1000
+++ linux-bartholomew/drivers/net/Makefile	2002-12-12 16:48:59.000000000 +1100
@@ -149,6 +149,7 @@
 obj-$(CONFIG_NET_SB1250_MAC) += sb1250-mac.o
 obj-$(CONFIG_GT64260_ETH) += gt64260_eth.o
 obj-$(CONFIG_NPNET) += npnet.o
+obj-$(CONFIG_ARCTIC_ENET) += arctic_enet.o 8390.o

 obj-$(CONFIG_PPP) += ppp_generic.o slhc.o
 obj-$(CONFIG_PPP_ASYNC) += ppp_async.o
diff -urN /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/arctic_enet.c linux-bartholomew/drivers/net/arctic_enet.c
--- /home/dgibson/kernel/linuxppc_2_4_devel/drivers/net/arctic_enet.c	Thu Jan 01 10:00:00 1970
+++ linux-bartholomew/drivers/net/arctic_enet.c	Fri Dec 13 14:03:36 2002
@@ -0,0 +1,658 @@
+/*
+ *  IPE405 (IBM IAP 405 chip evaluation board) Debug Support Board
+ *    Ehernet Driver
+ *  (C) Copyright 2001 by S.nishino (jl04348 at jp.ibm.com)  IBM-Japan
+ *
+ * ---------- Strategy ----------
+ *
+ *  This NIC is RTL8019AS, simply connected to External Bus Controller
+ *  of IAP 405 chip. As many folks of 8390 based NIC, 8390 core driver
+ *  is usable.  luckily, the following driver is already available for
+ *  Amiga zorro bus (however I don't know this architecture beyond
+ *  below), this is modified based on this driver (ariadne2).
+ *
+ * ---------- original header ----------
+ *  Amiga Linux/m68k Ariadne II Ethernet Driver
+ *
+ *  (C) Copyright 1998 by some Elitist 680x0 Users(TM)
+ *
+ *  ---------------------------------------------------------------------------
+ *
+ *  This program is based on all the other NE2000 drivers for Linux
+ *
+ *  ---------------------------------------------------------------------------
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of the Linux
+ *  distribution for more details.  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/irq.h>
+
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ppc4xx_pic.h>
+#if defined(CONFIG_ARCTIC2)
+#include <platforms/arctic2.h>
+#else
+#error The driver only works on Arctic
+#endif
+
+#include "8390.h"
+
+
+#define ARCTIC_ENET_BASE8	(ARCTIC2_FPGA8_PADDR + 256*1024)
+#define ARCTIC_ENET_BASE16	(ARCTIC2_FPGA16_PADDR + 0)
+
+#define ARCTIC_ENET_IOBASE	0x0300	/* io base offset from NIC region */
+
+#define ARCTIC_ENET_IRQ		29	/* irq number in UIC */
+#define ARCTIC_ENET_IRQ_MASK	(0x80000000 >> ARCTIC_ENET_IRQ)
+
+#define NE_BASE         (ARCTIC_ENET_BASE8 + ARCTIC_ENET_IOBASE)
+#define NE_BASE16       (ARCTIC_ENET_BASE16 + ARCTIC_ENET_IOBASE)
+
+/* 8390 register address */
+#define NE_CMD          (0x00)
+#define NE_DATAPORT     (0x10)	/* NatSemi-defined port window offset. */
+#define NE_DATAPORT16	(NE_DATAPORT / sizeof(u16))
+#define NE_RESET        (0x1f)	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT    (0x20)	/* region extent */
+
+#define NE_EN0_ISR      (0x07)
+#define NE_EN0_DCFG     (0x0e)
+
+#define NE_EN0_RSARLO   (0x08)
+#define NE_EN0_RSARHI   (0x09)
+#define NE_EN0_RCNTLO   (0x0a)
+#define NE_EN0_RXCR     (0x0c)
+#define NE_EN0_TXCR     (0x0d)
+#define NE_EN0_RCNTHI   (0x0b)
+#define NE_EN0_IMR      (0x0f)
+
+/* 8390 packet buffer page number */
+#define NESM_START_PG   0x40	/* First page of TX buffer */
+#define NESM_STOP_PG    0x80	/* Last page +1 of RX ring */
+
+static u8 *iobase8;
+static u16 *iobase16;
+
+static int arctic_enet_probe(struct net_device *dev);
+static int arctic_enet_init(struct net_device *dev);
+
+static int arctic_enet_open(struct net_device *dev);
+static int arctic_enet_close(struct net_device *dev);
+
+static void arctic_enet_reset_8390(struct net_device *dev);
+static void arctic_enet_get_8390_hdr(struct net_device *dev,
+				    struct e8390_pkt_hdr *hdr,
+				    int ring_page);
+static void arctic_enet_block_input(struct net_device *dev, int count,
+				   struct sk_buff *skb, int ring_offset);
+static void arctic_enet_block_output(struct net_device *dev,
+				    const int count,
+				    const unsigned char *buf,
+				    const int start_page);
+
+/* These macros will do something on Arctic-I if we ever add support
+ * for it back in */
+#define switch_16bit_bank()	do { } while (0)
+#define switch_8bit_bank()	do { } while (0)
+
+void p_dump(unsigned char *p, int sz)
+{
+	int i;
+	unsigned char *wp;
+
+	wp = p;
+
+	printk("------ PACKET START :  %d Bytes  ------ \n", sz);
+
+	for (i = 0; i < sz; i++) {
+		if (i % 16 == 0) {
+			printk("\n %04X: %02X ", i, *wp);
+		} else if (i % 16 == 15) {
+			printk("%02X", *wp);
+		} else {
+			printk("%02X ", *wp);
+		}
+		wp++;
+	}
+
+	printk("------ PACKET END   ------ \n");
+}
+
+/* Code for reading the MAC address from the Arctic ethernet based on
+ * similar code in PIBS */
+
+static void __init writereg_9346(volatile u8 *iobase, u8 value)
+{
+	/* Switch to register page 3 */
+	writeb(readb(iobase + NE_CMD) | 0xc0, iobase + NE_CMD);
+	writeb(value, iobase + 0x01);
+}
+
+static u8 __init readreg_9346(volatile u8 *iobase)
+{
+	/* Switch to register page 3 */
+	writeb(readb(iobase + NE_CMD) | 0xc0, iobase + NE_CMD);
+	return readb(iobase + 0x01);
+}
+
+static void __init write_bit_9346(volatile u8 *iobase, u8 bit)
+{
+	u8 mask = ~0x06;
+
+	writereg_9346(iobase, (readreg_9346(iobase) & mask) | bit);
+	udelay(1000);
+	writereg_9346(iobase, (readreg_9346(iobase) & mask) | bit | 0x04);
+	udelay(1000);
+}
+
+static u8 __init read_bit_9346(volatile u8 *iobase)
+{
+	u8 bit;
+	u8 mask = ~0x05;
+
+	mask = ~0x05;
+	writereg_9346(iobase, readreg_9346(iobase) & mask);
+	udelay(1000);
+	writereg_9346(iobase, (readreg_9346(iobase) & mask) | 0x04);
+	bit = readreg_9346(iobase) & 0x01;
+	udelay(1000);
+
+	return bit;
+}
+
+static u16 __init arctic_read_9346(volatile u8 *iobase, unsigned long addr)
+{
+	unsigned long flags;
+	int i;
+	u16 data;
+
+	local_irq_save(flags);
+
+	/* Put the chip into 8390 programming mode */
+	writereg_9346(iobase, (readreg_9346(iobase) & ~0xc0) | 0x80);
+	udelay(1000);
+
+	/* Send command (read 16-bit value) to EEPROM */
+	/* Bring CS Low */
+	writereg_9346(iobase, readreg_9346(iobase) & ~0x0f);
+	udelay(1000);
+	/* Bring CS High */
+	writereg_9346(iobase, (readreg_9346(iobase) & ~0x0f) | 0x08);
+	udelay(1000);
+
+	/* Send a 1 */
+	write_bit_9346(iobase, 0x02);
+	/* Send opcode 0b10 */
+	write_bit_9346(iobase, 0x02);
+	write_bit_9346(iobase, 0x00);
+	/* Send address to read */
+	for (i = 0; i < 6; i++) {
+		if (addr & 0x20)
+			write_bit_9346(iobase, 0x02);
+		else
+			write_bit_9346(iobase, 0x00);
+		addr <<= 1;
+	}
+
+	/* Read the value back, bit by bit */
+	data = 0;
+	for (i = 0; i < 16; i++) {
+		data <<= 1;
+		if (read_bit_9346(iobase))
+			data |= 0x1;
+	}
+
+	/* Bring CS Low */
+	writereg_9346(iobase, readreg_9346(iobase) & ~0x0f);
+	udelay(1000);
+	/* Bring the chip out of 8390 programming mode */
+	writereg_9346(iobase, readreg_9346(iobase) & ~0xc0);
+	udelay(1000);
+
+	/* Return to register page 0 */
+	writeb(readb(iobase + NE_CMD) & ~0xc0, iobase + NE_CMD);
+	udelay(1000);
+
+	local_irq_restore(flags);
+
+	return data;
+}
+
+static void __init arctic_get_macaddr(struct net_device *dev)
+{
+	u16 t0, t1, t2, v0, v1;
+
+	t0 = arctic_read_9346(iobase8, 0);
+	t1 = arctic_read_9346(iobase8, 2);
+	t2 = arctic_read_9346(iobase8, 4);
+	v0 = arctic_read_9346(iobase8, 6);
+	v1 = arctic_read_9346(iobase8, 8);
+
+	printk("arctic_enet:  %04x-%04x-%04x  (%04x/%04x)\n",
+	       t0, t1, t2, v0, v1);
+
+	if ( (v0 != 0x4d50) || (v1 != 0x5400) ) {
+		printk(KERN_WARNING "%s: MAC address is not set in EEPROM\n", dev->name);
+		return;
+	}
+
+	printk("%s: MAC address from EEPROM is %04x:%04x:%04x\n",
+	       dev->name, (unsigned)t0, (unsigned)t1, (unsigned)t2);
+
+	dev->dev_addr[0] = t0 >> 8;
+	dev->dev_addr[1] = t0 & 0xff;
+	dev->dev_addr[2] = t1 >> 8;
+	dev->dev_addr[3] = t1 & 0xff;
+	dev->dev_addr[4] = t2 >> 8;
+	dev->dev_addr[5] = t2 & 0xff;
+}
+
+int __init arctic_enet_probe(struct net_device *dev)
+{
+	unsigned long reset_start_time;
+
+	switch_8bit_bank();
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	reset_start_time = jiffies;
+
+	writeb(readb(iobase8 + NE_RESET), iobase8 + NE_RESET);
+
+	while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2 * HZ / 100) {
+			printk("arctic_enet: not found (no reset ack).\n");
+			return -ENODEV;
+		}
+
+	writeb(0xff, iobase8 + NE_EN0_ISR);	/* Ack all intr. */
+
+	arctic_get_macaddr(dev);
+
+	printk("arctic_enet: found at 0x%08x/0x%08x, MAC address "
+	       "%02x:%02x:%02x:%02x:%02x:%02x\n",
+	       NE_BASE, NE_BASE16,
+	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+	/* Hack to let 8390.c work properly - it assumes IO space
+	 * addresses */
+	dev->base_addr = (unsigned long)iobase8 - _IO_BASE;
+	dev->irq = ARCTIC_ENET_IRQ;
+
+	return 0;
+}
+
+static int __init arctic_enet_init(struct net_device *dev)
+{
+	static u32 arctic_enet_offsets[16] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	};
+
+	/* Since this irq is connected to uic as edge interrupt, its pending must be cleared. */
+	/* FIXME: it would be nice to get rid of the direct reference
+	 * to the 4xx irq structure */
+	ppc4xx_pic->ack(dev->irq);
+
+	/* Install the Interrupt handler */
+	if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk(" Unable to get memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Fill 8390 specific member for 8390 core driver
+	 */
+	ei_status.name = "RTL8019AS";
+	ei_status.tx_start_page = NESM_START_PG;
+	ei_status.stop_page = NESM_STOP_PG;
+	ei_status.word16 = 1;
+	ei_status.rx_start_page = NESM_START_PG + TX_PAGES;
+
+	ei_status.reset_8390 = &arctic_enet_reset_8390;
+	ei_status.block_input = &arctic_enet_block_input;
+	ei_status.block_output = &arctic_enet_block_output;
+	ei_status.get_8390_hdr = &arctic_enet_get_8390_hdr;
+	ei_status.reg_offset = arctic_enet_offsets;
+
+	NS8390_init(dev, 0);
+	return 0;
+}
+
+static int arctic_enet_open(struct net_device *dev)
+{
+	int err;
+	err = ei_open(dev);
+	if (err)
+		return err;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int arctic_enet_close(struct net_device *dev)
+{
+	int err;
+
+	err = ei_close(dev);
+	if (err)
+		return err;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void arctic_enet_reset_8390(struct net_device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (ei_debug > 1)
+		printk("resetting the 8390 t=%ld...", jiffies);
+
+	writeb(readb(iobase8 + NE_RESET), iobase8 + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2 * HZ / 100) {
+			printk("%s: ne_reset_8390() did not complete.\n",
+			       dev->name);
+			break;
+		}
+	writeb(ENISR_RESET, iobase8 + NE_EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void arctic_enet_get_8390_hdr(struct net_device *dev,
+				    struct e8390_pkt_hdr *hdr,
+				    int ring_page)
+{
+	int cnt;
+	u16 *ptrs;
+	unsigned char *ptrc;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_get_8390_hdr "
+		       "[DMAstat:%d][irqlock:%d].\n", dev->name,
+		       ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, iobase8 + NE_CMD);
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);
+	writeb(sizeof(struct e8390_pkt_hdr), iobase8 + NE_EN0_RCNTLO);
+	writeb(0, iobase8 + NE_EN0_RCNTHI);
+	writeb(0, iobase8 + NE_EN0_RSARLO);	/* On page boundary */
+	writeb(ring_page, iobase8 + NE_EN0_RSARHI);
+	writeb(E8390_RREAD + E8390_START, iobase8 + NE_CMD);
+
+	if (ei_status.word16) {
+		switch_16bit_bank();
+		ptrs = (u16 *) hdr;
+		for (cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr) >> 1);
+		     cnt++)
+			*ptrs++ = in_be16((u16 *) (iobase16 + NE_DATAPORT16));
+		switch_8bit_bank();
+	} else {
+
+		ptrc = (unsigned char *) hdr;
+		for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
+			*ptrc++ = readb(iobase8 + NE_DATAPORT);
+	}
+
+
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);	/* Ack intr. */
+
+	/* I am Big Endian, but received byte count is Little Endian. */
+	hdr->count = le16_to_cpu(hdr->count);
+
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using writeb. */
+
+static void arctic_enet_block_input(struct net_device *dev, int count,
+				   struct sk_buff *skb, int ring_offset)
+{
+	char *buf = skb->data;
+	u16 *ptrs;
+	unsigned char *ptrc;
+
+	int cnt;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_input "
+		       "[DMAstat:%d][irqlock:%d].\n",
+		       dev->name, ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, iobase8 + NE_CMD);
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);
+	writeb(count & 0xff, iobase8 + NE_EN0_RCNTLO);
+	writeb(count >> 8, iobase8 + NE_EN0_RCNTHI);
+	writeb(ring_offset & 0xff, iobase8 + NE_EN0_RSARLO);
+	writeb(ring_offset >> 8, iobase8 + NE_EN0_RSARHI);
+	writeb(E8390_RREAD + E8390_START, iobase8 + NE_CMD);
+
+
+	if (ei_status.word16) {
+
+		switch_16bit_bank();
+
+		ptrs = (u16 *) buf;
+		for (cnt = 0; cnt < (count >> 1); cnt++)
+			/* At 16 bits mode, bus acts as Little Endian mode
+			   That's swap is needed ??? */
+			*ptrs++ = in_be16((u16 *) (iobase16 + NE_DATAPORT16));
+		switch_8bit_bank();
+
+		if (count & 0x01)
+			buf[count - 1] = readb(iobase8 + NE_DATAPORT);
+
+	} else {
+
+
+		ptrc = (unsigned char *) buf;
+		for (cnt = 0; cnt < count; cnt++)
+			*ptrc++ = readb(iobase8 + NE_DATAPORT);
+	}
+
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void arctic_enet_block_output(struct net_device *dev, int count,
+				    const unsigned char *buf,
+				    const int start_page)
+{
+	unsigned long dma_start;
+	u16 *ptrs;
+	unsigned char *ptrc;
+	int cnt;
+
+	/* Round the count up for word writes.  Do we need to do this?
+	   What effect will an odd byte count have on the 8390?
+	   I should check someday. */
+	if (count & 0x01)
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne_block_output."
+		       "[DMAstat:%d][irqlock:%d]\n", dev->name,
+		       ei_status.dmaing, ei_status.irqlock);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+
+#if 1 /* FIXME: not sure what this is for -dwg */
+	writeb(0x42, iobase8 + EN0_RCNTLO);
+	writeb(0x00, iobase8 + EN0_RCNTHI);
+	writeb(0x42, iobase8 + EN0_RSARLO);
+	writeb(0x00, iobase8 + EN0_RSARHI);
+#endif
+	/* We should already be in page 0, but to be safe... */
+	writeb(E8390_PAGE0 + E8390_START + E8390_NODMA, iobase8 + NE_CMD);
+
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);
+
+	/* Now the normal output. */
+	writeb(count & 0xff, iobase8 + NE_EN0_RCNTLO);
+	writeb(count >> 8, iobase8 + NE_EN0_RCNTHI);
+	writeb(0x00, iobase8 + NE_EN0_RSARLO);
+	writeb(start_page, iobase8 + NE_EN0_RSARHI);
+
+	writeb(E8390_RWRITE + E8390_START, iobase8 + NE_CMD);
+
+	if (ei_status.word16) {
+		switch_16bit_bank();
+
+		ptrs = (u16 *) buf;
+		for (cnt = 0; cnt < count >> 1; cnt++) {
+			/* At 16 bits mode, bus acts as Little Endian mode
+			   That's swap is needed ??? */
+			out_be16((u16 *) (iobase16 + NE_DATAPORT16),
+				 *ptrs);
+			ptrs++;
+		}
+
+		switch_8bit_bank();
+
+	} else {
+		ptrc = (unsigned char *) buf;
+		for (cnt = 0; cnt < count; cnt++)
+			writeb(*ptrc++, iobase8 + NE_DATAPORT);
+	}
+
+	dma_start = jiffies;
+
+	while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RDC) == 0)
+		if (jiffies - dma_start > 2 * HZ / 100) {	/* 20ms */
+			printk("%s: timeout waiting for Tx RDC.\n",
+			       dev->name);
+			arctic_enet_reset_8390(dev);
+			NS8390_init(dev, 1);
+			break;
+		}
+
+	writeb(ENISR_RDC, iobase8 + NE_EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+	return;
+}
+
+static struct net_device arctic_enet_dev = {
+	.init	= arctic_enet_init,
+	.open	= arctic_enet_open,
+	.stop	= arctic_enet_close,
+};
+
+int init_arctic_enet(void)
+{
+	struct net_device *dev = &arctic_enet_dev;
+	int rsvd8 = 0;
+	int rsvd16 = 0;
+	int err;
+
+	/* First set up our IO regions */
+	if (! request_mem_region(NE_BASE, NE_IO_EXTENT, "arctic_enet"))
+		goto fail;
+	rsvd8 = 1;
+
+	iobase8 = ioremap(NE_BASE, NE_IO_EXTENT);
+	if (! iobase8) {
+		err = -EBUSY;
+		goto fail;
+	}
+
+	if (NE_BASE16 != NE_BASE) {
+		if (! request_mem_region(NE_BASE16, NE_IO_EXTENT, "arctic_enet"))
+			goto fail;
+		rsvd16 = 1;
+	}
+
+	iobase16 = ioremap(NE_BASE16, NE_IO_EXTENT);
+	if (! iobase16) {
+		err = -EBUSY;
+		goto fail;
+	}
+
+	/* Configure IRQ */
+	cli();
+	mtdcr(DCRN_UIC0_TR, mfdcr(DCRN_UIC0_TR) | ARCTIC_ENET_IRQ_MASK);
+	mtdcr(DCRN_UIC0_PR, mfdcr(DCRN_UIC0_PR) | ARCTIC_ENET_IRQ_MASK);
+	mtdcr(DCRN_UIC0_SR, ARCTIC_ENET_IRQ_MASK);
+	sti();
+
+	err = arctic_enet_probe(dev);
+	if (err) {
+		printk(KERN_ERR "arctic_enet: No Arctic ethernet card found.\n");
+		goto fail;
+	}
+
+	err = register_netdev(dev);
+	if (err)
+		goto fail;
+
+	return 0;
+
+ fail:
+	if (iobase16)
+		iounmap(iobase16);
+	if (rsvd16)
+		release_mem_region(NE_BASE16, NE_IO_EXTENT);
+	if (iobase8)
+		iounmap(iobase8);
+	if (rsvd8)
+		release_mem_region(NE_BASE, NE_IO_EXTENT);
+
+	return err;
+
+}
+
+void remove_arctic_enet(void)
+{
+	unregister_netdev(&arctic_enet_dev);
+	free_irq(ARCTIC_ENET_IRQ, &arctic_enet_dev);
+
+	if (iobase16) {
+		iounmap(iobase16);
+		release_mem_region(NE_BASE16, NE_IO_EXTENT);
+	}
+	if (iobase8) {
+		iounmap(iobase8);
+		release_mem_region(NE_BASE, NE_IO_EXTENT);
+	}
+}
+
+module_init(init_arctic_enet);
+module_exit(remove_arctic_enet);


--
David Gibson			| For every complex problem there is a
david at gibson.dropbear.id.au	| solution which is simple, neat and
				| wrong.
http://www.ozlabs.org/people/dgibson

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/





More information about the Linuxppc-embedded mailing list