📄 dp83815.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
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.
Module Name: DP83815.c
Author: Kirk Gremillion (kirkgrem)
Abstract:
Plain Vanilla Routines for National Semiconductor DP83815.
This mainly supports initialization and Sending/Receiving Ethernet
packets as well as interrupt handler.
Minimum error handling is provided here...
Notes:
Code is meant to be used for ethernet download and debugging only and
is expected to run in Kernel Mode...
This code assumes that you have a BIOS of some type that will set up the PCI device.
No hardware reset is performed, although configuration is.
--*/
#include <windows.h>
#include <halether.h>
#include <dp83815.h>
// DPUseIOSpace assumes an i486 CPU type.
#define DPUseIOSpace // use the IO space for accessing the device. If it is not defined, we use Memory mapped IO.
BOOL bEbootImage = FALSE;
#define TO_VIRT(addr) (bEbootImage ? (ULONG)addr : ((ULONG)addr | 0x80000000))
#define TO_PHYS(addr) (bEbootImage ? (ULONG)addr : ((ULONG)addr & ~0x80000000))
////////////////////////////////////////////////////////////////////////////////
// Misc utility functions
//
#ifdef DPUseIOSpace
static void
DP_WRITE_PORT_ULONG(volatile PULONG addr, ULONG val)
{
__asm {
mov edx, addr
mov eax, val
out dx, eax
}
}
static ULONG
DP_READ_PORT_ULONG(volatile PULONG addr)
{
ULONG val;
__asm {
mov edx, addr
in eax, dx
mov val, eax
}
return val;
}
#else // DPUseIOSpace was not defined so we want to use memory addressing. Redefine port IO for memory.
static void
DP_WRITE_PORT_ULONG(volatile PULONG addr, ULONG val)
{
*addr = val;
}
static ULONG
DP_READ_PORT_ULONG(volatile PULONG addr)
{
return *addr;
}
#endif // end #ifdef DPUseIOSpace.
////////////////////////////////////////////////////////////////////////////////
// DP83815ModifyLong()
// Implements a Read-modify-write cycle for any DP_OPS register
// caller supplies 'Value' as a DWORD with his needed bits (only) set or cleared.
// Function will read register, mask this with 'ValueMask' and compare with
// 'Value'. If equal, no write back is done. If a change is needed, a copy of
// the register value has the bits specified by 'ValueMask' cleared, and then
// the caller's 'Value' is ORed in. The result is then written out to Tag.
//
void
DPModifyLong(DWORD Tag, DWORD ValueMask, DWORD Value)
{
DWORD dwRegister,dwTemp;
dwTemp=(Value & ValueMask); // We don't want to accidentally set any bits outside of ValueMask.
if (dwTemp != Value)
{
PRINTF (1, ("DP83815: Misuse of DPModifyLong(). Possible error in register write.\r\n"));
PRINTF (1, ("DP83815: Check alignment of Value and ValueMask. Spin Forever.\r\n"));
while(1);
}
dwRegister=DPReadLong(Tag,DP_allbits); // get the current register value
dwTemp=(dwRegister & ValueMask); // see if the bits in question are already set as we want them.
if (dwTemp==Value)
{
return; // OK, we don't need to modify this register. NO WRITE BACK IS DONE.
}
else
{
dwRegister=(dwRegister&(~ValueMask)); // clear the bits in our copy of the register
dwRegister=(dwRegister|Value); // OR in the caller's validated bits
DPWriteLong(Tag,DP_allbits,dwRegister); // Write the new register value.
}
return;
} // end DPModifyLong()
////////////////////////////////////////////////////////////////////////////////
// DPDelay()
// Implements an arbitrary wait loop. Use only during Initialization please.
//
void
DPDelay(int i)
{
for(;i>0;i--)
DPReadLong(MyDP.UsedAddr, DP_allbits);
}
////////////////////////////////////////////////////////////////////////////////
// DP83815InitDMABuffer()
//
// This function sets up the frame descriptors and associated buffers.
// Each descriptor is allocated sizeof(DP83815FrameDescriptor) but always onto the next 4 byte boundary.
// Each buffer is allocated BUFFSIZE bytes.
// Allocation comes from the area defined by dwBuffAreaStartAddress.
//
BOOL
DP83815InitDMABuffer(DWORD dwBuffAreaStartAddress, DWORD dwBuffAreaSize)
{
// BUFFSIZE is defined in DP83815.h
DWORD dwAllocationPointer, dwCloseTheLoopPointer;
DWORD dwDescSize;
int i,SetChar=0xBB;
DWORD Scrap;
// Buffer must be at least 4 bytes to even get through the alignment stage
if(dwBuffAreaSize <0x04)
return FALSE; // Not enough buffer to even figure out the alignment.
// We can be called by eboot code or by OAL code - decide who's calling
// so we can initialize buffer chain with correct addresses.
if (dwBuffAreaStartAddress & 0x80000000)
bEbootImage = FALSE;
else
bEbootImage = TRUE;
dwBuffAreaStartAddress = TO_PHYS(dwBuffAreaStartAddress);
// First, make sure that start address is 32-bit aligned, and if not adjust both it and dwBuffAreaSize
Scrap= dwBuffAreaStartAddress%4;
Scrap= (Scrap ? (4-Scrap):Scrap);
dwBuffAreaStartAddress= dwBuffAreaStartAddress + Scrap;
dwBuffAreaSize = dwBuffAreaSize - Scrap;
// Clear the entire region.
memset((void *) TO_VIRT(dwBuffAreaStartAddress), SetChar, dwBuffAreaSize);
// Now lets calculate the size and alignment for a descriptor.
dwDescSize = sizeof(DP83815FrameDescriptor);
Scrap=dwDescSize%4;
Scrap= (Scrap ? (4-Scrap):Scrap);
dwDescSize= dwDescSize + Scrap; // make sure we allocate at least to the next 32bit boundary. (Requirement of DP83815 device)
// Need at least enough DMA buffer space for 4 (3 Tx and 1 Rx) descriptor/buffer pairs.
if(dwBuffAreaSize < (4*(dwDescSize+BUFFSIZE)))
return FALSE; // not enough buffer to work with.
// Set up 3 Tx descriptors and buffers in a ring.
dwAllocationPointer=dwBuffAreaStartAddress;
dwCloseTheLoopPointer=dwAllocationPointer;
for (i=0;i<3;i++)
{
MyDP.pCurrentTxDescriptor= (pDP83815FrameDescriptor)TO_VIRT(dwAllocationPointer); // beginning of descriptor.
dwAllocationPointer += dwDescSize;
MyDP.pCurrentTxDescriptor->bufptr=dwAllocationPointer; //beginning of buffer
dwAllocationPointer += BUFFSIZE; // now Allocation pointer is set for allocation of next descriptor.
MyDP.pCurrentTxDescriptor->link= (pDP83815FrameDescriptor) dwAllocationPointer;
MyDP.pCurrentTxDescriptor->cmdsts= ((DP83815_cmdsts_SIZE&BUFFSIZE)|(DP83815_cmdsts_OWN&0x0)); // size of buffer allocated, and clear OWN bit
} // end for( 3 times)
MyDP.pCurrentTxDescriptor->link=(pDP83815FrameDescriptor) dwCloseTheLoopPointer; // close the Tx ring.
MyDP.pCurrentTxDescriptor= (pDP83815FrameDescriptor)TO_VIRT(MyDP.pCurrentTxDescriptor->link);
// Just for grins, let's traverse the Tx ring. If we get lost, we don't want to go any farther anyway.
do
{
MyDP.pCurrentTxDescriptor=(pDP83815FrameDescriptor)TO_VIRT(MyDP.pCurrentTxDescriptor->link);
} while (MyDP.pCurrentTxDescriptor != (pDP83815FrameDescriptor)TO_VIRT(dwCloseTheLoopPointer));
DPReadLong(DP_OPS_ISR,DP_allbits) ; // not quite sure why, but have to clear ISR before proceeding.
// set up Rx descriptors, linked in a ring until we run out of Buffer space.
dwCloseTheLoopPointer=dwAllocationPointer;
while (dwAllocationPointer < (dwBuffAreaStartAddress+dwBuffAreaSize-(dwDescSize+BUFFSIZE))) //while(we still have room in the buffer space for another descriptor/buffer pair)
{
MyDP.pCurrentRxDescriptor=(pDP83815FrameDescriptor)TO_VIRT(dwAllocationPointer); // beginning of descriptor.
dwAllocationPointer += dwDescSize; // allocate enough memory for the descriptor.
MyDP.pCurrentRxDescriptor->bufptr=dwAllocationPointer; //beginning of buffer
dwAllocationPointer += BUFFSIZE; // Allocate buffer. Now Allocation pointer is set for allocation of next descriptor.
MyDP.pCurrentRxDescriptor->link= (pDP83815FrameDescriptor)dwAllocationPointer; // That will be the beginning of the next descriptor.
MyDP.pCurrentRxDescriptor->cmdsts= ((DP83815_cmdsts_SIZE&BUFFSIZE)|DP83815_cmdsts_MORE); // size of buffer allocated, and clear OWN bit, set MORE bit.
} // end while(we still have room in the buffer space)
MyDP.pCurrentRxDescriptor->link= (pDP83815FrameDescriptor) dwCloseTheLoopPointer; // which is still pointing to the first Rx descriptor.
MyDP.pCurrentRxDescriptor=(pDP83815FrameDescriptor)TO_VIRT(MyDP.pCurrentRxDescriptor->link); // now MyDP.pCurrentRxDescriptor is pointing at the first Rx descriptor we created.
// Just for grins, let's traverse the Rx ring. Again, better to wander into space now than later.
i=0;
do
{
MyDP.pCurrentRxDescriptor=(pDP83815FrameDescriptor)TO_VIRT(MyDP.pCurrentRxDescriptor->link);
} while (MyDP.pCurrentRxDescriptor != (pDP83815FrameDescriptor)TO_VIRT(dwCloseTheLoopPointer));
return TRUE;
} // DP83815InitDMABuffer()
////////////////////////////////////////////////////////////////////////////////
// DP83815Disable()
// Turns off the Tx and Rx state machines. This needs to be done after download
// since the state machines are free running and would keep running even through
// kernel start.
//
void
DP83815Disable(void)
{
DPModifyLong(DP_OPS_CR,DP_OPS_cr_TXD,DP_OPS_cr_TXD); // turn off the transmitter.
while((DPReadLong(DP_OPS_CR,DP_OPS_cr_TXE))) // this tells us when TXE goes reset.
{
PRINTF (1, ("DP83815: Waiting for transmitter to stop.\r\n"));
DPDelay(100);
}
PRINTF (1, ("DP83815:Transmitter stopped.\r\n"));
DPModifyLong(DP_OPS_CR,DP_OPS_cr_RXD,DP_OPS_cr_RXD); // turn off the receiver.
while((DPReadLong(DP_OPS_CR,DP_OPS_cr_RXE))) // this tells us when RXE goes reset.
{
PRINTF (1, ("DP83815: Waiting for Receiver to stop.\r\n"));
DPDelay(100);
}
PRINTF (1, ("DP83815: Receiver stopped.\r\n"));
} // DP83815Disable()
////////////////////////////////////////////////////////////////////////////////
// DP83815Init()
//
int
DP83815Init(BYTE *pbBaseAddress, ULONG dwMemOffset, USHORT MacAddr[3])
{
ULONG ulScrap=0,ulScrapOld=0;
BOOL test;
DWORD dwScrap=0;
#ifdef DPUseIOSpace
PRINTF (1, ("DP83815: IO Space was chosen. \r\n"));
#else
PRINTF (1, ("DP83815: Mem Space was chosen. \r\n"));
#endif
// get the base address into our local structure.
MyDP.UsedAddr= (DWORD) pbBaseAddress;
//Set our global MemOffset based on input parameter
MemOffset = dwMemOffset;
DP83815Disable();
//
// Soft Reset -
//
DPWriteLong(DP_OPS_CR,DP_allbits,DP_OPS_cr_RST);
while(DPReadLong(DP_OPS_CR, DP_OPS_cr_RST));
//Report the chip revision number.
PRINTF (1, ("DP83815: Silicon Revision number: 0x%x \r\n", DPReadLong(DP83815_SRR, DP_allbits)));
// Load up EEPROM defaults
DPModifyLong(DP_OPS_PTSCR,DP_OPS_ptscr_EELOAD_EN,DP_OPS_ptscr_EELOAD_EN);// Tell device to load defaults from EEPROM.
while(DPReadLong(DP_OPS_PTSCR,DP_OPS_ptscr_EELOAD_EN)); //Spin until finished reading EEPROM.
// Fill wMAC array from PMATCH registers.
test=DP83815ReadMacAddr(MacAddr); // We can do this after we have finished reading the EEPROM.
if (!test)
return FALSE;
// Config the transmit register.
DPWriteLong( DP_OPS_TXCFG, DP_allbits,0x10C00230 );
// This one write breaks out like this: (MSB down to LSB)
/*
CSI: 0 Carrier Sense Ignore
HBI: 0 HeartBeat Ignore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -