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