/******************************************************************************
 *
 * Copyright 2013, Silicon Image, Inc.  All rights reserved.
 * No part of this work may be reproduced, modified, distributed, transmitted,
 * transcribed, or translated into any language or computer format, in any form
 * or by any means without written permission of
 * Silicon Image, Inc., 1140 East Arques Avenue, Sunnyvale, California 94085
 *
 *****************************************************************************/
/**
 * @file isp.c
 *
 * @brief ISP APIs
 *
 *****************************************************************************/

/***** #include statements ***************************************************/

#include "si_system_api.h"
#include "si_drv_cra_api.h"

/***** local macro definitions ***********************************************/

#define ISP_OPCODE__WREN                    0x06
#define ISP_OPCODE__CHIP_ERASE              0x60
#define ISP_OPCODE__SECTOR_ERASE            0x20
#define ISP_OPCODE__PROG                    0x02
#define ISP_OPCODE__DATA_RD                 0x03
#define ISP_OPCODE__STATUS                  0x05

#define REG_ADDR_AUTO_INC_DISABLE()         SiiDrvCraWrite8(devId, 0x000C, 0x02)              /* Auto increment mode disabled */
#define REG_ADDR_AUTO_INC_ENABLE()          SiiDrvCraWrite8(devId, 0x000C, 0x03)              /* Auto increment mode enabled  */

#define REG_ISP_DISABLE()                   SiiDrvCraWrite8(devId, 0x7030, 0x00)              /* Disable isp */
#define REG_ISP_ENABLE()                    SiiDrvCraWrite8(devId, 0x7030, 0x07)              /* Enable isp in page mode, MCU Clock/2 */
#define REG_ISP_BURST_LEN(len)              SiiDrvCraWrite8(devId, 0x708c, (uint8_t)(len-1))  /* Length of burst write */
#define REG_ISP_OPCODE_SET(opcode)          SiiDrvCraWrite8(devId, 0x7080, opcode)            /* Enable for write access */
#define REG_ISP_ADDRESS_SET(addr)           SiiDrvCraWrite24(devId, 0x7084, addr)             /* Set flash address */
#define REG_ISP_DATA_WRITE(pdata, len)      SiiDrvCraBlockWrite8(devId, 0x7088, pdata, len)   /* Burst Write data */
#define REG_ISP_DATA_READ(pdata, len)       SiiDrvCraBlockRead8(devId, 0x7088, pdata, len)    /* Burst Read data */
#define REG_ISP_STATUS_GET()                SiiDrvCraRead8(devId, 0x708a)                     /* Read status */

#define REG_TIMER_CONFIG()                  SiiDrvCraWrite8(devId, 0x00F0, 0x01)
#define REG_TIMER_INT_MASK_ENABLE1()        SiiDrvCraWrite16(devId, 0x0072, 0x0C00);
#define REG_TIMER_INT_MASK_ENABLE2()        SiiDrvCraWrite8(devId, 0x00F2, 0x01)
#define REG_TIMER_INT_STATUS_CLEAR()        SiiDrvCraWrite8(devId, 0x00F3, 0x01)
#define REG_TIMER_SET(val)					SiiDrvCraWrite32(devId, 0x00F4, val*2000)


/***** local type definitions ************************************************/

/***** local prototypes ******************************************************/

static void sTimerInit( SiiPlatformDevId_t devId );
static void sSpiClockSpeedInit( SiiPlatformDevId_t devId );

/***** local data objects ****************************************************/

/***** public functions ******************************************************/

void SiiDrvIspReadInit( SiiPlatformDevId_t devId )
{
    sSpiClockSpeedInit(devId);
}

void SiiDrvIspChipErase( SiiPlatformDevId_t devId )
{
    sSpiClockSpeedInit(devId);
    sTimerInit(devId);

    REG_ISP_OPCODE_SET(ISP_OPCODE__WREN);
    REG_ISP_OPCODE_SET(ISP_OPCODE__CHIP_ERASE);
    REG_TIMER_SET(2000);
}

void SiiDrvIspSectorErase( SiiPlatformDevId_t devId, uint32_t addr )
{
    sSpiClockSpeedInit(devId);
    sTimerInit(devId);

    REG_ISP_OPCODE_SET(ISP_OPCODE__WREN);
    REG_ISP_OPCODE_SET(ISP_OPCODE__SECTOR_ERASE);
    REG_ISP_ADDRESS_SET(addr);
    REG_TIMER_SET(300);
}

void SiiDrvIspBurstWrite( SiiPlatformDevId_t devId, uint32_t addr, const uint8_t* pData, uint16_t len )
{
    REG_ISP_BURST_LEN(len);
    REG_ISP_OPCODE_SET(ISP_OPCODE__WREN);
    REG_ISP_OPCODE_SET(ISP_OPCODE__PROG);
    REG_ISP_ADDRESS_SET(addr);
    REG_ADDR_AUTO_INC_DISABLE();
    REG_ISP_DATA_WRITE(pData, len);
    REG_ADDR_AUTO_INC_ENABLE();
    REG_TIMER_SET(1);
}

void SiiDrvIspBurstRead( SiiPlatformDevId_t devId, uint32_t addr, uint8_t* pData, uint16_t len )
{
    REG_ISP_OPCODE_SET(ISP_OPCODE__DATA_RD);
    REG_ISP_ADDRESS_SET(addr);
    REG_ADDR_AUTO_INC_DISABLE();
    REG_ISP_DATA_READ(pData, len);
    REG_ADDR_AUTO_INC_ENABLE();
}

bool_t SiiDrvIspOperationDone( SiiPlatformDevId_t devId )
{
    REG_ISP_OPCODE_SET(ISP_OPCODE__STATUS);
    return ( (REG_ISP_STATUS_GET() & 0x01) == 0x00 );
}

/***** local functions *******************************************************/

void sTimerInit( SiiPlatformDevId_t devId )
{
	REG_TIMER_INT_STATUS_CLEAR();
    REG_TIMER_INT_MASK_ENABLE1();
	REG_TIMER_INT_MASK_ENABLE2();
	REG_TIMER_CONFIG();
}

static void sSpiClockSpeedInit( SiiPlatformDevId_t devId )
{
    /* Set SPI clock speed to 30MHz */
    SiiDrvCraWrite8(devId, 0x334F, 0x33);
    SiiDrvCraWrite8(devId, 0x0085, 0x48);

    REG_ISP_DISABLE();
    REG_ISP_ENABLE();
}

/***** end of file ***********************************************************/
