patch-2.3.35 linux/drivers/net/sunlance.c

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

diff -u --recursive --new-file v2.3.34/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.88 1999/08/20 00:31:45 davem Exp $
+/* $Id: sunlance.c,v 1.92 1999/12/15 14:08:09 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -60,15 +60,16 @@
  * 1.12:
  * 	 11/3/99: Fixed SMP race in lance_start_xmit found by davem.
  * 	          Anton Blanchard (anton@progsoc.uts.edu.au)
+ * 2.00: 11/9/99: Massive overhaul and port to new SBUS driver interfaces.
+ *		  David S. Miller (davem@redhat.com)
  */
 
 #undef DEBUG_DRIVER
 
 static char *version =
-	"sunlance.c:v1.12 11/Mar/99 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
+	"sunlance.c:v2.00 11/Sep/99 Miguel de Icaza (miguel@nuclecu.unam.mx)\n";
 
 static char *lancestr = "LANCE";
-static char *lancedma = "LANCE DMA";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -177,53 +178,55 @@
 #define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
 #define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
 #define TX_RING_LEN_BITS		((LANCE_LOG_TX_BUFFERS) << 29)
+#define TX_NEXT(__x)			(((__x)+1) & TX_RING_MOD_MASK)
 
 #define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
 #define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
 #define RX_RING_LEN_BITS		((LANCE_LOG_RX_BUFFERS) << 29)
+#define RX_NEXT(__x)			(((__x)+1) & RX_RING_MOD_MASK)
 
 #define PKT_BUF_SZ		1544
 #define RX_BUFF_SIZE            PKT_BUF_SZ
 #define TX_BUFF_SIZE            PKT_BUF_SZ
 
 struct lance_rx_desc {
-	unsigned short rmd0;        /* low address of packet */
-	unsigned char  rmd1_bits;   /* descriptor bits */
-	unsigned char  rmd1_hadr;   /* high address of packet */
-	short    length;    	    /* This length is 2s complement (negative)!
-				     * Buffer length
-				     */
-	unsigned short mblength;    /* This is the actual number of bytes received */
+	u16	rmd0;		/* low address of packet */
+	u8	rmd1_bits;	/* descriptor bits */
+	u8	rmd1_hadr;	/* high address of packet */
+	s16	length;		/* This length is 2s complement (negative)!
+				 * Buffer length
+				 */
+	u16	mblength;	/* This is the actual number of bytes received */
 };
 
 struct lance_tx_desc {
-	unsigned short tmd0;        /* low address of packet */
-	unsigned char  tmd1_bits;   /* descriptor bits */
-	unsigned char  tmd1_hadr;   /* high address of packet */
-	short length;          	    /* Length is 2s complement (negative)! */
-	unsigned short misc;
+	u16	tmd0;		/* low address of packet */
+	u8 	tmd1_bits;	/* descriptor bits */
+	u8 	tmd1_hadr;	/* high address of packet */
+	s16 	length;		/* Length is 2s complement (negative)! */
+	u16 	misc;
 };
 		
 /* The LANCE initialization block, described in databook. */
 /* On the Sparc, this block should be on a DMA region     */
 struct lance_init_block {
-	unsigned short mode;		/* Pre-set mode (reg. 15) */
-	unsigned char phys_addr[6];     /* Physical ethernet address */
-	unsigned filter[2];		/* Multicast filter. */
+	u16	mode;		/* Pre-set mode (reg. 15) */
+	u8	phys_addr[6];	/* Physical ethernet address */
+	u32	filter[2];	/* Multicast filter. */
 
 	/* Receive and transmit ring base, along with extra bits. */
-	unsigned short rx_ptr;		/* receive descriptor addr */
-	unsigned short rx_len;		/* receive len and high addr */
-	unsigned short tx_ptr;		/* transmit descriptor addr */
-	unsigned short tx_len;		/* transmit len and high addr */
+	u16	rx_ptr;		/* receive descriptor addr */
+	u16	rx_len;		/* receive len and high addr */
+	u16	tx_ptr;		/* transmit descriptor addr */
+	u16	tx_len;		/* transmit len and high addr */
     
 	/* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
 	struct lance_rx_desc brx_ring[RX_RING_SIZE];
 	struct lance_tx_desc btx_ring[TX_RING_SIZE];
     
-	char   tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
-	char   pad[2];			/* align rx_buf for copy_and_sum(). */
-	char   rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
+	u8	tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
+	u8	pad[2];		/* align rx_buf for copy_and_sum(). */
+	u8	rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
 };
 
 #define libdesc_offset(rt, elem) \
@@ -233,40 +236,48 @@
 ((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem][0])))))
 
 struct lance_private {
-	char *name;
-	volatile struct lance_regs *ll;
+	unsigned long	lregs;		/* Lance RAP/RDP regs.		*/
+	unsigned long	dregs;		/* DMA controller regs.		*/
 	volatile struct lance_init_block *init_block;
-	__u32 init_block_dvma;
     
-	int rx_new, tx_new;
-	int rx_old, tx_old;
+	int		rx_new, tx_new;
+	int		rx_old, tx_old;
     
 	struct net_device_stats	stats;
-	struct Linux_SBus_DMA *ledma; /* If set this points to ledma    */
-				      /* and arch = sun4m		*/
-
-	int tpe;		      /* cable-selection is TPE		*/
-	int auto_select;	      /* cable-selection by carrier	*/
-	int burst_sizes;	      /* ledma SBus burst sizes		*/
-
-	unsigned short busmaster_regval;
-	unsigned short pio_buffer;
-
-	struct net_device		 *dev;		  /* Backpointer	*/
-	struct lance_private	 *next_module;
-	struct linux_sbus        *sbus;
-	struct timer_list         multicast_timer;
+	struct sbus_dma *ledma;	/* If set this points to ledma	*/
+	char		tpe;		/* cable-selection is TPE	*/
+	char		auto_select;	/* cable-selection by carrier	*/
+	char		burst_sizes;	/* ledma SBus burst sizes	*/
+	char		pio_buffer;	/* init block in PIO space?	*/
+
+	unsigned short	busmaster_regval;
+
+	void (*init_ring)(struct net_device *);
+	void (*rx)(struct net_device *);
+	void (*tx)(struct net_device *);
+
+	char	       	       *name;
+	__u32			init_block_dvma;
+	struct net_device      *dev;		  /* Backpointer	*/
+	struct lance_private   *next_module;
+	struct sbus_dev	       *sdev;
+	struct timer_list       multicast_timer;
 };
 
 #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
 			lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\
 			lp->tx_old - lp->tx_new-1)
 
-/* On the sparc, the lance control ports are memory mapped */
-struct lance_regs {
-	unsigned short rdp;			/* register data port */
-	unsigned short rap;			/* register address port */
-};
+/* Lance registers. */
+#define RDP		0x00UL		/* register data port		*/
+#define RAP		0x02UL		/* register address port	*/
+#define LANCE_REG_SIZE	0x04UL
+
+#define STOP_LANCE(__lp) \
+do {	unsigned long __base = (__lp)->lregs; \
+	sbus_writew(LE_CSR0,	__base + RAP); \
+	sbus_writew(LE_C0_STOP,	__base + RDP); \
+} while (0)
 
 int sparc_lance_debug = 2;
 
@@ -275,7 +286,7 @@
 /* On the Sun4m we have to instruct the ledma to provide them    */
 /* Even worse, on scsi/ether SBUS cards, the init block and the
  * transmit/receive buffers are addresses as offsets from absolute
- * zero on the lebuffer PIO area. -davem
+ * zero on the lebuffer PIO area. -DaveM
  */
 
 #define LANCE_ADDR(x) ((long)(x) & ~0xff000000)
@@ -285,46 +296,36 @@
 #endif
 
 /* Load the CSR registers */
