tions for Forbidden Zones paper @ RTAS'20
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/ns83820.c
blob: 1576ac07216ec9bb7a65576336945804a4353168 (plain) (tree)
1
                      































































                                                                              

                                                                   
                                                                   

                                                                     



























                                                                       



                              
                              


                              



                                             




                           
                            
                          















                                                             




















































































































































































































































                                                                                            
                                          

                                                                 


                                                                         

                                                                 

                                                                          






















                                                                        
                             


                                          
                                      

                                            
                               








                                              
                                      

















                                                             













                                                                    
                                  
































                                                                                                
                                 











                                                                                     














                                                                 
                                                                                                                            












                                                                                                       
                   


















                                                           
                                                      



                                                                
                                                     








                                                                                                                                                                  
                                                               













                                                             
 
                                                  
                                                                         


                                   
                                                                       















                                                                          
                                                     




                                    
                                                         
 

                                                                            










                                                                                      
                                             

                                         
                                                                                  


























































                                                                                  
                                 
                                          











                                                                         

                                         
                                                        











































































                                                                             
                                             



































                                                                                          
                               


         
                                                    

















                                                                 
  
   
                                           




                                             

                     













































                                                                                
                                                               



                                                                    
                   
                                                  
                                                                 















                                                                              
                                 































































                                                                            

                                
 


































                                                                               
                                                    


















                                                                          









                                                      
                                                                       




























                                                                                         
                                    







                                                          
                                              
















                                                                       
                                      

















                                                                                  
                                                 
                                       
                                                         
                                                
                                                              





















                                                                             
                                                                               








                                                                                              
                                                                                



                                                                



























                                                                               
                            































                                                                              














































































































































                                                                                














                                                                                      
                                       



                                                

  
                                                         

                                                

                                                  
                                  
                                                       


                                                             
                                                   
















                                                            

                            













                                                                                                                    
                                                          

                                                           
                                                               
































                                                                                        

                                                                         









                                                                       
                                                        
                                 
                                                             




                                                                       
                                                                  

                                                                
                                                                       




                                                                      

                                                                    


                                                                           
                                                          

                                                        
                                                               

























































                                                                                        

                        

                            
                                                

























                                                                                    
                                                     












                                                                                 
                                                                


























































                                                                            
 




                                                                        
 

























                                                                   
                                                         















                                                                                                       
                            
















                                                    
                                                         


                                      
                                                    



























































































































































































                                                                                                     







                                                          
                                               
                                                     


                                                           



                                                                     







                                                                        
                                      
                                                               
                              
                                                                  

                              
                                                                      




                                                      
 



                         

                         




                                           

                                            
                                                 



                                                                       
                                                                            
























                                                                             
                                                                 

                                          
                                                                              
                                           












                                                                           
                                                                                






                                                                      
                                       
                                    





































                                                                                              
                                    
                                              



































                                                                            
                                                                        










                                                                           
                                                                  












                                                                            

                                                                  






















                                                                               
                                                         

































                                                                       







                                                                  
                                                                                                  


                                                        
                                                   















































































                                                                                                             
                                            























                                                                                     
#define VERSION "0.23"
/* ns83820.c by Benjamin LaHaise with contributions.
 *
 * Questions/comments/discussion to linux-ns83820@kvack.org.
 *
 * $Revision: 1.34.2.23 $
 *
 * Copyright 2001 Benjamin LaHaise.
 * Copyright 2001, 2002 Red Hat.
 *
 * Mmmm, chocolate vanilla mocha...
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * ChangeLog
 * =========
 *	20010414	0.1 - created
 *	20010622	0.2 - basic rx and tx.
 *	20010711	0.3 - added duplex and link state detection support.
 *	20010713	0.4 - zero copy, no hangs.
 *			0.5 - 64 bit dma support (davem will hate me for this)
 *			    - disable jumbo frames to avoid tx hangs
 *			    - work around tx deadlocks on my 1.02 card via
 *			      fiddling with TXCFG
 *	20010810	0.6 - use pci dma api for ringbuffers, work on ia64
 *	20010816	0.7 - misc cleanups
 *	20010826	0.8 - fix critical zero copy bugs
 *			0.9 - internal experiment
 *	20010827	0.10 - fix ia64 unaligned access.
 *	20010906	0.11 - accept all packets with checksum errors as
 *			       otherwise fragments get lost
 *			     - fix >> 32 bugs
 *			0.12 - add statistics counters
 *			     - add allmulti/promisc support
 *	20011009	0.13 - hotplug support, other smaller pci api cleanups
 *	20011204	0.13a - optical transceiver support added
 *				by Michael Clark <michael@metaparadigm.com>
 *	20011205	0.13b - call register_netdev earlier in initialization
 *				suppress duplicate link status messages
 *	20011117 	0.14 - ethtool GDRVINFO, GLINK support from jgarzik
 *	20011204 	0.15	get ppc (big endian) working
 *	20011218	0.16	various cleanups
 *	20020310	0.17	speedups
 *	20020610	0.18 -	actually use the pci dma api for highmem
 *			     -	remove pci latency register fiddling
 *			0.19 -	better bist support
 *			     -	add ihr and reset_phy parameters
 *			     -	gmii bus probing
 *			     -	fix missed txok introduced during performance
 *				tuning
 *			0.20 -	fix stupid RFEN thinko.  i am such a smurf.
 *	20040828	0.21 -	add hardware vlan accleration
 *				by Neil Horman <nhorman@redhat.com>
 *	20050406	0.22 -	improved DAC ifdefs from Andi Kleen
 *			     -	removal of dead code from Adrian Bunk
 *			     -	fix half duplex collision behaviour
 * Driver Overview
 * ===============
 *
 * This driver was originally written for the National Semiconductor
 * 83820 chip, a 10/100/1000 Mbps 64 bit PCI ethernet NIC.  Hopefully
 * this code will turn out to be a) clean, b) correct, and c) fast.
 * With that in mind, I'm aiming to split the code up as much as
 * reasonably possible.  At present there are X major sections that
 * break down into a) packet receive, b) packet transmit, c) link
 * management, d) initialization and configuration.  Where possible,
 * these code paths are designed to run in parallel.
 *
 * This driver has been tested and found to work with the following
 * cards (in no particular order):
 *
 *	Cameo		SOHO-GA2000T	SOHO-GA2500T
 *	D-Link		DGE-500T
 *	PureData	PDP8023Z-TG
 *	SMC		SMC9452TX	SMC9462TX
 *	Netgear		GA621
 *
 * Special thanks to SMC for providing hardware to test this driver on.
 *
 * Reports of success or failure would be greatly appreciated.
 */
