upstream u-boot with additional patches for our devices/boards:
https://lists.denx.de/pipermail/u-boot/2017-March/282789.html (AXP crashes) ;
Gbit ethernet patch for some LIME2 revisions ;
with SPI flash support
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
524 lines
12 KiB
524 lines
12 KiB
/*
|
|
* 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);
|
|
uint read_phy_reg(tsec_t *regbase, uint phyid, uint offset);
|
|
|
|
static int phy_id = -1;
|
|
|
|
/* Initialize device structure. returns 0 on failure, 1 on
|
|
* success */
|
|
int tsec_initialize(bd_t *bis)
|
|
{
|
|
struct eth_device* dev;
|
|
int i;
|
|
tsec_t *regs = (tsec_t *)(TSEC_BASE_ADDR);
|
|
|
|
dev = (struct eth_device*) malloc(sizeof *dev);
|
|
|
|
if(dev == NULL)
|
|
return 0;
|
|
|
|
memset(dev, 0, sizeof *dev);
|
|
|
|
sprintf(dev->name, "MOTO 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);
|
|
|
|
/* Reconfigure the PHY to advertise everything here
|
|
* so that it works with both gigabit and 10/100 */
|
|
#ifdef CONFIG_PHY_M88E1011
|
|
/* 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);
|
|
|
|
/* Locate PHYs. Skip TBIPA, which we know is 31.
|
|
*/
|
|
for (i=0; i<31; i++) {
|
|
if (read_phy_reg(regs, i, 2) == 0x141) {
|
|
if (phy_id == -1)
|
|
phy_id = i;
|
|
#ifdef TSEC_DEBUG
|
|
printf("Found Marvell PHY at 0x%02x\n", i);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef TSEC_DEBUG
|
|
printf("Using PHY ID 0x%02x\n", phy_id);
|
|
#endif
|
|
write_phy_reg(regs, phy_id, MIIM_CONTROL, MIIM_CONTROL_RESET);
|
|
|
|
RESET_ERRATA(regs, phy_id);
|
|
|
|
/* Configure the PHY to advertise gbit and 10/100 */
|
|
write_phy_reg(regs, phy_id, MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT);
|
|
write_phy_reg(regs, phy_id, MIIM_ANAR, MIIM_ANAR_INIT);
|
|
|
|
/* Reset the PHY so the new settings take effect */
|
|
write_phy_reg(regs, phy_id, MIIM_CONTROL, MIIM_CONTROL_RESET);
|
|
#endif
|
|
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, phy_id, MIIM_CONTROL, MIIM_CONTROL_INIT);
|
|
|
|
/* Wait until STATUS indicates Auto-Negotiation is done */
|
|
DBGPRINT("Waiting for Auto-negotiation to complete\n");
|
|
testval=read_phy_reg(regs, phy_id, MIIM_STATUS);
|
|
|
|
while((!(testval & MIIM_STATUS_AN_DONE))&& timeout--) {
|
|
testval=read_phy_reg(regs, phy_id, MIIM_STATUS);
|
|
}
|
|
|
|
if(testval & MIIM_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, phy_id, 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, phy_id, MIIM_PHY_STATUS);
|
|
|
|
timeout = TSEC_TIMEOUT;
|
|
while((!(testval & MIIM_PHYSTAT_SPDDONE)) && timeout--) {
|
|
testval = read_phy_reg(regs,phy_id,MIIM_PHY_STATUS);
|
|
}
|
|
|
|
if(!(testval & MIIM_PHYSTAT_SPDDONE))
|
|
DBGPRINT("Enet: Speed not resolved\n");
|
|
|
|
testval=read_phy_reg(regs, phy_id, 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);
|
|
|
|
}
|
|
|
|
#ifndef CONFIG_BITBANGMII
|
|
/*
|
|
* Read a MII PHY register.
|
|
*
|
|
* Returns:
|
|
* 0 on success
|
|
*/
|
|
int miiphy_read(unsigned char addr,
|
|
unsigned char reg,
|
|
unsigned short *value)
|
|
{
|
|
tsec_t *regs;
|
|
unsigned short rv;
|
|
|
|
regs = (tsec_t *)(TSEC_BASE_ADDR);
|
|
rv = (unsigned short)read_phy_reg(regs, addr, reg);
|
|
*value = rv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Write a MII PHY register.
|
|
*
|
|
* Returns:
|
|
* 0 on success
|
|
*/
|
|
int miiphy_write(unsigned char addr,
|
|
unsigned char reg,
|
|
unsigned short value)
|
|
{
|
|
tsec_t *regs;
|
|
|
|
regs = (tsec_t *)(TSEC_BASE_ADDR);
|
|
write_phy_reg(regs, addr, reg, value);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BITBANGMII */
|
|
#endif /* CONFIG_TSEC_ENET */
|
|
|