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

/* regIf - Register interface library */

/*

DESCRIPTION:
    This library module provides generic interface to ASIC device
    registers. The device driver programs use the interface functions
    of regIf module to read from or write to the ASIC registers.

    The register database consists of array of variable of type reginfo_t.
    Each reginfo_t variable holds all required register information like
    name, offset address and actual address. The actual address is computed
    during regdbInit() by combining base address with offset adress.

    Register interface library supports bit field registers. Bit field
    registers are a group of consecutive bits in a register that accessed
    using all register interface functions. A write to bit field register
    using 'regSet' will result in write modify of only the bits of that
    bit field register.

INCLUDES:
    regif.h

SEE ALSO:

*/

#include <common.h>
#include <config.h>
#include <command.h>

#ifndef CONFIG_OCTEON_AT5810
#include "systypes.h"
#endif

#include "rhOsif.h"
#include "rhRegif.h"

int regDelay = 1;
int gRegifDebug = 0;
int regSetDisplay = 1;

/*
 * buildMask
 * This function builds the 32bit mask using the hiBit and the LoBit.
 * Parameters   : hiBit, loBit
 * Returns      : Returns 32bit mask
 */
unsigned int buildMask (int hiBit, int loBit)
{
    return ( ((0xFFFFFFFF) << loBit) & ((0xFFFFFFFF) >> (31-hiBit)) );
}

static void dumpReg (reginfo_t *regp)
{
    osPrintf("\n********** Dump register ***********\n");
    osPrintf("id      %d\n", regp->regid);
    osPrintf("name    %s\n", regp->name);
    osPrintf("offset  %d\n", regp->offset);
    osPrintf("hibit   %d\n", regp->hiBit);
    osPrintf("lobit   %d\n", regp->loBit);
    osPrintf("prop    0x%x\n", regp->properties);
    osPrintf("instOff %d\n", regp->instOffset);
    osPrintf("instMax %d\n", regp->instMax);
    osPrintf("mdio_da 0x%x\n", regp->mdio_da);
    osPrintf("mdio_ra 0x%x\n", regp->mdio_ra);
    osPrintf("address 0x%x\n", regp->addr);
    osPrintf("mask    0x%x\n", regp->mask);
}

/*
 * validate a register
 */
int validateReg (reginfo_t *regp, int regid, int inst, int prop)
{
    if (regp == NULL){
        osPrintf("ERROR: validateReg: Null register...\n");
        return (ERROR);
    }
    if (regp->regid != regid){
        osPrintf("ERROR: validateReg: regid mismatch for %d\n", regid);
        dumpReg(regp);
        return (ERROR);
    }
    if ( !(regp->properties & prop) ){
        osPrintf("ERROR: validateReg: unsupported property %x\n", prop);
        dumpReg(regp);
        return (ERROR);
    }
    if ( inst >= regp->instMax ){
        osPrintf("ERROR: validateReg: invalid instance %d\n", inst);
        dumpReg(regp);
        return (ERROR);
    }
    return (OK);
}

/******************************************************************
* regdbInit - Initialize register database
*
* This routine initializes register data base with specified
* memory base address.  This routine must be called before
* invoking any of register interface library functions.
*
* RETURNS: OK or ERROR
*/
int regdbInit (
    reginfo_t regdb[],      /* Register database */
    unsigned int base_addr        /* Memory base address */
    )
{
    int regid;
    reginfo_t  *regp;

    for (regid = 0; regdb[regid].regid != -1; regid++) {
        regp = &regdb[regid];

        /* halt if table is not in order */
        if ( regp->regid != regid ) {
            osPrintf("ERROR: %s: unordered regid in table (%d, %d)\n",
                          FNAME, regid, regp->regid);
            return (ERROR);
        }

        /* check hi and low bit */
        if (regp->hiBit < regp->loBit){
            osPrintf("ERROR: %s: hbit is less than lbit for %s\n",
                    FNAME, regp->name);
            return (ERROR);
        }

        regp->addr = base_addr + regp->offset;
        regp->mask = buildMask(regp->hiBit, regp->loBit);
    }
    return (OK);
}

/*
 * Check type of access and read register.
 * Return whole register data.
 */
unsigned int readAddress (unsigned int address)
{
    unsigned int reg_addr;
    unsigned int data;

    reg_addr = address;

#ifdef REDHAWK_16BIT_CPU_INTERFACE
    /* first read gets upper 16bits; second read gets lower 16bits */
    data = READ16(reg_addr) & 0xFFFF;
    data <<= 16;
    data = data | ( READ16(reg_addr+2) & 0xFFFF );
#else
    data = READ32(reg_addr);
	/* READ32(reg_addr); */
#endif

    return (data);
}