//#define dprintk		printk
#define dprintk(x...)		do { } while (0)

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/init.h>
#include <linux/ip.h>	/* for iph */
#include <linux/in.h>	/* for IPPROTO_... */
#include <linux/compiler.h>
#include <linux/prefetch.h>
#include <linux/ethtool.h>
#include <linux/timer.h>
#include <linux/if_vlan.h>
#include <linux/rtnetlink.h>
#include <linux/jiffies.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>

#define DRV_NAME "ns83820"

/* Global parameters.  See module_param near the bottom. */
static int ihr = 2;
static int reset_phy = 0;
static int lnksts = 0;		/* CFG_LNKSTS bit polarity */

/* Dprintk is used for more interesting debug events */
#undef Dprintk
#define	Dprintk			dprintk

/* tunables */
#define RX_BUF_SIZE	1500	/* 8192 */
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define NS83820_VLAN_ACCEL_SUPPORT
#endif

/* Must not exceed ~65000. */
#define NR_RX_DESC	64
#define NR_TX_DESC	128

/* not tunable */
#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14)	/* rx/tx mac addr + type */

#define MIN_TX_DESC_FREE	8

/* register defines */
#define CFGCS		0x04

#define CR_TXE		0x00000001
#define CR_TXD		0x00000002
/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE
 * The Receive engine skips one descriptor and moves
 * onto the next one!! */
#define CR_RXE		0x00000004
#define CR_RXD		0x00000008
#define CR_TXR		0x00000010
#define CR_RXR		0x00000020
#define CR_SWI		0x00000080
#define CR_RST		0x00000100

#define PTSCR_EEBIST_FAIL       0x00000001
#define PTSCR_EEBIST_EN         0x00000002
#define PTSCR_EELOAD_EN         0x00000004
#define PTSCR_RBIST_FAIL        0x000001b8
#define PTSCR_RBIST_DONE        0x00000200
#define PTSCR_RBIST_EN          0x00000400
#define PTSCR_RBIST_RST         0x00002000

