view packages/devs/eth/arm/ebsa285/current/src/if_ebsa285.c @ 82:6736c52df507 ecos-sw-2000-04-14

Merge from eCos master repository on 2000-04-14-13:35:46-BST
author jlarmour
date Tue, 18 Apr 2000 21:51:55 +0000
parents
children 489eb5632bc3
line wrap: on
line source

//==========================================================================
//
//      if_ebsa285.c
//
//	Ethernet drivers
//	Intel EBSA285 and PRO/100+ platform specific support
//
//==========================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD or other sources,
// and are covered by the appropriate copyright disclaimers included herein.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    hmt, gthomas
// Contributors: Ron Spence, Pacific Softworks
// Date:         2000-02-01
// Purpose:      
// Description:  hardware driver for 82559 Intel PRO/100+ ethernet and
//               Intel StrongARM EBSA-285 development boards
//              
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
#include <netdev.h>
#include <eth_drv.h>

#ifdef CYGPKG_IO_PCI
#include <cyg/io/pci.h>
#else
#error "Need PCI package here"
#endif

// ------------------------------------------------------------------------

#define nDEBUG_82559
#define DEBUG

#define nKEEP_STATISTICS
#define nDISPLAY_STATISTICS
#define nDISPLAY_82559_STATISTICS


#define os_printf diag_printf
#define db_printf diag_printf

// ------------------------------------------------------------------------
// I/O access macros as inlines for type safety

static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address)
{   *((cyg_uint8 *)io_address) = value;    }

static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address)
{   *((cyg_uint16 *)io_address) = value;   }

static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address)
{   *((cyg_uint32 *)io_address) = value;   }

static inline cyg_uint8 INB(cyg_uint32 io_address)
{   return *((cyg_uint8 *)io_address);     }

static inline cyg_uint16 INW(cyg_uint32 io_address)
{   return *((cyg_uint16 *)io_address);    }

static inline cyg_uint32 INL(cyg_uint32 io_address)
{   return *((cyg_uint32 *)io_address);    }

#define VIRT_TO_BUS( _x_ ) virt_to_bus((cyg_uint32)(_x_))
static inline cyg_uint32 virt_to_bus(cyg_uint32 p_memory)
{    return (p_memory - CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE);    }

#define BUS_TO_VIRT( _x_ ) bus_to_virt((cyg_uint32)(_x_))
static inline cyg_uint32 bus_to_virt(cyg_uint32 p_memory)
{    return (p_memory + CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE);    }


// ------------------------------------------------------------------------
//                                                                      
//                   82559 REGISTER OFFSETS (I/O SPACE)                 
//                                                                      
// ------------------------------------------------------------------------
#define SCBStatus       0               // Rx/Command Unit command and status.
#define SCBCmd          2               // Rx/Command Unit command and status.
#define SCBPointer      4               // General purpose pointer.
#define SCBPort         8               // Misc. commands and operands.
#define SCBflash        12              // Flash memory control.
#define SCBeeprom       14              // EEPROM memory control.
#define SCBCtrlMDI      16              // MDI interface control.
#define SCBEarlyRx      20              // Early receive byte count.
#define SCBGenControl   28              // 82559 General Control Register
#define SCBGenStatus    29              // 82559 General Status register


// ------------------------------------------------------------------------
//
//               82559 SCB STATUS WORD DEFNITIONS
//
// ------------------------------------------------------------------------
#define SCB_STATUS_CX   0x8000          // CU finished command (transmit)
#define SCB_STATUS_FR   0x4000          // frame received
#define SCB_STATUS_CNA  0x2000          // CU left active state
#define SCB_STATUS_RNR  0x1000          // receiver left ready state
#define SCB_STATUS_MDI  0x0800          // MDI read/write cycle done
#define SCB_STATUS_SWI  0x0400          // software generated interrupt
#define SCB_STATUS_FCP  0x0100          // flow control pause interrupt


// ------------------------------------------------------------------------
//
//               82559 PORT INTERFACE COMMANDS
//
// ------------------------------------------------------------------------
#define I82559_RESET            0x00000000 // software reset
#define I82559_SELFTEST         0x00000001 // 82559 selftest command
#define I82559_SELECTIVE_RESET  0x00000002
#define I82559_DUMP             0x00000003
#define I82559_DUMP_WAKEUP      0x00000007



// ------------------------------------------------------------------------
//
//                   82559 GENERAL STATUS REGISTER
//
// ------------------------------------------------------------------------
#define GEN_STATUS_FDX          0x04    // 1 = full duplex, 0 = half
#define GEN_STATUS_100MBPS      0x02    // 1 = 100 Mbps, 0 = 10 Mbps
#define GEN_STATUS_LINK         0x01    // 1 = link up, 0 = link down


// ------------------------------------------------------------------------
//
//                   82559 EEPROM INTERFACE
//
// ------------------------------------------------------------------------
//  EEPROM_Ctrl bits.
#define EE_SHIFT_CLK	0x01            // EEPROM shift clock.
#define EE_CS		0x02            // EEPROM chip select.
#define EE_DATA_WRITE	0x04            // EEPROM chip data in.
#define EE_WRITE_0	0x01
#define EE_WRITE_1	0x05
#define EE_DATA_READ	0x08            // EEPROM chip data out.
#define EE_ENB		(0x4800 | EE_CS)

// Delay between EEPROM clock transitions.
#define eeprom_delay(usec)		udelay(usec);

// The EEPROM commands include the always-set leading bit.
// (and require the local variable "addr_len", usually 6 or 8)
#define EE_WRITE_CMD	(5 << addr_len)
#define EE_READ_CMD	(6 << addr_len)
#define EE_ERASE_CMD	(7 << addr_len)
#define EE_WRITE_EN_CMD (19 << (addr_len-2))


// ------------------------------------------------------------------------
//
//               SYSTEM CONTROL BLOCK COMMANDS
//
// ------------------------------------------------------------------------
// CU COMMANDS
#define CU_NOP          0x0000
#define	CU_START        0x0010
#define	CU_RESUME       0x0020
#define	CU_STATSADDR    0x0040          // Load Dump Statistics ctrs addr
#define	CU_SHOWSTATS    0x0050          // Dump statistics counters.
#define	CU_CMD_BASE     0x0060          // Base address to add to CU commands
#define	CU_DUMPSTATS    0x0070          // Dump then reset stats counters.

// RUC COMMANDS
#define RUC_NOP         0x0000
#define	RUC_START       0x0001
#define	RUC_RESUME      0x0002
#define RUC_ABORT       0x0004
#define	RUC_ADDR_LOAD   0x0006
#define RUC_RESUMENR    0x0007

#define SCB_M	        0x0100          // 0 = enable interrupt, 1 = disable
#define SCB_SI          0x0200          // 1 - cause device to interrupt

#define CU_STATUS_MASK  0x00C0
#define RU_STATUS_MASK  0x003C

#define CUC_ADDR_LOAD  0x0060
#define CUC_START      0x0010      
 
#define MAX_MEM_RESERVED_IOCTL 1000

// We use this as a templete when writing a new MAC address into the
// eeproms. The MAC address in the first few bytes is over written
// with the correct MAC address and then the whole lot is programmed
// into the serial EEPROM. The checksum is calculated on the fly and
// sent instead of the last two bytes.

static char eeprom_burn[126] = { 
  0x00, 0x90, 0x27, 0x8c, 0x57, 0x82, 0x03, 0x02, 0x00, 0x00, 0x01,
  0x02, 0x01, 0x47, 0x00, 0x00, 0x13, 0x72, 0x06, 0x83, 0xa2, 0x40,
  0x0c, 0x00, 0x86, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00 
};

 

// ------------------------------------------------------------------------
//
//               RECEIVE FRAME DESCRIPTORS
//
// ------------------------------------------------------------------------
typedef struct rfd {
    volatile cyg_uint32 status;         // result of receive operation
    volatile cyg_uint32 link;           // offset from RU base to next RFD
    volatile cyg_uint32 rdb_address;    // pointer to Rx data buffer
    volatile cyg_uint32 count:14,       // number of bytes received +
        f:1,                            //   + EOF & F flags
        eof:1,
            size:16;                    // size of the data buffer
    volatile cyg_uint8 buffer[0];       // data buffer (simple mode)
} RFD;

#define RFD_STATUS_EL   0x80000000      // 1=last RFD in RFA
#define RFD_STATUS_S    0x40000000      // 1=suspend RU after receiving frame
#define RFD_STATUS_H    0x00100000      // 1=RFD is a header RFD
#define RFD_STATUS_SF   0x00080000      // 0=simplified, 1=flexible mode
#define RFD_STATUS_C    0x00008000      // completion of received frame
#define RFD_STATUS_OK   0x00002000      // frame received with no errors

#define RFD_RX_CRC          0x00000800  // crc error
#define RFD_RX_ALIGNMENT    0x00000400  // alignment error
#define RFD_RX_RESOURCE     0x00000200  // out of space, no resources
#define RFD_RX_DMA_OVER     0x00000100  // DMA overrun
#define RFD_RX_SHORT        0x00000080  // short frame error
#define RFD_RX_LENGTH       0x00000020  //
#define RFD_RX_ERROR        0x00000010  // receive error
#define RFD_RX_NO_ADR_MATCH 0x00000004  // no address match
#define RFD_RX_IA_MATCH     0x00000002  // individual address does not match
#define RFD_RX_TCO          0x00000001  // TCO indication


typedef struct {
    volatile cyg_uint32 count:14,       // bytes used in buffer
        f:1,                            // buffer has been used (filled)
        eof:1;                          // last receive buffer in frame
    volatile cyg_uint32 next_rbd;       // next RBD (RU base relative)
    volatile cyg_uint32 buffer_address; // address of receive data buffer
    volatile cyg_uint32 size:15,        // size of the associated buffer
        el:1;                           // buffer of this RBD is last
} RBD;


// ------------------------------------------------------------------------
//
//               TRANSMIT FRAME DESCRIPTORS
//
// ------------------------------------------------------------------------
typedef struct {
    volatile cyg_uint32 status:16,      // result of transmit operation
        command:16;                     // transmit command
    volatile cyg_uint32 link;           // offset from RU base to next RFD
    volatile cyg_uint32 tbd_address;    // pointer to Rx data buffer
    volatile cyg_uint32 count:15,       // number of bytes in transmit buffer
        eof:1,
        tx_threshold:8,
        tbd_number:8;
    volatile cyg_uint8 buffer[0];       // data buffer (simple mode)
} TxCB;


#define TxCB_CMD_TRANSMIT   0x0004      // transmit command
#define TxCB_CMD_SF         0x0008      // 0=simplified, 1=flexible mode
#define TxCB_CMD_NC         0x0010      // 0=CRC insert by controller
#define TxCB_CMD_I          0x2000      // generate interrupt on completion
#define TxCB_CMD_S          0x4000      // suspend on completion
#define TxCB_CMD_EL         0x8000      // last command block in CBL


// ------------------------------------------------------------------------
//
//                   STRUCTURES ADDED FOR PROMISCUOUS MODE
//
// ------------------------------------------------------------------------
typedef struct {
    cyg_uint32 cb_status_word:13,
        cb_ok:1,
        cb_dc:1,
        cb_complete:1,
        cb_cmd:3,
        cb_cmd_word:10,
        cb_int:1,
        cb_suspend:1,
        cb_el:1;
    cyg_uint32 cb_link_offset;
} CB_STRUCT;


typedef struct {
    CB_STRUCT cb_entry;
    cyg_uint8 config_bytes[24];
} CONFIG_CMD_STRUCT;

// ------------------------------------------------------------------------
//
//                       STATISTICAL COUNTER STRUCTURE
//
// ------------------------------------------------------------------------
#ifdef KEEP_STATISTICS
typedef struct {
    cyg_uint32 tx_good;
    cyg_uint32 tx_max_collisions;
    cyg_uint32 tx_late_collisions;
    cyg_uint32 tx_underrun;
    cyg_uint32 tx_carrier_loss;
    cyg_uint32 tx_deferred;
    cyg_uint32 tx_single_collisions;
    cyg_uint32 tx_mult_collisions;
    cyg_uint32 tx_total_collisions;
    cyg_uint32 rx_good;
    cyg_uint32 rx_crc_errors;
    cyg_uint32 rx_align_errors;
    cyg_uint32 rx_resource_errors;
    cyg_uint32 rx_overrun_errors;
    cyg_uint32 rx_collisions;
    cyg_uint32 rx_short_frames;
    cyg_uint32 done;
} I82559_COUNTERS;


typedef struct {
    cyg_uint32 rx_count;
    cyg_uint32 rx_deliver;
    cyg_uint32 rx_resource;
    cyg_uint32 rx_restart;
    cyg_uint32 tx_count;
    cyg_uint32 tx_complete;
    cyg_uint32 tx_dropped;
} STATISTICS;


STATISTICS statistics[2];
I82559_COUNTERS i82559_counters[2];

#endif // KEEP_STATISTICS

// ------------------------------------------------------------------------

#define MAX_RX_PACKET_SIZE  1536        // maximum Rx packet size
#define MAX_TX_PACKET_SIZE  1536        // maximum Tx packet size

#ifndef MAX_RX_DESCRIPTORS
#define MAX_RX_DESCRIPTORS	128     // number of Rx descriptors
#endif
#ifndef MAX_TX_DESCRIPTORS
#define MAX_TX_DESCRIPTORS	128     // number of Tx descriptors
#endif



typedef struct i82559 {
    cyg_uint8                           // (split up for atomic byte access)
        found:1,                        // was hardware discovered?
        mac_addr_ok:1,                  // can we bring up?
        active:1,                       // has this if been brung up?
        spare1:5; 
    cyg_uint8
        out_of_resources:1,             // need to restart rx engine.
        spare2:7; 
    cyg_uint8
        tx_in_progress:1,               // transmit in progress flag
        tx_queue_full:1,                // all Tx descriptors used flag
        spare3:6; 
    cyg_uint8  index;                   // 0 or 1 or whatever
    cyg_uint32 devid;                   // PCI device id
    cyg_uint32 memory_address;          // PCI memory address
    cyg_uint32 io_address;              // memory mapped I/O address
    cyg_uint8  mac_address[6];          // mac (hardware) address
    void *ndp;                          // Network Device Pointer

    int next_rx_descriptor;             // descriptor index for RFDs
    RFD *rx_ring[MAX_RX_DESCRIPTORS];   // location of Rx descriptors

    int tx_descriptor_add;              // descriptor index for additions
    int tx_descriptor_remove;           // descriptor index for removals
    TxCB *tx_ring[MAX_TX_DESCRIPTORS];  // location of Tx descriptors
    unsigned long tx_keys[MAX_TX_DESCRIPTORS];
                                        // keys for tx q management

    // Interrupt handling stuff
    cyg_vector_t    vector;             // interrupt vector
    cyg_handle_t    interrupt_handle;   // handle for int.handler
    cyg_interrupt   interrupt_object;

#ifdef KEEP_STATISTICS
    void *p_statistics;                 // pointer to statistical counters
#endif

} I82559;


// This is encapsulated here so that a change to > 2 interfaces can
// easily be accommodated.

#define IF_BAD_82559( _p_ )                                     \
    CYG_ASSERT( (&i82559[0] == (_p_)) || (&i82559[1] == (_p_)), \
                "Bad pointer-to-i82559" );                      \
    if ( (&i82559[0] != (_p_)) && (&i82559[1] != (_p_)) )

// ------------------------------------------------------------------------
// Instantiate the interfaces that we have:

#define MAX_82559 2                     // number of interfaces

I82559 i82559[MAX_82559];               // i82559 device info. structure

// eth0

ETH_DRV_SC(ebsa285_sc0,
           &i82559[0],                  // Driver specific data
           "eth0",                      // Name for this interface
           i82559_start,
           i82559_stop,
           i82559_ioctl,
           i82559_can_send,
           i82559_send,
           i82559_recv);

NETDEVTAB_ENTRY(ebsa285_netdev0, 
                "ebsa285-0", 
                ebsa285_i82559_init, 
                &ebsa285_sc0);

#if 1

// eth1

ETH_DRV_SC(ebsa285_sc1,
           &i82559[1],                  // Driver specific data
           "eth1",                      // Name for this interface
           i82559_start,
           i82559_stop,
           i82559_ioctl,
           i82559_can_send,
           i82559_send,
           i82559_recv);

NETDEVTAB_ENTRY(ebsa285_netdev1, 
                "ebsa285-1", 
                ebsa285_i82559_init, 
                &ebsa285_sc1);

#else
int ebsa285_netdev1 = -1; // for asserts about valid addresses
int ebsa285_sc1 = -1;
#endif // eth1 is included

// This is in a macro so that if more devices arrive it can easily be changed
#define CHECK_NDP_SC_LINK() CYG_MACRO_START                                \
    CYG_ASSERT( ((void *)ndp == (void *)&ebsa285_netdev0) ||               \
                ((void *)ndp == (void *)&ebsa285_netdev1), "Bad ndp" );    \
    CYG_ASSERT( ((void *)sc == (void *)&ebsa285_sc0) ||                    \
                ((void *)sc == (void *)&ebsa285_sc1), "Bad sc" );          \
    CYG_ASSERT( (void *)p_i82559 == sc->driver_private, "sc pointer bad" );\
CYG_MACRO_END

// ------------------------------------------------------------------------
//
// Communications from ISR to DSR
//
// ------------------------------------------------------------------------

#define PACKET_RX   1
#define PACKET_TX   2

typedef struct {
    cyg_uint32 request;
    void *p;
    unsigned long key;
} I82559_QUEUE;

// 82559 thread request queue
#define I82559_QUEUE_SIZE \
   ((MAX_RX_DESCRIPTORS + MAX_TX_DESCRIPTORS) * MAX_82559)

I82559_QUEUE i82559_queue[I82559_QUEUE_SIZE];
int i82559_q_in;                        // queue insertion index
int i82559_q_out;                       // queue removal index
int i82559_q_full;                      // queue full flag


// ------------------------------------------------------------------------
//
// Managing the memory that is windowed onto the PCI bus
//
// ------------------------------------------------------------------------

static cyg_uint32 i82559_heap_size;
static cyg_uint8 *i82559_heap_base;
static cyg_uint8 *i82559_heap_free;

static void *mem_reserved_ioctl = (void*)0;
// uncacheable memory reserved for ioctl calls

// ------------------------------------------------------------------------
//
//                       FUNCTION PROTOTYPES
//
// ------------------------------------------------------------------------

static int pci_init_find_82559s(void);

static void i82559_reset(struct i82559* p_i82559);

static void InitRxRing(struct i82559* p_i82559);
static void ResetRxRing(struct i82559* p_i82559);
static void PacketRxReady(struct i82559* p_i82559);
static void InitTxRing(struct i82559* p_i82559);
static void ResetTxRing(struct i82559* p_i82559);

static int write_enable_eeprom(long ,  int );
static void program_eeprom(cyg_uint32 , cyg_uint32 , cyg_uint8 * );

static int eth_set_promiscuous_mode(struct i82559* p_i82559);

// ------------------------------------------------------------------------
// utility that is used in statistics routine
// ------------------------------------------------------------------------
static inline void wait_for_cmd_done(long scb_ioaddr)
{
    int wait = 10000;
    do /* nothing */ ;
    while( INB(scb_ioaddr) && --wait >= 0);
}

static inline void Mask82559Interrupt(struct i82559* p_i82559)
{
    cyg_drv_interrupt_mask(p_i82559->vector);
    cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_PCI_IRQ);
}

static inline void UnMask82559Interrupt(struct i82559* p_i82559)
{
    cyg_drv_interrupt_unmask(p_i82559->vector);
    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_PCI_IRQ);
}


