patch-2.3.27 linux/drivers/net/82596.c

Next file: linux/drivers/net/8390.c
Previous file: linux/drivers/net/3c59x.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.26/linux/drivers/net/82596.c linux/drivers/net/82596.c
@@ -11,10 +11,15 @@
    by Richard Hirst <richard@sleepie.demon.co.uk>
    Renamed to be 82596.c
 
-   *** Untested on Apricot hardware, and may require some hacking
-   *** to make it work.  The old 82596.c reported hasn't worked
-   *** since 1.3.xx anyway.  I have been unable to find any users
-   *** of Apricot hardware to test this on.
+   980825:  Changed to receive directly in to sk_buffs which are
+   allocated at open() time.  Eliminates copy on incoming frames
+   (small ones are still copied).  Shared data now held in a
+   non-cached page, so we can run on 68060 in copyback mode.
+
+   TBD:
+   * look at deferring rx frames rather than discarding (as per tulip)
+   * handle tx ring full as per tulip
+   * performace test to tune rx_copybreak
 
    Most of my modifications relate to the braindead big-endian
    implementation by Intel.  When the i596 is operating in
@@ -57,12 +62,22 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include <asm/pgtable.h>	/*?? */
+#include <asm/pgtable.h>
 
-#ifdef CONFIG_MVME16x_NET
+#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_MVME16x_NET_MODULE)
+#define ENABLE_MVME16x_NET
+#endif
+#if defined(CONFIG_BVME6000_NET) || defined(CONFIG_BVME6000_NET_MODULE)
+#define ENABLE_BVME6000_NET
+#endif
+#if defined(CONFIG_APRICOT) || defined(CONFIG_APRICOT_MODULE)
+#define ENABLE_APRICOT
+#endif
+
+#ifdef ENABLE_MVME16x_NET
 #include <asm/mvme16xhw.h>
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 #include <asm/bvme6000hw.h>
 #endif
 
@@ -83,11 +98,12 @@
 #define MACH_IS_APRICOT	0
 #else
 #define WSWAPrfd(x)	x
+#define WSWAPrbd(x)	((struct i596_rbd *)(x))
 #define WSWAPiscp(x)	((struct i596_iscp *)(x))
 #define WSWAPscb(x)	((struct i596_scb *)(x))
 #define WSWAPcmd(x)	x
 #define WSWAPtbd(x)	x
-#define WSWAPchar(x)	x
+#define WSWAPchar(x)	((char *)(x))
 #define ISCP_BUSY	0x0001
 #define MACH_IS_APRICOT	1
 #endif
@@ -103,19 +119,22 @@
 #define PORT_ALTSCP		0x02	/* alternate SCB address */
 #define PORT_ALTDUMP		0x03	/* Alternate DUMP address */
 
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(buff, size) kfree_s(buff,size)
-#endif
-
-#define APRICOT_DEBUG 2
+#define I82596_DEBUG 1
 
-#ifdef APRICOT_DEBUG
-int i596_debug = APRICOT_DEBUG;
+#ifdef I82596_DEBUG
+int i596_debug = I82596_DEBUG;
 #else
 int i596_debug = 1;
 #endif
 
+/* Copy frames shorter than rx_copybreak, otherwise pass on up in
+ * a full sized sk_buff.  Value of 100 stolen from tulip.c (!alpha).
+ */
+static int rx_copybreak = 100;
+
+#define PKT_BUF_SZ	1536
+#define MAX_MC_CNT	64
+
 #define I596_TOTAL_SIZE 17
 
 #define I596_NULL -1
@@ -179,12 +198,22 @@
 	unsigned short stat;
 	unsigned short cmd;
 	struct i596_rfd *next;
-	long rbd;
+	struct i596_rbd *rbd;
 	unsigned short count;
 	unsigned short size;
-	char data[1532];
 };
 
