/**
 * \file
 *
 * \brief Serial Peripheral Interface Driver for SAMB11
 *
 * Copyright (C) 2015-2016 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * 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 Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 *
 * \asf_license_stop
 *
 */
/*
 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
 */
#include "spi.h"

/**
 * \brief Determines if the SPI module is currently synchronizing to the bus.
 *
 * This function will check if the underlying hardware peripheral module is
 * currently synchronizing across multiple clock domains to the hardware bus.
 * This function can be used to delay further operations on the module until it
 * is ready.
 *
 * \param[in]  module  SPI hardware module
 *
 * \return Synchronization status of the underlying hardware module
 * \retval true   Module synchronization is ongoing
 * \retval false  Module synchronization is not ongoing
 *
 */
static bool _spi_is_active(Spi *const spi_module)
{
	Assert(spi_module);

	return spi_module->SPI_BUS_STATUS.bit.SPI_ACTIVE;
}

/**
 * \internal Enable SPI clock.
 *
 * This function will enable SPI clock.
 *
 * \param[in]  module  Pointer to the software instance struct
 */
static void _spi_clock_enable(struct spi_module *const module)
{
	Assert(module);

	Spi *const spi_module = (module->hw);

	if (spi_module == (void *)SPI0) {
		system_clock_peripheral_enable(PERIPHERAL_SPI0_SCK_CLK);
		system_clock_peripheral_enable(PERIPHERAL_SPI0_SCK_PHASE);
		system_clock_peripheral_enable(PERIPHERAL_SPI0_IF);
		system_clock_peripheral_enable(PERIPHERAL_SPI0_CORE);
	} else if (spi_module == (void *)SPI1) {
		system_clock_peripheral_enable(PERIPHERAL_SPI1_SCK_CLK);
		system_clock_peripheral_enable(PERIPHERAL_SPI1_SCK_PHASE);
		system_clock_peripheral_enable(PERIPHERAL_SPI1_IF);
		system_clock_peripheral_enable(PERIPHERAL_SPI1_CORE);
	}
}

/**
 * \internal Disable SPI clock.
 *
 * This function will disable SPI clock.
 *
 * \param[in]  module  Pointer to the software instance struct
 */
static void _spi_clock_disable(struct spi_module *const module)
{
	Assert(module);

	Spi *const spi_module = (module->hw);

	if (spi_module == (void *)SPI0) {
		system_clock_peripheral_disable(PERIPHERAL_SPI0_SCK_CLK);
		system_clock_peripheral_disable(PERIPHERAL_SPI0_SCK_PHASE);
		system_clock_peripheral_disable(PERIPHERAL_SPI0_IF);
		system_clock_peripheral_disable(PERIPHERAL_SPI0_CORE);
	} else if (spi_module == (void *)SPI1) {
		system_clock_peripheral_disable(PERIPHERAL_SPI1_SCK_CLK);
		system_clock_peripheral_disable(PERIPHERAL_SPI1_SCK_PHASE);
		system_clock_peripheral_disable(PERIPHERAL_SPI1_IF);
		system_clock_peripheral_disable(PERIPHERAL_SPI1_CORE);
	}
}

/**
 * \internal Writes an SPI configuration to the hardware module.
 *
 * This function will write out a given configuration to the hardware module.
 * Can only be done when the module is disabled.
 *
 * \param[in]  module  Pointer to the software instance struct
 * \param[in]  config  Pointer to the configuration struct
 *
 * \return The status of the configuration
 * \retval STATUS_ERR_INVALID_ARG  If invalid argument(s) were provided
 * \retval STATUS_OK               If the configuration was written
 */
static enum status_code _spi_set_config(
		struct spi_module *const module,
		const struct spi_config *const config)
{
	Assert(module);
	Assert(config);

	Spi *const spi_module = (module->hw);

	module->mode  = config->mode;

#if CONF_SPI_MASTER_ENABLE == true
	/* Find baud value and write it */
	if (config->mode == SPI_MODE_MASTER) {
		spi_module->SPI_CLK_DIVIDER.reg = config->clock_divider;
	}
#endif

	/* Set data order */
	if (config->data_order == SPI_DATA_ORDER_LSB) {
		spi_module->SPI_CONFIGURATION.bit.LSB_FIRST_ENABLE = 0x1;
	} else {
		spi_module->SPI_CONFIGURATION.bit.LSB_FIRST_ENABLE = 0x0;
	}

	/* Set clock polarity and clock phase */
	switch(config->transfer_mode)
	{
		case SPI_TRANSFER_MODE_0:
			spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x0;
			spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x0;
			break;
		case SPI_TRANSFER_MODE_1:
			spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x1;
			spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x0;
			break;
		case SPI_TRANSFER_MODE_2:
			spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x0;
			spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x1;
			break;
		case SPI_TRANSFER_MODE_3:
			spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x1;
			spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x1;
			break;
		default:
			break;
	}

	return STATUS_OK;
}

