/**
 * @file IxFeatureCtrl.c
 *
 * @author Intel Corporation
 * @date 29-Jan-2003
 *
 * @brief Feature Control Public API Implementation
 *
 * 
 * @par
 * IXP400 SW Release version 2.0
 * 
 * -- Copyright Notice --
 * 
 * @par
 * Copyright 2001-2005, Intel Corporation.
 * All rights reserved.
 * 
 * @par
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Intel Corporation nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * @par
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * @par
 * -- End of Copyright Notice --
*/

#include "IxOsal.h"
#include "IxVersionId.h"
#include "IxFeatureCtrl.h"

/* Macro to read from the Feature Control Register */
#define IX_FEATURE_CTRL_READ(result) \
do { \
ixFeatureCtrlExpMap(); \
(result) = IX_OSAL_READ_LONG(ixFeatureCtrlRegister); \
} while (0)

/* Macro to write to the Feature Control Register */
#define IX_FEATURE_CTRL_WRITE(value) \
do { \
ixFeatureCtrlExpMap(); \
IX_OSAL_WRITE_LONG(ixFeatureCtrlRegister, (value)); \
} while (0)

/*
 * This is the offset of the feature register relative to the base of the
 * Expansion Bus Controller MMR.
 */
#define IX_FEATURE_CTRL_REG_OFFSET (0x00000028)


/* Boolean to mark the fact that the EXP_CONFIG address space was mapped */
PRIVATE BOOL ixFeatureCtrlExpCfgRegionMapped = FALSE;

/* Pointer holding the virtual address of the Feature Control Register */
PRIVATE VUINT32 *ixFeatureCtrlRegister = NULL;

/* Place holder to store the software configuration */
PRIVATE BOOL swConfiguration[IX_FEATURECTRL_SWCONFIG_MAX];

/* Flag to control swConfiguration[] is initialized once */
PRIVATE BOOL swConfigurationFlag = FALSE ;

/* Array containing component mask values */
#ifdef __ixp42X
UINT32 componentMask[IX_FEATURECTRL_MAX_COMPONENTS] = {
    (0x1<<IX_FEATURECTRL_RCOMP),
    (0x1<<IX_FEATURECTRL_USB),
    (0x1<<IX_FEATURECTRL_HASH),
    (0x1<<IX_FEATURECTRL_AES),
    (0x1<<IX_FEATURECTRL_DES),
    (0x1<<IX_FEATURECTRL_HDLC),
    (0x1<<IX_FEATURECTRL_AAL),
    (0x1<<IX_FEATURECTRL_HSS),
    (0x1<<IX_FEATURECTRL_UTOPIA),
    (0x1<<IX_FEATURECTRL_ETH0),
    (0x1<<IX_FEATURECTRL_ETH1),
    (0x1<<IX_FEATURECTRL_NPEA),
    (0x1<<IX_FEATURECTRL_NPEB),
    (0x1<<IX_FEATURECTRL_NPEC),
    (0x1<<IX_FEATURECTRL_PCI),
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE,
    (0x3<<IX_FEATURECTRL_UTOPIA_PHY_LIMIT),
    (0x1<<IX_FEATURECTRL_UTOPIA_PHY_LIMIT_BIT2),
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE,
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE,
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE,
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE,
    IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE
};
#elif defined (__ixp46X)
UINT32 componentMask[IX_FEATURECTRL_MAX_COMPONENTS] = {
    (0x1<<IX_FEATURECTRL_RCOMP),
    (0x1<<IX_FEATURECTRL_USB),
    (0x1<<IX_FEATURECTRL_HASH),
    (0x1<<IX_FEATURECTRL_AES),
    (0x1<<IX_FEATURECTRL_DES),
    (0x1<<IX_FEATURECTRL_HDLC),
    IX_FEATURECTRL_COMPONENT_ALWAYS_AVAILABLE,  /* AAL component is always on */
    (0x1<<IX_FEATURECTRL_HSS),
    (0x1<<IX_FEATURECTRL_UTOPIA),
    (0x1<<IX_FEATURECTRL_ETH0),
    (0x1<<IX_FEATURECTRL_ETH1),
    (0x1<<IX_FEATURECTRL_NPEA),
    (0x1<<IX_FEATURECTRL_NPEB),
    (0x1<<IX_FEATURECTRL_NPEC),
    (0x1<<IX_FEATURECTRL_PCI),
    (0x1<<IX_FEATURECTRL_ECC_TIMESYNC),
    (0x3<<IX_FEATURECTRL_UTOPIA_PHY_LIMIT),
    (0x1<<IX_FEATURECTRL_UTOPIA_PHY_LIMIT_BIT2), /* NOT TO BE USED */
    (0x1<<IX_FEATURECTRL_USB_HOST_CONTROLLER),
    (0x1<<IX_FEATURECTRL_NPEA_ETH),
    (0x1<<IX_FEATURECTRL_NPEB_ETH),
    (0x1<<IX_FEATURECTRL_RSA),
    (0x3<<IX_FEATURECTRL_XSCALE_MAX_FREQ),
    (0x1<<IX_FEATURECTRL_XSCALE_MAX_FREQ_BIT2)
};
#endif /* __ixp42X */

/**
 * Forward declaration
 */
PRIVATE
void ixFeatureCtrlExpMap(void);

PRIVATE 
void ixFeatureCtrlSwConfigurationInit(void);

/**
 * Function to map EXP_CONFIG space
 */
PRIVATE
void ixFeatureCtrlExpMap(void)
{
    UINT32 expCfgBaseAddress = 0;

    /* If the EXP Configuration space has already been mapped then
     * return */
    if (ixFeatureCtrlExpCfgRegionMapped == TRUE)
    {
	return;
    }

    /* Map (get virtual address) for the EXP_CONFIG space */
    expCfgBaseAddress = (UINT32)
	(IX_OSAL_MEM_MAP(IX_OSAL_IXP400_EXP_BUS_REGS_PHYS_BASE,
			   IX_OSAL_IXP400_EXP_REG_MAP_SIZE));

    /* Assert that the mapping operation succeeded */
    IX_OSAL_ASSERT(expCfgBaseAddress);

    /* Set the address of the Feature register */
    ixFeatureCtrlRegister =
	(VUINT32 *) (expCfgBaseAddress + IX_FEATURE_CTRL_REG_OFFSET);

    /* Mark the fact that the EXP_CONFIG space has already been mapped */
    ixFeatureCtrlExpCfgRegionMapped = TRUE;
}

/**
 * Function definition: ixFeatureCtrlSwConfigurationInit
 * This function will only initialize software configuration once.
 */
PRIVATE void ixFeatureCtrlSwConfigurationInit(void)
{
  UINT32 i;
  if (FALSE == swConfigurationFlag)
  {
    for (i=0; i<IX_FEATURECTRL_SWCONFIG_MAX ; i++)
    {
        /* By default, all software configuration are enabled */
        swConfiguration[i]= TRUE ;
    }
    /*Make sure this function only initializes swConfiguration[] once*/
    swConfigurationFlag = TRUE ;
  }  
}

/**
 * Function definition: ixFeatureCtrlRead
 */
IxFeatureCtrlReg 
ixFeatureCtrlRead (void)
{
    IxFeatureCtrlReg result;

#if CPU!=SIMSPARCSOLARIS
    /* Read the feature control register */
    IX_FEATURE_CTRL_READ(result);
    return result;
#else
    /* Return an invalid value for VxWorks simulation */
    result = 0xFFFFFFFF;
    return result;
#endif
}

/**
 * Function definition: ixFeatureCtrlWrite
 */
void
ixFeatureCtrlWrite (IxFeatureCtrlReg expUnitReg)
{
#if CPU!=SIMSPARCSOLARIS
    /* Write value to feature control register */
    IX_FEATURE_CTRL_WRITE(expUnitReg);
#endif
}


/**
 * Function definition: ixFeatureCtrlHwCapabilityRead
 */
IxFeatureCtrlReg
ixFeatureCtrlHwCapabilityRead (void)
{ 
  IxFeatureCtrlReg currentReg, hwCapability;
  
  /* Capture a copy of feature control register */
  currentReg = ixFeatureCtrlRead(); 

  /* Try to enable all hardware components. 
   * Only software disable hardware can be enabled again */
  ixFeatureCtrlWrite(0);
  
  /* Read feature control register to know the hardware capability. */ 
  hwCapability = ixFeatureCtrlRead();
     
  /* Restore initial feature control value */
  ixFeatureCtrlWrite(currentReg);

  /* return Hardware Capability */
  return hwCapability;  
}


/**
 * Function definition: ixFeatureCtrlComponentCheck
 */
