/*
 * (C) Copyright 2004,2005
 * Cavium Networks
 *
 * 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 <miiphy.h>
#include <cvmx-mdio.h>

/* external prototypes */
extern int delete_tuple(int delete_addr);

/* local prototypes */
void neg_access_to_i2c_bus(void);
void rel_control_of_i2c_bus(void);

// #define MAC_DEBUG 1
#undef MAC_DEBUG

/* define this when we want to load firmware
   to the PHYs via mdio rather than via eeprom.
*/

//#define QT2025_MDIOBOOT 1
#define LIVE_PHYS 1

/* #define PHY_DEBUG 1 */ 
/* #define PHY_DEBUG_GETSTATUS 1   debug specifically for get_phy_status() */
/* #define PHY_DEBUG_FW 1    debug specifically for phy_fw() */
/* #define PHY_DEBUG_STATUS 1   debug specifically for print_link_status() */

#define OLD_MDIO_DATA 1  /* Pete: debug using Foxboro PHY eeprom firmware */
/* #undef OLD_MDIO_DATA */

#define SGMII_MODE 1     /* switch between SGMII and 1000BASE-X 1G modes */


#define EEPROM_FW_GROUP1 16384
#define EEPROM_FW_GROUP2 8192

#ifdef OLD_MDIO_DATA
#include "mdio_old_data.h"
#else
#include "mdio_data.c"
#endif

#define AMCC_PHY_MODE_1GE	1
#define AMCC_PHY_MODE_10GE	10

/* for debugging - local to this file:
   if defined, we run clause45-specific code,
   otherwise we run clause22.
*/

#define CLAUSE45 1

void dbg_print_octeon_status(void);

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

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

#if defined(CONFIG_OCTEON_GEF_SFP_PRESENT)
#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_EEPROM_BASE                                 0xA000
#define SFP_VS_BASE                                     0xAC00

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

#endif /* CONFIG_OCTEON_GEF_SFP_PRESENT */

#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 EXTENDED_PHY_SPECIFIC_CONTROL_2_REG             26
#define EXTENDED_PHY_SPECIFIC_STATUS_REG                27

// extern void setMACNonContiguousNamedAlloc(unsigned char *macs);
extern void setMACNonContiguousNamedAlloc(void);

extern int  eeprom_diags(void);
extern void uboot_version_num_store(void);
extern int  write_tlv_tuple(octeon_eeprom_header_t *tlv_hdr_ptr);
extern int  octeon_twsi_write8_addr16(uint8_t dev_addr, 
                                      uint16_t addr, 
                                      uint8_t data);
void sfpLinkSetup(void);
void sfpLinkStatus(void);
void get_phy_status(uint8_t mdio_addr, int mdio_bus);

int  checkboard (void);
void cvm_oct_led_init(void);
int  early_board_init(void);
void octeon_led_str_write(const char *str);

void late_board_init(void);
int  octeon_post_error_store(uint8_t error_code);
int  octeon_post_error_retrieve(uint8_t *post_error_code_buf_ptr);
int  octeon_post_error_clear(void);

int  do_sfp_show(void);
void sfpVSRegWrite(int dev, int reg, int mask, int newVal, int reset_wait);
void sfpVSRegRead(int dev, int reg, int *val);
/* void sfpInit(int interface); */
int  do_sfp_init(void);


/* 10G PHY */
int amcc10gPhyLinkSetup(uint8_t mdio_addr, int mdio_bus);
int amcc_is_phy_alive(uint8_t mdio_addr, int mdio_bus);

int phy_fw(uint8_t mdio_addr, int mdio_bus);

void phyRegWrite(uint8_t mdio_addr, uint16_t reg, uint16_t mask, uint16_t newVal, int wait, uint8_t dev_type, int bus_id);
void phyRegRead (uint8_t mdio_addr, uint16_t reg, uint16_t *val, uint8_t dev_type, int bus_id);

int cvmx_mdio_address_write_ieee_clause45(unsigned char phy_op, int bus_id, int phy_addr, uint8_t dev_type, uint16_t data);
int cvmx_mdio_read_ieee_clause45(int bus_id, int phy_addr, uint8_t dev_type);

/* keep everybody happy */
void phyLinkSetup(void)
{
   return;
}

/*****************************************************************************
 **                        Get MAC Address
 **
 **    This function performs all the appropriate steps to determine the 
 ** correct MAC address for the selected Ethernet port and then returns that
 ** MAC address to the calling code.
 **
 **   Each MAC address is assigned a unique value and not derived from
 ** a base address (unless no mac address can be found in which case a mac
 ** address is calculated from a default base address).  The MAC addresses 
 ** are kept in up to two locations:  
 ** 1) A FRU record managed by the H8, 2) A tuple in the SEEPROM.
 **
 **   The priority of where the MAC addresses come from is shown below:
 ** 1 - Get the MAC address from the H8.
 ** 2 - Check to see if a tuple containing a base MAC address is available.
 ** 3 - If the steps above do not produce a MAC address then calculate a
 **     mac address from a default base address and return that value to the
 **     calling code.
 **
 *************
 ** Option 1 *
 *************
 **   The MMC (H8) will create a FRU record containg the MAC addresses for all
 **   four Ethernet ports.  Once that record is created, every time the AMC
 **   card boots, the H8 will read up that FRU record and store all the
 **   available MAC addresses in the FPGA's dual-port ram in the format and
 **   offset shown below:
 **
 **   Offset Length Description
 **   ------ ------ -----------------------------------------------------
 **    0x87    1    MAC address count.  (Should be 4 for 5651x)  This field 
 **                 contains the total number of MAC addresses available in 
 **                 this table.  Each MAC address contains two fields as 
 **                 described below.
 **    0x88    1    Mac address type as described below:
 **                 0 = No MAC address is available for this port.
 **                 1 = This is the MAC address for the Base ports (Intel
 **                     82571 controller) going to the AMC connector
 **                 2 = This is a MAC address for Fabric ports going to
 **                     front panel.
 **                 3 = This is a MAC address for Fabric ports going to AMC
 **                     connector.
 **    0x89    6    MAC address for first Ethernet port connected to Fabric
 **                 ports 8-11 (QLM1).
 **    0x8F    1    Mac address type
 **    0x90    6    MAC address for second Ethernet port connected to Fabric
 **                 ports 17-20 (QLM3).
 **    0x96    1    Mac address type
 **    0x97    6    MAC address for first Ethernet port connected to Base
 **                 port 0 (Port 0 on 82571 on QLM2).
 **    0x9D    1    Mac address type
 **    0x9E    6    MAC address for second Ethernet port connected to Base
 **                 port 1 (Port 1 on 82571 on QLM2).
 **
 *************
 ** Option 2 *
 *************
 **   See if there is a tuple containg a base MAC address.  If one is found
 ** return a MAC address equal to the sum of the base mac address in this 
 ** tuple and the "eth_port" parameter received from the calling code.
 **
 *************
 ** Option 3 *
 *************
 **   If no MAC addresses can be found in any of the previous locations, then
 ** a mac address will be calculated from the sum of a default base MAC address
 ** and the "eth_port" parameter supplied by the calling code.
 **
 ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **
 **
 ** INPUT - "mac_addr" = pointer to where the calling code would like this
 **         function to store the mac address (once it is determined or
 **         calculated). Exactly 6 bytes will be written to the calling code's
 **         variable.
 **       - "eth_port" = the Etherenet port for which mac address should be
 **         determined as defined below:
 **
 **         Port 
 **         Val  Physical Ethernet Port
 **         ---- ----------------------------------------------------------
 **           0  Ethernet port attached to QLM1
 **           1  Ethernet port attached to QML3
 **           2  Ethernet port attached to port 0 in 82571 device on QLM2
 **           3  Ethernet port attached to port 1 in 82571 device on QLM2
 **
 ** OUTPUT = The mac address will be written to the array point to by the
 **          calling code.
 **
 ** RETURN - Nothing
 **
 **
 ** Addendum to these comments:
 **
 ** This function has been split into to separate functions:
 **
 ** get_mac_addr is called by any code requiring a mac address for a specific
 ** ethernet port.
 **
 ** initial_mac_setup is only called by get_mac_addr, and only the first
 ** time that get_mac_addr is called.  After initial_mac_setup is called,
 ** the mac data for all the ports is contained in the global data struct
 ** and all mmc/tuple mac address issues have been dealt with.  The named_alloc
 ** block that the Linux drivers access to obtain their mac addresses has
 ** also been set up.
 **
 ** Subsequent calls to get_mac_addr will simply return the appropriate
 ** mac address as stored in the global data struct.
 **
 ** The flow of initial_mac_setup essentially follows that of the TNPA5854.
 **
 ** There are two U-boot environment variables used by this code:
 **
 ** "mmcmacoverride yes" tells initial_mac_setup to skip the mmc altogether
 ** and only use what is in the tuple.  This should be set if the U-boot
 ** tuple differs from the mmc fru mac data and the user wishes to use the
 ** tuple instead.
 **
 ** "ncmac_alloc yes" is actually used in the setMACNonContiguousNamedAlloc
 ** function which is called by initial_mac_setup.  If this variable is found,
 ** the named_alloc block "ncmac_block" is setup and the mac addresses are
 ** stored into it for use by the Linux ethernet drivers (e1000 and cavium-ethernet).
 ** This variable should ALWAYS be set if U-boot is to supply the mac addresses
 ** to Linux.
 ****************************************************************************/