/**
 * \brief Checks if the SPI in master mode has shifted out last data, or if the
 * master has ended the transfer in slave mode.
 *
 * This function will check if the SPI master module has shifted out last data,
 * or if the slave select pin has been drawn high by the master for the SPI
 * slave module.
 *
 * \param[in]  module  Pointer to the software instance struct
 *
 * \return Indication of whether any writes are ongoing
 * \retval true   If the SPI master module has shifted out data, or slave select
 *                has been drawn high for SPI slave
 * \retval false  If the SPI master module has not shifted out data
 */
static inline bool _spi_is_write_complete(
		Spi *const spi_module)
{
	Assert(spi_module);

	/* Check interrupt flag */
	return (spi_module->TRANSMIT_STATUS.bit.TX_FIFO_EMPTY);
}


 /**
 * \brief Checks if the SPI module is ready to write data
 *
 * This function will check if the SPI module is ready to write data.
 *
 * \param[in]  module  Pointer to the software instance struct
 *
 * \return Indication of whether the module is ready to read data or not
 * \retval true   If the SPI module is ready to write data
 * \retval false  If the SPI module is not ready to write data
 */
static inline bool _spi_is_ready_to_write(
		Spi *const spi_module)
{
	Assert(spi_module);

	/* Check interrupt flag */
	return (spi_module->TRANSMIT_STATUS.bit.TX_FIFO_NOT_FULL);
}

/**
 * \brief Checks if the SPI module is ready to read data
 *
 * This function will check if the SPI module is ready to read data.
 *
 * \param[in]  module Pointer to the software instance struct
 *
 * \return Indication of whether the module is ready to read data or not
 * \retval true   If the SPI module is ready to read data
 * \retval false  If the SPI module is not ready to read data
 */
static inline bool _spi_is_ready_to_read(
		Spi *const spi_module)
{
	Assert(spi_module);

	/* Check interrupt flag */
	return (spi_module->RECEIVE_STATUS.bit.RX_FIFO_NOT_EMPTY);
}

/**
 * \brief Initializes an SPI peripheral slave device configuration structure to default values
 *
 * This function will initialize a given SPI slave device configuration
 * structure to a set of known default values. This function should be called
 * on any new instance of the configuration structures before being modified by
 * the user application.
 *
 * The default configuration is as follows:
 *  \li Slave Select on GPIO pin 12
 *  \li Addressing not enabled
 *
 * \param[out] config  Configuration structure to initialize to default values
 */
void spi_slave_inst_get_config_defaults(
		struct spi_slave_inst_config *const config)
{
	Assert(config);

	config->ss_pin          = PIN_LP_GPIO_12;
	config->address_enabled = false;
	config->address         = 0;
}

/**
 * \brief Initializes an SPI configuration structure to default values
 *
 * This function will initialize a given SPI configuration structure to a set
 * of known default values. This function should be called on any new
 * instance of the configuration structures before being modified by the
 * user application.
 *
 * The default configuration is as follows:
 *  \li Master mode enabled
 *  \li MSB of the data is transmitted first
 *  \li Transfer mode 0
 *  \li MUX Setting 0
 *  \li Character size 8 bit
 *  \li Not enabled in sleep mode
 *  \li Receiver enabled
 *  \li Baudrate 50000
 *  \li Default pinmux settings for all pads
 *  \li Clock source 0 (26MHz)
 *  \li	Clock divider  (Formula: baud_rate = ((clock input freq/clock_divider+1)/2))
 *                                  (For Example: if clock source is CLOCK_INPUT_0 then
 *                                  ((26000000/(129+1))/2) = 100000 bps)
 *
 * \param[in,out] config  Configuration structure to initialize to default values
 */
void spi_get_config_defaults(
		struct spi_config *const config)
{
	Assert(config);