static void udelay(int delay)
{
  int i;
  // the loop is going to take 3 ticks.  At 228 MHz, to give uS, multiply
  // by 228/3 = 76 near enough.  No volatile is needed on i; gcc recognizes
  // delay loops and does NOT elide them.
  for ( i = 76 * delay; i ; i--)
    ;
}

// ------------------------------------------------------------------------
//
//
//           CODE FOR DEBUGGING PURPOSES ONLY
//
//
// ------------------------------------------------------------------------
void dump_txcb(TxCB *p_txcb)
{
    os_printf("TxCB @ %x\n", (int)p_txcb);
    os_printf("status = %04X ", p_txcb->status);
    os_printf("command = %04X ", p_txcb->command);
    os_printf("link = %08X ", p_txcb->link);
    os_printf("tbd = %08X ", p_txcb->tbd_address);
    os_printf("count = %d ", p_txcb->count);
    os_printf("eof = %x ", p_txcb->eof);
    os_printf("threshold = %d ", p_txcb->tx_threshold);
    os_printf("tbd number = %d\n", p_txcb->tbd_number);
}


// This is intended to be the body of a THREAD that prints stuff every 10
// seconds or so:
#ifdef KEEP_STATISTICS
#ifdef DISPLAY_STATISTICS
void DisplayStatistics(void)
{
    int i;
    I82559_COUNTERS *p_statistics;
    cyg_uint32 *p_counter;
    cyg_uint32 *p_register;
    int reg_count;
    int status;
    
    while ( 1 ) {
#ifdef DISPLAY_82559_STATISTICS
        for ( i = 0; i < 2; i ++ ) {
            p_statistics = (I82559_COUNTERS *)i82559[i].p_statistics;
            if ( (p_statistics->done & 0xFFFF) == 0xA007 ) {
                p_counter = (cyg_uint32 *)&i82559_counters[i];
                p_register = (cyg_uint32 *)&p_statistics->tx_good;
                for ( reg_count = 20; reg_count != 0; reg_count--) {
                    *p_counter += *p_register;
                    p_counter++;
                    p_register++;
                }
                p_statistics->done = 0;
                // make sure no command operating
            	wait_for_cmd_done(i82559[i].io_address + SCBCmd);
                // start register dump
                OUTW(CU_DUMPSTATS, i82559[i].io_address + SCBCmd);
            }
        }
#endif
        os_printf("\nRx\nPackets = %d  %d\n",
        statistics[0].rx_count, statistics[1].rx_count);
        os_printf("Deliver   %d  %d\n",
        statistics[0].rx_deliver, statistics[1].rx_deliver);
        os_printf("Resource  %d  %d\n",
        statistics[0].rx_resource, statistics[1].rx_resource);
        os_printf("Restart   %d  %d\n",
        statistics[0].rx_restart, statistics[1].rx_restart);

#ifdef DISPLAY_82559_STATISTICS
        os_printf("Count     %d  %d\n",
        i82559_counters[0].rx_good, i82559_counters[1].rx_good);
        os_printf("CRC       %d  %d\n",
        i82559_counters[0].rx_crc_errors, i82559_counters[1].rx_crc_errors);
        os_printf("Align     %d  %d\n",
        i82559_counters[0].rx_align_errors, i82559_counters[1].rx_align_errors);
        os_printf("Resource  %d  %d\n",
        i82559_counters[0].rx_resource_errors, i82559_counters[1].rx_resource_errors);
        os_printf("Overrun   %d  %d\n",
        i82559_counters[0].rx_overrun_errors, i82559_counters[1].rx_overrun_errors);
        os_printf("Collision %d  %d\n",
        i82559_counters[0].rx_collisions, i82559_counters[1].rx_collisions);
        os_printf("Short     %d  %d\n",
        i82559_counters[0].rx_short_frames, i82559_counters[1].rx_short_frames);
#endif
        os_printf("\nTx\nPackets = %d  %d\n",
        statistics[0].tx_count, statistics[1].tx_count);
        os_printf("Complete  %d  %d\n",
        statistics[0].tx_complete, statistics[1].tx_complete);
        os_printf("Dropped   %d  %d\n",
        statistics[0].tx_dropped, statistics[1].tx_dropped);
        os_printf("Count     %d  %d\n",
        i82559_counters[0].tx_good, i82559_counters[1].tx_good);
#ifdef DISPLAY_82559_STATISTICS
        os_printf("Collision %d  %d\n",
        i82559_counters[0].tx_max_collisions,i82559_counters[1].tx_max_collisions);
        os_printf("Late Col. %d  %d\n",
        i82559_counters[0].tx_late_collisions,i82559_counters[1].tx_late_collisions);
        os_printf("Underrun  %d  %d\n",
        i82559_counters[0].tx_underrun,i82559_counters[1].tx_underrun);
        os_printf("Carrier   %d  %d\n",
        i82559_counters[0].tx_carrier_loss,i82559_counters[1].tx_carrier_loss);
        os_printf("Deferred  %d  %d\n",
        i82559_counters[0].tx_deferred, i82559_counters[1].tx_deferred);
        os_printf("1 Col     %d  %d\n",
        i82559_counters[0].tx_single_collisions, i82559_counters[0].tx_single_collisions);
        os_printf("Mult. Col %d  %d\n",
        i82559_counters[0].tx_mult_collisions, i82559_counters[0].tx_mult_collisions);
        os_printf("Total Col %d  %d\n",
        i82559_counters[0].tx_total_collisions, i82559_counters[0].tx_total_collisions);
#endif
        status = INB(i82559[0].io_address + SCBGenStatus);
        os_printf("Interface 0 Link = %s, %s Mbps, %s Duplex\n",
            status & GEN_STATUS_LINK ? "Up" : "Down",
            status & GEN_STATUS_100MBPS ?  "100" : "10",
            status & GEN_STATUS_FDX ? "Full" : "Half");

        status = INB(i82559[1].io_address + SCBGenStatus);
        os_printf("Interface 1 Link = %s, %s Mbps, %s Duplex\n",
            status & GEN_STATUS_LINK ? "Up" : "Down",
            status & GEN_STATUS_100MBPS ?  "100" : "10",
            status & GEN_STATUS_FDX ? "Full" : "Half");

        cyg_thread_delay(1000);
    }
}
#endif // DISPLAY_STATISTICS
#endif // KEEP_STATISTICS

void dump_rfd(RFD *p_rfd, int anyway )
{
    if ( (0 != p_rfd->status) || anyway ) {
        os_printf("RFD @ %x = ", (int)p_rfd);
        os_printf("status = %x ", p_rfd->status);
        os_printf("link = %x ", p_rfd->link);
//        os_printf("rdb_address = %x ", p_rfd->rdb_address);
        os_printf("count = %x ", p_rfd->count);
        os_printf("f = %x ", p_rfd->f);
        os_printf("eof = %x ", p_rfd->eof);
        os_printf("size = %x\n", p_rfd->size);
        os_printf("[%04x %04x %04x] ",
                  *((cyg_uint16 *)(&(p_rfd->buffer[0]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[2]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[4]))) );
        os_printf("[%04x %04x %04x] %04x : ",          
                  *((cyg_uint16 *)(&(p_rfd->buffer[6]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[8]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[10]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[12]))) );
        os_printf("(%04x %04x %04x %04x) ",            
                  *((cyg_uint16 *)(&(p_rfd->buffer[14]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[16]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[18]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[20]))) );
        os_printf("[%04x %04x %04x] ",                 
                  *((cyg_uint16 *)(&(p_rfd->buffer[22]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[24]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[26]))) );
        os_printf("%d.%d.%d.%d ",
                  *((cyg_uint8  *)(&(p_rfd->buffer[28]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[29]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[30]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[31]))) );
        os_printf("[%04x %04x %04x] ",                 
                  *((cyg_uint16 *)(&(p_rfd->buffer[32]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[34]))),
                  *((cyg_uint16 *)(&(p_rfd->buffer[36]))) );
        os_printf("%d.%d.%d.%d ...\n",
                  *((cyg_uint8  *)(&(p_rfd->buffer[38]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[39]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[40]))),
                  *((cyg_uint8  *)(&(p_rfd->buffer[41]))) );
    }
}

void dump_all_rfds( int intf )
{
    struct i82559* p_i82559 = &i82559[intf];
    int i, j;
    j = p_i82559->next_rx_descriptor;
    os_printf("rx descriptors for interface %d (eth%d):\n", intf, intf );
    for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ )
        dump_rfd( p_i82559->rx_ring[i], (i > (j-3) && (i <= j)) );
    os_printf("next rx descriptor = %x\n\n", j);
}


void dump_packet(cyg_uint8 *p_buffer, int length)
{
    int count;

    count = 0;
    while ( length > 0 ) {
        if ( count == 0 )
            os_printf("\n");
        count = (count + 1) & 0x0F;
        os_printf("%02X ", *p_buffer++);
        length--;
    }
    os_printf("\n");
}



// ------------------------------------------------------------------------
// Memory management
//
// Simply carve off from the front of the PCI mapped window into real memory

static void *pciwindow_mem_alloc(int size)
{
    void *p_memory;

    CYG_ASSERT(
        (CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE <= (int)i82559_heap_free)
        &&
        ((CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE + 
          CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_SIZE) > (int)i82559_heap_free)
        &&
        (0 < i82559_heap_size)
        &&
        (CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_SIZE >= i82559_heap_size)
        &&
        (CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE == (int)i82559_heap_base),
        "Heap variables corrupted" );

    p_memory = (void *)0;
    size = (size + 3) & ~3;
    if ( (i82559_heap_free+size) < (i82559_heap_base+i82559_heap_size) ) {
        cyg_uint32 *p;
        p_memory = (void *)i82559_heap_free;
        i82559_heap_free += size;
        for ( p = (cyg_uint32 *)p_memory; size > 0; size -= 4 )
            *p++ = 0;
    }

    return p_memory;
}



