diff options
Diffstat (limited to 'drivers/net/dm9000x.c')
| -rw-r--r-- | drivers/net/dm9000x.c | 388 | 
1 files changed, 263 insertions, 125 deletions
| diff --git a/drivers/net/dm9000x.c b/drivers/net/dm9000x.c index 68901cd81..844fb766d 100644 --- a/drivers/net/dm9000x.c +++ b/drivers/net/dm9000x.c @@ -36,7 +36,24 @@ v1.2   03/18/2003       Weilun Huang <weilun_huang@davicom.com.tw>:  -------------------------------------- -       12/15/2003       Initial port to u-boot by Sascha Hauer <saschahauer@web.de> +       12/15/2003       Initial port to u-boot by +       			Sascha Hauer <saschahauer@web.de> + +       06/03/2008	Remy Bohmer <linux@bohmer.net> +			- Fixed the driver to work with DM9000A. +			  (check on ISR receive status bit before reading the +			  FIFO as described in DM9000 programming guide and +			  application notes) +			- Added autodetect of databus width. +			- Made debug code compile again. +			- Adapt eth_send such that it matches the DM9000* +			  application notes. Needed to make it work properly +			  for DM9000A. +			- Adapted reset procedure to match DM9000 application +			  notes (i.e. double reset) +			- some minor code cleanups +			These changes are tested with DM9000{A,EP,E} together +			with a 200MHz Atmel AT91SAM92161 core  TODO: Homerun NIC and longrun NIC are not functional, only internal at the        moment. @@ -47,8 +64,6 @@ TODO: Homerun NIC and longrun NIC are not functional, only internal at the  #include <net.h>  #include <asm/io.h> -#ifdef CONFIG_DRIVER_DM9000 -  #include "dm9000x.h"  /* Board/System/Debug information/definition ---------------- */ @@ -59,10 +74,22 @@ TODO: Homerun NIC and longrun NIC are not functional, only internal at the  /* #define CONFIG_DM9000_DEBUG */  #ifdef CONFIG_DM9000_DEBUG -#define DM9000_DBG(fmt,args...) printf(fmt ,##args) -#else				/*  */ +#define DM9000_DBG(fmt,args...) printf(fmt, ##args) +#define DM9000_DMP_PACKET(func,packet,length)  \ +	do { \ +		int i; 							\ +		printf(func ": length: %d\n", length);			\ +		for (i = 0; i < length; i++) {				\ +			if (i % 8 == 0)					\ +				printf("\n%s: %02x: ", func, i);	\ +			printf("%02x ", ((unsigned char *) packet)[i]);	\ +		} printf("\n");						\ +	} while(0) +#else  #define DM9000_DBG(fmt,args...) -#endif				/*  */ +#define DM9000_DMP_PACKET(func,packet,length) +#endif +  enum DM9000_PHY_mode { DM9000_10MHD = 0, DM9000_100MHD =  	    1, DM9000_10MFD = 4, DM9000_100MFD = 5, DM9000_AUTO =  	    8, DM9000_1M_HPNA = 0x10 @@ -84,8 +111,11 @@ typedef struct board_info {  	u8 device_wait_reset;	/* device state */  	u8 nic_type;		/* NIC type */  	unsigned char srom[128]; +	void (*outblk)(volatile void *data_ptr, int count); +	void (*inblk)(void *data_ptr, int count); +	void (*rx_status)(u16 *RxStatus, u16 *RxLen);  } board_info_t; -board_info_t dmfe_info; +static board_info_t dm9000_info;  /* For module input parameter */  static int media_mode = DM9000_AUTO; @@ -124,10 +154,85 @@ dump_regs(void)  	DM9000_DBG("TSRII (0x04): %02x\n", DM9000_ior(4));  	DM9000_DBG("RCR   (0x05): %02x\n", DM9000_ior(5));  	DM9000_DBG("RSR   (0x06): %02x\n", DM9000_ior(6)); -	DM9000_DBG("ISR   (0xFE): %02x\n", DM9000_ior(ISR)); +	DM9000_DBG("ISR   (0xFE): %02x\n", DM9000_ior(DM9000_ISR));  	DM9000_DBG("\n");  } -#endif				/*  */ +#endif + +static void dm9000_outblk_8bit(volatile void *data_ptr, int count) +{ +	int i; +	for (i = 0; i < count; i++) +		DM9000_outb((((u8 *) data_ptr)[i] & 0xff), DM9000_DATA); +} + +static void dm9000_outblk_16bit(volatile void *data_ptr, int count) +{ +	int i; +	u32 tmplen = (count + 1) / 2; + +	for (i = 0; i < tmplen; i++) +		DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA); +} +static void dm9000_outblk_32bit(volatile void *data_ptr, int count) +{ +	int i; +	u32 tmplen = (count + 3) / 4; + +	for (i = 0; i < tmplen; i++) +		DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA); +} + +static void dm9000_inblk_8bit(void *data_ptr, int count) +{ +	int i; +	for (i = 0; i < count; i++) +		((u8 *) data_ptr)[i] = DM9000_inb(DM9000_DATA); +} + +static void dm9000_inblk_16bit(void *data_ptr, int count) +{ +	int i; +	u32 tmplen = (count + 1) / 2; + +	for (i = 0; i < tmplen; i++) +		((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA); +} +static void dm9000_inblk_32bit(void *data_ptr, int count) +{ +	int i; +	u32 tmplen = (count + 3) / 4; + +	for (i = 0; i < tmplen; i++) +		((u32 *) data_ptr)[i] = DM9000_inl(DM9000_DATA); +} + +static void dm9000_rx_status_32bit(u16 *RxStatus, u16 *RxLen) +{ +	u32 tmpdata; + +	DM9000_outb(DM9000_MRCMD, DM9000_IO); + +	tmpdata = DM9000_inl(DM9000_DATA); +	*RxStatus = tmpdata; +	*RxLen = tmpdata >> 16; +} + +static void dm9000_rx_status_16bit(u16 *RxStatus, u16 *RxLen) +{ +	DM9000_outb(DM9000_MRCMD, DM9000_IO); + +	*RxStatus = DM9000_inw(DM9000_DATA); +	*RxLen = DM9000_inw(DM9000_DATA); +} + +static void dm9000_rx_status_8bit(u16 *RxStatus, u16 *RxLen) +{ +	DM9000_outb(DM9000_MRCMD, DM9000_IO); + +	*RxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8); +	*RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8); +}  /*    Search DM9000 board, allocate space and register it @@ -236,7 +341,7 @@ program_dm9802(void)  static void  identify_nic(void)  { -	struct board_info *db = &dmfe_info;	/* Point a board information structure */ +	struct board_info *db = &dm9000_info;  	u16 phy_reg3;  	DM9000_iow(DM9000_NCR, NCR_EXT_PHY);  	phy_reg3 = phy_read(3); @@ -263,9 +368,35 @@ identify_nic(void)  static void  dm9000_reset(void)  { -	DM9000_DBG("resetting\n"); -	DM9000_iow(DM9000_NCR, NCR_RST); -	udelay(1000);		/* delay 1ms */ +	DM9000_DBG("resetting DM9000\n"); + +	/* Reset DM9000, +	   see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */ + +	/* DEBUG: Make all GPIO pins outputs */ +	DM9000_iow(DM9000_GPCR, 0x0F); +	/* Step 1: Power internal PHY by writing 0 to GPIO0 pin */ +	DM9000_iow(DM9000_GPR, 0); +	/* Step 2: Software reset */ +	DM9000_iow(DM9000_NCR, 3); + +	do { +		DM9000_DBG("resetting the DM9000, 1st reset\n"); +		udelay(25); /* Wait at least 20 us */ +	} while (DM9000_ior(DM9000_NCR) & 1); + +	DM9000_iow(DM9000_NCR, 0); +	DM9000_iow(DM9000_NCR, 3); /* Issue a second reset */ + +	do { +		DM9000_DBG("resetting the DM9000, 2nd reset\n"); +		udelay(25); /* Wait at least 20 us */ +	} while (DM9000_ior(DM9000_NCR) & 1); + +	/* Check whether the ethernet controller is present */ +	if ((DM9000_ior(DM9000_PIDL) != 0x0) || +	    (DM9000_ior(DM9000_PIDH) != 0x90)) +		printf("ERROR: resetting DM9000 -> not responding\n");  }  /* Initilize dm9000 board @@ -274,12 +405,46 @@ int  eth_init(bd_t * bd)  {  	int i, oft, lnk; +	u8 io_mode; +	struct board_info *db = &dm9000_info; +  	DM9000_DBG("eth_init()\n");  	/* RESET device */  	dm9000_reset();  	dm9000_probe(); +	/* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */ +	io_mode = DM9000_ior(DM9000_ISR) >> 6; + +	switch (io_mode) { +	case 0x0:  /* 16-bit mode */ +		printf("DM9000: running in 16 bit mode\n"); +		db->outblk    = dm9000_outblk_16bit; +		db->inblk     = dm9000_inblk_16bit; +		db->rx_status = dm9000_rx_status_16bit; +		break; +	case 0x01:  /* 32-bit mode */ +		printf("DM9000: running in 32 bit mode\n"); +		db->outblk    = dm9000_outblk_32bit; +		db->inblk     = dm9000_inblk_32bit; +		db->rx_status = dm9000_rx_status_32bit; +		break; +	case 0x02: /* 8 bit mode */ +		printf("DM9000: running in 8 bit mode\n"); +		db->outblk    = dm9000_outblk_8bit; +		db->inblk     = dm9000_inblk_8bit; +		db->rx_status = dm9000_rx_status_8bit; +		break; +	default: +		/* Assume 8 bit mode, will probably not work anyway */ +		printf("DM9000: Undefined IO-mode:0x%x\n", io_mode); +		db->outblk    = dm9000_outblk_8bit; +		db->inblk     = dm9000_inblk_8bit; +		db->rx_status = dm9000_rx_status_8bit; +		break; +	} +  	/* NIC Type: FASTETHER, HOMERUN, LONGRUN */  	identify_nic(); @@ -289,15 +454,22 @@ eth_init(bd_t * bd)  	/* Set PHY */  	set_PHY_mode(); -	/* Program operating register */ -	DM9000_iow(DM9000_NCR, 0x0);	/* only intern phy supported by now */ -	DM9000_iow(DM9000_TCR, 0);	/* TX Polling clear */ -	DM9000_iow(DM9000_BPTR, 0x3f);	/* Less 3Kb, 200us */ -	DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));	/* Flow Control : High/Low Water */ -	DM9000_iow(DM9000_FCR, 0x0);	/* SH FIXME: This looks strange! Flow Control */ -	DM9000_iow(DM9000_SMCR, 0);	/* Special Mode */ -	DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);	/* clear TX status */ -	DM9000_iow(DM9000_ISR, 0x0f);	/* Clear interrupt status */ +	/* Program operating register, only intern phy supported by now */ +	DM9000_iow(DM9000_NCR, 0x0); +	/* TX Polling clear */ +	DM9000_iow(DM9000_TCR, 0); +	/* Less 3Kb, 200us */ +	DM9000_iow(DM9000_BPTR, 0x3f); +	/* Flow Control : High/Low Water */ +	DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); +	/* SH FIXME: This looks strange! Flow Control */ +	DM9000_iow(DM9000_FCR, 0x0); +	/* Special Mode */ +	DM9000_iow(DM9000_SMCR, 0); +	/* clear TX status */ +	DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); +	/* Clear interrupt status */ +	DM9000_iow(DM9000_ISR, 0x0f);  	/* Set Node address */  #ifndef CONFIG_AT91SAM9261EK @@ -333,8 +505,11 @@ eth_init(bd_t * bd)  	DM9000_DBG("\n");  	/* Activate DM9000 */ -	DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);	/* RX enable */ -	DM9000_iow(DM9000_IMR, IMR_PAR);	/* Enable TX/RX interrupt mask */ +	/* RX enable */ +	DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); +	/* Enable TX/RX interrupt mask */ +	DM9000_iow(DM9000_IMR, IMR_PAR); +  	i = 0;  	while (!(phy_read(1) & 0x20)) {	/* autonegation complete bit */  		udelay(1000); @@ -376,54 +551,37 @@ eth_init(bd_t * bd)  int  eth_send(volatile void *packet, int length)  { -	char *data_ptr; -	u32 tmplen, i;  	int tmo; -	DM9000_DBG("eth_send: length: %d\n", length); -	for (i = 0; i < length; i++) { -		if (i % 8 == 0) -			DM9000_DBG("\nSend: 02x: ", i); -		DM9000_DBG("%02x ", ((unsigned char *) packet)[i]); -	} DM9000_DBG("\n"); +	struct board_info *db = &dm9000_info; -	/* Move data to DM9000 TX RAM */ -	data_ptr = (char *) packet; -	DM9000_outb(DM9000_MWCMD, DM9000_IO); +	DM9000_DMP_PACKET("eth_send", packet, length); -#ifdef CONFIG_DM9000_USE_8BIT -	/* Byte mode */ -	for (i = 0; i < length; i++) -		DM9000_outb((data_ptr[i] & 0xff), DM9000_DATA); +	DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_16BIT -	tmplen = (length + 1) / 2; -	for (i = 0; i < tmplen; i++) -		DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA); - -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_32BIT -	tmplen = (length + 3) / 4; -	for (i = 0; i < tmplen; i++) -		DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA); +	/* Move data to DM9000 TX RAM */ +	DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */ -#endif				/*  */ +	/* push the data to the TX-fifo */ +	(db->outblk)(packet, length);  	/* Set TX length to DM9000 */  	DM9000_iow(DM9000_TXPLL, length & 0xff);  	DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);  	/* Issue TX polling command */ -	DM9000_iow(DM9000_TCR, TCR_TXREQ);	/* Cleared after TX complete */ +	DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */  	/* wait for end of transmission */  	tmo = get_timer(0) + 5 * CFG_HZ; -	while (DM9000_ior(DM9000_TCR) & TCR_TXREQ) { +	while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) || +		!(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {  		if (get_timer(0) >= tmo) {  			printf("transmission timeout\n");  			break;  		}  	} +	DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ +  	DM9000_DBG("transmit done\n\n");  	return 0;  } @@ -452,86 +610,67 @@ eth_rx(void)  {  	u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0];  	u16 RxStatus, RxLen = 0; -	u32 tmplen, i; -#ifdef CONFIG_DM9000_USE_32BIT -	u32 tmpdata; -#endif +	struct board_info *db = &dm9000_info; -	/* Check packet ready or not */ -	DM9000_ior(DM9000_MRCMDX);	/* Dummy read */ -	rxbyte = DM9000_inb(DM9000_DATA);	/* Got most updated data */ -	if (rxbyte == 0) +	/* Check packet ready or not, we must check +	   the ISR status first for DM9000A */ +	if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */  		return 0; -	/* Status check: this byte must be 0 or 1 */ -	if (rxbyte > 1) { -		DM9000_iow(DM9000_RCR, 0x00);	/* Stop Device */ -		DM9000_iow(DM9000_ISR, 0x80);	/* Stop INT request */ -		DM9000_DBG("rx status check: %d\n", rxbyte); -	} -	DM9000_DBG("receiving packet\n"); +	DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */ -	/* A packet ready now  & Get status/length */ -	DM9000_outb(DM9000_MRCMD, DM9000_IO); +	/* There is _at least_ 1 package in the fifo, read them all */ +	for (;;) { +		DM9000_ior(DM9000_MRCMDX);	/* Dummy read */ + +		/* Get most updated data, +		   only look at bits 0:1, See application notes DM9000 */ +		rxbyte = DM9000_inb(DM9000_DATA) & 0x03; -#ifdef CONFIG_DM9000_USE_8BIT -	RxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8); -	RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8); +		/* Status check: this byte must be 0 or 1 */ +		if (rxbyte > DM9000_PKT_RDY) { +			DM9000_iow(DM9000_RCR, 0x00);	/* Stop Device */ +			DM9000_iow(DM9000_ISR, 0x80);	/* Stop INT request */ +			printf("DM9000 error: status check fail: 0x%x\n", +				rxbyte); +			return 0; +		} -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_16BIT -	RxStatus = DM9000_inw(DM9000_DATA); -	RxLen = DM9000_inw(DM9000_DATA); +		if (rxbyte != DM9000_PKT_RDY) +			return 0; /* No packet received, ignore */ -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_32BIT -	tmpdata = DM9000_inl(DM9000_DATA); -	RxStatus = tmpdata; -	RxLen = tmpdata >> 16; +		DM9000_DBG("receiving packet\n"); -#endif				/*  */ -	DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); +		/* A packet ready now  & Get status/length */ +		(db->rx_status)(&RxStatus, &RxLen); -	/* Move data from DM9000 */ -	/* Read received packet from RX SRAM */ -#ifdef CONFIG_DM9000_USE_8BIT -	for (i = 0; i < RxLen; i++) -		rdptr[i] = DM9000_inb(DM9000_DATA); +		DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_16BIT -	tmplen = (RxLen + 1) / 2; -	for (i = 0; i < tmplen; i++) -		((u16 *) rdptr)[i] = DM9000_inw(DM9000_DATA); +		/* Move data from DM9000 */ +		/* Read received packet from RX SRAM */ +		(db->inblk)(rdptr, RxLen); -#endif				/*  */ -#ifdef CONFIG_DM9000_USE_32BIT -	tmplen = (RxLen + 3) / 4; -	for (i = 0; i < tmplen; i++) -		((u32 *) rdptr)[i] = DM9000_inl(DM9000_DATA); +		if ((RxStatus & 0xbf00) || (RxLen < 0x40) +			|| (RxLen > DM9000_PKT_MAX)) { +			if (RxStatus & 0x100) { +				printf("rx fifo error\n"); +			} +			if (RxStatus & 0x200) { +				printf("rx crc error\n"); +			} +			if (RxStatus & 0x8000) { +				printf("rx length error\n"); +			} +			if (RxLen > DM9000_PKT_MAX) { +				printf("rx length too big\n"); +				dm9000_reset(); +			} +		} else { +			DM9000_DMP_PACKET("eth_rx", rdptr, RxLen); -#endif				/*  */ -	if ((RxStatus & 0xbf00) || (RxLen < 0x40) -	    || (RxLen > DM9000_PKT_MAX)) { -		if (RxStatus & 0x100) { -			printf("rx fifo error\n"); +			DM9000_DBG("passing packet to upper layer\n"); +			NetReceive(NetRxPackets[0], RxLen);  		} -		if (RxStatus & 0x200) { -			printf("rx crc error\n"); -		} -		if (RxStatus & 0x8000) { -			printf("rx length error\n"); -		} -		if (RxLen > DM9000_PKT_MAX) { -			printf("rx length too big\n"); -			dm9000_reset(); -		} -	} else { - -		/* Pass to upper layer */ -		DM9000_DBG("passing packet to upper layer\n"); -		NetReceive(NetRxPackets[0], RxLen); -		return RxLen;  	}  	return 0;  } @@ -592,12 +731,12 @@ phy_read(int reg)  	/* Fill the phyxcer register into REG_0C */  	DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);  	DM9000_iow(DM9000_EPCR, 0xc);	/* Issue phyxcer read command */ -	udelay(100);		/* Wait read complete */ +	udelay(100);			/* Wait read complete */  	DM9000_iow(DM9000_EPCR, 0x0);	/* Clear phyxcer read command */  	val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);  	/* The read data keeps on REG_0D & REG_0E */ -	DM9000_DBG("phy_read(%d): %d\n", reg, val); +	DM9000_DBG("phy_read(0x%x): 0x%x\n", reg, val);  	return val;  } @@ -615,8 +754,7 @@ phy_write(int reg, u16 value)  	DM9000_iow(DM9000_EPDRL, (value & 0xff));  	DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff));  	DM9000_iow(DM9000_EPCR, 0xa);	/* Issue phyxcer write command */ -	udelay(500);		/* Wait write complete */ +	udelay(500);			/* Wait write complete */  	DM9000_iow(DM9000_EPCR, 0x0);	/* Clear phyxcer write command */ -	DM9000_DBG("phy_write(reg:%d, value:%d)\n", reg, value); +	DM9000_DBG("phy_write(reg:0x%x, value:0x%x)\n", reg, value);  } -#endif				/* CONFIG_DRIVER_DM9000 */ |