void initial_mac_setup(void)
{
	DECLARE_GLOBAL_DATA_PTR;
	
    /* Populate global data from eeprom */
    int addr;
	int index, index1, index2, index3;
    int amcMacChanged, mmcMacPres;	
	char *s;
    int mac_byte;            /* current mac byte */
	uint8_t *fpga_ptr;       /* ptr to beg of fpga regs */
    uint8_t mac_count;           /* tot mac adrs in FPGA table */
	uint8_t ee_buf[OCTEON_EEPROM_MAX_TUPLE_LENGTH];
    octeon_eeprom_header_t *tlv_hdr_ptr = (void *)ee_buf;
	octeon_eeprom_mac_addr_t *ma_ptr;

    ma_ptr = (void *)ee_buf;
	
    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;
        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); /* delete old tuple */
        /* 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->qlm1_port), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->qlm3_port), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->amc_port0), (void *)(mac_old), 6);
        memcpy((void *)(ma_ptr->amc_port1), (void *)(mac_old), 6);
        mac_work = (ma_ptr->qlm1_port[2] << 24) | (ma_ptr->qlm1_port[3] << 16) |
                   (ma_ptr->qlm1_port[4] << 8) | (ma_ptr->qlm1_port[5] << 0);
        ++mac_work;
        ma_ptr->qlm3_port[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->qlm3_port[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->qlm3_port[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->qlm3_port[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->amc_port0[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->amc_port0[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->amc_port0[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->amc_port0[5] = (mac_work >> 0) & 0xFF;
        ++mac_work;
        ma_ptr->amc_port1[2] = (mac_work >> 24) & 0xFF;
        ma_ptr->amc_port1[3] = (mac_work >> 16) & 0xFF;
        ma_ptr->amc_port1[4] = (mac_work >> 8) & 0xFF;
        ma_ptr->amc_port1[5] = (mac_work >> 0) & 0xFF;
        ma_ptr->count = MAC_ADRS_COUNT;
        write_tlv_tuple(tlv_hdr_ptr);
    }
    addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 3, 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.qlm1_port[0] = 0x00;
        gd->mac_desc.qlm1_port[1] = 0xDE;
        gd->mac_desc.qlm1_port[2] = 0xAD;
        gd->mac_desc.qlm1_port[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.qlm1_port[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.qlm1_port[5] = 0x00;
        gd->mac_desc.qlm3_port[0] = 0x00;
        gd->mac_desc.qlm3_port[1] = 0xDE;
        gd->mac_desc.qlm3_port[2] = 0xAD;
        gd->mac_desc.qlm3_port[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.qlm3_port[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.qlm3_port[5] = 0x01;
        gd->mac_desc.amc_port0[0] = 0x00;
        gd->mac_desc.amc_port0[1] = 0xDE;
        gd->mac_desc.amc_port0[2] = 0xAD;
        gd->mac_desc.amc_port0[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.amc_port0[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.amc_port0[5] = 0x02;
        gd->mac_desc.amc_port1[0] = 0x00;
        gd->mac_desc.amc_port1[1] = 0xDE;
        gd->mac_desc.amc_port1[2] = 0xAD;
        gd->mac_desc.amc_port1[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.amc_port1[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.amc_port1[5] = 0x03;
    }

		/* at this point gd->mac_desc has default mac addresses either built off of
		   the old base address tuple (which is then deleted) or locally generated
		   because the new tuple which had just been written, could not be read.

		   if the old tuple did not previously exist, then if the new tuple was read,
		   it had been rpeviously populated and written the last time U-boot ran.

		   Now, if mmcmacoverride is NOT set, attempt to get mac addrs data from
		   the mmc (via shared memory in the fpga).  If mmcmacoverride is set and/or
		   there is no mmc mac data, use the data that was just generated.
		*/

	s = getenv("mmcmacoverride");
	if ((s == NULL) || (strcmp(s, "yes") != 0))
	{
        amcMacChanged = 0;

		fpga_ptr = (uint8_t *) BOARD_BASE_FPGA_ADDR; /* set up fpga reg pointer */
		mac_count = fpga_ptr[DPR_MAC_COUNT_OS]; /* get tot mac adrs avail */
		
#if MAC_DEBUG /* DEBUG */
			  for (index = 0x87; index < 0xA4; index++)
				  printf("offset %04x = %02x\n",index, fpga_ptr[index]);
#endif
		
			  if (mac_count == MAC_ADRS_COUNT)
			  {
					  /* calculate the offset of the requested mac adrs */
				  index = (DPR_MAC_COUNT_OS+1); /* offset of 1st mac adrs entry */
				  index += (TOT_MAC_ENTRY_BYTES * 0); /* inc to req mac adrs */
				  index++;                      /* inc past MAC adrs type */

				  index1 = (DPR_MAC_COUNT_OS+1); /* offset of 1st mac adrs entry */
				  index1 += (TOT_MAC_ENTRY_BYTES * 1); /* inc to req mac adrs */
				  index1++;                      /* inc past MAC adrs type */

				  index2 = (DPR_MAC_COUNT_OS+1); /* offset of 1st mac adrs entry */
				  index2 += (TOT_MAC_ENTRY_BYTES * 2); /* inc to req mac adrs */
				  index2++;                      /* inc past MAC adrs type */

				  index3 = (DPR_MAC_COUNT_OS+1); /* offset of 1st mac adrs entry */
				  index3 += (TOT_MAC_ENTRY_BYTES * 3); /* inc to req mac adrs */
				  index3++;                      /* inc past MAC adrs type */
				  
				  for (mac_byte = 0; mac_byte < 6; mac_byte++)
				  {
					  gd->mac_desc.qlm1_port[mac_byte] = fpga_ptr[index + mac_byte];
					  gd->mac_desc.qlm3_port[mac_byte] = fpga_ptr[index1 + mac_byte];
					  gd->mac_desc.amc_port0[mac_byte] = fpga_ptr[index2 + mac_byte];
					  gd->mac_desc.amc_port1[mac_byte] = fpga_ptr[index3 + mac_byte];
				  }

				  gd->mac_desc.count = MAC_ADRS_COUNT;
				  
				  mmcMacPres = 1;
				  
			  } /* if correct mac count from mmc */

			  if (mmcMacPres)
			  {
					  /* compare MMC MAC with AMC MAC */
				  addr = octeon_tlv_get_tuple_addr(CFG_DEF_EEPROM_ADDR, EEPROM_MAC_ADDR_TYPE, 3, ee_buf,
									 OCTEON_EEPROM_MAX_TUPLE_LENGTH);
				  
				  if (addr >= 0)
				  {
					  for (mac_byte = 0; mac_byte < 6; mac_byte++) {
						  if ((ma_ptr->qlm1_port[mac_byte] != gd->mac_desc.qlm1_port[mac_byte]) ||
							  (ma_ptr->qlm3_port[mac_byte] != gd->mac_desc.qlm3_port[mac_byte]) ||
							  (ma_ptr->amc_port0[mac_byte] != gd->mac_desc.amc_port0[mac_byte]) ||
							  (ma_ptr->amc_port1[mac_byte] != gd->mac_desc.amc_port1[mac_byte])) {
							  amcMacChanged = 1;
							  break;
						  }
					  }
								
				  } else {
					  amcMacChanged = 1;  /* tuple does not yet exist - need to write it anyway */
				  }

					  /* update AMC MAC if necessary */
				  if (amcMacChanged)
				  {
						  /* compare failed */
					  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;

					  for (mac_byte = 0; mac_byte < 6; mac_byte++)
					  {
						  ma_ptr->qlm1_port[mac_byte] = gd->mac_desc.qlm1_port[mac_byte];
						  ma_ptr->qlm3_port[mac_byte] = gd->mac_desc.qlm3_port[mac_byte];
						  ma_ptr->amc_port0[mac_byte] = gd->mac_desc.amc_port0[mac_byte];
						  ma_ptr->amc_port1[mac_byte] = gd->mac_desc.amc_port1[mac_byte];
					  }

					  ma_ptr->count = MAC_ADRS_COUNT;
					  printf("MAC Address Update... ");				
					  write_tlv_tuple(tlv_hdr_ptr);
					  printf("done\n");
				  }
			
			  } /* if mmcMacPres */
	} /* if NOT mmcmacoverride set */
	
				  /* create the named_alloc space for the Linux drivers to acces the
					 mac addresses. */
			  
    setMACNonContiguousNamedAlloc();

		/* from here on, the global ptr contains the mac addresses for U-boot code to access */
}

void get_mac_addr(unsigned char *mac_addr, int eth_port)
{
	DECLARE_GLOBAL_DATA_PTR;

    int mac_byte;            /* current mac byte */
	
	static int have_mac_addrs = 0;

	if (have_mac_addrs == 0) {
		initial_mac_setup();
		have_mac_addrs = 1;

#if MAC_DEBUG /* DEBUG */
		printf("\nget_mac_addr: qlm1 addr = ");
		for (mac_byte = 0; mac_byte < 6; mac_byte++)
		{
			printf(" %02x ", gd->mac_desc.qlm1_port[mac_byte]);
		}

		printf("\n");
				  
		printf("\nget_mac_addr: qlm3 addr = ");
		for (mac_byte = 0; mac_byte < 6; mac_byte++)
		{
			printf(" %02x ", gd->mac_desc.qlm3_port[mac_byte]);
		}

		printf("\n");

		printf("\nget_mac_addr: amc port0 addr = ");
		for (mac_byte = 0; mac_byte < 6; mac_byte++)
		{
			printf(" %02x ", gd->mac_desc.amc_port0[mac_byte]);
		}

		printf("\n");

		printf("\nget_mac_addr: amc port1 addr = ");
		for (mac_byte = 0; mac_byte < 6; mac_byte++)
		{
			printf(" %02x ", gd->mac_desc.amc_port1[mac_byte]);
		}
				  
#endif				  
		
	}

			/* return the requested mac addr */

		switch (eth_port) {
			case MAC_ETH0:  /* 1st Eth port attached to QLM1 */
				  for (mac_byte = 0; mac_byte < 6; mac_byte++)
				  {
					  mac_addr[mac_byte] = gd->mac_desc.qlm1_port[mac_byte];
				  }
				break;
			case MAC_ETH1:  /* 2nd Eth port attached to QLM3 */
				  for (mac_byte = 0; mac_byte < 6; mac_byte++)
				  {
					  mac_addr[mac_byte] = gd->mac_desc.qlm3_port[mac_byte];
				  }
				break;
			case MAC_ETH2:  /* Port 0 on 82571 on QLM2 */
				  for (mac_byte = 0; mac_byte < 6; mac_byte++)
				  {
					  mac_addr[mac_byte] = gd->mac_desc.amc_port0[mac_byte];
				  }
				break;
			case MAC_ETH3:  /* Port 1 on 82571 on QLM2 */
				  for (mac_byte = 0; mac_byte < 6; mac_byte++)
				  {
					  mac_addr[mac_byte] = gd->mac_desc.amc_port1[mac_byte];
				  }
				break;
		}

				
		
}

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

	uint64_t data;

		/* setup BOTH mdio clocks */
	
    cvmx_write_csr(CVMX_SMIX_EN(0), 1);
    data = cvmx_read_csr(CVMX_SMIX_CLK(0));

		/* for 10G PHYs */
	data |= 0x1000000; //set bit 24(MODE) = 1 (Clause 45 compliant for 10G phy)
 
	data |= 0x2000; //set bit 13 CLK_IDLE to 1. Makes clock not cycle when bus is idle
	cvmx_write_csr(CVMX_SMIX_CLK(0), data);


    cvmx_write_csr(CVMX_SMIX_EN(1), 1);
    data = cvmx_read_csr(CVMX_SMIX_CLK(1));

		/* for 10G PHYs */
	data |= 0x1000000; //set bit 24(MODE) = 1 (Clause 45 compliant for 10G phy)
 
	data |= 0x2000; //set bit 13 CLK_IDLE to 1. Makes clock not cycle when bus is idle
	cvmx_write_csr(CVMX_SMIX_CLK(1), data);
	
	
#endif

    return 0;
}

void cvm_oct_led_init(void)
{
    cvmx_led_en_t en;
	cvmx_led_udd_cntx_t udd_cnt;
	cvmx_led_prt_t prt_en;
	cvmx_led_prt_fmt_t frmt;

	/* enable led interface and set the number of bits in the shift register */
	en.u64 = 1;
	udd_cnt.u64 = 0;

	/* enable ports that are allowed to be displayed */
	prt_en.u64 = 0xFF; // 0:3 = ports 0-3 (interface 0) 4:7 = ports 4-7 (interface 1)

	/* set the port format and set the status bit */
	frmt.u64 = 0x0; // { LED_PRT_STATUSn[0] }

	/* write it to the csr */
	cvmx_write_csr(CVMX_LED_EN, en.u64);
	cvmx_write_csr(CVMX_LED_DBG, 0);
	cvmx_write_csr(CVMX_LED_UDD_CNTX(0), udd_cnt.u64);
	cvmx_write_csr(CVMX_LED_UDD_CNTX(1), udd_cnt.u64);
	cvmx_write_csr(CVMX_LED_PRT, prt_en.u64);
	cvmx_write_csr(CVMX_LED_PRT_FMT, frmt.u64);

	// set LED_PRT_STATUSn[0]
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(0), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(1), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(2), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(3), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(4), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(5), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(6), 0);
	cvmx_write_csr(CVMX_LED_PRT_STATUSX(7), 0);

	// DEBUGPRINT( "led: %s led frmt: 0x%lx led port enable: 0x%lx bit count: 0x%lx\n", 
	//             (en.u64) ? "enabled" : "disabled", 
	//             frmt.u64, prt_en.u64, udd_cnt.u64 );

}

/* this function really needs to obtain the board
   type from the H8 processor, probably via FPGA
   register.  For now, it is hard-coded.
*/

uint16_t get_ipmi_board_type(void)
{
	return CVMX_BOARD_TYPE_TNPA5651X;
}

/* determine whether the particular interface is
   going to the AMC ports or the front panel.
   if the interface is through the AMC ports,
   it is always 10G.  If through the front
   panel, then it can be either 10G or 1G,
   depending on how the fpga bits are set.

   possible return values:

   0  - interface routed through AMC ports
   1  - 1G out front panel
   10 - 10G out front panel
*/

uint16_t get_phy_mode(int interface)
{
	uint8_t *fpga_ptr;
	uint8_t fpga_data;

	uint16_t mode;
	
		/* QLM1 can go to either the AMC ports or the SFP for interface 0.
		   QLM3 can go to either the AMC ports or the SFP for interface 1.

		   Check FPGA register 0x01 to determine if either interface 0 or 1
		   is being utilized, and initialize only those interface(s) that
		   are being utilized.

		   Bits 6 and 7 are inverted from what the manual says (i.e.
		   0 = force QLMx to 1G).
		*/

	
    fpga_ptr = BOARD_FPGA_OCTEON_MODE_UBOOT;
	fpga_data = *fpga_ptr;
	wmb();

#ifdef PHY_DEBUG			
	printf("\nget_phy_mode: fpga reg %x for interface %d = %02x\n",
		   BOARD_FPGA_OCTEON_MODE_UBOOT, interface, fpga_data);
#endif
	
	if (interface == 0) {
		if (fpga_data & 0x10) {
				/* routed to front panel */
			if (fpga_data & 0x40) {
				mode = 10;
			} else {
				/* 1 G mode for QLM 1 (interface 0) */
				mode = 1;
			}
		} else {
			mode = 0;
		}
	} else {
		if (fpga_data & 0x20) {
				/* routed to front panel */
			if (fpga_data & 0x80) {
				mode = 10;
			} else {
				/* 1 G mode for QLM 3 (interface 1) */
				mode = 1;
			}
		} else {
			mode = 0;
		}

	}

	return mode;
}

/*****************************************************************************
 **                        Reset I2c Bus
 **
 **   This function resets the i2c bus back to an idel state even if the bus
 ** is currently hung.
 **
 ** 
 **
 *****************************************************************************/
void reset_octeon_i2c_bus(void)
{
  int bus_no;                  /* i2c bus to be reset */
  uint64_t data;               /* 64-bit working register */
  int micro_sec_delay;         /* microsesond delay after every bit toggle */
  int i;

  bus_no = 0;    /* always reset bus zero for right now */

  neg_access_to_i2c_bus(); /* negotiate access to com i2c bus */

  /* Give H8 time to finish any current transactions */
  udelay(3000);/* sleep for 3000 microseconds before we try again */

  /* reset the i2c controller */
  data = 0x0000000700000000ull;
  cvmx_write_csr(CVMX_MIO_TWS_SW_TWSI, data);

  /* give controller time to reset */
  udelay(1000);/* sleep for 1000 microseconds before we try again */

  /* clear reset condition just in case */
  data = 0x0000000000000000ull;
  cvmx_write_csr(CVMX_MIO_TWS_SW_TWSI, data);

  udelay(1000);/* sleep for 1000 microseconds before we try again */

  micro_sec_delay = 1000;

  /**   
   ** Force the i2c Clock line high for 9 cycles in an attempt to clear
   ** any hang condition on the bus.  This will not effect the bus if
   ** no hang condition is currently present.
   **/

  /* start with both clock and data bits low */
  data = 0x0000000000000000ull; /* clear "SLC_OVR" & "SDA_OVR" bits */
  cvmx_write_csr(CVMX_MIO_TWS_INT, data);
  udelay(micro_sec_delay); /* must hold bus static for a short time */

  for (i = 0; i < 9; i++)
    {
    data = 0x0000000000000200ull;          /* set "SLC_OVR" bit */
    cvmx_write_csr(CVMX_MIO_TWS_INT, data);

    udelay(micro_sec_delay); /* must hold bus static for a short time */

    data = 0x0000000000000000ull; /* clear "SLC_OVR" & "SDA_OVR" bits */
    cvmx_write_csr(CVMX_MIO_TWS_INT, data);

    udelay(micro_sec_delay); /* must hold bus static for a short time */
    }

  rel_control_of_i2c_bus(); /* release control of com i2c bus */
}


/* undef freq if already defined */
#ifdef TWSI_BUS_FREQ
#undef TWSI_BUS_FREQ
#endif
#define TWSI_BUS_FREQ 	(100000) /* 100KHz */
#ifndef TWSI_THP
#define TWSI_THP	    (24) /* TCLK half period (default 24) */
#endif
int octeon_boot_bus_init_twsi(void)
{
    uint64_t ECLK_MHZ;
    cvmx_mio_tws_sw_twsi_t sw_twsi;
    uint64_t data;
    int M_divider, N_divider;

    /* Read CPU clock multiplier PLL_MUL (4:0) which is 
       readable via NPEI_DBG_DATA (C_MUL bits 18-22)
       30.8 Clock signals Octeon spec page 1163 */
//    uint64_t data = cvmx_read_csr((uint64_t)0x80011F0000008510ull);
    data = cvmx_read_csr(CVMX_PEXP_NPEI_DBG_DATA);
    data = data >> 18;  /* shift for C_MUL bits */
    data &= 0x1f;       /* mask for C_MUL bits */
                        /* C_MUL = 0x0A-0x12 i.e.
                           10 times 50 to 18 times 50 */
    data *= 50;         /* cpu_ref PLL_REF_CLK = 
                           50 MHz * PLL_MUL 
                           (i.e. C_MUL) */
    ECLK_MHZ = data;    /* ECLK_MHz 500MHz-900MHz 
                           data = 500-900 */
#ifdef  NOCOMP  /* why? don't do this... */
    data += 200;        /* ECLK_MHz 500MHz-900MHz 
                           data = 500-900 */
    ECLK_MHZ = data;    /* + 200 to Give us some margin - why?
                           data = 700-1100 */
#endif  /* NOCOMP */

    /*
    ** Set the TWSI clock to a conservative 100KHz.  Compute the
    ** clocks M divider based on the core clock.
    **
    **  FOSCL (of 100KHz) = TCLK / ((2**N) * (M+1) * 10)
    **  TCLK = core freq (of 500-900MHz) / (2 * (THP+1))
    **  FOSCL (of 100KHz) = (core freq (of 500-900MHz) / (2 * (THP+1))) / ((2**N) * (M+1) * 10)
    **  TWSI_BUS_FREQ = ((ECLK_MHZ * 1000000) / (2 * (THP+1))) / ((2**N) * (M+1) * 10)
    **  TWSI_BUS_FREQ = ((ECLK_MHZ * 1000000) / (20 * (THP+1) * 2**N * (M+1))
    **	M+1 = ((ECLK_MHZ * 1000000) / (TWSI_BUS_FREQ * 20 * (THP+1) * 2**N))
    **	M = ((ECLK_MHZ * 1000000) / (TWSI_BUS_FREQ * 20 * (THP+1) * 2**N)) - 1
    **	M = ((ECLK_MHZ * 1000000) / (TWSI_BUS_FREQ * 20 * (THP+1) * 2**N)) - 1
    **                   10 = 1000000 / 100000
    **                   1/2 = 10/20
    **	M = (ECLK_MHZ * 10 / (20 * (THP+1) * 2**N)) - 1
    **	M = (ECLK_MHZ / (2 * (THP+1) * 2**N)) - 1
    **	M = (ECLK_MHZ / (2 * (THP+1) * (1 << N)) - 1
    **	For 600MHz (TCLK of 12MHz) N=0, M=0xB = ((600 / (2 * (25) * (1)) - 1)
    **	For 700MHz (TCLK of 14MHz) N=0, M=0xD = ((700 / (2 * (25) * (1)) - 1)
    **	For 750MHz (TCLK of 15MHz) N=0, M=0xE = ((750 / (2 * (25) * (1)) - 1)
    **	For 800MHz (TCLK of 16MHz) N=0, M=0xF = ((800 / (2 * (25) * (1)) - 1)
    */

    /* See Software to TWSI Reg MIO_TWS0/1_SW_TWSI 
       Table 18-14 for how to select TWSI_CLK and
       TWSI_CLKCTL registers
    */

    for (N_divider = 0; N_divider < 8; ++N_divider) {   /* N_divider is 3 bits max */
        M_divider = (ECLK_MHZ / (2 * (TWSI_THP+1) * (1<<N_divider))) - 1;
        /* M_divider is 4 bits max - check not over */
        if (M_divider < 16) {
            /* these M_divider & N_divider values are valid ranges */
            break;  
        }
    }

    /* Set THP in TWSI_CLK - 18.4.6 TWSI Master Clock Reg */
    sw_twsi.u64 = 0;            /* zero fields not set (i.e. SLONLY) */
    sw_twsi.s.v = 1;            /* set valid bit */
    sw_twsi.s.op = 0x4;         /* op=0x04 for TWSI_CLK register */
    sw_twsi.s.d = TWSI_THP;     /* TWSI_CLK value 18.4.7 */
    cvmx_write_csr(CVMX_MIO_TWS_SW_TWSI, sw_twsi.u64);

    /* Set M & N dividers TWSI_CLKCTL - 18.4.7 TWSI Master Clock Reg */
    sw_twsi.u64 = 0;            /* zero fields not set (i.e. SLONLY, R) */
    sw_twsi.s.v = 1;            /* set valid bit */
    sw_twsi.s.op = 0x6;         /* op=0x06 for see eop_ia field */
    sw_twsi.s.eop_ia = 0x3;     /* eop_ia=0x03 (R=0) for TWSI clock control TWSI_CLKCTL */
    sw_twsi.s.d = ((M_divider << 3) | N_divider);  /* TWSI_CLK (THP) value 18.4.6 */
    cvmx_write_csr(CVMX_MIO_TWS_SW_TWSI, sw_twsi.u64);
    return(0);
}

/** 
 * Init all of the stuff that needs to be configured early in 
 * the boot process (pre RAM relocation) 
 * 
 * 
 * @return int
 */
#include "cvmx-gpio.h"
int early_board_init(void)
{
    int cpu_ref = 33;

    DECLARE_GLOBAL_DATA_PTR;

#ifdef CONFIG_OCTEON_5651X
    BOOT_PROGRESS_UPDATE(BPM_EARLY_BOARD_INIT);
#endif


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

	/* clear shift register LEDs on the Octeon */
    cvm_oct_led_init();

    octeon_boot_bus_init_twsi(); /* ED's call goes here */

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

			/* we need to distinguish between 56x4 and 5651x */
			/* gd->board_desc.board_type = CVMX_BOARD_TYPE_TNPA56X4; */
			/* gd->board_desc.board_type = CVMX_BOARD_TYPE_TNPA5651X; */
		gd->board_desc.board_type = get_ipmi_board_type();

#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;

		/* ZBY: Set the Tuple if it was 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_TNPA5651X; */
		bd_ptr->board_type     = get_ipmi_board_type();
        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 33 */
        gd->ddr_clock_mhz = 200;
    }
#endif

    cpu_ref = 50;
    /* gd->ddr_clock_mhz = 333; */
    switch( (*BOARD_SDRAM_CONFIG_REG) & SDRAM_SPEED_MASK )
    {
        case SDRAM_SPEED_400:
            gd->ddr_clock_mhz = 200;
            break;
        case SDRAM_SPEED_533:
            /* printf("%s(): Clock speed from FPGA is 533\n",__FUNCTION__); */
            gd->ddr_clock_mhz = 266;
            break;
        case SDRAM_SPEED_667_5:
        case SDRAM_SPEED_667_4:
            /* printf("%s(): Clock speed from FPGA is 667\n",__FUNCTION__); */
            gd->ddr_clock_mhz = 333;
            break;
        case SDRAM_SPEED_800_6:
        case SDRAM_SPEED_800_5:
            gd->ddr_clock_mhz = 400;
            break;
        default:
            gd->ddr_clock_mhz = 266;
            break;
    }

    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.amc_port0[0] = 0x00;
        gd->mac_desc.amc_port0[1] = 0xDE;
        gd->mac_desc.amc_port0[2] = 0xAD;
        gd->mac_desc.amc_port0[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.amc_port0[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.amc_port0[5] = 0x00;
        gd->mac_desc.amc_port1[0] = 0x00;
        gd->mac_desc.amc_port1[1] = 0xDE;
        gd->mac_desc.amc_port1[2] = 0xAD;
        gd->mac_desc.amc_port1[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.amc_port1[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.amc_port1[5] = 0x01;
        gd->mac_desc.qlm1_port[0] = 0x00;
        gd->mac_desc.qlm1_port[1] = 0xDE;
        gd->mac_desc.qlm1_port[2] = 0xAD;
        gd->mac_desc.qlm1_port[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.qlm1_port[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.qlm1_port[5] = 0x02;
        gd->mac_desc.qlm3_port[0] = 0x00;
        gd->mac_desc.qlm3_port[1] = 0xDE;
        gd->mac_desc.qlm3_port[2] = 0xAD;
        gd->mac_desc.qlm3_port[3] = (gd->board_desc.rev_major<<4) | gd->board_desc.rev_minor;
        gd->mac_desc.qlm3_port[4] = gd->board_desc.serial_str[0];
        gd->mac_desc.qlm3_port[5] = 0x03;
    }

    /* Read CPU clock multiplier */
    uint64_t data = cvmx_read_csr((uint64_t)0x80011F0000008510ull);
    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;

    cvmx_write_csr (CVMX_GPIO_BIT_CFGX(12), 6ULL); /* GPIO12 is an int input */
    cvmx_write_csr (CVMX_GPIO_BIT_CFGX(13), 6ULL); /* GPIO13 is an int input */

    return 0;

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


/** 
 * all of the post relocation setup stuff
 */
void late_board_init(void)
{

#ifdef PHY_DEBUG			
	printf("FPGA Version: 0x%02x\n", *(unsigned char *)BOARD_BASE_FPGA_ADDR);
#endif
	
	/* bring up the SFPs */
	sfpLinkSetup();
											
#ifdef CONFIG_OCTEON_56x4
    BOOT_PROGRESS_UPDATE(BPM_VERSION_NUM_STORE);
#endif

    uboot_version_num_store();

		/* print link status */

	sfpLinkStatus();
}

int octeon_post_error_store(uint8_t error_code)
{
    int loc;

    neg_access_to_i2c_bus(); /* negotiate access to com i2c bus */

    /* 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);

            rel_control_of_i2c_bus(); /* release control of com i2c bus */

            return 0;
        }
    }

    rel_control_of_i2c_bus(); /* release control of com i2c bus */

    return -1;
}

int octeon_post_error_retrieve(uint8_t *post_error_code_buf_ptr)
{
    int loc;

    neg_access_to_i2c_bus(); /* negotiate access to com i2c bus */

    /* 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);
    }

    rel_control_of_i2c_bus(); /* release control of com i2c bus */

    return 0;
}

int octeon_post_error_clear(void)
{
    int loc;

    neg_access_to_i2c_bus(); /* negotiate access to com i2c bus */

    /* 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);
    }

    rel_control_of_i2c_bus(); /* release control of com i2c bus */

    return 0;
}


/**********************************************************
 ******  The following code is for boards with SFP's. ******
 *********************************************************/
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, 
        		SFP_EEPROM_BASE | 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, 
                		SFP_EEPROM_BASE | ID));
                break;
        }
        printf ("    Ext ID: 0x%02x\n", octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, 
        		SFP_EEPROM_BASE | EXT_ID));
        switch (octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, SFP_EEPROM_BASE | 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, 
                		SFP_EEPROM_BASE | CONNECTOR));
                break;
        }
        printf ("    Link Length: 9/125 mm Fiber (units of kilometers) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface,
                		SFP_EEPROM_BASE | 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, 
                		SFP_EEPROM_BASE | 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, 
                		SFP_EEPROM_BASE | 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, 
                		SFP_EEPROM_BASE | FIBER_62P5_125MM_UNITS_10M));
        printf ("    Link Length: Copper (units of meters) - 0x%02x\n",
                octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, 
                		SFP_EEPROM_BASE | COPPER_UNITS_M));
    }
    return (0);
}

void sfpVSRegWrite(int dev, int reg, int mask, int newVal, int reset_wait)
{
    int data;

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

void sfpVSRegRead(int dev, int reg, int *val)
{
    int data;

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

#if 0
void sfpInit(int interface)
{
	uint64_t pcs_misc_control_reg;
    uint64_t pcs_mrx_control_reg;

	connector_type[interface] = octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + interface, 
													SFP_EEPROM_BASE | CONNECTOR);

    if (connector_type[interface] == VACANT)
    {
        printf ("V ");
    }
    else if (connector_type[interface] == UNSPECIFIED)
    {
        printf ("C ");
  
        /* command SFPs/M88E1111 into SGMII mode */
        sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + interface, EXTENDED_PHY_SPECIFIC_STATUS_REG, 0x0000, 0x9084, 0);
        /* command SFPs/M88E1111 to advertise 1000BASE-T Full/Half-Duplex */
        sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + interface, CONTROL_REG_1000BASE_T, 0x0000, 0x0F00, 0);
        /* command SFPs/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
        sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + interface, AUTONEG_ADVERTISEMENT, 0x0000, 0x0DE1, 0);
        /* command SFPs into reset */
        sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + interface, CONTROL_REG, 0x0000, 0x9140, 1);
        /* command SFPs/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
        sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + interface, AUTONEG_ADVERTISEMENT, 0x0000, 0x0DE1, 0);
    }
    else
    {  
        printf ("F ");
        /* nothing to do here */
		pcs_misc_control_reg = 0;
        pcs_mrx_control_reg = 0;

		switch (interface)
		{

		case 0:

            pcs_misc_control_reg = 0x11800B8001078ull;
            pcs_mrx_control_reg = 0x11800B8001000ull;
            break;
		case 1:

			pcs_misc_control_reg = 0x11800B8001478ull;
            pcs_mrx_control_reg = 0x11800B8001400ull;
			break;
		case 2:
			pcs_misc_control_reg = 0x11800B8001878ull;
            pcs_mrx_control_reg = 0x11800B8001800ull;
            break;
		case 3:
			pcs_misc_control_reg = 0x11800B8001C78ull;
            pcs_mrx_control_reg = 0x11800B8001C00ull;
            break;
		}

		/* check to make sure we assigned an address above */
		if (pcs_misc_control_reg != 0)
		{
			/* setup PCS for 1000-Basex*/
            cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), 0x1101);
            /*setup PCS for auto neg*/
            cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), 0x1940);
            /*reset auto neg state machine */
            cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), 0x1B40);
		}
    }    
	return;
}

#endif /* if 0 */

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

		/* on the NPA-5651x, each PHY is on a different MDIO bus (the
		   octeon has two), so the BOARD_10G_PHY0_MDIO_ADDR remains the
		   same (0) on each.
		*/

#ifdef PHY_DEBUG		
	printf("Setting up phy at addr 0x%02x on mdio bus 0\n", BOARD_10G_PHY0_MDIO_ADDR);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_0);

#ifdef PHY_DEBUG			
	printf("Completed setting up phy at addr 0x%02x on mdio bus 0\n", BOARD_10G_PHY0_MDIO_ADDR);

	printf("Setting up phy at addr 0x%02x on mdio bus 1\n", BOARD_10G_PHY0_MDIO_ADDR);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_1);

#ifdef PHY_DEBUG			
	printf("Completed setting up phy at addr 0x%02x on mdio bus 1\n", BOARD_10G_PHY0_MDIO_ADDR);
#endif
	
	return (0);
}

/* read and print link status */

void print_link_status(uint64_t status, int interface, int port)
{
#ifdef PHY_DEBUG_STATUS	
	printf("\nLink status (0x%04x) for interface %d port %d\n\n",
		   status, interface, port);

	if (status & LINK_STATUS)
		printf("Link up\n");
	else
		printf("Link down\n");

	if (status & AUTO_NEG_ABLE)
		printf("Able to auto-negotiate\n");
	else
		printf("Not able to auto-negotiate\n");

	if (status & REMOTE_FAULT)
		printf("Remote fault detected\n");
	else
		printf("No remote fault detected\n");

	if (status & AUTO_NEG_COMPLETE)
		printf("Auto negotiation complete\n");
	else
		printf("Auto negotiation not complete\n");

	if (status & PRB_SUP)
		printf("Able to work without preamble bytes\n");
	else
		printf("Unable to work without preamble bytes\n");

	if (status & EXT_ST)
		printf("Extended status info available\n");
	else
		printf("No extended status info available\n");

	if (status & HUN_T2HD)
		printf("100 Base-T2 half duplex\n");

	if (status & HUN_T2FD)
		printf("100 Base-T2 full duplex\n");

	if (status & TEN_HD)
		printf("10 Mb half duplex\n");

	if (status & TEN_FD)
		printf("10 Mb full duplex\n");
	
	if (status & HUN_XHD)
		printf("100 Base-X half duplex\n");

	if (status & HUN_XHD)
		printf("100 Base-X full duplex\n");
	
	if (status & HUN_T4)
		printf("100 Base-T4 capable\n");
#endif	
}

void sfpLinkStatus(void)
{
	uint64_t pcs_mrx_status_reg;
	uint64_t status;

		/* interface 0 */
	
	pcs_mrx_status_reg = PCS0_MR0_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 0, 0);

	pcs_mrx_status_reg = PCS0_MR1_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 0, 1);


	pcs_mrx_status_reg = PCS0_MR2_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 0, 2);


	pcs_mrx_status_reg = PCS0_MR3_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 0, 3);


		/* interface 1 */

	pcs_mrx_status_reg = PCS1_MR0_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 1, 0);

	pcs_mrx_status_reg = PCS1_MR1_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 1, 1);


	pcs_mrx_status_reg = PCS1_MR2_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 1, 2);


	pcs_mrx_status_reg = PCS1_MR3_STATUS_REG;

	status = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));

	print_link_status(status, 1, 3);

	
}

/** 
 * Function to configure all 4 SFPs
 */
void sfpLinkSetup(void)
{
	uint64_t pcs_misc_control_reg;
    uint64_t pcs_mrx_control_reg;

#ifndef SGMII_MODE	
	uint64_t pcs_reg;
#endif
	
	uint64_t gmx_reg;

#if 0	
	uint64_t gmx_tx_prts;
	uint64_t gmx_rx_prts;
#endif /* if 0 */
	
	uint64_t data;
	int timeout;
	
#if 0
#ifdef SGMII_MODE	
	printf("\nsfpLinkSetup: Setup for SGMII operation\n");
#else
	printf("\nsfpLinkSetup: Setup for 1000 Base-X operation\n");
#endif
#endif /* if 0 */

		/* setup QLM 1 (interface 0) */

	pcs_misc_control_reg = PCS0_MISC0_CTL_REG;
	pcs_mrx_control_reg = PCS0_MR0_CONTROL_REG;

		/* enable interface */
	gmx_reg = GMX0_INF_MODE;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_ENABLE);

		/* disable GMX */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL_NOGMX);

#ifdef SGMII_MODE		
	/* setup PCS for SGMII */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg),
				   PCS_MISC_CTRL_VAL1SGMII_NOGMX);
#else
	/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg),
				   PCS_MISC_CTRL_VAL1_NOGMX);

	pcs_reg = PCS0_LINK0_TIMER_COUNT_REG;

	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_reg), LINK_TIMER_COUNT);

#endif /* if 0 */
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_NOAUTONEG_VAL1);

	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_NOAUTONEG_RESET);
	
	timeout = 1000;
	
	do {
		data = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg));
		cvmx_wait(1000);
		if (--timeout)
			break;
	} while (data & 0x1000);

	if (timeout == 0) {
		printf("\nControl Reg RESET never changed to 0\n");
	}

	gmx_reg = GMX0_PRT0_CFG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_EN_CLR);

	timeout = 1000;

		/* wait for RX/TX idle to be set */
	do {
		data = cvmx_read_csr(CVMX_ADD_IO_SEG(gmx_reg));
		cvmx_wait(1000);
		if (--timeout)
			break;
	} while (data & 0x3000);

	if (timeout == 0) {
		printf("\nGMX Control Reg RX/TX idle never got set\n");
	}

		/* do we really need to do this? */
#if 0 
		/* set to one lane operation */
	gmx_tx_prts = GMX0_TX_PRTS;
	gmx_rx_prts = GMX0_RX_PRTS;
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_tx_prts), GMX_PRTS);
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_rx_prts), GMX_PRTS);
#endif  /* if 0 */
	
		/* any GMX programming as recommended on pg 640 in the Octeon docs
		   should be done here - assume for now it has been done... */

	gmx_reg = GMX0_PRT0_CFG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_EN_SET);