// ------------------------------------------------------------------------
//
//                       READ EEPROM
//
// ------------------------------------------------------------------------
static int read_eeprom(long ioaddr, int location, int addr_len)
{
    unsigned short retval = 0;
    int ee_addr = ioaddr + SCBeeprom;
    int read_cmd = location | EE_READ_CMD;
    int i;
    
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    eeprom_delay(1);
    OUTW(EE_ENB, ee_addr);
    eeprom_delay(1);
    
    // Shift the read command bits out.
    for (i = 12; i >= 0; i--) {
        short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
        OUTW(EE_ENB | dataval, ee_addr);
        eeprom_delay(1);
        OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
        eeprom_delay(1);
    }
    OUTW(EE_ENB, ee_addr);
    eeprom_delay(1);
    
    for (i = 15; i >= 0; i--) {
        OUTW(EE_ENB | EE_SHIFT_CLK, ee_addr);
        eeprom_delay(1);
        retval = (retval << 1) | ((INW(ee_addr) & EE_DATA_READ) ? 1 : 0);
        OUTW(EE_ENB, ee_addr);
        eeprom_delay(1);
    }
    
    // Terminate the EEPROM access.
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    return retval;
}


// ------------------------------------------------------------------------
//
//                NETWORK INTERFACE INITIALIZATION
//
//  Function : Init82559
//
//  Description :
//       This routine resets, configures, and initializes the chip.
//       It also clears the ethernet statistics structure, and selects
//       which statistics are supported by this driver.
//
// ------------------------------------------------------------------------
static bool
ebsa285_i82559_init(struct cyg_netdevtab_entry * ndp)
{
    static int initialized = 0; // only probe PCI et al *once*

    struct eth_drv_sc *sc;
    cyg_uint32 selftest;
    volatile cyg_uint32 *p_selftest;
    cyg_uint32 ioaddr;
    cyg_uint16 checksum;
    int count;
    int i;
    int addr_length;
    cyg_uint8 mac_address[6];
    struct i82559 *p_i82559;

#ifdef DEBUG
    db_printf("ebsa285_i82559_init\n");
#endif

    sc = (struct eth_drv_sc *)(ndp->device_instance);
    p_i82559 = (struct i82559 *)(sc->driver_private);

    IF_BAD_82559( p_i82559 ) {
        os_printf( "Bad device private pointer %x\n", sc->driver_private );
        return 0;
    }

    CHECK_NDP_SC_LINK();

    if ( 0 == initialized++ ) {
        // then this is the first time ever:
        if ( ! pci_init_find_82559s() ) {
            os_printf( "pci_init_find_82559s failed" );
            return 0;
        }
    }

    if ( ! p_i82559->found ) // no device on PCI bus
        return (0);

    ioaddr = p_i82559->io_address; // get I/O address for 82559

#ifdef DEBUG
    os_printf("Init82559 %d @ %x\n82559 Self Test\n",
              p_i82559->index, (int)ndp);
#endif

    Mask82559Interrupt(p_i82559);

    wait_for_cmd_done(ioaddr + SCBCmd); // make sure no command operating

    i82559_reset(p_i82559);

    // Perform a system self-test. (get enough mem to round address)
    if ( (selftest = (cyg_uint32)pciwindow_mem_alloc(32) ) == 0)
        return (0);
    p_selftest = (cyg_uint32 *) ((selftest + 15) & ~0xf);
    p_selftest[0] = p_selftest[1] = -1;
        
    OUTL( (VIRT_TO_BUS(p_selftest)) | I82559_SELFTEST, ioaddr + SCBPort);
    count = 0x7FFFF;                // Timeout for self-test.
    do {
        udelay(10);
    } while ( (p_selftest[1] == -1)  &&  (--count >= 0) );

    UnMask82559Interrupt(p_i82559);
    
    if (count < 0) {
        // Test timed out.
        os_printf("Self test failed\n");
        return (0);
    }
#ifdef DEBUG
    os_printf("  General self-test: %s.\n"
              "  Serial sub-system self-test: %s.\n"
              "  Internal registers self-test: %s.\n"
              "  ROM checksum self-test: %s (%08X).\n",
              p_selftest[1] & 0x1000 ? "failed" : "passed",
              p_selftest[1] & 0x0020 ? "failed" : "passed",
              p_selftest[1] & 0x0008 ? "failed" : "passed",
              p_selftest[1] & 0x0004 ? "failed" : "passed",
              p_selftest[0]);
#endif

    // read eeprom and get 82559's mac address
    addr_length = (read_eeprom(ioaddr, 0, 6) == 0xffff) ? 8 : 6;
    // (this is the length of the *EEPROM*s address, not MAC address)

    for (checksum = 0, i = 0, count = 0; count < 64; count++) {
        cyg_uint16 value;
        // read word from eeprom
        value = read_eeprom(ioaddr, count, addr_length);
        checksum += value;
        if (count < 3) {
            mac_address[i++] = value & 0xFF;
            mac_address[i++] = (value >> 8) & 0xFF;
        }
    }

    // If the EEPROM checksum is wrong, the MAC address read from the
    // EEPROM is probably wrong as well. In that case, we don't set
    // mac_addr_ok, but continue the initialization. If then somebody calls
    // i82559_start without calling eth_set_mac_address() first, we refuse
    // to bring up the interface, because running with an invalid MAC
    // address is not a very brilliant idea.

    if ((checksum & 0xFFFF) != 0xBABA)  {
        // selftest verified checksum, verify again
        os_printf( "Warning: Invalid EEPROM checksum %04X for device %d\n",
                   checksum, p_i82559->index);
    } else {
        p_i82559->mac_addr_ok = 1;
    }
#ifdef DEBUG
    os_printf("Valid EEPROM checksum\n");
    os_printf("MAC Address = %02X %02X %02X %02X %02X %02X\n",
              mac_address[0], mac_address[1], mac_address[2], mac_address[3],
              mac_address[4], mac_address[5]);
#endif

    // record the MAC address in the device structure
    p_i82559->mac_address[0] = mac_address[0];
    p_i82559->mac_address[1] = mac_address[1];
    p_i82559->mac_address[2] = mac_address[2];
    p_i82559->mac_address[3] = mac_address[3];
    p_i82559->mac_address[4] = mac_address[4];
    p_i82559->mac_address[5] = mac_address[5];
    
    // and record the net dev pointer
    p_i82559->ndp = (void *)ndp;
    
    InitRxRing(p_i82559);
    InitTxRing(p_i82559);

    // Initialize upper level driver
    if ( p_i82559->mac_addr_ok )
        eth_drv_init(sc, &(p_i82559->mac_address[0]) );
    else
        eth_drv_init(sc, 0 );

    return (1);
}



// ------------------------------------------------------------------------
//
//  Function : i82559_start
//
// ------------------------------------------------------------------------
static void i82559_start( struct eth_drv_sc *sc,
                          unsigned char *enaddr, int flags )
{
    struct i82559 *p_i82559;
    cyg_uint32 ioaddr;
#ifdef KEEP_STATISTICS
    void *p_statistics;
#endif

    p_i82559 = (struct i82559 *)sc->driver_private;
    
    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_start: Bad device pointer %x\n", p_i82559 );
        return;
    }

    if ( ! p_i82559->mac_addr_ok ) {
        os_printf("i82559_start %d: invalid MAC address, "
                  "can't bring up interface\n",
                  p_i82559->index );
        return;
    }

    if ( p_i82559->active )
        i82559_stop( sc );

    ioaddr = p_i82559->io_address; // get 82559's I/O address

#ifdef KEEP_STATISTICS
#ifdef DISPLAY_82559_STATISTICS
    p_i82559->p_statistics =
        p_statistics = pciwindow_mem_alloc(sizeof(I82559_COUNTERS));
    memset(p_statistics, 0xFFFFFFFF, sizeof(I82559_COUNTERS));
    wait_for_cmd_done(ioaddr + SCBCmd); // make sure no command operating
                                        // set statistics dump address
    OUTL(VIRT_TO_BUS(p_statistics), ioaddr + SCBPointer);
    OUTW(SCB_M | CU_STATSADDR, ioaddr + SCBCmd);

    wait_for_cmd_done(ioaddr + SCBCmd); // make sure no command operating
    OUTW(SCB_M | CU_DUMPSTATS, ioaddr + SCBCmd); // start register dump
#endif
#endif

    wait_for_cmd_done(ioaddr + SCBCmd); // make sure no command operating

    OUTL(0, ioaddr + SCBPointer);       // load ru base address = 0
    OUTW(SCB_M | RUC_ADDR_LOAD, ioaddr + SCBCmd);

    wait_for_cmd_done(ioaddr + SCBCmd); // wait for SCB command complete
                                        // load pointer to Rx Ring
    OUTL(VIRT_TO_BUS(p_i82559->rx_ring[0]), ioaddr + SCBPointer);
    OUTW(RUC_START, ioaddr + SCBCmd);
    
    p_i82559->out_of_resources = 0;
    p_i82559->active = 1;

    if ( 0
#ifdef ETH_DRV_FLAGS_PROMISC_MODE
         != (flags & ETH_DRV_FLAGS_PROMISC_MODE)
#endif
        ) {
        eth_set_promiscuous_mode(p_i82559);
    }
#ifdef DEBUG
    {
        int status;
        status = INB(ioaddr + SCBGenStatus);
        os_printf("i82559_start %d flg %x Link = %s, %s Mbps, %s Duplex\n",
                  p_i82559->index,
                  *(int *)p_i82559,
                  status & GEN_STATUS_LINK ? "Up" : "Down",
                  status & GEN_STATUS_100MBPS ?  "100" : "10",
                  status & GEN_STATUS_FDX ? "Full" : "Half");
    }
#endif
}


// ------------------------------------------------------------------------
//
//  Function : BringDown82559
//
// ------------------------------------------------------------------------

static void i82559_stop( struct eth_drv_sc *sc )
{
    struct i82559 *p_i82559;

    p_i82559 = (struct i82559 *)sc->driver_private;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_stop: Bad device pointer %x\n", p_i82559 );
        return;
    }
   
#ifdef DEBUG
    os_printf("i82559_stop %d flg %x\n", p_i82559->index, *(int *)p_i82559 );
