/*
	 Copyright 2001 (c) by Authors,
	 Swiss Federal Institute of Technology,
	 Computer Engineering and Networks Laboratory.
 
	 TOPSY -- A Teachable Operating System.
	 Implementation of a tiny and simple
	 micro kernel for teaching purposes.
 
	 For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
	 This software is provided under the terms of the GNU General Public 
	 Licence. A full copy of the GNU GPL is provided in the file COPYING 
	 found in the development root of Topsy.
 */
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/sa1100/Drivers/Attic/IXPEthInit.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/

// this code is based upon parts of vera from Princeton

// Utilities for the Intel IXP1200
// Princeton University
// Scott Karlin and Tammo Spalink
//
// Copyright Princeton University Board of Trustees
// All rights reserved
//

#include "IXPEthInit.h"
#include "IXPMemAccess.h"
#include "IXPUtil.h"
#include "Support.h"

//
// ---------- Private Hardware Access Definitions ----------
//

#define DELAY_DURATION (50000)

typedef enum {
  INT_EN_clear = 0x00,
  INT_EN_RXOKE = 1 << 0,      // Receive OK
  INT_EN_RXEVE = 1 << 1,      // Receive Event
  INT_EN_TXOKE = 1 << 2,      // Transmit OK
  INT_EN_TXERE = 1 << 3,      // Transmit Error
  INT_EN_LNCHE = 1 << 4,      // Link Change
  INT_EN_OVFCE = 1 << 5,      // Counter Overflow
  INT_EN_STOPE = 1 << 6,      // Port Stop
  INT_EN_all   = 0x7F
} INT_EN_fields;

typedef enum {
  INT_STT_RXOK = 1 << 0,
  INT_STT_RXEV = 1 << 1,
  INT_STT_TXOK = 1 << 2,
  INT_STT_TXER = 1 << 3,
  INT_STT_LNCH = 1 << 4,
  INT_STT_OVFC = 1 << 5,
  INT_STT_STOP = 1 << 6
} INT_STT_fields;

typedef enum {
  TX_ERR_MOD_clear = 0x00,
  TX_ERR_MOD_UNFS  = 1 << 0,  // Stop [Continue] After FIFO Underflow
  TX_ERR_MOD_LCLS  = 1 << 1,  // stop [Continue] After Late Collision
  TX_ERR_MOD_XCLS  = 1 << 2,  // Stop [Continue] After Excessive Collisions
  TX_ERR_MOD_all   = 0x07
} TX_ERR_MOD_fields;

typedef enum {
  RX_FILT_MOD_clear = 0x00,
  RX_FILT_MOD_POVF  = 1 << 0, // Pass FIFO overflow
  RX_FILT_MOD_PCRC  = 1 << 1, // Pass CRC error packets
  RX_FILT_MOD_PDRB  = 1 << 2, // Pass alignment error packets
  RX_FILT_MOD_PSRT  = 1 << 3, // Pass short packets
  RX_FILT_MOD_PRTL  = 1 << 4, // Pass too long packets
  RX_FILT_MOD_PMER  = 1 << 5, // Pass MII error packets
  RX_FILT_MOD_PFLC  = 1 << 7, // Pass flow-control packets
  RX_FILT_MOD_all   = 0xBF
} RX_FILT_MOD_fields;

static uint32 Eth_CSR_Base[] = {
                                 0x38400000,
                                 0x38401000,
                                 0x38402000,
                                 0x38403000,
                                 0x38404000,
                                 0x38405000,
                                 0x38406000,
                                 0x38407000,
                               };

typedef enum {
  CHIP_INT       = 0x00,
  INT_STT        = 0x04,
  INT_EN         = 0x08,
  TXMT_STT       = 0x0C,
  RECV_STT       = 0x10,
  PORT_CTR       = 0x14,
  DEV_ID         = 0x18,
  REV_ID         = 0x1C,
  SER_COM        = 0x20,
  FFO_TSHD       = 0x44,
  FFO_BUS        = 0x48,
  TX_PARAM       = 0x4C,
  TX_ERR_MOD     = 0x50,
  TX_TSHD_BOFF   = 0x54,
  RX_PARAM       = 0x58,
  RX_FILT_MOD    = 0x5C,
  PAUSE_TIME_LSB = 0x60,
  PAUSE_TIME_MSB = 0x64,
  PKT_MAX_SZ_LSB = 0x68,
  PKT_MAX_SZ_MSB = 0x6C,
  SER_MOD        = 0x84,
  LNK_STT        = 0x88
} csrOffsets;

typedef enum {
  TX_UNI_OK_CNT    = 0x200,
  TX_MLT_OK_CNT    = 0x204,
  TX_BRD_OK_CNT    = 0x208,
  TX_DEFER_CNT     = 0x20C,
  TX_COL_CNT       = 0x210,
  TX_SCOL_CNT      = 0x214,
  TX_MCOL_CNT      = 0x218,
  TX_XCOL_CNT      = 0x21C,
  TX_LCOL_CNT      = 0x220,
  TX_PKT_64_CNT    = 0x224,
  TX_PKT_65_CNT    = 0x228,
  TX_PKT_128_CNT   = 0x22C,
  TX_PKT_256_CNT   = 0x230,
  TX_PKT_512_CNT   = 0x234,
  TX_PKT_1024_CNT  = 0x238,
  TX_PKT_1519_CNT  = 0x23C,
  TX_PAUSE_CNT     = 0x240,
  TX_ERR_CNT       = 0x244,
  TX_OCT_OK_CNT    = 0x260,
  TX_OCT_BAD_CNT   = 0x268,
  RX_OCT_OK_CNT    = 0x270,
  RX_OCT_BAD_CNT   = 0x278,
  RX_RUNT_CNT      = 0x280,
  RX_OVF_CNT       = 0x284,
  RX_SHORT_OK_CNT  = 0x288,
  RX_SHORT_CRC_CNT = 0x28C,
  RX_UNI_OK_CNT    = 0x290,
  RX_MLT_OK_CNT    = 0x294,
  RX_BRD_OK_CNT    = 0x298,
  RX_NORM_CRC_CNT  = 0x29C,
  RX_NORM_ALI_CNT  = 0x2A0,
  RX_LONG_OK_CNT   = 0x2A4,
  RX_LONG_CRC_CNT  = 0x2A8,
  RX_PKT_64_CNT    = 0x2AC,
  RX_PKT_65_CNT    = 0x2B0,
  RX_PKT_128_CNT   = 0x2B4,
  RX_PKT_256_CNT   = 0x2B8,
  RX_PKT_512_CNT   = 0x2BC,
  RX_PKT_1024_CNT  = 0x2C0,
  RX_PKT_1519_CNT  = 0x2C4,
  RX_PAUSE_CNT     = 0x2C8,
  RX_FALS_CRS_CNT  = 0x2CC,
  RX_SYM_ERR_CNT   = 0x2D0
} counterOffsets;

typedef enum {
  PORT_CTR_CNRST = 1 << 0,    // Counter Reset (special, see docs)
  PORT_CTR_CTRST = 1 << 1,    // Control Reset
  PORT_CTR_PTRST = 1 << 2,    // Port Reset
  PORT_CTR_RXEN  = 1 << 3,    // Receive Enabled [Disabled]
  PORT_CTR_TXEN  = 1 << 4,    // Transmit Enabled [Disabled]
  PORT_CTR_TXSTT = 1 << 5,    // Transmit Restart Trigger
} PORT_CTR_fields;

typedef enum {
  FFO_BUS_BEND = 1 << 0,      // Big [Little] Endian
  FFO_BUS_BWID = 1 << 1,      // 64 [32] bit Bus Width/Mode
  FFO_BUS_HRYD = 1 << 2,      // Header Ready Signal Disabled [Enabled]
  FFO_BUS_SPLT = 1 << 4       // Actually use entire split bus
} FFO_BUS_fields;

// The size of the FIFO transmit/receive thresholds will be ((8 *
// value) + 1). Possible values are 1-16.
#define FFO_TSHD_RTH(value) (putBits((value), 0, 4))
#define FFO_TSHD_TTH(value) (putBits((value), 4, 4))

typedef enum {
  TX_TSHD_BOFF_BKL = 0xA << 4 // Standard backoff limit
} TX_TSHD_BOFF_fields;

// The size of the transmit threshold will be ((8 * value) + 4).
// Possible values are 0-15.
#define TX_TSHD_BOFF_TSD(value) (putBits((value), 0, 4))

typedef enum {
  SER_MOD_clear = 0x00,
  SER_MOD_SYP   = 1 << 0,     // MII/SYM port mode
  SER_MOD_SCR   = 1 << 1,     // Scrambler mode for SYM PHYs
  SER_MOD_FDX   = 1 << 3,     // Full-Duplex mode
  SER_MOD_ELPK  = 1 << 4,     // External Loopback mode
  SER_MOD_ILPK  = 1 << 5      // Internal Loopback mode
} SER_MOD_fields;

typedef enum {
  TX_PARAM_clear = 0x00,
  TX_PARAM_PADD  = 1 << 0,    // Padding Disabled [Enabled]
  TX_PARAM_CRCD  = 1 << 1,    // CRC Append Disabled [Enabled]
  TX_PARAM_FLCE  = 1 << 2,    // Flow-Control FD-mode Enabled [Disabled]
  TX_PARAM_XOND  = 1 << 3,    // XON Flow-Control Sending Disabled [Enabled]
  TX_PARAM_SPM   = 1 << 4     // Single (outgoing) Packet Mode
} TX_PARAM_fields;

typedef enum {
  RX_PARAM_HRPL = 1 << 6,     // Header Replay mode Enable [Disabled]
  RX_PARAM_CRCR = 1 << 7      // CRC Remove mode Enable [Disabled]
} RX_PARAM_fields;


/*****************************************************************************/
/*                                                                           */
/* Function Name: csr                                                        */
/* Parameters:                                                               */
/*	uint8 		port	The port to access.			                                 */
/*	csrOffsets	address	The address of the CSR.                              */
/* Returns: The StrongARM's address of the given Control Status register of  */
/*          the port.                                                        */
/*                                                                           */
/*****************************************************************************/

static inline uint32
csr(int port, csrOffsets address)
{
  return address + Eth_CSR_Base[port];
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: Eth_InitPort                                               */
/* Parameters: uint8   The port to Initialize.                               */
/* Returns: <none>                                                           */
/* Effects: Resets the port to a known state.                                */
/*                                                                           */
/*****************************************************************************/

void
Eth_InitPort(uint8 port)
{
  led(0xE0);

  // Disable the port
  memWrite8(csr(port, INT_EN), INT_EN_STOPE);
  memWrite8(csr(port, PORT_CTR), 0);

  led(0xE1);

  // Wait until it agrees to really being disabled
  while (! ((memRead8(csr(port, PORT_CTR) & (PORT_CTR_RXEN | PORT_CTR_TXEN)) == 0) ||
            (memRead8(csr(port, INT_STT) & INT_STT_STOP))))
  {
    // wait
  }

  led(0xE2);

  // Reset the port
  memWrite8(csr(port, PORT_CTR), PORT_CTR_PTRST | PORT_CTR_CTRST);
  delay(DELAY_DURATION);
  //memWrite8(csr(port, PORT_CTR), 0);
  memWrite8(csr(port, PORT_CTR), PORT_CTR_CNRST);
  delay(DELAY_DURATION);
  //memWrite8(csr(port, PORT_CTR), 0);

  led(0xE3);

  // Configure the port
  memWrite8(csr(port, FFO_BUS), FFO_BUS_BWID | FFO_BUS_BEND);
  // XXX cannot see packets:
  //    memWrite8(csr(port, SER_MOD), SER_MOD_FDX | SER_MOD_SYP);
  // XXX causes odd extra packets to transmit:
  memWrite8(csr(port, SER_MOD), SER_MOD_FDX);
  // XXX memWrite8(csr(port, SER_MOD), SER_MOD_clear);
  memWrite8(csr(port, RX_PARAM), RX_PARAM_CRCR);
  memWrite8(csr(port, TX_PARAM),
            TX_PARAM_XOND | TX_PARAM_SPM | TX_PARAM_FLCE);
  memWrite8(csr(port, TX_TSHD_BOFF),
            TX_TSHD_BOFF_BKL | TX_TSHD_BOFF_TSD(15));
  memWrite8(csr(port, FFO_TSHD), FFO_TSHD_RTH(7) | FFO_TSHD_TTH(7));

  led(0xE4);
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: Eth_StartPort                                              */
/* Parameters: uint8   The port to start.                                    */
/* Returns: <none>                                                           */
/* Effects: Enables the port to receive and transmit data.                   */
/*                                                                           */
/*****************************************************************************/

void
Eth_StartPort(uint8 port)
{
  memWrite8(csr(port, PORT_CTR), PORT_CTR_RXEN | PORT_CTR_TXEN);
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: Eth_PrintCSRs                                              */
/* Parameters: uint8   The port to query.                                    */
/* Returns: <none>                                                           */
/* Effects: Prints the values of the Control Status Registers associated with*/
/*          the port.                                                        */
/*****************************************************************************/

void
Eth_PrintCSRs(uint8 port)
{
  uint8 CHIP_INT_value;
  uint8 TXMT_STT_value;

  CHIP_INT_value = memRead8(csr(port, CHIP_INT));
  TXMT_STT_value = memRead8(csr(port, TXMT_STT));

  printf("\n21440 Port %d\n"
         "  Interrupted    : %i\n"
         "  INT_EN         : %#.2x\n"
         "  INT_STT        : %#.2x\n"
         "  TXMT_STT Flags : %#.2x\n"
         "  TXMT_STT Count : %08i\n"
         "  RECV_STT       : %#.2x\n",
         port,
         (char) (getBits(CHIP_INT_value, port, 1) != 0),
         memRead8(csr(port, INT_EN))	& 0xFF,
         memRead8(csr(port, INT_STT))	& 0xFF,
         getBits(TXMT_STT_value, 0, 3),
         getBits(TXMT_STT_value, 6, 2),
         memRead8(csr(port, RECV_STT))	& 0xFF);

  printf("DEV_ID: %x   REV_ID: %x\n", memRead8(csr(0, DEV_ID)), memRead8(csr(0,REV_ID)) );
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: Eth_PrintCounters                                          */
/* Parameters: uint8   The port to query.                                    */
/* Returns: <none>                                                           */
/* Effects: Print the value of the RX_OCT_OK_CNT to standard output.         */
/*                                                                           */
/*****************************************************************************/

void
Eth_PrintCounters(uint8 port)
{
  printf("21440 Port %d\n  RX_OCT_OK_CNT: %08x\n",
         port, memRead32(csr(port, RX_OCT_OK_CNT)));
}