-static void load_csrs (struct lance_private *lp)
+static void load_csrs(struct lance_private *lp)
 {
-	volatile struct lance_regs *ll = lp->ll;
-	__u32 ib_dvma = lp->init_block_dvma;
-	int leptr;
+	u32 leptr;
 
-	/* This is right now because when we are using a PIO buffered
-	 * init block, init_block_dvma is set to zero. -DaveM
-	 */
-	leptr = LANCE_ADDR (ib_dvma);
-
-	ll->rap = LE_CSR1;
-	ll->rdp = (leptr & 0xFFFF);
-	ll->rap = LE_CSR2;
-	ll->rdp = leptr >> 16;
-	ll->rap = LE_CSR3;
-	ll->rdp = lp->busmaster_regval;
+	if (lp->pio_buffer)
+		leptr = 0;
+	else
+		leptr = LANCE_ADDR(lp->init_block_dvma);
+
+	sbus_writew(LE_CSR1,		  lp->lregs + RAP);
+	sbus_writew(leptr & 0xffff,	  lp->lregs + RDP);
+	sbus_writew(LE_CSR2,		  lp->lregs + RAP);
+	sbus_writew(leptr >> 16,	  lp->lregs + RDP);
+	sbus_writew(LE_CSR3,		  lp->lregs + RAP);
+	sbus_writew(lp->busmaster_regval, lp->lregs + RDP);
 
 	/* Point back to csr0 */
-	ll->rap = LE_CSR0;
+	sbus_writew(LE_CSR0, lp->lregs + RAP);
 }
 
-#define ZERO 0
-
 /* Setup the Lance Rx and Tx rings */
 /* Sets dev->tbusy */
-static void lance_init_ring (struct net_device *dev)
+static void lance_init_ring_dvma(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
-	__u32 ib_dvma = lp->init_block_dvma;
-	__u32 aib; /* for LANCE_ADDR computations */
-	int leptr;
+	__u32 aib = lp->init_block_dvma;
+	__u32 leptr;
 	int i;
     
-	/* This is right now because when we are using a PIO buffered
-	 * init block, init_block_dvma is set to zero. -DaveM
-	 */
-	aib = ib_dvma;
-
 	/* Lock out other processes while setting up hardware */
 	dev->tbusy = 1;
 	lp->rx_new = lp->tx_new = 0;
@@ -332,7 +333,6 @@
 
 	/* Copy the ethernet address to the lance init block
 	 * Note that on the sparc you need to swap the ethernet address.
-	 * Note also we want the CPU ptr of the init_block here.
 	 */
 	ib->phys_addr [0] = dev->dev_addr [1];
 	ib->phys_addr [1] = dev->dev_addr [0];
@@ -341,9 +341,6 @@
 	ib->phys_addr [4] = dev->dev_addr [5];
 	ib->phys_addr [5] = dev->dev_addr [4];
 
-	if (ZERO)
-		printk ("TX rings:\n");
-    
 	/* Setup the Tx ring entries */
 	for (i = 0; i <= TX_RING_SIZE; i++) {
 		leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i));
@@ -352,13 +349,9 @@
 		ib->btx_ring [i].tmd1_bits = 0;
 		ib->btx_ring [i].length    = 0xf000; /* The ones required by tmd2 */
 		ib->btx_ring [i].misc      = 0;
-		if (i < 3)
-			if (ZERO) printk ("%d: 0x%8.8x\n", i, leptr);
 	}
 
 	/* Setup the Rx ring entries */
-	if (ZERO)
-		printk ("RX rings:\n");
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		leptr = LANCE_ADDR(aib + libbuff_offset(rx_buf, i));
 
@@ -367,8 +360,6 @@
 		ib->brx_ring [i].rmd1_bits = LE_R1_OWN;
 		ib->brx_ring [i].length    = -RX_BUFF_SIZE | 0xf000;
 		ib->brx_ring [i].mblength  = 0;
-		if (i < 3 && ZERO)
-			printk ("%d: 0x%8.8x\n", i, leptr);
 	}
 
 	/* Setup the initialization block */
@@ -377,98 +368,155 @@
 	leptr = LANCE_ADDR(aib + libdesc_offset(brx_ring, 0));
 	ib->rx_len = (LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16);
 	ib->rx_ptr = leptr;
-	if (ZERO)
-		printk ("RX ptr: %8.8x\n", leptr);
     
 	/* Setup tx descriptor pointer */
 	leptr = LANCE_ADDR(aib + libdesc_offset(btx_ring, 0));
 	ib->tx_len = (LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16);
 	ib->tx_ptr = leptr;
-	if (ZERO)
-		printk ("TX ptr: %8.8x\n", leptr);
 }
 