#endif

    i82559_reset(p_i82559);             // that should stop it
    p_i82559->active = 0;               // and stop people tormenting it

    ResetRxRing( p_i82559 );
    ResetTxRing( p_i82559 );
}


// ------------------------------------------------------------------------
//
//  Function : InitRxRing
//
// ------------------------------------------------------------------------
static void InitRxRing(struct i82559* p_i82559)
{
    int i;
    RFD *rfd;
    RFD *p_rfd = 0;
#ifdef DEBUG_82559
    os_printf("InitRxRing %d\n", p_i82559->index);
#endif
    for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ ) {
        rfd = (RFD *)pciwindow_mem_alloc(sizeof(RFD) + MAX_RX_PACKET_SIZE);
        p_i82559->rx_ring[i] = rfd;
        if ( i )
            p_rfd->link = VIRT_TO_BUS(rfd);
        p_rfd = (RFD *)rfd;
    }
    // link last RFD to first:
    p_rfd->link = VIRT_TO_BUS(p_i82559->rx_ring[0]);

    ResetRxRing( p_i82559 );
}

// ------------------------------------------------------------------------
//
//  Function : ResetRxRing
//
// ------------------------------------------------------------------------
static void ResetRxRing(struct i82559* p_i82559)
{
    RFD *p_rfd;
    int i;
#ifdef DEBUG_82559
    os_printf("ResetRxRing %d\n", p_i82559->index);
#endif
    for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ ) {
        p_rfd = p_i82559->rx_ring[i];
        CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" );
        CYG_ASSERT( (cyg_uint8 *)p_rfd <  i82559_heap_free, "rfd over" );
        CYG_ASSERT( p_i82559->rx_ring[
                 ( i ? (i-1) : (MAX_RX_DESCRIPTORS-1) )
            ]->link == VIRT_TO_BUS(p_rfd), "rfd linked list broken" );
        p_rfd->status = 0;
        p_rfd->count = 0;
        p_rfd->f = 0;
        p_rfd->eof = 0;
        p_rfd->rdb_address = 0xFFFFFFFF;
        p_rfd->size = MAX_RX_PACKET_SIZE;
    }
    p_i82559->next_rx_descriptor = 0;
}

// ------------------------------------------------------------------------
//
//  Function : PacketRx
//
// ------------------------------------------------------------------------
static void PacketRxReady(struct i82559* p_i82559)
{
    struct cyg_netdevtab_entry *ndp;
    struct eth_drv_sc *sc;
    RFD *p_rfd;
    int next_descriptor;
    int length;

    ndp = (struct cyg_netdevtab_entry *)(p_i82559->ndp);
    sc = (struct eth_drv_sc *)(ndp->device_instance);

    CHECK_NDP_SC_LINK();

    next_descriptor = p_i82559->next_rx_descriptor;
    p_rfd = p_i82559->rx_ring[next_descriptor];

    CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" );
    CYG_ASSERT( (cyg_uint8 *)p_rfd <  i82559_heap_free, "rfd over" );

    while ( p_rfd->status & RFD_STATUS_C ) {
        p_rfd->status |= RFD_STATUS_EL;
        length = p_rfd->count;

#ifdef DEBUG_82559
        os_printf( "Device %d (eth%d), rx descriptor %d:\n", 
                   p_i82559->index, p_i82559->index, next_descriptor );
//        dump_rfd( p_rfd, 1 );
#endif

        // Offer the data to the network stack
        p_i82559->next_rx_descriptor = next_descriptor;
        eth_drv_recv( sc, length );

        p_rfd->count = 0;
        p_rfd->f = 0;
        p_rfd->eof = 0;
        p_rfd->status = 0;
#ifdef KEEP_STATISTICS
        statistics[p_i82559->index].rx_deliver++;
#endif
        if (++next_descriptor >= MAX_RX_DESCRIPTORS)
            next_descriptor = 0;
        p_rfd = p_i82559->rx_ring[next_descriptor];

        CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" );
        CYG_ASSERT( (cyg_uint8 *)p_rfd <  i82559_heap_free, "rfd over" );
    }

    if ( p_i82559->out_of_resources ) { // out of Rx resources ?
        if ( ! (p_rfd->status & RFD_STATUS_C) ) { // all buffers processed ?
            cyg_uint32 ioaddr;
            // yes, restart RU
#ifdef KEEP_STATISTICS
            statistics[p_i82559->index].rx_restart++;
#endif
            next_descriptor = 0;        // re-initialize next desc.
            p_i82559->out_of_resources = 0; // clear out of resource flag
            ioaddr = p_i82559->io_address; // get I/O address

            // wait for SCB command complete
            wait_for_cmd_done(ioaddr + SCBCmd);
            OUTW(RUC_ABORT, ioaddr + SCBCmd);
            
            // make sure no command operating
            wait_for_cmd_done(ioaddr + SCBCmd);

            OUTL(0, ioaddr + SCBPointer); // load ru base address = 0
            OUTW(SCB_M | RUC_ADDR_LOAD, ioaddr + SCBCmd);

            // wait for SCB command complete
            wait_for_cmd_done(ioaddr + SCBCmd);
            // load pointer to Rx Ring
            OUTL(VIRT_TO_BUS(p_i82559->rx_ring[0]),
                 ioaddr + SCBPointer);
            OUTW(RUC_START, ioaddr + SCBCmd);
        }
    }
    p_i82559->next_rx_descriptor = next_descriptor;
}

// and the callback function

static void i82559_recv( struct eth_drv_sc *sc,
                         struct eth_drv_sg *sg_list, int sg_len )
{
    struct i82559 *p_i82559;
    RFD *p_rfd;
    int next_descriptor;
    int total_len;
    struct eth_drv_sg *last_sg;
    volatile cyg_uint8 *from_p;

    p_i82559 = (struct i82559 *)sc->driver_private;
    
    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_recv: Bad device pointer %x\n", p_i82559 );
        return;
    }

    next_descriptor = p_i82559->next_rx_descriptor;
    p_rfd = p_i82559->rx_ring[next_descriptor];
    
    CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" );
    CYG_ASSERT( (cyg_uint8 *)p_rfd <  i82559_heap_free, "rfd over" );

    CYG_ASSERT( p_rfd->status & RFD_STATUS_C, "No complete frame" );
    CYG_ASSERT( p_rfd->status & RFD_STATUS_EL, "No marked frame" );
    
    if ( 0 == (p_rfd->status & RFD_STATUS_C) )
        return;
        
    total_len = p_rfd->count;
    
#ifdef DEBUG_82559
    os_printf("Rx %d %x (status %x): %d sg's, %d bytes\n",
              p_i82559->index, (int)priv, p_rfd->status, sg_len, total_len);
#endif

    // Copy the data to the network stack
    from_p = &p_rfd->buffer[0];

    // check we have memory to copy into; we would be called even if
    // caller was out of memory in order to maintain our state.
    if ( 0 == sg_len || 0 == sg_list )
        return; // caller was out of mbufs

    CYG_ASSERT( 0 < sg_len, "sg_len underflow" );
    CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" );

    for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
        cyg_uint8 *to_p;
        int l;
            
        to_p = (cyg_uint8 *)(sg_list->buf);
        l = sg_list->len;

        if ( 0 == l || 0 == to_p )
            return; // caller was out of mbufs

        if ( l > total_len )
            l = total_len;

        memcpy( to_p, (unsigned char *)from_p, l );
        from_p += l;
        total_len -= l;
    }

    CYG_ASSERT( 0 == total_len, "total_len mismatch in rx" );
    CYG_ASSERT( last_sg == sg_list, "sg count mismatch in rx" );
    CYG_ASSERT( &p_rfd->buffer[0] < from_p, "from_p wild in rx" );
    CYG_ASSERT( &p_rfd->buffer[0] + MAX_RX_PACKET_SIZE >= from_p,
                "from_p overflow in rx" );
}    


// ------------------------------------------------------------------------
//
//  Function : InitTxRing
//
// ------------------------------------------------------------------------
static void InitTxRing(struct i82559* p_i82559)
{
    int i;
    cyg_uint32 ioaddr;

#ifdef DEBUG_82559
    os_printf("InitTxRing %d\n", p_i82559->index);
#endif
    ioaddr = p_i82559->io_address;
    for ( i = 0; i < MAX_TX_DESCRIPTORS; i++) {
        p_i82559->tx_ring[i] = (TxCB *)pciwindow_mem_alloc(
            sizeof(TxCB) + MAX_TX_PACKET_SIZE);
        p_i82559->tx_keys[i] = 0;
    }

    ResetTxRing(p_i82559);
}

// ------------------------------------------------------------------------
//
//  Function : ResetTxRing
//
// ------------------------------------------------------------------------
static void ResetTxRing(struct i82559* p_i82559)
{
    int i;
    cyg_uint32 ioaddr;

#ifdef DEBUG_82559
    os_printf("ResetTxRing %d\n", p_i82559->index);
#endif
    ioaddr = p_i82559->io_address;
    p_i82559->tx_descriptor_add =
        p_i82559->tx_descriptor_remove = 0;
    p_i82559->tx_in_progress =
        p_i82559->tx_queue_full = 0;

    for ( i = 0; i < MAX_TX_DESCRIPTORS; i++) {
        TxCB *p_txcb = p_i82559->tx_ring[i];
        CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" );
        CYG_ASSERT( (cyg_uint8 *)p_txcb <  i82559_heap_free, "txcb over" );

        p_txcb->status = 0;
        p_txcb->command = 0;
        p_txcb->link = VIRT_TO_BUS((cyg_uint32)p_txcb);
        p_txcb->tbd_address = 0xFFFFFFFF;
        p_txcb->tbd_number = 0;
        p_txcb->tx_threshold = 16;
        p_txcb->eof = 1;
        p_txcb->count = 0;
        p_i82559->tx_keys[i] = 0;
    }
    
    wait_for_cmd_done(ioaddr + SCBCmd);
    OUTL(0, ioaddr + SCBPointer);
    OUTW(CU_CMD_BASE, ioaddr + SCBCmd);
}