#ifdef SGMII_MODE	
			/* enable GMX */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VALSGMII);
#else
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS0_MISC1_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS0_MISC2_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS0_MISC3_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
#endif	

		/* setup QLM 3 (interface 1) */
	
	pcs_misc_control_reg = PCS1_MISC0_CTL_REG;
	pcs_mrx_control_reg = PCS1_MR0_CONTROL_REG;

		/* enable interface */
	gmx_reg = GMX1_INF_MODE;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_ENABLE);

		/* disable GMX */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL_NOGMX);

#ifdef SGMII_MODE		
	/* setup PCS for SGMII */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg),
				   PCS_MISC_CTRL_VAL1SGMII_NOGMX);
#else
	/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg),
				   PCS_MISC_CTRL_VAL1_NOGMX);

	pcs_reg = PCS1_LINK0_TIMER_COUNT_REG;
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_reg), LINK_TIMER_COUNT);
	
#endif /* if 0 */

	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_NOAUTONEG_VAL1);

	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_NOAUTONEG_RESET);
	
	timeout = 1000;
	
	do {
		data = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg));
		cvmx_wait(1000);
		if (--timeout)
			break;
	} while (data & 0x1000);

	if (timeout == 0) {
		printf("\nControl Reg RESET never changed to 0\n");
	}

	gmx_reg = GMX1_PRT0_CFG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_EN_CLR);

	timeout = 1000;

		/* wait for RX/TX idle to be set */
	do {
		data = cvmx_read_csr(CVMX_ADD_IO_SEG(gmx_reg));
		cvmx_wait(1000);
		if (--timeout)
			break;
	} while (data & 0x3000);

	if (timeout == 0) {
		printf("\nGMX Control Reg RX/TX idle never got set\n");
	}

		/* do we really need to do this? */
#if 0 
		/* set to one lane operation */
	gmx_tx_prts = GMX1_TX_PRTS;
	gmx_rx_prts = GMX1_RX_PRTS;
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_tx_prts), GMX_PRTS);
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_rx_prts), GMX_PRTS);
#endif /* if 0 */
	
		/* any GMX programming as recommended on pg 640 in the Octeon docs
		   should be done here - assume for now it has been done... */

	gmx_reg = GMX1_PRT0_CFG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_reg), GMX_EN_SET);
	
			/* enable GMX */
#ifdef SGMII_MODE			
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VALSGMII);
#else
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS1_MISC1_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS1_MISC2_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);

	pcs_misc_control_reg = PCS1_MISC3_CTL_REG;
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
	
#endif	
		/* on the NPA-5651x, each PHY is on a different MDIO bus (the
		   octeon has two), so the BOARD_10G_PHY0_MDIO_ADDR remains the
		   same (0) on each.
		*/

#ifdef PHY_DEBUG			
	printf("Setting up phy at addr 0x%02x on mdio bus %d\n", BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_0);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_0);

#ifdef PHY_DEBUG			
	printf("Setting up phy at addr 0x%02x on mdio bus %d\n", BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_1);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_1);
	
}

/** 
 * Function to configure all 4 SFPs
 */
void sfpLinkSetup1(void)
{
	uint64_t pcs_misc_control_reg;
    uint64_t pcs_mrx_control_reg;

	uint64_t gmx_tx_prts;
	uint64_t gmx_rx_prts;
	
#if 0	
	printf ("SFP Type: ");
    sfpInit (0);
    sfpInit (1);
    sfpInit (2);
    sfpInit (3);
    printf ("\n");
#endif /* if 0 */

		/* setup QLM 1 (interface 0) */

	pcs_misc_control_reg = PCS0_MISC0_CTL_REG;
	pcs_mrx_control_reg = PCS0_MR0_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS0_MISC1_CTL_REG;
	pcs_mrx_control_reg = PCS0_MR1_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS0_MISC2_CTL_REG;
	pcs_mrx_control_reg = PCS0_MR2_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS0_MISC3_CTL_REG;
	pcs_mrx_control_reg = PCS0_MR3_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);
	
	
		/* set to one lane operation */
	gmx_tx_prts = GMX0_TX_PRTS;
	gmx_rx_prts = GMX0_RX_PRTS;
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_tx_prts), GMX_PRTS);
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_rx_prts), GMX_PRTS);

		/* on the NPA-5651x, each PHY is on a different MDIO bus (the
		   octeon has two), so the BOARD_10G_PHY0_MDIO_ADDR remains the
		   same (0) on each.
		*/

#ifdef PHY_DEBUG			
	printf("Setting up phy at addr 0x%02x on mdio bus %d\n", BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_0);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_0);

	
		/* setup QLM 3 (interface 1) */

	pcs_misc_control_reg = PCS1_MISC0_CTL_REG;
	pcs_mrx_control_reg = PCS1_MR0_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS1_MISC1_CTL_REG;
	pcs_mrx_control_reg = PCS1_MR1_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS1_MISC2_CTL_REG;
	pcs_mrx_control_reg = PCS1_MR2_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);

	pcs_misc_control_reg = PCS1_MISC3_CTL_REG;
	pcs_mrx_control_reg = PCS1_MR3_CONTROL_REG;

		/* setup PCS for 1000-Basex */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_misc_control_reg), PCS_MISC_CTRL_VAL);
		/* setup PCS for auto neg */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL1);
		/* reset auto neg state machine */
	cvmx_write_csr(CVMX_ADD_IO_SEG(pcs_mrx_control_reg), PCS_MRx_VAL2);
	

		/* set to one lane operation */
	gmx_tx_prts = GMX1_TX_PRTS;
	gmx_rx_prts = GMX1_RX_PRTS;
	
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_tx_prts), GMX_PRTS);
	cvmx_write_csr(CVMX_ADD_IO_SEG(gmx_rx_prts), GMX_PRTS);

		/* on the NPA-5651x, each PHY is on a different MDIO bus (the
		   octeon has two), so the BOARD_10G_PHY0_MDIO_ADDR remains the
		   same (0) on each.
		*/

#ifdef PHY_DEBUG			
	printf("Setting up phy at addr 0x%02x on mdio bus %d\n", BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_1);
#endif
	
	amcc10gPhyLinkSetup(BOARD_10G_PHY0_MDIO_ADDR, MDIO_BUS_1);

}

/**
 * Perform an MII read. This function is used to read PHY
 * registers controlling auto negotiation.
 *
 * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 *                 support multiple busses.
 * @param phy_id   The MII phy id
 * @param location Register location to read
 *
 * @return Result from the read or -1 on failure
 */
int cvmx_mdio_read_ieee_clause45(int bus_id, int phy_addr, uint8_t dev_type)
{
    cvmx_smix_cmd_t smi_cmd;
    cvmx_smix_rd_dat_t smi_rd;
    int timeout = 1000;
    unsigned char phy_op;

    phy_op = 0x03;//(0x00 = address; 0x01 = write, 0x11 = read)

    smi_cmd.u64 = 0;
    smi_cmd.s.phy_op = phy_op;
    smi_cmd.s.phy_adr = phy_addr;
    smi_cmd.s.reg_adr = dev_type;
    cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);

    do
    {
        cvmx_wait(1000);
        smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(bus_id));
    } while (smi_rd.s.pending && timeout--);

    if (!timeout)
    {
        printf("*** TIMEOUT waiting for CVMX_SMIX_RD_DAT\n");
    }
    if (smi_rd.s.val)
        return smi_rd.s.dat;
    else
        return -1;
}

/**
 * Perform an MII write. This function is used to write PHY
 * registers controlling auto negotiation.
 *
 * @param bus_id   MDIO bus number. Zero on most chips, but some chips (ex CN56XX)
 *                 support multiple busses.
 * @param phy_id   The MII phy id
 * @param location Register location to write
 * @param val      Value to write
 *
 * @return -1 on error
 *         0 on success
 */
int cvmx_mdio_address_write_ieee_clause45(unsigned char phy_op, int bus_id, int phy_addr, uint8_t dev_type, uint16_t data)
{
    cvmx_smix_cmd_t smi_cmd;
    cvmx_smix_wr_dat_t smi_wr;
    int timeout = 1000;

    smi_wr.u64 = 0;
    smi_wr.s.dat = data;/* for writes this will be the data. For address commands this will be the address */
    cvmx_write_csr(CVMX_SMIX_WR_DAT(bus_id), smi_wr.u64);

    smi_cmd.u64 = 0;
    smi_cmd.s.phy_op = (phy_op & 0x03);/*phy_op is only two bits so we mask it.(0x00 = address; 0x01 = write, 0x11 = read)*/
    smi_cmd.s.phy_adr = phy_addr;
    smi_cmd.s.reg_adr = dev_type;
    cvmx_write_csr(CVMX_SMIX_CMD(bus_id), smi_cmd.u64);

    do
    {
        cvmx_wait(1000);
        smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(bus_id));
    } while (smi_wr.s.pending && --timeout);
    if (timeout <= 0)
        return -1;

    return 0;
}




/* use for forcing 10base-t for regression testing */
int sfp_force_10baset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    printf("Commanding all SFPs to base 10T...");
	/* command SFPs/M88E1111 to advertise 10BASE-T Full/Half-Duplex */
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR , AUTONEG_ADVERTISEMENT, 0x0000, 0x0C61, 0);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 1, AUTONEG_ADVERTISEMENT, 0x0000, 0x0C61, 0);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 2, AUTONEG_ADVERTISEMENT, 0x0000, 0x0C61, 0);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 3, AUTONEG_ADVERTISEMENT, 0x0000, 0x0C61, 0);

	/* command SFPs into reset */
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR , CONTROL_REG, 0x0000, 0x9140, 1);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 1, CONTROL_REG, 0x0000, 0x9140, 1);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 2, CONTROL_REG, 0x0000, 0x9140, 1);
    sfpVSRegWrite (BOARD_SFP0_TWSI_ADDR + 3, CONTROL_REG, 0x0000, 0x9140, 1);

	printf("done\n");
	
	return 0;
}


/**
 * Perform an MII read. Called by the generic MII routines
 *
 * @param dev      Device to perform read for
 * @param phy_id   The MII phy id
 * @param location Register location to read
 * @return Result from the read or zero on failure
 */

int  lcl_miiphy_read(unsigned char addr, unsigned char reg, unsigned short * value, int bus_id)
{

    int retval = cvmx_mdio_read(bus_id, addr, reg);
    if (retval < 0)
        return -1;
    *value = (unsigned short)retval;
    return 0;
}
int lcl_miiphy_write(unsigned char  addr,
		 unsigned char  reg,
		 unsigned short value, int bus_id)
{

    if (cvmx_mdio_write(bus_id, addr, reg, value) < 0)
        return(-1);
    else
        return(0);
}

/**
 * Function to write data to a register on the MDIO interface.
 *
 * @param addr
 * @param reg
 * @param mask
 * @param newVal
 * @param wait
 * @param dev_type - dev_type is only used in clause 45 10G phy
 */
void phyRegWrite(uint8_t mdio_addr, uint16_t reg, uint16_t mask, uint16_t newVal, int wait, uint8_t dev_type, int bus_id)
{
    uint16_t data;
    int rc;

#ifdef CLAUSE45
    /* clause 45 MDIO access */
	phyRegRead(mdio_addr, reg, &data, dev_type, bus_id);
	data = (data & mask) | newVal;
	
	rc = cvmx_mdio_address_write_ieee_clause45(0x00, bus_id, mdio_addr, dev_type, reg);
	rc = cvmx_mdio_address_write_ieee_clause45(0x01, bus_id, mdio_addr, dev_type, data);

		/* On the NPA-5651x, the PHY is always a 10G PHY, so there is no need to
		   check.
		*/
#else
		/* cluase22 */

        /**
         *  Read the current value out of the register on the MDIO
         *  interface.
         *
         * int miiphy_read(unsigned char  addr, unsigned char  reg,
         * unsigned short *value)
         **/
        lcl_miiphy_read (mdio_addr, reg, &data, bus_id);
        data = (data & mask) | newVal;
        /**
         * Write the new data out to the register on the MDIO interface.
         *
         * int miiphy_write(unsigned char  addr, unsigned char  reg,
         * unsigned short value)
         **/
        lcl_miiphy_write (mdio_addr, reg, data, bus_id);
        if (wait)
        {
            do
            {
                lcl_miiphy_read (mdio_addr, reg, &data, bus_id);
            }
            while ((data & 0x8000) != 0) ; /* wait till finished */
        }

	
#endif /* CLAUSE45 */
	
}

