smc9000.c revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
#ifdef ALLMULTI
#endif
/*------------------------------------------------------------------------
* This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
*
* Copyright (C) 1998 Daniel Engstr�m <daniel.engstrom@riksnett.no>
* Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
* Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
* "Features" of the SMC chip:
* 4608 byte packet memory. ( for the 91C92/4. Others have more )
* EEPROM for configuration
*
* Authors
* Erik Stahlman <erik@vt.edu>
* Daniel Engstr�m <daniel.engstrom@riksnett.no>
*
* History
* 98-09-25 Daniel Engstr�m Etherboot driver crated from Eric's
* Linux driver.
*
*---------------------------------------------------------------------------*/
#define LINUX_OUT_MACROS 1
#define SMC9000_VERBOSE 1
#define SMC9000_DEBUG 0
#if SMC9000_DEBUG > 1
#else
#endif
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
#include "timer.h"
#include "smc9000.h"
static const char smc9000_version[] = "Version 0.99 98-09-30";
static unsigned int smc9000_base=0;
static const char *chip_ids[ 15 ] = {
/* 3 */ "SMC91C90/91C92",
/* 4 */ "SMC91C94",
/* 5 */ "SMC91C95",
NULL,
/* 7 */ "SMC91C100",
/* 8 */ "SMC91C100FD",
/* 9 */ "SMC91C11xFD",
};
static const char smc91c96_id[] = "SMC91C96";
/*------------------------------------------------------------
. Reads a register from the MII Management serial interface
.-------------------------------------------------------------*/
{
int oldBank;
unsigned int i;
int clk_idx = 0;
int input_idx;
// 32 consecutive ones on MDO to establish sync
for (i = 0; i < 32; ++i)
// Start code <01>
// Read command <10>
// Output the PHY address, msb first
for (i = 0; i < 5; ++i)
{
else
// Shift to next lowest bit
mask >>= 1;
}
// Output the phy register number, msb first
for (i = 0; i < 5; ++i)
{
else
// Shift to next lowest bit
mask >>= 1;
}
// Tristate and turnaround (2 bit times)
//bits[clk_idx++] = 0;
// Input starts at this bit time
// Will input 16 bits
for (i = 0; i < 16; ++i)
// Final clock bit
// Save the current bank
// Select bank 3
// Get the current MII register value
// Turn off all MII Interface bits
// Clock all 64 cycles
for (i = 0; i < sizeof(bits); ++i)
{
// Clock Low - output data
udelay(50);
// Clock Hi - input data
udelay(50);
}
// Return to idle state
// Set clock to low, data to low, and output tristated
udelay(50);
// Restore original bank select
// Recover input data
phydata = 0;
for (i = 0; i < 16; ++i)
{
phydata <<= 1;
phydata |= 0x0001;
}
#if (SMC_DEBUG > 2 )
printf("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
#endif
return(phydata);
}
/*------------------------------------------------------------
. Writes a register to the MII Management serial interface
.-------------------------------------------------------------*/
static void smc_write_phy_register(int ioaddr,
{
int oldBank;
unsigned int i;
int clk_idx = 0;
// 32 consecutive ones on MDO to establish sync
for (i = 0; i < 32; ++i)
// Start code <01>
// Write command <01>
// Output the PHY address, msb first
for (i = 0; i < 5; ++i)
{
else
// Shift to next lowest bit
mask >>= 1;
}
// Output the phy register number, msb first
for (i = 0; i < 5; ++i)
{
else
// Shift to next lowest bit
mask >>= 1;
}
// Tristate and turnaround (2 bit times)
// Write out 16 bits of data, msb first
mask = 0x8000;
for (i = 0; i < 16; ++i)
{
else
// Shift to next lowest bit
mask >>= 1;
}
// Final clock bit (tristate)
// Save the current bank
// Select bank 3
// Get the current MII register value
// Turn off all MII Interface bits
// Clock all cycles
for (i = 0; i < sizeof(bits); ++i)
{
// Clock Low - output data
udelay(50);
// Clock Hi - input data
udelay(50);
}
// Return to idle state
// Set clock to low, data to low, and output tristated
udelay(50);
// Restore original bank select
#if (SMC_DEBUG > 2 )
printf("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
#endif
}
/*------------------------------------------------------------
. Finds and reports the PHY address
.-------------------------------------------------------------*/
{
int phyaddr;
int found = 0;
// Scan all 32 PHY addresses if necessary
{
// Read the PHY identifiers
// Make sure it is a valid identifier
{
{
// Save the PHY's address
found = 1;
break;
}
}
}
if (!found)
{
printf("No PHY found\n");
return(0);
}
// Set the PHY type
{
printf("PHY=LAN83C183 (LAN91C111 Internal)\n");
}
{
printf("PHY=LAN83C180\n");
}
return(1);
}
/*------------------------------------------------------------
. Configures the specified PHY using Autonegotiation. Calls
. smc_phy_fixed() if the user has requested a certain config.
.-------------------------------------------------------------*/
static void smc_phy_configure(int ioaddr)
{
int timeout;
int failed = 0;
int rpc_cur_mode = RPC_DEFAULT;
int lastPhy18;
// Find the address and type of our phy
{
return;
}
// Reset the PHY, setting all other bits to zero
// Wait for the reset to complete, or time out
while (timeout--)
{
& PHY_CNTL_RST))
{
// reset complete
break;
}
}
if (timeout < 1)
{
PRINTK2("PHY reset timed out\n");
return;
}
// Read PHY Register 18, Status Output
// Enable PHY Interrupts (for register 18)
// Interrupts listed here are disabled
SMC_SELECT_BANK(ioaddr, 0);
// Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
if (my_phy_caps & PHY_STAT_CAP_T4)
my_ad_caps |= PHY_AD_T4;
if (my_phy_caps & PHY_STAT_CAP_TXF)
if (my_phy_caps & PHY_STAT_CAP_TXH)
if (my_phy_caps & PHY_STAT_CAP_TF)
if (my_phy_caps & PHY_STAT_CAP_TH)
// Update our Auto-Neg Advertisement Register
// Restart auto-negotiation process in order to advertise my caps
// Wait for the auto-negotiation to complete. This may take from
// 2 to 3 seconds.
// Wait for the reset to complete, or time out
while (timeout--)
{
if (status & PHY_STAT_ANEG_ACK)
{
// auto-negotiate complete
break;
}
// Restart auto-negotiation if remote fault
if (status & PHY_STAT_REM_FLT)
{
PRINTK2("PHY remote fault detected\n");
// Restart auto-negotiation
PRINTK2("PHY restarting auto-negotiation\n");
}
}
if (timeout < 1)
{
PRINTK2("PHY auto-negotiate timed out\n");
failed = 1;
}
// Fail if we detected an auto-negotiate remote fault
if (status & PHY_STAT_REM_FLT)
{
PRINTK2("PHY remote fault detected\n");
failed = 1;
}
// Set our sysctl parameters to match auto-negotiation results
if ( lastPhy18 & PHY_INT_SPDDET )
{
PRINTK2("PHY 100BaseT\n");
}
else
{
PRINTK2("PHY 10BaseT\n");
rpc_cur_mode &= ~RPC_SPEED;
}
if ( lastPhy18 & PHY_INT_DPLXDET )
{
PRINTK2("PHY Full Duplex\n");
rpc_cur_mode |= RPC_DPLX;
}
else
{
PRINTK2("PHY Half Duplex\n");
rpc_cur_mode &= ~RPC_DPLX;
}
}
/*
* Function: smc_reset( int ioaddr )
* Purpose:
* This sets the SMC91xx chip to its normal state, hopefully from whatever
* mess that any other DOS driver has put it in.
*
* Maybe I should reset more registers to defaults in here? SOFTRESET should
* do that for me.
*
* Method:
* 1. send a SOFT RESET
* 2. wait for it to finish
* 3. reset the memory management unit
* 4. clear all interrupts
*
*/
{
/* This resets the registers mostly to defaults, but doesn't
* affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK(ioaddr, 0);
/* this should pause enough for the chip to be happy */
/* Set the transmit and receive configuration registers to
* default values */
/* Reset the MMU */
/* Note: It doesn't seem that waiting for the MMU busy is needed here,
* but this is a place where future chipsets _COULD_ break. Be wary
* of issuing another MMU command right after this */
}
/*----------------------------------------------------------------------
* Function: smc_probe( int ioaddr )
*
* Purpose:
* Tests to see if a given ioaddr points to an SMC9xxx chip.
* Returns a 0 on success
*
* Algorithm:
* (1) see if the high byte of BANK_SELECT is 0x33
* (2) compare the ioaddr with the base register's address
* (3) see if I recognize the chip ID in the appropriate register
*
* ---------------------------------------------------------------------
*/
{
/* First, see if the high byte is 0x33 */
return -1;
}
/* The above MIGHT indicate a device, but I need to write to further
* test this. */
return -1;
}
/* well, we've already written once, so hopefully another time won't
* hurt. This time, I need to switch the bank register to bank 1,
* so I can access the base address register */
#ifdef SMC9000_VERBOSE
printf("SMC9000: IOADDR %hX doesn't match configuration (%hX)."
"Probably not a SMC chip\n",
#endif
/* well, the base address register didn't match. Must not have
* been a SMC chip after all. */
return -1;
}
/* check if the revision register is something that I recognize.
* These might need to be added to later, as future revisions
* could be added. */
/* I don't recognize this chip, so... */
#ifdef SMC9000_VERBOSE
printf("SMC9000: IO %hX: Unrecognized revision register:"
#endif
return -1;
}
/* at this point I'll assume that the chip is an SMC9xxx.
* It might be prudent to check a listing of MAC addresses
* against the hardware address, or do some other tests. */
return 0;
}
/**************************************************************************
* ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static void smc9000_transmit(
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
unsigned long time_out;
int i;
/* We dont pad here since we can have the hardware doing it for us */
/* convert to MMU pages */
if (numPages > 7 ) {
#ifdef SMC9000_VERBOSE
printf("SMC9000: Far too big packet error. \n");
#endif
return;
}
/* dont try more than, say 30 times */
for (i=0;i<30;i++) {
/* now, try to allocate the memory */
status = 0;
/* wait for the memory allocation to finnish */
if ( status & IM_ALLOC_INT ) {
/* acknowledge the interrupt */
break;
}
}
if ((status & IM_ALLOC_INT) != 0 ) {
/* We've got the memory */
break;
} else {
printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
}
}
/* If I get here, I _know_ there is a packet slot waiting for me */
if (packet_no & 0x80) {
/* or isn't there? BAD CHIP! */
printf("SMC9000: Memory allocation failed. \n");
return;
}
/* we have a packet address, so tell the card to use it */
/* point to the beginning of the packet */
#if SMC9000_DEBUG > 2
#endif
/* send the packet length ( +6 for status, length and ctl byte )
* and the status word ( set to zeros ) */
/* send the packet length ( +6 for status words, length, and ctl) */
/* Write the contents of the packet */
/* The ethernet header first... */
/* ... the data ... */
/* ... and the last byte, if there is one. */
if ((s & 1) == 0) {
} else {
}
/* and let the chipset deal with it */
do {
/* ack interrupt */
packet_no &= 0x7F;
/* select this as the packet to read from */
/* read the first word from this packet */
if (0 == (tx_status & TS_SUCCESS)) {
#ifdef SMC9000_VERBOSE
#endif
/* re-enable transmit */
}
/* kill the packet */
return;
}
printf("SMC9000: Waring TX timed out, resetting board\n");
return;
}
/**************************************************************************
* ETH_POLL - Wait for a frame
***************************************************************************/
{
if(!smc9000_base)
return 0;
return 0;
if ( ! retrieve ) return 1;
/* start reading from the start of the packet */
/* First read the status and check that we're ok */
/* Next: read the packet length and mask off the top bits */
/* the packet length includes the 3 extra words */
#if SMC9000_DEBUG > 2
printf(" Reading %d words (and %d byte(s))\n",
#endif
/* read the packet (and the last "extra" word) */
/* is there an odd last byte ? */
/* error or good, tell the card to get rid of this packet */
return 1;
}
printf("SMC9000: RX error\n");
/* error or good, tell the card to get rid of this packet */
return 0;
}
{
if(!smc9000_base)
return;
/* no more interrupts for me */
/* and tell the card to stay away from that nasty outside world */
}
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
* ETH_PROBE - Look for an adapter
***************************************************************************/
{
unsigned short revision;
int memory;
int media;
const char * version_string;
const char * if_string;
int i;
/*
* the SMC9000 can be at any of the following port addresses. To change,
* for a slightly different card, you can add it to the array. Keep in
* mind that the array must end in zero.
*/
static unsigned short portlist[] = {
#ifdef SMC9000_SCAN
#else
0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
#endif
0 };
/* if no addresses supplied, fall back on defaults */
if (probe_addrs == 0 || probe_addrs[0] == 0)
/* check every ethernet address */
for (i = 0; probe_addrs[i]; i++) {
/* check this specific address */
if (smc_probe(probe_addrs[i]) == 0)
smc9000_base = probe_addrs[i];
}
/* couldn't find anything */
if(0 == smc9000_base)
goto out;
/*
* Get the MAC address ( bank 1, regs 4 - 9 )
*/
for ( i = 0; i < 6; i += 2 ) {
}
/* get the memory information */
/*
* Now, I want to find out more about the chip. This is sort of
* redundant, but it's cleaner to have it in both, rather than having
* one VERY long probe procedure.
*/
/* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
* a revision starting at 6 */
}
if ( !version_string ) {
/* I shouldn't get here because this call was done before.... */
goto out;
}
/* is it using AUI or 10BaseT ? */
media = 2;
else
media = 1;
/* now, reset the chip, and put it into a known state */
#ifdef SMC9000_VERBOSE
printf("Copyright (C) 1998 Daniel Engstr\x94m\n");
printf("Copyright (C) 1996 Eric Stahlman\n");
#endif
printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n",
/*
* Print the Ethernet address
*/
/* Select which interface to use */
if ( media == 1 ) {
smc9000_base + CONFIG );
}
else if ( media == 2 ) {
smc9000_base + CONFIG );
}
/* Based on PnP ISA map */
return 1;
out:
#ifdef SMC9000_VERBOSE
/* printf("No SMC9000 adapters found\n"); */
#endif
smc9000_base = 0;
return (0);
}
.type = NIC_DRIVER,
.name = "SMC9000",
.probe = smc9000_probe,
.ioaddrs = 0,
};