+struct i596_rbd {
+    unsigned short count;
+    unsigned short zero1;
+    struct i596_rbd *next;
+    char *data;
+    unsigned short size;
+    unsigned short zero2;
+    struct sk_buff *skb;
+};
+
+#define TX_RING_SIZE 16
 #define RX_RING_SIZE 16
 
 struct i596_scb {
@@ -222,6 +251,9 @@
 	struct i596_cmd set_conf;
 	char i596_config[16];
 	struct i596_cmd tdr;
+	struct i596_cmd mc_cmd;		/* Keep these three together!!! */
+	short mc_cnt;			/* Keep these three together!!! */
+	char mc_addrs[MAX_MC_CNT*6];	/* Keep these three together!!! */
 	unsigned long stat;
 	int last_restart __attribute__((aligned(4)));
 	struct i596_rfd *rx_tail;
@@ -230,6 +262,11 @@
 	int cmd_backlog;
 	unsigned long last_cmd;
 	struct net_device_stats stats;
+	struct i596_rfd rfds[RX_RING_SIZE];
+	struct i596_rbd rbds[RX_RING_SIZE];
+	struct tx_cmd tx_cmds[TX_RING_SIZE];
+	struct i596_tbd tbds[TX_RING_SIZE];
+	int next_tx_cmd;
 };
 
 char init_setup[] =
@@ -262,23 +299,24 @@
 static void print_eth(char *);
 static void set_multicast_list(struct net_device *dev);
 
+static int rx_ring_size = RX_RING_SIZE;
 static int ticks_limit = 25;
 static int max_cmd_backlog = 16;
 
 
 static inline void CA(struct net_device *dev)
 {
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		((struct i596_reg *) dev->base_addr)->ca = 1;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile u32 i = *(volatile u32 *) (dev->base_addr);
 	}
 #endif
-#ifdef CONFIG_APRICOT_i596
+#ifdef ENABLE_APRICOT
 	if (MACH_IS_APRICOT) {
 		outw(0, (short) (dev->base_addr) + 4);
 	}
@@ -288,14 +326,14 @@
 
 static inline void MPU_PORT(struct net_device *dev, int c, volatile void *x)
 {
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		struct i596_reg *p = (struct i596_reg *) (dev->base_addr);
 		p->porthi = ((c) | (u32) (x)) & 0xffff;
 		p->portlo = ((c) | (u32) (x)) >> 16;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		u32 v = (u32) (c) | (u32) (x);
 		v = ((u32) (v) << 16) | ((u32) (v) >> 16);
@@ -307,7 +345,7 @@
 }
 
 
-#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET)
+#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
 static void i596_error(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct net_device *dev = dev_id;
@@ -333,77 +371,88 @@
 }
 #endif
 