-static int init_restart_lance (struct lance_private *lp)
+static void lance_init_ring_pio(struct net_device *dev)
 {
-	volatile struct lance_regs *ll = lp->ll;
+	struct lance_private *lp = (struct lance_private *) dev->priv;
+	volatile struct lance_init_block *ib = lp->init_block;
+	u32 leptr;
 	int i;
+    
+	/* Lock out other processes while setting up hardware */
+	dev->tbusy = 1;
+	lp->rx_new = lp->tx_new = 0;
+	lp->rx_old = lp->tx_old = 0;
 
-	if (lp->ledma) {
-		struct sparc_dma_registers *dregs = lp->ledma->regs;
-		unsigned long creg;
-
-		if (!(dregs->cond_reg & DMA_HNDL_ERROR)) {
-			/* E-Cache draining */
-			while (dregs->cond_reg & DMA_FIFO_ISDRAIN)
-				barrier();
-		}
+	/* Copy the ethernet address to the lance init block
+	 * Note that on the sparc you need to swap the ethernet address.
+	 */
+	sbus_writeb(dev->dev_addr[1], &ib->phys_addr[0]);
+	sbus_writeb(dev->dev_addr[0], &ib->phys_addr[1]);
+	sbus_writeb(dev->dev_addr[3], &ib->phys_addr[2]);
+	sbus_writeb(dev->dev_addr[2], &ib->phys_addr[3]);
+	sbus_writeb(dev->dev_addr[5], &ib->phys_addr[4]);
+	sbus_writeb(dev->dev_addr[4], &ib->phys_addr[5]);
 
-		creg = dregs->cond_reg;
-		if (lp->burst_sizes & DMA_BURST32)
-			creg |= DMA_E_BURST8;
-		else
-			creg &= ~DMA_E_BURST8;
+	/* Setup the Tx ring entries */
+	for (i = 0; i <= TX_RING_SIZE; i++) {
+		leptr = libbuff_offset(tx_buf, i);
+		sbus_writew(leptr,	&ib->btx_ring [i].tmd0);
+		sbus_writeb(leptr >> 16,&ib->btx_ring [i].tmd1_hadr);
+		sbus_writeb(0,		&ib->btx_ring [i].tmd1_bits);
+
+		/* The ones required by tmd2 */
+		sbus_writew(0xf000,	&ib->btx_ring [i].length);
+		sbus_writew(0,		&ib->btx_ring [i].misc);
+	}
 
-		creg |= (DMA_DSBL_RD_DRN | DMA_DSBL_WR_INV | DMA_FIFO_INV);
+	/* Setup the Rx ring entries */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		leptr = libbuff_offset(rx_buf, i);
 
-		if (lp->tpe)
-			creg |= DMA_EN_ENETAUI;
-		else
-			creg &= ~DMA_EN_ENETAUI;
-		udelay(20);
-		dregs->cond_reg = creg;
-		udelay(200);
+		sbus_writew(leptr,	&ib->brx_ring [i].rmd0);
+		sbus_writeb(leptr >> 16,&ib->brx_ring [i].rmd1_hadr);
+		sbus_writeb(LE_R1_OWN,	&ib->brx_ring [i].rmd1_bits);
+		sbus_writew(-RX_BUFF_SIZE|0xf000,
+			    &ib->brx_ring [i].length);
+		sbus_writew(0,		&ib->brx_ring [i].mblength);
 	}
 
-	ll->rap = LE_CSR0;
-	ll->rdp = LE_C0_INIT;
+	/* Setup the initialization block */
+    
+	/* Setup rx descriptor pointer */
+	leptr = libdesc_offset(brx_ring, 0);
+	sbus_writew((LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16),
+		    &ib->rx_len);
+	sbus_writew(leptr, &ib->rx_ptr);
+    
+	/* Setup tx descriptor pointer */
+	leptr = libdesc_offset(btx_ring, 0);
+	sbus_writew((LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16),
+		    &ib->tx_len);
+	sbus_writew(leptr, &ib->tx_ptr);
+}
+
+static void init_restart_ledma(struct lance_private *lp)
+{
+	u32 csr = sbus_readl(lp->dregs + DMA_CSR);
+
+	if (!(csr & DMA_HNDL_ERROR)) {
+		/* E-Cache draining */
+		while (sbus_readl(lp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN)
+			barrier();
+	}
+
+	csr = sbus_readl(lp->dregs + DMA_CSR);
+	csr &= ~DMA_E_BURSTS;
+	if (lp->burst_sizes & DMA_BURST32)
+		csr |= DMA_E_BURST32;
+	else
+		csr |= DMA_E_BURST16;
+
+	csr |= (DMA_DSBL_RD_DRN | DMA_DSBL_WR_INV | DMA_FIFO_INV);
+
+	if (lp->tpe)
+		csr |= DMA_EN_ENETAUI;
+	else
+		csr &= ~DMA_EN_ENETAUI;
+	udelay(20);
+	sbus_writel(csr, lp->dregs + DMA_CSR);
+	udelay(200);
+}
+
+static int init_restart_lance(struct lance_private *lp)
+{
+	u16 regval = 0;
+	int i;
+
+	if (lp->dregs)
+		init_restart_ledma(lp);
+
+	sbus_writew(LE_CSR0,	lp->lregs + RAP);
+	sbus_writew(LE_C0_INIT,	lp->lregs + RDP);
 
 	/* Wait for the lance to complete initialization */
-	for (i = 0; (i < 100) && !(ll->rdp & (LE_C0_ERR | LE_C0_IDON)); i++)
+	for (i = 0; i < 100; i++) {
+		regval = sbus_readw(lp->lregs + RDP);
+
+		if (regval & (LE_C0_ERR | LE_C0_IDON))
+			break;
 		barrier();
-	if ((i == 100) || (ll->rdp & LE_C0_ERR)) {
-		printk ("LANCE unopened after %d ticks, csr0=%4.4x.\n", i, ll->rdp);
-		if (lp->ledma)
-			printk ("dcsr=%8.8x\n",
-				(unsigned int) lp->ledma->regs->cond_reg);
+	}
+	if (i == 100 || (regval & LE_C0_ERR)) {
+		printk(KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n",
+		       i, regval);
+		if (lp->dregs)
+			printk("dcsr=%8.8x\n", sbus_readl(lp->dregs + DMA_CSR));
 		return -1;
 	}
 
 	/* Clear IDON by writing a "1", enable interrupts and start lance */
-	ll->rdp = LE_C0_IDON;
-	ll->rdp = LE_C0_INEA | LE_C0_STRT;
+	sbus_writew(LE_C0_IDON,			lp->lregs + RDP);
+	sbus_writew(LE_C0_INEA | LE_C0_STRT,	lp->lregs + RDP);
 
-	if (lp->ledma)
-		lp->ledma->regs->cond_reg |= DMA_INT_ENAB;
+	if (lp->dregs) {
+		u32 csr = sbus_readl(lp->dregs + DMA_CSR);
+
+		csr |= DMA_INT_ENAB;
+		sbus_writel(csr, lp->dregs + DMA_CSR);
+	}
 
 	return 0;
 }
 
-static int lance_rx (struct net_device *dev)
+static void lance_rx_dvma(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
 	volatile struct lance_rx_desc *rd;
-	unsigned char bits;
-	int len;
+	u8 bits;
+	int len, entry = lp->rx_new;
 	struct sk_buff *skb;
 
-#ifdef TEST_HITS
-	printk ("[");
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		if (i == lp->rx_new)
-			printk ("%s",
-				ib->brx_ring [i].rmd1_bits & LE_R1_OWN ? "_" : "X");
-		else
-			printk ("%s",
-				ib->brx_ring [i].rmd1_bits & LE_R1_OWN ? "." : "1");
-	}
-	printk ("]");
-#endif
-    
-	for (rd = &ib->brx_ring [lp->rx_new];
+	for (rd = &ib->brx_ring [entry];
 	     !((bits = rd->rmd1_bits) & LE_R1_OWN);
-	     rd = &ib->brx_ring [lp->rx_new]) {
+	     rd = &ib->brx_ring [entry]) {
 
 		/* We got an incomplete frame? */
 		if ((bits & LE_R1_POK) != LE_R1_POK) {
@@ -485,58 +533,57 @@
 			if (bits & LE_R1_EOP) lp->stats.rx_errors++;
 		} else {
 			len = (rd->mblength & 0xfff) - 4;
-			skb = dev_alloc_skb (len+2);
+			skb = dev_alloc_skb(len + 2);
 
-			if (skb == 0) {
-				printk ("%s: Memory squeeze, deferring packet.\n",
-					dev->name);
+			if (skb == NULL) {
+				printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n",
+				       dev->name);
 				lp->stats.rx_dropped++;
 				rd->mblength = 0;
 				rd->rmd1_bits = LE_R1_OWN;
-				lp->rx_new = (lp->rx_new + 1) & RX_RING_MOD_MASK;
-				return 0;
+				lp->rx_new = RX_NEXT(entry);
+				return;
 			}
 	    
 			lp->stats.rx_bytes += len;
 
 			skb->dev = dev;
-			skb_reserve (skb, 2);		/* 16 byte align */
-			skb_put (skb, len);		/* make room */
+			skb_reserve(skb, 2);		/* 16 byte align */
+			skb_put(skb, len);		/* make room */
 			eth_copy_and_sum(skb,
-					 (unsigned char *)&(ib->rx_buf [lp->rx_new][0]),
+					 (unsigned char *)&(ib->rx_buf [entry][0]),
 					 len, 0);
-			skb->protocol = eth_type_trans (skb, dev);
-			netif_rx (skb);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
 			lp->stats.rx_packets++;
 		}
 
 		/* Return the packet to the pool */
 		rd->mblength = 0;
 		rd->rmd1_bits = LE_R1_OWN;
-		lp->rx_new = (lp->rx_new + 1) & RX_RING_MOD_MASK;
+		entry = RX_NEXT(entry);
 	}
-	return 0;
+
+	lp->rx_new = entry;
 }
 
-static int lance_tx (struct net_device *dev)
+static void lance_tx_dvma(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
-	volatile struct lance_regs *ll = lp->ll;
-	volatile struct lance_tx_desc *td;
 	int i, j;
-	int status;
 
 	j = lp->tx_old;
 	for (i = j; i != lp->tx_new; i = j) {
-		td = &ib->btx_ring [i];
+		volatile struct lance_tx_desc *td = &ib->btx_ring [i];
+		u8 bits = td->tmd1_bits;
 
 		/* If we hit a packet not owned by us, stop */
-		if (td->tmd1_bits & LE_T1_OWN)
+		if (bits & LE_T1_OWN)
 			break;
 		
-		if (td->tmd1_bits & LE_T1_ERR) {
-			status = td->misc;
+		if (bits & LE_T1_ERR) {
+			u16 status = td->misc;
 	    
 			lp->stats.tx_errors++;
 			if (status & LE_T3_RTY)  lp->stats.tx_aborted_errors++;
@@ -546,15 +593,13 @@
 				lp->stats.tx_carrier_errors++;
 				if (lp->auto_select) {
 					lp->tpe = 1 - lp->tpe;
-					printk("%s: Carrier Lost, trying %s\n",
+					printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n",
 					       dev->name, lp->tpe?"TPE":"AUI");
-					/* Stop the lance */
-					ll->rap = LE_CSR0;
-					ll->rdp = LE_C0_STOP;
-					lance_init_ring (dev);
-					load_csrs (lp);
-					init_restart_lance (lp);
-					return 0;
+					STOP_LANCE(lp);
+					lp->init_ring(dev);
+					load_csrs(lp);
+					init_restart_lance(lp);
+					return;
 				}
 			}
 
@@ -564,72 +609,233 @@
 			if (status & (LE_T3_BUF|LE_T3_UFL)) {
 				lp->stats.tx_fifo_errors++;
 
-				printk ("%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
-					dev->name);
-				/* Stop the lance */
-				ll->rap = LE_CSR0;
-				ll->rdp = LE_C0_STOP;
-				lance_init_ring (dev);
-				load_csrs (lp);
-				init_restart_lance (lp);
-				return 0;
+				printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+				       dev->name);
+				STOP_LANCE(lp);
+				lp->init_ring(dev);
+				load_csrs(lp);
+				init_restart_lance(lp);
+				return;
 			}
-		} else if ((td->tmd1_bits & LE_T1_POK) == LE_T1_POK) {
+		} else if ((bits & LE_T1_POK) == LE_T1_POK) {
 			/*
 			 * So we don't count the packet more than once.
 			 */
-			td->tmd1_bits &= ~(LE_T1_POK);
+			td->tmd1_bits = bits & ~(LE_T1_POK);
 
 			/* One collision before packet was sent. */
-			if (td->tmd1_bits & LE_T1_EONE)
+			if (bits & LE_T1_EONE)
 				lp->stats.collisions++;
 
 			/* More than one collision, be optimistic. */
-			if (td->tmd1_bits & LE_T1_EMORE)
+			if (bits & LE_T1_EMORE)
 				lp->stats.collisions += 2;
 
 			lp->stats.tx_packets++;
 		}
 	
-		j = (j + 1) & TX_RING_MOD_MASK;
+		j = TX_NEXT(j);
 	}
 	lp->tx_old = j;
-	return 0;
 }
 
-static void lance_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+static void lance_piocopy_to_skb(struct sk_buff *skb, volatile void *piobuf, int len)
+{
+	u16 *p16 = (u16 *) skb->data;
+	u32 *p32;
+	u8 *p8;
+	unsigned long pbuf = (unsigned long) piobuf;
+
+	/* We know here that both src and dest are on a 16bit boundry. */
+	*p16++ = sbus_readw(pbuf);
+	p32 = (u32 *) p16;
+	pbuf += 2;
+	len -= 2;
+
+	while (len >= 4) {
+		*p32++ = sbus_readl(pbuf);
+		pbuf += 4;
+		len -= 4;
+	}
+	p8 = (u8 *) p32;
+	if (len >= 2) {
+		p16 = (u16 *) p32;
+		*p16++ = sbus_readw(pbuf);
+		pbuf += 2;
+		len -= 2;
+		p8 = (u8 *) p16;
+	}
+	if (len >= 1)
+		*p8 = sbus_readb(pbuf);
+}
+
+static void lance_rx_pio(struct net_device *dev)
+{
+	struct lance_private *lp = (struct lance_private *) dev->priv;
+	volatile struct lance_init_block *ib = lp->init_block;
+	volatile struct lance_rx_desc *rd;
+	unsigned char bits;
+	int len, entry;
+	struct sk_buff *skb;
+
+	entry = lp->rx_new;
+	for (rd = &ib->brx_ring [entry];
+	     !((bits = sbus_readb(&rd->rmd1_bits)) & LE_R1_OWN);
+	     rd = &ib->brx_ring [entry]) {
+
+		/* We got an incomplete frame? */
+		if ((bits & LE_R1_POK) != LE_R1_POK) {
+			lp->stats.rx_over_errors++;
+			lp->stats.rx_errors++;
+		} else if (bits & LE_R1_ERR) {
+			/* Count only the end frame as a rx error,
+			 * not the beginning
+			 */
+			if (bits & LE_R1_BUF) lp->stats.rx_fifo_errors++;
+			if (bits & LE_R1_CRC) lp->stats.rx_crc_errors++;
+			if (bits & LE_R1_OFL) lp->stats.rx_over_errors++;
+			if (bits & LE_R1_FRA) lp->stats.rx_frame_errors++;
+			if (bits & LE_R1_EOP) lp->stats.rx_errors++;
+		} else {
+			len = (sbus_readw(&rd->mblength) & 0xfff) - 4;
+			skb = dev_alloc_skb(len + 2);
+
+			if (skb == NULL) {
+				printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n",
+				       dev->name);
+				lp->stats.rx_dropped++;
+				sbus_writew(0, &rd->mblength);
+				sbus_writeb(LE_R1_OWN, &rd->rmd1_bits);
+				lp->rx_new = RX_NEXT(entry);
+				return;
+			}
+	    
+			lp->stats.rx_bytes += len;
+
+			skb->dev = dev;
+			skb_reserve (skb, 2);		/* 16 byte align */
+			skb_put(skb, len);		/* make room */
+			lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			lp->stats.rx_packets++;
+		}
+
+		/* Return the packet to the pool */
+		sbus_writew(0, &rd->mblength);
+		sbus_writeb(LE_R1_OWN, &rd->rmd1_bits);
+		entry = RX_NEXT(entry);
+	}
+
+	lp->rx_new = entry;
+}
+
+static void lance_tx_pio(struct net_device *dev)
+{
+	struct lance_private *lp = (struct lance_private *) dev->priv;
+	volatile struct lance_init_block *ib = lp->init_block;
+	int i, j;
+
+	j = lp->tx_old;
+	for (i = j; i != lp->tx_new; i = j) {
+		volatile struct lance_tx_desc *td = &ib->btx_ring [i];
+		u8 bits = sbus_readb(&td->tmd1_bits);
+
+		/* If we hit a packet not owned by us, stop */
+		if (bits & LE_T1_OWN)
+			break;
+		
+		if (bits & LE_T1_ERR) {
+			u16 status = sbus_readw(&td->misc);
+	    
+			lp->stats.tx_errors++;
+			if (status & LE_T3_RTY)  lp->stats.tx_aborted_errors++;
+			if (status & LE_T3_LCOL) lp->stats.tx_window_errors++;
+
+			if (status & LE_T3_CLOS) {
+				lp->stats.tx_carrier_errors++;
+				if (lp->auto_select) {
+					lp->tpe = 1 - lp->tpe;
+					printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n",
+					       dev->name, lp->tpe?"TPE":"AUI");
+					STOP_LANCE(lp);
+					lp->init_ring(dev);
+					load_csrs(lp);
+					init_restart_lance(lp);
+					return;
+				}
+			}
+
+			/* Buffer errors and underflows turn off the
+			 * transmitter, restart the adapter.
+			 */
+			if (status & (LE_T3_BUF|LE_T3_UFL)) {
+				lp->stats.tx_fifo_errors++;
+
+				printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n",
+				       dev->name);
+				STOP_LANCE(lp);
+				lp->init_ring(dev);
+				load_csrs(lp);
+				init_restart_lance(lp);
+				return;
+			}
+		} else if ((bits & LE_T1_POK) == LE_T1_POK) {
+			/*
+			 * So we don't count the packet more than once.
+			 */
+			sbus_writeb(bits & ~(LE_T1_POK), &td->tmd1_bits);
+
+			/* One collision before packet was sent. */
+			if (bits & LE_T1_EONE)
+				lp->stats.collisions++;
+
+			/* More than one collision, be optimistic. */
+			if (bits & LE_T1_EMORE)
+				lp->stats.collisions += 2;
+
+			lp->stats.tx_packets++;
+		}
+	
+		j = TX_NEXT(j);
+	}
+	lp->tx_old = j;
+}
+
+static void lance_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct net_device *dev = (struct net_device *)dev_id;
 	struct lance_private *lp = (struct lance_private *)dev->priv;
