/*
 * (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>

#include <config.h>

#include <net.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)
    /* Disable MDIO and init registers */
    cvmx_write_csr(CVMX_SMI_EN, 0);
    /* Delay */
    udelay(10000); /* 10ms */
    cvmx_write_csr(CVMX_SMI_CMD, 0);
    cvmx_write_csr(CVMX_SMI_WR_DAT, 0);
    cvmx_write_csr(CVMX_SMI_RD_DAT, 0);
    cvmx_write_csr(CVMX_SMI_CLK, 0x0000000000001264ull);
    /* Delay */
    udelay(10000); /* 10ms */
    /* Enable MDIO */
    cvmx_write_csr(CVMX_SMI_EN, 1);
    /* Delay */
    udelay(10000); /* 10ms */
#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);
extern int do_inite1000eeprom (cmd_tbl_t *, int, int, char *);
extern void setMACNonContiguousNamedAlloc(void);
extern int delete_tuple(int);

int early_board_init(void)
{
    int cpu_ref = 33;
    char *sfpLinkOn = getenv("sfplinkon");

    DECLARE_GLOBAL_DATA_PTR;

#ifdef CONFIG_OCTEON_GEF_BOOT_PROGRESS_SUPPORT
    BOOT_PROGRESS_UPDATE(BPM_EARLY_BOARD_INIT);
#endif
    if (sfpLinkOn != NULL)
    {
        if (strcmp(sfpLinkOn, "yes") != 0)
        {
            *BOARD_TX_CONTROL_REG = 0x0F; /* disable all SFPs */
        }
    }
    else
    {
        *BOARD_TX_CONTROL_REG = 0x0F; /* disable all SFPs */
    }
    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;
        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);
    }
    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, 1, ee_buf, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
    if (addr >= 0)
    {
		/* carry over obsolete tuple values to new tuple */
        octeon_eeprom_mac_addr_old *ma_o_ptr = (void *)ee_buf;
        octeon_eeprom_mac_addr_t *ma_ptr = (void *)ee_buf;
        uint8_t mac_old[6];
        int count_old, mac_work;

        /* this tuple is obsolete - get MAC and delete it */
        memcpy((void *)(mac_old), (void *)(ma_o_ptr->mac_addr_base), 6);
        count_old = ma_o_ptr->count;
        /* delete_tuple(addr); */
        /* create new tuple with old value */
        tlv_hdr_ptr->type = EEPROM_MAC_ADDR_TYPE;
        tlv_hdr_ptr->length = sizeof(octeon_eeprom_mac_addr_t);
        tlv_hdr_ptr->checksum = 0;
        tlv_hdr_ptr->major_version = OCTEON_EEPROM_MAC_ADDR_MAJOR_VER;
        tlv_hdr_ptr->minor_version = OCTEON_EEPROM_MAC_ADDR_MINOR_VER;
        memcpy((void *)(ma_ptr->fabric_int0), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fabric_int1), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fabric_int2), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fabric_int3), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fp_int0), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fp_int1), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fp_int2), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->fp_int3), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->base_int0), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->base_int1), (void *)(mac_old), 6);
        mac_work = (ma_ptr->fabric_int0[2] << 24) | (ma_ptr->fabric_int0[3] << 16) |
                   (ma_ptr->fabric_int0[4] << 8) | (ma_ptr->fabric_int0[5] << 0);
        ++mac_work;
        ma_ptr->fabric_int1[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fabric_int1[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fabric_int1[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fabric_int1[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fabric_int2[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fabric_int2[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fabric_int2[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fabric_int2[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fabric_int3[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fabric_int3[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fabric_int3[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fabric_int3[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fp_int0[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fp_int0[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fp_int0[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fp_int0[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fp_int1[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fp_int1[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fp_int1[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fp_int1[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fp_int2[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fp_int2[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fp_int2[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fp_int2[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->fp_int3[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->fp_int3[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->fp_int3[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->fp_int3[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->base_int0[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->base_int0[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->base_int0[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->base_int0[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->base_int1[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->base_int1[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->base_int1[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->base_int1[5] = (mac_work >> 0) & 0xFF;
        ma_ptr->count = count_old;
        write_tlv_tuple(tlv_hdr_ptr);
    }
    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 2, 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 = OCTEON_NUM_MACS;
        gd->mac_desc.fabric_int0[0] = 0x00;
        gd->mac_desc.fabric_int0[1] = 0xDE;
        gd->mac_desc.fabric_int0[2] = 0xAD;
        gd->mac_desc.fabric_int0[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fabric_int0[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fabric_int0[5] = 0x00;
        gd->mac_desc.fabric_int1[0] = 0x00;
        gd->mac_desc.fabric_int1[1] = 0xDE;
        gd->mac_desc.fabric_int1[2] = 0xAD;
        gd->mac_desc.fabric_int1[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fabric_int1[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fabric_int1[5] = 0x01;
        gd->mac_desc.fabric_int2[0] = 0x00;
        gd->mac_desc.fabric_int2[1] = 0xDE;
        gd->mac_desc.fabric_int2[2] = 0xAD;
        gd->mac_desc.fabric_int2[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fabric_int2[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fabric_int2[5] = 0x02;
        gd->mac_desc.fabric_int3[0] = 0x00;
        gd->mac_desc.fabric_int3[1] = 0xDE;
        gd->mac_desc.fabric_int3[2] = 0xAD;
        gd->mac_desc.fabric_int3[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fabric_int3[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fabric_int3[5] = 0x03;
        gd->mac_desc.fp_int0[0] = 0x00;
        gd->mac_desc.fp_int0[1] = 0xDE;
        gd->mac_desc.fp_int0[2] = 0xAD;
        gd->mac_desc.fp_int0[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fp_int0[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fp_int0[5] = 0x04;
        gd->mac_desc.fp_int1[0] = 0x00;
        gd->mac_desc.fp_int1[1] = 0xDE;
        gd->mac_desc.fp_int1[2] = 0xAD;
        gd->mac_desc.fp_int1[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fp_int1[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fp_int1[5] = 0x05;
        gd->mac_desc.fp_int2[0] = 0x00;
        gd->mac_desc.fp_int2[1] = 0xDE;
        gd->mac_desc.fp_int2[2] = 0xAD;
        gd->mac_desc.fp_int2[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fp_int2[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fp_int2[5] = 0x06;
        gd->mac_desc.fp_int3[0] = 0x00;
        gd->mac_desc.fp_int3[1] = 0xDE;
        gd->mac_desc.fp_int3[2] = 0xAD;
        gd->mac_desc.fp_int3[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.fp_int3[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.fp_int3[5] = 0x07;
        gd->mac_desc.base_int0[0] = 0x00;
        gd->mac_desc.base_int0[1] = 0xDE;
        gd->mac_desc.base_int0[2] = 0xAD;
        gd->mac_desc.base_int0[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.base_int0[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.base_int0[5] = 0x08;
        gd->mac_desc.base_int1[0] = 0x00;
        gd->mac_desc.base_int1[1] = 0xDE;
        gd->mac_desc.base_int1[2] = 0xAD;
        gd->mac_desc.base_int1[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.base_int1[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.base_int1[5] = 0x09;
    }
    /* 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 FIBER                                           0
#define COPPER                                          1

//#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, int verbose, int disableLink)
{
    uint64_t sfpPresInfo;
    uint8_t txControlMask;

    txControlMask = (1 << (3 - interface));
    *BOARD_TX_CONTROL_REG &= ~txControlMask; /* enable SFP for initialization */
    sfpPresInfo = cvmx_read_csr(CVMX_GPIO_RX_DAT);
    if (sfpPresInfo & (0x08ull >> interface))
    {
		printf("SFP Present not detected\n");
        connector_type[interface] = VACANT;
    }
    else
    {
        connector_type[interface] = octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, TRANSCEIVER_6);
        if (connector_type[interface] != VACANT)
        {
            if (connector_type[interface] & 0x08)
            {
                connector_type[interface] = COPPER;
            }
            else
            {
                connector_type[interface] = FIBER;
            }
        }
    }
    if (connector_type[interface] == VACANT)
    {
        if (verbose)
        {
            printf ("V ");
        }
    }
    else if (connector_type[interface] == COPPER)
    {
    	int dev =  BOARD_SFP0_TWSI_ADDR + interface;
    	int i2c_addr = sfp_get_i2c_addr(interface);

    	if (i2c_addr < 0)
        {
            if (verbose)
            {
                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;
        }
        if (verbose)
        {
            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
    {
        if (verbose)
        {
            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);
    }
    if (disableLink)
    {
        char *sfpLinkOn = getenv("sfplinkon");

        if (sfpLinkOn != NULL)
        {
            if (strcmp(sfpLinkOn, "yes") != 0)
            {
                *BOARD_TX_CONTROL_REG |= txControlMask; /* disable SFP */
            }
        }
        else
        {
            *BOARD_TX_CONTROL_REG |= txControlMask; /* disable SFP */
        }
    }
}

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, 1, 1);
        sfpInit (1, 1, 1);
        sfpInit (2, 1, 1);
        sfpInit (3, 1, 1);
        printf ("\n");
    }
}

extern struct eth_device *eth_current;

void link_up_wait (void)
{
    int i;

    for (i = 0; i < LINK_UP_TIMEOUT_5651X; i++)
    {
        udelay (1 * 1000 * 1000); /* seconds */
    }
}

void board_link_assert(int link)
{
    uint8_t linkNegated;

    linkNegated = ((*BOARD_TX_CONTROL_REG) >> (3 - link)) & 0x01;
    if (linkNegated)
    {
        sfpInit(link, 0, 0);
        link_up_wait();
    }
}

void board_link_process(void)
{
	if (strcmp(eth_current->name, "octeth0") == 0)
	{
		board_link_assert(0);
	}
	else if (strcmp(eth_current->name, "octeth1") == 0)
	{
		board_link_assert(1);
	}
	else if (strcmp(eth_current->name, "octeth2") == 0)
	{
		board_link_assert(2);
	}
	else if (strcmp(eth_current->name, "octeth3") == 0)
	{
		board_link_assert(3);
	}
}

extern int do_validatenfi (cmd_tbl_t *, int, int, char *[]);
extern int do_reset(cmd_tbl_t *, int, int, char *[]);

#ifndef CONFIG_OCTEON_FAILSAFE
void uboot_soak_update(void)
{
    uint64_t testFlag, soakCounter, bootBank, *pSource;
    char *pCmd[] = {"validatenfi", "0" "0", "0", "0", "0"};
    /*                command   bootBank   size   fastBoot testInd soakCount */

    testFlag = *((uint64_t *)CFG_NFI_TEST_FLAG_ADDR);
    if ((testFlag != 0xFFFFFFFFFFFFFFFFull) && (testFlag))
    {
        soakCounter = *((uint64_t *)CFG_NFI_SOAK_COUNTER_ADDR);
        bootBank = *((uint64_t *)CFG_NFI_BOOT_BANK_ADDR);
        if (soakCounter == 0)
        {
            /* soak failed - reset NFI parameters */
            printf("Flash image at: 0x%08x SOAK FAILED! - fallback image assert\n", CONFIG_IMAGE_BASE);
            switch (bootBank)
            {
                case 0:
                    pSource = (uint64_t *)CFG_NORMAL1_BOOTLOADER_BASE;
                    pCmd[1] = "1"; /* specify boot bank */
                    break;
                case 1:
                default:
                    pSource = (uint64_t *)CFG_NORMAL0_BOOTLOADER_BASE;
                    pCmd[1] = "0"; /* specify boot bank */
                    break;
            }
            if (*pSource == -1)
            {
                pCmd[2] = "0"; /* force Failsafe because no valid image found */
            }
            else
            {
                pCmd[2] = "0xFFFFFFFF"; /* max size for this bootbank */
            }
            do_validatenfi((cmd_tbl_t *)NULL, 0, 6, pCmd);
            printf("\nRESET IN PROGRESS...\n\n");
            /* perform reset */
            do_reset((cmd_tbl_t *)NULL, 0, 0, (char **)NULL);
            /* does not return */
        }
        else
        {
            char soakCounterString[2];

            /* soak in progress - decrement the soak counter */
            printf("Flash image at: 0x%08x soak in progress\n", CONFIG_IMAGE_BASE);
            if (bootBank == 0)
            {
                pCmd[1] = "0"; /* specify boot bank */
            }
            else
            {
                pCmd[1] = "1"; /* specify boot bank */
            }
            pCmd[2] = "0xFFFFFFFF"; /* specify size (will be capped) */
            pCmd[4] = "1"; /* specify test indicator */
            --soakCounter;
            soakCounterString[0] = '0' + (soakCounter % 10);
            soakCounterString[1] = '\0';
            pCmd[5] = &soakCounterString[0]; /* specify soak counter */
            do_validatenfi((cmd_tbl_t *)NULL, 0, 6, pCmd);
        }
    }
}
#endif

void uboot_mac_addr_update(void)
{
    DECLARE_GLOBAL_DATA_PTR;
    int addr, ind, amcMacFromMMC[BOARD_MMC_AMC_MAC_SIZE];
    int amcMacChanged, mmcMacPres;
    uint16_t eeprom_buf16[OCTEON_EEPROM_MAX_TUPLE_LENGTH/2 + 1];
    uint8_t *eeprom_buf = (void *)eeprom_buf16;
    uint8_t *tpl_mac_ptr, *gd_mac_ptr;;
    octeon_eeprom_mac_addr_t *amcMac = (void *)eeprom_buf;
    char *s;

	s = getenv("mmcmacoverride");
	if ((s == NULL) || (strcmp(s, "yes") != 0))
	{
        amcMacChanged = 0;
        /* get AMC MAC from MMC space EEPROM if it exists */
        for (ind = 0, addr = BOARD_MMC_AMC_MAC_START, mmcMacPres = 0; ind < BOARD_MMC_AMC_MAC_SIZE; ind++, addr++)
        {
            amcMacFromMMC[ind] = octeon_twsi_read8(BOARD_EEPROM_TWSI_ADDR, addr);
            if (amcMacFromMMC[ind] != 0xFF)
            {
                mmcMacPres = 1;
            }
        }
        if (mmcMacPres)
        {
            /* compare MMC MAC with AMC MAC */
            addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 2, (uint8_t *)amcMac, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
            if (addr >= 0)
            {
                tpl_mac_ptr = &(amcMac->fabric_int0[0]);
                for (ind = 0; ind < BOARD_MMC_AMC_MAC_SIZE; ind++)
                {
                    if (amcMacFromMMC[ind] != tpl_mac_ptr[ind])
                    {
                        amcMacChanged = 1;
                        break;
                    }
                }
            }
            else
            {
                amcMacChanged = 1;
            }
            /* update AMC MAC if necessary */
            if (amcMacChanged)
            {
                amcMac->header.type = EEPROM_MAC_ADDR_TYPE;
                amcMac->header.length = sizeof(octeon_eeprom_mac_addr_t);
                amcMac->header.checksum = 0;
                amcMac->header.major_version = OCTEON_EEPROM_MAC_ADDR_MAJOR_VER;
                amcMac->header.minor_version = OCTEON_EEPROM_MAC_ADDR_MINOR_VER;
                amcMac->count = OCTEON_NUM_MACS;
                tpl_mac_ptr = &(amcMac->fabric_int0[0]);
                for (ind = 0; ind < BOARD_MMC_AMC_MAC_SIZE; ind++)
                {
                    tpl_mac_ptr[ind] = amcMacFromMMC[ind];
                }
                printf("MAC Address Update... ");
                write_tlv_tuple((octeon_eeprom_header_t *)amcMac);
                printf("done\n");
                /* update global data */
                gd_mac_ptr = (uint8_t *)&(gd->mac_desc.fabric_int0[0]);
                for (ind = 0; ind < BOARD_MMC_AMC_MAC_SIZE; ind++)
                {
                    gd_mac_ptr[ind] = tpl_mac_ptr[ind];
                }
                /* update 82546 eeprom */
                do_inite1000eeprom ((cmd_tbl_t *)NULL, (int)NULL, (int)NULL, (char *)NULL);
                printf("MAC Address Change!... (RESET SYSTEM TO TAKE EFFECT)\n");
            }
        }
    }
    setMACNonContiguousNamedAlloc();
}

void uboot_boot_bank_update(void)
{
    uint8_t mmc_boot_bank;
    uint8_t update_nfi = 0;
    uint64_t nfi_boot_bank;
    char *pCmd[] = {"validatenfi", "0" "0", "0", "0", "0"};
    /*                command   bootBank   size   fastBoot testInd soakCount */

    nfi_boot_bank = *((uint64_t *)CFG_NFI_BOOT_BANK_ADDR);
    if (nfi_boot_bank == 0)
    {
        printf("NFI Boot Bank: 0\n");
	}
    else if (nfi_boot_bank == 1)
    {
        printf("NFI Boot Bank: 1\n");
    }
    else
    {
        printf("NFI Boot Bank: Unspecified\n");
    }
    mmc_boot_bank = octeon_twsi_read8(BOARD_EEPROM_TWSI_ADDR, BOARD_MMC_BOOT_BANK);
    if (mmc_boot_bank != 0xFF)
    {
        mmc_boot_bank = (mmc_boot_bank & 0x01) ? 1 : 0;
        /* MMC is asserting a boot bank */

#ifndef CONFIG_OCTEON_FAILSAFE
        if (mmc_boot_bank != nfi_boot_bank)
#endif

        {
            /* update the NFI boot bank */
            printf("Updating Bootbank with MMC Bootbank specifier (%d) (RESET SYSTEM TO TAKE EFFECT)\n", mmc_boot_bank);
            update_nfi = 1;
        }
        if ((((*((uint64_t *)CFG_NFI_TEST_FLAG_ADDR)) != 0)    && ((*((uint64_t *)CFG_NFI_TEST_FLAG_ADDR)) != -1)) ||
            (((*((uint64_t *)CFG_NFI_SOAK_COUNTER_ADDR)) != 0) && ((*((uint64_t *)CFG_NFI_SOAK_COUNTER_ADDR)) != -1)))
        {
            printf("SFU Test / Soak is being cancelled\n");
            update_nfi = 1;
        }
        if (update_nfi)
        {
            if (mmc_boot_bank == 0)
            {
                pCmd[1] = "0"; /* specify boot bank */
            }
            else
            {
                pCmd[1] = "1"; /* specify boot bank */
            }
            pCmd[2] = "0xFFFFFFFF"; /* specify size (will be capped) */
            do_validatenfi((cmd_tbl_t *)NULL, 0, 6, pCmd);
        }
    }
}

void late_board_init(void)
{
    int addr;
    uint8_t ee_buf[OCTEON_EEPROM_MAX_TUPLE_LENGTH];
    char *sfpLinkOn = getenv("sfplinkon");

    if (sfpLinkOn != NULL)
    {
        if (strcmp(sfpLinkOn, "yes") == 0)
        {
            *BOARD_TX_CONTROL_REG = 0x00; /* enable all SFPs */
        }
    }

#ifndef CONFIG_OCTEON_FAILSAFE
    uboot_soak_update();
#endif

    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 1, ee_buf, OCTEON_EEPROM_MAX_TUPLE_LENGTH);
    if (addr >= 0)
    {
        printf("Obsolete MAC Tuple has been upgraded\n");
        delete_tuple(addr);
	}

    printf("FPGA Version: 0x%02x\n", *(unsigned char *)BOARD_BASE_FPGA_ADDR);
    phyLinkSetup();
    uboot_version_num_store();

#ifdef CONFIG_OCTEON_GEF_BOOT_PROGRESS_SUPPORT
    BOOT_PROGRESS_UPDATE(BPM_MAC_ADDR_UPDATE);
#endif
    uboot_mac_addr_update();

#ifdef CONFIG_OCTEON_GEF_BOOT_PROGRESS_SUPPORT
    BOOT_PROGRESS_UPDATE(BPM_BOOT_BANK_UPDATE);
#endif
    uboot_boot_bank_update();
    *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, 1, 1);
    sfpInit (1, 1, 1);
    sfpInit (2, 1, 1);
    sfpInit (3, 1, 1);
    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;
}