-static inline int init_rx_bufs(struct net_device *dev, int num)
+static inline void init_rx_bufs(struct net_device *dev)
 {
-	struct i596_private *lp = (struct i596_private *) dev->priv;
+	struct i596_private *lp = (struct i596_private *)dev->priv;
 	int i;
 	struct i596_rfd *rfd;
-
-	lp->scb.rfd = (struct i596_rfd *) I596_NULL;
+	struct i596_rbd *rbd;
 
 	if (i596_debug > 1)
-		printk("%s: init_rx_bufs %d.\n", dev->name, num);
+		printk ("%s: init_rx_bufs %d.\n", dev->name, rx_ring_size);
 
-	for (i = 0; i < num; i++) {
-		if (!(rfd = (struct i596_rfd *) kmalloc(sizeof(struct i596_rfd), GFP_KERNEL)))
-			 break;
+	/* First build the Receive Buffer Descriptor List */
 
-		rfd->stat = 0x0000;
-		rfd->rbd = I596_NULL;
-		rfd->count = 0;
-		rfd->size = 1532;
-		if (i == 0) {
-			rfd->cmd = CMD_EOL;
-			lp->rx_tail = rfd;
-		} else
-			rfd->cmd = 0x0000;
+	for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
+		struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
 
-		rfd->next = lp->scb.rfd;
-		lp->scb.rfd = WSWAPrfd(rfd);
+		if (skb == NULL)
+			panic("82596: alloc_skb() failed");
+		skb->dev = dev;
+		rbd->next = WSWAPrbd(rbd+1);
+		rbd->skb = skb;
+		rbd->data = WSWAPchar(skb->tail);
+		rbd->size = PKT_BUF_SZ;
+#ifdef __mc68000__
+		cache_clear(virt_to_phys(skb->tail), PKT_BUF_SZ);
+#endif
 	}
+	lp->rbds[rx_ring_size-1].next = WSWAPrbd(lp->rbds);
 
-	if (i != 0)
-		lp->rx_tail->next = lp->scb.rfd;
+	/* Now build the Receive Frame Descriptor List */
 
-	return (i);
+	for (i = 0, rfd = lp->rfds; i < rx_ring_size; i++, rfd++) {
+		rfd->rbd = (struct i596_rbd *)I596_NULL;
+		rfd->next = WSWAPrfd(rfd+1);
+		rfd->cmd = CMD_FLEX;
+	}
+	lp->scb.rfd = WSWAPrfd(lp->rfds);
+	lp->rfds[0].rbd = WSWAPrbd(lp->rbds);
+	rfd = lp->rfds + rx_ring_size - 1;
+	lp->rx_tail = rfd;
+	rfd->next = WSWAPrfd(lp->rfds);
+	rfd->cmd = CMD_EOL|CMD_FLEX;
 }
 
 static inline void remove_rx_bufs(struct net_device *dev)
 {
-	struct i596_private *lp = (struct i596_private *) dev->priv;
-	struct i596_rfd *rfd = WSWAPrfd(lp->scb.rfd);
-
-	lp->rx_tail->next = (struct i596_rfd *) I596_NULL;
+	struct i596_private *lp = (struct i596_private *)dev->priv;
+	struct i596_rbd *rbd;
+	int i;
 
-	do {
-		lp->scb.rfd = rfd->next;
-		kfree(rfd);
-		rfd = WSWAPrfd(lp->scb.rfd);
+	for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
+		if (rbd->skb == NULL)
+			break;
+		dev_kfree_skb(rbd->skb);
 	}
-	while (rfd != lp->rx_tail);
 }
 
 static inline void init_i596_mem(struct net_device *dev)
 {
 	struct i596_private *lp = (struct i596_private *) dev->priv;
-#if !defined(CONFIG_MVME16x_NET) && !defined(CONFIG_BVME6000_NET)
+#if !defined(ENABLE_MVME16x_NET) && !defined(ENABLE_BVME6000_NET)
 	short ioaddr = dev->base_addr;
 #endif
 	int boguscnt = 100000;
 	unsigned long flags;
+	int i;
 
-#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET)
-#ifdef CONFIG_MVME16x_NET
+#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
 
 		/* Disable all ints for now */
 		pcc2[0x28] = 1;
 		pcc2[0x2a] = 0x40;
-		pcc2[0x2b] = 0x40;	/* Set snooping bits now! */
+		/* Following disables snooping.  Snooping is not required
+		 * as we make appropriate use of non-cached pages for
+		 * shared data, and cache_push/cache_clear.
+		 */
+		pcc2[0x2b] = 0x00;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
 
@@ -419,7 +468,7 @@
 
 	MPU_PORT(dev, PORT_ALTSCP, &lp->scp);
 
-#else
+#elif defined(ENABLE_APRICOT)
 
 	/* change the scp address */
 	outw(0, ioaddr);
@@ -431,15 +480,15 @@
 
 	lp->last_cmd = jiffies;
 
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x)
 		lp->scp.sysbus = 0x00000054;
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000)
 		lp->scp.sysbus = 0x0000004c;
 #endif
-#ifdef CONFIG_APRICOT_i596
+#ifdef ENABLE_APRICOT
 	if (MACH_IS_APRICOT)
 		lp->scp.sysbus = 0x00440000;
 #endif
@@ -454,7 +503,7 @@
 	if (i596_debug > 1)
 		printk("%s: starting i82596.\n", dev->name);
 
-#if !defined(CONFIG_MVME16x_NET) && !defined(CONFIG_BVME6000_NET)
+#if defined(ENABLE_APRICOT)
 	(void) inb(ioaddr + 0x10);
 	outb(4, ioaddr + 0xf);
 #endif
@@ -466,9 +515,22 @@
 			     dev->name, lp->scb.status, lp->scb.command);
 			break;
 		}
+
+	/* Ensure rx frame/buffer descriptors are tidy */
+	/* Bit naff doing this here as well as in init_rx_bufs() */
+
+	for (i = 0; i < rx_ring_size; i++) {
+		lp->rfds[i].rbd = (struct i596_rbd *)I596_NULL;
+		lp->rfds[i].cmd = CMD_FLEX;
+	}
+	lp->rfds[rx_ring_size-1].cmd = CMD_EOL|CMD_FLEX;
+	lp->scb.rfd = WSWAPrfd(lp->rfds);
+	lp->rfds[0].rbd = WSWAPrbd(lp->rbds);
+	lp->rx_tail = lp->rfds + rx_ring_size - 1;
+
 	lp->scb.command = 0;
 
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
 
@@ -478,7 +540,7 @@
 		pcc2[0x2b] = 0x55;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
 
