patch-2.3.99-pre6 linux/drivers/net/am79c961a.c

Next file: linux/drivers/net/am79c961a.h
Previous file: linux/drivers/net/acenic.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre5/linux/drivers/net/am79c961a.c linux/drivers/net/am79c961a.c
@@ -33,40 +33,49 @@
 
 #include "am79c961a.h"
 
+static int am79c961_probe1 (struct net_device *dev);
+static int am79c961_open (struct net_device *dev);
+static int am79c961_sendpacket (struct sk_buff *skb, struct net_device *dev);
+static void am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void am79c961_rx (struct net_device *dev, struct dev_priv *priv);
+static void am79c961_tx (struct net_device *dev, struct dev_priv *priv);
+static int am79c961_close (struct net_device *dev);
+static struct enet_statistics *am79c961_getstats (struct net_device *dev);
+static void am79c961_setmulticastlist (struct net_device *dev);
+static void am79c961_timeout(struct net_device *dev);
+
 static unsigned int net_debug = NET_DEBUG;
 
 static void
 am79c961_setmulticastlist (struct net_device *dev);
 
-static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.00\n";
+static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.01\n";
 
 #define FUNC_PROLOGUE \
 	struct dev_priv *priv = (struct dev_priv *)dev->priv
 
 /* --------------------------------------------------------------------------- */
 
