CRIS V32 Ethernet driver update

This patch applies to the CRIS V32 (ETRAX FS) Ethernet driver in the Linux 2.6 kernel supplied in the 2.10 software distribution (os-linux-2.6-tag–devboard-R2_10-4).

Index: os/linux-2.6/drivers/net/cris/eth_v32.c
===================================================================
--- os/linux-2.6/drivers/net/cris/eth_v32.c	6 Feb 2007 10:10:37 -0000	1.96
+++ os/linux-2.6/drivers/net/cris/eth_v32.c	4 Sep 2007 11:19:32 -0000	1.96.2.1
@@ -34,7 +34,11 @@
 #include <asm/arch/hwregs/reg_rdwr.h>
 #include <asm/arch/hwregs/dma.h>
 #include <asm/arch/hwregs/eth_defs.h>
+#ifdef CONFIG_ETRAXFS
 #include <asm/arch/hwregs/config_defs.h>
+#else
+#include <asm/arch/hwregs/clkgen_defs.h>
+#endif
 #include <asm/arch/hwregs/intr_vect_defs.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -45,6 +49,13 @@
 
 #include "eth_v32.h"
 
+#ifndef CONFIG_ETRAXFS
+#define ETH0_INTR_VECT ETH_INTR_VECT
+#define ETH1_INTR_VECT ETH_INTR_VECT
+#define regi_eth0 regi_eth
+#define regi_eth1 regi_
+#endif
+
 #define DEBUG(x)
 #define GET_BIT(bit,val)   (((val) >> (bit)) & 0x01)
 
@@ -53,10 +64,9 @@ static int use_network_leds = 1;
 
 static void update_rx_stats(struct crisv32_ethernet_local *np);
 static void update_tx_stats(struct crisv32_ethernet_local *np); 
+static int crisv32_eth_poll(struct net_device *dev, int *budget);
 static void crisv32_eth_setup_controller(struct net_device *dev);
 static int  crisv32_eth_request_irqdma(struct net_device *dev);
-static void crisv32_eth_init_rings(struct net_device *dev);
-static void crisv32_eth_reset_rings(struct net_device *dev);
 static void crisv32_ethernet_bug(struct net_device *dev);
 
 /*
@@ -77,6 +87,8 @@ struct transceiver_ops transceivers[] = 
 	{0x04de, intel_check_speed, intel_check_duplex},
 	/* National Semiconductor DP83865 */
 	{0x0017, national_check_speed, national_check_duplex},
+	/* Vitesse VCS8641 */
+	{0x01c1, vitesse_check_speed, vitesse_check_duplex},
 	/* Generic, must be last. */
 	{0x0000, generic_check_speed, generic_check_duplex}
 };
@@ -84,6 +96,11 @@ struct transceiver_ops transceivers[] = 
 static struct net_device *crisv32_dev[2];
 static struct crisv32_eth_leds *crisv32_leds[3];
 