/**
 * Function to read data from a register on the MDIO interface.
 *
 * @param addr
 * @param reg
 * @param val
 * @param dev_type - dev_type is only used in clause 45 10G phy
 */
void phyRegRead(uint8_t mdio_addr, uint16_t reg, uint16_t *val, uint8_t dev_type, int bus_id)
{
    int rc;

#ifdef CLAUSE45
    /* clause 45 MDIO access */
	rc = cvmx_mdio_address_write_ieee_clause45(0x00, bus_id, mdio_addr, dev_type, reg);
	*val = cvmx_mdio_read_ieee_clause45(bus_id, mdio_addr, dev_type);
	rc = *val;

		/* On the NPA-5651x, the PHY is always a 10G PHY, so there is no need to
		   check.
		*/
#else
	/* clause 22 MDIO access */
        /* read the current value out of the register on the MDIO interface */
        lcl_miiphy_read (mdio_addr, reg, val, bus_id);

#endif /* CLAUSE45 */
	
}

/* local function to convert an ascii string of hex digits
   to its binary value.
*/

int xtoi(const char* xs, int szlen, uint16_t * result)
{
	int i, xv, fact;
	char cstr[16];
	int is_valid;
	
	if (szlen > 0) {
		
			/* Converting more than 32bit hexadecimal value? */
		
		if (szlen > 8)
			return 2; /* exit */

			/* Begin conversion here */
		*result = 0;
		fact = 1;

			/*
		cstr[0] = (char) *xs;
		cstr[1] = (char) *(xs + 1);
			*/

		strcpy((char *)&cstr[0], xs);
		
			/* Run until no more character to convert */
		for (i = szlen - 1; i >= 0; i--) {

				/* c = *(xs + i); */

		   is_valid = 0;
		   
		   if ((cstr[i] >= '0') && (cstr[i] <= '9')) {
			   is_valid = 1;
		   }
	
		   if ((cstr[i] >= 'A') && (cstr[i] <= 'F')) {
			  is_valid = 1;
		   }
	
		   if ((cstr[i] >= 'a') && (cstr[i] <= 'f')) {	
			  is_valid = 1;
		   }
			
		   if (is_valid == 1) {
				if (cstr[i] >= 97) {
					xv = (cstr[i] - 97) + 10;
				}
				else
					if (cstr[i] >= 65) {
						xv = (cstr[i] - 65) + 10;
					}
					else {
						xv = cstr[i] - 48;
					}
				*result += (xv * fact);
				fact *= 16;
			}
			else
			{
			   /* Conversion was abnormally terminated
				  by non hexadecimal digit.
				  returning only the converted with
				  an error value 4 (illegal hex character)
			   */

				return 4;
			}
		}

		return 0;
	}
	
		/* Nothing to convert */
	
	return 1;
}

int phy_fw(uint8_t mdio_addr, int mdio_bus)
{

	int index;
	uint16_t byteval;
	unsigned char byte;

#ifdef PHY_DEBUG_FW			
	printf("\nphy_fw: send first group\n");
#endif
	
	for (index = 0; index < FW_GROUP1_BYTES; index++) {

		byteval = 0;
		byte = eeprom_fw_grp1[index];
		byteval = byte;
		
			phyRegWrite(mdio_addr, (0x8000 + index), 0x0000 /*mask*/, byteval, 0,
						AMCC_DEV_ADDR_PCS, mdio_bus);
	}

#ifdef PHY_DEBUG_FW			
	printf("\nphy_fw: first group send complete sent %d bytes, send second group\n", index);
#endif
	
	for (index = 0; index < FW_GROUP2_BYTES; index++) {

		byteval = 0;
		byte = eeprom_fw_grp2[index];
		byteval = byte;
		
			phyRegWrite(mdio_addr, (0x8000 + index), 0x0000 /*mask*/, byteval, 0, AMCC_DEV_ADDR_XGXS, mdio_bus);
	}

#ifdef PHY_DEBUG_FW			
	printf("\nphy_fw: second group send complete: sent %d bytes\n", index);
#endif	

	return 0;


}

