patch-2.4.15 linux/drivers/net/wan/z85230.c

Next file: linux/drivers/net/wan/z85230.h
Previous file: linux/drivers/net/wan/farsync.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/drivers/net/wan/z85230.c linux/drivers/net/wan/z85230.c
@@ -4,8 +4,8 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	(c) Copyright 1998 Building Number Three Ltd
- *	(c) Copyright 2000 Red Hat Software
+ *	(c) Copyright 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *	(c) Copyright 2000, 2001 Red Hat Inc
  *
  *	Development of this driver was funded by Equiinet Ltd
  *			http://www.equiinet.com
@@ -18,6 +18,8 @@
  *	DMA now uses get_free_page as kmalloc buffers may span a 64K 
  *	boundary.
  *
+ *	Modified for SMP safety and SMP locking by Alan Cox <alan@redhat.com>
+ *
  *	Performance
  *
  *	Z85230:
@@ -53,8 +55,6 @@
 #include "z85230.h"
 
 
-static spinlock_t z8530_buffer_lock = SPIN_LOCK_UNLOCKED;
-
 /**
  *	z8530_read_port - Architecture specific interface function
  *	@p: port to read
@@ -116,21 +116,14 @@
  *	
  *	Most of the Z8530 registers are indexed off the control registers.
  *	A read is done by writing to the control register and reading the
- *	register back. We do the locking needed to protect this 
- *	operation.
+ *	register back.  The caller must hold the lock
  */
  
 static inline u8 read_zsreg(struct z8530_channel *c, u8 reg)
 {
-	u8 r;
-	unsigned long flags;
-	save_flags(flags);
-	cli();
 	if(reg)
 		z8530_write_port(c->ctrlio, reg);
-	r=z8530_read_port(c->ctrlio);
-	restore_flags(flags);
-	return r;
+	return z8530_read_port(c->ctrlio);
 }
 
 /**
@@ -154,7 +147,7 @@
  *	@reg: Register number
  *	@val: Value to write
  *
- *	Write a value to an indexed register. Perform the locking needed
+ *	Write a value to an indexed register. The caller must hold the lock
  *	to honour the irritating delay rules. We know about register 0
  *	being fast to access.
  */
@@ -162,12 +155,14 @@
 static inline void write_zsreg(struct z8530_channel *c, u8 reg, u8 val)
 {
 	unsigned long flags;
-	save_flags(flags);
-	cli();
+
+	spin_lock_irqsave(c->lock, flags);
+
 	if(reg)
 		z8530_write_port(c->ctrlio, reg);
 	z8530_write_port(c->ctrlio, val);
-	restore_flags(flags);
+
+	spin_unlock_irqrestore(c->lock, flags);
 }
 
 /**
@@ -299,7 +294,7 @@
  *	@set: 1 to set, 0 to clear
  *
  *	Sets or clears DTR/RTS on the requested line. All locking is handled
- *	for the caller. For now we assume all boards use the actual RTS/DTR
+ *	by the caller. For now we assume all boards use the actual RTS/DTR
  *	on the chip. Apparently one or two don't. We'll scream about them
  *	later.
  */
@@ -333,12 +328,14 @@
  *	do it yourself but consider medical assistance first. This non DMA 
  *	synchronous mode is portable code. The DMA mode assumes PCI like 
  *	ISA DMA