#define MEAR_EEDI		0x00000001
#define MEAR_EEDO		0x00000002
#define MEAR_EECLK		0x00000004
#define MEAR_EESEL		0x00000008
#define MEAR_MDIO		0x00000010
#define MEAR_MDDIR		0x00000020
#define MEAR_MDC		0x00000040

#define ISR_TXDESC3	0x40000000
#define ISR_TXDESC2	0x20000000
#define ISR_TXDESC1	0x10000000
#define ISR_TXDESC0	0x08000000
#define ISR_RXDESC3	0x04000000
#define ISR_RXDESC2	0x02000000
#define ISR_RXDESC1	0x01000000
#define ISR_RXDESC0	0x00800000
#define ISR_TXRCMP	0x00400000
#define ISR_RXRCMP	0x00200000
#define ISR_DPERR	0x00100000
#define ISR_SSERR	0x00080000
#define ISR_RMABT	0x00040000
#define ISR_RTABT	0x00020000
#define ISR_RXSOVR	0x00010000
#define ISR_HIBINT	0x00008000
#define ISR_PHY		0x00004000
#define ISR_PME		0x00002000
#define ISR_SWI		0x00001000
#define ISR_MIB		0x00000800
#define ISR_TXURN	0x00000400
#define ISR_TXIDLE	0x00000200
#define ISR_TXERR	0x00000100
#define ISR_TXDESC	0x00000080
#define ISR_TXOK	0x00000040
#define ISR_RXORN	0x00000020
#define ISR_RXIDLE	0x00000010
#define ISR_RXEARLY	0x00000008
#define ISR_RXERR	0x00000004
#define ISR_RXDESC	0x00000002
#define ISR_RXOK	0x00000001

#define TXCFG_CSI	0x80000000
#define TXCFG_HBI	0x40000000
#define TXCFG_MLB	0x20000000
#define TXCFG_ATP	0x10000000
#define TXCFG_ECRETRY	0x00800000
#define TXCFG_BRST_DIS	0x00080000
#define TXCFG_MXDMA1024	0x00000000
#define TXCFG_MXDMA512	0x00700000
#define TXCFG_MXDMA256	0x00600000
#define TXCFG_MXDMA128	0x00500000
#define TXCFG_MXDMA64	0x00400000
#define TXCFG_MXDMA32	0x00300000
#define TXCFG_MXDMA16	0x00200000
#define TXCFG_MXDMA8	0x00100000

#define CFG_LNKSTS	0x80000000
#define CFG_SPDSTS	0x60000000
#define CFG_SPDSTS1	0x40000000
#define CFG_SPDSTS0	0x20000000
#define CFG_DUPSTS	0x10000000
#define CFG_TBI_EN	0x01000000
#define CFG_MODE_1000	0x00400000
/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy.
 * Read the Phy response and then configure the MAC accordingly */
#define CFG_AUTO_1000	0x00200000
#define CFG_PINT_CTL	0x001c0000
#define CFG_PINT_DUPSTS	0x00100000
#define CFG_PINT_LNKSTS	0x00080000
#define CFG_PINT_SPDSTS	0x00040000
#define CFG_TMRTEST	0x00020000
#define CFG_MRM_DIS	0x00010000
#define CFG_MWI_DIS	0x00008000
#define CFG_T64ADDR	0x00004000
#define CFG_PCI64_DET	0x00002000
#define CFG_DATA64_EN	0x00001000
#define CFG_M64ADDR	0x00000800
#define CFG_PHY_RST	0x00000400
#define CFG_PHY_DIS	0x00000200
#define CFG_EXTSTS_EN	0x00000100
#define CFG_REQALG	0x00000080
#define CFG_SB		0x00000040
#define CFG_POW		0x00000020
#define CFG_EXD		0x00000010
#define CFG_PESEL	0x00000008
#define CFG_BROM_DIS	0x00000004
#define CFG_EXT_125	0x00000002
#define CFG_BEM		0x00000001

#define EXTSTS_UDPPKT	0x00200000
#define EXTSTS_TCPPKT	0x00080000
#define EXTSTS_IPPKT	0x00020000
#define EXTSTS_VPKT	0x00010000
#define EXTSTS_VTG_MASK	0x0000ffff

#define SPDSTS_POLARITY	(CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0))

#define MIBC_MIBS	0x00000008
#define MIBC_ACLR	0x00000004
#define MIBC_FRZ	0x00000002
#define MIBC_WRN	0x00000001