-	volatile struct lance_regs *ll = lp->ll;
 	int csr0;
     
 	if (dev->interrupt)
-		printk ("%s: again", dev->name);
+		printk(KERN_ERR "%s: again", dev->name);
     
 	dev->interrupt = 1;
 
-	ll->rap = LE_CSR0;
-	csr0 = ll->rdp;
+	sbus_writew(LE_CSR0, lp->lregs + RAP);
+	csr0 = sbus_readw(lp->lregs + RDP);
 
 	/* Acknowledge all the interrupt sources ASAP */
-	ll->rdp = csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT);
+	sbus_writew(csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT),
+		    lp->lregs + RDP);
     
-	if ((csr0 & LE_C0_ERR)) {
+	if ((csr0 & LE_C0_ERR) != 0) {
 		/* Clear the error condition */
-		ll->rdp = LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
-			  LE_C0_CERR | LE_C0_MERR;
+		sbus_writew((LE_C0_BABL | LE_C0_ERR | LE_C0_MISS |
+			     LE_C0_CERR | LE_C0_MERR),
+			    lp->lregs + RDP);
 	}
     
 	if (csr0 & LE_C0_RINT)
-		lance_rx (dev);
+		lp->rx(dev);
     
 	if (csr0 & LE_C0_TINT)
-		lance_tx (dev);
+		lp->tx(dev);
     
-	if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) {
+	if ((TX_BUFFS_AVAIL > 0) && dev->tbusy) {
 		dev->tbusy = 0;
-		mark_bh (NET_BH);
+		mark_bh(NET_BH);
 	}
 
 	if (csr0 & LE_C0_BABL)
@@ -639,103 +845,122 @@
 		lp->stats.rx_errors++;
 
 	if (csr0 & LE_C0_MERR) {
-		struct sparc_dma_registers *dregs = lp->ledma->regs;
-		unsigned long tst = (unsigned long)dregs->st_addr;
+		if (lp->dregs) {
+			u32 addr = sbus_readl(lp->dregs + DMA_ADDR);
+
+			printk(KERN_ERR "%s: Memory error, status %04x, addr %06x\n",
+			       dev->name, csr0, addr & 0xffffff);
+		} else {
+			printk(KERN_ERR "%s: Memory error, status %04x\n",
+			       dev->name, csr0);
+		}
 
-		printk ("%s: Memory error, status %04x, addr %06lx\n",
-			dev->name, csr0, tst & 0xffffff);
+		sbus_writew(LE_C0_STOP, lp->lregs + RDP);
 
-		ll->rdp = LE_C0_STOP;
+		if (lp->dregs) {
+			u32 dma_csr = sbus_readl(lp->dregs + DMA_CSR);
 
-		if (lp->ledma)
-			lp->ledma->regs->cond_reg |= DMA_FIFO_INV;
+			dma_csr |= DMA_FIFO_INV;
+			sbus_writel(dma_csr, lp->dregs + DMA_CSR);
+		}
 
-		lance_init_ring (dev);
-		load_csrs (lp);
-		init_restart_lance (lp);
+		lp->init_ring(dev);
+		load_csrs(lp);
+		init_restart_lance(lp);
 		dev->tbusy = 0;
 	}
 
-	ll->rdp = LE_C0_INEA;
+	sbus_writew(LE_C0_INEA, lp->lregs + RDP);
 	dev->interrupt = 0;
 }
 
