/*
Copyright (C) 2008, 2009  GE Intelligent Platforms, Inc
 
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifdef __U_BOOT__
#  include <common.h>
#  include <command.h>
#  include <malloc.h>
#  include <devices.h>
#  include <net.h>
#  include <environment.h>
#  include "octeon_boot.h"
#  include <lib_octeon.h>
#  include <cvmx-bootmem.h>
#  include <octeon_eeprom_types.h>
#  include <lib_octeon_shared.h>
#  include "npaEagle.h"
#  include "eagleReg.h"

#  define NPA_CONFIG_EAGLE				1
#  define DPRINTK(a, b, c, d, ...)
#  define ERROR( x )					-1
#elif defined( LINUX_OS )
#  include <linux/module.h>
#  include <linux/moduleparam.h>
#  include <linux/init.h>
#  include "config.h"
#  include <linux/kernel.h> /* printk() */
#  include <linux/fs.h>     /* everything... */
#  include <linux/errno.h>  /* -1 codes */
#  include <linux/types.h>  /* size_t */
#  include <linux/vmalloc.h>
#  include <linux/hdreg.h>
#  include <linux/spinlock.h>
#  include <linux/netdevice.h>
#  include <linux/ethtool.h>
#  include <linux/delay.h>
#  include "cvmx.h"
#  include "npaDevice.h"
#  include "npaIoctl.h"
#  include "eagle/npaEagle.h"
#  include "eagle/eagleReg.h"
#  define ERROR( x )					x
#endif

#if defined( NPA_CONFIG_EAGLE )


static int npaEgAsicTwoWireSerialRead(
	struct npaEagleObj_t * eagleObj,
    int portNum,                /* port number */
    int moduleAddr,             /* module address */
    int regAddr,                /* register address */
    unsigned int *data          /* data value */
    );
static int npaEgAsicTwoWireSerialWrite(
	struct npaEagleObj_t * eagleObj,
    int portNum,                /* port number */
    int moduleAddr,             /* module address */
    int regAddr,                /* register address */
	unsigned int data			/* data value */
	);

int npaEgSfpVsRegWrite(struct npaEagleObj_t * eagle, int portNum, int reg, int mask, int newVal )
{
	unsigned int data = 0;

	do  {
		if( 0 != npaEgSfpVsRegRead( eagle, portNum, reg, &data ) )
			break;
		data = ( data & mask ) | newVal;
		if( 0 != npaEgAsicTwoWireSerialWrite( eagle, portNum, 6, reg, (data >> 8) ))
			break;
		if( 0 != npaEgAsicTwoWireSerialWrite( eagle, portNum, 6, reg, (data & 0xFF) ))
			break;
		return 0;
	} while( 0 );
	return -1;
}

int npaEgSfpVsRegRead(struct npaEagleObj_t * eagle, int portNum, int reg, unsigned int *val )
{
    unsigned int hi, lo;
	unsigned int data;

	do
	{
		if( 0 != npaEgAsicTwoWireSerialRead( eagle, portNum, 6, reg, &hi ) )
			break;
		if( 0 != npaEgAsicTwoWireSerialRead( eagle, portNum, 6, reg, &lo ) )
			break;
		data = ( hi << 8 ) | lo;
		*val = data;
		return 0;
	} while(0);
	return -1;
}

int npaEgPhyRegWrite(struct npaEagleObj_t * eagle, int portNum, int reg, int mask, int newVal )
{
	unsigned int data = 0;

	do  {
		if( 0 != npaEgPhyRegRead( eagle, portNum, reg, &data ) )
			break;
		data = ( data & mask ) | newVal;
		if( 0 != npaEgAsicTwoWireSerialWrite( eagle, portNum, 0, reg, data ))
			break;
		return 0;
	} while( 0 );
	return -1;
}

int npaEgPhyRegRead(struct npaEagleObj_t * eagle, int portNum, int reg, unsigned int *val )
{
	unsigned int data;

	do
	{
		if( 0 != npaEgAsicTwoWireSerialRead( eagle, portNum, 0, reg, &data ) )
			break;
		*val = data;
		return 0;
	} while(0);
	return -1;
}