#define PCR_PSEN	(1 << 31)
#define PCR_PS_MCAST	(1 << 30)
#define PCR_PS_DA	(1 << 29)
#define PCR_STHI_8	(3 << 23)
#define PCR_STLO_4	(1 << 23)
#define PCR_FFHI_8K	(3 << 21)
#define PCR_FFLO_4K	(1 << 21)
#define PCR_PAUSE_CNT	0xFFFE

#define RXCFG_AEP	0x80000000
#define RXCFG_ARP	0x40000000
#define RXCFG_STRIPCRC	0x20000000
#define RXCFG_RX_FD	0x10000000
#define RXCFG_ALP	0x08000000
#define RXCFG_AIRL	0x04000000
#define RXCFG_MXDMA512	0x00700000
#define RXCFG_DRTH	0x0000003e
#define RXCFG_DRTH0	0x00000002

#define RFCR_RFEN	0x80000000
#define RFCR_AAB	0x40000000
#define RFCR_AAM	0x20000000
#define RFCR_AAU	0x10000000
#define RFCR_APM	0x08000000
#define RFCR_APAT	0x07800000
#define RFCR_APAT3	0x04000000
#define RFCR_APAT2	0x02000000
#define RFCR_APAT1	0x01000000
#define RFCR_APAT0	0x00800000
#define RFCR_AARP	0x00400000
#define RFCR_MHEN	0x00200000
#define RFCR_UHEN	0x00100000
#define RFCR_ULM	0x00080000

#define VRCR_RUDPE	0x00000080
#define VRCR_RTCPE	0x00000040
#define VRCR_RIPE	0x00000020
#define VRCR_IPEN	0x00000010
#define VRCR_DUTF	0x00000008
#define VRCR_DVTF	0x00000004
#define VRCR_VTREN	0x00000002
#define VRCR_VTDEN	0x00000001

#define VTCR_PPCHK	0x00000008
#define VTCR_GCHK	0x00000004
#define VTCR_VPPTI	0x00000002
#define VTCR_VGTI	0x00000001

#define CR		0x00
#define CFG		0x04
#define MEAR		0x08
#define PTSCR		0x0c
#define	ISR		0x10
#define	IMR		0x14
#define	IER		0x18
#define	IHR		0x1c
#define TXDP		0x20
#define TXDP_HI		0x24
#define TXCFG		0x28
#define GPIOR		0x2c
#define RXDP		0x30
#define RXDP_HI		0x34
#define RXCFG		0x38
#define PQCR		0x3c
#define WCSR		0x40
#define PCR		0x44
#define RFCR		0x48
#define RFDR		0x4c

#define SRR		0x58

#define VRCR		0xbc
#define VTCR		0xc0
#define VDR		0xc4
#define CCSR		0xcc

#define TBICR		0xe0
#define TBISR		0xe4
#define TANAR		0xe8
#define TANLPAR		0xec
#define TANER		0xf0
#define TESR		0xf4

#define TBICR_MR_AN_ENABLE	0x00001000
#define TBICR_MR_RESTART_AN	0x00000200

#define TBISR_MR_LINK_STATUS	0x00000020
#define TBISR_MR_AN_COMPLETE	0x00000004

#define TANAR_PS2 		0x00000100
#define TANAR_PS1 		0x00000080
#define TANAR_HALF_DUP 		0x00000040
#define TANAR_FULL_DUP 		0x00000020

#define GPIOR_GP5_OE		0x00000200
#define GPIOR_GP4_OE		0x00000100
#define GPIOR_GP3_OE		0x00000080
#define GPIOR_GP2_OE		0x00000040
#define GPIOR_GP1_OE		0x00000020
#define GPIOR_GP3_OUT		0x00000004
#define GPIOR_GP1_OUT		0x00000001

#define LINK_AUTONEGOTIATE	0x01
#define LINK_DOWN		0x02
#define LINK_UP			0x04

#define HW_ADDR_LEN	sizeof(dma_addr_t)
#define desc_addr_set(desc, addr)				\
	do {							\
		((desc)[0] = cpu_to_le32(addr));		\
		if (HW_ADDR_LEN == 8)		 		\
			(desc)[1] = cpu_to_le32(((u64)addr) >> 32);	\
	} while(0)
#define desc_addr_get(desc)					\
	(le32_to_cpu((desc)[0]) | \
	(HW_ADDR_LEN == 8 ? ((dma_addr_t)le32_to_cpu((desc)[1]))<<32 : 0))

