/***************************************************************************
 *
 *  Oxford Semiconductor Proprietary and Confidential Information
 *
 *  Copyright:      Oxford Semiconductor Ltd, 2007
 *
 *  Description:    Implementation of OS dependant HW access functions.
 *
 ***************************************************************************/

#include "Misc.h"
#include "OsHwAccess.h"
#include "DbgTrace.h"
#if defined(EVENT_TRACING)
/*
 *  The trace message header (.tmh) file must be included in a source file
 *  before any WPP macro calls and after defining a WPP_CONTROL_GUIDS
 *  macro (defined in trace.h). During the compilation, WPP scans the source
 *  files for DoTraceMessage() calls and builds a .tmh file which stores a unique
 *  data GUID for each message, the text resource string for each message,
 *  and the data types of the variables passed in for each message.
 *  This file is automatically generated and used during post-processing.
 */
#include "OsHwAccess.tmh"
#endif



/**
 *  Read an 8-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U08
OsRegisterIoRead08(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    unsigned int pReg;
    U08 value = 0;
    
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > offset);

    pReg = (UADDR)pRegBaseDescr->pBase + offset;
    
    value = inb(pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO RD 08:  0x%02X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Read a 16-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U16
OsRegisterIoRead16(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    unsigned int pReg;
    U16 value = 0;
    
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 1));

    pReg = (UADDR)pRegBaseDescr->pBase + offset;
    
    value = inw(pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO RD 16: 0x%04X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Read a 32-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U32
OsRegisterIoRead32(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    unsigned int pReg;
    U32 value = 0;
    
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 3));

    pReg = (UADDR)pRegBaseDescr->pBase + offset;

    value = inl(pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO RD 32: 0x%08X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Write to an 8-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterIoWrite08(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U08                               value)
{
    unsigned int pReg;

    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > offset);

    pReg = (UADDR)pRegBaseDescr->pBase + offset;
    outb(value, pReg); 
    
    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO WR 08: 0x%02X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}



/**
 *  Write to a 16-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterIoWrite16(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U16                               value)
{
    unsigned int pReg;

    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 1));

    pReg = (UADDR)pRegBaseDescr->pBase + offset;
    outw(value, pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO WR 16: 0x%04X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}



/**
 *  Write to a 32-bit wide register in I/O space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterIoWrite32(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U32                               value)
{
    unsigned int pReg;
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && !(pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 3));

    pReg = (UADDR)pRegBaseDescr->pBase + offset;
    outl(value, pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "IO WR 32: 0x%08X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}



/**
 *  Read an 8-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U08
OsRegisterMemRead08(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    void * pReg;
    U08 value = 0;
    
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > offset);

    pReg = pRegBaseDescr->pBase + offset;
    value = ioread8(pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM RD 08: 0x%02X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Read a 16-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U16
OsRegisterMemRead16(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    void * pReg;
    U16 value = 0;
    
    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 1));

    pReg = pRegBaseDescr->pBase + offset;
    value = ioread16(pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM RD 16: value 0x%04X address 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Read a 32-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @return Value read from the register.
 */
U32
OsRegisterMemRead32(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset)
{
    void * pReg;
    U32 value = 0;
    
    PATH_TESTED(); 

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 3));

    pReg = pRegBaseDescr->pBase + offset;
    value = ioread32(pReg);

    OsTrace(OS_LEVEL_VERBOSE, 
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM RD 32: value 0x%08X address 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);

    return value;
}



/**
 *  Write to an 8-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterMemWrite08(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U08                               value)
{
    void * pReg;

    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > offset);

    pReg = pRegBaseDescr->pBase + offset;
    iowrite8(value, pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM WR 08: 0x%02X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}



/**
 *  Write to a 16-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterMemWrite16(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U16                               value)
{
    void * pReg;

    PATH_NOT_YET_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 1));

    pReg = pRegBaseDescr->pBase + offset;
    iowrite16(value, pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM WR 16: 0x%04X 0x%p offset 0x%0X\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}



/**
 *  Write to a 32-bit wide register in memory space.
 * @param A register base descriptor.
 * @param A register offset in bytes.
 * @param A value to be written.
 */
void
OsRegisterMemWrite32(
    OsRegBaseDescriptor const * const pRegBaseDescr,
    size_t                            offset,
    U32                               value)
{
    void * pReg;

    PATH_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);
    PRECONDITION(pRegBaseDescr->pBase != NULL);
    PRECONDITION(!(pRegBaseDescr->hwResource.flags & IORESOURCE_IO) 
              && (pRegBaseDescr->hwResource.flags & IORESOURCE_MEM));
    PRECONDITION(pRegBaseDescr->hwResource.length > (offset + 3));

    pReg = pRegBaseDescr->pBase + offset;
    iowrite32(value, pReg);

    OsTrace(OS_LEVEL_VERBOSE,
            OS_TRACE_FLAG_HW_ACCESS,
            "MEM WR 32: 0x%08X 0x%p offset 0x%0X - disabled\n",
            value,
            pRegBaseDescr->pBase,
            offset);
}


/**
 *  Initialise a register base.
 * @param A register base descriptor.
 * @param A pointer to a partial HW resource descriptor.
 */
void
OsRegBaseInit(
    OsRegBaseDescriptor * const pRegBaseDescr,
    OsHwResource  const * const pHwResource)
{
    PATH_TESTED();

    OsDebugOutput(OS_LEVEL_CRITICAL,
                  TRACE_FLAG_FUNC_TRACE,
                  "OsRegBaseInit\n");

    PRECONDITION(pRegBaseDescr != NULL);

    if (   (pHwResource != NULL) 
        && (   (pHwResource->flags & IORESOURCE_IO) 
            || (pHwResource->flags & IORESOURCE_MEM) ) )
    {
        PATH_TESTED(); 
        pRegBaseDescr->hwResource = *pHwResource;
    }
    else
    {
        PATH_TESTED(); 
        pRegBaseDescr->hwResource.base = 0 ;
        pRegBaseDescr->hwResource.length = 0 ;
        pRegBaseDescr->hwResource.flags = 0 ;
    }

    pRegBaseDescr->pBase  = NULL;
    pRegBaseDescr->bMapped   = FALSE;

    pRegBaseDescr->pfRead08  = NULL; 
    pRegBaseDescr->pfRead16  = NULL; 
    pRegBaseDescr->pfRead32  = NULL; 
    pRegBaseDescr->pfWrite08 = NULL; 
    pRegBaseDescr->pfWrite16 = NULL; 
    pRegBaseDescr->pfWrite32 = NULL; 
}



/**
 *  Get the number of bytes occupied by a register base.
 * @param A register base descriptor.
 * @return Register base length.
 */
size_t
OsRegBaseGetLength(
    OsRegBaseDescriptor const * const pRegBaseDescr)
{
    size_t length;

    PATH_TESTED();

    PRECONDITION(pRegBaseDescr != NULL);

    length = pRegBaseDescr->hwResource.length;
    
    OsDebugOutput(OS_LEVEL_CRITICAL,
                  TRACE_FLAG_FUNC_TRACE,
                  "OsRegBaseGetLength %d\n", length);

                  return length;
}

/**
 *  Map the given register base to nonpaged system space
 *  if it is in memory (and not in I/O space).
 * @param A register base descriptor.
 * @return OS_STATUS__OK or OS_STATUS__NO_RESOURCES.
 */