// ------------------------------------------------------------------------
//
//  Function : i82559_can_send
//
// ------------------------------------------------------------------------

static int 
i82559_can_send(struct eth_drv_sc *sc)
{
//    return 1;

    struct i82559 *p_i82559;

    p_i82559 = (struct i82559 *)sc->driver_private;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_send: Bad device pointer %x\n", p_i82559 );
        return 0;
    }

    return ! p_i82559->tx_queue_full;
}

// ------------------------------------------------------------------------
//
//  Function : i82559_send
//
// ------------------------------------------------------------------------

static void 
i82559_send(struct eth_drv_sc *sc,
            struct eth_drv_sg *sg_list, int sg_len, int total_len,
            unsigned long key)
{
    struct i82559 *p_i82559;
    int tx_descriptor_add;
    TxCB *p_txcb;
    cyg_uint32 ioaddr;

    p_i82559 = (struct i82559 *)sc->driver_private;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_send: Bad device pointer %x\n", p_i82559 );
        return;
    }

#ifdef DEBUG_82559
    os_printf("Tx %d %x: %d sg's, %d bytes, KEY %x\n",
              p_i82559->index, (int)priv, sg_len, total_len, key );
#endif

    if ( ! p_i82559->active )
        return;                         // device inactive, no return
#ifdef KEEP_STATISTICS
    statistics[p_i82559->index].tx_count++;
#endif
    ioaddr = p_i82559->io_address;      // get device I/O address

    if ( p_i82559->tx_queue_full ) {
#ifdef KEEP_STATISTICS
        statistics[p_i82559->index].tx_dropped++
#endif
        os_printf( "i82559_send: Queue full, device %x, key %x\n",
                   p_i82559, key );
    }
    else {
        struct eth_drv_sg *last_sg;
        volatile cyg_uint8 *to_p;

        tx_descriptor_add = p_i82559->tx_descriptor_add;

        p_i82559->tx_keys[tx_descriptor_add] = key;

        p_txcb = p_i82559->tx_ring[tx_descriptor_add];

        CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" );
        CYG_ASSERT( (cyg_uint8 *)p_txcb <  i82559_heap_free, "txcb over" );

        p_txcb->status = 0;
        p_txcb->command = TxCB_CMD_TRANSMIT | TxCB_CMD_S
                                | TxCB_CMD_I | TxCB_CMD_EL;
        p_txcb->link = VIRT_TO_BUS((cyg_uint32)p_txcb);
        p_txcb->tbd_address = 0xFFFFFFFF;
        p_txcb->tbd_number = 0;
        p_txcb->tx_threshold = 16;
        p_txcb->eof = 1;
        p_txcb->count = total_len;

        // Copy from the sglist into the txcb
        to_p = &p_txcb->buffer[0];

        CYG_ASSERT( 0 < sg_len, "sg_len underflow" );
        CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" );

        for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
            cyg_uint8 *from_p;
            int l;
            
            from_p = (cyg_uint8 *)(sg_list->buf);
            l = sg_list->len;

            if ( l > total_len )
                l = total_len;

            memcpy( (unsigned char *)to_p, from_p, l );
            to_p += l;
            total_len -= l;

            if ( 0 > total_len ) 
                break; // Should exit via sg_last normally
        }

        CYG_ASSERT( 0 == total_len, "length mismatch in tx" );
        CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" );
        CYG_ASSERT( &p_txcb->buffer[0] < to_p, "to_p wild in tx" );
        CYG_ASSERT( &p_txcb->buffer[0] + MAX_TX_PACKET_SIZE >= to_p,
                    "to_p overflow in tx" );
  
        if ( ++tx_descriptor_add >= MAX_TX_DESCRIPTORS)
            tx_descriptor_add = 0;

        // no more interrupts until started
        Mask82559Interrupt(p_i82559);

        p_i82559->tx_descriptor_add = tx_descriptor_add;
        
        if ( p_i82559->tx_descriptor_remove == tx_descriptor_add )
            p_i82559->tx_queue_full = 1;

        if ( ! p_i82559->tx_in_progress ) { // if no Tx operation running
#ifdef DEBUG_82559
            os_printf("Tx %d %x: Starting Engines, KEY %x\n",
                      p_i82559->index, (int)priv, key );
#endif
            // flag start of Tx operation
            p_i82559->tx_in_progress = 1; 
            // make sure no command operating
    	    wait_for_cmd_done(ioaddr + SCBCmd); 
            // start Tx operation
            OUTL(VIRT_TO_BUS(p_txcb), ioaddr + SCBPointer);
            OUTW(CU_START, ioaddr + SCBCmd);
        }
        UnMask82559Interrupt(p_i82559);     // Allow this device to interrupt
    }

}



// ------------------------------------------------------------------------
//
//  Function : TxComplete
//
// ------------------------------------------------------------------------
static unsigned long TxComplete(struct i82559* p_i82559)
{
    int tx_descriptor_remove;
    cyg_uint32 ioaddr;
    TxCB *p_txcb;
    unsigned long key;

    tx_descriptor_remove = p_i82559->tx_descriptor_remove;
    
    CYG_ASSERT( p_i82559->tx_in_progress, "Tx not in progress but Tx intr" );

    key = p_i82559->tx_keys[tx_descriptor_remove];
    if ( ++tx_descriptor_remove >= MAX_TX_DESCRIPTORS )
        tx_descriptor_remove = 0;
    p_i82559->tx_descriptor_remove = tx_descriptor_remove;
    p_i82559->tx_queue_full = 0;
    if ( p_i82559->tx_descriptor_add != tx_descriptor_remove ) {
        // get device I/O address
        ioaddr = p_i82559->io_address;  
        p_txcb = p_i82559->tx_ring[tx_descriptor_remove];
        CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" );
        CYG_ASSERT( (cyg_uint8 *)p_txcb <  i82559_heap_free, "txcb over" );
        // make sure no command operating
        wait_for_cmd_done(ioaddr + SCBCmd); 
        // start Tx operation
        OUTL(VIRT_TO_BUS(p_txcb), ioaddr + SCBPointer);
        OUTW(CU_START, ioaddr + SCBCmd);
    }
    else
        p_i82559->tx_in_progress = 0;

    return key;
}

// ------------------------------------------------------------------------

static void
PacketTxDone(struct i82559* p_i82559, unsigned long key)
{
    struct cyg_netdevtab_entry *ndp;
    struct eth_drv_sc *sc;

    // Just tell the stack that this device has done a Tx.
    ndp = (struct cyg_netdevtab_entry *)(p_i82559->ndp);
    sc = (struct eth_drv_sc *)(ndp->device_instance);

    CHECK_NDP_SC_LINK();

#ifdef DEBUG_82559
    os_printf("TxDone %d %x: KEY %x\n",
              p_i82559->index, (int)p_i82559, key );
#endif

    eth_drv_tx_done( sc, key, 1 /* status */ );
}


// ------------------------------------------------------------------------
//
//  Function : QueueThreadRequest
//
// ------------------------------------------------------------------------
static void QueueThreadRequest(int type,
                               struct i82559* p_i82559,
                               unsigned long key)
{
    if ( ! i82559_q_full ) {

        i82559_queue[i82559_q_in].request = type;
        i82559_queue[i82559_q_in].p = p_i82559;
        i82559_queue[i82559_q_in].key = key;

        if ( ++i82559_q_in >= I82559_QUEUE_SIZE )
            i82559_q_in = 0;

        if ( i82559_q_in == i82559_q_out )
            i82559_q_full = 1;
    }
}



// ------------------------------------------------------------------------
//
//  Function : i82559_isr
//
// ------------------------------------------------------------------------
int last_status[16] = { 0 };
int last_index = 0;

static int i82559_isr(struct i82559* p_i82559)
{
    cyg_uint16 status;
    cyg_uint32 io_address;
    int return_value;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_isr: Bad device pointer %x\n", p_i82559 );
        return 0;
    }

    io_address = p_i82559->io_address;
    status = INW(io_address + SCBStatus);
    OUTW(status & 0xFC00, io_address + SCBStatus);

    return_value = 0;

    last_status[last_index++] = status;
    last_status[last_index &= 15] = -1;

    // receiver left ready state ?
    if ( status & SCB_STATUS_RNR ) {    
#ifdef KEEP_STATISTICS
        statistics[p_i82559->index].rx_resource++;
#endif
        // flag out of resources
        p_i82559->out_of_resources = 1; 
    }

    // frame receive interrupt ?
    if ( status & SCB_STATUS_FR ) {     
#ifdef KEEP_STATISTICS
        statistics[p_i82559->index].rx_count++;
#endif
        QueueThreadRequest(PACKET_RX, p_i82559, 0);
        return_value = 1;
    }

    // transmit interrupt ?
    if ( status & SCB_STATUS_CX ) {
        unsigned long key;
#ifdef KEEP_STATISTICS
        statistics[p_i82559->index].tx_complete++;
#endif
        key = TxComplete(p_i82559);
        QueueThreadRequest(PACKET_TX, p_i82559, key);
        return_value = 1;
    }

    return return_value;
}

// ------------------------------------------------------------------------
//
//  Function : i82559_mux_isr
//
// ------------------------------------------------------------------------
static int i82559_mux_isr(void)
{
    int return_value = 0;

    static int mux_device_index = 0;

    int device_index = mux_device_index;

    return_value = i82559_isr( &i82559[device_index] );

    mux_device_index ^= 1;              // look at the other one first next time.
    
    if ( return_value )
        return return_value;

    device_index = mux_device_index;

    return_value = i82559_isr( &i82559[device_index] );

    return return_value;
}

// ------------------------------------------------------------------------
//
//  Function : i82559_reset
//
// ------------------------------------------------------------------------
static void i82559_reset(struct i82559* p_i82559)
{
    cyg_uint32 ioaddr;
    int count;

    ioaddr = p_i82559->io_address;
    // make sure no command operating
    wait_for_cmd_done(ioaddr + SCBCmd);   
 
    OUTL(I82559_SELECTIVE_RESET, ioaddr + SCBPort);
  
    for (count = 10 ; count-- ; ) {
        udelay(1000);
    }

    OUTL(I82559_RESET, ioaddr + SCBPort);
    
    for (count = 10 ; count-- ; ) {
      udelay(1000);
    }
}


// ------------------------------------------------------------------------
//
//  Function : eth_set_promiscuous_mode
//
//  Return : 0 = It worked.
//           non0 = It failed.
// ------------------------------------------------------------------------

static int eth_set_promiscuous_mode(struct i82559* p_i82559)
{
    cyg_uint32  ioaddr;
    volatile CONFIG_CMD_STRUCT *ccs;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "eth_set_promiscuos_mode: Bad device pointer %x\n",
                   p_i82559 );
        return -1;
    }

    ioaddr = p_i82559->io_address;  
    wait_for_cmd_done(ioaddr + SCBCmd); 
    // load cu base address = 0 */ 
    OUTL(0, ioaddr + SCBPointer);         
    // 32 bit linear addressing used
                                        
    OUTW(SCB_M | CUC_ADDR_LOAD, ioaddr + SCBCmd);
    // wait for SCB command complete
    wait_for_cmd_done(ioaddr + SCBCmd);   
  
    ccs = (CONFIG_CMD_STRUCT *)mem_reserved_ioctl;
  
    // Check the malloc we did earlier worked
    if (ccs == (void*)0) 
        return 2; // Failed
  
    ccs->cb_entry.cb_cmd=0x2;
    ccs->cb_entry.cb_cmd_word=0x0;
    ccs->cb_entry.cb_status_word=0x0;
    ccs->cb_entry.cb_int=0;
    ccs->cb_entry.cb_suspend=1;
    ccs->cb_entry.cb_el=1;
    ccs->cb_entry.cb_complete=0;
    ccs->cb_entry.cb_link_offset=VIRT_TO_BUS((cyg_uint32)&ccs);
    
    // Default values from the Intel Manual
    ccs->config_bytes[0]=0x13;
    ccs->config_bytes[1]=0x8;
    ccs->config_bytes[2]=0x0;
    ccs->config_bytes[3]=0x0;
    ccs->config_bytes[4]=0x0;
    ccs->config_bytes[5]=0x0;
    ccs->config_bytes[6]=0xb2;
    ccs->config_bytes[7]=0x0;
    ccs->config_bytes[8]=0x0;
    ccs->config_bytes[9]=0x0;
    ccs->config_bytes[10]=0x28;
    ccs->config_bytes[11]=0x0;
    ccs->config_bytes[12]=0x60;
    ccs->config_bytes[13]=0x0;            // arp
    ccs->config_bytes[14]=0x0;            // arp
    
    ccs->config_bytes[15]=0x81;           // promiscuous mode set
    
    ccs->config_bytes[16]=0x0;
    ccs->config_bytes[17]=0x40;
    ccs->config_bytes[18]=0x70;
    
    // wait for SCB command complete
    wait_for_cmd_done(ioaddr + SCBCmd);   
    
    OUTL(VIRT_TO_BUS(ccs), ioaddr + SCBPointer); 
    OUTW(SCB_M | CUC_START, ioaddr + SCBCmd);    
  
    // now check for result ...
    wait_for_cmd_done(ioaddr + SCBCmd);   
  
    if ( (!ccs->cb_entry.cb_ok) || (!ccs->cb_entry.cb_complete) )
        return 1; // Failed
    return 0; // OK
}

// ------------------------------------------------------------------------
//
//  Function : eth_set_mac_address
//
//  Return : 0 = It worked.
//           non0 = It failed.
// ------------------------------------------------------------------------
static int eth_set_mac_address(struct i82559* p_i82559, char *addr)
{
    int addr_length;
  
    cyg_uint32  ioaddr;
    volatile CONFIG_CMD_STRUCT *ccs;
  
    IF_BAD_82559( p_i82559 ) {
        os_printf( "eth_set_mac_address : Bad device pointer %x\n",
                   p_i82559 );
        return -1;
    }

    ioaddr = p_i82559->io_address;      
    
    wait_for_cmd_done(ioaddr + SCBCmd); 
    // load cu base address = 0 */ 
    OUTL(0, ioaddr + SCBPointer);       
    // 32 bit linear addressing used
                                        
    OUTW(SCB_M | CUC_ADDR_LOAD, ioaddr + SCBCmd);
    // wait for SCB command complete
    wait_for_cmd_done(ioaddr + SCBCmd); 
    
    ccs = (CONFIG_CMD_STRUCT *)mem_reserved_ioctl;
    if (ccs == (void*)0)
        return 2;

    ccs->cb_entry.cb_cmd=0x1;
    ccs->cb_entry.cb_cmd_word=0x0;
    ccs->cb_entry.cb_status_word=0x0;
    ccs->cb_entry.cb_int=0;
    ccs->cb_entry.cb_suspend=1;
    ccs->cb_entry.cb_el=1;
  
    memcpy((char *)(ccs->config_bytes),addr,6);

    ccs->config_bytes[6]=0x0;
    ccs->config_bytes[7]=0x0;
  
    ioaddr = p_i82559->io_address;  
  
    OUTL(VIRT_TO_BUS(ccs), ioaddr + SCBPointer); 
    OUTW(SCB_M | CUC_START, ioaddr + SCBCmd);    
    // Next delay seems to be required, otherwise,
    // cb_ok/cb_complete won't be set later.

    udelay(100);
    wait_for_cmd_done(ioaddr + SCBCmd);   
  
    // now check for result ...
    if ( (!ccs->cb_entry.cb_ok) || (!ccs->cb_entry.cb_complete) )
        return 3;
  
    // now set this address in the device eeprom ....
    (void)memcpy(eeprom_burn,addr,6);
    eeprom_burn[20] &= 0xfe;   
    eeprom_burn[20] |= p_i82559->index;   

    addr_length=6; // eeprom address word length in bits ...
    program_eeprom(ioaddr,addr_length,eeprom_burn);
   
    // now update netword stack ...
    // FIXME - no feedback to stack when MAC changes

    // update 82559 driver data structure ...
    (void)memcpy((char *)(p_i82559->mac_address),addr,6);
    p_i82559->mac_addr_ok = 1;
    return 0;
}

// ------------------------------------------------------------------------
//
//  Function : write_enable_eeprom
//
// ------------------------------------------------------------------------
static int write_enable_eeprom(long ioaddr,  int addr_len)
{
    int ee_addr = ioaddr + SCBeeprom;
    int write_en_cmd = EE_WRITE_EN_CMD; 
    int i;
  
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    eeprom_delay(1);
    OUTW(EE_ENB, ee_addr);
    eeprom_delay(1);

  
    // Shift the wr/er enable command bits out.
    for (i = (addr_len+2); i >= 0; i--) {
        short dataval = (write_en_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
        OUTW(EE_ENB | dataval, ee_addr);
        eeprom_delay(1);
        OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
        eeprom_delay(1);
    }

    // Terminate the EEPROM access.
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    eeprom_delay(1);
    return 0;
}

// ------------------------------------------------------------------------
//
//  Function : write_eeprom
//
//  Return : 0 = It worked.
//           1 = It failed.
// ------------------------------------------------------------------------
static int write_eeprom(long ioaddr, int location,
                 int addr_len, unsigned short value)
{
    unsigned short retval = 0;
    int ee_addr = ioaddr + SCBeeprom;
    int write_cmd = location | EE_WRITE_CMD; 
    int i;
  
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    eeprom_delay(1);
    OUTW(EE_ENB, ee_addr);
    eeprom_delay(1);

    // Shift the write command bits out.
    for (i = (addr_len+2); i >= 0; i--) {
        short dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
        OUTW(EE_ENB | dataval, ee_addr);
        eeprom_delay(1);
        OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
        eeprom_delay(1);
    }
    OUTW(EE_ENB, ee_addr);
    eeprom_delay(1);
    
    for (i = 15; i >= 0; i--) {
        short dataval = (value & (1 << i)) ? EE_DATA_WRITE : 0;
        OUTW(EE_ENB | dataval, ee_addr);
        eeprom_delay(1);
        OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
        eeprom_delay(1);
    }

    // Terminate the EEPROM access.
    OUTW(EE_ENB & ~EE_CS, ee_addr);
    eeprom_delay(10000);
    return retval;
}

// ------------------------------------------------------------------------
//
//  Function : Program EEPROM
//  Parameters : ioaddr
//               addr_length in bits (8 or 6)
//               char[128] data to program
//  Return : void
// ------------------------------------------------------------------------
static void program_eeprom(cyg_uint32 ioaddr,
                    cyg_uint32 eeprom_size,
                    cyg_uint8 * data)
{
    cyg_uint32 i;
    cyg_uint16 checksum = 0;
    cyg_uint16 value;
    cyg_uint16 * cyg_uint16_data = (cyg_uint16 *) data;

    // first enable erase/write operations on the eeprom ...
    // this is done through the EWEN instruction ...

    write_enable_eeprom(ioaddr,eeprom_size);
  
    // Now write the bytes, except the checksum
    for (i=0 ; i< 63 ; i++) {
        value = ((unsigned short*)cyg_uint16_data)[i];
        checksum += value;
        write_eeprom(ioaddr,i,eeprom_size,value);
    }
  
    // Work out the checksum and program it
    value = 0xBABA - checksum;
    write_eeprom(ioaddr,i,eeprom_size,value);
}

// ------------------------------------------------------------------------
//
//  Function : eth_get_mac_address
//
// ------------------------------------------------------------------------
#ifdef ETH_DRV_GET_MAC_ADDRESS
static int eth_get_mac_address(struct i82559* p_i82559, char *addr)
{
    IF_BAD_82559( p_i82559 ) {
        os_printf( "eth_get_mac_address : Bad device pointer %x\n",
                   p_i82559 );
        return -1;
    }

    memcpy( addr, (char *)(&p_i82559->mac_address[0]), 6 );
    return (0);
}
#endif
// ------------------------------------------------------------------------
//
//  Function : i82559_ioctl
//
// ------------------------------------------------------------------------
static int i82559_ioctl(struct eth_drv_sc *sc, unsigned long key,
                        void *data, int data_length)
{
    struct i82559 *p_i82559;

    p_i82559 = (struct i82559 *)sc->driver_private;

    IF_BAD_82559( p_i82559 ) {
        os_printf( "i82559_ioctl/control: Bad device pointer %x\n", p_i82559 );
        return -1;
    }

    switch ( key ) {

#ifdef ETH_DRV_SET_MAC_ADDRESS
    case ETH_DRV_SET_MAC_ADDRESS:
        if ( 6 != data_length )
            return -2;
        eth_set_mac_address( p_i82559, data );
        return 0;
#endif

#ifdef ETH_DRV_GET_MAC_ADDRESS
    case ETH_DRV_GET_MAC_ADDRESS:
        eth_get_mac_address( p_i82559, data );
        return 0;
#endif

    default:
        break;
    }
    return -1;
}

// ------------------------------------------------------------------------
//
//                       INTERRUPT HANDLERS
//
// ------------------------------------------------------------------------

static cyg_uint32 eth_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    int schedule_dsr;

    cyg_drv_interrupt_mask(vector);     // mask this interrupt

    schedule_dsr = i82559_isr( (struct i82559 *)data ); // process interrupt

    cyg_drv_interrupt_acknowledge(vector); // acknowledge interrupt

    // schedule further processing ?
    if ( schedule_dsr )
        return CYG_ISR_CALL_DSR;        // yes, schedule DSR

    // else...
    cyg_drv_interrupt_unmask(vector);   // allow more of these interrupts
    return CYG_ISR_HANDLED;
}