#define DESC_LINK		0
#define DESC_BUFPTR		(DESC_LINK + HW_ADDR_LEN/4)
#define DESC_CMDSTS		(DESC_BUFPTR + HW_ADDR_LEN/4)
#define DESC_EXTSTS		(DESC_CMDSTS + 4/4)

#define CMDSTS_OWN	0x80000000
#define CMDSTS_MORE	0x40000000
#define CMDSTS_INTR	0x20000000
#define CMDSTS_ERR	0x10000000
#define CMDSTS_OK	0x08000000
#define CMDSTS_RUNT	0x00200000
#define CMDSTS_LEN_MASK	0x0000ffff

#define CMDSTS_DEST_MASK	0x01800000
#define CMDSTS_DEST_SELF	0x00800000
#define CMDSTS_DEST_MULTI	0x01000000

#define DESC_SIZE	8		/* Should be cache line sized */

struct rx_info {
	spinlock_t	lock;
	int		up;
	unsigned long	idle;

	struct sk_buff	*skbs[NR_RX_DESC];

	__le32		*next_rx_desc;
	u16		next_rx, next_empty;

	__le32		*descs;
	dma_addr_t	phy_descs;
};


struct ns83820 {
	struct net_device_stats	stats;
	u8			__iomem *base;

	struct pci_dev		*pci_dev;
	struct net_device	*ndev;

#ifdef NS83820_VLAN_ACCEL_SUPPORT
	struct vlan_group	*vlgrp;
#endif

	struct rx_info		rx_info;
	struct tasklet_struct	rx_tasklet;

	unsigned		ihr;
	struct work_struct	tq_refill;

	/* protects everything below.  irqsave when using. */
	spinlock_t		misc_lock;

	u32			CFG_cache;

	u32			MEAR_cache;
	u32			IMR_cache;

	unsigned		linkstate;

	spinlock_t	tx_lock;

	u16		tx_done_idx;
	u16		tx_idx;
	volatile u16	tx_free_idx;	/* idx of free desc chain */
	u16		tx_intr_idx;

	atomic_t	nr_tx_skbs;
	struct sk_buff	*tx_skbs[NR_TX_DESC];

	char		pad[16] __attribute__((aligned(16)));
	__le32		*tx_descs;
	dma_addr_t	tx_phy_descs;

	struct timer_list	tx_watchdog;
};

static inline struct ns83820 *PRIV(struct net_device *dev)
{
	return netdev_priv(dev);
}

#define __kick_rx(dev)	writel(CR_RXE, dev->base + CR)

static inline void kick_rx(struct net_device *ndev)
{
	struct ns83820 *dev = PRIV(ndev);
	dprintk("kick_rx: maybe kicking\n");
	if (test_and_clear_bit(0, &dev->rx_info.idle)) {
		dprintk("actually kicking\n");
		writel(dev->rx_info.phy_descs +
			(4 * DESC_SIZE * dev->rx_info.next_rx),
		       dev->base + RXDP);
		if (dev->rx_info.next_rx == dev->rx_info.next_empty)
			printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n",
				ndev->name);
		__kick_rx(dev);
	}
}

