📄 smsc9118.c
字号:
// 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_SF);
*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) | 0x00000101);
*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);
}
done:
return (RetVal);
}
static void
lan9118_close(void)
{
// Release the TX buffer.
if (txbp != NULL) {
free(txbp);
}
}
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -