/******************************************************************************
 *
 * Copyright 2013 Altera Corporation. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

#include "alt_spi.h"
#include "alt_reset_manager.h"
#include "socal/alt_rstmgr.h"

// Timeout for reset manager
#define  ALT_SPI_RESET_TMO_INIT      8192

// Maximum value of SPI Clock Divider
#define  ALT_SPI_MAX_CLK_DIV         65534
// Minimum value of SPI Clock Divider
#define  ALT_SPI_MIN_CLK_DIV         2
// Timeout for waiting interrupt
#define  ALT_SPI_TMO_WAITER          2500000

/////

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

/////

//
// Check whether spi space is correct.
//
static ALT_STATUS_CODE alt_spi_checking(ALT_SPI_DEV_t * spi_dev)

{
    if (!spi_dev)
    {
        //Incorrect pointer
        return ALT_E_FALSE;
    }

    if (   spi_dev->location != (void *)ALT_SPI_SPIS0
        && spi_dev->location != (void *)ALT_SPI_SPIS1
        && spi_dev->location != (void *)ALT_SPI_SPIM0
        && spi_dev->location != (void *)ALT_SPI_SPIM1)
    {
        // Incorrect device
        return ALT_E_FALSE;
    }

    
    return ALT_E_TRUE;
}

//
// Initialize the specified SPI controller instance for use and return a device
// handle referencing it.
//
ALT_STATUS_CODE alt_spi_init(const ALT_SPI_CTLR_t spi,
                             ALT_SPI_DEV_t * spi_dev)

{
    if (!spi_dev || spi == 0)
    {
        return ALT_E_BAD_ARG;
    }

    //Save spi start address to the instance
    spi_dev->location = (void *)spi;
    
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (alt_clk_is_enabled(ALT_CLK_SPI_M) != ALT_E_TRUE)
    {
        return ALT_E_BAD_CLK;
    }

    // Define operation mode
    if  (   spi_dev->location == (void *)ALT_SPI_SPIM0
         || spi_dev->location == (void *)ALT_SPI_SPIM1)
    {
        spi_dev->op_mode = ALT_SPI_OP_MODE_MASTER;
    }
    else if (   spi_dev->location == (void *)ALT_SPI_SPIS0
             || spi_dev->location == (void *)ALT_SPI_SPIS1)
    {
        spi_dev->op_mode = ALT_SPI_OP_MODE_SLAVE;
    }

    /////

    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (status == ALT_E_SUCCESS)
    {
        // Get spi clock frequence
        status = alt_clk_freq_get(ALT_CLK_SPI_M, &spi_dev->clock_freq);
    }

    // Reset spi module
    if (status == ALT_E_SUCCESS)
    {
        status = alt_spi_reset(spi_dev);
    }

    return status;
}

//
// Reset spi module by reset manager
//
static ALT_STATUS_CODE alt_spi_rstmgr_set(ALT_SPI_DEV_t * spi_dev)
{
    uint32_t rst_mask = ALT_RSTMGR_PERMODRST_SPIM0_SET_MSK;

    // Assert the appropriate SPI module reset signal via the Reset Manager Peripheral Reset register.
    switch ((ALT_SPI_CTLR_t)spi_dev->location)
    {
    case ALT_SPI_SPIM0:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIM0_SET_MSK;
        break;
    case ALT_SPI_SPIM1:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIM1_SET_MSK;
        break;
    case ALT_SPI_SPIS0:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIS0_SET_MSK;
        break;
    case ALT_SPI_SPIS1:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIS1_SET_MSK;
        break;
    default:
        return ALT_E_BAD_ARG;
    }

    alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, rst_mask);

    return ALT_E_SUCCESS;
}

//
// Reset spi module by reset manager
//
static ALT_STATUS_CODE alt_spi_rstmgr_strobe(ALT_SPI_DEV_t * spi_dev)
{
    uint32_t rst_mask = ALT_RSTMGR_PERMODRST_SPIM0_SET_MSK;

    // Assert the appropriate SPI module reset signal via the Reset Manager Peripheral Reset register.
    switch ((ALT_SPI_CTLR_t)spi_dev->location)
    {
    case ALT_SPI_SPIM0:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIM0_SET_MSK;
        break;
    case ALT_SPI_SPIM1:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIM1_SET_MSK;
        break;
    case ALT_SPI_SPIS0:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIS0_SET_MSK;
        break;
    case ALT_SPI_SPIS1:
        rst_mask = ALT_RSTMGR_PERMODRST_SPIS1_SET_MSK;
        break;
    default:
        return ALT_E_BAD_ARG;
    }

    alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, rst_mask);

    volatile uint32_t timeout = ALT_SPI_RESET_TMO_INIT;

    // Wait while spi modure is reseting
    while (timeout--)
        ;

    // Deassert the appropriate SPI module reset signal via the Reset Manager Peripheral Reset register.
    alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, rst_mask);

    return ALT_E_SUCCESS;
}

//
// Reset spi module
//
ALT_STATUS_CODE alt_spi_reset(ALT_SPI_DEV_t * spi_dev)
{

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    ALT_STATUS_CODE    status = ALT_E_SUCCESS;
    bool already_enabled = (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE);

    if (already_enabled)
    {
        // Temporarily disable controller
        status = alt_spi_disable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
    }
    
    // Reset spi module by reset manager
    alt_spi_rstmgr_strobe(spi_dev);

    // Reset the last target address cache.
    spi_dev->last_slave_mask = 0xffffffff;
    spi_dev->last_transfer_mode = (uint32_t)(-1);

    if (already_enabled)
    {
        // Re-enable controller
        status = alt_spi_enable(spi_dev);
    }

    return status;
}


//
// Uninitialize the SPI controller referenced by the spi_dev handle.
//
ALT_STATUS_CODE alt_spi_uninit(ALT_SPI_DEV_t * spi_dev)
{
    ALT_STATUS_CODE    status = ALT_E_SUCCESS;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    // Disable spi controller
    status = alt_spi_disable(spi_dev);
    if (status != ALT_E_SUCCESS)
    {
        return status;
    }

    // Reset spi module by reset manager
    alt_spi_rstmgr_set(spi_dev);

    return status;
}


//
// Enables the SPI controller.
//
ALT_STATUS_CODE alt_spi_enable(ALT_SPI_DEV_t * spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_setbits_word(ALT_SPIM_SPIENR_ADDR(spi_dev->location),
                         ALT_SPIM_SPIENR_SPI_EN_SET_MSK);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_setbits_word(ALT_SPIS_SPIENR_ADDR(spi_dev->location),
                         ALT_SPIS_SPIENR_SPI_EN_SET_MSK);
        break;
    }
    
    return ALT_E_SUCCESS;
}

//
// Disables the SPI controller
//
ALT_STATUS_CODE alt_spi_disable(ALT_SPI_DEV_t * spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    // If spi controller is enabled, return with sucess
    if (alt_spi_is_enabled(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_SUCCESS;
    }
  
    // Else clear enable bit of spi_enable register
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_clrbits_word(ALT_SPIM_SPIENR_ADDR(spi_dev->location),
                         ALT_SPIM_SPIENR_SPI_EN_SET_MSK);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_clrbits_word(ALT_SPIS_SPIENR_ADDR(spi_dev->location),
                         ALT_SPIS_SPIENR_SPI_EN_SET_MSK);
        break;
    }

    // Clear interrapts mask and clear interrupt status
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_ALL);
    alt_spi_int_clear(spi_dev, ALT_SPI_STATUS_ALL);

    return ALT_E_SUCCESS;
}

//
// Check whether spi controller is enable
//
ALT_STATUS_CODE alt_spi_is_enabled(ALT_SPI_DEV_t * spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    ALT_STATUS_CODE en_status = ALT_E_FALSE;
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        en_status = ALT_SPIM_SPIENR_SPI_EN_GET(alt_read_word(ALT_SPIM_SPIENR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        en_status = ALT_SPIS_SPIENR_SPI_EN_GET(alt_read_word(ALT_SPIS_SPIENR_ADDR(spi_dev->location)));
        break;
    }
    return en_status;
}

ALT_STATUS_CODE alt_spi_is_busy(ALT_SPI_DEV_t *spi_dev)

{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }
   
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if (ALT_SPIM_SR_BUSY_GET(alt_read_word(ALT_SPIM_SR_ADDR(spi_dev->location))))
        {
            return ALT_E_TRUE;
        }
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if (ALT_SPIS_SR_BUSY_GET(alt_read_word(ALT_SPIS_SR_ADDR(spi_dev->location))))
        {
            return ALT_E_TRUE;
        }
        break;
    }
   
    return ALT_E_FALSE;
}

//
// Get config parameters from appropriate registers.
//
ALT_STATUS_CODE alt_spi_config_get(ALT_SPI_DEV_t *spi_dev,
                                   ALT_SPI_CONFIG_t* cfg)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !cfg)
    {
        return ALT_E_BAD_ARG;
    }

    uint32_t cfg_register;
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        cfg_register = alt_read_word(ALT_SPIM_CTLR0_ADDR(spi_dev->location));
      
        cfg->frame_size    = (ALT_SPI_DFS_t)ALT_SPIM_CTLR0_DFS_GET(cfg_register);
        cfg->frame_format  = (ALT_SPI_FRF_t)ALT_SPIM_CTLR0_FRF_GET(cfg_register);
        cfg->clk_phase     = (ALT_SPI_SCPH_t)ALT_SPIM_CTLR0_SCPH_GET(cfg_register);
        cfg->clk_polarity  = (ALT_SPI_SCPOL_t)ALT_SPIM_CTLR0_SCPOL_GET(cfg_register);
        cfg->transfer_mode = (ALT_SPI_TMOD_t)ALT_SPIM_CTLR0_TMOD_GET(cfg_register);
        cfg->loopback_mode = (bool)ALT_SPIM_CTLR0_SRL_GET(cfg_register);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        cfg_register       = alt_read_word(ALT_SPIS_CTLR0_ADDR(spi_dev->location));

        cfg->frame_size    = (ALT_SPI_DFS_t)ALT_SPIS_CTLR0_DFS_GET(cfg_register);
        cfg->frame_format  = (ALT_SPI_FRF_t)ALT_SPIS_CTLR0_FRF_GET(cfg_register);
        cfg->clk_phase     = (ALT_SPI_SCPH_t)ALT_SPIS_CTLR0_SCPH_GET(cfg_register);
        cfg->clk_polarity  = (ALT_SPI_SCPOL_t)ALT_SPIS_CTLR0_SCPOL_GET(cfg_register);
        cfg->transfer_mode = (ALT_SPI_TMOD_t)ALT_SPIS_CTLR0_TMOD_GET(cfg_register);
        cfg->slave_output_enable = (bool)ALT_SPIS_CTLR0_SLV_OE_GET(cfg_register);
        cfg->loopback_mode = (bool)ALT_SPIS_CTLR0_SRL_GET(cfg_register);
        break;
    }
    return ALT_E_SUCCESS;
}

//
// Set config parameters to appropriate registers.
//
ALT_STATUS_CODE alt_spi_config_set(ALT_SPI_DEV_t *spi_dev,
                                   const ALT_SPI_CONFIG_t* cfg)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !cfg)
    {
        return ALT_E_BAD_ARG;
    }

    if (cfg->frame_size < ALT_SPI_DFS_4BIT ||cfg->frame_size > ALT_SPI_DFS_16BIT
        || cfg->frame_format > ALT_SPI_FRF_MICROWIRE || cfg->clk_polarity > ALT_SPI_SCPOL_INACTIVE_HIGH
        || cfg->clk_phase > ALT_SPI_SCPH_TOGGLE_START)
    {
        return ALT_E_ARG_RANGE;
    }

    // Set config parameters to appropriate registers
    uint32_t cfg_register;
    uint32_t cfg_mask;
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        cfg_register = ALT_SPIM_CTLR0_DFS_SET(cfg->frame_size)
                     | ALT_SPIM_CTLR0_FRF_SET(cfg->frame_format)
                     | ALT_SPIM_CTLR0_SCPH_SET(cfg->clk_phase)
                     | ALT_SPIM_CTLR0_SCPOL_SET(cfg->clk_polarity)
                     | ALT_SPIM_CTLR0_TMOD_SET(cfg->transfer_mode)
                     | ALT_SPIM_CTLR0_SRL_SET(cfg->loopback_mode);

        cfg_mask     = ALT_SPIM_CTLR0_DFS_SET_MSK
                     | ALT_SPIM_CTLR0_FRF_SET_MSK
                     | ALT_SPIM_CTLR0_SCPH_SET_MSK
                     | ALT_SPIM_CTLR0_SCPOL_SET_MSK
                     | ALT_SPIM_CTLR0_TMOD_SET_MSK
                     | ALT_SPIM_CTLR0_SRL_SET_MSK;
                       
        alt_replbits_word(ALT_SPIM_CTLR0_ADDR(spi_dev->location), cfg_mask, cfg_register);
        break;
    case ALT_SPI_OP_MODE_SLAVE:

    cfg_register     = ALT_SPIS_CTLR0_DFS_SET(cfg->frame_size)
                     | ALT_SPIS_CTLR0_FRF_SET(cfg->frame_format)
                     | ALT_SPIS_CTLR0_SCPH_SET(cfg->clk_phase)
                     | ALT_SPIS_CTLR0_SCPOL_SET(cfg->clk_polarity)
                     | ALT_SPIS_CTLR0_TMOD_SET(cfg->transfer_mode)
                     | ALT_SPIS_CTLR0_SLV_OE_SET(cfg->slave_output_enable)
                     | ALT_SPIS_CTLR0_SRL_SET(cfg->loopback_mode);

        cfg_mask     = ALT_SPIS_CTLR0_DFS_SET_MSK
                     | ALT_SPIS_CTLR0_FRF_SET_MSK
                     | ALT_SPIS_CTLR0_SCPH_SET_MSK
                     | ALT_SPIS_CTLR0_SCPOL_SET_MSK
                     | ALT_SPIS_CTLR0_TMOD_SET_MSK
                     | ALT_SPIS_CTLR0_SLV_OE_SET_MSK
                     | ALT_SPIS_CTLR0_SRL_SET_MSK;
                       
        alt_replbits_word(ALT_SPIS_CTLR0_ADDR(spi_dev->location), cfg_mask, cfg_register);
        break;
    }

    spi_dev->last_transfer_mode = cfg->transfer_mode;

    return status;

}

//
// Get config parameters from appropriate registers for microwire mode.
//
ALT_STATUS_CODE alt_spi_mw_config_get(ALT_SPI_DEV_t *spi_dev,
                                      ALT_SPI_MW_CONFIG_t* cfg)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !cfg)
    {
        return ALT_E_BAD_ARG;
    }

    uint32_t cfg_register, mwcr_register;
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        cfg_register           = alt_read_word(ALT_SPIM_CTLR0_ADDR(spi_dev->location));
        mwcr_register          = alt_read_word(ALT_SPIM_MWCR_ADDR(spi_dev->location));
        
        cfg->ctl_frame_size    = ALT_SPIM_CTLR0_DFS_GET(cfg_register);
        cfg->mode              = (ALT_SPI_MW_MODE_t)ALT_SPIM_MWCR_MWMOD_GET(mwcr_register);
        cfg->dir               = (ALT_SPI_MW_DIR_t)ALT_SPIM_MWCR_MDD_GET(mwcr_register);
        cfg->handshake_enabled = ALT_SPIM_MWCR_MHS_GET(mwcr_register);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        cfg_register          = alt_read_word(ALT_SPIS_CTLR0_ADDR(spi_dev->location));
        mwcr_register         = alt_read_word(ALT_SPIS_MWCR_ADDR(spi_dev->location));
        
        cfg->ctl_frame_size = ALT_SPIS_CTLR0_DFS_GET(cfg_register);
        cfg->mode             = (ALT_SPI_MW_MODE_t)ALT_SPIS_MWCR_MWMOD_GET(mwcr_register);
        cfg->dir              = (ALT_SPI_MW_DIR_t)ALT_SPIS_MWCR_MDD_GET(mwcr_register);
        break;
    }
    return ALT_E_SUCCESS;
}

//
// Set config parameters to appropriate registers for microwire mode.
//
ALT_STATUS_CODE alt_spi_mw_config_set(ALT_SPI_DEV_t *spi_dev,
                                      const ALT_SPI_MW_CONFIG_t* cfg)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !cfg)
    {
        return ALT_E_BAD_ARG;
    }

    if (cfg->ctl_frame_size > ALT_SPI_MW_CTL_FRAME_SIZE_MAX
        || cfg->mode > ALT_SPI_MW_SEQUENTIAL || cfg->dir > ALT_SPI_MW_DIR_TX)
    {
        return ALT_E_ARG_RANGE;
    }

    // Set config parameters to appropriate registers
    uint32_t mwcr_register;
    uint32_t mwcr_mask;
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        mwcr_register = ALT_SPIM_MWCR_MWMOD_SET(cfg->mode)
                      | ALT_SPIM_MWCR_MDD_SET(cfg->dir)
                      | ALT_SPIM_MWCR_MHS_SET(cfg->handshake_enabled);

        mwcr_mask     = ALT_SPIM_MWCR_MWMOD_SET_MSK
                      | ALT_SPIM_MWCR_MDD_SET_MSK
                      | ALT_SPIM_MWCR_MHS_SET_MSK;
                        
        alt_replbits_word(ALT_SPIM_MWCR_ADDR(spi_dev->location), mwcr_mask, mwcr_register);
        alt_replbits_word(ALT_SPIM_CTLR0_ADDR(spi_dev->location), 
                          ALT_SPIM_CTLR0_DFS_SET_MSK,
                          ALT_SPIM_CTLR0_DFS_SET(cfg->ctl_frame_size));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        mwcr_register = ALT_SPIS_MWCR_MWMOD_SET(cfg->mode)
                      | ALT_SPIS_MWCR_MDD_SET(cfg->dir);

        mwcr_mask     = ALT_SPIS_MWCR_MWMOD_SET_MSK
                      | ALT_SPIS_MWCR_MDD_SET_MSK;
                        
        alt_replbits_word(ALT_SPIS_MWCR_ADDR(spi_dev->location), mwcr_mask, mwcr_register);
        alt_replbits_word(ALT_SPIS_CTLR0_ADDR(spi_dev->location), 
                          ALT_SPIS_CTLR0_DFS_SET_MSK,
                          ALT_SPIS_CTLR0_DFS_SET(cfg->ctl_frame_size));
        break;
    }

    return status;

}

//
// Disable the specified SPI controller slave select output lines..
//
ALT_STATUS_CODE alt_spi_slave_select_disable(ALT_SPI_DEV_t *spi_dev,
                                             const uint32_t mask)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (mask > ALT_SPI_SLAVE_MASK_ALL)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_clrbits_word(ALT_SPIM_SER_ADDR(spi_dev->location), mask);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}

//
// Enable the specified SPI controller slave select output lines.
//
ALT_STATUS_CODE alt_spi_slave_select_enable(ALT_SPI_DEV_t *spi_dev,
                                            const uint32_t mask)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (mask > ALT_SPI_SLAVE_MASK_ALL)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_setbits_word(ALT_SPIM_SER_ADDR(spi_dev->location), mask);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}

//
// Get the configured baud rate divider value for the specified SPI controller
//
ALT_STATUS_CODE alt_spi_divider_get(ALT_SPI_DEV_t *spi_dev, uint32_t *div)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *div = ALT_SPIM_BAUDR_SCKDV_GET(alt_read_word(ALT_SPIM_BAUDR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
    default:
        return ALT_E_ERROR;
    }

    return ALT_E_SUCCESS;
}

//
// Set the baud rate divider to configure the generated sclk_out frequency
//
ALT_STATUS_CODE alt_spi_divider_set(ALT_SPI_DEV_t *spi_dev, const uint32_t div)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (spi_dev->op_mode != ALT_SPI_OP_MODE_MASTER)
    {
        return ALT_E_ERROR;
    }

    if (div < ALT_SPI_MIN_CLK_DIV || div > ALT_SPI_MAX_CLK_DIV)
    {
        return ALT_E_BAD_ARG;
    }

    /////

    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    bool already_enabled = (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE);

    if (already_enabled)
    {
        // Temporarily disable controller
        status = alt_spi_disable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
    }

    alt_replbits_word(ALT_SPIM_BAUDR_ADDR(spi_dev->location),
                      ALT_SPIM_BAUDR_SCKDV_SET_MSK,
                      ALT_SPIM_BAUDR_SCKDV_SET(div));

    if (already_enabled)
    {
        // Re-enable controller
        status = alt_spi_enable(spi_dev);
    }

    return status;
}

    
//
// Get the configured baud rate divider value for the specified SPI controller
//
ALT_STATUS_CODE alt_spi_baud_rate_get(ALT_SPI_DEV_t *spi_dev, uint32_t *div)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !div)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *div = ALT_SPIM_BAUDR_SCKDV_GET(alt_read_word(ALT_SPIM_BAUDR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}
    
//
// Set the baud rate divider to configure the generated sclk_out frequency
//
ALT_STATUS_CODE alt_spi_baud_rate_set(ALT_SPI_DEV_t *spi_dev, const uint32_t div)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE )
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if(div < ALT_SPI_MIN_CLK_DIV || div > ALT_SPI_MAX_CLK_DIV)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_replbits_word(ALT_SPIM_BAUDR_ADDR(spi_dev->location), 
                          ALT_SPIM_BAUDR_SCKDV_SET_MSK,
                          ALT_SPIM_BAUDR_SCKDV_SET(div));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;

}

//
// Return bus speed by configuration of spi controller for master mode.
//
ALT_STATUS_CODE alt_spi_speed_get(ALT_SPI_DEV_t * spi_dev,
                                  uint32_t * speed_in_hz)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    uint32_t div;

    status = alt_spi_divider_get(spi_dev, &div);
    if (status != ALT_E_SUCCESS)
    {
        return status;
    }
 
    if (div < ALT_SPI_MIN_CLK_DIV || div > ALT_SPI_MAX_CLK_DIV)
    {
        return ALT_E_BAD_ARG;
    }
        
    //<speed, Hz> = <internal clock> / <lcount>
    *speed_in_hz = spi_dev->clock_freq / div;

    return ALT_E_SUCCESS;
}

//
// Fill struct with configuration of spi controller for master mode by bus speed
//
ALT_STATUS_CODE alt_spi_speed_set(ALT_SPI_DEV_t * spi_dev,
                                  uint32_t speed_in_hz)

{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    // If speed is not standard or fast return range error
    if (speed_in_hz > spi_dev->clock_freq || speed_in_hz == 0)
    {
        return ALT_E_ARG_RANGE;
    }

    //<lcount> = <internal clock> / <speed, Hz>
    uint32_t div = spi_dev->clock_freq / speed_in_hz;

    return alt_spi_divider_set(spi_dev, div);
}

//
// Fill struct with configuration of spi controller for master mode by bus speed
//
ALT_STATUS_CODE alt_spi_speed_to_divider(ALT_SPI_DEV_t * spi_dev, 
                                         uint32_t speed_in_hz, 
                                         uint32_t * div)
    
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !div)
    {
        return ALT_E_BAD_ARG;
    }

    // If speed is not standard or fast return range error
    if (speed_in_hz > spi_dev->clock_freq || speed_in_hz == 0)
    {
        return ALT_E_ARG_RANGE;
    }
    
    // <lcount> = <internal clock> / <speed, Hz>
    *div = spi_dev->clock_freq / speed_in_hz;

    return ALT_E_SUCCESS;
}

//
// Return bus speed by configuration of spi controller for master mode.
//
ALT_STATUS_CODE alt_spi_divider_to_speed(ALT_SPI_DEV_t * spi_dev, 
                                         uint32_t * speed_in_hz, 
                                         const uint32_t * div)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !div)
    {
        return ALT_E_BAD_ARG;
    }

    if (*div < ALT_SPI_MIN_CLK_DIV || *div > ALT_SPI_MAX_CLK_DIV)
    {
        return ALT_E_BAD_ARG;
    }
    
    // <speed, Hz> = <internal clock> / <lcount>
    *speed_in_hz = spi_dev->clock_freq / *div;

    return ALT_E_SUCCESS;
}

//
// Get the current number of data frames configured for the SPI controller.
//
ALT_STATUS_CODE alt_spi_num_data_frames_get(ALT_SPI_DEV_t *spi_dev, uint32_t *num_data_frames)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !num_data_frames)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *num_data_frames = ALT_SPIM_CTLR1_NDF_GET(alt_read_word(ALT_SPIM_CTLR1_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}
   
//
// Set the number of data frames configured for the SPI controller.
//
ALT_STATUS_CODE alt_spi_num_data_frames_set(ALT_SPI_DEV_t *spi_dev, const uint32_t num_data_frames)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE )
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if(num_data_frames & ALT_SPIM_CTLR1_NDF_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_replbits_word(ALT_SPIM_CTLR1_ADDR(spi_dev->location), 
                          ALT_SPIM_CTLR1_NDF_SET_MSK,
                          ALT_SPIM_CTLR1_NDF_SET(num_data_frames));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;

}

//
// Returns the current SPI controller interrupt status conditions.
//
ALT_STATUS_CODE alt_spi_int_status_get(ALT_SPI_DEV_t *spi_dev,
                                       uint32_t *status)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !status)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *status = alt_read_word(ALT_SPIM_ISR_ADDR(spi_dev->location));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *status = alt_read_word(ALT_SPIS_ISR_ADDR(spi_dev->location));
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Returns the SPI controller raw interrupt status conditions irrespective of
// the interrupt status condition enablement state.
//
ALT_STATUS_CODE alt_spi_int_raw_status_get(ALT_SPI_DEV_t *spi_dev,
                                           uint32_t *status)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !status)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *status = alt_read_word(ALT_SPIM_RISR_ADDR(spi_dev->location));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *status = alt_read_word(ALT_SPIS_RISR_ADDR(spi_dev->location));
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Clears the specified SPI controller interrupt status conditions identified
// in the mask (for slave mode).
//
static ALT_STATUS_CODE alt_spi_slave_int_clear(ALT_SPI_DEV_t *spi_dev, const uint32_t mask)
{
    if (mask == ALT_SPI_STATUS_ALL)
    {
        alt_read_word(ALT_SPIS_ICR_ADDR(spi_dev->location));
        return ALT_E_SUCCESS;
    }
    
    // For different status clear different register
    if (mask & ALT_SPI_STATUS_TXOI)
    {
        alt_read_word(ALT_SPIS_TXOICR_ADDR(spi_dev->location));
    } 
    if (mask & ALT_SPI_STATUS_RXOI)
    {
        alt_read_word(ALT_SPIS_RXOICR_ADDR(spi_dev->location));
    }
    if (mask & ALT_SPI_STATUS_RXUI)
    {
        alt_read_word(ALT_SPIS_RXUICR_ADDR(spi_dev->location));
    }

    return ALT_E_SUCCESS;
}

//
// Clears the specified SPI controller interrupt status conditions identified
// in the mask (for master mode).
//
static ALT_STATUS_CODE alt_spi_master_int_clear(ALT_SPI_DEV_t *spi_dev, const uint32_t mask)
{
    if (mask == ALT_SPI_STATUS_ALL)
    {
        alt_read_word(ALT_SPIM_ICR_ADDR(spi_dev->location));
        return ALT_E_SUCCESS;
    }
    
    // For different status clear different register
    if (mask & ALT_SPI_STATUS_TXOI)
    {
        alt_read_word(ALT_SPIM_TXOICR_ADDR(spi_dev->location));
    }
    if (mask & ALT_SPI_STATUS_RXOI)
    {
        alt_read_word(ALT_SPIM_RXOICR_ADDR(spi_dev->location));
    }
    if (mask & ALT_SPI_STATUS_RXUI)
    {
        alt_read_word(ALT_SPIM_RXUICR_ADDR(spi_dev->location));
    }

    return ALT_E_SUCCESS;
}

//
// Clears the specified SPI controller interrupt status conditions identified
// in the mask.
//
ALT_STATUS_CODE alt_spi_int_clear(ALT_SPI_DEV_t *spi_dev, const uint32_t mask)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        status = alt_spi_master_int_clear(spi_dev, mask);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = alt_spi_slave_int_clear(spi_dev, mask);
        break;
    }
    return status;
}

//
// Disable the specified SPI controller interrupt status conditions identified in
// the mask.
//
ALT_STATUS_CODE alt_spi_int_disable(ALT_SPI_DEV_t *spi_dev, const uint32_t mask)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_clrbits_word(ALT_SPIM_IMR_ADDR(spi_dev->location), mask);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_clrbits_word(ALT_SPIS_IMR_ADDR(spi_dev->location), mask);
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Enable the specified SPI controller interrupt status conditions identified in
// the mask.
//
ALT_STATUS_CODE alt_spi_int_enable(ALT_SPI_DEV_t *spi_dev, const uint32_t mask)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_setbits_word(ALT_SPIM_IMR_ADDR(spi_dev->location), mask);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_setbits_word(ALT_SPIS_IMR_ADDR(spi_dev->location), mask);
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Returns ALT_E_TRUE when the receive FIFO is empty.
//
ALT_STATUS_CODE alt_spi_rx_fifo_is_empty(ALT_SPI_DEV_t *spi_dev)
{
    ALT_STATUS_CODE status = ALT_E_FALSE;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if (ALT_SPIM_SR_RFNE_GET(alt_read_word(ALT_SPIM_SR_ADDR(spi_dev->location))) == ALT_SPIM_SR_RFNE_E_EMPTY)
        {
            status = ALT_E_TRUE;
        }
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if (ALT_SPIS_SR_RFNE_GET(alt_read_word(ALT_SPIS_SR_ADDR(spi_dev->location))) == ALT_SPIS_SR_RFNE_E_EMPTY)
        {
            status =  ALT_E_TRUE;
        }
        break;
    }
    return status;
}

//
// Returns ALT_E_TRUE when the receive FIFO is completely full.
//
ALT_STATUS_CODE alt_spi_rx_fifo_is_full(ALT_SPI_DEV_t *spi_dev)
{
    ALT_STATUS_CODE status = ALT_E_FALSE;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if (ALT_SPIM_SR_RFF_GET(alt_read_word(ALT_SPIM_SR_ADDR(spi_dev->location))) == ALT_SPIM_SR_RFF_E_FULL)
        {
            status = ALT_E_TRUE;
        }
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if (ALT_SPIS_SR_RFF_GET(alt_read_word(ALT_SPIS_SR_ADDR(spi_dev->location))) == ALT_SPIS_SR_RFF_E_FULL)
        {
            status = ALT_E_TRUE;
        }
        break;
    }
    return status;
}

//
// Returns the number of valid entries in the receive FIFO.
//
ALT_STATUS_CODE alt_spi_rx_fifo_level_get(ALT_SPI_DEV_t *spi_dev,
                                          uint32_t *num_entries)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !num_entries)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *num_entries = ALT_SPIM_RXFLR_RXTFL_GET(alt_read_word(ALT_SPIM_RXFLR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *num_entries = ALT_SPIS_RXFLR_RXTFL_GET(alt_read_word(ALT_SPIS_RXFLR_ADDR(spi_dev->location)));
        break;
    }

    return ALT_E_SUCCESS;

}

//
// Gets the current receive FIFO threshold level value.
//
ALT_STATUS_CODE alt_spi_rx_fifo_threshold_get(ALT_SPI_DEV_t *spi_dev,
                                              uint8_t *threshold)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !threshold)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *threshold = ALT_SPIM_RXFTLR_RFT_GET(alt_read_word(ALT_SPIM_RXFTLR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *threshold = ALT_SPIS_RXFTLR_RFT_GET(alt_read_word(ALT_SPIS_RXFTLR_ADDR(spi_dev->location)));
        break;
    }

    return ALT_E_SUCCESS;

}

//
// Sets the current receive FIFO threshold level value.
//
ALT_STATUS_CODE alt_spi_rx_fifo_threshold_set(ALT_SPI_DEV_t *spi_dev,
                                              const uint8_t threshold)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_replbits_word(ALT_SPIM_RXFTLR_ADDR(spi_dev->location), 
                          ALT_SPIM_RXFTLR_RFT_SET_MSK,
                          ALT_SPIM_RXFTLR_RFT_SET(threshold));
        
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_replbits_word(ALT_SPIS_RXFTLR_ADDR(spi_dev->location), 
                          ALT_SPIS_RXFTLR_RFT_SET_MSK,
                          ALT_SPIS_RXFTLR_RFT_SET(threshold));
        
        break;
    }

    return status;
}

//
// Returns ALT_E_TRUE when the transmit FIFO is empty.
//
ALT_STATUS_CODE alt_spi_tx_fifo_is_empty(ALT_SPI_DEV_t *spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if (ALT_SPIM_SR_TFE_GET(alt_read_word(ALT_SPIM_SR_ADDR(spi_dev->location))) == ALT_SPIM_SR_TFE_E_EMPTY)
        {
            return ALT_E_TRUE;
        }
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if (ALT_SPIS_SR_TFE_GET(alt_read_word(ALT_SPIS_SR_ADDR(spi_dev->location))) == ALT_SPIS_SR_TFE_E_EMPTY)
        {
            return ALT_E_TRUE;
        }
        break;
    }

    return ALT_E_FALSE;
}

//
// Returns ALT_E_TRUE when the transmit FIFO is completely full.
//
ALT_STATUS_CODE alt_spi_tx_fifo_is_full(ALT_SPI_DEV_t *spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if (ALT_SPIM_SR_TFNF_GET(alt_read_word(ALT_SPIM_SR_ADDR(spi_dev->location))) == ALT_SPIM_SR_TFNF_E_FULL)
        {
            return ALT_E_TRUE;
        }
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if (ALT_SPIS_SR_TFNF_GET(alt_read_word(ALT_SPIS_SR_ADDR(spi_dev->location))) == ALT_SPIS_SR_TFNF_E_FULL)
        {
            return ALT_E_TRUE;
        }
        break;
    }

    return ALT_E_FALSE;
}

//
// Returns the number of valid entries in the transmit FIFO.
//
ALT_STATUS_CODE alt_spi_tx_fifo_level_get(ALT_SPI_DEV_t *spi_dev,
                                          uint32_t *num_entries)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !num_entries)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *num_entries = ALT_SPIM_TXFLR_TXTFL_GET(alt_read_word(ALT_SPIM_TXFLR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *num_entries = ALT_SPIS_TXFLR_TXTFL_GET(alt_read_word(ALT_SPIS_TXFLR_ADDR(spi_dev->location)));
        break;
    }

    return ALT_E_SUCCESS;

}


//
// Sets the current transmit FIFO threshold level value.
//
ALT_STATUS_CODE alt_spi_tx_fifo_threshold_get(ALT_SPI_DEV_t *spi_dev,
                                              uint8_t *threshold)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !threshold)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *threshold = ALT_SPIM_TXFTLR_TFT_GET(alt_read_word(ALT_SPIM_TXFTLR_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *threshold = ALT_SPIS_TXFTLR_TFT_GET(alt_read_word(ALT_SPIS_TXFTLR_ADDR(spi_dev->location)));
        break;
    }

    return ALT_E_SUCCESS;

}

//
// Sets the current transmit FIFO threshold level value.
//
ALT_STATUS_CODE alt_spi_tx_fifo_threshold_set(ALT_SPI_DEV_t *spi_dev,
                                              const uint8_t threshold)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_replbits_word(ALT_SPIM_TXFTLR_ADDR(spi_dev->location), 
                          ALT_SPIM_TXFTLR_TFT_SET_MSK,
                          ALT_SPIM_TXFTLR_TFT_SET(threshold));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_replbits_word(ALT_SPIS_TXFTLR_ADDR(spi_dev->location), 
                          ALT_SPIS_TXFTLR_TFT_SET_MSK,
                          ALT_SPIS_TXFTLR_TFT_SET(threshold));
        break;
    }

    return status;
}

//
// Get the configured Rx sample delay value.
//
ALT_STATUS_CODE alt_spi_rx_sample_delay_get(ALT_SPI_DEV_t *spi_dev, uint32_t *delay)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !delay)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *delay = ALT_SPIM_RX_SMPL_DLY_RSD_GET(alt_read_word(ALT_SPIM_RX_SMPL_DLY_ADDR(spi_dev->location)));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}

//
// Set the configured Rx sample delay value.
//
ALT_STATUS_CODE alt_spi_rx_sample_delay_set(ALT_SPI_DEV_t *spi_dev, const uint32_t delay)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_is_enabled(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        if(delay & ALT_SPIM_RX_SMPL_DLY_RSD_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_replbits_word(ALT_SPIM_RX_SMPL_DLY_ADDR(spi_dev->location), 
                          ALT_SPIM_RX_SMPL_DLY_RSD_SET_MSK,
                          ALT_SPIM_RX_SMPL_DLY_RSD_SET(delay));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        status = ALT_E_ERROR;
        break;
    }

    return status;
}

//
// Disable the transmit (Tx) FIFO DMA channel.
//
ALT_STATUS_CODE alt_spi_dma_tx_disable(ALT_SPI_DEV_t *spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    // Else clear enable bit of spi_enable register
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_clrbits_word(ALT_SPIM_DMACR_ADDR(spi_dev->location), 
                         ALT_SPIM_DMACR_TDMAE_SET_MSK);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_clrbits_word(ALT_SPIS_DMACR_ADDR(spi_dev->location), 
                         ALT_SPIS_DMACR_TDMAE_SET_MSK);
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Enable and set the transmit data level for the transmit (Tx) FIFO DMA channel.
//
ALT_STATUS_CODE alt_spi_dma_tx_enable(ALT_SPI_DEV_t *spi_dev, const uint32_t level)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_setbits_word(ALT_SPIM_DMACR_ADDR(spi_dev->location),
                         ALT_SPIM_DMACR_TDMAE_SET_MSK);
        if(level & ALT_SPIM_DMATDLR_DMATDL_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_replbits_word(ALT_SPIM_DMATDLR_ADDR(spi_dev->location), 
                          ALT_SPIM_DMATDLR_DMATDL_SET_MSK,
                          ALT_SPIM_DMATDLR_DMATDL_SET(level));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if(level & ALT_SPIS_DMATDLR_DMATDL_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_setbits_word( ALT_SPIS_DMACR_ADDR(spi_dev->location),
                          ALT_SPIS_DMACR_TDMAE_SET_MSK);
        alt_replbits_word(ALT_SPIS_DMATDLR_ADDR(spi_dev->location), 
                          ALT_SPIS_DMATDLR_DMATDL_SET_MSK,
                          ALT_SPIS_DMATDLR_DMATDL_SET(level));
        break;
    }
    
    return status;
}

//
// Disable the receive (Rx) FIFO DMA channel.
//
ALT_STATUS_CODE alt_spi_dma_rx_disable(ALT_SPI_DEV_t *spi_dev)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    // Else clear enable bit of spi_enable register
    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_clrbits_word(ALT_SPIM_DMACR_ADDR(spi_dev->location), 
                         ALT_SPIM_DMACR_RDMAE_SET_MSK);
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_clrbits_word(ALT_SPIS_DMACR_ADDR(spi_dev->location), 
                         ALT_SPIS_DMACR_RDMAE_SET_MSK);
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Enable and set the receive data level for the receive (Rx) FIFO DMA channel.
//
ALT_STATUS_CODE alt_spi_dma_rx_enable(ALT_SPI_DEV_t *spi_dev, const uint32_t level)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_setbits_word( ALT_SPIM_DMACR_ADDR(spi_dev->location),
                          ALT_SPIM_DMACR_RDMAE_SET_MSK);
        if(level & ALT_SPIM_DMARDLR_DMARDL_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_replbits_word(ALT_SPIM_DMARDLR_ADDR(spi_dev->location), 
                          ALT_SPIM_DMARDLR_DMARDL_SET_MSK,
                          ALT_SPIM_DMARDLR_DMARDL_SET(level));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        if(level & ALT_SPIS_DMARDLR_DMARDL_CLR_MSK)
        {
            status = ALT_E_BAD_ARG;
            break;
        }
        alt_setbits_word( ALT_SPIS_DMACR_ADDR(spi_dev->location),
                          ALT_SPIS_DMACR_RDMAE_SET_MSK);
        alt_replbits_word(ALT_SPIS_DMARDLR_ADDR(spi_dev->location), 
                          ALT_SPIS_DMARDLR_DMARDL_SET_MSK,
                          ALT_SPIS_DMARDLR_DMARDL_SET(level));
        break;
    }
    
    return status;
}

//
// Reads a data frame from the receive (Rx) FIFO.
//
ALT_STATUS_CODE alt_spi_rx_fifo_deq(ALT_SPI_DEV_t *spi_dev, uint16_t *data)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE || !data)
    {
        return ALT_E_BAD_ARG;
    }

    if (alt_spi_is_enabled(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_ERROR;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        *data = (ALT_SPIM_DR_DR_GET(alt_read_word(ALT_SPIM_DR_ADDR(spi_dev->location))));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        *data = (ALT_SPIS_DR_DR_GET(alt_read_word(ALT_SPIS_DR_ADDR(spi_dev->location))));
        break;
    }

    return ALT_E_SUCCESS;
}

//
// Writes a data frame to the transmit (Tx) FIFO for transmittal.
//
ALT_STATUS_CODE alt_spi_tx_fifo_enq(ALT_SPI_DEV_t *spi_dev, const uint16_t data)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (alt_spi_is_enabled(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_ERROR;
    }

    switch (spi_dev->op_mode)
    {
    case ALT_SPI_OP_MODE_MASTER:
        alt_write_word(ALT_SPIM_DR_ADDR(spi_dev->location), ALT_SPIM_DR_DR_SET(data));
        break;
    case ALT_SPI_OP_MODE_SLAVE:
        alt_write_word(ALT_SPIS_DR_ADDR(spi_dev->location), ALT_SPIS_DR_DR_SET(data));
        break;
    }

    return ALT_E_SUCCESS;
}

//
// This function writes a single data byte to the transmit FIFO. 
//
static ALT_STATUS_CODE alt_spi_transmit_error_handler(ALT_SPI_DEV_t *spi_dev, ALT_STATUS_CODE *err_status)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;
    uint32_t int_status = 0;

    status = alt_spi_int_raw_status_get(spi_dev, &int_status);

    if (status == ALT_E_TRUE && int_status != 0)
    {
        if(int_status 
            & (ALT_SPI_STATUS_TXOI
                | ALT_SPI_STATUS_RXOI
                | ALT_SPI_STATUS_RXUI))
        {
            *err_status = ALT_E_BUF_OVF;
        }
    }

    return status;
}

//
// Write or read bulk of data form master mode
//
static ALT_STATUS_CODE alt_spi_master_transfer_helper(ALT_SPI_DEV_t *spi_dev, 
                                                      size_t data_send,
                                                      size_t data_recv,
                                                      const uint16_t * tx_buf, 
                                                      uint16_t * rx_buf)
{
    ALT_STATUS_CODE    status  = ALT_E_SUCCESS;

    const uint16_t * buffer_tx = tx_buf;
    uint16_t * buffer_rx       = rx_buf;
    uint32_t timeout           = MAX(data_send, data_recv) * 10000;
    
    while (data_send > 0 || data_recv > 0)
    {
        // Error handling
        ALT_STATUS_CODE err_status = 0;
        status = alt_spi_transmit_error_handler(spi_dev, &err_status);
    
        if (status != ALT_E_SUCCESS)
        {
            break;
        }
    
        if (err_status != ALT_E_SUCCESS)
        {
            status = err_status;
            break;
        }
    
        // Top up the TX FIFO with sending data
    
        if (data_send > 0)
        {
            uint32_t level = 0;
            status = alt_spi_tx_fifo_level_get(spi_dev, &level);
            if (status != ALT_E_SUCCESS)
            {
                break;
            }
    
            uint32_t space = ALT_SPI_TX_FIFO_NUM_ENTRIES - level;
    
            space = MIN(data_send, space);
    
            for (uint32_t i = 0; i < space; ++i)
            {
                alt_write_word(ALT_SPIM_DR_ADDR(spi_dev->location), ALT_SPIM_DR_DR_SET(*buffer_tx));
                ++buffer_tx;
            }
    
            data_send -= space;
        }
    
        // Read out the resulting received data as they come in.
    
        if (data_recv > 0)
        {
            uint32_t level = 0;
            status = alt_spi_rx_fifo_level_get(spi_dev, &level);
            if (status != ALT_E_SUCCESS)
            {
                break;
            }
    
            if (level == 0)
            {
                timeout--;
                if (timeout == 0)
                {
                    status = ALT_E_TMO;
                    break;
                }
            }
    
            level = MIN(data_recv, level);
    
            for (uint32_t i = 0; i < level; ++i)
            {
                uint32_t data_read = alt_read_word(ALT_SPIM_DR_ADDR(spi_dev->location));
                *buffer_rx = (ALT_SPIM_DR_DR_GET(data_read));
                //printf("data %x\n", (unsigned int)data_read);
                ++buffer_rx;
            }
    
            data_recv -= level;
        }
    }

    return status;
}

//
// This function performs a master SPI/SSP serial master transfer operations (use by all transfer functions).
//
static ALT_STATUS_CODE alt_spi_master_transfer(ALT_SPI_DEV_t *spi_dev, 
                                               const uint32_t slave_select,
                                               const size_t num_frames,
                                               const uint16_t * tx_buf, 
                                               uint16_t * rx_buf,
                                               ALT_SPI_TMOD_t transfer_mode,
                                               const uint8_t opcode,
                                               const uint32_t eeprom_addr)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (spi_dev->op_mode == ALT_SPI_OP_MODE_SLAVE)
    {
        return ALT_E_ERROR;
    }
    
    if (num_frames == 0) 
    {
        return ALT_E_SUCCESS;
    }

    ALT_STATUS_CODE    status = ALT_E_SUCCESS;

    if(alt_spi_is_busy(spi_dev) == ALT_E_TRUE)
    {
        return ALT_E_ERROR;
    }

    // Checking necessity to update slave mask
    if (slave_select != spi_dev->last_slave_mask)
    {
        status = alt_spi_disable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
        // Update slave select mask
        alt_spi_slave_select_disable(spi_dev, ALT_SPI_SLAVE_MASK_ALL);
        status = alt_spi_slave_select_enable(spi_dev, slave_select);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }

        spi_dev->last_slave_mask = slave_select;
    }

    // Checking necessity to update transfer mode
    if (transfer_mode != spi_dev->last_transfer_mode)
    {
        status = alt_spi_disable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
        // Update transfer mode
        alt_replbits_word(ALT_SPIM_CTLR0_ADDR(spi_dev->location),
                          ALT_SPIM_CTLR0_TMOD_SET_MSK,
                          ALT_SPIM_CTLR0_TMOD_SET(transfer_mode));
        spi_dev->last_transfer_mode = transfer_mode;
    }
    
    if (transfer_mode == ALT_SPI_TMOD_RX || transfer_mode == ALT_SPI_TMOD_EEPROM)
    {
        status = alt_spi_disable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
        // Set number of data frames for read
        status = alt_spi_num_data_frames_set(spi_dev, num_frames - 1);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
    }

    // Clear interrupt status and disable interrupt mask
    alt_spi_int_clear(spi_dev, ALT_SPI_STATUS_ALL);
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_ALL);

    if (alt_spi_is_enabled(spi_dev) == ALT_E_FALSE)
    {
        status = alt_spi_enable(spi_dev);
        if (status != ALT_E_SUCCESS)
        {
            return status;
        }
    }
    
    //Enable abort and TXFIFO empty interrupt status
    alt_spi_int_enable(spi_dev, ALT_SPI_STATUS_RXOI | 
                                ALT_SPI_STATUS_RXUI | 
                                ALT_SPI_STATUS_TXOI);

    // This SPI controller requires that a read issue be performed for each byte requested.
    // Read issue takes space in the TX FIFO, which may asynchronously handling a previous request.
    
    uint32_t data_send = 0;
    uint32_t data_recv = 0;
    
    if (transfer_mode == ALT_SPI_TMOD_TX || transfer_mode == ALT_SPI_TMOD_TXRX)
    {
        data_send = num_frames;
    }
    
    if (transfer_mode == ALT_SPI_TMOD_RX   || 
        transfer_mode == ALT_SPI_TMOD_TXRX ||
        transfer_mode == ALT_SPI_TMOD_EEPROM)
    {
        data_recv = num_frames;
    }
    
    if (transfer_mode == ALT_SPI_TMOD_EEPROM)
    {
        //Send opcode and eeprom address
        alt_write_word(ALT_SPIM_DR_ADDR(spi_dev->location), ALT_SPIM_DR_DR_SET(opcode));
        alt_write_word(ALT_SPIM_DR_ADDR(spi_dev->location), ALT_SPIM_DR_DR_SET(((eeprom_addr & 0xFF00) >> 8)));
        alt_write_word(ALT_SPIM_DR_ADDR(spi_dev->location), ALT_SPIM_DR_DR_SET(eeprom_addr & 0xFF));
    }
    if (transfer_mode == ALT_SPI_TMOD_RX)
    {
        //Activate rx transfer
        alt_spi_tx_fifo_enq(spi_dev, 0);
    }
    
    // Write or read bulk of data
    status = alt_spi_master_transfer_helper(spi_dev,
                                            data_send, data_recv,
                                            tx_buf,  
                                            rx_buf);

    // Need reset for set spi bus in idle state
    if(status == ALT_E_TMO)
        alt_spi_reset(spi_dev);

    // Mask all interrupts
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_ALL);

    return status;
}

//
// This function performs a master SPI/SSP serial transmit and receive transfer.
//
ALT_STATUS_CODE alt_spi_master_tx_rx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                              const uint32_t slave_select,
                                              const size_t num_frames,
                                              const uint16_t * tx_buf, 
                                              uint16_t * rx_buf)
{
    return alt_spi_master_transfer(spi_dev, 
                                   slave_select, 
                                   num_frames,
                                   tx_buf, 
                                   rx_buf, 
                                   ALT_SPI_TMOD_TXRX, 
                                   0, 0);
}

//
// This function performs a master SPI/SSP serial transmit only transfer.
//
ALT_STATUS_CODE alt_spi_master_tx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                           const uint32_t slave_select,
                                           const size_t num_frames,
                                           const uint16_t * tx_buf)
{
    return alt_spi_master_transfer(spi_dev, 
                                   slave_select, 
                                   num_frames,
                                   tx_buf, NULL, 
                                   ALT_SPI_TMOD_TX, 
                                   0, 0);
}

//
// This function performs a master SPI/SSP serial receive only transfer.
//
ALT_STATUS_CODE alt_spi_master_rx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                           const uint32_t slave_select,
                                           const size_t num_frames,
                                           uint16_t * rx_buf)
{
    return alt_spi_master_transfer(spi_dev, 
                                   slave_select, 
                                   num_frames,
                                   NULL, 
                                   rx_buf, 
                                   ALT_SPI_TMOD_RX, 
                                   0, 0);
}

//
// This function performs a master SPI EEPROM read transfer.
//
ALT_STATUS_CODE alt_spi_master_eeprom_transfer(ALT_SPI_DEV_t *spi_dev, 
                                               const uint32_t slave_select,
                                               const uint8_t opcode,
                                               const uint16_t eeprom_addr,
                                               const size_t num_frames,
                                               uint16_t * rx_buf)
{
    return alt_spi_master_transfer(spi_dev, 
                                   slave_select, 
                                   num_frames,
                                   NULL, 
                                   rx_buf, 
                                   ALT_SPI_TMOD_EEPROM, 
                                   opcode, 
                                   eeprom_addr);
}

//
// Write or read bulk of data form slave mode
//
static ALT_STATUS_CODE alt_spi_slave_transfer_helper(ALT_SPI_DEV_t *spi_dev, 
                                              size_t data_send,
                                              size_t data_recv,
                                              const uint16_t * tx_buf, 
                                              uint16_t * rx_buf)
{
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    const uint16_t * buffer_tx = tx_buf;
    uint16_t * buffer_rx = rx_buf;
    uint32_t timeout = MAX(data_send, data_recv) * 10000;
    
    while (data_send > 0 || data_recv > 0)
    {
        // Error handling
        ALT_STATUS_CODE err_status = 0;
        status = alt_spi_transmit_error_handler(spi_dev, &err_status);
    
        if (status != ALT_E_SUCCESS)
        {
            break;
        }
    
        if (err_status != ALT_E_SUCCESS)
        {
            status = err_status;
            break;
        }
    
        // Read out the resulting received data as they come in.
        
        if (data_recv > 0)
        {
            uint32_t level = 0;
            status = alt_spi_rx_fifo_level_get(spi_dev, &level);
            if (status != ALT_E_SUCCESS)
            {
                break;
            }
        
            if (level == 0)
            {
                if (--timeout == 0)
                {
                    status = ALT_E_TMO;
                    break;
                }
            }
        
            level = MIN(data_recv, level);
        
            for (uint32_t i = 0; i < level; ++i)
            {
                *buffer_rx = (ALT_SPIS_DR_DR_GET(alt_read_word(ALT_SPIS_DR_ADDR(spi_dev->location))));
                ++buffer_rx;
            }
        
            data_recv -= level;
        }

        // Top up the TX FIFO with sending data
        
        if (data_send > 0)
        {
            uint32_t level = 0;
            status = alt_spi_tx_fifo_level_get(spi_dev, &level);
            if (status != ALT_E_SUCCESS)
            {
                break;
            }
    
            uint32_t space = ALT_SPI_TX_FIFO_NUM_ENTRIES - level;
    
            space = MIN(data_send, space);
    
            for (uint32_t i = 0; i < space; ++i)
            {
                alt_write_word(ALT_SPIS_DR_ADDR(spi_dev->location), ALT_SPIS_DR_DR_SET(*buffer_tx));
                ++buffer_tx;
            }
    
            data_send -= space;
        }
    
    }

    return status;
}
//
// This function performs a slave SPI/SSP serial slave transfer.
//
static ALT_STATUS_CODE alt_spi_slave_transfer(ALT_SPI_DEV_t *spi_dev, 
                                              const uint16_t * tx_buf, 
                                              uint16_t * rx_buf,
                                              const size_t buf_len,
                                              ALT_SPI_TMOD_t transfer_mode)
{
    if (alt_spi_checking(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_BAD_ARG;
    }

    if (spi_dev->op_mode == ALT_SPI_OP_MODE_MASTER)
    {
        return ALT_E_ERROR;
    }
    
    if (buf_len == 0) 
    {
        return ALT_E_SUCCESS;
    }

    if (alt_spi_is_enabled(spi_dev) == ALT_E_FALSE)
    {
        return ALT_E_ERROR;
    }
    
    ALT_STATUS_CODE status = ALT_E_SUCCESS;

    // Clear interrupt status and disable interrupt mask
    alt_spi_int_clear(  spi_dev,ALT_SPI_STATUS_ALL);
    alt_spi_int_disable(spi_dev,ALT_SPI_STATUS_ALL);
    // Enable abort and TXFIFO empty interrupt status
    alt_spi_int_enable( spi_dev,ALT_SPI_STATUS_RXOI | 
                                ALT_SPI_STATUS_RXUI | 
                                ALT_SPI_STATUS_TXOI);

    // This SPI controller requires that a read issue be performed for each byte requested.
    // Read issue takes space in the TX FIFO, which may asynchronously handling a previous request.
    
    uint32_t data_send = 0;
    uint32_t data_recv = 0;
    
    if (transfer_mode == ALT_SPI_TMOD_TX || transfer_mode == ALT_SPI_TMOD_TXRX)
    {
        data_send = buf_len;
    }
    
    if (transfer_mode == ALT_SPI_TMOD_RX || transfer_mode == ALT_SPI_TMOD_TXRX)
    {
        data_recv = buf_len;
    }
    
    // Write or read bulk of data
    status = alt_spi_slave_transfer_helper(spi_dev, 
                                           data_send, 
                                           data_recv,
                                           tx_buf, 
                                           rx_buf);

    // Need reset for set spi bus in idle state
    if(status == ALT_E_TMO)
        alt_spi_reset(spi_dev);

    // Mask all interrupts
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_ALL);

    return status;

}

//
// This function performs a slave SPI/SSP serial transmit and receive transfer.
//
ALT_STATUS_CODE alt_spi_slave_tx_rx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                             const uint16_t * tx_buf, 
                                             uint16_t * rx_buf,
                                             const size_t buf_len)
{
    return alt_spi_slave_transfer(spi_dev, 
                                  tx_buf, 
                                  rx_buf, 
                                  buf_len, 
                                  ALT_SPI_TMOD_TXRX);
}

//
// This function performs a slave SPI/SSP serial transmit only transfer.
//
ALT_STATUS_CODE alt_spi_slave_tx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                          const uint16_t * tx_buf,
                                          const size_t buf_len)
{
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_RXFI);
    return alt_spi_slave_transfer(spi_dev, 
                                  tx_buf, 
                                  NULL, 
                                  buf_len, 
                                  ALT_SPI_TMOD_TXRX);
}

//
// This function performs a slave SPI/SSP serial receive only transfer.
//
ALT_STATUS_CODE alt_spi_slave_rx_transfer(ALT_SPI_DEV_t *spi_dev, 
                                          uint16_t * rx_buf,
                                          const size_t buf_len)
{
    alt_spi_int_disable(spi_dev, ALT_SPI_STATUS_TXEI);
    return alt_spi_slave_transfer(spi_dev, 
                                  NULL, 
                                  rx_buf, 
                                  buf_len, 
                                  ALT_SPI_TMOD_TXRX);
}