+ *
+ *	Called with the device lock held
  */
  
 static void z8530_rx(struct z8530_channel *c)
 {
 	u8 ch,stat;
-	 
+ 
 	while(1)
 	{
 		/* FIFO empty ? */
@@ -382,6 +379,10 @@
 			}
 			else
 			{
+				/*
+				 *	Drop the lock for RX processing, or
+		 		 *	there are deadlocks
+		 		 */
 				z8530_rx_done(c);
 				write_zsctrl(c, RES_Rx_CRC);
 			}
@@ -407,8 +408,7 @@
  
 static void z8530_tx(struct z8530_channel *c)
 {
-	while(c->txcount)
-	{
+	while(c->txcount) {
 		/* FIFO full ? */
 		if(!(read_zsreg(c, R0)&4))
 			break;
@@ -424,8 +424,8 @@
 			write_zsctrl(c, RES_EOM_L);
 			write_zsreg(c, R10, c->regs[10]&~ABUNDER);
 		}
-		return;
 	}
+
 	
 	/*
 	 *	End of frame TX - fire another one
@@ -434,7 +434,6 @@
 	write_zsctrl(c, RES_Tx_P);
 
 	z8530_tx_done(c);	 
-/*	write_zsreg(c, R8, *c->tx_ptr++); */
 	write_zsctrl(c, RES_H_IUS);
 }
 
@@ -451,8 +450,10 @@
 
 static void z8530_status(struct z8530_channel *chan)
 {
-	u8 status=read_zsreg(chan, R0);
-	u8 altered=chan->status^status;
+	u8 status, altered;
+
+	status=read_zsreg(chan, R0);
+	altered=chan->status^status;
 	
 	chan->status=status;
 	
@@ -512,11 +513,12 @@
 	{
 		/* Special condition check only */
 		u8 status;
-
+	
 		read_zsreg(chan, R7);
 		read_zsreg(chan, R6);
 		
 		status=read_zsreg(chan, R1);
+	
 		if(status&END_FR)
 		{
 			z8530_rx_done(chan);	/* Fire up the next one */
@@ -565,16 +567,20 @@
  
 static void z8530_dma_status(struct z8530_channel *chan)
 {
-	unsigned long flags;
-	u8 status=read_zsreg(chan, R0);
-	u8 altered=chan->status^status;
+	u8 status, altered;
+
+	status=read_zsreg(chan, R0);
+	altered=chan->status^status;
 	
 	chan->status=status;
 
+
 	if(chan->dma_tx)
 	{
 		if(status&TxEOM)
 		{
+			unsigned long flags;
+	
 			flags=claim_dma_lock();
 			disable_dma(chan->txdma);
 			clear_dma_ff(chan->txdma);	
@@ -706,6 +712,10 @@
  *	the channel specific call backs for each channel that has events.
  *	We have to use callback functions because the two channels can be
  *	in different modes.
+ *
+ *	Locking is done for the handlers. Note that locking is done
+ *	at the chip level (the 5uS delay issue is per chip not per
+ *	channel). c->lock for both channels points to dev->lock
  */
 
 void z8530_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -714,6 +724,7 @@
 	u8 intr;
 	static volatile int locker=0;
 	int work=0;
+	struct z8530_irqhandler *irqs=dev->chanA.irqs;
 	
 	if(locker)
 	{
@@ -721,11 +732,12 @@
 		return;
 	}
 	locker=1;
-	
+
+	spin_lock(&dev->lock);
+
 	while(++work<5000)
 	{
-		struct z8530_irqhandler *irqs=dev->chanA.irqs;
-		
+
 		intr = read_zsreg(&dev->chanA, R3);
 		if(!(intr & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT)))
 			break;
@@ -758,6 +770,7 @@
 				irqs->status(&dev->chanB);
 		}
 	}
+	spin_unlock(&dev->lock);
 	if(work==5000)
 		printk(KERN_ERR "%s: interrupt jammed - abort(0x%X)!\n", dev->name, intr);
 	/* Ok all done */
@@ -786,12 +799,17 @@
  
 int z8530_sync_open(struct net_device *dev, struct z8530_channel *c)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(c->lock, flags);
+
 	c->sync = 1;
 	c->mtu = dev->mtu+64;
 	c->count = 0;
 	c->skb = NULL;
 	c->skb2 = NULL;
 	c->irqs = &z8530_sync;
+
 	/* This loads the double buffer up */
 	z8530_rx_done(c);	/* Load the frame ring */
 	z8530_rx_done(c);	/* Load the backup frame */
@@ -800,6 +818,8 @@
 	c->regs[R1]|=TxINT_ENAB;
 	write_zsreg(c, R1, c->regs[R1]);
 	write_zsreg(c, R3, c->regs[R3]|RxENABLE);
+
+	spin_unlock_irqrestore(c->lock, flags);
 	return 0;
 }
 
@@ -818,7 +838,9 @@
 int z8530_sync_close(struct net_device *dev, struct z8530_channel *c)
 {
 	u8 chk;
+	unsigned long flags;
 	
+	spin_lock_irqsave(c->lock, flags);
 	c->irqs = &z8530_nop;
 	c->max = 0;
 	c->sync = 0;
@@ -826,6 +848,8 @@
 	chk=read_zsreg(c,R0);
 	write_zsreg(c, R3, c->regs[R3]);
 	z8530_rtsdtr(c,0);
+
+	spin_unlock_irqrestore(c->lock, flags);
 	return 0;
 }
 
@@ -887,6 +911,8 @@
 	/*
 	 *	Enable DMA control mode
 	 */
+
+	spin_lock_irqsave(c->lock, flags);
 	 
 	/*
 	 *	TX DMA via DIR/REQ
@@ -945,6 +971,8 @@
 	c->irqs = &z8530_dma_sync;
 	z8530_rtsdtr(c,1);
 	write_zsreg(c, R3, c->regs[R3]|RxENABLE);
+
+	spin_unlock_irqrestore(c->lock, flags);
 	
 	return 0;
 }
@@ -986,6 +1014,8 @@
 	c->txdma_on = 0;
 	c->tx_dma_used = 0;
 
+	spin_lock_irqsave(c->lock, flags);
+
 	/*
 	 *	Disable DMA control mode
 	 */
@@ -1011,6 +1041,9 @@
 	chk=read_zsreg(c,R0);
 	write_zsreg(c, R3, c->regs[R3]);
 	z8530_rtsdtr(c,0);
+
+	spin_unlock_irqrestore(c->lock, flags);
+
 	return 0;
 }
 
@@ -1038,20 +1071,6 @@
 	c->skb2 = NULL;
 	
 	/*
-	 *	Load the PIO receive ring
-	 */
-
-	z8530_rx_done(c);
-	z8530_rx_done(c);
-
- 	/*
-	 *	Load the DMA interfaces up
-	 */
-
-	c->rxdma_on = 0;
-	c->txdma_on = 0;
-	
-	/*
 	 *	Allocate the DMA flip buffers. Limit by page size.
 	 *	Everyone runs 1500 mtu or less on wan links so this
 	 *	should be fine.
@@ -1066,6 +1085,23 @@
 
 	c->tx_dma_buf[1] = c->tx_dma_buf[0] + PAGE_SIZE/2;
 
+
+	spin_lock_irqsave(c->lock, flags);
+
+	/*
+	 *	Load the PIO receive ring
+	 */
+
+	z8530_rx_done(c);
+	z8530_rx_done(c);
+
+ 	/*
+	 *	Load the DMA interfaces up
+	 */
+
+	c->rxdma_on = 0;
+	c->txdma_on = 0;
+	
 	c->tx_dma_used=0;
 	c->dma_num=0;
 	c->dma_ready=1;
@@ -1106,10 +1142,9 @@
 	c->tx_dma_used = 1;
 	 
 	c->irqs = &z8530_txdma_sync;
-	printk("Loading RX\n");
 	z8530_rtsdtr(c,1);
-	printk("Rx interrupts ON\n");	
 	write_zsreg(c, R3, c->regs[R3]|RxENABLE);
+	spin_unlock_irqrestore(c->lock, flags);
 	
 	return 0;
 }
@@ -1129,6 +1164,9 @@
 {
 	unsigned long flags;
 	u8 chk;
+
+	
+	spin_lock_irqsave(c->lock, flags);
 	
 	c->irqs = &z8530_nop;
 	c->max = 0;
@@ -1208,25 +1246,11 @@
 
 EXPORT_SYMBOL(z8530_describe);
 
-/**
- *	z8530_init - Initialise a Z8530 device
- *	@dev: Z8530 device to initialise.
- *
- *	Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
- *	is present, identify the type and then program it to hopefully
- *	keep quite and behave. This matters a lot, a Z8530 in the wrong
- *	state will sometimes get into stupid modes generating 10Khz
- *	interrupt streams and the like.
- *
- *	We set the interrupt handler up to discard any events, in case
- *	we get them during reset or setp.
- *
- *	Return 0 for success, or a negative value indicating the problem
- *	in errno form.
+/*
+ *	Locked operation part of the z8530 init code
  */
-
  
-int z8530_init(struct z8530_dev *dev)
+static int do_z8530_init(struct z8530_dev *dev)
 {
 	/* NOP the interrupt handlers first - we might get a
 	   floating IRQ transition when we reset the chip */
@@ -1234,6 +1258,12 @@
 	dev->chanB.irqs=&z8530_nop;
 	dev->chanA.dcdcheck=DCD;
 	dev->chanB.dcdcheck=DCD;
+
+	/* Set up the chip level lock */
+	spin_lock_init(&dev->lock);
+	dev->chanA.lock = &dev->lock;
+	dev->chanB.lock = &dev->lock;
+
 	/* Reset the chip */
 	write_zsreg(&dev->chanA, R9, 0xC0);
 	udelay(200);
@@ -1287,6 +1317,40 @@
 	return 0;
 }
 
+/**
+ *	z8530_init - Initialise a Z8530 device
+ *	@dev: Z8530 device to initialise.
+ *
+ *	Configure up a Z8530/Z85C30 or Z85230 chip. We check the device
+ *	is present, identify the type and then program it to hopefully
+ *	keep quite and behave. This matters a lot, a Z8530 in the wrong
+ *	state will sometimes get into stupid modes generating 10Khz
+ *	interrupt streams and the like.
+ *
+ *	We set the interrupt handler up to discard any events, in case
+ *	we get them during reset or setp.
+ *
+ *	Return 0 for success, or a negative value indicating the problem
+ *	in errno form.
+ */
+
+int z8530_init(struct z8530_dev *dev)
+{
+	unsigned long flags;
+	int ret;
+
+	/* Set up the chip level lock */
+	spin_lock_init(&dev->lock);
+	dev->chanA.lock = &dev->lock;
+	dev->chanB.lock = &dev->lock;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	ret = do_z8530_init(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return ret;
+}
+
 
 EXPORT_SYMBOL(z8530_init);
 
@@ -1297,15 +1361,22 @@
  *	We set the interrupt handlers to silence any interrupts. We then 
  *	reset the chip and wait 100uS to be sure the reset completed. Just
  *	in case the caller then tries to do stuff.
+ *
+ *	This is called without the lock held
  */
  
 int z8530_shutdown(struct z8530_dev *dev)
 {
+	unsigned long flags;
 	/* Reset the chip */
+
+	spin_lock_irqsave(&dev->lock, flags);
 	dev->chanA.irqs=&z8530_nop;
 	dev->chanB.irqs=&z8530_nop;
 	write_zsreg(&dev->chanA, R9, 0xC0);
+	/* We must lock the udelay, the chip is offlimits here */
 	udelay(100);
+	spin_unlock_irqrestore(&dev->lock, flags);
 	return 0;
 }
 
@@ -1324,6 +1395,10 @@
 
 int z8530_channel_load(struct z8530_channel *c, u8 *rtable)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(c->lock, flags);
+
 	while(*rtable!=255)
 	{
 		int reg=*rtable++;
@@ -1344,6 +1419,8 @@
 	c->status=read_zsreg(c, R0);
 	c->sync=1;
 	write_zsreg(c, R3, c->regs[R3]|RxENABLE);
+
+	spin_unlock_irqrestore(c->lock, flags);
 	return 0;
 }
 
@@ -1360,6 +1437,8 @@
  *
  *	Note: We are handling this code path in the interrupt path, keep it
  *	fast or bad things will happen.
+ *
+ *	Called with the lock held.
  */
 
 static void z8530_tx_begin(struct z8530_channel *c)
@@ -1430,8 +1509,7 @@
 		}
 		else
 		{
-			save_flags(flags);
-			cli();
+
 			/* ABUNDER off */
 			write_zsreg(c, R10, c->regs[10]);
 			write_zsctrl(c, RES_Tx_CRC);
@@ -1442,7 +1520,7 @@
 				write_zsreg(c, R8, *c->tx_ptr++);
 				c->txcount--;
 			}
-			restore_flags(flags);
+
 		}
 	}
 }
