📄 smc.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:
Abstract:
Routines for the SMC9000 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 Odo SMC implementation sometimes encounters read errors if we
// optimize this routine. So for now I'm just going to turn of all
// optimizations.
#pragma optimize("", off)
#include <windows.h>
#include <halether.h>
#include <ceddk.h>
#include "smchw.h"
#include "smcsw.h"
#include "udp.h"
// The base address of the SMC registers. Also, allow for a multiplier, since the Odo
// platform uses 32 bit accesses of the registers.
static BYTE volatile *pbEthernetBase;
static DWORD dwRegisterMultiplier;
#define ReadWord( wOffset ) READ_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset*dwRegisterMultiplier)))
#define WriteWord( wOffset, Value ) WRITE_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset*dwRegisterMultiplier)),(USHORT)(Value))
// If this is defined, broadcast frames will be discarded. Since we need to process
// ARPs, leave broadcast filter off, and filter out packets as soon as we can. This, since
// we have to take an interrupt anyway, looking at the first few bytes isn't that bad. If
// the SMC chip could be configured so that we didn't even get interrupts for broadcast
// packets, we might look at updating the ARP cache on the desktop and filtering out all
// broadcasts...
#define NO_BROADCAST 0
#define BROADCAST_FILTER_BIT 0x4000
// If any of the following bits are set in the received status word, discard frame
#if NO_BROADCAST
#define FRAME_FILTER (0xAC00 | BROADCAST_FILTER_BIT) // No broadcast
#else
#define FRAME_FILTER 0xAC00 // Allow broadcast
#endif
// For debugging, if defined, this will cause all Rx/Tx data to be dumped to debug serial port
#undef SMC_DUMP_FRAMES
#define SMC_DUMP_FRAMES
// Mask to use for interrupts
static UINT16 wIntMask;
// Controls whether we filter out broadcast packets (except ARP requests...)
static DWORD dwConfigOptions;
static void SendFrameError( char *pszPrefix, UINT16 wCompletionCode );
#ifdef SMC_DUMP_FRAMES
static void DumpEtherFrame( BYTE *pFrame, WORD cwFrameLength );
#endif
// This is called to initialze the ethernet low level driver. The base address of the ethernet hardware
// is passed into the routine. The routine will return TRUE for a successful initialization.
static void smc_udelay(unsigned int us)
{
volatile int i;
for(i=0 ; (unsigned int)i < us ; i++)
{
}
}
static WORD smc_read_phy_register (unsigned char phyreg)
{
int oldBank;
int i;
BYTE mask;
WORD mii_reg;
BYTE bits[64];
int clk_idx = 0;
int input_idx;
WORD phydata;
BYTE phyaddr = SMC_PHY_ADDR;
/* 32 consecutive ones on MDO to establish sync */
for (i = 0; i < 32; ++i)
bits[clk_idx++] = MII_MDOE | MII_MDO;
/* Start code <01> */
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE | MII_MDO;
/* Read command <10> */
bits[clk_idx++] = MII_MDOE | MII_MDO;
bits[clk_idx++] = MII_MDOE;
/* Output the PHY address, msb first */
mask = (BYTE) 0x10;
for (i = 0; i < 5; ++i) {
if (phyaddr & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Output the phy register number, msb first */
mask = (BYTE) 0x10;
for (i = 0; i < 5; ++i) {
if (phyreg & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Tristate and turnaround (2 bit times) */
bits[clk_idx++] = 0;
/*bits[clk_idx++] = 0; */
/* Input starts at this bit time */
input_idx = clk_idx;
/* Will input 16 bits */
for (i = 0; i < 16; ++i)
bits[clk_idx++] = 0;
/* Final clock bit */
bits[clk_idx++] = 0;
/* Save the current bank */
oldBank = ReadWord(BANKSEL_REG);
/* Select bank 3 */
WriteWord( BANKSEL_REG, BANK3 );
/* Get the current MII register value */
mii_reg = ReadWord (MII_REG);
/* Turn off all MII Interface bits */
mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO);
/* Clock all 64 cycles */
for (i = 0; i < sizeof bits; ++i) {
/* Clock Low - output data */
WriteWord (MII_REG, mii_reg | bits[i]);
smc_udelay (SMC_PHY_CLOCK_DELAY);
/* Clock Hi - input data */
WriteWord ( MII_REG, mii_reg | bits[i] | MII_MCLK);
smc_udelay (SMC_PHY_CLOCK_DELAY);
bits[i] |= ReadWord (MII_REG) & MII_MDI;
}
/* Return to idle state */
/* Set clock to low, data to low, and output tristated */
WriteWord (MII_REG, mii_reg);
smc_udelay (SMC_PHY_CLOCK_DELAY);
/* Restore original bank select */
WriteWord( BANKSEL_REG, oldBank );
/* Recover input data */
phydata = 0;
for (i = 0; i < 16; ++i) {
phydata <<= 1;
if (bits[input_idx++] & MII_MDI)
phydata |= 0x0001;
}
return (phydata);
}
/*------------------------------------------------------------
. Writes a register to the MII Management serial interface
.-------------------------------------------------------------*/
static void smc_write_phy_register (BYTE phyreg, WORD phydata)
{
int oldBank;
int i;
WORD mask;
WORD mii_reg;
BYTE bits[65];
int clk_idx = 0;
BYTE phyaddr = SMC_PHY_ADDR;
/* 32 consecutive ones on MDO to establish sync */
for (i = 0; i < 32; ++i)
bits[clk_idx++] = MII_MDOE | MII_MDO;
/* Start code <01> */
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE | MII_MDO;
/* Write command <01> */
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE | MII_MDO;
/* Output the PHY address, msb first */
mask = (BYTE) 0x10;
for (i = 0; i < 5; ++i) {
if (phyaddr & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Output the phy register number, msb first */
mask = (BYTE) 0x10;
for (i = 0; i < 5; ++i) {
if (phyreg & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Tristate and turnaround (2 bit times) */
bits[clk_idx++] = 0;
bits[clk_idx++] = 0;
/* Write out 16 bits of data, msb first */
mask = 0x8000;
for (i = 0; i < 16; ++i) {
if (phydata & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Final clock bit (tristate) */
bits[clk_idx++] = 0;
/* Save the current bank */
oldBank = ReadWord(BANKSEL_REG);
/* Select bank 3 */
WriteWord( BANKSEL_REG, BANK3 );
/* Get the current MII register value */
mii_reg = ReadWord (MII_REG);
/* Turn off all MII Interface bits */
mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO);
/* Clock all cycles */
for (i = 0; i < sizeof bits; ++i) {
/* Clock Low - output data */
WriteWord ( MII_REG, mii_reg | bits[i]);
smc_udelay (SMC_PHY_CLOCK_DELAY);
/* Clock Hi - input data */
WriteWord ( MII_REG, mii_reg | bits[i] | MII_MCLK);
smc_udelay (SMC_PHY_CLOCK_DELAY);
bits[i] |= ReadWord (MII_REG) & MII_MDI;
}
/* Return to idle state */
/* Set clock to low, data to low, and output tristated */
WriteWord ( MII_REG, mii_reg);
smc_udelay (SMC_PHY_CLOCK_DELAY);
/* Restore original bank select */
WriteWord( BANKSEL_REG, oldBank );
}
#if 1
static void smc_wait_ms(unsigned int ms)
{
volatile int i;
unsigned int i2;
for(i2=0 ; i2 < ms ; i2++)
{
for(i=0 ; i < 1000 ; i++)
{
}
}
}
static void smc_phy_configure ()
{
int timeout;
BYTE phyaddr;
WORD my_phy_caps; /* My PHY capabilities */
WORD my_ad_caps; /* My Advertised capabilities */
WORD status = 0; /*;my status = 0 */
int failed = 0;
EdbgOutputDebugString("smc_phy_configure()\r\n");
EdbgOutputDebugString("phy cfg reg1: 0x%x\n", smc_read_phy_register(PHY_CFG1_REG) );
/* Get the detected phy address */
phyaddr = SMC_PHY_ADDR;
/* Reset the PHY, setting all other bits to zero */
smc_write_phy_register (PHY_CNTL_REG, PHY_CNTL_RST);
timeout = 6; /* Wait up to 3 seconds */
while (timeout--) {
if (!(smc_read_phy_register (PHY_CNTL_REG) & PHY_CNTL_RST)) {
/* reset complete */
break;
}
smc_wait_ms (500); /* wait 500 millisecs */
}
if (timeout < 1) {
EdbgOutputDebugString ("%s:PHY reset timed out\n", SMC_DEV_NAME);
goto smc_phy_configure_exit;
}
/* Read PHY Register 18, Status Output */
/* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */
/* Enable PHY Interrupts (for register 18) */
/* Interrupts listed here are disabled */
smc_write_phy_register (PHY_MASK_REG, 0x0000);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -