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 + -
显示快捷键?