#include <boot_ioc.h>
#include <defs.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()
{
#ifndef SOCLIB_IOC
    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");
#endif

    return 0;
}

/**
 * _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
 */
int boot_ioc_read(unsigned int lba, void* buffer, unsigned int count)
{
#ifdef SOCLIB_IOC

    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();

#else
    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;
        }   
    }
    
#endif

    return 0;
}

/**
 * boot_ioc_write()
 *
 * Transfer data from a memory buffer to a file on the block_device.
 *
 * \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 The source buffer must be in user address space.
 *
 *in_reset int _ioc_write(unsigned int lba, void* buffer, unsigned int count)
 *{
 *#ifdef SOCLIB_IOC
 *
 *    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_WRITE);
 *
 *    _boot_ioc_completed();
 *
 *#else   
 *        
 *    sdcard_dev_lseek(&_sdcard_device, lba);
 *    sdcard_dev_write(&_sdcard_device, buffer, count*512);
 *
 *#endif
 *
 *    return 0;
 *}
 */

/**
 * _dcache_buf_invalidate()
 *
 * Invalidate all cache lines corresponding to a memory buffer.
 * This is used by the block_device driver.
 *
 *in_reset static void _dcache_buf_invalidate(const void * buffer, unsigned int size)
 *{
 *    unsigned int i;
 *    unsigned int dcache_line_size;
 *
 *    // retrieve dcache line size from config register (bits 12:10)
 *    asm volatile("mfc0 %0, $16, 1" : "=r" (dcache_line_size));
 *
 *    dcache_line_size = 2 << ((dcache_line_size>>10) & 0x7);
 *
 *    // iterate on lines to invalidate each one of them
 *    for ( i=0; i<size; i+=dcache_line_size )
 *        asm volatile
 *            (" mtc2 %0,     $7\n"
 *             :
 *             : "r" (*((char*)buffer+i))
 *             );
 *}
 */

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