text: /**
   * \file
   *
   * \brief Serial Peripheral Interface Driver for SAMB11
   *
   * Copyright (C) 2015 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_MUX2_SPI0_SCK;
  	config->pin_number_pad[1] = PIN_LP_GPIO_11_MUX2_SPI0_MOSI;
  	config->pin_number_pad[2] = PIN_LP_GPIO_12_MUX2_SPI0_SSN;
  	config->pin_number_pad[3] = PIN_LP_GPIO_13_MUX2_SPI0_MISO;
  
  	config->pinmux_sel_pad[0] = MUX_LP_GPIO_10_MUX2_SPI0_SCK;
  	config->pinmux_sel_pad[1] = MUX_LP_GPIO_11_MUX2_SPI0_MOSI;
  	config->pinmux_sel_pad[2] = MUX_LP_GPIO_12_MUX2_SPI0_SSN;
  	config->pinmux_sel_pad[3] = MUX_LP_GPIO_13_MUX2_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_SPI_MODULE_ENABLE_MASK) {
  		spi_module->SPI_MODULE_ENABLE.reg = (0x0ul << SPI_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_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_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;
  }
  