	config->mode             = SPI_MODE_MASTER;
	config->data_order       = SPI_DATA_ORDER_MSB;
	config->transfer_mode    = SPI_TRANSFER_MODE_0;
	config->clock_source     = SPI_CLK_INPUT_0;
	config->clock_divider    = 129;

	config->pin_number_pad[0] = PIN_LP_GPIO_10;
	config->pin_number_pad[1] = PIN_LP_GPIO_11;
	config->pin_number_pad[2] = PIN_LP_GPIO_12;
	config->pin_number_pad[3] = PIN_LP_GPIO_13;

	config->pinmux_sel_pad[0] = MUX_LP_GPIO_10_SPI0_SCK;
	config->pinmux_sel_pad[1] = MUX_LP_GPIO_11_SPI0_MOSI;
	config->pinmux_sel_pad[2] = MUX_LP_GPIO_12_SPI0_SSN;
	config->pinmux_sel_pad[3] = MUX_LP_GPIO_13_SPI0_MISO;
};

/**
 * \brief Attaches an SPI peripheral slave
 *
 * This function will initialize the software SPI peripheral slave, based on
 * the values of the config struct. The slave can then be selected and
 * optionally addressed by the \ref spi_select_slave function.
 *
 * \param[out] slave   Pointer to the software slave instance struct
 * \param[in]  config  Pointer to the config struct
 *
 */
void spi_attach_slave(
		struct spi_slave_inst *const slave,
		struct spi_slave_inst_config *const config)
{
	Assert(slave);
	Assert(config);

	slave->ss_pin          = config->ss_pin;
	slave->address_enabled = config->address_enabled;
	slave->address         = config->address;

	struct gpio_config config_gpio;
	gpio_get_config_defaults(&config_gpio);
	config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
	gpio_pin_set_config(slave->ss_pin, &config_gpio);

	gpio_pin_set_output_level(slave->ss_pin, true);
}

/**
 * \brief Resets the SPI module
 *
 * This function will reset the SPI module to its power on default values and
 * disable it.
 *
 * \param[in,out] module Pointer to the software instance struct
 */
void spi_reset(struct spi_module *const module)
{
	/* Sanity check arguments */
	Spi *const spi_module = (module->hw);

	/* Disable the module */
	spi_disable(module);

	/* Software reset the module */
	if(spi_module == (void *)SPI0) {
		system_peripheral_reset(PERIPHERAL_SPI0_CORE);
		system_peripheral_reset(PERIPHERAL_SPI0_IF);
	} else if (spi_module == (void *)SPI1) {
		system_peripheral_reset(PERIPHERAL_SPI1_CORE);
		system_peripheral_reset(PERIPHERAL_SPI1_IF);
	}
}

/**
 * \brief Initializes the SPI module
 *
 * This function will initialize the SPI module, based on the values
 * of the config struct.
 *
 * \param[out]  module  Pointer to the software instance struct
 * \param[in]   hw      Pointer to hardware instance
 * \param[in]   config  Pointer to the config struct
 *
 * \return Status of the initialization
 * \retval STATUS_OK               Module initiated correctly
 * \retval STATUS_ERR_DENIED       If module is enabled
 * \retval STATUS_BUSY             If module is busy resetting
 * \retval STATUS_ERR_INVALID_ARG  If invalid argument(s) were provided
 */
enum status_code spi_init(
		struct spi_module *const module,
		Spi *const hw,
		const struct spi_config *const config)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(hw);
	Assert(config);

	uint8_t idx;

	/* Initialize device instance */
	module->hw = hw;

	Spi *const spi_module = (module->hw);

	/* Check if module is enabled. */
	if (spi_module->SPI_MODULE_ENABLE.reg & SPI_MODULE_ENABLE_MASK) {
		spi_module->SPI_MODULE_ENABLE.reg = (0x0ul << SPI_MODULE_ENABLE_ENABLE_Pos);
	}

	spi_reset(module);
	_spi_clock_enable(module);

#if SPI_CALLBACK_MODE == true
	if (module->hw == SPI0) {
		_spi_instances[0] = module;
		system_register_isr(RAM_ISR_TABLE_SPIRX0_INDEX, (uint32_t)spi_rx0_isr_handler);
		system_register_isr(RAM_ISR_TABLE_SPITX0_INDEX, (uint32_t)spi_tx0_isr_handler);
	} else if (module->hw == SPI1) {
		_spi_instances[1] = module;
		system_register_isr(RAM_ISR_TABLE_SPIRX1_INDEX, (uint32_t)spi_rx1_isr_handler);
		system_register_isr(RAM_ISR_TABLE_SPITX1_INDEX, (uint32_t)spi_tx1_isr_handler);
	}
