2008 version (for reference).
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/sunlance.c
blob: 68e4f660367d5e1795ffbef7a4fb2fe6e75339ac (plain) (tree)

































































                                                                              
  



                   

                                 



























                                                                              
                     


                                                                












                                                                        



























































































                                                                                 
 











                                                                    
 


                                                                           
 















                                                                            
 



                                       
 















                                                                          






























                                                                          




























                                                           
 




































                                                                                    
 



                                                                  
 











                                                                   
 







































                                                                        
 




                                                                 
 


















































































































                                                                                           
 

                                                  

                                                                           
                                                    
                                                                                   
                                              






























                                                                 
 

                                              
 















































                                                                                           
 
















































































                                                                                           
 

                                                  

































                                                                                
 

                                                           
 















































                                                                                           
 










                                       
                                                         
 
                                        

                                                    
 





                                                                  
 





                                                                  
 

                              
 

                              
 



















































































                                                                                     
                                                                
                                                  
                                                                       


























































                                                                              
 























































































































































                                                                                   
                                                                               






















                                                                                      
 


















                                                                       
 

















                                                                           
 












































































































                                                                                         
                                                           



                                                          


                                                                     

                                        
                                                  






























































                                                                                                    



                                                                             





                            

                                                                       



                                                        

                                                                          

                                                                            

                                                                       


                                                      


                                                                          
 

                                                            
 
                                                              


                                                  

                                                                           

                                                  





























                                                                                          
                                              


























                                                                       
                                              
















                                                       
                                                                    















                                                                
                                 
                                           
 

                                                          
                                                               


                                                                     



                       
                                            
 
                                                                         

                                             
                                   
 
                                   


                             
                                                    
 

                 
 


                                                                                                 
 
                                                          

                

                                                                
 

                                                                                          
 






                                                                                              








                                                                
                                   













                                                    


























                                                                         

 

                              
/* $Id: sunlance.c,v 1.112 2002/01/15 06:48:55 davem Exp $
 * lance.c: Linux/Sparc/Lance driver
 *
 *	Written 1995, 1996 by Miguel de Icaza
 * Sources:
 *	The Linux  depca driver
 *	The Linux  lance driver.
 *	The Linux  skeleton driver.
 *	The NetBSD Sparc/Lance driver.
 *	Theo de Raadt (deraadt@openbsd.org)
 *	NCR92C990 Lan Controller manual
 *
 * 1.4:
 *	Added support to run with a ledma on the Sun4m
 *
 * 1.5:
 *	Added multiple card detection.
 *
 *	 4/17/96: Burst sizes and tpe selection on sun4m by Eddie C. Dost
 *		  (ecd@skynet.be)
 *
 *	 5/15/96: auto carrier detection on sun4m by Eddie C. Dost
 *		  (ecd@skynet.be)
 *
 *	 5/17/96: lebuffer on scsi/ether cards now work David S. Miller
 *		  (davem@caip.rutgers.edu)
 *
 *	 5/29/96: override option 'tpe-link-test?', if it is 'false', as
 *		  this disables auto carrier detection on sun4m. Eddie C. Dost
 *		  (ecd@skynet.be)
 *
 * 1.7:
 *	 6/26/96: Bug fix for multiple ledmas, miguel.
 *
 * 1.8:
 *		  Stole multicast code from depca.c, fixed lance_tx.
 *
 * 1.9:
 *	 8/21/96: Fixed the multicast code (Pedro Roque)
 *
 *	 8/28/96: Send fake packet in lance_open() if auto_select is true,
 *		  so we can detect the carrier loss condition in time.
 *		  Eddie C. Dost (ecd@skynet.be)
 *
 *	 9/15/96: Align rx_buf so that eth_copy_and_sum() won't cause an
 *		  MNA trap during chksum_partial_copy(). (ecd@skynet.be)
 *
 *	11/17/96: Handle LE_C0_MERR in lance_interrupt(). (ecd@skynet.be)
 *
 *	12/22/96: Don't loop forever in lance_rx() on incomplete packets.
 *		  This was the sun4c killer. Shit, stupid bug.
 *		  (ecd@skynet.be)
 *
 * 1.10:
 *	 1/26/97: Modularize driver. (ecd@skynet.be)
 *
 * 1.11:
 *	12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz)
 *
 * 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)
 * 2.01:
 *      11/08/01: Use library crc32 functions (Matt_Domsch@dell.com)
 *
 */

#undef DEBUG_DRIVER