//free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC
#define start_tx_okay(dev)	\
	(((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE)


#ifdef NS83820_VLAN_ACCEL_SUPPORT
static void ns83820_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
{
	struct ns83820 *dev = PRIV(ndev);

	spin_lock_irq(&dev->misc_lock);
	spin_lock(&dev->tx_lock);

	dev->vlgrp = grp;

	spin_unlock(&dev->tx_lock);
	spin_unlock_irq(&dev->misc_lock);
}
#endif

/* Packet Receiver
 *
 * The hardware supports linked lists of receive descriptors for
 * which ownership is transfered back and forth by means of an
 * ownership bit.  While the hardware does support the use of a
 * ring for receive descriptors, we only make use of a chain in
 * an attempt to reduce bus traffic under heavy load scenarios.
 * This will also make bugs a bit more obvious.  The current code
 * only makes use of a single rx chain; I hope to implement
 * priority based rx for version 1.0.  Goal: even under overload
 * conditions, still route realtime traffic with as low jitter as
 * possible.
 */
static inline void build_rx_desc(struct ns83820 *dev, __le32 *desc, dma_addr_t link, dma_addr_t buf, u32 cmdsts, u32 extsts)
{
	desc_addr_set(desc + DESC_LINK, link);
	desc_addr_set(desc + DESC_BUFPTR, buf);
	desc[DESC_EXTSTS] = cpu_to_le32(extsts);
	mb();
	desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
}

#define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC)
static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb)
{
	unsigned next_empty;
	u32 cmdsts;
	__le32 *sg;
	dma_addr_t buf;

	next_empty = dev->rx_info.next_empty;

	/* don't overrun last rx marker */
	if (unlikely(nr_rx_empty(dev) <= 2)) {
		kfree_skb(skb);
		return 1;
	}

#if 0
	dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n",
		dev->rx_info.next_empty,
		dev->rx_info.nr_used,
		dev->rx_info.next_rx
		);
#endif

	sg = dev->rx_info.descs + (next_empty * DESC_SIZE);
	BUG_ON(NULL != dev->rx_info.skbs[next_empty]);
	dev->rx_info.skbs[next_empty] = skb;

	dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC;
	cmdsts = REAL_RX_BUF_SIZE | CMDSTS_INTR;
	buf = pci_map_single(dev->pci_dev, skb->data,
			     REAL_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
	build_rx_desc(dev, sg, 0, buf, cmdsts, 0);
	/* update link of previous rx */
	if (likely(next_empty != dev->rx_info.next_rx))
		dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4));

	return 0;
}

static inline int rx_refill(struct net_device *ndev, gfp_t gfp)
{
	struct ns83820 *dev = PRIV(ndev);
	unsigned i;
	unsigned long flags = 0;

	if (unlikely(nr_rx_empty(dev) <= 2))
		return 0;

	dprintk("rx_refill(%p)\n", ndev);
	if (gfp == GFP_ATOMIC)
		spin_lock_irqsave(&dev->rx_info.lock, flags);
	for (i=0; i<NR_RX_DESC; i++) {
		struct sk_buff *skb;
		long res;

		/* extra 16 bytes for alignment */
		skb = __netdev_alloc_skb(ndev, REAL_RX_BUF_SIZE+16, gfp);
		if (unlikely(!skb))
			break;

		skb_reserve(skb, skb->data - PTR_ALIGN(skb->data, 16));
		if (gfp != GFP_ATOMIC)
			spin_lock_irqsave(&dev->rx_info.lock, flags);
		res = ns83820_add_rx_skb(dev, skb);
		if (gfp != GFP_ATOMIC)
			spin_unlock_irqrestore(&dev->rx_info.lock, flags);
		if (res) {
			i = 1;
			break;
		}
	}
	if (gfp == GFP_ATOMIC)
		spin_unlock_irqrestore(&dev->rx_info.lock, flags);

	return i ? 0 : -ENOMEM;
}

static void rx_refill_atomic(struct net_device *ndev)
{
	rx_refill(ndev, GFP_ATOMIC);
}

/* REFILL */
static inline void queue_refill(struct work_struct *work)
{
	struct ns83820 *dev = container_of(work, struct ns83820, tq_refill);
	struct net_device *ndev = dev->ndev;

	rx_refill(ndev, GFP_KERNEL);
	if (dev->rx_info.up)
		kick_rx(ndev);
}

static inline void clear_rx_desc(struct ns83820 *dev, unsigned i)
{
	build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0);
}

static void phy_intr(struct net_device *ndev)
{
	struct ns83820 *dev = PRIV(ndev);
	static const char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" };
	u32 cfg, new_cfg;
	u32 tbisr, tanar, tanlpar;
	int speed, fullduplex, newlinkstate;

	cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;

	if (dev->CFG_cache & CFG_TBI_EN) {
		/* we have an optical transceiver */
		tbisr = readl(dev->base + TBISR);
		tanar = readl(dev->base + TANAR);
		tanlpar = readl(dev->base + TANLPAR);
		dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n",
			tbisr, tanar, tanlpar);

		if ( (fullduplex = (tanlpar & TANAR_FULL_DUP)
		      && (tanar & TANAR_FULL_DUP)) ) {

			/* both of us are full duplex */
			writel(readl(dev->base + TXCFG)
			       | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
			       dev->base + TXCFG);
			writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
			       dev->base + RXCFG);
			/* Light up full duplex LED */
			writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
			       dev->base + GPIOR);

		} else if(((tanlpar & TANAR_HALF_DUP)
			   && (tanar & TANAR_HALF_DUP))
			|| ((tanlpar & TANAR_FULL_DUP)
			    && (tanar & TANAR_HALF_DUP))
			|| ((tanlpar & TANAR_HALF_DUP)
			    && (tanar & TANAR_FULL_DUP))) {

			/* one or both of us are half duplex */
			writel((readl(dev->base + TXCFG)
				& ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP,
			       dev->base + TXCFG);
			writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD,
			       dev->base + RXCFG);
			/* Turn off full duplex LED */
			writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT,
			       dev->base + GPIOR);
		}

		speed = 4; /* 1000F */

	} else {
		/* we have a copper transceiver */
		new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS);

		if (cfg & CFG_SPDSTS1)
			new_cfg |= CFG_MODE_1000;
		else
			new_cfg &= ~CFG_MODE_1000;

		speed = ((cfg / CFG_SPDSTS0) & 3);
		fullduplex = (cfg & CFG_DUPSTS);

		if (fullduplex) {
			new_cfg |= CFG_SB;
			writel(readl(dev->base + TXCFG)
					| TXCFG_CSI | TXCFG_HBI,
			       dev->base + TXCFG);
			writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
			       dev->base + RXCFG);
		} else {
			writel(readl(dev->base + TXCFG)
					& ~(TXCFG_CSI | TXCFG_HBI),
			       dev->base + TXCFG);
			writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD),
			       dev->base + RXCFG);
		}

		if ((cfg & CFG_LNKSTS) &&
		    ((new_cfg ^ dev->CFG_cache) != 0)) {
			writel(new_cfg, dev->base + CFG);
			dev->CFG_cache = new_cfg;
		}

		dev->CFG_cache &= ~CFG_SPDSTS;
		dev->CFG_cache |= cfg & CFG_SPDSTS;
	}

	newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;

	if (newlinkstate & LINK_UP
	    && dev->linkstate != newlinkstate) {
		netif_start_queue(ndev);
		netif_wake_queue(ndev);
		printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n",
			ndev->name,
			speeds[speed],
			fullduplex ? "full" : "half");
	} else if (newlinkstate & LINK_DOWN
		   && dev->linkstate != newlinkstate) {
		netif_stop_queue(ndev);
		printk(KERN_INFO "%s: link now down.\n", ndev->name);
	}

	dev->linkstate = newlinkstate;
}

static int ns83820_setup_rx(struct net_device *ndev)
{
	struct ns83820 *dev = PRIV(ndev);
	unsigned i;
	int ret;

	dprintk("ns83820_setup_rx(%p)\n", ndev);

	dev->rx_info.idle = 1;
	dev->rx_info.next_rx = 0;
	dev->rx_info.next_rx_desc = dev->rx_info.descs;
	dev->rx_info.next_empty = 0;

	for (i=0; i<NR_RX_DESC; i++)
		clear_rx_desc(dev, i);

	writel(0, dev->base + RXDP_HI);
	writel(dev->rx_info.phy_descs, dev->base + RXDP);

	ret = rx_refill(ndev, GFP_KERNEL);
	if (!ret) {
		dprintk("starting receiver\n");
		/* prevent the interrupt handler from stomping on us */
		spin_lock_irq(&dev->rx_info.lock);

		writel(0x0001, dev->base + CCSR);
		writel(0, dev->base + RFCR);
		writel(0x7fc00000, dev->base + RFCR);
		writel(0xffc00000, dev->base + RFCR);

		dev->rx_info.up = 1;

		phy_intr(ndev);

		/* Okay, let it rip */
		spin_lock_irq(&dev->misc_lock);
		dev->IMR_cache |= ISR_PHY;
		dev->IMR_cache |= ISR_RXRCMP;
		//dev->IMR_cache |= ISR_RXERR;
		//dev->IMR_cache |= ISR_RXOK;
		dev->IMR_cache |= ISR_RXORN;
		dev->IMR_cache |= ISR_RXSOVR;
		dev->IMR_cache |= ISR_RXDESC;
		dev->IMR_cache |= ISR_RXIDLE;
		dev->IMR_cache |= ISR_TXDESC;
		dev->IMR_cache |= ISR_TXIDLE;

		writel(dev->IMR_cache, dev->base + IMR);
		writel(1, dev->base + IER);
		spin_unlock(&dev->misc_lock);

		kick_rx(ndev);

		spin_unlock_irq(&dev->rx_info.lock);
	}
	return ret;
}

static void ns83820_cleanup_rx(struct ns83820 *dev)
{
	unsigned i;
	unsigned long flags;

	dprintk("ns83820_cleanup_rx(%p)\n", dev);

	/* disable receive interrupts */
	spin_lock_irqsave(&dev->misc_lock, flags);
	dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE);
	writel(dev->IMR_cache, dev->base + IMR);
	spin_unlock_irqrestore(&dev->misc_lock, flags);

	/* synchronize with the interrupt handler and kill it */
	dev->rx_info.up = 0;
	synchronize_irq(dev->pci_dev->irq);

	/* touch the pci bus... */
	readl(dev->base + IMR);

	/* assumes the transmitter is already disabled and reset */
	writel(0, dev->base + RXDP_HI);
	writel(0, dev->base + RXDP);

	for (i=0; i<NR_RX_DESC; i++) {
		struct sk_buff *skb = dev->rx_info.skbs[i];
		dev->rx_info.skbs[i] = NULL;
		clear_rx_desc(dev, i);
		kfree_skb(skb);
	}
}

static void ns83820_rx_kick(struct net_device *ndev)
{
	struct ns83820 *dev = PRIV(ndev);
	/*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ {
		if (dev->rx_info.up) {
			rx_refill_atomic(ndev);
			kick_rx(ndev);
		}
	}

	if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4)
		schedule_work(&dev->tq_refill);
	else
		kick_rx(ndev);
	if (dev->rx_info.idle)
		printk(KERN_DEBUG "%s: BAD\n", ndev->name);
}

/* rx_irq
 *
 */
static void rx_irq(struct net_device *ndev)
{
	struct ns83820 *dev = PRIV(ndev);
	struct rx_info *info = &dev->rx_info;
	unsigned next_rx;
	int rx_rc, len;
	u32 cmdsts;
	__le32 *desc;
	unsigned long flags;
	int nr = 0;

	dprintk("rx_irq(%p)\n", ndev);
	dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n",
		readl(dev->base + RXDP),
		(long)(dev->rx_info.phy_descs),
		(int)dev->rx_info.next_rx,
		(dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)),
		(int)dev->rx_info.next_empty,
		(dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty))
		);

	spin_lock_irqsave(&info->lock, flags);
	if (!info->up)
		goto out;

	dprintk("walking descs\n");
	next_rx = info->next_rx;
	desc = info->next_rx_desc;
	while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) &&
	       (cmdsts != CMDSTS_OWN)) {
		struct sk_buff *skb;
		u32 extsts = le32_to_cpu(desc[DESC_EXTSTS]);
		dma_addr_t bufptr = desc_addr_get(desc + DESC_BUFPTR);

		dprintk("cmdsts: %08x\n", cmdsts);
		dprintk("link: %08x\n", cpu_to_le32(desc[DESC_LINK]));
		dprintk("extsts: %08x\n", extsts);

		skb = info->skbs[next_rx];
		info->skbs[next_rx] = NULL;
		info->next_rx = (next_rx + 1) % NR_RX_DESC;

		mb();
		clear_rx_desc(dev, next_rx);

		pci_unmap_single(dev->pci_dev, bufptr,
				 RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
		len = cmdsts & CMDSTS_LEN_MASK;
#ifdef NS83820_VLAN_ACCEL_SUPPORT
		/* NH: As was mentioned below, this chip is kinda
		 * brain dead about vlan tag stripping.  Frames
		 * that are 64 bytes with a vlan header appended
		 * like arp frames, or pings, are flagged as Runts
		 * when the tag is stripped and hardware.  This
		 * also means that the OK bit in the descriptor
		 * is cleared when the frame comes in so we have
		 * to do a specific length check here to make sure
		 * the frame would have been ok, had we not stripped
		 * the tag.
		 */
		if (likely((CMDSTS_OK & cmdsts) ||
			((cmdsts & CMDSTS_RUNT) && len >= 56))) {
#else
		if (likely(CMDSTS_OK & cmdsts)) {
#endif
			skb_put(skb, len);
			if (unlikely(!skb))
				goto netdev_mangle_me_harder_failed;
			if (cmdsts & CMDSTS_DEST_MULTI)
				dev->stats.multicast ++;
			dev->stats.rx_packets ++;
			dev->stats.rx_bytes += len;
			if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) {
				skb->ip_summed = CHECKSUM_UNNECESSARY;
			} else {
				skb->ip_summed = CHECKSUM_NONE;