@@ -1454,25 +1532,22 @@
  *	This is called when we complete a packet send. We wake the queue,
  *	start the next packet going and then free the buffer of the existing
  *	packet. This code is fairly timing sensitive.
+ *
+ *	Called with the register lock held.
  */ 
  
 static void z8530_tx_done(struct z8530_channel *c)
 {
-	unsigned long flags;
 	struct sk_buff *skb;
 
-	spin_lock_irqsave(&z8530_buffer_lock, flags);
 	netif_wake_queue(c->netdevice);
 	/* Actually this can happen.*/
 	if(c->tx_skb==NULL)
-	{
-		spin_unlock_irqrestore(&z8530_buffer_lock, flags);
 		return;
-	}
+
 	skb=c->tx_skb;
 	c->tx_skb=NULL;
 	z8530_tx_begin(c);
-	spin_unlock_irqrestore(&z8530_buffer_lock, flags);
 	c->stats.tx_packets++;
 	c->stats.tx_bytes+=skb->len;
 	dev_kfree_skb_irq(skb);
@@ -1489,7 +1564,7 @@
  
 void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb)
 {
-	kfree_skb(skb);
+	dev_kfree_skb_any(skb);
 }
 
 EXPORT_SYMBOL(z8530_null_rx);
@@ -1503,6 +1578,8 @@
  *	ESCC mode, but on the older chips we have no choice. We flip to the
  *	new buffer immediately in DMA mode so that the DMA of the next
  *	frame can occur while we are copying the previous buffer to an sk_buff
+ *
+ *	Called with the lock held
  */
  
 static void z8530_rx_done(struct z8530_channel *c)
@@ -1673,6 +1750,9 @@
  *	hard to hit interrupt latencies for the Z85230 per packet 
  *	even in DMA mode we do the flip to DMA buffer if needed here
  *	not in the IRQ.
+ *
+ *	Called from the network code. The lock is not held at this 
+ *	point.
  */
 
 int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb)
@@ -1711,9 +1791,9 @@
 	c->tx_next_skb=skb;
 	RT_UNLOCK;
 	
-	spin_lock_irqsave(&z8530_buffer_lock, flags);
+	spin_lock_irqsave(c->lock, flags);
 	z8530_tx_begin(c);
-	spin_unlock_irqrestore(&z8530_buffer_lock, flags);
+	spin_unlock_irqrestore(c->lock, flags);
 	
 	netif_wake_queue(c->netdevice);
 	return 0;
@@ -1727,6 +1807,9 @@
  *
  *	Get the statistics block. We keep the statistics in software as
  *	the chip doesn't do it for us.
+ *
+ *	Locking is ignored here - we could lock for a copy but its
+ *	not likely to be that big an issue
  */
  
 struct net_device_stats *z8530_get_stats(struct z8530_channel *c)

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