static cyg_uint32 eth_mux_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    int schedule_dsr;

    cyg_drv_interrupt_mask( vector );   // mask this interrupt

    schedule_dsr = i82559_mux_isr();    // process interrupt

    cyg_drv_interrupt_acknowledge( vector ); // ack interrupt

    // schedule further processing ?
    if ( schedule_dsr )
        return CYG_ISR_CALL_DSR;        // yes, schedule DSR
    
    // else...
    cyg_drv_interrupt_unmask( vector ); // allow more of these interrupts
    return CYG_ISR_HANDLED;
}

// ------------------------------------------------------------------------

void eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    while ( (i82559_q_out != i82559_q_in) || (i82559_q_full) ) {
        switch ( i82559_queue[i82559_q_out].request ) {
        case PACKET_RX:
            PacketRxReady(i82559_queue[i82559_q_out].p);
            break;
            
        case PACKET_TX:
            PacketTxDone(i82559_queue[i82559_q_out].p,
                         i82559_queue[i82559_q_out].key);
            break;
        }
        i82559_q_full = 0;
        if ( ++i82559_q_out == I82559_QUEUE_SIZE )
            i82559_q_out = 0;
    }
    // allow interrupts to continue from whichever source
    // (if multiple device actions were dispatched in this DSR call,
    //  another call will occur almost immediately)
    cyg_drv_interrupt_unmask(vector);
}

// ------------------------------------------------------------------------
//
//  Function : pci_init_find_82559s
//
// This is called exactly once at the start of time to:
//  o scan the PCI bus for objects
//  o record them in the device table
//  o acquire all the info needed for the driver to access them
//  o instantiate interrupts for them
//  o attach those interrupts appropriately
// ------------------------------------------------------------------------
static int
pci_init_find_82559s( void )
{
    cyg_pci_device_id devid;
    cyg_pci_device dev_info;
    cyg_uint16 cmd;
    int device_index;

    // MUX interrupt - special case when 2 cards share one intr.
    static cyg_handle_t mux_interrupt_handle = 0;
    static cyg_interrupt mux_interrupt_object;

#ifdef DEBUG
    db_printf("pci_init_find_82559s()\n");
#endif

    // allocate memory to be used in ioctls later
    if (mem_reserved_ioctl != (void*)0) {
        db_printf("pci_init_find_82559s() called > once\n");
        return 0;
    }

    // First initialize the heap in PCI window'd memory
    i82559_heap_size = CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_SIZE;
    i82559_heap_base = (cyg_uint8 *)CYGHWR_HAL_ARM_EBSA285_PCI_MEM_MAP_BASE;
    i82559_heap_free = i82559_heap_base;

    mem_reserved_ioctl = pciwindow_mem_alloc(MAX_MEM_RESERVED_IOCTL);     

    // initialize the event queue for DSR actions
    i82559_q_out = i82559_q_in = 0;
    i82559_q_full = 0;

    cyg_pci_init();
#ifdef DEBUG
    db_printf("Finished cyg_pci_init();\n");
#endif
    devid = CYG_PCI_NULL_DEVID;

    for (device_index = 0; device_index < MAX_82559; device_index++) {
        struct i82559 *p_i82559 = &i82559[device_index];
        p_i82559->index = device_index;

        if (cyg_pci_find_device(0x8086, 0x1229, &devid) ) {
#ifdef DEBUG
            db_printf("eth%d = 82559\n", device_index);
#endif
            cyg_pci_get_device_info(devid, &dev_info);

            if (cyg_pci_translate_interrupt(&dev_info, &p_i82559->vector)) {
#ifdef DEBUG
                db_printf(" Wired to HAL vector %d\n", p_i82559->vector);
#endif
                cyg_drv_interrupt_create(
                    p_i82559->vector,
                    0,                  // Priority - unused
                    (CYG_ADDRWORD)p_i82559, // Data item passed to ISR
                    eth_isr,            // ISR
                    eth_dsr,            // DSR
                    &p_i82559->interrupt_handle, // handle to intr obj
                    &p_i82559->interrupt_object ); // space for int obj

                cyg_drv_interrupt_attach(p_i82559->interrupt_handle);

                // Don't unmask the interrupt yet, that could get us into a
                // race.

                // ALSO attach it to interrupt #18 for multiplexed
                // interrupts.  This is for certain boards where the
                // PCI backplane is wired "straight through" instead of
                // with a rotation of interrupt lines in the different
                // slots.
                if ( ! mux_interrupt_handle ) {
#ifdef DEBUG
                    db_printf(" Also attaching to HAL vector %d\n", 
                              CYGNUM_HAL_INTERRUPT_PCI_IRQ);
#endif
                    cyg_drv_interrupt_create(
                        CYGNUM_HAL_INTERRUPT_PCI_IRQ,
                        0,              // Priority - unused
                        0,              // Data item passed to ISR (not used)
                        eth_mux_isr,    // ISR
                        eth_dsr,        // DSR
                        &mux_interrupt_handle,
                        &mux_interrupt_object );
                    
                    cyg_drv_interrupt_attach(mux_interrupt_handle);
                }
            }
            else {
                p_i82559->vector=0;
#ifdef DEBUG
                db_printf(" Does not generate interrupts.\n");
#endif
            }

            if (cyg_pci_configure_device(&dev_info)) {
#ifdef DEBUG
                int i;
                db_printf("Found device on bus %d, devfn 0x%02x:\n",
                          CYG_PCI_DEV_GET_BUS(devid),
                          CYG_PCI_DEV_GET_DEVFN(devid));

                if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) {
                    db_printf(" Note that board is active. Probed"
                              " sizes and CPU addresses invalid!\n");
                }
                db_printf(" Vendor    0x%04x", dev_info.vendor);
                db_printf("\n Device    0x%04x", dev_info.device);
                db_printf("\n Command   0x%04x, Status 0x%04x\n",
                          dev_info.command, dev_info.status);
                
                db_printf(" Class/Rev 0x%08x", dev_info.class_rev);
                db_printf("\n Header 0x%02x\n", dev_info.header_type);

                db_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
                          dev_info.header.normal.sub_vendor, 
                          dev_info.header.normal.sub_id);

                for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
                    db_printf(" BAR[%d]    0x%08x /", i, dev_info.base_address[i]);
                    db_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
                              dev_info.base_size[i], dev_info.base_map[i]);
                }
                db_printf(" eth%d configured\n", device_index);
#endif
                p_i82559->found = 1;
                p_i82559->active = 0;
                p_i82559->devid = devid;
                p_i82559->memory_address = dev_info.base_map[0];
                p_i82559->io_address = dev_info.base_map[1];
#ifdef DEBUG
                db_printf(" memory address = 0x%08x\n", dev_info.base_map[0]);
                db_printf(" I/O address = 0x%08x\n", dev_info.base_map[1]);
#endif

                // Don't use cyg_pci_set_device_info since it clears
                // some of the fields we want to print out below.
                cyg_pci_read_config_uint16(dev_info.devid, CYG_PCI_CFG_COMMAND, &cmd);
                cmd |= CYG_PCI_CFG_COMMAND_IO // enable I/O space
                    | CYG_PCI_CFG_COMMAND_MEMORY // enable memory space
                    | CYG_PCI_CFG_COMMAND_MASTER; // enable bus master
                cyg_pci_write_config_uint16(dev_info.devid, CYG_PCI_CFG_COMMAND, cmd);

                // Now the PCI part of the device is configured, reset it. This 
                // should make it safe to enable the interrupt
                i82559_reset(p_i82559);

                if (p_i82559->vector != 0) {
                    cyg_interrupt_acknowledge(p_i82559->vector);
                    cyg_drv_interrupt_unmask(p_i82559->vector);
                }
#ifdef DEBUG
                db_printf(" **** Device enabled for I/O and Memory and Bus Master\n");
#endif
            }
            else {
                p_i82559->found = 0;
                p_i82559->active = 0;
                db_printf("Failed to configure device %d\n",device_index);
            }
        }
        else {
            p_i82559->found = 0;
            p_i82559->active = 0;
            db_printf("eth%d not found\n", device_index);
        }
    }

    // Now enable the mux shared interrupt if it is in use
    if (mux_interrupt_handle) {
        cyg_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_PCI_IRQ);
        cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_PCI_IRQ);
    }

    return 1;
}



// ------------------------------------------------------------------------

// EOF if_ebsa285.c