@@ -525,38 +587,88 @@
 
 static inline int i596_rx(struct net_device *dev)
 {
-	struct i596_private *lp = (struct i596_private *) dev->priv;
+	struct i596_private *lp = (struct i596_private *)dev->priv;
 	struct i596_rfd *rfd;
+	struct i596_rbd *rbd;
 	int frames = 0;
 
 	if (i596_debug > 3)
-		printk("i596_rx()\n");
+		printk ("i596_rx()\n");
 
-	rfd = WSWAPrfd(lp->scb.rfd);	/* Reference next frame descriptor to check */
+	rfd = WSWAPrfd(lp->scb.rfd);		/* Ref next frame to check */
 
-	while ((rfd->stat) & STAT_C) {	/* Loop while we have complete frames */
-		if (i596_debug > 2)
-			print_eth(rfd->data);
+	while ((rfd->stat) & STAT_C) {		/* Loop while complete frames */
+		rbd = WSWAPrbd(rfd->rbd);       /* Ref associated buffer desc */
+
+		if (i596_debug >2)
+			print_eth(WSWAPchar(rbd->data));
 
 		if ((rfd->stat) & STAT_OK) {
 			/* a good frame */
-			int pkt_len = rfd->count & 0x3fff;
-			struct sk_buff *skb = dev_alloc_skb(pkt_len);
+			int pkt_len = rbd->count & 0x3fff;
+			struct sk_buff *skb = rbd->skb;
+			int rx_in_place = 0;
 
 			frames++;
 
+			/* Check if the packet is long enough to just accept
+			 * without copying to a properly sized skbuff.
+			 */
+
+			if (pkt_len > rx_copybreak) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Get fresh skbuff to replace filled one. */
+				newskb = dev_alloc_skb(PKT_BUF_SZ);
+				if (newskb == NULL) {
+					skb = NULL;	/* drop pkt */
+					goto memory_squeeze;
+				}
+				/* Pass up the skb already on the Rx ring. */
+				temp = skb_put(skb, pkt_len);
+				if (WSWAPchar(rbd->data) != temp)
+					printk(KERN_ERR "%s: Internal consistency error "
+						"-- the skbuff addresses do not match"
+						" in i596_rx: %p vs. %p / %p.\n", dev->name,
+						WSWAPchar(rbd->data),
+						skb->head, temp);
+				rx_in_place = 1;
+				rbd->skb = newskb;
+				newskb->dev = dev;
+				rbd->data = WSWAPchar(newskb->tail);
+#ifdef __mc68000__
+				cache_clear(virt_to_phys(newskb->tail), PKT_BUF_SZ);
+#endif
+			}
+			else
+				skb = dev_alloc_skb(pkt_len + 2);
+memory_squeeze:
 			if (skb == NULL) {
-				printk("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
+				/* XXX tulip.c can defer packets here!! */
+				printk ("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
-			} else {
+			}
+			else {
 				skb->dev = dev;
-				memcpy(skb_put(skb, pkt_len), rfd->data, pkt_len);
-				skb->protocol = eth_type_trans(skb, dev);
+				if (!rx_in_place) {
+					/* 16 byte align the data fields */
+					skb_reserve(skb, 2);
+					memcpy(skb_put(skb,pkt_len),
+						WSWAPchar(rbd->data), pkt_len);
+				}
+				skb->protocol=eth_type_trans(skb,dev);
+				skb->len = pkt_len;
+#ifdef __mc68000__
+				cache_clear(virt_to_phys(rbd->skb->tail),
+						pkt_len);
+#endif
 				netif_rx(skb);
 				lp->stats.rx_packets++;
-				lp->stats.rx_bytes += pkt_len;
+				lp->stats.rx_bytes+=pkt_len;
 			}
-		} else {
+		}
+		else {
 			lp->stats.rx_errors++;
 			if ((rfd->stat) & 0x0001)
 				lp->stats.collisions++;
@@ -576,21 +688,40 @@
 
 		/* Clear the buffer descriptor count and EOF + F flags */
 
+		if (rbd != (struct i596_rbd *)I596_NULL)
+			rbd->count=0;
+		else
+			printk("%s: Null rbd - oops!\n", dev->name);
+
+		/* Tidy the frame descriptor, marking it as end of list */
+
+		rfd->rbd = (struct i596_rbd *)I596_NULL;
 		rfd->stat = 0;
+		rfd->cmd = CMD_EOL|CMD_FLEX;
 		rfd->count = 0;
-		rfd->cmd = CMD_EOL;
-		lp->rx_tail->cmd = 0;
+
+		/* Remove end-of-list from old end descriptor */
+
+		lp->rx_tail->cmd = CMD_FLEX;
+
+		/* Update last frame descriptor to reference the one just
+		 * processed */
+
 		lp->rx_tail = rfd;
+
+		/* Update record of next frame descriptor to process */
+
 		lp->scb.rfd = rfd->next;
-		rfd = WSWAPrfd(lp->scb.rfd);	/* Next frame descriptor to check */
+		rfd = WSWAPrfd(lp->scb.rfd);	/* Next frame desc. to check */
 	}
 
 	if (i596_debug > 3)
-		printk("frames %d\n", frames);
+		printk ("frames %d\n", frames);
 
 	return 0;
 }
 
+
 static inline void i596_cleanup_cmd(struct i596_private *lp)
 {
 	struct i596_cmd *ptr;
@@ -617,13 +748,12 @@
 				lp->stats.tx_aborted_errors++;
 
 				ptr->next = (struct i596_cmd *) I596_NULL;
-				kfree(tx_cmd);
+				tx_cmd->cmd.command = 0;  /* Mark as free */
 				break;
 			}
 		case CmdMulticastList:
 			{
 				ptr->next = (struct i596_cmd *) I596_NULL;
-				kfree(ptr);
 				break;
 			}
 		default:
@@ -743,26 +873,19 @@
 
 static int i596_open(struct net_device *dev)
 {
-	int i;
-
 	if (i596_debug > 1)
 		printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
 
-	if (request_irq(dev->irq, &i596_interrupt, 0, "apricot", dev))
+	if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev))
 		return -EAGAIN;
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
-		if (request_irq(0x56, &i596_error, 0, "apricot_error", dev))
+		if (request_irq(0x56, &i596_error, 0, "i82596_error", dev))
 			return -EAGAIN;
 	}
 #endif
-	if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
-		printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
+	init_rx_bufs(dev);
 
-	if (i < 4) {
-		free_irq(dev->irq, dev);
-		return -EAGAIN;
-	}
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
@@ -779,6 +902,7 @@
 	struct i596_private *lp = (struct i596_private *) dev->priv;
 	int ioaddr = dev->base_addr;
 	struct tx_cmd *tx_cmd;
+	struct i596_tbd *tbd;
 
 	if (i596_debug > 2)
 		printk("%s: 82596 start xmit\n", dev->name);
@@ -810,7 +934,8 @@
 		dev->trans_start = jiffies;
 	}
 	if (i596_debug > 3)
-		printk("%s: i596_start_xmit() called\n", dev->name);
+		printk("%s: i596_start_xmit(%x,%x) called\n", dev->name,
+				skb->len, (unsigned int)skb->data);
 
 	/* Block a timer-based transmit from overlapping.  This could better be
 	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
@@ -820,14 +945,18 @@
 		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 		dev->trans_start = jiffies;
 
-		tx_cmd = (struct tx_cmd *) kmalloc((sizeof(struct tx_cmd) + sizeof(struct i596_tbd)), GFP_ATOMIC);
-		if (tx_cmd == NULL) {
-			printk("%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
+		tx_cmd = lp->tx_cmds + lp->next_tx_cmd;
+		tbd = lp->tbds + lp->next_tx_cmd;
+
+		if (tx_cmd->cmd.command) {
+			printk ("%s: xmit ring full, dropping packet.\n",
+					dev->name);
 			lp->stats.tx_dropped++;
 
 			dev_kfree_skb(skb);
 		} else {
-			struct i596_tbd *tbd = (struct i596_tbd *) (tx_cmd + 1);
+			if (++lp->next_tx_cmd == TX_RING_SIZE)
+				lp->next_tx_cmd = 0;
 			tx_cmd->tbd = WSWAPtbd(tbd);
 			tbd->next = (struct i596_tbd *) I596_NULL;
 
@@ -841,6 +970,9 @@
 
 			tbd->data = WSWAPchar(skb->data);
 
+#ifdef __mc68000__
+			cache_push(virt_to_phys(skb->data), length);
+#endif
 			if (i596_debug > 3)
 				print_eth(skb->data);
 			i596_add_cmd(dev, (struct i596_cmd *) tx_cmd);
@@ -854,7 +986,6 @@
 
 	return 0;
 }
-
 
 static void print_eth(char *add)
 {
@@ -879,14 +1010,15 @@
 	struct i596_private *lp;
 	char eth_addr[6];
 
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		static int probed = 0;
-
+#ifdef XXX_FIXME
 		if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) {
 			printk("Ethernet probe disabled - chip not present\n");
 			return ENODEV;
 		}
+#endif
 		if (probed)
 			return ENODEV;
 		probed++;
@@ -895,7 +1027,7 @@
 		dev->irq = (unsigned) MVME16x_IRQ_I596;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE;
 		unsigned char msr = rtc[3];
@@ -909,7 +1041,7 @@
 		dev->irq = (unsigned) BVME_IRQ_I596;
 	}
 #endif
-#ifdef CONFIG_APRICOT_INTEL
+#ifdef ENABLE_APRICOT
 	int checksum = 0;
 	int ioaddr = 0x300;
 
@@ -953,22 +1085,28 @@
 	if (i596_debug > 0)
 		printk(version);
 
-	/* The APRICOT-specific entries in the device structure. */
+	/* The 82596-specific entries in the device structure. */
 	dev->open = &i596_open;
 	dev->stop = &i596_close;
 	dev->hard_start_xmit = &i596_start_xmit;
 	dev->get_stats = &i596_get_stats;
 	dev->set_multicast_list = &set_multicast_list;
 
-	dev->mem_start = (int) kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL);
-	/* align for scp */
-	dev->priv = (void *) ((dev->mem_start + 0xf) & 0xfffffff0);
+	dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0);
+	dev->priv = (void *)(dev->mem_start);
 
 	lp = (struct i596_private *) dev->priv;
 	if (i596_debug)
-		printk("%s: lp at 0x%08lx, lp->scb at 0x%08lx\n"
-		,dev->name, (unsigned long) lp, (unsigned long) &lp->scb);
+		printk ("%s: lp at 0x%08lx (%d bytes), lp->scb at 0x%08lx\n",
+			dev->name, (unsigned long)lp,
+			sizeof(struct i596_private), (unsigned long)&lp->scb);
 	memset((void *) lp, 0, sizeof(struct i596_private));
+
+#ifdef __mc68000__
+	cache_push(virt_to_phys((void *)(dev->mem_start)), 4096);
+	cache_clear(virt_to_phys((void *)(dev->mem_start)), 4096);
+	kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER);
+#endif
 	lp->scb.command = 0;
 	lp->scb.cmd = (struct i596_cmd *) I596_NULL;
 	lp->scb.rfd = (struct i596_rfd *) I596_NULL;
@@ -984,7 +1122,7 @@
 	int boguscnt = 2000;
 	unsigned short status, ack_cmd = 0;
 
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) {
 			i596_error(BVME_IRQ_I596, NULL, NULL);
@@ -1063,13 +1201,12 @@
 					dev_kfree_skb(skb);
 
 					ptr->next = (struct i596_cmd *) I596_NULL;
-					kfree(tx_cmd);
+					tx_cmd->cmd.command = 0; /* Mark free */
 					break;
 				}
 			case CmdMulticastList:
 				{
 					ptr->next = (struct i596_cmd *) I596_NULL;
-					kfree(ptr);
 					break;
 				}
 			case CmdTDR:
@@ -1141,7 +1278,7 @@
 		}
 	lp->scb.command = ack_cmd;
 
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		/* Ack the interrupt */
 
@@ -1150,7 +1287,7 @@
 		pcc2[0x2a] |= 0x08;
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
 
@@ -1158,7 +1295,7 @@
 		*ethirq = 3;
 	}
 #endif
-#ifdef CONFIG_APRICOT_INTEL
+#ifdef ENABLE_APRICOT
 	(void) inb(ioaddr + 0x10);
 	outb(4, ioaddr + 0xf);
 #endif
@@ -1208,7 +1345,7 @@
 
 	i596_cleanup_cmd(lp);
 
-#ifdef CONFIG_MVME16x_NET
+#ifdef ENABLE_MVME16x_NET
 	if (MACH_IS_MVME16x) {
 		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
 
@@ -1218,7 +1355,7 @@
 		pcc2[0x2b] = 0x40;	/* Set snooping bits now! */
 	}
 #endif
