smsc100.c
来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C语言 代码 · 共 1,011 行 · 第 1/3 页
C
1,011 行
/*++
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.
Copyright (c) 1995-1998 Microsoft Corporation
Module Name:
Abstract:
Routines for the SMSC91C100FD ethernet controller used for the Windows CE
debug ethernet services, and bootloaders.
Functions:
Notes:
These routines are called at system init and other times when system calls aren't allowed, so
they can't make any system calls.
--*/
//
// Note: The implementation sometimes encounters read errors if this routine is optimized.
// The solution for now is to disable optimizations.
//
#pragma optimize("", off)
#include <windows.h>
#include <halether.h>
#include "Smsc100.h"
#include <S100HW.h>
static BYTE volatile *pbEthernetBase=NULL;
static DWORD dwRegisterMultiplier=1;
//
// Macros for reading and writing a register in the SMSC100FD
//
#define ReadWord(wOffset) (*(volatile PUSHORT const)(pbEthernetBase + (wOffset*dwRegisterMultiplier)))
#define ReadDWord(wOffset) (*(volatile PDWORD const)(pbEthernetBase + (wOffset*dwRegisterMultiplier)))
#define WriteWord(wOffset, wValue) (*(volatile PUSHORT const)(pbEthernetBase + (wOffset*dwRegisterMultiplier)) = wValue)
#define WriteDWord(wOffset, dwValue) (*(volatile PDWORD const)(pbEthernetBase + (wOffset*dwRegisterMultiplier)) = dwValue)
// For debugging, if defined, this will cause all Rx/Tx data to be dumped to debug serial port
#undef SMSC_DUMP_FRAMES
#ifdef SMSC_DUMP_FRAMES
static void DumpEtherFrame (BYTE *pFrame, WORD cwFrameLength);
#endif
// These flags are set by the ISR routine when the current Frame being transmitted has completed
// transmission with or without error. They are cleared by the SendFrame () routine before transmission
// begins and are polled until being set by the ISR. Only 1 Frame will be in the send queue
// at any given time.
static UINT16 fTXEMPTY_INT = 0; // Means that the current set of frames to be transmitted have been processed, either with or without error.
static UINT16 fTX_INT = 0; // Means that there was an error when trying to send the current frame.
// This flag indicates when Frames are pending in the receive buffer.
// It is set by the ISR when a IS_RCV_INT is generated, and will be cleared when the GetFrame() routine
// detects that all Frames have been removed from the buffer.
static UINT16 fFrameReceived = 0;
// Mask to use for interrupts
static UINT16 wIntMask = 0;
VOID Smsc100FdWritePhy (USHORT usPhyRegister, USHORT usPhyData);
USHORT Smsc100FdReadPhy (USHORT usPhyRegister);
//
// This is called to initialze the Ethernet driver. The base address of the Ethernet hardware
// is passed into the routine. The routine will return TRUE for a successful initialization.
//
BOOL
Smsc100FdInit (BYTE *pbBaseAddress, DWORD dwMultiplier,USHORT MacAddr[3])
{
DWORD dwStartTime;
USHORT usBSR;
USHORT usPhy;
USHORT usNicTransmitConfig;
if (! pbBaseAddress)
return FALSE;
pbEthernetBase = pbBaseAddress;
dwRegisterMultiplier = dwMultiplier;
//
// The SMSC91C100FD may require as much as 750 usec after reset while the EEPROM information is being loaded.
// Until the initialization from EEPROM is completed, the SMSC91C100FD registers are inaccessible.
//
EdbgOutputDebugString ("+SMSC100FDInit\r\n");
SelectBank (BANK1);
dwStartTime = OEMEthGetSecs ();
while (ReadWord (CONTROL) & (CTL_STORE | CTL_RELOAD) && OEMEthGetSecs () - dwStartTime < 2);
//
// Verify the base address. The upper byte of the BANK_SELECT register always reads 0x33.
//
usBSR = ReadWord (BANK_SELECT);
if ((usBSR & 0xFF00) != BANK0)
{
EdbgOutputDebugString (" SMSC100FDInit: SMSC91C100FD not detected at hardware base address 0x%X, BANK_SELECT: 0x%X\n", pbEthernetBase, usBSR);
return (FALSE);
}
EdbgOutputDebugString (" SMSC100FDInit: SMSC91C100FD detected at hardware base address 0x%X\r\n", pbEthernetBase);
//
// Read the MAC address from the SMSC91C100FD. This should have been read in from the EEPROM during reset.
//
if( NULL != MacAddr ) {
SelectBank (BANK1);
MacAddr[0] = ReadWord (ADDR0);
MacAddr[1] = ReadWord (ADDR1);
MacAddr[2] = ReadWord (ADDR2);
EdbgOutputDebugString (" SMSC100FDInit: Ethernet MAC Address: %B:%B:%B:%B:%B:%B\r\n", MacAddr[0] & 0x00FF, MacAddr[0] >> 8, MacAddr[1] & 0x00FF, MacAddr[1] >> 8, MacAddr[2] & 0x00FF, MacAddr[2] >> 8);
}
// Initialize the control register
SelectBank (BANK1);
WriteWord (CONTROL, CTL_AUTO_RELEASE | CTL_ONE_2 | CTL_TE_ENABLE | CTL_LE_ENABLE);
//
// Initialize the Configuration register
WriteWord (CONFIG, ReadWord (CONFIG) | CFG_MII_SELECT);
// Initialize memory configuration register
SelectBank (BANK0);
WriteWord (MCR, 4); // 4 * 512 bytes reserver for transmitter
// Initialize transmit control register
SelectBank (BANK0);
WriteWord (TCR, TCR_PAD_ENABLE | TCR_ENABLE);
// Initialize interrupt mask register. all interrupts disabled initially.
SelectBank (BANK2);
WriteWord (INTERRUPT_REGISTER, 0);
// The receive register should be the last thing initialzed so that we don't start
// getting Frames before we're ready for them.
SelectBank (BANK0);
WriteWord (RCR, RCR_ENABLE);
// Even if the card is present, it likes to have a second to stabilize before the first transmission.
dwStartTime = OEMEthGetSecs ();
while (OEMEthGetSecs () - dwStartTime < 2);
//
// Reset the PHY.
//
Smsc100FdWritePhy ((USHORT)PHY_CONTROL, (USHORT)PHY_CTRL_RESET);
dwStartTime = OEMEthGetSecs ();
while (OEMEthGetSecs () - dwStartTime < 5)
{
usPhy = Smsc100FdReadPhy (PHY_CONTROL);
if (!(usPhy & PHY_CTRL_RESET))
break;
}
if (usPhy & PHY_CTRL_RESET)
return (FALSE);
//
// Wait for the autonegotiation to complete
//
dwStartTime = OEMEthGetSecs ();
while (OEMEthGetSecs () - dwStartTime < 5)
{
usPhy = Smsc100FdReadPhy (PHY_STATUS);
if (usPhy & PHY_STAT_ANCOMPLETE)
break;
}
//
// Reset the Isolation bit in the PHY, and adjust the TCR based on the PHY autonegotiation.
//
usPhy = Smsc100FdReadPhy (PHY_CONTROL);
Smsc100FdWritePhy ((USHORT)PHY_CONTROL, (USHORT)(usPhy & ~PHY_CTRL_ISOLATION));
usNicTransmitConfig = TCR_PAD_ENABLE | TCR_ENABLE;
if (usPhy & PHY_CTRL_DUPLEX)
usNicTransmitConfig |= TCR_SWFDUP;
SelectBank (BANK0);
WriteWord (TCR, usNicTransmitConfig);
EdbgOutputDebugString ("-SMSC100FDInit\r\n");
return (TRUE);
}
// ISR routine. Three global flags are used to communicate with GetFrame() and SendFrame().
// They are fFrameReceived, fTXEMPTY_INT and fTX_INT and are described at the top of the file.
static void
Smsc100FdInterrupt (void)
{
UINT16 wInterruptReg;
UINT16 wBankSave;
//
// Save and restore the affected registers in the SMSC91C100FD
//
wBankSave = ReadWord (BANK_SELECT);
SelectBank (BANK2);
//
// Get the currently pending interrupts
//
wInterruptReg = ReadWord (INTERRUPT_REGISTER);
// EdbgOutputDebugString ("+Smsc100FdInterrupt: Interrupts pending: 0x%04X\r\n", wInterruptReg);
//
// Disable all interrupts.
//
WriteWord (INTERRUPT_REGISTER, 0);
//
// Check if the receiver has been overrun.
//
if (wInterruptReg & IS_OVRN_INT)
{
EdbgOutputDebugString ("!Smsc100FdInterrupt: Receive Overrun.\r\n");
WriteWord (INTERRUPT_REGISTER, (wInterruptReg & 0xff00) | IS_OVRN_INT);
}
//
// Check if the LINK status has changed.
//
if (wInterruptReg & IS_EPH_INT)
{
USHORT usPhy;
USHORT usNicTransmitConfig;
// EdbgOutputDebugString ("!Smsc100FdInterrupt: EPH Interrupt.\r\n");
usPhy = Smsc100FdReadPhy (PHY_STATUS); // The LINK status is latched low, so read twice to get the current state
usPhy = Smsc100FdReadPhy (PHY_STATUS); // The LINK status is latched low, so read twice to get the current state
if (usPhy & PHY_STAT_LINK)
{
DWORD dwStartTime;
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is UP.\r\n");
//
// Reset the Isolation bit in the PHY, and adjust the TCR based on the PHY autonegotiation.
//
usPhy = Smsc100FdReadPhy (PHY_CONTROL);
Smsc100FdWritePhy ((USHORT)PHY_CONTROL, (USHORT)(usPhy & ~PHY_CTRL_ISOLATION));
//
// Wait for the autonegotiation to complete
//
dwStartTime = OEMEthGetSecs ();
while (OEMEthGetSecs () - dwStartTime < 5)
{
usPhy = Smsc100FdReadPhy (PHY_STATUS);
if (usPhy & PHY_STAT_ANCOMPLETE)
break;
}
usPhy = Smsc100FdReadPhy (PHY_CONTROL);
usNicTransmitConfig = TCR_PAD_ENABLE | TCR_ENABLE;
if (usPhy & PHY_CTRL_DUPLEX)
{
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is FULL Duplex.\r\n");
usNicTransmitConfig |= TCR_SWFDUP;
}
else
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is HALF Duplex.\r\n");
SelectBank (BANK0);
WriteWord (TCR, usNicTransmitConfig);
if (usPhy & PHY_CTRL_SPEED)
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is 100Mb/s.\r\n");
else
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is 10Mb/s.\r\n");
}
else
EdbgOutputDebugString ("!Smsc100FdInterrupt: Link is DOWN.\r\n");
//
// Acknowledge the interrupt to the PHY.
//
Smsc100FdWritePhy (PHY_INTERRUPTACK, 0);
//
// Acknowledge the interrupt to the MAC.
//
SelectBank (BANK1);
WriteWord (CONTROL, ReadWord (CONTROL) & ~CTL_LE_ENABLE); // Turn the LE Enable OFF
WriteWord (CONTROL, ReadWord (CONTROL) | CTL_LE_ENABLE); // Turn the LE Enable ON
//
// Go back to the proper bank.
//
SelectBank (BANK2);
}
// If this interrupt occurs when AUTO RELEASE is selected, it means that the buffer is now empty.
// If an error occured during transmission the IS_TX_INT will have been triggered.
// IS_TX_INT will be generated if an error occured during transmission. If an error occurred in transmission,
// then the untransmitted Frame needs to be deleted from the buffer, which will bee done by the
// Smsc100FdSendFrame routine.
//
if ((wInterruptReg & IS_TXEMPTY_INT) || (wInterruptReg & IS_TX_INT))
{
if (wInterruptReg & IS_TXEMPTY_INT)
fTXEMPTY_INT = 1;
if (wInterruptReg & IS_TX_INT)
fTX_INT = 1;
//
// Clear the interrupt. Note that clearing TXEMPTY_INT here with the buffer empty will prevent
// it from interrupting after a TX_INT error.
//
WriteWord (INTERRUPT_REGISTER, (wInterruptReg & 0xff00) | IS_TX_INT | IS_TXEMPTY_INT);
}
//
// This interrupt indicates that the RX buffer is not empty. It just stays asserted until
// all the Frames are read out of the buffer. One of these frames will be accepted and the fFrameReceived
// flag is set to indicate this to the Smsc100FdGetFrame routine, then, disable the interrupt from here.
// After Smsc100FdGetFrame gets called it will check the buffer and if it is empty it will re-enable the
// interrupt.
//
if (wInterruptReg & IS_RCV_INT)
{
fFrameReceived = 1;
wInterruptReg &= ~IM_RCV_INT;
// EdbgOutputDebugString ("!Smsc100FdInterrupt: Packet Ready to Received.\r\n");
}
//
// Restore the interrupt mask register, with possible modification.
//
WriteWord (INTERRUPT_REGISTER, wInterruptReg & 0xFF00);
SelectBank (wBankSave);
// EdbgOutputDebugString ("-Smsc100FdInterrupt: Interrupts pending: 0x%04X, fTXEMPTY: %u, fTX_INT: %u, fFrameReceived: %u\r\n", ReadWord (INTERRUPT_REGISTER), fTXEMPTY_INT, fTX_INT, fFrameReceived);
}
//
// Interrupts are disabled at initialization.
// Call this function to turn interrupts on.
//
void
Smsc100FdEnableInts ()
{
// EdbgOutputDebugString ("+Smsc100FdEnableInts\r\n");
// Only enable receive interrupts (poll for Tx completion)
wIntMask = IM_RCV_INT;
SelectBank (BANK2);
WriteWord (INTERRUPT_REGISTER, wIntMask);
}
void
Smsc100FdDisableInts ()
{
// EdbgOutputDebugString ("+Smsc100FdDisable\r\n");
wIntMask = 0;
SelectBank (BANK2);
WriteWord (INTERRUPT_REGISTER, wIntMask);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?