#endif

	//Program the pinmux.
	struct gpio_config config_gpio;
	gpio_get_config_defaults(&config_gpio);

	/* Set the pinmux for this spi module. */
	for(idx = 0; idx < 4; idx++) {
		if (config->pin_number_pad[idx] != PINMUX_UNUSED) {
			if (config->mode == SPI_MODE_MASTER) {
				config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
			} else if (config->mode == SPI_MODE_SLAVE) {
				config_gpio.direction = GPIO_PIN_DIR_INPUT;
			}
			gpio_pin_set_config(config->pin_number_pad[idx], &config_gpio);
			gpio_pinmux_cofiguration(config->pin_number_pad[idx], \
						(uint16_t)(config->pinmux_sel_pad[idx]));
		}
	}

	/* Set up the input clock for the module */
	spi_module->CLOCK_SOURCE_SELECT.reg = config->clock_source;

#  if CONF_SPI_MASTER_ENABLE == true
	if (config->mode == SPI_MODE_MASTER) {
		/* Set the mode in SPI master mode */
		spi_module->SPI_MASTER_MODE.reg = SPI_MODE_MASTER;
	}
#  endif

#  if CONF_SPI_SLAVE_ENABLE == true
	if (config->mode == SPI_MODE_SLAVE) {
		/* Set the mode in SPI slave mode */
		spi_module->SPI_MASTER_MODE.reg = SPI_MODE_SLAVE;
	}
#  endif

#if SPI_CALLBACK_MODE == true
	/* Temporary variables */
	uint8_t i;

	/* Initialize parameters */
	for (i = 0; i < SPI_CALLBACK_N; i++) {
		module->callback[i]        = NULL;
	}
	module->tx_buffer_ptr              = NULL;
	module->rx_buffer_ptr              = NULL;
	module->remaining_tx_buffer_length = 0x0000;
	module->remaining_rx_buffer_length = 0x0000;
	module->registered_callback        = 0x00;
	module->enabled_callback           = 0x00;
	module->status                     = STATUS_OK;
	module->dir                        = SPI_DIRECTION_IDLE;
	module->locked                     = 0;
#endif

	/* Write configuration to module and return status code */
	return _spi_set_config(module, config);
}

/**
 * \name Enable/Disable
 * @{
 */

/**
 * \brief Enables the SPI module
 *
 * This function will enable the SPI module.
 *
 * \param[in,out] module  Pointer to the software instance struct
 */
void spi_enable(struct spi_module *const module)
{
	Spi *const spi_module = (module->hw);

#if SPI_CALLBACK_MODE == true
	if(spi_module == SPI0) {
		NVIC_EnableIRQ(SPI0_RX_IRQn);
		NVIC_EnableIRQ(SPI0_TX_IRQn);
	} else if(spi_module == SPI1) {
		NVIC_EnableIRQ(SPI1_RX_IRQn);
		NVIC_EnableIRQ(SPI1_TX_IRQn);
	}
#endif

	/* Enable SPI */
	spi_module->SPI_MODULE_ENABLE.reg = SPI_MODULE_ENABLE_ENABLE;
}

/**
 * \brief Disables the SPI module
 *
 * This function will disable the SPI module.
 *
 * \param[in,out] module  Pointer to the software instance struct
 */
void spi_disable(struct spi_module *const module)
{
	Spi *const spi_module = (module->hw);

#  if SPI_CALLBACK_MODE == true
	if(spi_module == SPI0) {
		NVIC_DisableIRQ(SPI0_RX_IRQn);
		NVIC_DisableIRQ(SPI0_TX_IRQn);
	} else if(spi_module == SPI1) {
		NVIC_DisableIRQ(SPI1_RX_IRQn);
		NVIC_DisableIRQ(SPI1_TX_IRQn);
	}
#  endif

	/* Disable SPI */
	spi_module->SPI_MODULE_ENABLE.reg = (0x0ul << SPI_MODULE_ENABLE_ENABLE_Pos);
	_spi_clock_disable(module);
}

/**
 * \brief Attempt to get lock on driver instance
 *
 * This function checks the instance's lock, which indicates whether or not it
 * is currently in use, and sets the lock if it was not already set.
 *
 * The purpose of this is to enable exclusive access to driver instances, so
 * that, e.g., transactions by different services will not interfere with each
 * other.
 *
 * \param[in,out] module Pointer to the driver instance to lock
 *
 * \retval STATUS_OK   If the module was locked
 * \retval STATUS_BUSY If the module was already locked
 */