+/* Default MAC address for interface 0.
+ * The real one will be set later. */
+static struct sockaddr default_mac_iface0 = 
+  {0, {0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00}};
+
 #ifdef CONFIG_CPU_FREQ
 static int
 crisv32_ethernet_freq_notifier(struct notifier_block *nb, unsigned long val,
@@ -215,8 +232,6 @@ crisv32_eth_setup_controller(struct net_
 {
 	struct crisv32_ethernet_local *np = netdev_priv(dev);
 	
-	reg_config_rw_pad_ctrl pad_ctrl;
-	
 	reg_eth_rw_tr_ctrl tr_ctrl = {
 		.retry = regk_eth_yes,
 		.pad = regk_eth_yes,
@@ -257,54 +272,26 @@ crisv32_eth_setup_controller(struct net_
 	udelay(500);
 
 	/* done */
+	
+#ifdef CONFIG_ETRAXFS
+	reg_config_rw_pad_ctrl pad_ctrl;
 	pad_ctrl = REG_RD(config, regi_config, rw_pad_ctrl);
 	pad_ctrl.phyrst_n = 1;
 	REG_WR(config, regi_config, rw_pad_ctrl, pad_ctrl);
+#else
+	gen_ctrl.phyrst_n = 1;
+	REG_WR(eth, np->eth_inst, rw_gen_ctrl, gen_ctrl);
+#endif
 
 	/* Let the PHY reset (RESET_WAIT) */
 	udelay(200);
 		
-	crisv32_eth_probe_transceiver(dev);
-}
-
-static void __init
-crisv32_eth_init_rings(struct net_device *dev)
-{
-	struct crisv32_ethernet_local *np = netdev_priv(dev);
-	int i;
-	
-	/* Initialise receive descriptors for interface. */
-	for (i = 0; i < NBR_RX_DESC; i++) {
-		struct sk_buff *skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE);
-
-		np->dma_rx_descr_list[i].skb = skb;
-		np->dma_rx_descr_list[i].descr.buf =
-			(char*)virt_to_phys(skb->data);
-		np->dma_rx_descr_list[i].descr.after =
-		    (char*)virt_to_phys(skb->data + MAX_MEDIA_DATA_SIZE);
-
-		np->dma_rx_descr_list[i].descr.eol = 0;
-		np->dma_rx_descr_list[i].descr.in_eop = 0;
-		np->dma_rx_descr_list[i].descr.next =
-		    (void *) virt_to_phys(&np->dma_rx_descr_list[i + 1].descr);
-	}
-	/* bend the list into a ring */	
-	np->dma_rx_descr_list[NBR_RX_DESC - 1].descr.next =
-		(void *) virt_to_phys(&np->dma_rx_descr_list[0].descr);
-	
-	/* Initialize transmit descriptors. */
-	for (i = 0; i < NBR_TX_DESC; i++) {
-		np->dma_tx_descr_list[i].descr.wait = 1;
-		np->dma_tx_descr_list[i].descr.eol = 0;
-		np->dma_tx_descr_list[i].descr.out_eop = 0;
-		np->dma_tx_descr_list[i].descr.next =
-			(void*)virt_to_phys(&np->dma_tx_descr_list[i+1].descr);
+	if(crisv32_eth_probe_transceiver(dev)) {
+		printk("%s: No transceiver found, removing interface\n", 
+		       dev->name);
+		unregister_netdev(dev);
+		/* We should probably do more here... */
 	}
-	/* bend the list into a ring */
-	np->dma_tx_descr_list[NBR_TX_DESC - 1].descr.next =
-		(void *) virt_to_phys(&np->dma_tx_descr_list[0].descr);
-
-	crisv32_eth_reset_rings(dev);
 }
 
 static void
@@ -366,6 +353,8 @@ crisv32_eth_reset_rings(struct net_devic
 	np->prev_rx_desc = &np->dma_rx_descr_list[NBR_RX_DESC - 1];
 	np->last_rx_desc = np->prev_rx_desc;
 	np->dma_rx_descr_list[NBR_RX_DESC - 1].descr.eol = 1;
+	/* ready to accept new packets.  */
+	np->new_rx_package = 1;
 	
 	/* reset tx-ring */
 	np->dma_tx_descr_list[0].descr.buf =
@@ -388,6 +377,93 @@ crisv32_eth_reset_rings(struct net_devic
 		(void *)virt_to_phys(&np->dma_tx_descr_list[0].descr);
 }
 
+/*
+ * Really advance the receive ring. RX interrupts must be off.
+ */
+static void
+__crisv32_eth_rx_ring_advance(struct crisv32_ethernet_local *np)
+{
+	if (np->newbuf)
+		np->active_rx_desc->descr.buf = (void *) np->newbuf;
+	np->active_rx_desc->descr.after =
+		np->active_rx_desc->descr.buf + MAX_MEDIA_DATA_SIZE;	
+	np->active_rx_desc->descr.eol = 1;
+	np->active_rx_desc->descr.in_eop = 0;
+	np->active_rx_desc = phys_to_virt((int)np->active_rx_desc->descr.next);
+	barrier();
+	np->prev_rx_desc->descr.eol = 0;
+
+	/* Workaround cache bug.  */
+	flush_dma_descr(&np->prev_rx_desc->descr, 0);
+	np->prev_rx_desc = phys_to_virt((int)np->prev_rx_desc->descr.next);
+	flush_dma_descr(&np->prev_rx_desc->descr, 1);
+}
+
+/*
+ * Advance the receive ring. RX interrupts must be off.
+ */
+static inline void
+crisv32_eth_rx_ring_advance(struct crisv32_ethernet_local *np)
+{
+	/*
+	 * When the input DMA reaches eol precaution must be taken, otherwise
+	 * the DMA could stop. The problem occurs if the eol flag is re-placed
+	 * on the descriptor that the DMA stands on before the DMA proceed to
+	 * the next descriptor. This case could, for example, happen if there
+	 * is a traffic burst and then the network goes silent. To prevent this
+	 * we make sure that we do not set the eol flag on the descriptor that
+	 * the DMA stands on.
+	 */
+	if(virt_to_phys(&np->active_rx_desc->descr) !=
+	   REG_RD_INT(dma, np->dma_in_inst, rw_saved_data)) {
+		/* Now really advance the ring one step.  */
+		__crisv32_eth_rx_ring_advance(np);
+	} else {
+		/* delay the advancing of the ring.  */
+		np->new_rx_package = 0;
+	}
+}
+
+static void __init
+crisv32_eth_init_rings(struct net_device *dev)
+{
+	struct crisv32_ethernet_local *np = netdev_priv(dev);
+	int i;
+	
+	/* Initialise receive descriptors for interface. */
+	for (i = 0; i < NBR_RX_DESC; i++) {
+		struct sk_buff *skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE);
+
+		np->dma_rx_descr_list[i].skb = skb;
+		np->dma_rx_descr_list[i].descr.buf =
+			(char*)virt_to_phys(skb->data);
+		np->dma_rx_descr_list[i].descr.after =
+		    (char*)virt_to_phys(skb->data + MAX_MEDIA_DATA_SIZE);
+
+		np->dma_rx_descr_list[i].descr.eol = 0;
+		np->dma_rx_descr_list[i].descr.in_eop = 0;
+		np->dma_rx_descr_list[i].descr.next =
+		    (void *) virt_to_phys(&np->dma_rx_descr_list[i + 1].descr);
+	}
+	/* bend the list into a ring */	
+	np->dma_rx_descr_list[NBR_RX_DESC - 1].descr.next =
+		(void *) virt_to_phys(&np->dma_rx_descr_list[0].descr);
+	
+	/* Initialize transmit descriptors. */
+	for (i = 0; i < NBR_TX_DESC; i++) {
+		np->dma_tx_descr_list[i].descr.wait = 1;
+		np->dma_tx_descr_list[i].descr.eol = 0;
+		np->dma_tx_descr_list[i].descr.out_eop = 0;
+		np->dma_tx_descr_list[i].descr.next =
+			(void*)virt_to_phys(&np->dma_tx_descr_list[i+1].descr);
+	}
+	/* bend the list into a ring */
+	np->dma_tx_descr_list[NBR_TX_DESC - 1].descr.next =
+		(void *) virt_to_phys(&np->dma_tx_descr_list[0].descr);
+
+	crisv32_eth_reset_rings(dev);
+}
+
 static void __init
 crisv32_init_leds(int ledgrp, struct net_device* dev)
 {
@@ -400,15 +476,18 @@ crisv32_init_leds(int ledgrp, struct net
 		return;
 	}
 
-	crisv32_leds[ledgrp] = kmalloc(sizeof(struct crisv32_eth_leds),GFP_KERNEL);
+	crisv32_leds[ledgrp] = kmalloc(sizeof(struct crisv32_eth_leds),
+				       GFP_KERNEL);
 
 	crisv32_leds[ledgrp]->ledgrp = ledgrp;
 	crisv32_leds[ledgrp]->led_active = 0;
-	/* NOTE: Should this value be set to zero as the jiffies timer can wrap? */
+	/* NOTE: Should this value be set to zero as the jiffies timer 
+	   can wrap? */
 	crisv32_leds[ledgrp]->led_next_time = jiffies;
 
 	crisv32_leds[ledgrp]->clear_led_timer = timer_init;
-	crisv32_leds[ledgrp]->clear_led_timer.function = crisv32_clear_network_leds;
+	crisv32_leds[ledgrp]->clear_led_timer.function = 
+		crisv32_clear_network_leds;
 	crisv32_leds[ledgrp]->clear_led_timer.data = (unsigned long) dev;
 
 	spin_lock_init(&crisv32_leds[ledgrp]->led_lock);
@@ -425,13 +504,26 @@ crisv32_ethernet_init(void)
 	printk("ETRAX FS 10/100MBit ethernet v0.01 (c)"
 	       " 2003 Axis Communications AB\n");
 
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+	{
+		reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen, 
+							 rw_clk_ctrl);
+		clk_ctrl.eth = clk_ctrl.dma0_1_eth = regk_clkgen_yes;  
+		REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl);
+	}
+#endif
 #ifdef CONFIG_ETRAX_ETHERNET_IFACE0
 {
 	int iface0 = 0;
-	/* Default MAC address for interface 0.
-	 * The real one will be set later. */
-	static struct sockaddr default_mac_iface0 = 
-		{0, {0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00}};
+
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+	if (crisv32_pinmux_alloc_fixed(pinmux_eth))
+		panic("Eth pinmux\n");
+#ifdef CONFIG_ETRAX_ETHERNET_GBIT
+	if (crisv32_pinmux_alloc_fixed(pinmux_geth))
+		panic("Eth pinmux\n");
+#endif
+#endif
 	
 	if (!(crisv32_dev[iface0] = alloc_etherdev(sizeof *np)))
 		return -ENOMEM;
@@ -553,6 +645,14 @@ crisv32_ethernet_device_init(struct net_
 #ifdef CONFIG_NET_POLL_CONTROLLER
 	dev->poll_controller = crisv32_netpoll;
 #endif
+	/*
+	 * 8 skbs keeps the system very reponsive even under high load.
+	 * At 64 the system locks, pretty much the same way as without NAPI.
+	 * 
+	 * TODO: meassure with 2 interfaces
+	 */
+	dev->weight = 8;
+	dev->poll = crisv32_eth_poll;
 	
 	np = netdev_priv(dev);
 
@@ -582,9 +682,12 @@ crisv32_eth_open(struct net_device *dev)
 {
 	struct sockaddr mac_addr;
 	reg_dma_rw_ack_intr ack_intr = { .data = 1,.in_eop = 1 };
-	reg_dma_rw_cfg dma_cfg = { .en = 1 };
 	reg_eth_rw_clr_err clr_err = {.clr = regk_eth_yes};
-	int intr_mask_nw = 0x1cff;
+	/*
+	 * dont interrupt us at any stat counter thresholds, only at urun
+	 * and exc_col.
+	 */
+	int intr_mask_nw = 0x1800;
 	int eth_ack_intr = 0xffff;
 	struct crisv32_ethernet_local *np = netdev_priv(dev);
 
@@ -620,10 +723,11 @@ crisv32_eth_open(struct net_device *dev)
 	crisv32_start_receiver(np);	
 	
 	/* Prepare output DMA. */
+	DMA_RESET(np->dma_out_inst);
+	DMA_ENABLE(np->dma_out_inst);
 #ifdef CONFIG_CRIS_MACH_ARTPEC3
 	DMA_WR_CMD(np->dma_out_inst, regk_dma_set_w_size4);
 #endif
-	REG_WR(dma, np->dma_out_inst, rw_cfg, dma_cfg);
 	netif_start_queue(dev);
 	crisv32_enable_tx_ints(np);
 	
@@ -633,10 +737,11 @@ crisv32_eth_open(struct net_device *dev)
 
 	spin_unlock(&np->lock);
 	/* 
-	 * We are now ready to accept transmit requeusts from the queueing
+	 * We are now ready to accept transmit requests from the queueing
 	 * layer of the networking.
 	 */
 	netif_carrier_on(dev);
+	netif_poll_enable(dev);
 
 	return 0;
 }
@@ -650,11 +755,13 @@ crisv32_eth_close(struct net_device *dev
 	unsigned long flags;
 
 	printk(KERN_INFO "Closing %s.\n", dev->name);
+	spin_lock_irqsave(&np->lock, flags);
 
 	/* stop the receiver before the DMA channels to avoid overruns. */
+	crisv32_disable_rx_ints(np);
+	netif_poll_disable(dev);
 	crisv32_stop_receiver(np);
 	
-	spin_lock_irqsave(&np->lock, flags);
 	netif_stop_queue(dev);
 
 	/* Reset the TX DMA in case it has hung on something. */
@@ -669,7 +776,6 @@ crisv32_eth_close(struct net_device *dev
 	ack_intr.data = 1;
 	REG_WR(dma, np->dma_out_inst, rw_ack_intr, ack_intr);
 
-	crisv32_disable_rx_ints(np);
 	ack_intr.in_eop = 1;
 	REG_WR(dma, np->dma_in_inst, rw_ack_intr, ack_intr);
 
@@ -725,42 +831,36 @@ crisv32_eth_set_mac_address(struct net_d
 static irqreturn_t
 crisv32rx_eth_interrupt(int irq, void *dev_id)
 {
-	reg_dma_r_masked_intr masked_in;
-	reg_dma_rw_cmd cmd = {0};
-	reg_dma_rw_ack_intr ack_intr = {0};	
 	struct net_device *dev = (struct net_device *) dev_id;
 	struct crisv32_ethernet_local *np = netdev_priv(dev);
+	reg_dma_r_masked_intr masked_in;
 
 	masked_in = REG_RD(dma, np->dma_in_inst, r_masked_intr);
 	
 	if (masked_in.in_eop) {
-		DEBUG(printk("EOP_IN interrupt\n"));
-
-		/* Acknowledge input dma interrupt. */
-		ack_intr.in_eop = 1;
-		REG_WR(dma, np->dma_in_inst, rw_ack_intr, ack_intr);
+		reg_dma_rw_ack_intr ack_intr = {0};	
 
-		np->new_rx_package = 1;
-		/* Check if complete packets were indeed received. */
-		while (np->active_rx_desc->descr.in_eop == 1
-		       && np->new_rx_package) {
 			/*
-			 * Take out the buffer and give it to the OS, then
-			 * allocate a new buffer to put a packet in.
+		 * Ack the rx irq even if we are not prepared to start
+		 * polling. This is needed to handle incomming packets
+		 * during the stop sequence.
 			 */
-			crisv32_eth_receive_packet(dev);
-			
-			/* Update number of packets received. */
-			np->stats.rx_packets++;
-
-			/* Restarts input dma. */
-			cmd.cont_data = 1;
-			REG_WR(dma, np->dma_in_inst, rw_cmd, cmd);
-
-			/* Acknowledge input dma interrupt. */
+		ack_intr.in_eop = 1;
 			REG_WR(dma, np->dma_in_inst, rw_ack_intr, ack_intr);
+		if (netif_rx_schedule_prep(dev)) {
+			crisv32_disable_rx_ints(np);
+			/* put us onto the poll list */
+			__netif_rx_schedule(dev);
 		}
+	} else {
+		/* Unexpected, ACK it and hope for the best.  */
+		reg_dma_rw_ack_intr ack_intr = {0xffff};
+		printk("unexpected eth-rx irq %x\n",
+		       REG_RD_INT(dma, np->dma_in_inst, r_masked_intr));
+		ack_intr.in_eop = 0;
+		REG_WR(dma, np->dma_in_inst, rw_ack_intr, ack_intr);
 	}
+
 	return IRQ_HANDLED;
 }
 
@@ -786,8 +886,12 @@ crisv32tx_eth_interrupt(int irq, void *d
 		dma_pos = phys_to_virt(REG_RD_INT(dma, np->dma_out_inst,
 						  rw_data));
 	
-	/* ack the interrupt */
+	/* ack the interrupt, if it was active */
+	if (masked_out.data)
 	REG_WR(dma, np->dma_out_inst, rw_ack_intr, ack_intr);
+	else
+		printk("unexpected eth-tx irq %x\n", 
+		       REG_RD_INT(dma, np->dma_out_inst, r_masked_intr));
 	
 	/* protect against ethernet excessive-col interrupts */
 	spin_lock_irqsave(&np->lock, flags);
@@ -959,7 +1063,8 @@ crisv32_eth_receive_packet(struct net_de
 
 	/* Activate LED */
 	spin_lock_irqsave(&np->leds->led_lock, flags);
-	if (!np->leds->led_active && time_after(jiffies, np->leds->led_next_time)) {
+	if (!np->leds->led_active && time_after(jiffies, 
+						np->leds->led_next_time)) {
 		/* light the network leds depending on the current speed. */
 		crisv32_set_network_leds(LED_ACTIVITY, dev);
 
@@ -998,9 +1103,10 @@ crisv32_eth_receive_packet(struct net_de
 			skb->protocol = eth_type_trans(skb, dev);
 			skb->ip_summed = CHECKSUM_NONE;
 			/* Send the packet to the upper layer. */
-			netif_rx(skb);
+			netif_receive_skb(skb);
 			np->last_rx_desc =
 				(void *) phys_to_virt(np->last_rx_desc->descr.next);
+			np->newbuf = 0;
 		} else {
 #endif
 			tmp = dev_alloc_skb(MAX_MEDIA_DATA_SIZE);
@@ -1015,17 +1121,15 @@ crisv32_eth_receive_packet(struct net_de
 			np->active_rx_desc->skb = tmp;
 			skb_put(skb, length);
 
-			np->active_rx_desc->descr.buf =
-				(void *) virt_to_phys(np->active_rx_desc->skb->data);
-			np->active_rx_desc->descr.after =
-				np->active_rx_desc->descr.buf + MAX_MEDIA_DATA_SIZE;
+			np->newbuf = 
+				virt_to_phys(np->active_rx_desc->skb->data);
 
 			skb->dev = dev;
 			skb->protocol = eth_type_trans(skb, dev);
 			skb->ip_summed = CHECKSUM_NONE;
 
 			/* Send the packet to the upper layer. */
-			netif_rx(skb);
+			netif_receive_skb(skb);
 			np->last_rx_desc =
 				phys_to_virt((int)
 					     np->last_rx_desc->descr.next);
@@ -1033,32 +1137,77 @@ crisv32_eth_receive_packet(struct net_de
 #ifdef CONFIG_CRIS_MACH_ARTPEC3
 	}
 #endif
+
+	/* Forward rotate the receive ring.  */
+	crisv32_eth_rx_ring_advance(np);
+}
+
+/*
+ * Is there work to do in the rx-path?
+ */
+static inline int crisv32_has_rx_work(struct crisv32_ethernet_local *np,
+				      dma_descr_data *active)
+{
+	int mw;
+	mw = (active->in_eop == 1 && np->new_rx_package);
+	return mw;
+}
+
+/*
+ * NAPI poll
+ *
+ * We are allowed to poll dev->quota skb's from the rx-ring.
+ * Must update *budget and dev->quota. If we are done, remove us from the
+ * poll list and re-enable interrupts.
+ *
+ * TODO: Add tx-reclaim,
+ *	 Are there races when we re-enable ints?
+ */
+static int crisv32_eth_poll(struct net_device *dev, int *budget)
+{
+	struct crisv32_ethernet_local *np = netdev_priv(dev);
+	int workdone = dev->quota;
+	int morework;
+	reg_dma_rw_ack_intr ack_intr = {0};	
+
+	ack_intr.in_eop = 1;
+
+	if (np->new_rx_package == 0) {
 	/*
-	 * When the input DMA reaches eol precaution must be taken, otherwise
-	 * the DMA could stop. The problem occurs if the eol flag is re-placed
-	 * on the descriptor that the DMA stands on before the DMA proceed to
-	 * the next descriptor. This case could, for example, happen if there
-	 * is a traffic burst and then the network goes silent. To prevent this
-	 * we make sure that we do not set the eol flag on the descriptor that
-	 * the DMA stands on.
+		 * In the previous round we pulled a packet from the ring but 
+		 * we didn't advance the ring due to hw DMA bug. Try to do it
+		 * now.
 	 */
-	if(virt_to_phys(&np->active_rx_desc->descr) !=
-	   REG_RD_INT(dma, np->dma_in_inst, rw_saved_data)) {
-		np->active_rx_desc->descr.after =
-			np->active_rx_desc->descr.buf + MAX_MEDIA_DATA_SIZE;
-		np->active_rx_desc->descr.eol = 1;
-		np->active_rx_desc->descr.in_eop = 0;
-		np->active_rx_desc =
-			phys_to_virt((int)np->active_rx_desc->descr.next);
-		barrier();
-		np->prev_rx_desc->descr.eol = 0;
-		flush_dma_descr(&np->prev_rx_desc->descr, 0); // Workaround cache bug
-		np->prev_rx_desc =
-			phys_to_virt((int)np->prev_rx_desc->descr.next);
-		flush_dma_descr(&np->prev_rx_desc->descr, 1); // Workaround cache bug
-	} else {
-		np->new_rx_package = 0;
+		np->new_rx_package = 1;
+		crisv32_eth_rx_ring_advance(np);
+	}
+
+	morework = crisv32_has_rx_work(np, &np->active_rx_desc->descr);
+
+	while (morework && dev->quota)
+	{
+		crisv32_eth_receive_packet(dev);
+		dev->quota--;
+		np->stats.rx_packets++;
+
+		/* Ack irq and restart rx dma */
+		REG_WR(dma, np->dma_in_inst, rw_ack_intr, ack_intr);
+		DMA_CONTINUE_DATA(np->dma_in_inst);
+
+		morework = crisv32_has_rx_work(np, &np->active_rx_desc->descr);
 	}
+	update_rx_stats(np);
+
+	/* it's our responsability to update the global budget. */
+	*budget -= (workdone - dev->quota);
+
+	if (!morework) {		
+		/* first mark as done, then enable irq's */
+		netif_rx_complete(dev);
+		crisv32_enable_rx_ints(np);
+	}
+
+	return morework;
 }
 
 /* 
@@ -1075,7 +1224,8 @@ crisv32_eth_send_packet(struct sk_buff *
 	dev->trans_start = jiffies;
 	
 	spin_lock_irqsave(&np->leds->led_lock, flags);
-	if (!np->leds->led_active && time_after(jiffies, np->leds->led_next_time)) {
+	if (!np->leds->led_active && time_after(jiffies, 
+						np->leds->led_next_time)) {
 		/* light the network leds depending on the current speed. */
 		crisv32_set_network_leds(LED_ACTIVITY, dev);
 
@@ -1116,9 +1266,17 @@ crisv32_eth_send_packet(struct sk_buff *
 	{
 		crisv32_eth_hw_send_packet(buf, skb->len, np);
 	}
+
 	/* Stop queue if full. */
-	if (np->active_tx_desc == np->catch_tx_desc)
+	if (
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+		phys_to_virt((int)np->active_tx_desc->descr.next)
+#else
+		np->active_tx_desc
+#endif
+		== np->catch_tx_desc) {
 		netif_stop_queue(dev);
+	}
 	
 	np->txpackets++;
 	spin_unlock_irqrestore(&np->lock, flags);
@@ -1136,7 +1294,8 @@ crisv32_eth_hw_send_packet(unsigned char
 	/* Configure the tx dma descriptor. */
 #ifdef CONFIG_CRIS_MACH_ARTPEC3
 	if (np->gigabit_mode) {
-	  np->active_tx_desc->descr.buf = (unsigned char *) crisv32_intmem_virt_to_phys(buf);
+	  np->active_tx_desc->descr.buf = 
+		  (unsigned char *) crisv32_intmem_virt_to_phys(buf);
 	} else 
 #endif
 	{
@@ -1185,14 +1344,14 @@ static void
 crisv32_eth_tx_timeout(struct net_device *dev)
 {
 	struct crisv32_ethernet_local *np = netdev_priv(dev);
-	reg_dma_rw_cfg cfg = {0};
 	reg_dma_rw_stat stat = {0};
 	unsigned long flags;
 
 	printk(KERN_WARNING "%s: transmit timed out\n", dev->name);
 
-	
 	spin_lock_irqsave(&np->lock, flags);
+	update_tx_stats(np);
+
 	crisv32_ethernet_bug(dev);
 
 	np->txpackets = 0;
@@ -1200,9 +1359,7 @@ crisv32_eth_tx_timeout(struct net_device
 	np->stats.tx_errors++; 
 
 	/* Reset the TX DMA in case it has hung on something. */
-	cfg.en = 0;
-	REG_WR(dma, np->dma_out_inst, rw_cfg, cfg);
-	
+	DMA_RESET(np->dma_out_inst);
 	do {
 		stat = REG_RD(dma, np->dma_out_inst, rw_stat);
 	} while (stat.mode != regk_dma_rst);
@@ -1220,12 +1377,26 @@ crisv32_eth_tx_timeout(struct net_device
 			phys_to_virt((int)np->catch_tx_desc->descr.next);
 	} while (np->catch_tx_desc != np->active_tx_desc);
 
+	reg_eth_rw_gen_ctrl gen_ctrl = { 0 };
+	gen_ctrl.en = regk_eth_yes;
+        REG_WR(eth, np->eth_inst, rw_gen_ctrl, gen_ctrl);
+
+        int ack_intr = 0xffff;
+        REG_WR_INT(eth, np->eth_inst, rw_ack_intr, ack_intr);
+        reg_eth_rw_clr_err clr_err;
+        clr_err.clr = 1;
+        REG_WR(eth, np->eth_inst, rw_clr_err, clr_err);
+
+        crisv32_enable_tx_ints(np);
+
+	DMA_ENABLE(np->dma_out_inst);
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+	DMA_WR_CMD(np->dma_out_inst, regk_dma_set_w_size4);
+#endif
+
+	/* Next packet will restart output DMA. */
+	np->sender_started = 0;
 
-	/* Start output DMA. */
-	REG_WR(dma, np->dma_out_inst, rw_group_down,
-	       (int) virt_to_phys(&np->ctxt_out));
-	DMA_WR_CMD(np->dma_out_inst, regk_dma_load_c);
-	DMA_WR_CMD(np->dma_out_inst, regk_dma_load_d | regk_dma_burst);
 	spin_unlock_irqrestore(&np->lock, flags);
 
 	/* Tell the upper layers we're ok again. */
@@ -1453,7 +1624,7 @@ static void crisv32_eth_get_drvinfo(stru
 			     struct ethtool_drvinfo *info)
 {
 	strncpy(info->driver, "ETRAX FS", sizeof(info->driver) - 1);
-	strncpy(info->version, "$Revision: 1.96 $", sizeof(info->version) - 1);
+	strncpy(info->version, "$Revision: 1.96.2.1 $", sizeof(info->version) - 1);
 	strncpy(info->fw_version, "N/A", sizeof(info->fw_version) - 1);
 	strncpy(info->bus_info, "N/A", sizeof(info->bus_info) - 1);
 }
@@ -1559,10 +1730,12 @@ crisv32_eth_switch_intmem_usage(struct n
 			/* Setup the list entry */
 			np->tx_intmem_buf_list[i].free = 1;
 			np->tx_intmem_buf_list[i].buf = intmem_tmp;
-			np->tx_intmem_buf_list[i].next = &np->tx_intmem_buf_list[i + 1];
+			np->tx_intmem_buf_list[i].next = 
+				&np->tx_intmem_buf_list[i + 1];
 		}
 		/* Setup the last list entry */
-		np->tx_intmem_buf_list[NBR_INTMEM_TX_BUF - 1].next = &np->tx_intmem_buf_list[0];
+		np->tx_intmem_buf_list[NBR_INTMEM_TX_BUF - 1].next = 
+			&np->tx_intmem_buf_list[0];
 		/* Setup initial pointer */
 		np->intmem_tx_buf_active = np->tx_intmem_buf_list;
 		np->intmem_tx_buf_catch = np->tx_intmem_buf_list;
@@ -1571,7 +1744,8 @@ crisv32_eth_switch_intmem_usage(struct n
 		for (i=0; i < NBR_INTMEM_RX_DESC; i++) {
 			/* Allocate internal memory */
 			intmem_tmp = NULL;
-			intmem_tmp = crisv32_intmem_alloc(MAX_MEDIA_DATA_SIZE, 32);
+			intmem_tmp = crisv32_intmem_alloc(MAX_MEDIA_DATA_SIZE, 
+							  32);
 			/* Check that we really got the memory */
 			if (intmem_tmp == NULL) {
 				printk(KERN_ERR "%s: Can't allocate intmem for"
@@ -1595,7 +1769,8 @@ crisv32_eth_switch_intmem_usage(struct n
 		   (void*) virt_to_phys(&np->dma_rx_descr_list[0].descr);
 		/* Initialise initial receive pointers. */
 		np->active_rx_desc = &np->dma_rx_descr_list[0];
-		np->prev_rx_desc = &np->dma_rx_descr_list[NBR_INTMEM_RX_DESC - 1];
+		np->prev_rx_desc = 
+			&np->dma_rx_descr_list[NBR_INTMEM_RX_DESC - 1];
 		np->last_rx_desc = np->prev_rx_desc;
 
 		np->gigabit_mode = 1;
@@ -1655,6 +1830,10 @@ crisv32_eth_switch_intmem_usage(struct n
 
 	DMA_WR_CMD(np->dma_in_inst, regk_dma_load_c);
 	DMA_WR_CMD(np->dma_in_inst, regk_dma_load_d | regk_dma_burst);
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+  /* Reset the dma word size. */
+	DMA_WR_CMD(np->dma_in_inst, regk_dma_set_w_size2);
+#endif
 
 	netif_wake_queue(dev);
 
@@ -1983,6 +2162,34 @@ national_check_duplex(struct net_device 
 }
 
 static void
+vitesse_check_speed(struct net_device *dev)
+{
+	unsigned long data;
+	struct crisv32_ethernet_local *np = netdev_priv(dev);
+
+	data = crisv32_eth_get_mdio_reg(dev, MDIO_VIT_AUX_STAT);
+	if ((data & 0x18) == MDIO_VIT_1000)
+		np->current_speed = 1000;
+	else if ((data & 0x18) == MDIO_VIT_100)
+		np->current_speed = 100;
+	else
+		np->current_speed = 10;	
+}
+
+static void
+vitesse_check_duplex(struct net_device *dev)
+{
+	unsigned long data;
+	struct crisv32_ethernet_local *np = netdev_priv(dev);
+
+	data = crisv32_eth_get_mdio_reg(dev, MDIO_VIT_AUX_STAT);
+	if (data & 0x20)
+		np->full_duplex = 1;
+	else
+		np->full_duplex = 0;
+}
+
+static void
 crisv32_eth_reset_tranceiver(struct net_device *dev)
 {
 	int i;
@@ -2120,7 +2327,8 @@ crisv32_clear_network_leds(unsigned long
 	unsigned long flags;
 
 	spin_lock_irqsave(&np->leds->led_lock, flags);
-	if (np->leds->led_active && time_after(jiffies, np->leds->led_next_time)) {
+	if (np->leds->led_active && time_after(jiffies, 
+					       np->leds->led_next_time)) {
 		crisv32_set_network_leds(LED_NOACTIVITY, dev);
 
 		/* Set the earliest time we may set the LED */
@@ -2169,7 +2377,8 @@ crisv32_set_network_leds(int active, str
 	}
 
 	if (!np->current_speed) {
-		/* Set link down if none of the interfaces that use this led group is up */
+		/* Set link down if none of the interfaces that use this led
+		   group is up */
 		if ((np->leds->ifisup[0] + np->leds->ifisup[1]) == 0) {
 #if defined(CONFIG_ETRAX_NETWORK_RED_ON_NO_CONNECTION)
 			/* Make LED red, link is down */
@@ -2195,7 +2404,7 @@ crisv32_set_network_leds(int active, str
 static void
 crisv32_netpoll(struct net_device* netdev)
 {
-	crisv32rx_eth_interrupt(DMA0_INTR_VECT, netdev, NULL);
+	crisv32rx_eth_interrupt(DMA0_INTR_VECT, netdev);
 }
 #endif
 
@@ -2239,20 +2448,29 @@ static void crisv32_ethernet_bug(struct 
 		
 	/* Get the current output dma position. */
 	stat = REG_RD(dma, np->dma_out_inst, rw_stat);
-	dma_pos = phys_to_virt(REG_RD_INT(dma, np->dma_out_inst, rw_data));
+
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+	if (np->gigabit_mode) {
+		dma_pos = crisv32_intmem_phys_to_virt(
+			REG_RD_INT(dma, np->dma_out_inst, rw_data));
+		in_dma_pos = crisv32_intmem_phys_to_virt(
+			REG_RD_INT(dma, np->dma_in_inst, rw_data));
+	} else
+#endif
+	{
+		dma_pos = phys_to_virt(REG_RD_INT(dma, np->dma_out_inst, 
+						  rw_data));
+		in_dma_pos = phys_to_virt(REG_RD_INT(dma, np->dma_in_inst, 
+						     rw_data));
+	}
 	in_stat = REG_RD(dma, np->dma_in_inst, rw_stat);
-	in_dma_pos = phys_to_virt(REG_RD_INT(dma, np->dma_in_inst, rw_data));
 	
 	printk("%s:\n"
 	       "stat.list_state=%x\n"
 	       "stat.mode=%x\n"
 	       "stat.stream_cmd_src=%x\n"
 	       "dma_pos=%x\n"
-	       "in_stat.list_state=%x\n"
-	       "in_stat.mode=%x\n"
-	       "in_stat.stream_cmd_src=%x\n"
-	       "in_dma_pos=%x\n"
-	       "catch=%x active=%x\n"
+	       "tx catch=%x active=%x\n"
 	       "packets=%d queue=%d\n"
 	       "intr_vect.r_vect=%x\n"
 	       "dma.r_masked_intr=%x dma.rw_ack_intr=%x "
@@ -2261,8 +2479,6 @@ static void crisv32_ethernet_bug(struct 
 	       __func__,
 	       stat.list_state, stat.mode, stat.stream_cmd_src,
 	       (unsigned int)dma_pos,
-	       in_stat.list_state, in_stat.mode, in_stat.stream_cmd_src,
-	       (unsigned int)in_dma_pos,
 	       (unsigned int)&np->catch_tx_desc->descr,
 	       (unsigned int)&np->active_tx_desc->descr,
 	       np->txpackets,
@@ -2274,6 +2490,39 @@ static void crisv32_ethernet_bug(struct 
 	       REG_RD_INT(dma, np->dma_out_inst, rw_intr_mask),
 	       REG_RD_INT(eth, np->eth_inst, r_stat));
 
+	printk("in_stat.list_state=%x\n"
+	       "in_stat.mode=%x\n"
+	       "in_stat.stream_cmd_src=%x\n"
+	       "in_dma_pos=%x\n"
+	       "rx last=%x prev=%x active=%x\n",
+	       in_stat.list_state, in_stat.mode, in_stat.stream_cmd_src,
+	       (unsigned int)in_dma_pos,
+	       (unsigned int)&np->last_rx_desc->descr,
+	       (unsigned int)&np->prev_rx_desc->descr,
+	       (unsigned int)&np->active_rx_desc->descr);
+	       
+
+	printk("rx-descriptors:\n");
+	for (i = 0; i < NBR_RX_DESC; i++) {
+		printk("rxdesc[%d]=0x%x\n", i, (unsigned int)
+		       virt_to_phys(&np->dma_rx_descr_list[i].descr));
+		printk("rxdesc[%d].skb=0x%x\n", i,
+		       (unsigned int)np->dma_rx_descr_list[i].skb);
+		printk("rxdesc[%d].buf=0x%x\n", i,
+		       (unsigned int)np->dma_rx_descr_list[i].descr.buf);
+		printk("rxdesc[%d].after=0x%x\n", i,
+		       (unsigned int)np->dma_rx_descr_list[i].descr.after);
+		printk("rxdesc[%d].intr=%x\n", i,
+		       np->dma_rx_descr_list[i].descr.intr);
+		printk("rxdesc[%d].eol=%x\n", i,
+		       np->dma_rx_descr_list[i].descr.eol);
+		printk("rxdesc[%d].out_eop=%x\n", i,
+		       np->dma_rx_descr_list[i].descr.out_eop);
+		printk("rxdesc[%d].in_eop=%x\n", i,
+		       np->dma_rx_descr_list[i].descr.in_eop);
+		printk("rxdesc[%d].wait=%x\n", i,
+		       np->dma_rx_descr_list[i].descr.wait);
+	}
 	printk("tx-descriptors:\n");
 	for (i = 0; i < NBR_TX_DESC; i++) {
 		printk("txdesc[%d]=0x%x\n", i, (unsigned int)
@@ -2290,6 +2539,8 @@ static void crisv32_ethernet_bug(struct 
 		       np->dma_tx_descr_list[i].descr.eol);
 		printk("txdesc[%d].out_eop=%x\n", i,
 		       np->dma_tx_descr_list[i].descr.out_eop);
+		printk("txdesc[%d].in_eop=%x\n", i,
+		       np->dma_tx_descr_list[i].descr.in_eop);
 		printk("txdesc[%d].wait=%x\n", i,
 		       np->dma_tx_descr_list[i].descr.wait);
 	}
@@ -2302,4 +2553,26 @@ crisv32_init_module(void)
 	return crisv32_ethernet_init();
 }
 
+static int __init
+crisv32_boot_setup(char* str)
+{
+	struct sockaddr sa = {0};
+	int i;
+
+	/* Parse the colon separated Ethernet station address */
+	for (i = 0; i <  ETH_ALEN; i++) {
+		unsigned int tmp;
+		if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
+			printk(KERN_WARNING "Malformed station address");
+			return 0;
+		}
+		sa.sa_data[i] = (char)tmp;
+	}
+
+	default_mac_iface0 = sa;
+	return 1;
+}
+
+__setup("crisv32_eth=", crisv32_boot_setup);
+
 module_init(crisv32_init_module);
Index: os/linux-2.6/drivers/net/cris/eth_v32.h
===================================================================
RCS file: /usr/local/cvs/linux/os/linux-2.6/drivers/net/cris/eth_v32.h,v
retrieving revision 1.28
retrieving revision 1.28.2.1
diff -b -u -p -r1.28 -r1.28.2.1
--- os/linux-2.6/drivers/net/cris/eth_v32.h	6 Feb 2007 10:10:37 -0000	1.28
+++ os/linux-2.6/drivers/net/cris/eth_v32.h	4 Sep 2007 11:19:32 -0000	1.28.2.1
@@ -12,10 +12,10 @@
 
 #define MAX_MEDIA_DATA_SIZE 1522	/* Max packet size. */
 
-#define NBR_RX_DESC 64			/* Number of RX descriptors. */
+#define NBR_RX_DESC 16			/* Number of RX descriptors. */
 #define NBR_TX_DESC 16			/* Number of TX descriptors. */
 #ifdef CONFIG_CRIS_MACH_ARTPEC3
-#define NBR_INTMEM_RX_DESC 5		/* Number of RX descriptors in int. mem.
+#define NBR_INTMEM_RX_DESC 16		/* Number of RX descriptors in int. mem.
 					 * when running in gigabit mode.
 					 * Should be less then NBR_RX_DESC 
 					 */
@@ -62,6 +62,13 @@
 #define MDIO_NAT_100             (0x0001 << 3)
 #define MDIO_NAT_FULL_DUPLEX_IND (0x0001 << 1)
 
+/* Vitesse VCS8641 specific */
+#define MDIO_VIT_AUX_STAT 0x1c
+#define MDIO_VIT_1000            (0x2 << 3)
+#define MDIO_VIT_100             (0x1 << 3)
+#define MDIO_VIT_10              0
+#define MDIO_VIT_FD              (0x1 << 5)
+
 /* Network flash constants */
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
 #define NET_FLASH_PAUSE                 (HZ/100) /* 10 ms */
@@ -171,7 +178,7 @@ struct crisv32_ethernet_local {
 	unsigned int mdio_phy_addr;
 
 	struct transceiver_ops *transceiver;
-
+	unsigned long newbuf;
 	/* 
 	 * TX control lock. This protects the transmit buffer ring state along
 	 * with the "tx full" state of the driver.  This means all netif_queue
@@ -185,6 +192,7 @@ struct crisv32_ethernet_local {
 static int crisv32_ethernet_init(void);
 static int crisv32_ethernet_device_init(struct net_device* dev);
 static int crisv32_eth_open(struct net_device *dev);
+static int crisv32_eth_poll(struct net_device *dev, int *budget);
 static int crisv32_eth_close(struct net_device *dev);
 static int crisv32_eth_set_mac_address(struct net_device *dev, void *vpntr);
 static irqreturn_t crisv32rx_eth_interrupt(int irq, void *dev_id);
@@ -221,6 +229,8 @@ static void intel_check_speed(struct net
 static void intel_check_duplex(struct net_device *dev);
 static void national_check_speed(struct net_device* dev);
 static void national_check_duplex(struct net_device *dev);
+static void vitesse_check_speed(struct net_device* dev);
+static void vitesse_check_duplex(struct net_device *dev);
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void crisv32_netpoll(struct net_device* dev);

Save the patch in a file, e.g. eth_patch. Apply the patch from the root of your tree:

patch -p 0 < eth_patch

rebuild the kernel:

make -C packages/os/linux-2.6 install

and build a new image:

make images

Jesper Bengtsson 2007/09/10 09:27

 
v32_eth_dr_patch.txt · Last modified: 2007/09/10 11:19 by jesper
 
All text is available under the terms of the GNU Free Documentation License (see Copyrights for details).