diff options
Diffstat (limited to 'cpu/mpc85xx/tsec.c')
| -rw-r--r-- | cpu/mpc85xx/tsec.c | 441 | 
1 files changed, 441 insertions, 0 deletions
| diff --git a/cpu/mpc85xx/tsec.c b/cpu/mpc85xx/tsec.c new file mode 100644 index 000000000..4a5731e6a --- /dev/null +++ b/cpu/mpc85xx/tsec.c @@ -0,0 +1,441 @@ +/* + * tsec.c + * Motorola Three Speed Ethernet Controller driver + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * (C) Copyright 2003, Motorola, Inc. + * maintained by Xianghua Xiao (x.xiao@motorola.com) + * author Andy Fleming + * + */ + +#include <config.h> +#include <mpc85xx.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <command.h> + +#if defined(CONFIG_TSEC_ENET) +#include "tsec.h" + +#define TX_BUF_CNT 2 + +#undef TSEC_DEBUG +#ifdef TSEC_DEBUG +#define DBGPRINT(x) printf(x) +#else +#define DBGPRINT(x) +#endif + +static uint rxIdx;	/* index of the current RX buffer */ +static uint txIdx;	/* index of the current TX buffer */ + +typedef volatile struct rtxbd { +	txbd8_t txbd[TX_BUF_CNT]; +	rxbd8_t rxbd[PKTBUFSRX]; +}  RTXBD; + +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +static int tsec_send(struct eth_device* dev, volatile void *packet, int length); +static int tsec_recv(struct eth_device* dev); +static int tsec_init(struct eth_device* dev, bd_t * bd); +static void tsec_halt(struct eth_device* dev); +static void init_registers(tsec_t *regs); +static void startup_tsec(tsec_t *regs); +static void init_phy(tsec_t *regs); + +/* Initialize device structure.  returns 0 on failure, 1 on + * success */ +int tsec_initialize(bd_t *bis) +{ +	struct eth_device* dev; +	int i; + +	dev = (struct eth_device*) malloc(sizeof *dev); + +	if(dev == NULL) +		return 0; + +	memset(dev, 0, sizeof *dev); + +	sprintf(dev->name, "MOTOROLA ETHERNET"); +	dev->iobase = 0; +	dev->priv   = 0; +	dev->init   = tsec_init; +	dev->halt   = tsec_halt; +	dev->send   = tsec_send; +	dev->recv   = tsec_recv; + +	/* Tell u-boot to get the addr from the env */ +	for(i=0;i<6;i++) +		dev->enetaddr[i] = 0; + +	eth_register(dev); + +	return 1; +} + + +/* Initializes data structures and registers for the controller, + * and brings the interface up */ +int tsec_init(struct eth_device* dev, bd_t * bd) +{ +	tsec_t *regs; +	uint tempval; +	char tmpbuf[MAC_ADDR_LEN]; +	int i; + +	regs = (tsec_t *)(TSEC_BASE_ADDR); + +	/* Make sure the controller is stopped */ +	tsec_halt(dev); + +	/* Reset the MAC */ +	regs->maccfg1 |= MACCFG1_SOFT_RESET; + +	/* Clear MACCFG1[Soft_Reset] */ +	regs->maccfg1 &= ~(MACCFG1_SOFT_RESET); + +	/* Init MACCFG2.  Defaults to GMII/MII */ +	regs->maccfg2 = MACCFG2_INIT_SETTINGS; + +	/* Init ECNTRL */ +	regs->ecntrl = ECNTRL_INIT_SETTINGS; + +	/* Copy the station address into the address registers. +	 * Backwards, because little endian MACS are dumb */ +	for(i=0;i<MAC_ADDR_LEN;i++) { +		tmpbuf[MAC_ADDR_LEN - 1 - i] = bd->bi_enetaddr[i]; +	} +	(uint)(regs->macstnaddr1) = *((uint *)(tmpbuf)); + +	tempval = *((uint *)(tmpbuf +4)); + +	(uint)(regs->macstnaddr2) = tempval; + +	/* Initialize the PHY */ +	init_phy(regs); + +	/* reset the indices to zero */ +	rxIdx = 0; +	txIdx = 0; + +	/* Clear out (for the most part) the other registers */ +	init_registers(regs); + +	/* Ready the device for tx/rx */ +	startup_tsec(regs); + +	return 1; + +} + + +/* Reads from the register at offset in the PHY at phyid, */ +/* using the register set defined in regbase.  It waits until the */ +/* bits in the miimstat are valid (miimind notvalid bit cleared), */ +/* and then passes those bits on to the variable specified in */ +/* value */ +/* Before it does the read, it needs to clear the command field */ +uint read_phy_reg(tsec_t *regbase, uint phyid, uint offset) +{ +	uint value; + +	/* Put the address of the phy, and the register number into +	 * MIIMADD +	 */ +	regbase->miimadd = (phyid << 8) | offset; + +	/* Clear the command register, and wait */ +	regbase->miimcom = 0; +	asm("msync"); + +	/* Initiate a read command, and wait */ +	regbase->miimcom = MIIM_READ_COMMAND; +	asm("msync"); + +	/* Wait for the the indication that the read is done */ +	while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))); + +	/* Grab the value read from the PHY */ +	value = regbase->miimstat; + +	return value; +} + +/* Setup the PHY */ +static void init_phy(tsec_t *regs) +{ +	uint testval; +	unsigned int timeout = TSEC_TIMEOUT; + +	/* Assign a Physical address to the TBI */ +	regs->tbipa=TBIPA_VALUE; + +	/* reset the management interface */ +	regs->miimcfg=MIIMCFG_RESET; + +	regs->miimcfg=MIIMCFG_INIT_VALUE; + +	/* Wait until the bus is free */ +	while(regs->miimind & MIIMIND_BUSY); + +#ifdef CONFIG_PHY_CIS8201 +	/* override PHY config settings */ +	write_phy_reg(regs, 0, MIIM_AUX_CONSTAT, MIIM_AUXCONSTAT_INIT); + +	/* Set up interface mode */ +	write_phy_reg(regs, 0, MIIM_EXT_CON1, MIIM_EXTCON1_INIT); +#endif + +	/* Set the PHY to gigabit, full duplex, Auto-negotiate */ +	write_phy_reg(regs, 0, MIIM_CONTROL, MIIM_CONTROL_INIT); + +	/* Wait until TBI_STATUS indicates AN is done */ +	DBGPRINT("Waiting for Auto-negotiation to complete\n"); +	testval=read_phy_reg(regs, 0, MIIM_TBI_STATUS); + +	while((!(testval & MIIM_TBI_STATUS_AN_DONE))&& timeout--) { +		testval=read_phy_reg(regs, 0, MIIM_TBI_STATUS); +	} + +	if(testval & MIIM_TBI_STATUS_AN_DONE) +		DBGPRINT("Auto-negotiation done\n"); +	else +		DBGPRINT("Auto-negotiation timed-out.\n"); + +#ifdef CONFIG_PHY_CIS8201 +	/* Find out what duplexity (duplicity?) we have */ +	/* Read it twice to make sure */ +	testval=read_phy_reg(regs, 0, MIIM_AUX_CONSTAT); + +	if(testval & MIIM_AUXCONSTAT_DUPLEX) { +		DBGPRINT("Enet starting in full duplex\n"); +		regs->maccfg2 |= MACCFG2_FULL_DUPLEX; +	} else { +		DBGPRINT("Enet starting in half duplex\n"); +		regs->maccfg2 &= ~MACCFG2_FULL_DUPLEX; +	} + +	/* Also, we look to see what speed we are at +	 * if Gigabit, MACCFG2 goes in GMII, otherwise, +	 * MII mode. +	 */ +	if((testval & MIIM_AUXCONSTAT_SPEED) != MIIM_AUXCONSTAT_GBIT) { +		if((testval & MIIM_AUXCONSTAT_SPEED) == MIIM_AUXCONSTAT_100) +			DBGPRINT("Enet starting in 100BT\n"); +		else +			DBGPRINT("Enet starting in 10BT\n"); + +		/* mark the mode in MACCFG2 */ +		regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | MACCFG2_MII); +	} else { +		DBGPRINT("Enet starting in 1000BT\n"); +	} + +#endif + +#ifdef CONFIG_PHY_M88E1011 +	/* Read the PHY to see what speed and duplex we are */ +	testval=read_phy_reg(regs, 0, MIIM_PHY_STATUS); + +	timeout = TSEC_TIMEOUT; +	while((!(testval & MIIM_PHYSTAT_SPDDONE)) && timeout--) { +		testval = read_phy_reg(regs,0,MIIM_PHY_STATUS); +	} + +	if(!(testval & MIIM_PHYSTAT_SPDDONE)) +		DBGPRINT("Enet: Speed not resolved\n"); + +	testval=read_phy_reg(regs, 0, MIIM_PHY_STATUS); +	if(testval & MIIM_PHYSTAT_DUPLEX) { +		DBGPRINT("Enet starting in Full Duplex\n"); +		regs->maccfg2 |= MACCFG2_FULL_DUPLEX; +	} else { +		DBGPRINT("Enet starting in Half Duplex\n"); +		regs->maccfg2 &= ~MACCFG2_FULL_DUPLEX; +	} + +	if(!((testval&MIIM_PHYSTAT_SPEED) == MIIM_PHYSTAT_GBIT)) { +		if((testval & MIIM_PHYSTAT_SPEED) == MIIM_PHYSTAT_100) +			DBGPRINT("Enet starting in 100BT\n"); +		else +			DBGPRINT("Enet starting in 10BT\n"); + +		regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | MACCFG2_MII); +	} else { +		DBGPRINT("Enet starting in 1000BT\n"); +	} +#endif + +} + + +static void init_registers(tsec_t *regs) +{ +	/* Clear IEVENT */ +	regs->ievent = IEVENT_INIT_CLEAR; + +	regs->imask = IMASK_INIT_CLEAR; + +	regs->hash.iaddr0 = 0; +	regs->hash.iaddr1 = 0; +	regs->hash.iaddr2 = 0; +	regs->hash.iaddr3 = 0; +	regs->hash.iaddr4 = 0; +	regs->hash.iaddr5 = 0; +	regs->hash.iaddr6 = 0; +	regs->hash.iaddr7 = 0; + +	regs->hash.gaddr0 = 0; +	regs->hash.gaddr1 = 0; +	regs->hash.gaddr2 = 0; +	regs->hash.gaddr3 = 0; +	regs->hash.gaddr4 = 0; +	regs->hash.gaddr5 = 0; +	regs->hash.gaddr6 = 0; +	regs->hash.gaddr7 = 0; + +	regs->rctrl = 0x00000000; + +	/* Init RMON mib registers */ +	memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t)); + +	regs->rmon.cam1 = 0xffffffff; +	regs->rmon.cam2 = 0xffffffff; + +	regs->mrblr = MRBLR_INIT_SETTINGS; + +	regs->minflr = MINFLR_INIT_SETTINGS; + +	regs->attr = ATTR_INIT_SETTINGS; +	regs->attreli = ATTRELI_INIT_SETTINGS; + +} + +static void startup_tsec(tsec_t *regs) +{ +	int i; + +	/* Point to the buffer descriptors */ +	regs->tbase = (unsigned int)(&rtx.txbd[txIdx]); +	regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + +	/* Initialize the Rx Buffer descriptors */ +	for (i = 0; i < PKTBUFSRX; i++) { +		rtx.rxbd[i].status = RXBD_EMPTY; +		rtx.rxbd[i].length = 0; +		rtx.rxbd[i].bufPtr = (uint)NetRxPackets[i]; +	} +	rtx.rxbd[PKTBUFSRX -1].status |= RXBD_WRAP; + +	/* Initialize the TX Buffer Descriptors */ +	for(i=0; i<TX_BUF_CNT; i++) { +		rtx.txbd[i].status = 0; +		rtx.txbd[i].length = 0; +		rtx.txbd[i].bufPtr = 0; +	} +	rtx.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP; + +	/* Enable Transmit and Receive */ +	regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN); + +	/* Tell the DMA it is clear to go */ +	regs->dmactrl |= DMACTRL_INIT_SETTINGS; +	regs->tstat = TSTAT_CLEAR_THALT; +	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); +} + +/* This returns the status bits of the device.  The return value + * is never checked, and this is what the 8260 driver did, so we + * do the same.  Presumably, this would be zero if there were no + * errors */ +static int tsec_send(struct eth_device* dev, volatile void *packet, int length) +{ +	int i; +	int result = 0; +	tsec_t * regs = (tsec_t *)(TSEC_BASE_ADDR); + +	/* Find an empty buffer descriptor */ +	for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { +		if (i >= TOUT_LOOP) { +			DBGPRINT("tsec: tx buffers full\n"); +			return result; +		} +	} + +	rtx.txbd[txIdx].bufPtr = (uint)packet; +	rtx.txbd[txIdx].length = length; +	rtx.txbd[txIdx].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT); + +	/* Tell the DMA to go */ +	regs->tstat = TSTAT_CLEAR_THALT; + +	/* Wait for buffer to be transmitted */ +	for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { +		if (i >= TOUT_LOOP) { +			DBGPRINT("tsec: tx error\n"); +			return result; +		} +	} + +	txIdx = (txIdx + 1) % TX_BUF_CNT; +	result = rtx.txbd[txIdx].status & TXBD_STATS; + +	return result; +} + +static int tsec_recv(struct eth_device* dev) +{ +	int length; +	tsec_t *regs = (tsec_t *)(TSEC_BASE_ADDR); + +	while(!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) { + +		length = rtx.rxbd[rxIdx].length; + +		/* Send the packet up if there were no errors */ +		if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) { +			NetReceive(NetRxPackets[rxIdx], length - 4); +		} + +		rtx.rxbd[rxIdx].length = 0; + +		/* Set the wrap bit if this is the last element in the list */ +		rtx.rxbd[rxIdx].status = RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); + +		rxIdx = (rxIdx + 1) % PKTBUFSRX; +	} + +	if(regs->ievent&IEVENT_BSY) { +		regs->ievent = IEVENT_BSY; +		regs->rstat = RSTAT_CLEAR_RHALT; +	} + +	return -1; + +} + + +static void tsec_halt(struct eth_device* dev) +{ +	tsec_t *regs = (tsec_t *)(TSEC_BASE_ADDR); + +	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); +	regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS); + +	while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))); + +	regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN); + +} +#endif /* CONFIG_TSEC_ENET */ |