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 00000000000..4a5731e6ae0 --- /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 */ |