enum status_code spi_lock(struct spi_module *const module)
{
	enum status_code status;

	if (module->locked) {
		status = STATUS_BUSY;
	} else {
		module->locked = true;
		status = STATUS_OK;
	}

	return status;
}

/**
 * \brief Unlock driver instance
 *
 * This function clears the instance lock, indicating that it is available for
 * use.
 *
 * \param[in,out] module Pointer to the driver instance to lock.
 *
 * \retval STATUS_OK   If the module was locked
 * \retval STATUS_BUSY If the module was already locked
 */
void spi_unlock(struct spi_module *const module)
{
	module->locked = false;
}

/**
 * \brief Transfers a single SPI character
 *
 * This function will send a single SPI character via SPI and ignore any data
 * shifted in by the connected device. To both send and receive data, use the
 * \ref spi_transceive_wait function or use the \ref spi_read function after
 * writing a character.
 *
 * Note that this function does not handle the SS (Slave Select)
 * pin(s) in master mode; this must be handled from the user application.
 *
 * \note In slave mode, the data will not be transferred before a master
 *       initiates a transaction.
 *
 * \param[in] module   Pointer to the software instance struct
 * \param[in] tx_data  Data to transmit
 *
 * \return Status of the procedure
 * \retval STATUS_OK    If the data was written
 * \retval STATUS_BUSY  If the last write was not completed
 */
enum status_code spi_write(struct spi_module *module, uint8_t tx_data)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);

	Spi *const spi_module = (module->hw);

	/* Check if the data register has been copied to the shift register */
	if (!_spi_is_ready_to_write(spi_module)) {
		/* Data register has not been copied to the shift register, return */
		return STATUS_BUSY;
	}

	/* Write the character to the DATA register */
	spi_module->TRANSMIT_DATA.reg = tx_data & SPI_TRANSMIT_DATA_MASK;

	return STATUS_OK;
}

/**
 * \brief Reads last received SPI character
 *
 * This function will return the last SPI character shifted into the receive
 * register by the \ref spi_write function
 *
 * \note Receiver must be enabled in the configuration
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[out] rx_data  Pointer to store the received data
 *
 * \returns Status of the read operation.
 * \retval STATUS_OK            If data was read
 * \retval STATUS_ERR_IO        If no data is available
 * \retval STATUS_ERR_OVERFLOW  If the data is overflown
 */
enum status_code spi_read(
		struct spi_module *const module,
		uint8_t *rx_data)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);

	Spi *const spi_module = (module->hw);

	/* Check if data is ready to be read */
	if (!_spi_is_ready_to_read(spi_module)) {
		/* No data has been received, return */
		return STATUS_ERR_IO;
	}

	/* Return value */
	enum status_code retval = STATUS_OK;

	/* Check if data is overflown */
	if (spi_module->RECEIVE_STATUS.bit.FIFO_OVERRUN) {
		retval = STATUS_ERR_OVERFLOW;
	}

	/* Read the character from the DATA register */
	*rx_data = ((uint8_t)spi_module->RECEIVE_DATA.reg & SPI_RECEIVE_DATA_MASK);

	return retval;
}

/**
 * \brief Transceive requested amount of data to and from the SPI.
 *
 * This function will return after sending and receiving requested amount of data
 *
 * \note Receiver must be enabled in the configuration
 * \note The \ref spi_select_slave function should be called before calling
 *       this function.
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[in] tx_data   Pointer containing the data to be transmitted
 * \param[in] length    Length of data to be read
 * \param[out] rx_data  Pointer to store the received data
 *
 * \returns Status of the read operation.
 * \retval STATUS_OK            If data was read
 * \retval STATUS_ERR_IO        If no data is available
 * \retval STATUS_ERR_OVERFLOW  If the data is overflown
 */
