/**************************************************************************
*
* pcnet32.c -- Etherboot device driver for the AMD PCnet32
* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Portions of this code based on:
* pcnet32.c: An AMD PCnet32 ethernet driver for linux:
*
* (C) 1996-1999 Thomas Bogendoerfer
* See Linux Driver for full information
*
* The transmit and poll functions were written with reference to:
* lance.c - LANCE NIC driver for Etherboot written by Ken Yap
*
* Linux Driver Version 1.27a, 10.02.2002
*
*
* REVISION HISTORY:
* ================
* v1.0 08-06-2003 timlegge Initial port of Linux driver
* v1.1 08-23-2003 timlegge Add multicast support
* v1.2 01-17-2004 timlegge Initial driver output cleanup
* v1.3 03-29-2004 timlegge More driver cleanup
*
* Indent Options: indent -kr -i8
***************************************************************************/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
/* Include the time functions */
#include "timer.h"
#include "mii.h"
/* void hex_dump(const char *data, const unsigned int len); */
/* Etherboot Specific definations */
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
#ifdef EDEBUG
#else
#define dprintf(x)
#endif
/* Condensed operations for readability. */
/* End Etherboot Specific */
#ifdef REMOVE
/* FIXME: Remove these they are probably pointless */
/*
* VLB I/O addresses
*/
{ 0x300, 0x320, 0x340, 0x360, 0 };
#endif
/*
* table to translate option values from tulip
* to internal options
*/
static unsigned char options_mapping[] = {
PCNET32_PORT_ASEL, /* 0 Auto-select */
PCNET32_PORT_AUI, /* 1 BNC/AUI */
PCNET32_PORT_AUI, /* 2 AUI/BNC */
PCNET32_PORT_ASEL, /* 3 not supported */
PCNET32_PORT_ASEL, /* 5 not supported */
PCNET32_PORT_ASEL, /* 6 not supported */
PCNET32_PORT_ASEL, /* 7 not supported */
PCNET32_PORT_ASEL, /* 8 not supported */
PCNET32_PORT_MII, /* 9 MII 10baseT */
PCNET32_PORT_MII, /* 11 MII (autosel) */
PCNET32_PORT_10BT, /* 12 10BaseT */
PCNET32_PORT_ASEL /* 15 not supported */
};
/*
* Theory of Operation
*
* This driver uses the same software structure as the normal lance
* driver. So look for a verbose description in lance.c. The differences
* to the normal lance driver is the use of the 32bit mode of PCnet32
* and PCnetPCI chips. Because these chips are 32bit chips, there is no
* 16MB limitation and we don't need bounce buffers.
*/
/*
* Set the number of Tx and Rx buffers, using Log_2(# buffers).
* Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
* That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
*/
#ifndef PCNET32_LOG_TX_BUFFERS
#endif
/* FIXME: Fix this to allow multiple tx_ring descriptors */
/* Offsets from base I/O address. */
/* Buffers for the tx and Rx */
/* Create a static buffer of size PKT_BUF_SZ for each
TX Descriptor. All descriptors point to a
part of this buffer */
// __attribute__ ((aligned(16)));
/* Create a static buffer of size PKT_BUF_SZ for each
RX Descriptor All descriptors point to a
part of this buffer */
// __attribute__ ((aligned(16)));
/* The PCNET32 Rx and Tx ring descriptors. */
struct pcnet32_rx_head {
};
struct pcnet32_tx_head {
};
/* The PCNET32 32-Bit initialization block, described in databook. */
struct pcnet32_init_block {
/* Receive and transmit ring base, along with extra bits. */
};
/* PCnet32 access functions */
struct pcnet32_access {
void (*reset) (unsigned long);
};
/* Define the TX Descriptor */
/* Define the RX Descriptor */
/* May need to be moved to mii.h */
struct mii_if_info {
int phy_id;
int advertising;
};
/*
* The first three fields of pcnet32_private are read by the ethernet device
* so we allocate the structure should be allocated by pci_alloc_consistent().
*/
struct pcnet32_private {
const char *name;
struct pcnet32_access a;
char tx_full;
int options;
} lpx;
#if 0
int val);
#endif
enum pci_flags_bit {
};
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
};
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
};
/* Initialize the PCNET32 Rx and Tx rings. */
{
int i;
for (i = 0; i < RX_RING_SIZE; i++) {
}
/* The Tx buffer address is filled in as needed, but we do need to clear
the upper ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
}
for (i = 0; i < 6; i++)
return 0;
}
/**************************************************************************
RESET - Reset adapter
***************************************************************************/
{
/* put the card in its initial state */
int i;
/* Reset the PCNET32 */
/* switch pcnet32 to 32bit mode */
val |= 2;
/* handle full duplex setting */
if (lp->full_duplex) {
val |= 1;
val |= 2;
/* workaround of xSeries250, turn on for 79C975 only */
i = ((lp->a.
89) << 16)) >>
12) & 0xffff;
if (i == 0x2627)
val |= 3;
}
}
val |= 0x10;
val |= 0x10;
val |= 0x08;
} else {
val |= 0x20;
}
}
#ifdef DO_DXSUFLO
val |= 0x40;
}
#endif
}
/* Re-initialize the PCNET32, and start it when done. */
i = 0;
while (i++ < 100)
break;
/*
* We used to clear the InitDone bit, 0x0100, here but Mark Stockton
* reports that doing so triggers a bug in the '974.
*/
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
{
/* return true if there's an ethernet packet ready to read */
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
int status;
int entry;
if (status < 0)
return 0;
if ( ! retrieve ) return 1;
if (status == 0x03) {
/* Andrew Boyd of QNX reports that some revs of the 79C765
* clear the buffer length */
/* Switch to the next Rx ring buffer */
} else {
return 0;
}
return 1;
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p)
{ /* Packet */
/* send the packet to destination */
unsigned long time;
status = 0x8300;
/* point to the current txb incase multiple tx_rings are used */
/* copy the packet to ring buffer */
s += ETH_HLEN;
while (s < ETH_ZLEN) /* pad to min length */
ptxb[s++] = '\0';
/* we set the top byte as the very last thing */
/* Trigger an immediate send poll */
/* wait for transmit complete */
printf("PCNET32 timed out on transmit\n");
/* Stop pointing at the current txb
* otherwise the card continues to send the packet */
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
{
/* Stop the PCNET32 here -- it ocassionally polls memory if we don't */
/*
* Switch back to 16-bit mode to avoid problesm with dumb
* DOS packet driver after a warm reboot
*/
}
/**************************************************************************
IRQ - Enable, Disable, or Force interrupts
***************************************************************************/
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
You should omit the last argument struct pci_device * for a non-PCI NIC
***************************************************************************/
{
int i, media;
int chip_version;
char *chipname;
struct pcnet32_access *a = NULL;
return 0;
/* BASE is used throughout to address the card */
/* reset the chip */
/* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
&& pcnet32_wio_check(ioaddr)) {
a = &pcnet32_wio;
} else {
&& pcnet32_dwio_check(ioaddr)) {
a = &pcnet32_dwio;
} else
return 0;
}
return 0;
/* initialize variables */
switch (chip_version) {
case 0x2420:
break;
case 0x2430:
if (shared)
else
break;
case 0x2621:
fdx = 1;
break;
case 0x2623:
fdx = 1;
mii = 1;
fset = 1;
ltint = 1;
break;
case 0x2624:
fdx = 1;
mii = 1;
fset = 1;
break;
case 0x2625:
fdx = 1;
mii = 1;
break;
case 0x2626:
fdx = 1;
/*
* This is based on specs published at www.amd.com. This section
* assumes that a card with a 79C978 wants to go into 1Mb HomePNA
* mode. The 79C978 can also go into standard ethernet, and there
* probably should be some sort of module option to select the
* mode by which the card should operate
*/
/* switch to home wiring mode */
break;
case 0x2627:
fdx = 1;
mii = 1;
break;
default:
printf("PCnet version %#x, no PCnet32 chip.\n",
return 0;
}
/*
* On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
* starting until the packet is loaded. Strike one for reliability, lose
* one for latency - although on PCI this isnt a big loss. Older chips
* have FIFO's smaller than a packet, so you can't do this.
*/
if (fset) {
dxsuflo = 1;
ltint = 1;
}
/* read PROM address */
for (i = 0; i < 6; i++)
/* Update the nic structure with the MAC Address */
for (i = 0; i < ETH_ALEN; i++) {
}
/* Print out some hardware info */
ioaddr);
/* Set to pci bus master */
/* point to private storage */
#if EBDEBUG
dprintf((" tx_start_pt(0x%hX):", i));
switch (i >> 10) {
case 0:
dprintf((" 20 bytes,"));
break;
case 1:
dprintf((" 64 bytes,"));
break;
case 2:
dprintf((" 128 bytes,"));
break;
case 3:
dprintf(("~220 bytes,"));
break;
}
if (i & (1 << 5))
dprintf(("BurstWrEn "));
if (i & (1 << 6))
dprintf(("BurstRdEn "));
if (i & (1 << 7))
dprintf(("DWordIO "));
if (i & (1 << 11))
dprintf(("NoUFlow "));
if (i & (1 << 14))
dprintf(("LowLatRx"));
}
#endif
/* FIXME: Fix Options for only one card */
if ((cards_found >= MAX_UNITS)
else
if (!a) {
printf("No access methods\n");
return 0;
}
lp->a = *a;
for (i = 0; i < 6; i++)
/* switch pcnet32 to 32bit mode */
/*
* To auto-IRQ we enable the initialization-done and DMA error
* interrupts. For ISA boards we get a DMA error, but VLB and PCI
* boards will work.
*/
/* Trigger an initialization just for the interrupt. */
mdelay(1);
cards_found++;
/* point to NIC specific routines */
if (1) {
int tmp;
if ((mii_status & 0x0040) == 0) {
dprintf (("MII PHY found at address %d, status "
}
}
}
if (phy_idx == 0)
printf("No MII transceiver found!\n");
printf("100Mbps Full-Duplex\n");
printf("100Mbps Half-Duplex\n");
printf("10Mbps Full-Duplex\n");
printf("10Mbps Half-Duplex\n");
else
printf("\n");
}
return 1;
}
{
int phyaddr;
return 0;
return val_out;
}
#if 0
int val)
{
int phyaddr;
return;
}
#endif
};
.type = NIC_DRIVER,
.probe = pcnet32_probe,
.ids = pcnet32_nics,
.class = 0,
};