static char lancestr[] = "LANCE";

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/crc32.h>
#include <linux/errno.h>
#include <linux/socket.h> /* Used for the temporal inet entries and routing */
#include <linux/route.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/pgtable.h>
#include <asm/byteorder.h>	/* Used by the checksum routines */
#include <asm/idprom.h>
#include <asm/sbus.h>
#include <asm/prom.h>
#include <asm/auxio.h>		/* For tpe-link-test? setting */
#include <asm/irq.h>

#define DRV_NAME	"sunlance"
#define DRV_VERSION	"2.02"
#define DRV_RELDATE	"8/24/03"
#define DRV_AUTHOR	"Miguel de Icaza (miguel@nuclecu.unam.mx)"

static char version[] =
	DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";

MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION("Sun Lance ethernet driver");
MODULE_LICENSE("GPL");

/* Define: 2^4 Tx buffers and 2^4 Rx buffers */
#ifndef LANCE_LOG_TX_BUFFERS
#define LANCE_LOG_TX_BUFFERS 4
#define LANCE_LOG_RX_BUFFERS 4
#endif

#define LE_CSR0 0
#define LE_CSR1 1
#define LE_CSR2 2
#define LE_CSR3 3

#define LE_MO_PROM      0x8000  /* Enable promiscuous mode */

#define	LE_C0_ERR	0x8000	/* Error: set if BAB, SQE, MISS or ME is set */
#define	LE_C0_BABL	0x4000	/* BAB:  Babble: tx timeout. */
#define	LE_C0_CERR	0x2000	/* SQE:  Signal quality error */
#define	LE_C0_MISS	0x1000	/* MISS: Missed a packet */
#define	LE_C0_MERR	0x0800	/* ME:   Memory error */
#define	LE_C0_RINT	0x0400	/* Received interrupt */
#define	LE_C0_TINT	0x0200	/* Transmitter Interrupt */
#define	LE_C0_IDON	0x0100	/* IFIN: Init finished. */
#define	LE_C0_INTR	0x0080	/* Interrupt or error */
#define	LE_C0_INEA	0x0040	/* Interrupt enable */
#define	LE_C0_RXON	0x0020	/* Receiver on */
#define	LE_C0_TXON	0x0010	/* Transmitter on */
#define	LE_C0_TDMD	0x0008	/* Transmitter demand */
#define	LE_C0_STOP	0x0004	/* Stop the card */
#define	LE_C0_STRT	0x0002	/* Start the card */
#define	LE_C0_INIT	0x0001	/* Init the card */

#define	LE_C3_BSWP	0x4     /* SWAP */
#define	LE_C3_ACON	0x2	/* ALE Control */
#define	LE_C3_BCON	0x1	/* Byte control */

/* Receive message descriptor 1 */
#define LE_R1_OWN       0x80    /* Who owns the entry */
#define LE_R1_ERR       0x40    /* Error: if FRA, OFL, CRC or BUF is set */
#define LE_R1_FRA       0x20    /* FRA: Frame error */
#define LE_R1_OFL       0x10    /* OFL: Frame overflow */
#define LE_R1_CRC       0x08    /* CRC error */
#define LE_R1_BUF       0x04    /* BUF: Buffer error */
#define LE_R1_SOP       0x02    /* Start of packet */
#define LE_R1_EOP       0x01    /* End of packet */
#define LE_R1_POK       0x03    /* Packet is complete: SOP + EOP */

#define LE_T1_OWN       0x80    /* Lance owns the packet */
#define LE_T1_ERR       0x40    /* Error summary */
#define LE_T1_EMORE     0x10    /* Error: more than one retry needed */
#define LE_T1_EONE      0x08    /* Error: one retry needed */
#define LE_T1_EDEF      0x04    /* Error: deferred */
#define LE_T1_SOP       0x02    /* Start of packet */
#define LE_T1_EOP       0x01    /* End of packet */
#define LE_T1_POK	0x03	/* Packet is complete: SOP + EOP */

#define LE_T3_BUF       0x8000  /* Buffer error */
#define LE_T3_UFL       0x4000  /* Error underflow */
#define LE_T3_LCOL      0x1000  /* Error late collision */
#define LE_T3_CLOS      0x0800  /* Error carrier loss */
#define LE_T3_RTY       0x0400  /* Error retry */
#define LE_T3_TDR       0x03ff  /* Time Domain Reflectometry counter */

#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 {
	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 {
	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 {
	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. */
	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];

	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) \
((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem])))))

#define libbuff_offset(rt, elem) \
((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem][0])))))

struct lance_private {
	void __iomem	*lregs;		/* Lance RAP/RDP regs.		*/
	void __iomem	*dregs;		/* DMA controller regs.		*/
	struct lance_init_block __iomem *init_block_iomem;
	struct lance_init_block *init_block_mem;

	spinlock_t	lock;