void dbg_print_octeon_status(void)
{
    uint64_t data;

	printf("\n\ndbg_print_octeon_status:");
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR0_CONTROL_REG));
	printf("\nPCS0_MR0_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR1_CONTROL_REG));
	printf("\nPCS0_MR1_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR2_CONTROL_REG));
	printf("\nPCS0_MR2_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR3_CONTROL_REG));
	printf("\nPCS0_MR3_CONTROL_REG:0x%08x%08x", data);

	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR0_CONTROL_REG));
	printf("\nPCS1_MR0_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR1_CONTROL_REG));
	printf("\nPCS1_MR1_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR2_CONTROL_REG));
	printf("\nPCS1_MR2_CONTROL_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR3_CONTROL_REG));
	printf("\nPCS1_MR3_CONTROL_REG:0x%08x%08x", data);

	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR0_STATUS_REG));
	printf("\nPCS0_MR0_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR1_STATUS_REG));
	printf("\nPCS0_MR1_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR2_STATUS_REG));
	printf("\nPCS0_MR2_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS0_MR3_STATUS_REG));
	printf("\nPCS0_MR3_STATUS_REG:0x%08x%08x", data);

	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR0_STATUS_REG));
	printf("\nPCS1_MR0_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR1_STATUS_REG));
	printf("\nPCS1_MR1_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR2_STATUS_REG));
	printf("\nPCS1_MR2_STATUS_REG:0x%08x%08x", data);
	data = cvmx_read_csr(CVMX_ADD_IO_SEG(PCS1_MR3_STATUS_REG));
	printf("\nPCS1_MR3_STATUS_REG:0x%08x%08x\n\n", data);
}

int setup_amcc_phy(int interface, uint8_t  mdio_addr, int mdio_bus, uint8_t mode/*1G or 10G*/, uint8_t mdio_boot /*0 or 1*/)
{

	int result;
	
    uint16_t data;
    uint16_t fw_major, fw_minor, fw_bugfix;
    uint16_t fw_year, fw_month, fw_day;
    int rc;
    int timeout;
    uint16_t data_last;
    uint16_t heartbeat;
	uint8_t sfpType;
	uint16_t opMode;

    rc = 0;
    timeout = 1000; /*ms*/
    heartbeat = 0;
    data_last = 0;/* clear it out before we start */
	sfpType = 0;


	if (mode == AMCC_PHY_MODE_1GE)
	{
		opMode = 0x00C8;
        sfpType = SFP_TYPE(octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR + mdio_addr, TRANSCEIVER_6));
        printf ("SFP Type: %s", SFP_STRING(sfpType));
	}
	else
	{
        /* was CONNECTOR, changed back to TRANSCEIVER_6 */
		if (mdio_bus == 0) {	
			sfpType = SFP_TYPE(octeon_twsi_read8(BOARD_SFP0_TWSI_ADDR,
												 SFP_EEPROM_BASE | TRANSCEIVER_6));
		} else {
			sfpType = SFP_TYPE(octeon_twsi_read8(BOARD_SFP1_TWSI_ADDR,
												 SFP_EEPROM_BASE | TRANSCEIVER_6));
		}
		
		printf ("SFP+ Type: %s", SFP_STRING(sfpType));
	
		opMode = (sfpType == SFP_FIBER) ? 0x0008 : (sfpType == SFP_DIRECT_ATTACHED) ? 0x0020 : 0x0000;
	}
		
    /* Send down the MDIO start commands to the AMCC phy */

    /* issue a initial soft reset to PHY */
    phyRegWrite(mdio_addr/*phy addr*/, QT2025_PMA_PMD_CTRL1_REG/*reg*/, 0xFFFF /*mask*/, 0x8000/*data*/, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

    /* hold the internal Phy's 8051 in reset during initial MDIO command set */
    phyRegWrite(mdio_addr, QT2025_PMA_MDIO_RESETS_REG, 0x0000 /*mask*/, 0x0000, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

    /* setup the internal microconroller config register, uc clock and sync error ref clock */
    phyRegWrite(mdio_addr, QT2025_PMA_UC8051_CFG_REG, 0x0000 /*mask*/, 0x0004 /*UC_CLK_SEL=156.25*/, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

	if (!mdio_boot)
	{   /* we don't do this when using mdio firmware download */
		
		/* set the PMA i2c slave address where eeprom is connected to internal microcontroller */
		phyRegWrite(mdio_addr, QT2025_PMA_I2C_SLAVE_ADDR_REG, 0x0000 /*mask*/, 0x0051, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
	}
	else
	{
		/* set it wrong so it doesn't try to boot from eeprom */
		phyRegWrite(mdio_addr, QT2025_PMA_I2C_SLAVE_ADDR_REG, 0x0000 /*mask*/, 0x0000, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
	}
	
    /* fill PMA spare 2 reg, oper mode, and bit rate selection */
    phyRegWrite(mdio_addr, QT2025_PMA_UC8051_SPARE_2_REG, 0x0000 /*mask*/, opMode, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

    /* fill PMA spare 3 reg, sfp+ module type, FTX and XDRV overrides */
    phyRegWrite(mdio_addr, QT2025_PMA_UC8051_SPARE_3_REG, 0x0000 /*mask*/, 0x0098, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

    /* set 10G seed 0 register, freeze point control */
    phyRegWrite(mdio_addr, QT2025_PCS_10G_R_SEED_B0_REG, 0x0000 /*mask*/, 0x0E00, 0, AMCC_DEV_ADDR_PCS, mdio_bus);

    /* when in reset this is BOOT_CTRL_REG, sets the FTX and FRX FR4 loss, setup hardware LED control*/
	if (mode == AMCC_PHY_MODE_1GE)
	{
		data = 0x0012;
	}
	else
	{
		data = 0x0013;
	}
    phyRegWrite(mdio_addr, QT2025_PCS_10G_R_SEED_B1_REG, 0x0000 /*mask*/, data, 0, AMCC_DEV_ADDR_PCS, mdio_bus);

    /* set 10G seed 2 register, boot key */
    phyRegWrite(mdio_addr, QT2025_PCS_10G_R_SEED_B2_REG, 0x0000 /*mask*/, 0xA528, 0, AMCC_DEV_ADDR_PCS, mdio_bus);

	if (mode == AMCC_PHY_MODE_10GE)
	{
		/* invert the Tx polarity */
		phyRegWrite(mdio_addr, QT2025_PMA_PMD_MODE_REG, ~0x0080 /*mask*/, 0x80, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
	
		/* NOTE: LED settings will not work unless reg 3.0027h LED1_CTRL=1 */
		/* set the Activity LED to blink on activity only (was 0x0002) */
		phyRegWrite(mdio_addr, QT2025_GPIO_LED1_REG, 0x0000 /*mask*/, 0x0001, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
	
		/* set the Link LED to report link state only (was 0x0009) */
		phyRegWrite(mdio_addr, QT2025_GPIO_LED2_REG, 0x0000 /*mask*/, 0x0009, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
		/****** end LED setup ******/
	
		/* turn off pre-emphasis if it's not already disabled */
		phyRegRead(mdio_addr, QT2025_XDRV_CTRL_5_REG, &data, AMCC_DEV_ADDR_XGXS, mdio_bus);
	
		if ((data & 0xFF) != 0xF0)
		{
			printf("\nQt2025: Turning Off Pre-emphasis");
			/* turn pre-emphasis off */
			phyRegWrite(mdio_addr, QT2025_XDRV_CTRL_5_REG, 0x0000 /*mask*/, 0xF0, 0, AMCC_DEV_ADDR_XGXS, mdio_bus);
		}
	}
    /*
     * release micro controller from reset, allow controller to boot out of eeprom (now: mdio download instead)
     * check for heart beat at this time
     */
    phyRegWrite(mdio_addr, QT2025_PMA_MDIO_RESETS_REG, 0x0000 /*mask*/, 0x0002, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

	if (mdio_boot)
	{
        /********************************** THIS CODE WAS ADDED TO DOWNLOAD FIRMWARE VIA MDIO ***************************/
        /* suspend CPU */
        phyRegWrite(mdio_addr, QT2025_PCS_SW_DLD_REG, 0x0000 /*mask*/, 0x00C0, 0, AMCC_DEV_ADDR_PCS, mdio_bus);

        result = phy_fw(mdio_addr, mdio_bus);
        
		/* set the PMA i2c slave address where eeprom is connected to internal microcontroller */
		phyRegWrite(mdio_addr, QT2025_PMA_I2C_SLAVE_ADDR_REG, 0x0000 /*mask*/, 0x0051, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

		/* release the micro with the upload flag set (force EEPROM to be updated) */
        phyRegWrite(mdio_addr, QT2025_PCS_SW_DLD_REG, 0x0000 /*mask*/, 0x0060, 0, AMCC_DEV_ADDR_PCS, mdio_bus);
		/****************************************************************************************************************/
		timeout = 50; /*ms x 100*/
        do
        {
            /* check the status reg to see if the firmware is fully booted */
            phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
            timeout--;
            udelay(100000);/* sleep for 100 ms before we try again */

        }while (timeout && (data == 0x00 || data == 0x10 || data == 0x20 || data == 0x40 ));
	}

    while (timeout && (heartbeat < AMCC_HEARTBEAT_THRESHOLD) )
    {
        /* check the heartbeat reg*/
        phyRegRead(mdio_addr, QT2025_PCS_REG_FW_HEARTB, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        /* compare and see if the heartbeat register is changing*/
        if (data != data_last)
        {
            heartbeat++;
        }

        data_last = data;
        timeout--;
        udelay(1000);/* sleep for 1 ms before we try again */
    }

    if (heartbeat < AMCC_HEARTBEAT_THRESHOLD)
    {
        printf("\n\n***Qt2025 PHY[%d] ERROR!!! Phy did not start!***\n", mdio_addr);
        rc = -1;
    }

    if (!rc)
    {
        timeout = 50; /*ms x 100*/
        do
        {
            /* check the status reg to see if the firmware is fully booted */
            phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
            timeout--;
            udelay(100000);/* sleep for 100 ms before we try again */

        }while (timeout && (data == 0x00 || data == 0x10 || data == 0x20 || data == 0x40 ));

        /* use this to determine if the phy is booted */
        if (timeout)/* data should be 0x30 || 0x50 || 0x60 || 0x70 */
        {
            /* get date of FW version */
            phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD1_REG, &fw_year, AMCC_DEV_ADDR_PCS, mdio_bus);
            phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD2_REG, &fw_month, AMCC_DEV_ADDR_PCS, mdio_bus);
            phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD3_REG, &fw_day, AMCC_DEV_ADDR_PCS, mdio_bus);
			
            /* check to see if we need to apply the finisar workaround */
            if ( (fw_year == 9 && (fw_month < 4 || (fw_month == 4 && fw_day < 23))) || (fw_year < 9) )/* if date of build is < 4/23/2009 */
            {
                printf("\nQt2025 Applying clock phase workaround.");
                /**
                 * Workaround for Finisar SFP+s not working with loopback plugs.
                 * This should not affect any other SFP+ actions.
                 */
                /* set phase to work with Finisar SFP+s */
                phyRegWrite(mdio_addr, QT2025_PCS_REG_PHASE_REG, 0x0000 /*mask*/, 0xFA, 0, AMCC_DEV_ADDR_PCS, mdio_bus);
                /* read LOS state: RX_LOS; 1=LOS 0=Signal OK */
                phyRegRead(mdio_addr, QT2025_PMA_PMD_PAD_IN_STATUS_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

                /* if link up */
                if ((data & 0x04) == 0)
                {
                    printf("\nQt2025 forcing link down to apply clock phase workaround.");
                    /* Force link down*/
                    phyRegWrite(mdio_addr, QT2025_PMA_PMD_PAD_IN_INV_REG, 0x0000 /*mask*/, 0x04, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
                    udelay(100000);/* sleep for 100 ms to give firmware time to respond to LOS condition */
                    /* bring link back up */
                    phyRegWrite(mdio_addr, QT2025_PMA_PMD_PAD_IN_INV_REG, 0x0000 /*mask*/, 0x00, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
                }
                /******* end workaround *******/
            }

            /* get major, minor, bugfix FW version */
            phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION1_REG, &fw_major, AMCC_DEV_ADDR_PCS, mdio_bus);
            phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION2_REG, &fw_minor, AMCC_DEV_ADDR_PCS, mdio_bus);
            phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION3_REG, &fw_bugfix, AMCC_DEV_ADDR_PCS, mdio_bus);

					
            if (fw_major == 255)/* early release versioning scheme */
            {
                printf ("\nQt2025 Phy%d is running, Version %d.%d.%d ", mdio_addr, fw_major, fw_minor, fw_bugfix);
            }
            else /* new versioning scheme */
            {
                printf ("\nQt2025 Phy%d is running, Version %d.%d.%d.%d ",
                        mdio_addr, fw_major >> 4/*upper nibble*/, fw_major & 0x0F /* lower nibble */, fw_minor, fw_bugfix);
            }
            printf("%d/%d/%02d", fw_month, fw_day, fw_year); /* print out the date stamp of the firmware build */

			if (mdio_boot)
			{
				/* wait for firmware initialization to complete */
				do {
					phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
					udelay(1000);/* sleep for 1 ms before we try again */
				} while (((data & 0xff) == 0x10) || ((data & 0xff) == 0x00));
				
				phyRegWrite(mdio_addr, QT2025_PMA_FINAL_INIT1_REG, 0x0000 /*mask*/, 0x0020, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
				phyRegWrite(mdio_addr, QT2025_PMA_FINAL_INIT2_REG, 0x0000 /*mask*/, 0x0002, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
				phyRegWrite(mdio_addr, QT2025_PMA_FINAL_INIT2_REG, 0x0000 /*mask*/, 0x0000, 0, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
	
				/* setup LED functionality on the PHY */
                phyRegWrite(mdio_addr, QT2025_PCS_LED_CTRL_REG, 0x0000 /*mask*/, 0x0018, 0, AMCC_DEV_ADDR_PCS, mdio_bus);
			}   
        }
        else
        {					
            printf("\nUnexpected Qt2025 Phy%d error! UC_STATUS:0x%02x", mdio_addr, data);	
			rc = -1;
        }
    }

    printf ("\n");
	
    return rc;
}

int amcc10gPhyLinkSetup(uint8_t  mdio_addr, int mdio_bus)
{

	int interface;
	uint16_t mode;
	int rc;
	uint16_t data, tstdata, result;
	int timeout;
	uint64_t pcs_mrx_status_reg;
	uint8_t mdio_boot;
	/* need to determine which port we are setting up */
	
	if (mdio_bus == 0)
	  interface = 0;
	else
	  interface = 1;

	mdio_boot = 0;//boot from EEPROM


	mode = get_phy_mode(interface);

	if (mode == AMCC_PHY_MODE_1GE)
	{
		printf("\nInterface %d: setup for 1G mode\n", interface);
		rc = setup_amcc_phy(interface, mdio_addr, mdio_bus, AMCC_PHY_MODE_1GE, mdio_boot);
		if (rc == -1)
		{           /* try MDIO boot*/
			printf("****Qt2025: Attempting to boot over MDIO.***\n");
			mdio_boot = 1;
			rc = setup_amcc_phy(interface, mdio_addr, mdio_bus, AMCC_PHY_MODE_1GE, mdio_boot);
		}
	}
	else if (mode == AMCC_PHY_MODE_10GE)
	{
		printf("\nInterface %d: setup for 10G mode\n", interface);
		rc = setup_amcc_phy(interface, mdio_addr, mdio_bus, AMCC_PHY_MODE_10GE, mdio_boot);
		if (rc == -1)
		{           /* try MDIO boot*/
			printf("****Qt2025: Attempting to boot over MDIO.***\n");
			mdio_boot = 1;
			rc = setup_amcc_phy(interface, mdio_addr, mdio_bus, AMCC_PHY_MODE_10GE, mdio_boot); 
		}  
	}
	else
	{
		printf("\nInterface %d: undetermined mode\n");
        return -1;  /* not connected to front panel */
	}

#if 0	
	phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
	printf("\nPHY Firmware Status for interface %d = 0x%02x\n", mdio_bus, data);
#endif /* if 0 */
	
	get_phy_status(mdio_addr, mdio_bus);

	if (mdio_bus == 0) {
			/* wait for link between Octeon and PHY */

		printf("\nInterface 0: wait for link up...\n");

		if (mode == 10) 
		{
				/* for 10G, read the STATUS2 reg to clear any
				   initial faults before reading STATUS1 for
				   link up.  The mask is the same for 1G or
				   10G.
				*/
			pcs_mrx_status_reg = PCSX0_STATUS2_REG;
			data = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));
			pcs_mrx_status_reg = PCSX0_STATUS1_REG;
		} 
		else 
		{
			pcs_mrx_status_reg = PCS0_MR0_STATUS_REG;
		}
	} 
	else 
	{
		/* wait for link between Octeon and PHY */
        printf("\nInterface 1: wait for link up...\n");

		if (mode == 10) 
		{
			/* for 10G, read the STATUS2 reg to clear any
			   initial faults before reading STATUS1 for
			   link up.  The mask is the same for 1G or
			   10G.
			*/
			pcs_mrx_status_reg = PCSX1_STATUS2_REG;
			data = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));
			pcs_mrx_status_reg = PCSX1_STATUS1_REG;
		} else {
			pcs_mrx_status_reg = PCS1_MR0_STATUS_REG;
		} 
	}

	tstdata = (uint16_t) 0x04;
    result = 0;    
    timeout = 1000;

	do {

		data = cvmx_read_csr(CVMX_ADD_IO_SEG(pcs_mrx_status_reg));
        cvmx_wait(1000);
        if (--timeout == 0)
            break;

		result = (data & tstdata);

	} while (result == 0);

    if (timeout == 0) {
    	printf("\nInterface %d: (status = 0x%04x) Octeon link to PHY never came up\n", mdio_bus, data);
    } else {
    	printf("\nInterface %d: (status = 0x%04x) Octeon link to PHY is up\n", mdio_bus, data);
    }
	
    printf ("\n");
    return rc;
}


void get_phy_status(uint8_t mdio_addr, int mdio_bus)
{
	uint16_t data, data1;
    
#ifdef PHY_DEBUG_GETSTATUS

        phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY Firmware Status for interface %d = 0x%02x:", mdio_bus, data);

        switch (data) {
                case 0x00: printf("\nFirmware not yet running\n");
                        break;
                case 0x10: printf("\nCustomer configuration attained\n");
                        break;
                case 0x20: printf("\nSaving RAM to EEPROM\n");
                        break;
                case 0x30: printf("\nNo module detected - waiting for module\n");
                        break;
                case 0x31: printf("\nModule detected - updating NVR\n");
                        break;
                case 0x32: printf("\nPort disabled, chip powered down except for micro (DISABLE_PORT=1)\n");
                        break;
                case 0x40: printf("\nPreparing to start Acquisition\n");
                        break;
                case 0x50: printf("\nAcquisition - attempting to acquire and lock to incoming signal\n");
                        break;
                case 0x60: printf("\nAcquisition completed\n");
                        break;
                case 0x70: printf("\nTracking incoming signal\n");
                        break;
                case 0xF0: printf("\nChecksum failed\n");
                        break;
                case 0xF8: printf("\nDevice version and firmware version are not compatible\n");
                        break;
                default: printf("\nUnknown status\n");
        }

        phyRegRead(mdio_addr, QT2025_PCS_CSUMSTAT0_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY Checksum Status0 for interface %d = 0x%02x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PCS_CSUMSTAT1_REG, &data1, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY Checksum Status1 for interface %d = 0x%02x", mdio_bus, data1);

        if ((data == 0x00F6) && (data1 == 0x0028)) {
                printf("\nFirmware checksum PASSED for interface %d\n", mdio_bus);
        } else {
                if ((data == 0x00DE) && (data1 == 0x00AD  )) {
                        printf("\nFirmware checksum FAILED for interface %d\n", mdio_bus);
                } else {
                        printf("\nFirmware checksum UNKNOWN RESULT for interface %d\n", mdio_bus);
                }
        }

                /* get date of FW version */
        phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD1_REG, &fw_year, AMCC_DEV_ADDR_PCS, mdio_bus);
        phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD2_REG, &fw_month, AMCC_DEV_ADDR_PCS, mdio_bus);
        phyRegRead(mdio_addr, QT2025_PCS_FW_BUILD3_REG, &fw_day, AMCC_DEV_ADDR_PCS, mdio_bus);

        printf("\nFirmware date for interface %d: year = %d  month = %d  year = %02d\n",
                   mdio_bus, fw_year, fw_month, fw_day);

                /* get major, minor, bugfix FW version */
        phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION1_REG, &fw_major, AMCC_DEV_ADDR_PCS, mdio_bus);
        phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION2_REG, &fw_minor, AMCC_DEV_ADDR_PCS, mdio_bus);
        phyRegRead(mdio_addr, QT2025_PCS_FW_VERSION3_REG, &fw_bugfix, AMCC_DEV_ADDR_PCS, mdio_bus);

        if (fw_major == 255)/* early release versioning scheme */
        {
                printf ("\nQt2025 Phy%d is running, Version %d.%d.%d\n", mdio_addr, fw_major, fw_minor, fw_bugfix);
        }
        else /* new versioning scheme */
        {
                printf ("\nQt2025 Phy%d is running, Version %d.%d.%d.%d\n",
                                mdio_addr, fw_major >> 4/*upper nibble*/, fw_major & 0x0F /* lower nibble */, fw_minor, fw_bugfix);
        }

        phyRegRead(mdio_addr, QT2025_PCS_R_STATUS_1_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY R_STATUS_1 for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PCS_R_STATUS_2_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY R_STATUS_2 for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PMA_RXSIGOK_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
        printf("\nPHY PMA_RXSIGOK_REG for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PHY_XGXS_LANE_STATUS_REG, &data, AMCC_DEV_ADDR_XGXS, mdio_bus);
        printf("\nPHY PHY_XGXS_LANE_STATUS_REG for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PCS_RX_1G_STATUS_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY RX_1G_STATUS for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PCS_TX_1G_STATUS_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        printf("\nPHY TX_1G_STATUS for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PMA_RX_ALRM_STAT_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
        printf("\nPHY PMA_RX_ALRM_STAT for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PMA_TX_ALRM_STAT_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
        printf("\nPHY PMA_TX_ALRM_STAT for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PMA_PMD_PAD_IN_STATUS_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
        printf("\nPHY PAD_IN_STATUS_REG for interface %d = 0x%04x", mdio_bus, data);

        phyRegRead(mdio_addr, QT2025_PMA_PMD_PAD_OUT_STATUS_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);
        printf("\nPHY PAD_OUT_STATUS_REG for interface %d = 0x%04x", mdio_bus, data);

        printf("\n\n");

#else
			/* don't print, just read the regs */
		
		/* FW status */
 	phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* checksum status 0 */
	phyRegRead(mdio_addr, QT2025_PCS_CSUMSTAT0_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* checksum status 1 */
	phyRegRead(mdio_addr, QT2025_PCS_CSUMSTAT1_REG, &data1, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* R_STATUS_1 */
	phyRegRead(mdio_addr, QT2025_PCS_R_STATUS_1_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* R_STATUS_2 */
	phyRegRead(mdio_addr, QT2025_PCS_R_STATUS_2_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* PMA_RXSIGOK_REG */
	phyRegRead(mdio_addr, QT2025_PMA_RXSIGOK_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

		/* PHY_XGXS_LANE_STATUS_REG */
	phyRegRead(mdio_addr, QT2025_PHY_XGXS_LANE_STATUS_REG, &data, AMCC_DEV_ADDR_XGXS, mdio_bus);

		/* RX_1G_STATUS */
	phyRegRead(mdio_addr, QT2025_PCS_RX_1G_STATUS_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);

		/* TX_1G_STATUS */
	phyRegRead(mdio_addr, QT2025_PCS_TX_1G_STATUS_REG, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
	
		/* PMA_RX_ALRM_STAT */
	phyRegRead(mdio_addr, QT2025_PMA_RX_ALRM_STAT_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

		/* PAD_IN_STATUS_REG */
	phyRegRead(mdio_addr, QT2025_PMA_PMD_PAD_IN_STATUS_REG, &data, AMCC_DEV_ADDR_PMA_PMD, mdio_bus);

	
#endif /* PHY_DEBUG */
	
}

int amcc_is_phy_alive(uint8_t mdio_addr, int mdio_bus)
{
    uint16_t data;
    int rc;
    int timeout;

    rc = 0;
    timeout = 100; /*ms x 100*/

    do
    {
        /* check the status reg to see if the firmware is fully booted */
        phyRegRead(mdio_addr, QT2025_PCS_REG_UC_STATUS, &data, AMCC_DEV_ADDR_PCS, mdio_bus);
        timeout--;
        udelay(100000);/* sleep for 100 ms before we try again */

    }while (timeout && (data == 0x00 || data == 0x10 || data == 0x20 || data == 0x40 )); 
		
    /* use this to determine if the SFP+ is there */
    if ( data < 0x40 && data != 0x30)
    {
        rc = -1;
    }

    return rc;
}

/*****************************************************************************
 **                  Relinquish Control Of Common I2c Bus
 **
 **  This function works in conjunction with "neg_access_to_i2c_bus()" function
 ** below to release control of the i2c bus when the last i2c task has been
 ** completed.
 **
 **  I2c read/write routines should call this function after all data has been
 ** successfully or unsyccessfully read or written on the i2c bus to let the H8
 ** know the i2c bus is now available for use.
 **
 ** INPUT - Nothing
 **
 ** OUTPUT - "BOARD_DPR_OCT_I2C_IN_USE" is cleared to zero (FALSE).
 **
 ** RETURN - Nothing
 **
 *****************************************************************************/
void rel_control_of_i2c_bus(void)
{
#if 1
  unsigned char bval;             /* 8-bit working reg */
  int loop_count;

  loop_count = 0;

  /* clear Octeon's "in use" flag */
  *BOARD_DPR_OCT_I2C_IN_USE = (uint8_t) FALSE; 
  wmb();                             /* force the write to the hardware */

  bval = *BOARD_DPR_H8_I2C_IN_USE;   /* read H8's "in use" flag */
  wmb();                             /* force the read from hardware */
 
  /* If H8's "in use" flag is set, wait up to max time for it to clear */
  if (bval == TRUE)
    {
      //printf("\nH8 was waiting for the bus...");
    for (loop_count = 0; loop_count < MAX_H8_WAIT_TRYS; loop_count++)
      {
      udelay(100);/* sleep for 100 microseconds before we try again */

      bval = *BOARD_DPR_H8_I2C_IN_USE;   /* read H8's "in use" flag */
      wmb();                             /* force the read from hardware */
 
      if (bval == FALSE)                 /* exit when H8 releases the bus */
        {
          //printf("released!\n");
        return;
        }
      } /* end for */

    } /* end if */

  if (loop_count >= MAX_H8_WAIT_TRYS)
    {
    loop_count = 0; /* get rid of warning */
    //printf("timed out!\n");
    }
#endif
}

/*****************************************************************************
 **                    Negotiate Access To I2c Bus
 **
 **   The TNPA-5651x design included one i2c bus that is accessible by both the
 ** H8 (MMC) processor and the Octeon.  This causes problems because both the 
 ** H8 and the Octeon consider themselves masters on the i2c bus.  Furthermore,
 ** the Octeon would hang occasionally and/or experience loss of data on 
 ** certain i2c operations if the H8 was also accessing the bus at the same 
 ** time.
 **
 **   For these reasons, a protocol was created to negotiate access to the bus
 ** so only the Octeon or the H8 had access to the shared bus at any given
 ** time.  The protocol includes times out so neither processor is ever locked
 ** out from access to the bus.  Also, the Octeon driver was modified to 
 ** eliminate hangs waiting for i2c bus hardware.  Now an error is returned if
 ** the bus is not accessible for a predetermined amount of time.
 **
 **  This function is called by the appropriate i2c read/write functions, to
 ** negotiate access to the bus, just prior to performing the i2c bus action.
 **
 **   The logic for the Octeon to negotiate access to the i2c bus is described
 ** below:
 **
 **  First there are two bytes of FPGA dual-port RAM used to negotiate access 
 ** to the common i2c bus as decribed below:
 ** Name              Description
 ** ----------------- ---------------------------------------------------
 ** H8 "IN USE" flag  Flag indicating the H8 is currently using the i2c bus
 ** OCT "IN USE" flag Flag indicating the Octeon is currently using the i2c bus
 **
 ** NOTE: Setting a flag to zero (FALSE) indicates the flag is not active
 **       Setting a flag to one (TRUE) indicates a flag is active
 **
 **  1 - Set the Octeon's "IN USE" flag.
 **  2 - Wait, up to a specified time, for H8's "IN USE" flag to be clear 
 **      (FALSE) X times in a row.
 **      Total maximum wait time is 1 millisecond.
 **      When "IN USE" flag is clear X in a row or max time is reached,
 **      proceed to next step.
 **      X = 5
 **  3 - Return to calling code so I2c operation can be started
 **
 **  4 - When i2c operation is completed, i2c driver must clear Octeon's
 **      "IN USE" flag.
 ** 
 ** INPUT - Nothing
 **
 ** OUTPUT - "BOARD_DPR_OCT_I2C_IN_USE" is set to a one (TRUE).
 **
 ** RETURN - This function will return to the calling code when access to the
 **          i2c bus has been negotiated (or max wait time has expired).
 **
 *****************************************************************************/
void neg_access_to_i2c_bus(void)
{
#if 1
  uint8_t clear_loop_count;       /* consecutive count of H8's "IN USE" flag 
                                     being clear */
  unsigned char bval;             /* 8-bit working reg */
  int i,j;

#if 0 /* DEBUG */
  int to_count=0;
do
  {
#endif
  /* 1 - let H8 know we are requesting the i2c bus */
  *BOARD_DPR_OCT_I2C_IN_USE = (uint8_t) TRUE;
  wmb();                             /* force the write to the hardware */

  /* 2 - wait for bus avail or time is up */
  i = 0;
  do
    {
    clear_loop_count = 0;
    for (j = 0; j < (MIN_H8_CLR_COUNT+1); j++)
      {
      bval = *BOARD_DPR_H8_I2C_IN_USE;
      wmb();                             /* force the read from hardware */
      if (bval == FALSE)  
        clear_loop_count++;     /* inc count if bus is avail */
 
      /* 3 - exit if bus is avail specified number of times in a row */
      if (clear_loop_count == MIN_H8_CLR_COUNT)
        {
          //printf("\nI2c avail!\n"); /* DEBUG */
        return;
        }
      } /* end for */

    udelay(100);/* sleep for 100 microseconds before we try again */

    } while (i++ < MAX_H8_WAIT_TRYS); /* Nornal operation */

#if 0
  rel_control_of_i2c_bus();

  udelay(1000);/* sleep for 1000 microseconds before we try again */

  printf("\nCommon i2c bus wait time = %d has expiried!!\n",to_count++); /* DEBUG */

  } while(1); /* DEBUG outer loop */
#endif

//printf("\nCommon i2c bus wait time has expiried!!\n"); /* DEBUG */

  return; /* 3 - exit if max wait expired */
#endif
}