OsStatus
OsRegBaseMap(
    OsRegBaseDescriptor * const pRegBaseDescr)
{
    struct resource *       resource ;
    OsStatus osStatus = OS_STATUS__NO_RESOURCES;
    
    PATH_TESTED();

    OsDebugOutput(OS_LEVEL_CRITICAL,
                  TRACE_FLAG_FUNC_TRACE,
                  "OsRegBaseMap\n");

    PRECONDITION(pRegBaseDescr != NULL);

    pRegBaseDescr->bMapped = FALSE;
    
    if ((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) != 0)
    {
        PATH_NOT_YET_TESTED();
        PRECONDITION(pRegBaseDescr->hwResource.flags & IORESOURCE_IO);
        
        resource = request_region( pRegBaseDescr->hwResource.base,
                                   pRegBaseDescr->hwResource.length,
                                   DRIVER_NAME );
        
        if (resource== NULL )
        {
            PATH_NOT_YET_TESTED();
            // request_region failed
            pRegBaseDescr->pBase  = NULL;
            pRegBaseDescr->pfRead08  = NULL;
            pRegBaseDescr->pfRead16  = NULL;
            pRegBaseDescr->pfRead32  = NULL;
            pRegBaseDescr->pfWrite08 = NULL;
            pRegBaseDescr->pfWrite16 = NULL;
            pRegBaseDescr->pfWrite32 = NULL;
        }
        else
        {
            PATH_NOT_YET_TESTED();
            pRegBaseDescr->pBase = (void *)pRegBaseDescr->hwResource.base ;
            
            pRegBaseDescr->pfRead08  = &OsRegisterIoRead08;
            pRegBaseDescr->pfRead16  = &OsRegisterIoRead16;
            pRegBaseDescr->pfRead32  = &OsRegisterIoRead32;
            pRegBaseDescr->pfWrite08 = &OsRegisterIoWrite08;
            pRegBaseDescr->pfWrite16 = &OsRegisterIoWrite16;
            pRegBaseDescr->pfWrite32 = &OsRegisterIoWrite32;
    
            osStatus = OS_STATUS__OK;
        }
        
        OsDebugOutput(OS_LEVEL_CRITICAL,
                      TRACE_FLAG_FUNC_TRACE,
                      "OsRegBaseMap IORESOURCE_IO base %p length %lu\n",
                      pRegBaseDescr->pBase,
                      pRegBaseDescr->hwResource.length);
    }
    else if ((pRegBaseDescr->hwResource.flags & IORESOURCE_MEM) != 0)
    {
        PATH_TESTED(); 
        OsDebugOutput(OS_LEVEL_CRITICAL,
                      TRACE_FLAG_FUNC_TRACE,
                      "OsRegBaseMap gDriverName = %s\n",
                      DRIVER_NAME ); 

        resource = request_mem_region( pRegBaseDescr->hwResource.base,
                                       pRegBaseDescr->hwResource.length,
                                       DRIVER_NAME ); 

        if ( resource == NULL )
        {
            PATH_NOT_YET_TESTED();
             // request_region failed
            pRegBaseDescr->pBase = NULL ;
            
            pRegBaseDescr->pfRead08  = NULL;
            pRegBaseDescr->pfRead16  = NULL;
            pRegBaseDescr->pfRead32  = NULL;
            pRegBaseDescr->pfWrite08 = NULL;
            pRegBaseDescr->pfWrite16 = NULL;
            pRegBaseDescr->pfWrite32 = NULL;
        }
        else
        {
            pRegBaseDescr->pBase = ioremap_nocache(pRegBaseDescr->hwResource.base, 
                                                   pRegBaseDescr->hwResource.length ) ;
            if (pRegBaseDescr->pBase == NULL )
            {
                PATH_NOT_YET_TESTED();
                  
                 release_mem_region(pRegBaseDescr->hwResource.base, 
                                    pRegBaseDescr->hwResource.length ) ;
            }
            PATH_TESTED();
            
            pRegBaseDescr->bMapped = TRUE;
            pRegBaseDescr->pfRead08  = &OsRegisterMemRead08;
            pRegBaseDescr->pfRead16  = &OsRegisterMemRead16;
            pRegBaseDescr->pfRead32  = &OsRegisterMemRead32;
            pRegBaseDescr->pfWrite08 = &OsRegisterMemWrite08;
            pRegBaseDescr->pfWrite16 = &OsRegisterMemWrite16;
            pRegBaseDescr->pfWrite32 = &OsRegisterMemWrite32;

            osStatus = OS_STATUS__OK;
        }
        
        OsDebugOutput(OS_LEVEL_CRITICAL,
                      TRACE_FLAG_FUNC_TRACE,
                      "OsRegBaseMap IORESOURCE_MEM base %p length %lu\n",
                      pRegBaseDescr->pBase,
                      pRegBaseDescr->hwResource.length);

    }
    else
    {
        PRECONDITION(FALSE);
    }

    return osStatus;
}



/**
 *  Unmap the given register base from nonpaged system space (if mapped).
 * @param A register base descriptor.
 */
void
OsRegBaseUnmap(
    OsRegBaseDescriptor * const pRegBaseDescr)
{
    PATH_TESTED();

    OsDebugOutput(OS_LEVEL_CRITICAL,
                  TRACE_FLAG_FUNC_TRACE,
                  "OsRegBaseUnmap\n");

    PRECONDITION(pRegBaseDescr != NULL);

    if ((pRegBaseDescr->hwResource.flags & IORESOURCE_IO) != 0)
    {
        PATH_NOT_YET_TESTED();
        PRECONDITION(!pRegBaseDescr->bMapped);
        
        release_region(pRegBaseDescr->hwResource.base,
                       pRegBaseDescr->hwResource.length);
    }
    else if ((pRegBaseDescr->hwResource.flags & IORESOURCE_MEM) != 0)
    {
        PATH_TESTED();

        if (pRegBaseDescr->bMapped)
        {
            PATH_TESTED();
            PRECONDITION(pRegBaseDescr->pBase != NULL);
            
            iounmap(pRegBaseDescr->pBase);
        }

        release_mem_region(pRegBaseDescr->hwResource.base,
                           pRegBaseDescr->hwResource.length);
    }
    else
    {
        PRECONDITION(FALSE);     
    }

    pRegBaseDescr->pBase  = NULL;
    pRegBaseDescr->bMapped   = FALSE;

    pRegBaseDescr->pfRead08  = NULL;
    pRegBaseDescr->pfRead16  = NULL;
    pRegBaseDescr->pfRead32  = NULL;
    pRegBaseDescr->pfWrite08 = NULL;
    pRegBaseDescr->pfWrite16 = NULL;
    pRegBaseDescr->pfWrite32 = NULL;
}