+/* Build a fake network packet and send it to ourselves. */
+static void build_fake_packet(struct lance_private *lp)
+{
+	struct net_device *dev = lp->dev;
+	volatile struct lance_init_block *ib = lp->init_block;
+	u16 *packet;
+	struct ethhdr *eth;
+	int i, entry;
+
+	entry = lp->tx_new & TX_RING_MOD_MASK;
+	packet = (u16 *) &(ib->tx_buf[entry][0]);
+	eth = (struct ethhdr *) packet;
+	if (lp->pio_buffer) {
+		for (i = 0; i < (ETH_ZLEN / sizeof(u16)); i++)
+			sbus_writew(0, &packet[i]);
+		for (i = 0; i < 6; i++) {
+			sbus_writeb(dev->dev_addr[i], &eth->h_dest[i]);
+			sbus_writeb(dev->dev_addr[i], &eth->h_source[i]);
+		}
+		sbus_writew((-ETH_ZLEN) | 0xf000, &ib->btx_ring[entry].length);
+		sbus_writew(0, &ib->btx_ring[entry].misc);
+		sbus_writeb(LE_T1_POK|LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits);
+	} else {
+		memset(packet, 0, ETH_ZLEN);
+		for (i = 0; i < 6; i++) {
+			eth->h_dest[i] = dev->dev_addr[i];
+			eth->h_source[i] = dev->dev_addr[i];
+		}
+		ib->btx_ring[entry].length = (-ETH_ZLEN) | 0xf000;
+		ib->btx_ring[entry].misc = 0;
+		ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
+	}
+	lp->tx_new = TX_NEXT(entry);
+}
+
 struct net_device *last_dev = 0;
 
-static int lance_open (struct net_device *dev)
+static int lance_open(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *)dev->priv;
-	volatile struct lance_regs *ll = lp->ll;
 	volatile struct lance_init_block *ib = lp->init_block;
 	int status = 0;
 
 	last_dev = dev;
 
-	if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
-			 lancestr, (void *) dev)) {
-		printk ("Lance: Can't get irq %s\n", __irq_itoa(dev->irq));
+	if (request_irq(dev->irq, &lance_interrupt, SA_SHIRQ,
+			lancestr, (void *) dev)) {
+		printk(KERN_ERR "Lance: Can't get irq %s\n", __irq_itoa(dev->irq));
 		return -EAGAIN;
 	}
 
-	/* Stop the Lance */
-	ll->rap = LE_CSR0;
-	ll->rdp = LE_C0_STOP;
+	STOP_LANCE(lp);
 
 	/* On the 4m, setup the ledma to provide the upper bits for buffers */
-	if (lp->ledma)
-		lp->ledma->regs->dma_test = ((__u32) lp->init_block_dvma) & 0xff000000;
+	if (lp->dregs) {
+		u32 regval = lp->init_block_dvma & 0xff000000;
 
-	/* Set mode and clear multicast filter only at device open,
-	   so that lance_init_ring() called at any error will not
-	   forget multicast filters.
+		sbus_writel(regval, lp->dregs + DMA_TEST);
+	}
 
-	   BTW it is common bug in all lance drivers! --ANK
+	/* Set mode and clear multicast filter only at device open,
+	 * so that lance_init_ring() called at any error will not
+	 * forget multicast filters.
+	 *
+	 * BTW it is common bug in all lance drivers! --ANK
 	 */
-	ib->mode = 0;
-	ib->filter [0] = 0;
-	ib->filter [1] = 0;
+	if (lp->pio_buffer) {
+		sbus_writew(0, &ib->mode);
+		sbus_writel(0, &ib->filter[0]);
+		sbus_writel(0, &ib->filter[1]);
+	} else {
+		ib->mode = 0;
+		ib->filter [0] = 0;
+		ib->filter [1] = 0;
+	}
 
-	lance_init_ring (dev);
-	load_csrs (lp);
+	lp->init_ring(dev);
+	load_csrs(lp);
 
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
 
-	status = init_restart_lance (lp);
-#if 0
-	/* To emulate SunOS, we add a route to the local network */
-	rt_add (RTF_UP,
-		dev->pa_addr & ip_get_mask (dev->pa_addr),
-		ip_get_mask (dev->pa_addr),
-		0, dev, dev->mtu, 0, 0);
-#endif
+	status = init_restart_lance(lp);
 	if (!status && lp->auto_select) {
-		/*
-		 * Build a fake network packet and send it to ourselfs.
-		 */
-		volatile struct lance_init_block *ib = lp->init_block;
-		volatile unsigned long flush;
-		unsigned char packet[ETH_ZLEN];
-		struct ethhdr *eth = (struct ethhdr *)packet;
-		int i, entry;
-
-		memset(packet, 0, ETH_ZLEN);
-		for (i = 0; i < 6; i++) {
-			eth->h_dest[i] = dev->dev_addr[i];
-			eth->h_source[i] = dev->dev_addr[i];
-		}
-
-		entry = lp->tx_new & TX_RING_MOD_MASK;
-		ib->btx_ring[entry].length = (-ETH_ZLEN) | 0xf000;
-		ib->btx_ring[entry].misc = 0;
-
-		memcpy((char *)&ib->tx_buf[entry][0], packet, ETH_ZLEN);
-		ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
-		lp->tx_new = (lp->tx_new + 1) & TX_RING_MOD_MASK;
-
-		ll->rdp = LE_C0_INEA | LE_C0_TDMD;
-		flush = ll->rdp;
+		build_fake_packet(lp);
+		sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);
 	}
 
 	if (!status)
@@ -744,74 +969,162 @@
 	return status;
 }
 
