/*
 * (C) Copyright 2007
 * GE Intelligent Platforms
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <asm/au1x00.h>
#include <asm/mipsregs.h>
#include "octeon_boot.h"
#include <pci.h>
#include <lib_octeon_shared.h>
#include <lib_octeon.h>

#if defined(CONFIG_PCI)
extern void init_octeon_pci (void);

void pci_init_board (void)
{
	init_octeon_pci();
}
#endif

extern int eeprom_diags(void);
extern void uboot_version_num_store(void);

int checkboard (void)
{
#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
    /* Enable MDIO */

    cvmx_write_csr(CVMX_SMI_EN, 1);
#endif
    // SK: the test here was specific to ebt3000 and was removed
    // see the 1.6 version to revert
    
    return 0;
}


extern int write_tlv_tuple(octeon_eeprom_header_t *tlv_hdr_ptr);
int early_board_init(void)
{
    int cpu_ref = 33;

    DECLARE_GLOBAL_DATA_PTR;

    memset((void *)&(gd->mac_desc), 0x0, sizeof(octeon_eeprom_mac_addr_t));
    memset((void *)&(gd->clock_desc), 0x0, sizeof(octeon_eeprom_clock_desc_t));
    memset((void *)&(gd->board_desc), 0x0, sizeof(octeon_eeprom_board_desc_t));

    /* NOTE: this is early in the init process, so the serial port is not yet configured */

    /* Populate global data from eeprom */
    uint8_t ee_buf[OCTEON_EEPROM_MAX_TUPLE_LENGTH];
    int addr;

	octeon_eeprom_header_t *tlv_hdr_ptr = (void *)ee_buf;
    octeon_eeprom_board_desc_t *bd_ptr = (void *)ee_buf;

    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_CLOCK_DESC_TYPE, 0, ee_buf, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
    if (addr >= 0)
    {
        memcpy((void *)&(gd->clock_desc), ee_buf, sizeof(octeon_eeprom_clock_desc_t));
    }


    /* Determine board type/rev */
    strncpy((char *)(gd->board_desc.serial_str), "unknown", SERIAL_LEN);
    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_BOARD_DESC_TYPE, 0, ee_buf, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
    if (addr >= 0)
    {
        memcpy((void *)&(gd->board_desc), ee_buf, sizeof(octeon_eeprom_board_desc_t));
    }
    else
    {
        gd->flags |= GD_FLG_BOARD_DESC_MISSING;
        gd->board_desc.rev_minor = 0;
        gd->board_desc.board_type = CVMX_BOARD_TYPE_TNPA5804;

#ifdef OBSOLETE
        /* Try to determine board rev by looking at PAL */
        if (octeon_ebt3000_rev1())
        {
            gd->board_desc.rev_major = 1;
        }
        else
        {
            gd->board_desc.rev_major = 2;
        }
#endif

        gd->board_desc.rev_major = 1;
		
		/* set a default board info tuple if not there */
		tlv_hdr_ptr->type = EEPROM_BOARD_DESC_TYPE;
		tlv_hdr_ptr->length = sizeof(octeon_eeprom_board_desc_t);
		tlv_hdr_ptr->checksum = 0; 
		tlv_hdr_ptr->major_version = OCTEON_EEPROM_BOARD_DESC_MAJOR_VER;
		tlv_hdr_ptr->minor_version = OCTEON_EEPROM_BOARD_DESC_MINOR_VER;
        bd_ptr->board_type     = CVMX_BOARD_TYPE_TNPA5804;
        bd_ptr->rev_major      = 1;
        bd_ptr->rev_minor      = 0;
        memset(bd_ptr->serial_str, 0, SERIAL_LEN);
        strncpy((char *)bd_ptr->serial_str, "unknown", SERIAL_LEN);
        write_tlv_tuple(tlv_hdr_ptr);
    }


#ifdef OBSOLETE
    if (gd->board_desc.rev_major == 1)
    {
        if (gd->clock_desc.cpu_ref_clock_mhz_x_8)
        {
            cpu_ref = gd->clock_desc.cpu_ref_clock_mhz_x_8/8;
            gd->ddr_clock_mhz = gd->clock_desc.ddr_clock_mhz;
        }
        else
        {
            gd->flags |= GD_FLG_CLOCK_DESC_MISSING;
            /* Default values */
            cpu_ref = 33;
            gd->ddr_clock_mhz = EBT3000_REV1_DEF_DRAM_FREQ;
        }
    }
    else
    {
        /* Even though the CPU ref freq is stored in the clock descriptor, we don't read it here.  The MCU
        ** reads it and configures the clock, and we read how the clock is actually configured.
        ** The bootloader does not need to read the clock descriptor tuple for normal operation on
        ** rev 2 and later boards
        */
        cpu_ref = octeon_mcu_read_cpu_ref();
        gd->ddr_clock_mhz = octeon_mcu_read_ddr_clock();
    }
    /* Some sanity checks */
    if (cpu_ref <= 0)
    {
        /* Default to 33 */
        cpu_ref = 33;
    }
    if (gd->ddr_clock_mhz <= 0)
    {
        /* Default to 200 */
        gd->ddr_clock_mhz = 200;
    }
#endif

    cpu_ref = 50;
    gd->ddr_clock_mhz = 333;

 /*
    ** For the cn58xx the DDR reference clock frequency is used to
    ** configure the appropriate internal DDR_CK/DDR2_REF_CLK ratio in
    ** order to generate the ddr clock frequency specified by
    ** ddr_clock_mhz.  For cn38xx this setting should match the state
    ** of the DDR2 output clock divide by 2 pin DDR2_PLL_DIV2.
    **
    ** For DDR@_PLL_DIV2 = 0 the DDR_CK/DDR2_REF_CLK ratio is 4
    ** For DDR@_PLL_DIV2 = 1 the DDR_CK/DDR2_REF_CLK ratio is 2
    */
#define FIXED_DDR_REF_CLOCK_RATIO	4

    /*
    ** More details about DDR clock configuration used for LMC
    ** configuration for the CN58XX.  Not used for CN38XX.  Since the
    ** reference clock frequency is not known it is inferred from the
    ** specified DCLK frequency divided by the DDR_CK/DDR2_REF_CLK
    ** ratio.
    */
	gd->ddr_ref_hertz = gd->ddr_clock_mhz * 1000000 / FIXED_DDR_REF_CLOCK_RATIO;

    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 0, ee_buf, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
    if (addr >= 0)
    {
        memcpy((void *)&(gd->mac_desc), ee_buf, sizeof(octeon_eeprom_mac_addr_t));
    }
    else
    {
        /* Make up some MAC addresses */
        gd->mac_desc.count = 255;
        gd->mac_desc.mac_addr_base[0] = 0x00;
        gd->mac_desc.mac_addr_base[1] = 0xDE;
        gd->mac_desc.mac_addr_base[2] = 0xAD;
        gd->mac_desc.mac_addr_base[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.mac_addr_base[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.mac_addr_base[5] = 0x00;
    }

    /* Read CPU clock multiplier */
    uint64_t data = cvmx_read_csr((uint64_t)0x80011F00000001E8ull);
    data = data >> 18;
    data &= 0x1f;

    gd->cpu_clock_mhz = data * cpu_ref;

    /* adjust for 33.33 Mhz clock */
    if (cpu_ref == 33)
        gd->cpu_clock_mhz += (data)/4 + data/8;

    if (gd->cpu_clock_mhz < 100 || gd->cpu_clock_mhz > 600)
    {
        gd->cpu_clock_mhz = DEFAULT_ECLK_FREQ_MHZ;
    }

    return 0;

}
void octeon_led_str_write(const char *str)
{
    octeon_led_str_write_std(str);
}

#define MAX_NUM_SFP                                     4

#define ID                                              0
#define EXT_ID                                          1
#define CONNECTOR                                       2
#define TRANSCEIVER_3                                   3
#define TRANSCEIVER_4                                   4
#define TRANSCEIVER_5                                   5
#define TRANSCEIVER_6                                   6
#define TRANSCEIVER_7                                   7
#define TRANSCEIVER_8                                   8
#define TRANSCEIVER_9                                   9
#define TRANSCEIVER_10                                  10
#define FIBER_9_125MM_UNITS_KM                          14
#define FIBER_9_125MM_UNITS_100M                        15
#define FIBER_50_125MM_UNITS_10M                        16
#define FIBER_62P5_125MM_UNITS_10M                      17
#define COPPER_UNITS_M                                  18

#define UNSPECIFIED                                     0
#define LC                                              7
#define VACANT                                          0xFF

//#define SFP_VS_BASE                                     0x600

#define CONTROL_REG                                     0
#define STATUS_REG                                      1
#define AUTONEG_ADVERTISEMENT                           4
#define CONTROL_REG_1000BASE_T                          9
#define EXTENDED_PHY_SPECIFIC_CONTROL_REG               20
#define EXTENDED_ADDRESS_REG                            22
#define LED_CONTROL_REG                                 24
#define EXTENDED_PHY_SPECIFIC_CONTROL_2_REG             26
#define EXTENDED_PHY_SPECIFIC_STATUS_REG                27

/// number of bytes read from EEPROM to determine the manufacturer
#define NUM_MFG_ID_BYTES 								5

/// offset in EEPROM of the first mfg id byte
#define MFG_ID_OFFSET									20

/// bits 1-3 of the I2C addresses of the SFP devices, by manufacturer
#define AVAGO_I2C_ADDR 									6
#define STRAT_I2C_ADDR									3 

extern int octeon_twsi_write8_addr16(uint8_t, uint16_t, uint8_t);
extern int miiphy_write(unsigned char, unsigned char, unsigned short);
extern int miiphy_read(unsigned char, unsigned char, unsigned short *);

unsigned char connector_type[MAX_NUM_SFP] = {VACANT, VACANT, VACANT, VACANT};

void phyRegWrite (int interface, int reg, int mask, int newVal, int wait)
{
    unsigned short data;

    miiphy_read (interface, reg, &data);
    data = (data & mask) | newVal;
    miiphy_write (interface, reg, data);
    if (wait)
    {
        do
        {
            miiphy_read (interface, reg, &data);
        }
        while ((data & 0x8000) != 0) ; /* wait till finished */
    }
}

void phyRegRead (int interface, int reg, unsigned short *val)
{
    miiphy_read (interface, reg, val);
}

void sfpVSRegWrite (int dev, int i2c_addr, int reg, int mask, int newVal, int reset_wait)
{
    int data;
    uint16_t addr = ( ((uint8_t) i2c_addr) << 8 ) + reg;

    data = octeon_twsi_read8(dev, addr);
    data = octeon_twsi_read8(dev, addr) | (data << 8);
    data = (data & mask) | newVal;
    if (reset_wait)
    {
        octeon_twsi_write8_addr16 (dev, addr, (data & 0x7FFF) >> 8);
        octeon_twsi_write8_addr16 (dev, addr, data & 0x00FF);
        octeon_twsi_write8_addr16 (dev, addr, data >> 8);
        octeon_twsi_write8_addr16 (dev, addr, data & 0x00FF);
        do
        {
            data = octeon_twsi_read8(dev, addr);
            data = octeon_twsi_read8(dev, addr) | (data << 8);
        }
        while ((data & 0x8000) != 0);
	}
	else
    {
        octeon_twsi_write8_addr16 (dev, addr, data >> 8);
        octeon_twsi_write8_addr16 (dev, addr, data & 0x00FF);
	}
}

void sfpVSRegRead (int dev, int i2c_addr, int reg, int *val)
{
    int data;
   	uint16_t addr = (((uint8_t) i2c_addr) << 8) + reg;

    data = octeon_twsi_read8(dev, addr);
    data = octeon_twsi_read8(dev, addr) | ((data) << 8);
    *val = data;
}

/**
 * @brief
 *
 * function for comparing strings, similar to standard strcmp()
 * 
 * @param str1
 * first string to compare
 * 
 * @param str2
 * second string to compare
 * 
 * @param len
 * This parameter decides on how many bytes in the strings to compare.
 * It is irrelevant whether the strings are null terminated or not.
 * 
 * @return
 * An integral value indicating the relationship between strings.
 * A zero value indicates that the first len number of bytes
 * are identical. A positive value indicates that the first byte 
 * that does not match has a greater value in str1 than str2. 
 * A negative value indicates the opposite.
 */

int str_cmp(char * str1, char * str2, unsigned len)
{
	unsigned i;
	
	for (i = 0; i < len; i++)
	{
		if (str1[i] > str2[i]) return (i+1);
		if (str1[i] < str2[i]) return (-i-1);
	}
	
	return 0;
	
}

/**
 * @brief
 * this function finds bits 1-3 of the 
 * sfp device PHY I2C bus address by reading from 
 * the EEPROM to recognize the manufacturer
 * 
 * @param interface
 * the interface number, has to be in 0..3 for NPA5804
 * 
 * @return
 * bits 1-3 or -1 if interface number beyond 0..3 
 * -2 if error reading EEPROM  
 */
 
int sfp_get_i2c_addr(int interface)
{
	int i;
	int eeprom_byte;
	char mfg_id[NUM_MFG_ID_BYTES + 1];
	
	uint8_t dev_addr = BOARD_SFP0_TWSI_ADDR + interface;
	
	if (interface < 0 || interface > 3) return -1;
	
	for (i = 0; i < NUM_MFG_ID_BYTES; i++)
	{
		eeprom_byte = octeon_twsi_read8(dev_addr, MFG_ID_OFFSET + i);
		if (eeprom_byte < 0)
		{
			printf("sfp_get_mfg_code: error reading EEPROM\n");
			return -1;
		}
		mfg_id[i] = (char) eeprom_byte;
	}
	
	if (!str_cmp(mfg_id,"Strat",NUM_MFG_ID_BYTES)) return STRAT_I2C_ADDR;
	else return AVAGO_I2C_ADDR; // default to AVAGO, 
}


void sfpInit (int interface)
{   
    connector_type[interface] = octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, CONNECTOR);
    if (connector_type[interface] == VACANT)
    {
        printf ("V ");
    }
    else if (connector_type[interface] == UNSPECIFIED)
    {
    	int dev =  BOARD_SFP0_TWSI_ADDR + interface;
    	int i2c_addr = sfp_get_i2c_addr(interface);
    	
    	if (i2c_addr < 0)
        {
        	printf("%s: could not get the SFP's I2C address for interface %d\n", 
        		__FUNCTION__, interface);
        	printf("sfp_get_i2c_addr returned %d\n", interface);
        	return; 
        }
    	
        printf ("C ");
        
        /* command XCVRs/M88E1143 into RGMII to SGMII */
        phyRegWrite (interface, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8006, 0);
        /* command XCVRs/M88E1143 into approp mode (power up TC drivers as output clock) */
        phyRegWrite (interface, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        /* command LED/M88E1143 into approp mode Receive activity/Link */
        phyRegWrite (interface, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        /* command XCVRs/M88E1143 into approp mode (ANEG ON + FULL DUPLEX) and reset */
        phyRegWrite (interface, CONTROL_REG, 0xFFFF, 0x9100, 1);
       
        /* command SFPs/M88E1111 into SGMII mode */
        sfpVSRegWrite (dev, i2c_addr, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0x0000, 0x9084, 0);
        /* command SFPs/M88E1111 to advertise 1000BASE-T Full/Half-Duplex */
        sfpVSRegWrite (dev, i2c_addr, CONTROL_REG_1000BASE_T, 0x0000, 0x0F00, 0);
        /* command SFPs/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
        sfpVSRegWrite (dev, i2c_addr, AUTONEG_ADVERTISEMENT, 0x0000, 0x0DE1, 0);
        /* command SFPs into reset */
        sfpVSRegWrite (dev, i2c_addr, CONTROL_REG, 0x0000, 0x9140, 1);
        /* command SFPs/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
        sfpVSRegWrite (dev, i2c_addr, AUTONEG_ADVERTISEMENT, 0x0000, 0x0DE1, 0);
    }
    else
    {
        printf ("F ");
        /* command XCVRs/M88E1143 into RGMII to Fiber */
        phyRegWrite (interface, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x9003, 0);
        /* command XCVRs/M88E1143 into approp mode (power up TC drivers as output clock) */
        phyRegWrite (interface, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        /* command LED/M88E1143 into approp mode Receive activity/Link */
        phyRegWrite (interface, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        /* command XCVRs/M88E1143 into approp mode (ANEG ON + FULL DUPLEX) and reset */
        phyRegWrite (interface, CONTROL_REG, 0xFFFF, 0x9100, 1);
    }
}

void phyLinkSetup(void)
{
    /* command XCVRs/M88E1143 to page 1 */
    phyRegWrite (0, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (1, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (2, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (3, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (4, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (5, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (6, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);
    phyRegWrite (7, EXTENDED_ADDRESS_REG, 0xFF00, 0x0001, 0);

    /* initialize Fat Pipes */
    /* command XCVRs/M88E1143 into RGMII to Fiber (for Fat Pipe SERDES operation) */
    phyRegWrite (4, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
    phyRegWrite (5, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
    phyRegWrite (6, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
    phyRegWrite (7, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
    /* command XCVRs/M88E1143 into approp mode (power up TC drivers as output clock) */
    phyRegWrite (4, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
    phyRegWrite (5, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
    phyRegWrite (6, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
    phyRegWrite (7, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
    /* command LED/M88E1143 into approp mode Receive activity/Link */
    phyRegWrite (4, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
    phyRegWrite (5, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
    phyRegWrite (6, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
    phyRegWrite (7, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
    /* command XCVRs/M88E1143 into approp mode (ANEG ON + FULL DUPLEX) and reset */
    phyRegWrite (4, CONTROL_REG, 0xFFFF, 0x9100, 1);
    phyRegWrite (5, CONTROL_REG, 0xFFFF, 0x9100, 1);
    phyRegWrite (6, CONTROL_REG, 0xFFFF, 0x9100, 1);
    phyRegWrite (7, CONTROL_REG, 0xFFFF, 0x9100, 1);

    if ((*BOARD_ID_DIPSWITCH_REG) & EGRESS_IO_VIA_RTM)
    {
        /* RTM Module found */
        printf ("RTM Initialization\n");
        /* command XCVRs/M88E1143 into RGMII to Fiber (for Fat Pipe SERDES operation) */
        phyRegWrite (0, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
        phyRegWrite (1, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
        phyRegWrite (2, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
        phyRegWrite (3, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0xFFF0, 0x8003, 0);
        /* command XCVRs/M88E1143 into approp mode (power up TC drivers as output clock) */
        phyRegWrite (0, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        phyRegWrite (1, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        phyRegWrite (2, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        phyRegWrite (3, EXTENDED_PHY_SPECIFIC_CONTROL_2_REG, 0xFFFF, 0x0010, 0);
        /* command LED/M88E1143 into approp mode Receive activity/Link */
        phyRegWrite (0, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        phyRegWrite (1, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        phyRegWrite (2, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        phyRegWrite (3, LED_CONTROL_REG, 0xFFFF, 0x0002, 0);
        /* command XCVRs/M88E1143 into approp mode (ANEG ON + FULL DUPLEX) and reset */
        phyRegWrite (0, CONTROL_REG, 0xFFFF, 0x9100, 1);
        phyRegWrite (1, CONTROL_REG, 0xFFFF, 0x9100, 1);
        phyRegWrite (2, CONTROL_REG, 0xFFFF, 0x9100, 1);
        phyRegWrite (3, CONTROL_REG, 0xFFFF, 0x9100, 1);
    }
    else
    {
        /* initialize SFPs */
        printf ("SFP Type: ");
        sfpInit (0);
        sfpInit (1);
        sfpInit (2);
        sfpInit (3);
        printf ("\n");
    }
}

void late_board_init(void)
{
    printf("FPGA Version: 0x%02x\n", *(unsigned char *)BOARD_BASE_FPGA_ADDR);
    phyLinkSetup();
	
	uboot_version_num_store();
    *BOARD_LED_CONTROL_REG |= BOARD_LED_GREEN; /* turn on the green LED */
}	 

int do_sfp_show (void)
{
    int interface;

    for (interface = 0; interface < MAX_NUM_SFP; interface++)
    {
        printf ("Interface %d:\n", interface);
        switch (octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, ID))
        {
            case 0:
                printf ("    ID: Unknown or unspecified\n");
                break;
            case 1:
                printf ("    ID: GBIC\n");
                break;
            case 2:
                printf ("    ID: Module/connector soldered to motherboard\n");
                break;
            case 3:
                printf ("    ID: SFP transceiver\n");
                break;
            default:
                printf ("    ID: 0x%02x\n", octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, ID));
                break;
        }
        printf ("    Ext ID: 0x%02x\n", octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, EXT_ID));
        switch (octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, CONNECTOR))
        {
            case 0:
                printf ("    Connector: Unknown or unspecified\n");
                break;
            case 1:
                printf ("    Connector: SC\n");
                break;
            case 2:
                printf ("    Connector: Fiber Channel Style 1 copper connector\n");
                break;
            case 3:
                printf ("    Connector: Fiber Channel Style 2 copper connector\n");
                break;
            case 4:
                printf ("    Connector: BNC/TNC\n");
                break;
            case 5:
                printf ("    Connector: Fiber Channel coaxial headers\n");
                break;
            case 6:
                printf ("    Connector: Fiber Jack\n");
                break;
            case 7:
                printf ("    Connector: LC\n");
                break;
            case 8:
                printf ("    Connector: MT-RJ\n");
                break;
            case 9:
                printf ("    Connector: MU\n");
                break;
            case 10:
                printf ("    Connector: SG\n");
                break;
            case 11:
                printf ("    Connector: Optical Pigtail\n");
                break;
            case 32:
                printf ("    Connector: HSSDC II\n");
                break;
            case 33:
                printf ("    Connector: Copper Pigtail\n");
                break;
            default:
                printf ("    Connector: 0x%02x\n", octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, CONNECTOR));
                break;
        }
        printf ("    Link Length: 9/125 mm Fiber (units of kilometers) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, FIBER_9_125MM_UNITS_KM));
        printf ("    Link Length: 9/125 mm Fiber (units of 100 meters) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, FIBER_9_125MM_UNITS_100M));
        printf ("    Link Length: 50/125 mm Fiber (units of 10 meters) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, FIBER_50_125MM_UNITS_10M));
        printf ("    Link Length: 62.5/125 mm Fiber (units of 10 meters) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, FIBER_62P5_125MM_UNITS_10M));
        printf ("    Link Length: Copper (units of meters) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, COPPER_UNITS_M));
    }
    return (0);
}

int do_sfp_init(void)
{
    /* initialize SFPs */
    printf ("SFP Type: ");
    sfpInit (0);
    sfpInit (1);
    sfpInit (2);
    sfpInit (3);
    printf ("\n");
    return (0);
}

int octeon_post_error_store(uint8_t error_code)
{
    int loc;

    /* find location to store error code */
    for (loc = 0; loc < BOARD_POST_LOG_SIZE; loc++)
    {
        if (octeon_twsi_read8(BOARD_EEPROM_TWSI_ADDR, BOARD_POST_LOG_START + loc) == NO_POST_ERROR)
        {
            octeon_twsi_write8_addr16(BOARD_EEPROM_TWSI_ADDR, BOARD_POST_LOG_START + loc, error_code);
            return 0;
        }
    }
    return -1;
}

int octeon_post_error_retrieve(uint8_t *post_error_code_buf_ptr)
{
    int loc;

    /* retrieve the entire buffer */
    for (loc = 0; loc < BOARD_POST_LOG_SIZE; loc++)
    {
        post_error_code_buf_ptr[loc] = octeon_twsi_read8(BOARD_EEPROM_TWSI_ADDR, BOARD_POST_LOG_START + loc);
    }
    return 0;
}

int octeon_post_error_clear(void)
{
    int loc;

    /* clear the entire buffer */
    for (loc = 0; loc < BOARD_POST_LOG_SIZE; loc++)
    {
        octeon_twsi_write8_addr16(BOARD_EEPROM_TWSI_ADDR, BOARD_POST_LOG_START + loc, NO_POST_ERROR);
    }
    return 0;
}
