* Copyright(c) 2005-2007 SMSC
*
* Use of this source code is subject to the terms of the SMSC Software
* License Agreement (SLA) under which you licensed this software product.
* If you did not accept the terms of the SLA, you are not authorized to use
* this source code.
*
* This code and information is provided as is without warranty of any kind,
* either expressed or implied, including but not limited to the implied
* warranties of merchantability and/or fitness for a particular purpose.
*
* File name : smsc9118.c
* Description : smsc9118 polled driver (non-interrupt driven)
*
* History :
* 09-27-06 MDG v1.0 (First Release)
* modified for ARM platform
* 08-22-07 MDG support entire 118/218/9211 family
* 06-07-08 AH support entire 9210/9220/9221 family
* 08-04-08 AH v1.10 Code cleanup and updates per latest LAN9118 family PAS
* 09-05-24 www.diffview.com 检查提供 check for offer
*----------------------------------------------------------------------------*/
#include <common.h>
#include <command.h>
#include <config.h>
#include "smsc9118.h"
#include <net.h>
#ifdef CONFIG_DRIVER_SMSC9118
//*************************************************************************
// FUNCTION PROTOTYPES
//*************************************************************************
int eth_init(bd_t *bd);
void eth_halt(void);
int eth_rx(void);
int eth_send(volatile void *packet, int length);
extern void *malloc( unsigned ); // <stdlib.h>
extern void free( void * ); // <stdlib.h>
//*************************************************************************
// LOCAL DEFINITIONS AND MACROS
//*************************************************************************
//#define DEBUG
#define GPIO_OUT(val) (*GPIO_CFG = ((*GPIO_CFG & ~GPIO_CFG_GPIOD_MSK) | (val & GPIO_CFG_GPIOD_MSK)))
#define ENET_MAX_MTU PKTSIZE
#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN
#define NUM_RX_BUFF PKTBUFSRX
#define ENET_ADDR_LENGTH 6
#define TX_TIMEOUT_COUNT 30 // waiting for TX_FIFO to drain
//*************************************************************************
// GLOBAL DATA
//*************************************************************************
static const char date_code[] = BUILD_NUMBER;
static char * txbp; // TX buffer pointer (only 1 buffer)
static volatile char * rxbp[PKTBUFSRX]; // Receiver buffer queue (IP layers)
static struct rxQue rxAvlQue[PKTBUFSRX]; // Receive buffer available queue
static int rxNdx = 0; // Current receive buffer index
static int rxNdxIn = 0; // Used for input
static int rxNdxOut = 0; // Used for output to protocol layer
static ushort lastTxTag = 0x0;
static unsigned char macAddr[ENET_ADDR_LENGTH];
// Temp variables
//#ifdef DEBUG
ulong MaxRxFifoSz;
ulong TotalInts = 0;
ulong TotalRXE = 0;
ulong TotalRxPackets = 0;
ulong TotalBytes = 0;
ulong EmptyReads = 0;
ulong RxPacketBuf[400];
ulong SWIntTriggered = FALSE;
ulong TotalRxDrop = 0;
ulong TotalPackets = 0;
ulong TotalWords = 0;
ulong TBLower1, TBLower2;
//#endif
// Temp variables
//*************************************************************************
// EXTERNS
//*************************************************************************
#ifdef DEBUG
extern int use_smsc9118;
#endif
static void lan9118_udelay(unsigned long delta) // Arg is really microseconds
{
const unsigned long start = *FREE_RUN, // Start timing
usec = delta * (25000000/1000000);
// usec adjusted for 25MHz on-chip clock, 1 microsecond (1/1000000) scaling
do {
delta = *FREE_RUN;
if (delta >= start)
delta = (delta - start);
else
delta = (delta - start) + 1; // use 0x100000000, not 0xffffffff
} while (delta < usec);
}
static int MacBusy(int ReqTO)
{
int timeout = ReqTO;
int RetVal = FALSE; // No timeout
while (timeout--) {
if (!(*MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY)) {
goto done;
}
}
RetVal = TRUE; // Timeout
done:
return (RetVal);
}
static ulong
GetMacReg(int Reg)
{
ulong RegVal = 0xffffffff;
if (*MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
LAN9118_WARN("GetMacReg: previous command not complete/n");
goto done;
}
*MAC_CSR_CMD = MAC_RD_CMD(Reg);
DELAY(1);
if (MacBusy(MAC_TIMEOUT) == TRUE) {
LAN9118_WARN("GetMacReg: timeout waiting for response "
"from MAC/n");
goto done;
}
RegVal = *MAC_CSR_DATA;
done:
return (RegVal);
}
static int
PhyBusy(int ReqTO)
{
int timeout = ReqTO;
int RetVal = FALSE; // No timeout
while (timeout--) {
if (!(GetMacReg(MAC_MIIACC) & MAC_MIIACC_MII_BUSY)) {
goto done;
}
}
RetVal = TRUE; // Timeout
done:
return (RetVal);
}
static int
SetMacReg(int Reg, ulong Value)
{
int RetVal = FALSE;
if (*MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
LAN9118_WARN("SetMacReg: previous command not complete/n");
goto done;
}
*MAC_CSR_DATA = Value;
DELAY(1);
*MAC_CSR_CMD = MAC_WR_CMD(Reg);
DELAY(1);
if (MacBusy(MAC_TIMEOUT) == TRUE) {
LAN9118_WARN("SetMacReg: timeout waiting for response "
"from MAC/n");
goto done;
}
RetVal = TRUE;
done:
return (RetVal);
}
static ushort
GetPhyReg(unchar Reg)
{
ushort RegVal = 0xffff;
if (GetMacReg(MAC_MIIACC) & MAC_MIIACC_MII_BUSY) {
LAN9118_WARN("GetPhyReg: MII busy/n");
RegVal = 0;
goto done;
}
SetMacReg(MAC_MIIACC, MAC_MII_RD_CMD((unchar)PHY_ADDR, Reg));
DELAY(1);
if (PhyBusy(PHY_TIMEOUT) == TRUE) {
LAN9118_WARN("GetPhyReg: timeout waiting for MII command/n");
goto done;
}
RegVal = (ushort)GetMacReg(MAC_MIIDATA);
done:
return (RegVal);
}
static int
SetPhyReg(unchar Reg, ushort Value)
{
int RetVal = FALSE;
if (GetMacReg(MAC_MIIACC) & MAC_MIIACC_MII_BUSY) {
LAN9118_WARN("SetPhyReg: MII busy/n");
goto done;
}
SetMacReg(MAC_MIIDATA, Value);
DELAY(1);
SetMacReg(MAC_MIIACC, MAC_MII_WR_CMD((unchar)PHY_ADDR, Reg));
DELAY(1);
if (PhyBusy(PHY_TIMEOUT) == TRUE) {
LAN9118_WARN("SetPhyReg: timeout waiting for MII command/n");
goto done;
}
RetVal = TRUE;
done:
return (RetVal);
}
// Display directly accessed, Control/Status Registers
static int
DumpCsrRegs(void)
{
printf("ID_REV:/t/t0x%0.8x/n", *ID_REV);
printf("IRQ_CFG:/t0x%0.8x/n", *IRQ_CFG);
printf("INT_STS:/t0x%0.8x/n", *INT_STS);
printf("INT_EN:/t/t0x%0.8x/n", *INT_EN);
printf("BYTE_TEST:/t0x%0.8x/n", *BYTE_TEST);
printf("FIFO_INT:/t0x%0.8x/n", *FIFO_INT);
printf("RX_CFG:/t/t0x%0.8x/n", *RX_CFG);
printf("TX_CFG:/t/t0x%0.8x/n", *TX_CFG);
printf("HW_CFG:/t/t0x%0.8x/n", *HW_CFG);
printf("RX_DP_CTL:/t0x%0.8x/n", *RX_DP_CTL);
printf("RX_FIFO_INF:/t0x%0.8x/n", *RX_FIFO_INF);
printf("TX_FIFO_INF:/t0x%0.8x/n", *TX_FIFO_INF);
printf("PWR_MGMT:/t0x%0.8x/n", *PWR_MGMT);
printf("GPIO_CFG:/t0x%0.8x/n", *GPIO_CFG);
printf("GPT_CFG:/t0x%0.8x/n", *GPT_CFG);
printf("GPT_CNT:/t0x%0.8x/n", *GPT_CNT);
printf("FPGA_REV:/t0x%0.8x/n", *FPGA_REV);
printf("ENDIAN:/t/t0x%0.8x/n", *ENDIAN);
printf("FREE_RUN/t0x%0.8x/n", *FREE_RUN);
printf("RX_DROP/t/t0x%0.8x/n", *RX_DROP);
printf("MAC_CSR_CMD/t0x%0.8x/n", *MAC_CSR_CMD);
printf("MAC_CSR_DATA/t0x%0.8x/n", *MAC_CSR_DATA);
printf("AFC_CFG/t/t0x%0.8x/n", *AFC_CFG);
return (0);
}
// Display Media Access Controller Registers
static int
DumpMacRegs(void)
{
printf("MAC_CR/t/t0x%0.8x/n", GetMacReg(MAC_CR));
printf("MAC_ADDRH/t0x%0.8x/n", GetMacReg(MAC_ADDRH));
printf("MAC_ADDRL/t0x%0.8x/n", GetMacReg(MAC_ADDRL));
printf("MAC_HASHH/t0x%0.8x/n", GetMacReg(MAC_HASHH));
printf("MAC_HASHL/t0x%0.8x/n", GetMacReg(MAC_HASHL));
printf("MAC_MIIACC/t0x%0.8x/n", GetMacReg(MAC_MIIACC));
printf("MAC_MIIDATA/t0x%0.8x/n", GetMacReg(MAC_MIIDATA));
printf("MAC_FLOW/t0x%0.8x/n", GetMacReg(MAC_FLOW));
printf("MAC_VLAN1/t0x%0.8x/n", GetMacReg(MAC_VLAN1));
printf("MAC_VLAN2/t0x%0.8x/n", GetMacReg(MAC_VLAN2));
printf("MAC_WUFF/t0x%0.8x/n", GetMacReg(MAC_WUFF));
printf("MAC_WUCSR/t0x%0.8x/n", GetMacReg(MAC_WUCSR));
return (0);
}
// Display PHYsical media interface registers
static int
DumpPhyRegs(void)
{
printf("PHY_BCR/t/t0x%0.4x/n", GetPhyReg(PHY_BCR));
printf("PHY_BSR/t/t0x%0.4x/n", GetPhyReg(PHY_BSR));
printf("PHY_ID1/t/t0x%0.4x/n", GetPhyReg(PHY_ID1));
printf("PHY_ID2/t/t0x%0.4x/n", GetPhyReg(PHY_ID2));
printf("PHY_ANAR/t0x%0.4x/n", GetPhyReg(PHY_ANAR));
printf("PHY_ANLPAR/t0x%0.4x/n", GetPhyReg(PHY_ANLPAR));
printf("PHY_ANEXPR/t0x%0.4x/n", GetPhyReg(PHY_ANEXPR));
printf("PHY_SILREV/t0x%0.4x/n", GetPhyReg(PHY_SILREV));
printf("PHY_MCSR/t0x%0.4x/n", GetPhyReg(PHY_MCSR));
printf("PHY_SPMODES/t0x%0.4x/n", GetPhyReg(PHY_SPMODES));
printf("PHY_CSIR/t0x%0.4x/n", GetPhyReg(PHY_CSIR));
printf("PHY_ISR/t/t0x%0.4x/n", GetPhyReg(PHY_ISR));
printf("PHY_IMR/t/t0x%0.4x/n", GetPhyReg(PHY_IMR));
printf("PHY_PHYSCSR/t0x%0.4x/n", GetPhyReg(PHY_PHYSCSR));
return (0);
}
static int
lan9118_open(bd_t *bis)
{
int RetVal = TRUE;
int timeout;
int i;
printf("DRIVER_VERSION : %X, ", DRIVER_VERSION);
printf("DATECODE : %s/r/n", BUILD_NUMBER);
#ifdef DEBUG
TotalInts = 0;
TotalRXE = 0;
TotalBytes = 0;
if (bis->bi_bootflags & 0x40000000) {
use_smsc9118 = 1;
}
#endif //DEBUG
// Because we just came out of h/w reset we can't be sure that
// the chip has completed reset and may have to implement the
// workaround for Errata 5, stepping A0. Therefore we need to
// check the ID_REV in little endian, the reset default.
switch (*ID_REV & ID_REV_ID_MASK) {
case 0x01180000:
case 0x01170000:
case 0x01160000:
case 0x01150000:
case 0x118a0000:
case 0x117a0000:
case 0x116a0000:
case 0x115a0000:
case 0x92100000:
case 0x92110000:
case 0x92200000:
case 0x92210000:
printf("LAN9x18 (0x%08x) detected./n", *ID_REV);
break;
default:
printf("Failed to detect LAN9118. ID_REV = 0x%08x/n", *ID_REV);
RetVal = FALSE;
goto done;
}
// Does SoftReset to 118
*HW_CFG = HW_CFG_SRST;
DELAY(10);
// Is the internal PHY running?
if ((*PWR_MGMT & PWR_MGMT_PM_MODE_MSK) != 0) {
// Apparently not...
*BYTE_TEST = 0x0; // Wake it up
DELAY(1);
timeout = PHY_TIMEOUT;
while (timeout-- && ((*PWR_MGMT & PWR_MGMT_PME_READY) == 0)) {
lan9118_udelay(1);
}
if ((*PWR_MGMT & PWR_MGMT_PME_READY) == 0) {
LAN9118_WARN("LAN9118: PHY not ready");
LAN9118_WARN(" - aborting/n");
RetVal = FALSE;
goto done;
}
}
// Setup TX and RX resources.
// There is one TX buffer.
if ((txbp = (char *)malloc(ENET_MAX_MTU_ALIGNED)) == NULL) {
LAN9118_WARN("lan9118_open: can't get TX buffer/n");
goto cleanup;
}
// The receive buffers are allocated and aligned by upper layer
// software.
for (i = 0; i < PKTBUFSRX; i++) {
rxbp[i] = NetRxPackets[i];
rxAvlQue[i].index = -1;
}
rxNdx = 0;
rxNdxIn = 0;
rxNdxOut = 0;
lastTxTag = 0x0;
// Set TX Fifo Size
*HW_CFG = 0x00040000; // 4K for TX
// This value is dependent on TX Fifo Size since there's a limited
// amount of Fifo space.
MaxRxFifoSz = 13440; // Decimal
// Set automatic flow control.
*AFC_CFG = 0x008c46af;
// Flash LEDs.
*GPIO_CFG = 0x70700000;
// Disable interrupts until the rest of initialization is complete.
*INT_EN = 0x0; // Clear interrupt enables
*INT_STS = 0xffffffff; // Clear pending interrupts
*IRQ_CFG = 0x00000001; // IRQ disable
// Enable flow control and pause frame time
SetMacReg(MAC_FLOW, 0xffff0002);
// Set MAC address, if octet 0 is non-null assume it's all good.
{
unsigned mac_addrh;
unsigned mac_addrl;
memcpy(macAddr, bis->bi_enetaddr, ENET_ADDR_LENGTH);
mac_addrh = macAddr[5] << 8 | macAddr[4];
mac_addrl = macAddr[3] << 24 | macAddr[2] << 16 |
macAddr[1] << 8 | macAddr[0];
if (mac_addrh != 0 || mac_addrl != 0) {
SetMacReg(MAC_ADDRH, mac_addrh);
SetMacReg(MAC_ADDRL, mac_addrl);
}
}
// Dump old status and data
*TX_CFG = (TX_CFG_TXS_DUMP | TX_CFG_TXD_DUMP);
*RX_CFG = (RX_CFG_FORCE_DISCARD);
// Initialize Tx parameters
*HW_CFG = ((*HW_CFG & HW_CFG_TX_FIF_SZ_MSK) | HW_CFG_MBO);
*FIFO_INT = FIFO_INT_TDAL_MSK; // Max out value
*INT_EN |= INT_EN_TDFA_INT_EN;
{
// Disable MAC heartbeat SQE and enable MAC transmitter
ulong macCR = GetMacReg(MAC_CR);
macCR |= (MAC_CR_TXEN | MAC_CR_HBDIS);
macCR &= ~MAC_CR_PRMS; // Turn off promiscuous mode
macCR |= MAC_CR_BCAST; // Don't accept broadcast frames
SetMacReg(MAC_CR, macCR);
}
// Initialize Rx parameters
*RX_CFG = 0x00000000; // 4byte end-alignment
{
// Enable receiver.
ulong macCR = GetMacReg(MAC_CR);
SetMacReg(MAC_CR, (macCR | MAC_CR_RXEN));
}
*FIFO_INT = ((*FIFO_INT & 0xffff0000) | 0x00000001);
*INT_EN |= (INT_EN_RSFL_INT_EN | INT_EN_RXE_INT_EN);
*INT_EN |= INT_EN_RXDFH_INT_EN;
// Initialize PHY parameters
if (((GetPhyReg(PHY_ID1) == PHY_ID1_LAN9118) &&
(GetPhyReg(PHY_ID2) == PHY_ID2_LAN9118)) ||
((GetPhyReg(PHY_ID1) == PHY_ID1_LAN9218) &&
(GetPhyReg(PHY_ID2) == PHY_ID2_LAN9218)))
{
// Reset the PHY
SetPhyReg(PHY_BCR, PHY_BCR_RST);
timeout = PHY_TIMEOUT;
lan9118_udelay(50*1000); // > 50ms
while(timeout-- && (GetPhyReg(PHY_BCR) & PHY_BCR_RST))
{
lan9118_udelay(10);
}
if (timeout == 0)
{
LAN9118_WARN("PHY reset incomplete/n");
RetVal = FALSE;
goto done;
}
// Setup and start auto negotiation
{
ushort anar;
ushort bcr;
char * spddplx;
anar = GetPhyReg(PHY_ANAR);
anar &= ~PHY_ANAR_PAUSE_OP_MSK;
anar |= PHY_ANAR_PAUSE_OP_BOTH;
anar |= (PHY_ANAR_10_FDPLX | PHY_ANAR_10_ABLE |
PHY_ANAR_100_TX_FDPLX | PHY_ANAR_100_TX_ABLE);
SetPhyReg(PHY_ANAR, anar);
DELAY(2);
bcr = GetPhyReg(PHY_BCR);
bcr |= (PHY_BCR_SS | PHY_BCR_FDPLX);
SetPhyReg(PHY_BCR, bcr);
DELAY(2);
printf("start Auto negotiation... (take ~2sec)/n");
bcr = GetPhyReg(PHY_BCR);
bcr |= (PHY_BCR_ANE | PHY_BCR_RSTAN);
SetPhyReg(PHY_BCR, bcr);
DELAY(2);
timeout = PHY_AN_TIMEOUT;
while((timeout--) && ((GetPhyReg(PHY_BSR) & PHY_BSR_ANC) == 0)) {
lan9118_udelay(500000);
}
if ((GetPhyReg(PHY_BSR) & PHY_BSR_ANC) == 0) {
LAN9118_WARN("Auto negotiation failed/n");
RetVal = FALSE;
goto done;
}
if ((GetPhyReg(PHY_BSR) & PHY_BSR_LINK_STATUS) == 0) {
LAN9118_WARN("Link down/n");
RetVal = FALSE;
goto done;
}
switch ((GetPhyReg(PHY_PHYSCSR) & PHY_PHYSCSR_SPEED_MSK)>>2) {
case 0x01:
spddplx = "10BaseT, half duplex";
break;
case 0x02:
spddplx = "100BaseTX, half duplex";
break;
case 0x05:
spddplx = "10BaseT, full duplex";
break;
case 0x06:
spddplx = "100BaseTX, full duplex";
break;
default:
spddplx = "Unknown";
break;
}
printf("Auto negotiation complete, %s/n", spddplx);
}
// If PHYs auto negotiated for full duplex, enable full duplex in MAC.
if ((GetPhyReg(PHY_ANAR) & GetPhyReg(PHY_ANLPAR)) & 0x0140) {
SetMacReg(MAC_CR, (GetMacReg(MAC_CR) | 0x00100000));
}
// correct PHY_ID is detected
goto done;
}
else
{
printf("Unknown PHY ID : 0x%x, 0x%x/n", GetPhyReg(PHY_ID1), GetPhyReg(PHY_ID2));
}
goto done;
cleanup:
if (txbp != NULL) {
free(txbp);
txbp = (char *)NULL;
}
done:
return (RetVal);
}
static void
lan9118_close(void)
{
// Release the TX buffer.
if (txbp != NULL) {
free(txbp);
txbp = (char *)NULL;
}
}
static int
lan9118_read()
{
int curBufNdx;
int loopCount = 0;
ulong rxStatus;
ulong count;
ulong len;
int ffwdOk = TRUE;
int timeout;
int handled = 0;
while((*RX_FIFO_INF & 0x00ff0000) != 0) {
if (loopCount >= NUM_RX_BUFF) {
//printf("read: loopCount exceeded/n");
break; // Packet buffers full
}
curBufNdx = rxNdx;
loopCount++;
if (++rxNdx >= NUM_RX_BUFF) {
rxNdx = 0; // Wrap buffer slot #
}
rxStatus = *RX_STATUS_FIFO_PORT;
len = count = rxStatus >> 16;
if (count >= 4*sizeof(ulong)) {
ffwdOk = TRUE; // Use h/w to toss packet
} else {
ffwdOk = FALSE; // Have to empty manually on error
}
if (count != 0) {
if (count > ENET_MAX_MTU) {
count = 0;
} else {
if ((rxStatus & TX_STATUS_FIFO_ES) != 0) {
count = 0;
}
}
}
if (count == 0) {
if (ffwdOk == TRUE) {
// Drain it the fast way
*RX_DP_CTL = RX_DP_FFWD;
timeout = FFWD_TIMEOUT;
while (timeout-- && (*RX_DP_CTL & RX_DP_FFWD)) {
lan9118_udelay(1);
}
if ((*RX_DP_CTL & RX_DP_FFWD) != 0) {
LAN9118_WARN("lan9118_read: fast "
"forward op failed/n");
break;
}
} else {
// Drain it manually
while (len--) {
volatile ulong tmp = *RX_FIFO_PORT;
}
}
} else if (rxAvlQue[rxNdxIn].index != -1) {
LAN9118_WARN("lan9118_read: read buffers full!/n");
break;
} else {
register ulong *rxbpl;
int ndx;
TotalRxPackets++;
TotalBytes += count;
rxAvlQue[rxNdxIn].index = curBufNdx;
rxAvlQue[rxNdxIn].len = count;
if (++rxNdxIn >= NUM_RX_BUFF) {
rxNdxIn = 0;
}
// Copy this packet to a NetRxPacket buffer
handled = 1;
//printf("read: %d empty reads prior to this one/n", EmptyReads);
EmptyReads = 0;
rxbpl = (ulong *)rxbp[curBufNdx];
for (ndx = (count+3)/sizeof(ulong); ndx > 0; ndx--) {
*rxbpl++ = *RX_FIFO_PORT;
}
#if 0
{
printf("Received: packet contents follows./n");
int i;
for (i = 1; i <= count; i++) {
printf("0x%02x ", rxbp[curBufNdx][i-1]);
if (!(i%16))
printf("/n");
}
printf("/n");
}
#endif
DELAY(3);
}
}
if (handled) {
for (;;) {
curBufNdx = rxAvlQue[rxNdxOut].index;
if (curBufNdx == -1) {
len = -1; // Nothing else received
//printf("read: nothing else received: rxNdxOut: %d curBufNdx: %d/n", rxNdxOut, curBufNdx);
break;
}
len = rxAvlQue[rxNdxOut].len;
//printf("read: sending a packet up: rxNdxOut: %d curBufNdx: %d/n", rxNdxOut, curBufNdx);
NetReceive(NetRxPackets[curBufNdx], len - 4);
rxAvlQue[rxNdxOut].index = -1; // Free buffer
if (++rxNdxOut >= NUM_RX_BUFF) {
rxNdxOut = 0; // Handle wrap
}
}
} else {
EmptyReads++;
return (-1); // Nothing was received
}
return (len);
}
static int sendToNet(uchar * txbp, int len)
{
ulong tx_cmd_a, tx_cmd_b;
int i;
ulong * txbpl = (ulong *)txbp;
lastTxTag++;
#if DEBUG
{
printf("sendToNet: packet contents follows./n");
int i;
int j = 0;
for (i = 0; i < len; i++) {
if (++j == 20) {
j = 0;
printf("/n");
}
printf("%0.1x ", txbp[i]);
}
printf("/n");
// printf("sendToNet: peek TX status: 0x%0.8x/n",
// *TX_STATUS_FIFO_PEEK);
}
#endif // DEBUG
tx_cmd_a = (((ulong)txbp & 0x3) << 16) | 0x00003000 | len;
tx_cmd_b = (lastTxTag << 16) | len;
#if DEBUG
printf("sendToNet: tx_cmd_a: 0x%0.8x tx_cmd_b: 0x%0.8x/n",
tx_cmd_a, tx_cmd_b);
#endif // DEBUG
*TX_FIFO_PORT = tx_cmd_a;
*TX_FIFO_PORT = tx_cmd_b;
for (i = (len+3)/sizeof(ulong); i > 0; i--) {
*TX_FIFO_PORT = *txbpl++;
}
*TX_CFG = TX_CFG_TX_ON; // Enable transmitter
return (TRUE);
}
static int lan9118_write(volatile void *ptr, int len)
{
ulong startTime;
ulong timeout;
char statusStr[64];
if (len > ENET_MAX_MTU) {
len = ENET_MAX_MTU;
}
// Copy the packet.
memcpy((void *)txbp, (void *)ptr, len);
// Drain the TX status fifo just in case there are old (good) statuses.
for (timeout=0; timeout<TX_TIMEOUT_COUNT; timeout++)
{
if ((*TX_FIFO_INF & TX_FIFO_TXSUSED_MSK) == 0) {
break;
}
printf("lan9118_write: discarded old TX status/n");
}
if (timeout == TX_TIMEOUT_COUNT) // timed out? Yes--
{
DumpCsrRegs();
DumpMacRegs();
DumpPhyRegs();
}
if (sendToNet(txbp, len) == FALSE) {
return (-1);
}
//printf("write: sent packet out: len: %d/n", len);
startTime = get_timer(0);
while (1) {
if ((*TX_FIFO_INF & TX_FIFO_TXSUSED_MSK) == 0) {
// No status yet
if ((get_timer(0) - startTime) > TX_TIMEOUT) {
return (-1);
}
} else {
ulong txStatus = *TX_STATUS_FIFO_PORT;
if ((txStatus & TX_STATUS_FIFO_ES) == TX_STATUS_FIFO_ES) {
sprintf(statusStr, "lan9118_write: error "
"status: 0x%0.8x/n", txStatus);
LAN9118_WARN(statusStr);
return (-1);
} else {
*TX_CFG |= TX_CFG_STOP_TX; // Stop transmitter
return (len); // successful send
}
}
}
}
int eth_init(bd_t *bd)
{
return lan9118_open(bd);
}
void eth_halt(void)
{
lan9118_close();
}
int eth_rx(void)
{
int r;
r = lan9118_read();
return r;
}
int eth_send(volatile void *packet, int length)
{
return lan9118_write(packet, length);
}
#endif // #ifdef CONFIG_DRIVER_SMSC9118