-static int lance_close (struct net_device *dev)
+static int lance_close(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
-	volatile struct lance_regs *ll = lp->ll;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 	del_timer(&lp->multicast_timer);
 
-	/* Stop the card */
-	ll->rap = LE_CSR0;
-	ll->rdp = LE_C0_STOP;
+	STOP_LANCE(lp);
 
-	free_irq (dev->irq, (void *) dev);
+	free_irq(dev->irq, (void *) dev);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
-static inline int lance_reset (struct net_device *dev)
+static int lance_reset(struct net_device *dev)
 {
-	struct lance_private *lp = (struct lance_private *)dev->priv;
-	volatile struct lance_regs *ll = lp->ll;
+	struct lance_private *lp = (struct lance_private *) dev->priv;
 	int status;
     
-	/* Stop the lance */
-	ll->rap = LE_CSR0;
-	ll->rdp = LE_C0_STOP;
+	STOP_LANCE(lp);
 
 	/* On the 4m, reset the dma too */
-	if (lp->ledma) {
-		printk ("resetting ledma\n");
-		lp->ledma->regs->cond_reg |= DMA_RST_ENET;
-		udelay (200);
-		lp->ledma->regs->cond_reg &= ~DMA_RST_ENET;
-		lp->ledma->regs->dma_test = ((__u32) lp->init_block_dvma) & 0xff000000;
+	if (lp->dregs) {
+		u32 csr, addr;
+
+		printk(KERN_ERR "resetting ledma\n");
+		csr = sbus_readl(lp->dregs + DMA_CSR);
+		sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);
+		udelay(200);
+		sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR);
+
+		addr = lp->init_block_dvma & 0xff000000;
+		sbus_writel(addr, lp->dregs + DMA_TEST);
 	}
-	lance_init_ring (dev);
-	load_csrs (lp);
+	lp->init_ring(dev);
+	load_csrs(lp);
 	dev->trans_start = jiffies;
 	dev->interrupt = 0;
 	dev->start = 1;
 	dev->tbusy = 0;
-	status = init_restart_lance (lp);
-#ifdef DEBUG_DRIVER
-	printk ("Lance restart=%d\n", status);
-#endif
+	status = init_restart_lance(lp);
 	return status;
 }
 
-static int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
+static void lance_piocopy_from_skb(volatile void *dest, char *src, int len)
 {
-	struct lance_private *lp = (struct lance_private *)dev->priv;
-	volatile struct lance_regs *ll = lp->ll;
+	unsigned long piobuf = (unsigned long) dest;
+	u32 *p32;
+	u16 *p16;
+	u8 *p8;
+
+	switch ((unsigned long)src & 0x3) {
+	case 0:
+		p32 = (u32 *) src;
+		while (len >= 4) {
+			sbus_writel(*p32, piobuf);
+			p32++;
+			piobuf += 4;
+			len -= 4;
+		}
+		src = (char *) p32;
+		break;
+	case 1:
+	case 3:
+		p8 = (u8 *) src;
+		while (len >= 4) {
+			u32 val;
+
+			val  = p8[0] << 24;
+			val |= p8[1] << 16;
+			val |= p8[2] << 8;
+			val |= p8[3];
+			sbus_writel(val, piobuf);
+			p8 += 4;
+			piobuf += 4;
+			len -= 4;
+		}
+		src = (char *) p8;
+		break;
+	case 2:
+		p16 = (u16 *) src;
+		while (len >= 4) {
+			u32 val = p16[0]<<16 | p16[1];
+			sbus_writel(val, piobuf);
+			p16 += 2;
+			piobuf += 4;
+			len -= 4;
+		}
+		src = (char *) p16;
+		break;
+	};
+	if (len >= 2) {
+		u16 val = src[0] << 8 | src[1];
+		sbus_writew(val, piobuf);
+		src += 2;
+		piobuf += 2;
+		len -= 2;
+	}
+	if (len >= 1)
+		sbus_writeb(src[0], piobuf);
+}
+
+static void lance_piozero(volatile void *dest, int len)
+{
+	unsigned long piobuf = (unsigned long) dest;
+
+	if (piobuf & 1) {
+		sbus_writeb(0, piobuf);
+		piobuf += 1;
+		len -= 1;
+		if (len == 0)
+			return;
+	}
+	if (len == 1) {
+		sbus_writeb(0, piobuf);
+		return;
+	}
+	if (piobuf & 2) {
+		sbus_writew(0, piobuf);
+		piobuf += 2;
+		len -= 2;
+		if (len == 0)
+			return;
+	}
+	while (len >= 4) {
+		sbus_writel(0, piobuf);
+		piobuf += 4;
+		len -= 4;
+	}
+	if (len >= 2) {
+		sbus_writew(0, piobuf);
+		piobuf += 2;
+		len -= 2;
+	}
+	if (len >= 1)
+		sbus_writeb(0, piobuf);
+}
+
+static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
-	volatile unsigned long flush;
 	unsigned long flags;
 	int entry, skblen, len;
 
-	if (test_and_set_bit (0, (void *) &dev->tbusy) != 0) {
+	if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
 		int tickssofar = jiffies - dev->trans_start;
 
 		if (tickssofar < 100)
 			return 1;
 
-		printk ("%s: transmit timed out, status %04x, reset\n",
-			dev->name, ll->rdp);
+		printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n",
+		       dev->name, sbus_readw(lp->lregs + RDP));
 		lp->stats.tx_errors++;
-		lance_reset (dev);
+		lance_reset(dev);
 
 		return 1;
 	}
@@ -830,38 +1143,43 @@
 	lp->stats.tx_bytes += len;
 
 	entry = lp->tx_new & TX_RING_MOD_MASK;
-	ib->btx_ring [entry].length = (-len) | 0xf000;
-	ib->btx_ring [entry].misc = 0;
-    
-	memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+	if (lp->pio_buffer) {
+		sbus_writew((-len) | 0xf000, &ib->btx_ring[entry].length);
+		sbus_writew(0, &ib->btx_ring[entry].misc);
+		lance_piocopy_from_skb(&ib->tx_buf[entry][0], skb->data, skblen);
+		if (len != skblen)
+			lance_piozero(&ib->tx_buf[entry][skblen], len - skblen);
+		sbus_writeb(LE_T1_POK | LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits);
+	} else {
+		ib->btx_ring [entry].length = (-len) | 0xf000;
+		ib->btx_ring [entry].misc = 0;
+		memcpy((char *)&ib->tx_buf [entry][0], skb->data, skblen);
+		if (len != skblen)
+			memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);
+		ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN);
+	}
 
-	/* Clear the slack of the packet, do I need this? */
-	/* For a firewall its a good idea - AC */
-	if (len != skblen)
-		memset ((char *) &ib->tx_buf [entry][skblen], 0, len - skblen);
-    
-	/* Now, give the packet to the lance */
-	ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN);
-	lp->tx_new = (lp->tx_new+1) & TX_RING_MOD_MASK;
+	lp->tx_new = TX_NEXT(entry);
 
 	/* Kick the lance: transmit now */
-	ll->rdp = LE_C0_INEA | LE_C0_TDMD;
+	sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP);
 	dev->trans_start = jiffies;
-	dev_kfree_skb (skb);
+	dev_kfree_skb(skb);
     
 	if (TX_BUFFS_AVAIL)
 		dev->tbusy = 0;
 
 	/* Read back CSR to invalidate the E-Cache.
-	 * This is needed, because DMA_DSBL_WR_INV is set. */
-	if (lp->ledma)
-		flush = ll->rdp;
+	 * This is needed, because DMA_DSBL_WR_INV is set.
+	 */
+	if (lp->dregs)
+		sbus_readw(lp->lregs + RDP);
 
 	restore_flags(flags);
 	return 0;
 }
 
-static struct net_device_stats *lance_get_stats (struct net_device *dev)
+static struct net_device_stats *lance_get_stats(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 
@@ -869,25 +1187,35 @@
 }
 
 /* taken from the depca driver */
