#include <reset_ioc.h>

#if USE_SPI
static struct sdcard_dev     _sdcard_device;
static struct spi_dev *const _spi_device = (struct spi_dev*) IOC_PADDR_BASE;
#endif

#define SDCARD_RESET_ITER_MAX   4

///////////////////////////////////
inline void reset_sleep(int cycles)
{
    int i;
    for (i = 0; i < cycles; i++);
}

#if USE_SPI
///////////////////////////////////////////////////////////////////////////////
//     reset_ioc_init
// This function initializes the SDCARD / required for FPGA.
///////////////////////////////////////////////////////////////////////////////
int reset_ioc_init()
{
    unsigned char sdcard_rsp;

    reset_puts("Initializing block device\n\r");

    /**
     * Initializing the SPI controller
     */
    spi_dev_config (
      _spi_device   ,
      200000        , /**< SPI_clk: 200 Khz */
      SYSCLK_FREQ   , /**< Sys_clk          */
      8             , /**< Charlen: 8       */
      SPI_TX_NEGEDGE,
      SPI_RX_POSEDGE
    );

    /**
     * Initializing the SD Card
     */
    unsigned int iter = 0;
    while(1)
    {
        reset_puts("Trying to initialize SD card... ");

        sdcard_rsp = sdcard_dev_open(&_sdcard_device, _spi_device, 0);
        if (sdcard_rsp == 0)
        {
            reset_puts("OK\n");
            break;
        }

        reset_puts("KO\n");
        reset_sleep(1000);
        if (++iter >= SDCARD_RESET_ITER_MAX)
        {
            reset_puts("\nERROR: During SD card reset to IDLE state\n"
                      "/ card response = ");
            reset_putx(sdcard_rsp);
            reset_puts("\n");
            reset_exit();
        }
    }

    /**
     * Set the block length of the SD Card
     */
    sdcard_rsp = sdcard_dev_set_blocklen(&_sdcard_device, 512);
    if (sdcard_rsp)
    {
        reset_puts("ERROR: During SD card blocklen initialization\n");
        reset_exit();
    }

    /**
     * Incrementing SDCARD clock frequency for normal function
     */
    spi_dev_config (
        _spi_device ,
        10000000    , /**< SPI_clk 10 Mhz */
        SYSCLK_FREQ , /**< Sys_clk        */
        -1          , /**< Charlen: 8     */
        -1          ,
        -1
    );

    reset_puts("Finish block device initialization\n\r");

    return 0;
} // end reset_ioc_init()
#endif 

//////////////////////////////////////////////////////////////////////////////
// reset_bdv_read()
/////////////////////////////////////////////////////////////////////////////
#if USE_BDV
int reset_bdv_read( unsigned int lba, 
                    void*        buffer, 
                    unsigned int count )
{
    unsigned int * ioc_address = (unsigned int*)IOC_PADDR_BASE;

    // block_device configuration
    iowrite32( &ioc_address[BLOCK_DEVICE_BUFFER], (unsigned int) buffer );
    iowrite32( &ioc_address[BLOCK_DEVICE_COUNT], count );
    iowrite32( &ioc_address[BLOCK_DEVICE_LBA], lba );
    iowrite32( &ioc_address[BLOCK_DEVICE_IRQ_ENABLE], 0 );

    // block_device trigger transfer
    iowrite32( &ioc_address[BLOCK_DEVICE_OP], ( unsigned int )
               BLOCK_DEVICE_READ );

    unsigned int status = 0;
    while ( 1 )
    {
        status = ioread32(&ioc_address[BLOCK_DEVICE_STATUS]);
        if ( status == BLOCK_DEVICE_READ_SUCCESS )
        {
            break;
        }
        if ( status == BLOCK_DEVICE_READ_ERROR   ) {
            reset_puts("ERROR during read on the BLK device\n");
            return 1;
        }
    }
#if (CACHE_COHERENCE == 0) || USE_IOB
    reset_buf_invalidate(buffer, CACHE_LINE_SIZE, count * 512);
#endif
    return 0;
}
#endif

//////////////////////////////////////////////////////////////////////////////
// reset_spi_read()
/////////////////////////////////////////////////////////////////////////////
#if USE_SPI
static int reset_spi_read( unsigned int lba, 
                           void*        buffer, 
                           unsigned int count )
{
    unsigned int sdcard_rsp;
    unsigned int i;

    sdcard_dev_lseek(&_sdcard_device, lba);
    for(i = 0; i < count; i++)
    {
        unsigned char* buf = (unsigned char *) buffer + (512 * i);
        if (( sdcard_rsp = sdcard_dev_read ( &_sdcard_device, buf, 512 ) ))
        {
            reset_puts("ERROR during read on the SDCARD device. Code: ");
            reset_putx(sdcard_rsp);
            reset_puts("\n");
            return 1;
        }
    }
    return 0;
}
#endif

//////////////////////////////////////////////////////////////////////////////
// reset_rdk_read()
/////////////////////////////////////////////////////////////////////////////
#if USE_RDK
static int reset_rdk_read( unsigned int lba,
                           void*        buffer,
                           unsigned int count )
{
    unsigned int* rdk_address = (unsigned int*) RDK_PADDR_BASE;
    char* src = (char*) rdk_address + (lba * 512);

    memcpy(buffer, (void*) src, count * 512);
    return 0;
}
#endif

///////////////////////////////////////////////////////////////////////////////
//      reset_ioc_read()
// Transfer data from disk to a memory buffer
// - param lba    : first block index on the disk
// - param buffer : base address of the memory buffer
// - param count  : number of blocks to be transfered
// This is a blocking function. The function returns once the transfer is
// completed.
//
// The USE_BDV, USE_SPI and USE_RDK variables signal if the disk is accessed
// through a BLOCK DEVICE, SPI or RAMDISK respectively
///////////////////////////////////////////////////////////////////////////////
int reset_ioc_read( unsigned int lba, 
                    void*        buffer, 
                    unsigned int count )
{
#if USE_BDV
    return reset_bdv_read(lba, buffer, count);
#elif USE_SPI
    return reset_spi_read(lba, buffer, count);
#elif USE_RDK
    return reset_rdk_read(lba, buffer, count);
#else
#   error "reset_ioc_read() : No supported disk controller chosen"
#endif
}

/*
 * vim: tabstop=4 : shiftwidth=4 : expandtab
 */