int npaEgAsicTwoWireSerialWrite(
	struct npaEagleObj_t * eagleObj,
    int portNum,                /* port number */
    int moduleAddr,             /* module address */
    int regAddr,                /* register address */
	unsigned int data			/* data value */
	)
{
    unsigned int regVal, sdlVal;
	int timeout = 1000;

	DPRINTK( TRACE, INFO, "eagle", "TWSI Write %x to %x on Port: %d\n", data, regAddr, portNum );

    /* Check for the port number argument */
    if (portNum < 0 || portNum > 23) {
        DPRINTK( ERROR, ERR, "eagle", "The port number argument passed is incorrect\n");
        DPRINTK( ERROR, ERR, "eagle", "The port number can take value between 0 and 23\n");
        return -1;
    }

    if ((portNum % 4) == 0)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 1);
    else if ((portNum % 4) == 1)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 2);
    else if ((portNum % 4) == 2)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 4);
    else
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 8);

    sdlVal = 1 << (portNum / 4);
    npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK( 9, 4 ), sdlVal);

    /* Form the value to be written to the register for the read operation */
    regVal = 0;
    regVal = regAddr | moduleAddr << 8 | (1 << 12) | ((data & 0xFF) << 16);
    npaEgRegSet (eagleObj, AD_CfgCo2WireSlaveCommandReg, 0, regVal);

    /* Check for the two wire busy signal */
	while( timeout-- )
	{
        npaEgRegGet (eagleObj, AD_StsCo2WireMaster, 0, &regVal);
		if( ( regVal & 0x100 ) == 0x0 )
			break;
		udelay(100);
	}
    npaEgRegSet (eagleObj, AD_CfgCo2WireSlaveCommandReg, 0, 0 );
    npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK( 9, 0 ), 0 );

	if( ( regVal & 0x300 ) == 0x200 )
	{
		DPRINTK( ERROR, ERR, "eagle", "-1 : 2 Wire Write Failed for port number %d\n", portNum);
		return -1;
	}
	if( timeout == 0 )
	{
		DPRINTK( ERROR, ERR, "eagle", "timeout while waiting for TWSI\n");
		return -1;
	}
    return 0;
}

int npaEgAsicTwoWireSerialRead(
	struct npaEagleObj_t * eagleObj,
    int portNum,                /* port number */
    int moduleAddr,             /* module address */
    int regAddr,                /* register address */
    unsigned int *data          /* data value */
    )
{
    unsigned int regVal, sdlVal;
	int timeout = 1000;

 	DPRINTK( TRACE, INFO, "eagle", "TWSI Read at %x on Port: %d\n", regAddr, portNum );

	/* Check for the port number argument */
    if (portNum < 0 || portNum > 23) {
        DPRINTK( ERROR, ERR, "eagle", "The port number argument passed is incorrect\n");
        DPRINTK( ERROR, ERR, "eagle", "The port number can take value between 0 and 23\n");
        return -1;
    }

    if ((portNum % 4) == 0)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 1);
    else if ((portNum % 4) == 1)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 2);
    else if ((portNum % 4) == 2)
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 4);
    else
        npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK(3, 0), 8);

    sdlVal = 1 << (portNum / 4);
    npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK( 9, 4 ), sdlVal);
	npaEgRegGet( eagleObj, AD_CfgCo2WireMasterSetup, 0, &regVal );

    *data = 0;

    /* Form the value to be written to the register for the read operation */
    regVal = 0;
    regVal = regAddr | moduleAddr << 8 | 1 << 13;
    npaEgRegSet (eagleObj, AD_CfgCo2WireSlaveCommandReg, 0, regVal);

    /* Check for the two wire busy signal */
	while( timeout-- )
	{
        npaEgRegGet (eagleObj, AD_StsCo2WireMaster, 0, &regVal);
		if( ( regVal & 0x100 ) == 0x0 )
			break;
		udelay(100);
	}
    npaEgRegSet (eagleObj, AD_CfgCo2WireSlaveCommandReg, 0, 0 );
    npaEgRegSet (eagleObj, AD_CfgCo2WireMasterSetup, MAKE_MASK( 9, 0 ), 0 );

	if( ( regVal & 0x300 ) == 0x200 )
	{
		DPRINTK( ERROR, ERR, "eagle", "-1 : 2 Wire Read Failed for port number %d\n", portNum);
		return -1;
	}
	if( timeout == 0 )
	{
		DPRINTK( ERROR, ERR, "eagle", "timeout while waiting for TWSI\n");
		return -1;
	}

    *data = regVal & 0xFF;

	DPRINTK( TRACE, INFO, "eagle", "TWSI Read at %x on Port: %d, Returned: %x\n", regAddr, portNum, (regVal & 0xFF) );
	return 0;
}




#endif