IX_STATUS 
ixFeatureCtrlComponentCheck (IxFeatureCtrlComponentType componentType)
{
  IxFeatureCtrlReg expUnitReg; 
  UINT32 mask = 0;

  /* Lookup mask of component */
  mask=componentMask[componentType];

  /* Check if mask is available or not */
  if(IX_FEATURECTRL_COMPONENT_NOT_AVAILABLE == mask)
  {
      return IX_FEATURE_CTRL_COMPONENT_DISABLED;
  }

  if(IX_FEATURECTRL_COMPONENT_ALWAYS_AVAILABLE == mask)
  {
      return IX_FEATURE_CTRL_COMPONENT_ENABLED;
  }

  /* Read feature control register to know current hardware capability. */ 
  expUnitReg = ixFeatureCtrlRead();

  /* For example: To check for Hashing Coprocessor (bit-2) 
   *                   expUniteg    = 0x0010
   *                  ~expUnitReg   = 0x1101 
   *                  componentType = 0x0100
   *    ~expUnitReg & componentType = 0x0100 (Not zero)                      
   */
 
  /* 
   * Inverse the bit value because available component is 0 in value 
   */
  expUnitReg = ~expUnitReg ;

  if (expUnitReg & mask)
  {
     return (IX_FEATURE_CTRL_COMPONENT_ENABLED);
  }   
  else
  {  
     return (IX_FEATURE_CTRL_COMPONENT_DISABLED);
  } 
}


/**
 * Function definition: ixFeatureCtrlProductIdRead
 */
IxFeatureCtrlProductId
ixFeatureCtrlProductIdRead ()
{
#if CPU!=SIMSPARCSOLARIS
  IxFeatureCtrlProductId  pdId = 0 ;
   
  /* Use ARM instruction to move register0 from coprocessor to ARM register */ 
    
#ifndef __wince
    __asm__("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(pdId) :);
#else
      
#ifndef IN_KERNEL
        BOOL  mode;
#endif
    extern  IxFeatureCtrlProductId AsmixFeatureCtrlProductIdRead();
    
#ifndef IN_KERNEL
    mode = SetKMode(TRUE);
#endif
    pdId = AsmixFeatureCtrlProductIdRead();
#ifndef IN_KERNEL
    SetKMode(mode);
#endif

#endif
  return (pdId);
#else
  /* Return an invalid value for VxWorks simulation */
  return 0xffffffff;
#endif
}

/**
 * Function definition: ixFeatureCtrlDeviceRead
 */
IxFeatureCtrlDeviceId
ixFeatureCtrlDeviceRead ()
{
  return ((ixFeatureCtrlProductIdRead() >> IX_FEATURE_CTRL_DEVICE_TYPE_OFFSET) 
             & IX_FEATURE_CTRL_DEVICE_TYPE_MASK);
} /* End function ixFeatureCtrlDeviceRead */


/**
 * Function definition: ixFeatureCtrlSwConfigurationCheck
 */
IX_STATUS
ixFeatureCtrlSwConfigurationCheck (IxFeatureCtrlSwConfig swConfigType)
{
  if (swConfigType >= IX_FEATURECTRL_SWCONFIG_MAX)  
  {
     ixOsalLog(IX_OSAL_LOG_LVL_WARNING, 
               IX_OSAL_LOG_DEV_STDOUT,
               "FeatureCtrl: Invalid software configuraiton input.\n",
               0, 0, 0, 0, 0, 0);  

     return IX_FEATURE_CTRL_SWCONFIG_DISABLED;
  }

  /* The function will only initialize once. */
  ixFeatureCtrlSwConfigurationInit();
  
  /* Check and return software configuration */
  return  ((swConfiguration[(UINT32)swConfigType] == TRUE) ? IX_FEATURE_CTRL_SWCONFIG_ENABLED: IX_FEATURE_CTRL_SWCONFIG_DISABLED);
}

/**
 * Function definition: ixFeatureCtrlSwConfigurationWrite
 */
void
ixFeatureCtrlSwConfigurationWrite (IxFeatureCtrlSwConfig swConfigType, BOOL enabled)
{
  if (swConfigType >= IX_FEATURECTRL_SWCONFIG_MAX)  
  {
     ixOsalLog(IX_OSAL_LOG_LVL_WARNING, 
               IX_OSAL_LOG_DEV_STDOUT,
               "FeatureCtrl: Invalid software configuraiton input.\n",
               0, 0, 0, 0, 0, 0);  

     return;
  }

  /* The function will only initialize once. */
  ixFeatureCtrlSwConfigurationInit();
  
  /* Write software configuration */
  swConfiguration[(UINT32)swConfigType]=enabled ;
}

/**
 * Function definition: ixFeatureCtrlIxp400SwVersionShow
 */
void
ixFeatureCtrlIxp400SwVersionShow (void)
{
    printf ("\nIXP400 Software Release %s %s\n\n", IX_VERSION_ID, IX_VERSION_INTERNAL_ID);

}

/**
 * Function definition: ixFeatureCtrlSoftwareBuildGet
 */
IxFeatureCtrlBuildDevice
ixFeatureCtrlSoftwareBuildGet (void)
{
    #ifdef __ixp42X
    return IX_FEATURE_CTRL_SW_BUILD_IXP42X;
    #else
    return IX_FEATURE_CTRL_SW_BUILD_IXP46X;
    #endif
}