+#ifdef __arm__
 static void
 write_rreg (unsigned long base, unsigned int reg, unsigned short val)
 {
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		strh	%0, [%2, #-4]	@ NET_RDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		str%?h	%0, [%2, #-4]	@ NET_RDP
 		" : : "r" (val), "r" (reg), "r" (0xf0000464));
 }
 
 static inline void
 write_ireg (unsigned long base, unsigned int reg, unsigned short val)
 {
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		strh	%0, [%2, #8]	@ NET_RDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		str%?h	%0, [%2, #8]	@ NET_RDP
 		" : : "r" (val), "r" (reg), "r" (0xf0000464));
 }
 
 #define am_writeword(dev,off,val)\
-	__asm__("\
-		strh	%0, [%1]\
-		" : : "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
+	__asm__("str%?h	%0, [%1]" : : \
+		"r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
 
 static inline void
 am_writebuffer(struct net_device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
@@ -74,30 +83,28 @@
 	offset = 0xe0000000 + (offset << 1);
 	length = (length + 1) & ~1;
 	if ((int)buf & 2) {
-		__asm__ __volatile__("
-			strh	%2, [%0], #4
-		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
 		buf += 2;
 		length -= 2;
 	}
 	while (length > 8) {
 		unsigned int tmp, tmp2;
 		__asm__ __volatile__("
-			ldmia	%1!, {%2, %3}
-			strh	%2, [%0], #4
-			mov	%2, %2, lsr #16
-			strh	%2, [%0], #4
-			strh	%3, [%0], #4
-			mov	%3, %3, lsr #16
-			strh	%3, [%0], #4
+			ldm%?ia	%1!, {%2, %3}
+			str%?h	%2, [%0], #4
+			mov%?	%2, %2, lsr #16
+			str%?h	%2, [%0], #4
+			str%?h	%3, [%0], #4
+			mov%?	%3, %3, lsr #16
+			str%?h	%3, [%0], #4
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
 		  : "0" (offset), "1" (buf));
 		length -= 8;
 	}
 	while (length > 0) {
-		__asm__ __volatile__("
-			strh	%2, [%0], #4
-		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		__asm__ __volatile__("str%?h	%2, [%0], #4"
+		 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
 		buf += 2;
 		length -= 2;
 	}
@@ -107,9 +114,8 @@
 read_rreg (unsigned int base_addr, unsigned int reg)
 {
 	unsigned short v;
-	__asm__("
-		strh	%1, [%2]	@ NET_RAP
-		ldrh	%0, [%2, #-4]	@ NET_IDP
+	__asm__("str%?h	%1, [%2]	@ NET_RAP
+		ldr%?h	%0, [%2, #-4]	@ NET_IDP
 		" : "=r" (v): "r" (reg), "r" (0xf0000464));
 	return v;
 }
@@ -120,9 +126,7 @@
 	unsigned long address = 0xe0000000 + (off << 1);
 	unsigned short val;
 
-	__asm__("
-		ldrh	%0, [%1]
-		" : "=r" (val): "r" (address));
+	__asm__("ldr%?h	%0, [%1]" : "=r" (val): "r" (address));
 	return val;
 }
 
@@ -134,23 +138,23 @@
 	if ((int)buf & 2) {
 		unsigned int tmp;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			strb	%2, [%1], #1
-			mov	%2, %2, lsr #8
-			strb	%2, [%1], #1
+			ldr%?h	%2, [%0], #4
+			str%?b	%2, [%1], #1
+			mov%?	%2, %2, lsr #8
+			str%?b	%2, [%1], #1
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
 		length -= 2;
 	}
 	while (length > 8) {
 		unsigned int tmp, tmp2, tmp3;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			ldrh	%3, [%0], #4
-			orr	%2, %2, %3, lsl #16
-			ldrh	%3, [%0], #4
-			ldrh	%4, [%0], #4
-			orr	%3, %3, %4, lsl #16
-			stmia	%1!, {%2, %3}
+			ldr%?h	%2, [%0], #4
+			ldr%?h	%3, [%0], #4
+			orr%?	%2, %2, %3, lsl #16
+			ldr%?h	%3, [%0], #4
+			ldr%?h	%4, [%0], #4
+			orr%?	%3, %3, %4, lsl #16
+			stm%?ia	%1!, {%2, %3}
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
 		  : "0" (offset), "1" (buf));
 		length -= 8;
@@ -158,14 +162,17 @@
 	while (length > 0) {
 		unsigned int tmp;
 		__asm__ __volatile__("
-			ldrh	%2, [%0], #4
-			strb	%2, [%1], #1
-			mov	%2, %2, lsr #8
-			strb	%2, [%1], #1
+			ldr%?h	%2, [%0], #4
+			str%?b	%2, [%1], #1
+			mov%?	%2, %2, lsr #8
+			str%?b	%2, [%1], #1
 		" : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
 		length -= 2;
 	}
 }
+#else
+#error Not compatable
+#endif
 
 static int
 am79c961_ramtest(struct net_device *dev, unsigned int val)
@@ -259,7 +266,7 @@
 	write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
 	write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
 	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
-	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM);
+	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
 	write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
 }
 
@@ -304,7 +311,7 @@
 	/*
 	 * The PNP initialisation should have been done by the ether bootp loader.
 	 */
-	inb ((dev->base_addr + NET_RESET) >> 1);	/* reset the device */
+	inb((dev->base_addr + NET_RESET) >> 1);	/* reset the device */
 
 	udelay (5);
 
@@ -343,6 +350,7 @@
 	dev->hard_start_xmit = am79c961_sendpacket;
 	dev->get_stats = am79c961_getstats;
 	dev->set_multicast_list = am79c961_setmulticastlist;
+	dev->tx_timeout = am79c961_timeout;
 
 	/* Fill in the fields of the device structure with ethernet values. */
 	ether_setup(dev);
@@ -382,14 +390,15 @@
 
 	memset (&priv->stats, 0, sizeof (priv->stats));
 
-	if (request_irq(dev->irq, am79c961_interrupt, 0, "am79c961", dev))
+	if (request_irq(dev->irq, am79c961_interrupt, 0, "am79c961", dev)) {
+		MOD_DEC_USE_COUNT;
 		return -EAGAIN;
+	}
 
 	am79c961_init_for_open(dev);
 
-	dev->tbusy = 0;
-	dev->interrupt = 0;
-	dev->start = 1;
+	netif_start_queue(dev);
+
 	return 0;
 }
 
@@ -399,8 +408,7 @@
 static int
 am79c961_close(struct net_device *dev)
 {
-	dev->tbusy = 1;
-	dev->start = 0;
+	netif_stop_queue(dev);
 
 	am79c961_init(dev);
 
@@ -420,26 +428,116 @@
 	return &priv->stats;
 }
 
+static inline u32 update_crc(u32 crc, u8 byte)
+{
+	int i;
+
+	for (i = 8; i != 0; i--) {
+		byte ^= crc & 1;
+		crc >>= 1;
+
+		if (byte & 1)
+			crc ^= 0xedb88320;
+
+		byte >>= 1;
+	}
+
+	return crc;
+}
+
+static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash)
+{
+	if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) {
+		int i, idx, bit;
+		u32 crc;
+
+		crc = 0xffffffff;
+
+		for (i = 0; i < ETH_ALEN; i++)
+			crc = update_crc(crc, dmi->dmi_addr[i]);
+
+		idx = crc >> 30;
+		bit = (crc >> 26) & 15;
+
+		hash[idx] |= 1 << bit;
+	}
+}
+
 /*
  * Set or clear promiscuous/multicast mode filter for this adaptor.
- *
- * We don't attempt any packet filtering.  The card may have a SEEQ 8004
- * in which does not have the other ethernet address registers present...
  */
 static void am79c961_setmulticastlist (struct net_device *dev)
 {
 	unsigned long flags;
-	int i;
+	unsigned short multi_hash[4], mode;
+	int i, stopped;
 
-	dev->flags &= ~IFF_ALLMULTI;
+	mode = MODE_PORT0;
 
-	i = MODE_PORT0;
-	if (dev->flags & IFF_PROMISC)
-		i |= MODE_PROMISC;
+	if (dev->flags & IFF_PROMISC) {
+		mode |= MODE_PROMISC;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		memset(multi_hash, 0xff, sizeof(multi_hash));
+	} else {
+		struct dev_mc_list *dmi;
 
-	save_flags_cli (flags);
-	write_rreg (dev->base_addr, MODE, i);
-	restore_flags (flags);
+		memset(multi_hash, 0x00, sizeof(multi_hash));
+
+		for (dmi = dev->mc_list; dmi; dmi = dmi->next)
+			am79c961_mc_hash(dmi, multi_hash);
+	}
+
+	save_flags_cli(flags);
+
+	stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
+
+	if (!stopped) {
+		/*
+		 * Put the chip into suspend mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
+
+		/*
+		 * Spin waiting for chip to report suspend mode
+		 */
+		while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
+			restore_flags(flags);
+			nop();
+			save_flags_cli(flags);
+		}
+	}
+
+	/*
+	 * Update the multicast hash table
+	 */
+	for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++)
+		write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
+
+	/*
+	 * Write the mode register
+	 */
+	write_rreg(dev->base_addr, MODE, mode);
+
+	if (!stopped) {
+		/*
+		 * Put the chip back into running mode
+		 */
+		write_rreg(dev->base_addr, CTRL1, 0);
+	}
+
+	restore_flags(flags);
+}
+
+static void am79c961_timeout(struct net_device *dev)
+{
+	printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n",
+		dev->name);
+
+	/*
+	 * ought to do some setup of the tx side here
+	 */
+
+	netif_wake_queue(dev);
 }
 
 /*
@@ -449,46 +547,34 @@
 am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
 {
 	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned int hdraddr, bufaddr;
+	unsigned int head;
+	unsigned long flags;
 
-	if (!dev->tbusy) {
-again:
-		if (!test_and_set_bit(0, (void*)&dev->tbusy)) {
-			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-			unsigned int hdraddr, bufaddr;
-			unsigned long flags;
-
-			hdraddr = priv->txhdr + (priv->txhead << 3);
-			bufaddr = priv->txbuffer[priv->txhead];
-			priv->txhead ++;
-			if (priv->txhead >= TX_BUFFERS)
-				priv->txhead = 0;
-
-			am_writebuffer (dev, bufaddr, skb->data, length);
-			am_writeword (dev, hdraddr + 4, -length);
-			am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
-
-			save_flags_cli (flags);
-			write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
-			dev->trans_start = jiffies;
-			restore_flags (flags);
-
-			if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN))
-				dev->tbusy = 0;
-			dev_kfree_skb (skb);
-			return 0;
-		} else
-			printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
-			return 1;
-	} else {
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
-			return 1;
-		printk (KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name);
-		/* Try to restart the adaptor. */
-		dev->tbusy = 0;
-		dev->trans_start = jiffies;
-		goto again;
-	}
+	head = priv->txhead;
+	hdraddr = priv->txhdr + (head << 3);
+	bufaddr = priv->txbuffer[head];
+	head += 1;
+	if (head >= TX_BUFFERS)
+		head = 0;
+
+	am_writebuffer (dev, bufaddr, skb->data, length);
+	am_writeword (dev, hdraddr + 4, -length);
+	am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+	priv->txhead = head;
+
+	save_flags_cli (flags);
+	write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
+	dev->trans_start = jiffies;
+	restore_flags (flags);
+
+	if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN))
+		netif_stop_queue(dev);
+
+	dev_kfree_skb(skb);
+
+	return 0;
 }
 
 static void
@@ -503,8 +589,6 @@
 		printk(KERN_DEBUG "am79c961irq: %d ", irq);
 #endif
 
-	dev->interrupt = 1;
-
 	status = read_rreg (dev->base_addr, CSR0);
 	write_rreg (dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
 
@@ -515,8 +599,6 @@
 	if (status & CSR0_MISS)
 		priv->stats.rx_dropped ++;
 
-	dev->interrupt = 0;
-
 #if NET_DEBUG > 1
 	if(net_debug & DEBUG_INT)
 		printk("done\n");
@@ -613,7 +695,7 @@
 			am_writeword (dev, hdraddr + 6, 0);
 
 			if (status2 & TST_RTRY)
-				priv->stats.collisions += 1;
+				priv->stats.collisions += 16;
 			if (status2 & TST_LCOL)
 				priv->stats.tx_window_errors ++;
 			if (status2 & TST_LCAR)
@@ -625,7 +707,5 @@
 		priv->stats.tx_packets ++;
 	} while (priv->txtail != priv->txhead);
 
-	dev->tbusy = 0;
-	mark_bh (NET_BH);
+	netif_wake_queue(dev);
 }
-

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