/*
 * Check type of access and write register.
 * write whole register data.
 */
void writeAddress (unsigned int address, unsigned int data)
{
    unsigned int reg_addr;

    reg_addr = address;

#ifdef REDHAWK_16BIT_CPU_INTERFACE
    /* write upper 16bits first and then write lower 16bits */
    WRITE16(reg_addr, ((data >> 16) & 0xFFFF));
    osDelay(regDelay);
    WRITE16(reg_addr+2, (data & 0xFFFF));
#else
    WRITE32(reg_addr, data);
#endif
    regDetail("addr 0x%x data 0x%x\n", reg_addr, data);
}

/*
 * Check type of access and read register.
 * Return whole register data.
 */
unsigned int readRegister (reginfo_t *regp, int inst)
{
    unsigned int reg_addr;
    unsigned int data;

    reg_addr = (regp->addr + (inst * regp->instOffset));
    regDetail("addr 0x%x\n", reg_addr);

#ifdef REDHAWK_16BIT_CPU_INTERFACE
    /* first read gets upper 16bits; second read gets lower 16bits */
    data = READ16(reg_addr) & 0xFFFF;
    data <<= 16;
    data = data | ( READ16(reg_addr+2) & 0xFFFF );
#else
    data = READ32(reg_addr);
	/* READ32(reg_addr); */
#endif

    return (data);
}

/*
 * Check type of access and write register.
 * write whole register data.
 */
void writeRegister (reginfo_t *regp, int inst, unsigned int data)
{
    unsigned int reg_addr;

    reg_addr = (regp->addr + (inst * regp->instOffset));

#ifdef REDHAWK_16BIT_CPU_INTERFACE
    /* write upper 16bits first and then write lower 16bits */
    WRITE16(reg_addr, ((data >> 16) & 0xFFFF));
    osDelay(regDelay);
    WRITE16(reg_addr+2, (data & 0xFFFF));
#else
    WRITE32(reg_addr, data);
#endif
	if (regSetDisplay)
    	regDetail("addr 0x%x data 0x%x\n", reg_addr, data);
}

/******************************************************************
* regGet - Get register value
*
* This routine returns value from register specified by
* register database and register Id.
*
* RETURNS: OK or ERROR. On success the parameter 'regval' contains
* register value.
*/
int regGet (
    reginfo_t regdb[],      /* register data base */
    int reg,                /* register id */
    unsigned int inst,      /* register instance */
    unsigned int *regval    /* register value */
    )
{
    unsigned int val;
    reginfo_t *regp;

    regp = &regdb[reg];
    if (validateReg(regp, reg, inst, PROP_RD) == ERROR)
        return (ERROR);

    regTrace("'%s'\n", regp->name);

    val = readRegister(regp, inst);        /* handles swap if needed */

    /* Return complete or partial reg value */
    if ( isRegWhole(regp->properties) )
        *regval = val;
    else
        *regval = (val & regp->mask) >> regp->loBit;

    regTrace("read 0x%x, return 0x%x\n", val, *regval);

    return OK;
}

/******************************************************************
* regSet - Set register value
*
* This routine sets value specified by 'regval' to register
* specified by register database and registerId.
*
* If instance is set to -1 then all instances of specified
* register is set to the same value.
*
* RETURNS: OK or ERROR
*/
int regSet (
    reginfo_t regdb[],      /* register database */
    int reg,                /* register id */
    unsigned int inst,      /* register instance */
    unsigned int regval     /* register value to set */
    )
{
    unsigned int curVal;
    unsigned int newVal;
    reginfo_t *regp;

    regp = &regdb[reg];
    if (validateReg(regp, reg, inst, PROP_WR) == ERROR)
        return (ERROR);

    regTrace("0x%x >> '%s' %d\n", regval, regp->name, inst);

    /*
     * Check properties and set complete or partial reg value.
     */
    if ( isRegWhole(regp->properties) ){
        newVal = regval;
    } else {
        curVal = readRegister(regp, inst);     /* handles read swap */
        curVal = curVal & ~(regp->mask);
        newVal = curVal | ((regval << regp->loBit) & regp->mask);
    }

    writeRegister(regp, inst, newVal);         /* handles write swap */
    osDelay(regDelay);

    return OK;
}