-static void lance_load_multicast (struct net_device *dev)
+static void lance_load_multicast(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
-	volatile u16 *mcast_table = (u16 *)&ib->filter;
-	struct dev_mc_list *dmi=dev->mc_list;
+	volatile u16 *mcast_table = (u16 *) &ib->filter;
+	struct dev_mc_list *dmi = dev->mc_list;
 	char *addrs;
 	int i, j, bit, byte;
 	u32 crc, poly = CRC_POLYNOMIAL_LE;
 	
 	/* set all multicast bits */
 	if (dev->flags & IFF_ALLMULTI) {
-		ib->filter [0] = 0xffffffff;
-		ib->filter [1] = 0xffffffff;
+		if (lp->pio_buffer) {
+			sbus_writel(0xffffffff, &ib->filter[0]);
+			sbus_writel(0xffffffff, &ib->filter[1]);
+		} else {
+			ib->filter [0] = 0xffffffff;
+			ib->filter [1] = 0xffffffff;
+		}
 		return;
 	}
 	/* clear the multicast filter */
-	ib->filter [0] = 0;
-	ib->filter [1] = 0;
+	if (lp->pio_buffer) {
+		sbus_writel(0, &ib->filter[0]);
+		sbus_writel(0, &ib->filter[1]);
+	} else {
+		ib->filter [0] = 0;
+		ib->filter [1] = 0;
+	}
 
 	/* Add addresses */
 	for (i = 0; i < dev->mc_count; i++) {
@@ -911,37 +1239,29 @@
 			}
 		}
 		crc = crc >> 26;
-		mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+		if (lp->pio_buffer) {
+			u16 tmp = sbus_readw(&mcast_table[crc>>4]);
+			tmp |= 1 << (crc & 0xf);
+			sbus_writew(tmp, &mcast_table[crc>>4]);
+		} else {
+			mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+		}
 	}
 }
 
