diff options
Diffstat (limited to 'drivers/net/plb2800_eth.c')
| -rw-r--r-- | drivers/net/plb2800_eth.c | 396 | 
1 files changed, 396 insertions, 0 deletions
| diff --git a/drivers/net/plb2800_eth.c b/drivers/net/plb2800_eth.c new file mode 100644 index 000000000..0ae5d808a --- /dev/null +++ b/drivers/net/plb2800_eth.c @@ -0,0 +1,396 @@ +/* + * PLB2800 internal switch ethernet driver. + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#include <common.h> + +#if defined(CONFIG_CMD_NET) \ +	&& defined(CONFIG_NET_MULTI) && defined(CONFIG_PLB2800_ETHER) + +#include <malloc.h> +#include <net.h> +#include <asm/addrspace.h> + + +#define NUM_RX_DESC	PKTBUFSRX +#define TOUT_LOOP	1000000 + +#define LONG_REF(addr) (*((volatile unsigned long*)addr)) + +#define CMAC_CRX_CTRL	LONG_REF(0xb800c870) +#define CMAC_CTX_CTRL	LONG_REF(0xb800c874) +#define SYS_MAC_ADDR_0	LONG_REF(0xb800c878) +#define SYS_MAC_ADDR_1	LONG_REF(0xb800c87c) +#define MIPS_H_MASK	LONG_REF(0xB800C810) + +#define MA_LEARN	LONG_REF(0xb8008004) +#define DA_LOOKUP	LONG_REF(0xb8008008) + +#define CMAC_CRX_CTRL_PD	0x00000001 +#define CMAC_CRX_CTRL_CG	0x00000002 +#define CMAC_CRX_CTRL_PL_SHIFT	2 +#define CMAC_CRIT		0x0 +#define CMAC_NON_CRIT		0x1 +#define MBOX_STAT_ID_SHF	28 +#define MBOX_STAT_CP		0x80000000 +#define MBOX_STAT_MB		0x00000001 +#define EN_MA_LEARN		0x02000000 +#define EN_DA_LKUP		0x01000000 +#define MA_DEST_SHF		11 +#define DA_DEST_SHF		11 +#define DA_STATE_SHF		19 +#define TSTAMP_MS		0x00000000 +#define SW_H_MBOX4_MASK		0x08000000 +#define SW_H_MBOX3_MASK		0x04000000 +#define SW_H_MBOX2_MASK		0x02000000 +#define SW_H_MBOX1_MASK		0x01000000 + +typedef volatile struct { +  unsigned int stat; +  unsigned int cmd; +  unsigned int cnt; +  unsigned int adr; +} mailbox_t; + +#define MBOX_REG(mb) ((mailbox_t*)(0xb800c830+(mb<<4))) + +typedef volatile struct { +  unsigned int word0; +  unsigned int word1; +  unsigned int word2; +} mbhdr_t; + +#define MBOX_MEM(mb) ((void*)(0xb800a000+((3-mb)<<11))) + + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis); +static int plb2800_eth_send(struct eth_device *dev, volatile void *packet, +						  int length); +static int plb2800_eth_recv(struct eth_device *dev); +static void plb2800_eth_halt(struct eth_device *dev); + +static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr); +static unsigned char * plb2800_get_mac_addr(void); + +static int rx_new; +static int mac_addr_set = 0; + + +int plb2800_eth_initialize(bd_t * bis) +{ +	struct eth_device *dev; +	ulong temp; + +#ifdef DEBUG +	printf("Entered plb2800_eth_initialize()\n"); +#endif + +	if (!(dev = (struct eth_device *) malloc (sizeof *dev))) +	{ +		printf("Failed to allocate memory\n"); +		return 0; +	} +	memset(dev, 0, sizeof(*dev)); + +	sprintf(dev->name, "PLB2800 Switch"); +	dev->init = plb2800_eth_init; +	dev->halt = plb2800_eth_halt; +	dev->send = plb2800_eth_send; +	dev->recv = plb2800_eth_recv; + +	eth_register(dev); + +	/* bug fix */ +	*(ulong *)0xb800e800 = 0x838; + +	/* Set MBOX ownership */ +	temp = CMAC_CRIT << MBOX_STAT_ID_SHF; +	MBOX_REG(0)->stat = temp; +	MBOX_REG(1)->stat = temp; + +	temp = CMAC_NON_CRIT << MBOX_STAT_ID_SHF; +	MBOX_REG(2)->stat = temp; +	MBOX_REG(3)->stat = temp; + +	plb2800_set_mac_addr(dev, plb2800_get_mac_addr()); + +	/* Disable all Mbox interrupt */ +	temp = MIPS_H_MASK; +	temp &= ~ (SW_H_MBOX1_MASK | SW_H_MBOX2_MASK | SW_H_MBOX3_MASK | SW_H_MBOX4_MASK) ; +	MIPS_H_MASK = temp; + +#ifdef DEBUG +	printf("Leaving plb2800_eth_initialize()\n"); +#endif + +	return 1; +} + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis) +{ +#ifdef DEBUG +	printf("Entering plb2800_eth_init()\n"); +#endif + +	plb2800_set_mac_addr(dev, dev->enetaddr); + +	rx_new = 0; + +#ifdef DEBUG +	printf("Leaving plb2800_eth_init()\n"); +#endif + +	return 0; +} + + +static int plb2800_eth_send(struct eth_device *dev, volatile void *packet, +						  int length) +{ +	int                    i; +	int                    res         = -1; +	u32                    temp; +	mailbox_t *            mb          = MBOX_REG(0); +	char      *            mem         = MBOX_MEM(0); + +#ifdef DEBUG +	printf("Entered plb2800_eth_send()\n"); +#endif + +	if (length <= 0) +	{ +		printf ("%s: bad packet size: %d\n", dev->name, length); +		goto Done; +	} + +	if (length < 64) +	{ +		length = 64; +	} + +	temp = CMAC_CRX_CTRL_CG | ((length + 4) << CMAC_CRX_CTRL_PL_SHIFT); + +#ifdef DEBUG +	printf("0 mb->stat = 0x%x\n",  mb->stat); +#endif + +	for(i = 0; mb->stat & (MBOX_STAT_CP | MBOX_STAT_MB); i++) +	{ +		if (i >= TOUT_LOOP) +		{ +			printf("%s: tx buffer not ready\n", dev->name); +			printf("1 mb->stat = 0x%x\n",  mb->stat); +			goto Done; +		} +	} + +		/* For some strange reason, memcpy doesn't work, here! +		 */ +	do +	{ +		int words = (length >> 2) + 1; +		unsigned int* dst = (unsigned int*)(mem); +		unsigned int* src = (unsigned int*)(packet); +		for (i = 0; i < words; i++) +		{ +			*dst = *src; +			dst++; +			src++; +		}; +	} while(0); + +	CMAC_CRX_CTRL = temp; +	mb->cmd = MBOX_STAT_CP; + +#ifdef DEBUG +	printf("2 mb->stat = 0x%x\n",  mb->stat); +#endif + +	res = length; +Done: + +#ifdef DEBUG +	printf("Leaving plb2800_eth_send()\n"); +#endif + +	return res; +} + + +static int plb2800_eth_recv(struct eth_device *dev) +{ +	int                    length  = 0; +	mailbox_t            * mbox    = MBOX_REG(3); +	unsigned char        * hdr     = MBOX_MEM(3); +	unsigned int           stat; + +#ifdef DEBUG +	printf("Entered plb2800_eth_recv()\n"); +#endif + +	for (;;) +	{ +		stat = mbox->stat; + +		if (!(stat & MBOX_STAT_CP)) +		{ +			break; +		} + +		length = ((*(hdr + 6) & 0x3f) << 8) + *(hdr + 7); +		memcpy((void *)NetRxPackets[rx_new], hdr + 12, length); + +		stat &= ~MBOX_STAT_CP; +		mbox->stat = stat; +#ifdef DEBUG +		{ +			int i; +			for (i=0;i<length - 4;i++) +			{ +				if (i % 16 == 0) printf("\n%04x: ", i); +				printf("%02X ", NetRxPackets[rx_new][i]); +			} +			printf("\n"); +		} +#endif + +		if (length) +		{ +#ifdef DEBUG +			printf("Received %d bytes\n", length); +#endif +			NetReceive((void*)(NetRxPackets[rx_new]), +			            length - 4); +		} +		else +		{ +#if 1 +			printf("Zero length!!!\n"); +#endif +		} + +		rx_new = (rx_new + 1) % NUM_RX_DESC; +	} + +#ifdef DEBUG +	printf("Leaving plb2800_eth_recv()\n"); +#endif + +	return length; +} + + +static void plb2800_eth_halt(struct eth_device *dev) +{ +#ifdef DEBUG +	printf("Entered plb2800_eth_halt()\n"); +#endif + +#ifdef DEBUG +	printf("Leaving plb2800_eth_halt()\n"); +#endif +} + +static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr) +{ +	char packet[60]; +	ulong temp; +	int ix; + +	if (mac_addr_set || +	    NULL == addr || memcmp(addr, "\0\0\0\0\0\0", 6) == 0) +	{ +		return; +	} + +	/* send one packet through CPU port +	 * in order to learn system MAC address +	 */ + +	/* Set DA_LOOKUP register */ +	temp = EN_MA_LEARN | (0 << DA_STATE_SHF) | (63 << DA_DEST_SHF); +	DA_LOOKUP = temp; + +	/* Set MA_LEARN register */ +	temp = 50 << MA_DEST_SHF; 	/* static entry */ +	MA_LEARN = temp; + +	/* set destination address */ +	for (ix=0;ix<6;ix++) +		packet[ix] = 0xff; + +	/* set source address = system MAC address */ +	for (ix=0;ix<6;ix++) +		packet[6+ix] = addr[ix]; + +	/* set type field */ +	packet[12]=0xaa; +	packet[13]=0x55; + +	/* set data field */ +	for(ix=14;ix<60;ix++) +		packet[ix] = 0x00; + +#ifdef DEBUG +	for (ix=0;ix<6;ix++) +		printf("mac_addr[%d]=%02X\n", ix, (unsigned char)packet[6+ix]); +#endif + +	/* set one packet */ +	plb2800_eth_send(dev, packet, sizeof(packet)); + +	/* delay for a while */ +	for(ix=0;ix<65535;ix++) +		temp = ~temp; + +	/* Set CMAC_CTX_CTRL register */ +	temp = TSTAMP_MS;	/* no autocast */ +	CMAC_CTX_CTRL = temp; + +	/* Set DA_LOOKUP register */ +	temp = EN_DA_LKUP; +	DA_LOOKUP = temp; + +	mac_addr_set = 1; +} + +static unsigned char * plb2800_get_mac_addr(void) +{ +	static unsigned char addr[6]; +	char *tmp, *end; +	int i; + +	tmp = getenv ("ethaddr"); +	if (NULL == tmp) return NULL; + +	for (i=0; i<6; i++) { +		addr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; +		if (tmp) +			tmp = (*end) ? end+1 : end; +	} + +	return addr; +} + +#endif /* CONFIG_PLB2800_ETHER */ |