enum status_code spi_transceive_buffer_wait(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint8_t *rx_data,
		uint16_t length)
{
	Spi *spi_module = module->hw;
	uint8_t dummy = 0;
	uint8_t skip_mosi = 0;
	uint8_t skip_miso = 0;
	uint8_t status;
	uint16_t transfer_len = 0;

	if(spi_module == 0) {
		return STATUS_ERR_NOT_INITIALIZED;
	}
	if(!tx_data) {
		tx_data = &dummy;
		*tx_data = module->tx_dummy_byte;
		skip_mosi = 1;
	} else if(!rx_data) {
		rx_data = &dummy;
		skip_miso = 1;
	} else if(length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

	/* Check for Idle */
	do {
		status = _spi_is_active(spi_module);
	}while(status);

	/* Clear all status registers */
	spi_module->RECEIVE_STATUS.reg;
	spi_module->TRANSMIT_STATUS.reg;

	/* Start transfer */
	while(transfer_len < length) {
		/* Write data to MOSI */
		while(!_spi_is_ready_to_write(spi_module));
		spi_module->TRANSMIT_DATA.reg = *tx_data;
		/* Read data shifted from MISO */
		while(!_spi_is_ready_to_read(spi_module));
		*rx_data = spi_module->RECEIVE_DATA.reg;
		transfer_len++;
		if (!skip_mosi) {
			tx_data++;
		}
		if (!skip_miso) {
			rx_data++;
		}
	}
	/* check TXFIFO is empty */
	do {
		status = _spi_is_write_complete(spi_module);
	}while(!status);

	return STATUS_OK;

}

/**
 * \brief Transceive single byte of data to and from the SPI.
 *
 * This function will return after single byte of data transceived.
 *
 * \note Receiver must be enabled in the configuration
 * \note The \ref spi_select_slave function should be called before calling
 *       this function.
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[in] tx_data   Pointer containing the data to be transmitted
 * \param[out] rx_data  Pointer to store the received data
 *
 * \returns Status of the read operation.
 * \retval STATUS_OK            If data was read
 * \retval STATUS_ERR_IO        If no data is available
 * \retval STATUS_ERR_OVERFLOW  If the data is overflown
 */
enum status_code spi_transceive_wait(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint8_t *rx_data)
{
	return spi_transceive_buffer_wait(module, tx_data, rx_data, 1);
}

/**
 * \brief Reads requested amount of data from the SPI.
 *
 * This function will return after reading requested amount of data
 *
 * \note Receiver must be enabled in the configuration
 * \note The \ref spi_select_slave function should be called before calling
 *       this function.
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[in] length    Length of data to be read
 * \param[in] dummy     Dummy byte to be sent on bus when reading data
 * \param[out] rx_data  Pointer to store the received data
 *
 * \returns Status of the read operation.
 * \retval STATUS_OK            If data was read
 * \retval STATUS_ERR_IO        If no data is available
 * \retval STATUS_ERR_OVERFLOW  If the data is overflown
 */
enum status_code spi_read_buffer_wait(
		struct spi_module *const module,
		uint8_t *rx_data,
		uint16_t length,
		uint8_t dummy)
{
	module->tx_dummy_byte = dummy;
	return spi_transceive_buffer_wait(module, NULL, rx_data, length);
}

/**
 * \brief Writes requested amount of data to the SPI.
 *
 * This function will return after writing requested amount of data
 *
 * \note The \ref spi_select_slave function should be called before calling
 *       this function.
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[in] length    length of data to be read
 * \param[out] tx_data  Pointer to buffer to be transmitted
 *
 * \returns Status of the read operation.
 * \retval STATUS_OK            If data was read
 * \retval STATUS_ERR_IO        If no data is available
 * \retval STATUS_ERR_OVERFLOW  If the data is overflown
 */
enum status_code spi_write_buffer_wait(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint16_t length)
{
	return spi_transceive_buffer_wait(module, tx_data, NULL, length);
}

/**
 * \brief Asserting/Deasserting the slave select for the corresponding slave.
 *
 * This function will assert or deassert the SS of the requested slave device.
 *
 * \param[in] module    Pointer to the software instance struct
 * \param[in] slave     Pointer containing slave instance
 * \param[in] select    Bool to select the salve or deselect
 *
 * \returns Status of the slave select operation.
 *
 * \retval STATUS_OK                If SS pin is a valid one and selected/deselected
 * \retval STATUS_ERR_INVALID_ARG   Invalid SS pin
 */
enum status_code spi_select_slave(
		struct spi_module *const module,
		struct spi_slave_inst *const slave,
		bool select)
{
	uint8_t gpio_num = slave->ss_pin;
	if(select) {
		/* ASSERT Slave select pin */
		gpio_pin_set_output_level(gpio_num, false);
	} else {
		/* DEASSERT Slave select pin */
		gpio_pin_set_output_level(gpio_num, true);
	}

	return STATUS_OK;
}