-static void lance_set_multicast (struct net_device *dev)
+static void lance_set_multicast(struct net_device *dev)
 {
 	struct lance_private *lp = (struct lance_private *) dev->priv;
 	volatile struct lance_init_block *ib = lp->init_block;
-	volatile struct lance_regs *ll = lp->ll;
+	u16 mode;
 
 	if (!dev->start)
 		return;
 
-	if (dev->tbusy) {
+	if (test_and_set_bit(0, (void *)&dev->tbusy)) {
 		mod_timer(&lp->multicast_timer, jiffies + 2);
 		return;
 	}
-	/* This CANNOT be correct. Chip is running
-	   and dev->tbusy may change any moment.
-	   It is useless to set it.
-
-	   Generally, usage of dev->tbusy in this driver is completely
-	   wrong.
-
-	   I protected calls to this function
-	   with start_bh_atomic, so that set_multicast_list
-	   and hard_start_xmit are serialized now by top level. --ANK
-
-	   The same is true about a2065.
-	 */
-	set_bit (0, (void *) &dev->tbusy);
 
 	if (lp->tx_old != lp->tx_new) {
 		mod_timer(&lp->multicast_timer, jiffies + 4);
@@ -949,93 +1269,130 @@
 		return;
 	}
 
-	ll->rap = LE_CSR0;
-	ll->rdp = LE_C0_STOP;
-	lance_init_ring (dev);
+	STOP_LANCE(lp);
+	lp->init_ring(dev);
 
+	if (lp->pio_buffer)
+		mode = sbus_readw(&ib->mode);
+	else
+		mode = ib->mode;
 	if (dev->flags & IFF_PROMISC) {
-		ib->mode |= LE_MO_PROM;
+		mode |= LE_MO_PROM;
+		if (lp->pio_buffer)
+			sbus_writew(mode, &ib->mode);
+		else
+			ib->mode = mode;
 	} else {
-		ib->mode &= ~LE_MO_PROM;
-		lance_load_multicast (dev);
+		mode &= ~LE_MO_PROM;
+		if (lp->pio_buffer)
+			sbus_writew(mode, &ib->mode);
+		else
+			ib->mode = mode;
+		lance_load_multicast(dev);
 	}
-	load_csrs (lp);
-	init_restart_lance (lp);
+	load_csrs(lp);
+	init_restart_lance(lp);
 	dev->tbusy = 0;
 	mark_bh(NET_BH);
 }
 
-static int __init 
-sparc_lance_init (struct net_device *dev, struct linux_sbus_device *sdev,
-		  struct Linux_SBus_DMA *ledma,
-		  struct linux_sbus_device *lebuffer)
+static void lance_free_hwresources(struct lance_private *lp)
+{
+	if (lp->lregs)
+		sbus_iounmap(lp->lregs, LANCE_REG_SIZE);
+	if (lp->init_block != NULL) {
+		if (lp->pio_buffer) {
+			sbus_iounmap((unsigned long)lp->init_block,
+				     sizeof(struct lance_init_block));
+		} else {
+			sbus_free_consistant(lp->sdev,
+					     sizeof(struct lance_init_block),
+					     (void *)lp->init_block,
+					     lp->init_block_dvma);
+		}
+	}
+}
+
+static int __init sparc_lance_init(struct net_device *dev,
+				   struct sbus_dev *sdev,
+				   struct sbus_dma *ledma,
+				   struct sbus_dev *lebuffer)
 {
 	static unsigned version_printed = 0;
+	struct lance_private *lp = NULL;
 	int    i;
-	struct lance_private *lp;
-	volatile struct lance_regs *ll;
 
 	if (dev == NULL) {
 		dev = init_etherdev (0, sizeof (struct lance_private) + 8);
 	} else {
-		dev->priv = kmalloc (sizeof (struct lance_private) + 8,
-				     GFP_KERNEL);
+		dev->priv = kmalloc(sizeof (struct lance_private) + 8,
+				    GFP_KERNEL);
 		if (dev->priv == NULL)
 			return -ENOMEM;
 		memset(dev->priv, 0, sizeof (struct lance_private) + 8);
 	}
 	if (sparc_lance_debug && version_printed++ == 0)
-		printk (version);
+		printk (KERN_INFO "%s", version);
+
+	printk(KERN_INFO "%s: LANCE ", dev->name);
 
-	printk ("%s: LANCE ", dev->name);
-	/* Fill the dev fields */
-	dev->base_addr = (long) sdev;
+	/* Make certain the data structures used by the LANCE are aligned. */
+	dev->priv = (void *)(((unsigned long)dev->priv + 7) & ~7);
+	lp = (struct lance_private *) dev->priv;
 
 	/* Copy the IDPROM ethernet address to the device structure, later we
 	 * will copy the address in the device structure to the lance
 	 * initialization block.
 	 */
 	for (i = 0; i < 6; i++)
-		printk ("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i],
-			i == 5 ? ' ': ':');
+		printk("%2.2x%c", dev->dev_addr[i] = idprom->id_ethaddr[i],
+		       i == 5 ? ' ': ':');
 	printk("\n");
 
 	/* Get the IO region */
-	prom_apply_sbus_ranges (sdev->my_bus, &sdev->reg_addrs [0],
-				sdev->num_registers, sdev);
-	ll = sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0,
-			     sizeof (struct lance_regs), lancestr,
-			     sdev->reg_addrs[0].which_io, 0x0);
+	lp->lregs = sbus_ioremap(&sdev->resource[0], 0,
+				 LANCE_REG_SIZE, lancestr);
+	if (lp->lregs == 0UL) {
+		printk(KERN_ERR "%s: Cannot map SunLance registers.\n",
+		       dev->name);
+		goto fail;
+	}
 
-	/* Make certain the data structures used by the LANCE are aligned. */
-	dev->priv = (void *)(((unsigned long)dev->priv + 7) & ~7);
-	lp = (struct lance_private *) dev->priv;
-	lp->sbus = sdev->my_bus;
-	if (lebuffer){
-		prom_apply_sbus_ranges (lebuffer->my_bus,
-					&lebuffer->reg_addrs [0],
-					lebuffer->num_registers,
-					lebuffer);
-
-		lp->init_block = (void *)
-			sparc_alloc_io (lebuffer->reg_addrs [0].phys_addr, 0,
-				sizeof (struct lance_init_block), "lebuffer", 
-				lebuffer->reg_addrs [0].which_io, 0);
+	lp->sdev = sdev;
+	if (lebuffer) {
+		lp->init_block = (volatile struct lance_init_block *)
+			sbus_ioremap(&lebuffer->resource[0], 0,
+				     sizeof(struct lance_init_block), "lebuffer");
+		if (lp->init_block == NULL) {
+			printk(KERN_ERR "%s: Cannot map SunLance PIO buffer.\n",
+			       dev->name);
+			goto fail;
+		}
 		lp->init_block_dvma = 0;
-
 		lp->pio_buffer = 1;
+		lp->init_ring = lance_init_ring_pio;
+		lp->rx = lance_rx_pio;
+		lp->tx = lance_tx_pio;
 	} else {
-		lp->init_block = (void *)
-			sparc_dvma_malloc (sizeof (struct lance_init_block),
-					   lancedma, &lp->init_block_dvma);
+		lp->init_block = (volatile struct lance_init_block *)
+			sbus_alloc_consistant(sdev, sizeof(struct lance_init_block),
+					      &lp->init_block_dvma);
+		if (lp->init_block == NULL ||
+		    lp->init_block_dvma == 0) {
+			printk(KERN_ERR "%s: Cannot allocate consistant DMA memory.\n",
+			       dev->name);
+			goto fail;
+		}
 		lp->pio_buffer = 0;
+		lp->init_ring = lance_init_ring_dvma;
+		lp->rx = lance_rx_dvma;
+		lp->tx = lance_tx_dvma;
 	}
 	lp->busmaster_regval = prom_getintdefault(sdev->prom_node,
 						  "busmaster-regval",
 						  (LE_C3_BSWP | LE_C3_ACON |
 						   LE_C3_BCON));
 
-	lp->ll = ll;
 	lp->name = lancestr;
 	lp->ledma = ledma;
 
@@ -1043,24 +1400,25 @@
 	if (lp->ledma) {
 		char prop[6];
 		unsigned int sbmask;
+		u32 csr;
 
 		/* Find burst-size property for ledma */
-		lp->burst_sizes = prom_getintdefault(ledma->SBus_dev->prom_node,
+		lp->burst_sizes = prom_getintdefault(ledma->sdev->prom_node,
 						     "burst-sizes", 0);
 
 		/* ledma may be capable of fast bursts, but sbus may not. */
-		sbmask = prom_getintdefault(ledma->SBus_dev->my_bus->prom_node,
+		sbmask = prom_getintdefault(ledma->sdev->bus->prom_node,
 					    "burst-sizes", DMA_BURSTBITS);
 		lp->burst_sizes &= sbmask;
 
 		/* Get the cable-selection property */
 		memset(prop, 0, sizeof(prop));
-		prom_getstring(ledma->SBus_dev->prom_node, "cable-selection",
+		prom_getstring(ledma->sdev->prom_node, "cable-selection",
 			       prop, sizeof(prop));
 		if (prop[0] == 0) {
 			int topnd, nd;
 
-			printk("%s: using auto-carrier-detection.\n",
+			printk(KERN_INFO "%s: using auto-carrier-detection.\n",
 			       dev->name);
 
 			/* Is this found at /options .attributes in all
@@ -1080,9 +1438,9 @@
 				       sizeof(prop));
 
 			if (strcmp(prop, "true")) {
-				printk("%s: warning: overriding option "
+				printk(KERN_NOTICE "%s: warning: overriding option "
 				       "'tpe-link-test?'\n", dev->name);
-				printk("%s: warning: mail any problems "
+				printk(KERN_NOTICE "%s: warning: mail any problems "
 				       "to ecd@skynet.be\n", dev->name);
 				set_auxio(AUXIO_LINK_TEST, 0);
 			}
@@ -1097,17 +1455,21 @@
 			lp->tpe = 1;
 		}
 
+		lp->dregs = ledma->regs;
+
 		/* Reset ledma */
-		lp->ledma->regs->cond_reg |= DMA_RST_ENET;
-		udelay (200);
-		lp->ledma->regs->cond_reg &= ~DMA_RST_ENET;
-	}
+		csr = sbus_readl(lp->dregs + DMA_CSR);
+		sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);
+		udelay(200);
+		sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR);
+	} else
+		lp->dregs = 0;
 
 	/* This should never happen. */
 	if ((unsigned long)(lp->init_block->brx_ring) & 0x07) {
-		printk("%s: ERROR: Rx and Tx rings not on even boundary.\n",
+		printk(KERN_ERR "%s: ERROR: Rx and Tx rings not on even boundary.\n",
 		       dev->name);
-		return ENODEV;
+		goto fail;
 	}
 
 	lp->dev = dev;
@@ -1120,7 +1482,7 @@
 	dev->irq = sdev->irqs[0];
 
 	dev->dma = 0;
-	ether_setup (dev);
+	ether_setup(dev);
 
 	/* We cannot sleep if the chip is busy during a
 	 * multicast list update event, because such events
@@ -1138,18 +1500,23 @@
 	root_lance_dev = lp;
 #endif
 	return 0;
+
+fail:
+	if (lp != NULL)
+		lance_free_hwresources(lp);
+	return ENODEV;
 }
 
 /* On 4m, find the associated dma for the lance chip */
-static inline struct Linux_SBus_DMA *
-find_ledma (struct linux_sbus_device *dev)
+static inline struct sbus_dma *find_ledma(struct sbus_dev *sdev)
 {
-	struct Linux_SBus_DMA *p;
+	struct sbus_dma *p;
 
-	for_each_dvma(p)
-		if (p->SBus_dev == dev)
+	for_each_dvma(p) {
+		if (p->sdev == sdev)
 			return p;
-	return 0;
+	}
+	return NULL;
 }
 
 #ifdef CONFIG_SUN4
@@ -1159,16 +1526,16 @@
 /* Find all the lance cards on the system and initialize them */
 int __init sparc_lance_probe(void)
 {
-	static struct linux_sbus_device sdev;
+	static struct sbus_dev sdev;
 	static int called = 0;
 
-	if(called)
+	if (called)
 		return ENODEV;
 	called++;
 
 	if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) ||
 	    (idprom->id_machtype == (SM_SUN4|SM_4_470))) {
-		memset (&sdev, 0, sizeof(sdev));
+		memset(&sdev, 0, sizeof(sdev));
 		sdev.reg_addrs[0].phys_addr = sun4_eth_physaddr;
 		sdev.irqs[0] = 6;
 		return sparc_lance_init(NULL, &sdev, 0, 0);
@@ -1179,37 +1546,38 @@
 #else /* !CONFIG_SUN4 */
 
 /* Find all the lance cards on the system and initialize them */
-int __init sparc_lance_probe (void)
+int __init sparc_lance_probe(void)
 {
-	struct linux_sbus *bus;
-	struct linux_sbus_device *sdev = 0;
+	struct sbus_bus *bus;
+	struct sbus_dev *sdev = 0;
 	struct net_device *dev = NULL;
-	struct Linux_SBus_DMA *ledma = 0;
+	struct sbus_dma *ledma = 0;
 	static int called = 0;
 	int cards = 0, v;
 
-	if(called)
+	if (called)
 		return ENODEV;
 	called++;
 
 	for_each_sbus (bus) {
 		for_each_sbusdev (sdev, bus) {
-			if (cards) dev = NULL;
-			if (strcmp (sdev->prom_name, "le") == 0) {
+			if (cards)
+				dev = NULL;
+			if (strcmp(sdev->prom_name, "le") == 0) {
 				cards++;
 				if ((v = sparc_lance_init(dev, sdev, 0, 0)))
 					return v;
 				continue;
 			}
-			if (strcmp (sdev->prom_name, "ledma") == 0) {
+			if (strcmp(sdev->prom_name, "ledma") == 0) {
 				cards++;
-				ledma = find_ledma (sdev);
+				ledma = find_ledma(sdev);
 				if ((v = sparc_lance_init(dev, sdev->child,
 							  ledma, 0)))
 					return v;
 				continue;
 			}
-			if (strcmp (sdev->prom_name, "lebuffer") == 0){
+			if (strcmp(sdev->prom_name, "lebuffer") == 0){
 				cards++;
 				if ((v = sparc_lance_init(dev, sdev->child,
 							  0, sdev)))
@@ -1242,6 +1610,7 @@
 		lp = root_lance_dev->next_module;
 
 		unregister_netdev(root_lance_dev->dev);
+		lance_free_hwresources(root_lance_dev);
 		kfree(root_lance_dev->dev);
 		root_lance_dev = lp;
 	}

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