	int		rx_new, tx_new;
	int		rx_old, tx_old;

	struct net_device_stats	stats;
	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;
	dma_addr_t		init_block_dvma;
	struct net_device      *dev;		  /* Backpointer	*/
	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)

/* Lance registers. */
#define RDP		0x00UL		/* register data port		*/
#define RAP		0x02UL		/* register address port	*/
#define LANCE_REG_SIZE	0x04UL

#define STOP_LANCE(__lp) \
do {	void __iomem *__base = (__lp)->lregs; \
	sbus_writew(LE_CSR0,	__base + RAP); \
	sbus_writew(LE_C0_STOP,	__base + RDP); \
} while (0)

int sparc_lance_debug = 2;

/* The Lance uses 24 bit addresses */
/* On the Sun4c the DVMA will provide the remaining bytes for us */
/* 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
 */

#define LANCE_ADDR(x) ((long)(x) & ~0xff000000)

/* Load the CSR registers */
static void load_csrs(struct lance_private *lp)
{
	u32 leptr;

	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 */
	sbus_writew(LE_CSR0, lp->lregs + RAP);
}

/* Setup the Lance Rx and Tx rings */
static void lance_init_ring_dvma(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	struct lance_init_block *ib = lp->init_block_mem;
	dma_addr_t aib = lp->init_block_dvma;
	__u32 leptr;
	int i;

	/* Lock out other processes while setting up hardware */
	netif_stop_queue(dev);
	lp->rx_new = lp->tx_new = 0;
	lp->rx_old = lp->tx_old = 0;

	/* Copy the ethernet address to the lance init block
	 * Note that on the sparc you need to swap the ethernet address.
	 */
	ib->phys_addr [0] = dev->dev_addr [1];
	ib->phys_addr [1] = dev->dev_addr [0];
	ib->phys_addr [2] = dev->dev_addr [3];
	ib->phys_addr [3] = dev->dev_addr [2];
	ib->phys_addr [4] = dev->dev_addr [5];
	ib->phys_addr [5] = dev->dev_addr [4];

	/* Setup the Tx ring entries */
	for (i = 0; i <= TX_RING_SIZE; i++) {
		leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i));
		ib->btx_ring [i].tmd0      = leptr;
		ib->btx_ring [i].tmd1_hadr = leptr >> 16;
		ib->btx_ring [i].tmd1_bits = 0;
		ib->btx_ring [i].length    = 0xf000; /* The ones required by tmd2 */
		ib->btx_ring [i].misc      = 0;
	}

	/* Setup the Rx ring entries */
	for (i = 0; i < RX_RING_SIZE; i++) {
		leptr = LANCE_ADDR(aib + libbuff_offset(rx_buf, i));

		ib->brx_ring [i].rmd0      = leptr;
		ib->brx_ring [i].rmd1_hadr = leptr >> 16;
		ib->brx_ring [i].rmd1_bits = LE_R1_OWN;
		ib->brx_ring [i].length    = -RX_BUFF_SIZE | 0xf000;
		ib->brx_ring [i].mblength  = 0;
	}

	/* Setup the initialization block */

	/* Setup rx descriptor pointer */
	leptr = LANCE_ADDR(aib + libdesc_offset(brx_ring, 0));
	ib->rx_len = (LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16);
	ib->rx_ptr = 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;
}

static void lance_init_ring_pio(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	struct lance_init_block __iomem *ib = lp->init_block_iomem;
	u32 leptr;
	int i;

	/* Lock out other processes while setting up hardware */
	netif_stop_queue(dev);
	lp->rx_new = lp->tx_new = 0;
	lp->rx_old = lp->tx_old = 0;

	/* 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]);

	/* 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);
	}

	/* Setup the Rx ring entries */
	for (i = 0; i < RX_RING_SIZE; i++) {
		leptr = libbuff_offset(rx_buf, i);

		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);
	}

	/* 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; i++) {
		regval = sbus_readw(lp->lregs + RDP);

		if (regval & (LE_C0_ERR | LE_C0_IDON))
			break;
		barrier();
	}
	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 */
	sbus_writew(LE_C0_IDON,			lp->lregs + RDP);
	sbus_writew(LE_C0_INEA | LE_C0_STRT,	lp->lregs + RDP);

	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 void lance_rx_dvma(struct net_device *dev)
{
	struct lance_private *lp = netdev_priv(dev);
	struct lance_init_block *ib = lp->init_block_mem;
	struct lance_rx_desc *rd;
	u8 bits;
	int len, entry = lp->rx_new;
	struct sk_buff *skb;

	for (rd = &ib->brx_ring [entry];
	     !((bits = 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 = (rd->mblength & 0xfff) - 4;