#include <boot_ioc.h>

#ifndef SOCLIB_IOC

#ifndef SYSCLK_FREQ
#warning "Using default value for SYSCLK_FREQ = 50000000"
#define SYSCLK_FREQ 50000000U
#endif

static struct sdcard_dev  _sdcard_device;
static struct spi_dev   * _spi_device   = ( struct spi_dev * )IOC_BASE;
#endif


int boot_ioc_init()
{
#ifdef SOCLIB_IOC
    return 0;
#else
    unsigned char sdcard_rsp;

    boot_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
     */
    if ( (sdcard_rsp = sdcard_dev_open(&_sdcard_device, _spi_device, 0)) )
        return sdcard_rsp;

    if ( (sdcard_rsp = sdcard_dev_set_blocklen(&_sdcard_device, 512)) )
        return sdcard_rsp;

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

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

    return 0;
#endif
}

/**
 * _boot_ioc_completed()
 *
 * This blocking function checks completion of an I/O transfer and reports errors.
 *
 * \note It returns 0 if the transfer is successfully completed.
 *       It returns -1 if an error has been reported.
 */
#ifdef SOCLIB_IOC
static int _boot_ioc_completed()
{
    unsigned int status = 0;


    unsigned int * ioc_address = ( unsigned int * )VCIBD_BASE;
  
    while ( 1 )
    { 
        status = ioread32(&ioc_address[BLOCK_DEVICE_STATUS]);

        if (( status == BLOCK_DEVICE_READ_SUCCESS ) ||
            ( status == BLOCK_DEVICE_READ_ERROR  ))
        break;
    }
    
    return status;
}
#endif

/**
 * boot_ioc_read()
 * 
 * Transfer data from a file on the block device 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
 *
 * \note This is a blocking function. The function returns once the transfer
 *       has finished
 */

#ifdef SOCLIB_IOC
///////////////////////////////////////////////////////////////////////////////
// SOCLIB version of the boot_ioc_read function

int boot_ioc_read(unsigned int lba, void* buffer, unsigned int count)
{

    unsigned int * ioc_address  = (unsigned int*)VCIBD_BASE;

    // block_device configuration
    iowrite32( &ioc_address[BLOCK_DEVICE_BUFFER],
            ( unsigned int ) buffer );

    iowrite32( &ioc_address[BLOCK_DEVICE_COUNT],
            ( unsigned int ) count );

    iowrite32( &ioc_address[BLOCK_DEVICE_LBA],
            ( unsigned int ) lba );

    iowrite32( &ioc_address[BLOCK_DEVICE_IRQ_ENABLE],
            ( unsigned int ) 0 );

    iowrite32( &ioc_address[BLOCK_DEVICE_OP],
            ( unsigned int ) BLOCK_DEVICE_READ );

    _boot_ioc_completed();

#if (CACHE_COHERENCE == 0)
    boot_dbuf_invalidate(buffer, CACHE_LINE_SIZE, count * 512);
#endif
    return 0;
}

#else
///////////////////////////////////////////////////////////////////////////////
// FPGA version of the boot_ioc_read function

int boot_ioc_read(unsigned int lba, void* buffer, unsigned int count)
{
    unsigned int sdcard_rsp;

    sdcard_dev_lseek(&_sdcard_device, lba);

    unsigned int i;
    for(i = 0; i < count; i++)
    {
        if (( sdcard_rsp = sdcard_dev_read (
                        &_sdcard_device,
                        (unsigned char *) buffer + (512 * i),
                        512
                        ) 
            ))
        {
            boot_puts("ERROR during read on the SDCARD device. Code: "); 
            boot_putx(sdcard_rsp);
            boot_puts("\n\r");

            return 1;
        }   
    }

    return 0;
}
#endif

/**
 * _dcache_buf_invalidate()
 *
 * Invalidate all data cache lines corresponding to a memory 
 * buffer (identified by an address and a size).
 */
#if (CACHE_COHERENCE == 0)
void boot_dbuf_invalidate (
        const void * buffer,
        unsigned int line_size,
        unsigned int size)
{
    unsigned int i;

    // iterate on cache lines 
    for (i = 0; i < size; i += line_size) {
        asm volatile(
            " cache %0, %1"
            :// no outputs
            :"i" (0x11), "R" (*((unsigned char *) buffer + i))
            );
    }
}
#endif

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