/*
 * \file    spi.c
 * \data    31 August 2012
 * \author  Cesar Fuguet <cesar.fuguet-tortolero@lip6.fr>
 */
#include <spi.h>

#define in_reset __attribute__((section (".reset")))

/**
 * \param   spi :   Initialized pointer to the SPI controller
 *
 * \brief   Wait until the SPI controller has finished a transfer
 *
 * Wait until the GO_BUSY bit of the SPI controller be deasserted
 */
in_reset static void _spi_wait_if_busy(struct spi_dev * spi)
{
    volatile register int delay;

    while(SPI_IS_BUSY(spi))
    {
        for (delay = 0; delay < 10000; delay++);
    }
}

/**
 * \param   spi : Initialized pointer to the SPI controller
 *
 * \return  void
 *
 * \brief   Init transfer of the tx registers to the selected slaves
 */
in_reset static void _spi_init_transfer(struct spi_dev * spi)
{
    unsigned int spi_ctrl = ioread32(&spi->ctrl);

    iowrite32(&spi->ctrl, spi_ctrl | SPI_CTRL_GO_BSY);
}

/**
 * \param   spi_freq    : Desired frequency for the generated clock from the SPI
 *                        controller
 * \param   sys_freq    : System clock frequency
 *
 * \brief   Calculated the value for the divider register in order to obtain the SPI
 *          desired clock frequency 
 */
in_reset static unsigned int _spi_calc_divider_value  (
        unsigned int spi_freq   ,
        unsigned int sys_freq   )
{
    return ((sys_freq / (spi_freq * 2)) - 1);
}

in_reset void spi_put_tx(struct spi_dev * spi, unsigned char byte, int index)
{
    _spi_wait_if_busy(spi);
    {
        iowrite8(&spi->rx_tx[index % 4], byte);
        _spi_init_transfer(spi);

        asm volatile("sync");
    }
    _spi_wait_if_busy(spi);
}

in_reset volatile unsigned char spi_get_rx(struct spi_dev * spi, int index)
{
    return ioread8(&spi->rx_tx[index % 4]);
}

in_reset void spi_ss_assert(struct spi_dev * spi, int index)
{
    unsigned int spi_ss = ioread32(&spi->ss);

    iowrite32(&spi->ss, spi_ss | (1 << index));
}

in_reset void spi_ss_deassert(struct spi_dev * spi, int index)
{
    unsigned int spi_ss = ioread32(&spi->ss);

    iowrite32(&spi->ss, spi_ss & ~(1 << index));
}

in_reset void spi_dev_config (
        struct spi_dev * spi,
        int spi_freq     	,
        int sys_freq        ,
        int char_len		,
        int tx_edge			,
        int rx_edge			)
{
    unsigned int spi_ctrl = ioread32(&spi->ctrl);

    if      ( tx_edge == 0 ) spi_ctrl |=  SPI_CTRL_TXN_EN;
    else if ( tx_edge == 1 ) spi_ctrl &= ~SPI_CTRL_TXN_EN;
    if      ( rx_edge == 0 ) spi_ctrl |=  SPI_CTRL_RXN_EN;
    else if ( rx_edge == 1 ) spi_ctrl &= ~SPI_CTRL_RXN_EN;
    if      ( char_len > 0 ) spi_ctrl  = (spi_ctrl & ~SPI_CTRL_CHAR_LEN_MASK) |
        (char_len &  SPI_CTRL_CHAR_LEN_MASK);

    iowrite32(&spi->ctrl, spi_ctrl);

    if (spi_freq > 0 && sys_freq > 0)
        iowrite32(&spi->divider, _spi_calc_divider_value(spi_freq, sys_freq));

}

#undef in_reset

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