-#ifdef CONFIG_BVME6000_NET
+#ifdef ENABLE_BVME6000_NET
 	if (MACH_IS_BVME6000) {
 		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
 
@@ -1249,7 +1386,7 @@
 {
 	struct i596_private *lp = (struct i596_private *) dev->priv;
 	struct i596_cmd *cmd;
-	int config = 0;
+	int config = 0, cnt;
 
 	if (i596_debug > 1)
 		printk("%s: set multicast list, %d entries, promisc %s, allmulti %s\n", dev->name, dev->mc_count, dev->flags & IFF_PROMISC ? "ON" : "OFF", dev->flags & IFF_ALLMULTI ? "ON" : "OFF");
@@ -1279,18 +1416,24 @@
 			i596_add_cmd(dev, &lp->set_conf);
 		}
 	}
+
+	cnt = dev->mc_count;
+	if (cnt > MAX_MC_CNT)
+	{
+		cnt = MAX_MC_CNT;
+		printk("%s: Only %d multicast addresses supported",
+			dev->name, cnt);
+	}
+	
 	if (dev->mc_count > 0) {
 		struct dev_mc_list *dmi;
 		unsigned char *cp;
-		cmd = (struct i596_cmd *) kmalloc(sizeof(struct i596_cmd) + 2 + dev->mc_count * 6, GFP_ATOMIC);
-		if (cmd == NULL) {
-			printk("%s: set_multicast Memory squeeze.\n", dev->name);
-			return;
-		}
+
+		cmd = &lp->mc_cmd;
 		cmd->command = CmdMulticastList;
 		*((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
 		cp = ((unsigned char *) (cmd + 1)) + 2;
-		for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) {
+		for(dmi=dev->mc_list;cnt && dmi!=NULL;dmi=dmi->next,cnt--) {
 			memcpy(cp, dmi->dmi_addr, 6);
 			if (i596_debug > 1)
 				printk("%s: Adding address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, *(cp + 0), *(cp + 1), *(cp + 2), *(cp + 3), *(cp + 4), *(cp + 5));
@@ -1306,40 +1449,58 @@
 static unsigned int i596_portlist[] __initdata =
 {0x300, 0};
 struct netdev_entry i596_drv =
-{"apricot", i82596_probe, I596_TOTAL_SIZE, apricot_portlist};
+{"i82596", i82596_probe, I596_TOTAL_SIZE, i596_portlist};
 #endif
 
 #ifdef MODULE
 static char devicename[9] =
 {0,};
-static struct net_device dev_apricot =
+static struct net_device dev_82596 =
 {
-	devicename,		/* device name inserted by /linux/drivers/net/net_init.c */
+	devicename,	/* device name inserted by drivers/net/net_init.c */
 	0, 0, 0, 0,
-	0x300, 10,
+	0, 0,		/* base, irq */
 	0, 0, 0, NULL, i82596_probe};
 
+#ifdef ENABLE_APRICOT
 static int io = 0x300;
 static int irq = 10;
 MODULE_PARM(irq, "i");
+#endif
+
+MODULE_PARM(debug, "i");
+static int debug = -1;
 
 int init_module(void)
 {
-	dev_apricot.base_addr = io;
-	dev_apricot.irq = irq;
-	if (register_netdev(&dev_apricot) != 0)
+#ifdef ENABLE_APRICOT
+	dev_82596.base_addr = io;
+	dev_82596.irq = irq;
+#endif
+	if (debug >= 0)
+		i596_debug = debug;
+	if (register_netdev(&dev_82596) != 0)
 		return -EIO;
 	return 0;
 }
 
 void cleanup_module(void)
 {
-	unregister_netdev(&dev_apricot);
-	kfree((void *) dev_apricot.mem_start);
-	dev_apricot.priv = NULL;
+	unregister_netdev(&dev_82596);
+#ifdef __mc68000__
+	/* XXX This assumes default cache mode to be IOMAP_FULL_CACHING,
+	 * XXX which may be invalid (CONFIG_060_WRITETHROUGH)
+	 */
 
+	kernel_set_cachemode((u32)(dev_82596.mem_start), 4096,
+			IOMAP_FULL_CACHING);
+#endif
+	free_page ((u32)(dev_82596.mem_start));
+	dev_82596.priv = NULL;
+#ifdef ENABLE_APRICOT
 	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_apricot.base_addr, I596_TOTAL_SIZE);
+	release_region(dev_82596.base_addr, I596_TOTAL_SIZE);
+#endif
 }
 
 #endif				/* MODULE */

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