summaryrefslogtreecommitdiff
path: root/cpu/mpc85xx/tsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/mpc85xx/tsec.c')
-rw-r--r--cpu/mpc85xx/tsec.c441
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 */