/**
 * \file
 *
 * \brief SAM Serial Peripheral Interface Driver
 *
 * Copyright (c) 2013-2018 Microchip Technology Inc. and its subsidiaries.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip
 * software and any derivatives exclusively with Microchip products.
 * It is your responsibility to comply with third party license terms applicable
 * to your use of third party software (including open source software) that
 * may accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * \asf_license_stop
 *
 */
/*
 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
 */
#include "spi_interrupt.h"

/**
 * \internal
 *
 * Dummy byte to send when reading in master mode.
 */
uint16_t dummy_write;

/**
 * \internal
 * Starts transceive of buffers with a given length
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[in]  rx_data  Pointer to data to be received
 * \param[in]  tx_data  Pointer to data to be transmitted
 * \param[in]  length   Length of data buffer
 *
 */
static void _spi_transceive_buffer(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint8_t *rx_data,
		uint16_t length)
{
	Assert(module);
	Assert(tx_data);

	/* Write parameters to the device instance */
	module->remaining_tx_buffer_length = length;
	module->remaining_rx_buffer_length = length;
	module->rx_buffer_ptr = rx_data;
	module->tx_buffer_ptr = tx_data;
	module->status = STATUS_BUSY;

	module->dir = SPI_DIRECTION_BOTH;

	/* Get a pointer to the hardware module instance */
	SercomSpi *const hw = &(module->hw->SPI);

	/* Enable the Data Register Empty and RX Complete Interrupt */
	hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
			SPI_INTERRUPT_FLAG_RX_COMPLETE);

#  if CONF_SPI_SLAVE_ENABLE == true
	if (module->mode == SPI_MODE_SLAVE) {
		/* Clear TXC flag if set */
		hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
		/* Enable transmit complete interrupt for slave */
		hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
	}
#  endif
}

/**
 * \internal
 * Starts write of a buffer with a given length
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[in]  tx_data  Pointer to data to be transmitted
 * \param[in]  length   Length of data buffer
 *
 */
static void _spi_write_buffer(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint16_t length)
{
	Assert(module);
	Assert(tx_data);

	/* Write parameters to the device instance */
	module->remaining_tx_buffer_length = length;
	module->remaining_dummy_buffer_length = length;
	module->tx_buffer_ptr = tx_data;
	module->status = STATUS_BUSY;

	module->dir = SPI_DIRECTION_WRITE;

	/* Get a pointer to the hardware module instance */
	SercomSpi *const hw = &(module->hw->SPI);

#  if CONF_SPI_SLAVE_ENABLE == true
	if (module->mode == SPI_MODE_SLAVE) {
		/* Clear TXC flag if set */
		hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
		/* Enable transmit complete interrupt for slave */
		hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
	}
#  endif

	if (module->receiver_enabled) {
		/* Enable the Data Register Empty and RX Complete interrupt */
		hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
				SPI_INTERRUPT_FLAG_RX_COMPLETE);
	} else {
		/* Enable the Data Register Empty interrupt */
		hw->INTENSET.reg = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
	}
}

/**
 * \internal
 * Setup SPI to read a buffer with a given length
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[in]  rx_data  Pointer to data to be received
 * \param[in]  length   Length of data buffer
 *
 */
static void _spi_read_buffer(
		struct spi_module *const module,
		uint8_t *rx_data,
		uint16_t length)
{
	Assert(module);
	Assert(rx_data);

	uint8_t tmp_intenset = 0;

	/* Set length for the buffer and the pointer, and let
	 * the interrupt handler do the rest */
	module->remaining_rx_buffer_length = length;
	module->remaining_dummy_buffer_length = length;
	module->rx_buffer_ptr = rx_data;
	module->status = STATUS_BUSY;

	module->dir = SPI_DIRECTION_READ;

	/* Get a pointer to the hardware module instance */
	SercomSpi *const hw = &(module->hw->SPI);

	/* Enable the RX Complete Interrupt */
	tmp_intenset = SPI_INTERRUPT_FLAG_RX_COMPLETE;

#  if CONF_SPI_MASTER_ENABLE == true
	if (module->mode == SPI_MODE_MASTER && module->dir == SPI_DIRECTION_READ) {
		/* Enable Data Register Empty interrupt for master */
		tmp_intenset |= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
	}
#  endif
#  if CONF_SPI_SLAVE_ENABLE == true
	if (module->mode == SPI_MODE_SLAVE) {
		/* Clear TXC flag if set */
		hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
		/* Enable transmit complete interrupt for slave */
		tmp_intenset |= SPI_INTERRUPT_FLAG_TX_COMPLETE;

		/* Workaround for SSL flag enable */
#ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
		/* Clear SSL flag if set */
		hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
		/* Enable Slave Select Low Interrupt for slave */
		tmp_intenset |= SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
#endif
	}
#  endif

	/* Enable all interrupts simultaneously */
	hw->INTENSET.reg = tmp_intenset;
}

/**
 * \brief Registers a SPI callback function
 *
 * Registers a callback function which is implemented by the user.
 *
 * \note The callback must be enabled by \ref spi_enable_callback, in order
 *       for the interrupt handler to call it when the conditions for the
 *       callback type are met.
 *
 * \param[in]  module         Pointer to USART software instance struct
 * \param[in]  callback_func  Pointer to callback function
 * \param[in]  callback_type  Callback type given by an enum
 *
 */
void spi_register_callback(
		struct spi_module *const module,
		spi_callback_t callback_func,
		enum spi_callback callback_type)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(callback_func);

	/* Register callback function */
	module->callback[callback_type] = callback_func;

	/* Set the bit corresponding to the callback_type */
	module->registered_callback |= (1 << callback_type);
}

/**
 * \brief Unregisters a SPI callback function
 *
 * Unregisters a callback function which is implemented by the user.
 *
 * \param[in] module         Pointer to SPI software instance struct
 * \param[in] callback_type  Callback type given by an enum
 *
 */
void spi_unregister_callback(
		struct spi_module *const module,
		enum spi_callback callback_type)
{
	/* Sanity check arguments */
	Assert(module);

	/* Unregister callback function */
	module->callback[callback_type] = NULL;

	/* Clear the bit corresponding to the callback_type */
	module->registered_callback &= ~(1 << callback_type);
}

/**
 * \brief Asynchronous buffer write
 *
 * Sets up the driver to write to the SPI from a given buffer. If registered
 * and enabled, a callback function will be called when the write is finished.
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[out] tx_data  Pointer to data buffer to receive
 * \param[in]  length   Data buffer length
 *
 * \returns Status of the write request operation.
 * \retval STATUS_OK               If the operation completed successfully
 * \retval STATUS_ERR_BUSY         If the SPI was already busy with a write
 *                                 operation
 * \retval STATUS_ERR_INVALID_ARG  If requested write length was zero
 */
enum status_code spi_write_buffer_job(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint16_t length)
{
	Assert(module);
	Assert(tx_data);

	if (length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

	/* Check if the SPI is busy transmitting or slave waiting for TXC*/
	if (module->status == STATUS_BUSY) {
		return STATUS_BUSY;
	}

	/* Issue internal write */
	_spi_write_buffer(module, tx_data, length);

	return STATUS_OK;
}

/**
 * \brief Asynchronous buffer read
 *
 * Sets up the driver to read from the SPI to a given buffer. If registered
 * and enabled, a callback function will be called when the read is finished.
 *
 * \note If address matching is enabled for the slave, the first character
 *       received and placed in the RX buffer will be the address.
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[out] rx_data  Pointer to data buffer to receive
 * \param[in]  length   Data buffer length
 * \param[in]  dummy    Dummy character to send when reading in master mode
 *
 * \returns Status of the operation.
 * \retval  STATUS_OK               If the operation completed successfully
 * \retval  STATUS_ERR_BUSY         If the SPI was already busy with a read
 *                                  operation
 * \retval  STATUS_ERR_DENIED       If the receiver is not enabled
 * \retval  STATUS_ERR_INVALID_ARG  If requested read length was zero
 */
enum status_code spi_read_buffer_job(
		struct spi_module *const module,
		uint8_t *rx_data,
		uint16_t length,
		uint16_t dummy)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(rx_data);

	if (length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

	if (!(module->receiver_enabled)) {
		return STATUS_ERR_DENIED;
	}

	/* Check if the SPI is busy transmitting or slave waiting for TXC*/
	if (module->status == STATUS_BUSY) {
		return STATUS_BUSY;
	}

	dummy_write = dummy;
	/* Issue internal read */
	_spi_read_buffer(module, rx_data, length);
	return STATUS_OK;
}

/**
 * \brief Asynchronous buffer write and read
 *
 * Sets up the driver to write and read to and from given buffers. If registered
 * and enabled, a callback function will be called when the transfer is finished.
 *
 * \note If address matching is enabled for the slave, the first character
 *       received and placed in the RX buffer will be the address.
 *
 * \param[in]  module   Pointer to SPI software instance struct
 * \param[in] tx_data   Pointer to data buffer to send
 * \param[out] rx_data  Pointer to data buffer to receive
 * \param[in]  length   Data buffer length
 *
 * \returns Status of the operation.
 * \retval  STATUS_OK               If the operation completed successfully
 * \retval  STATUS_ERR_BUSY         If the SPI was already busy with a read
 *                                  operation
 * \retval  STATUS_ERR_DENIED       If the receiver is not enabled
 * \retval  STATUS_ERR_INVALID_ARG  If requested read length was zero
 */
enum status_code spi_transceive_buffer_job(
		struct spi_module *const module,
		uint8_t *tx_data,
		uint8_t *rx_data,
		uint16_t length)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(rx_data);

	if (length == 0) {
		return STATUS_ERR_INVALID_ARG;
	}

	if (!(module->receiver_enabled)) {
		return STATUS_ERR_DENIED;
	}

	/* Check if the SPI is busy transmitting or slave waiting for TXC*/
	if (module->status == STATUS_BUSY) {
		return STATUS_BUSY;
	}

	/* Issue internal transceive */
	_spi_transceive_buffer(module, tx_data, rx_data, length);

	return STATUS_OK;
}
/**
 * \brief Aborts an ongoing job
 *
 * This function will abort the specified job type.
 *
 * \param[in]  module    Pointer to SPI software instance struct
 */
void spi_abort_job(
		struct spi_module *const module)
{
	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw
		= &(module->hw->SPI);

	/* Abort ongoing job */

	/* Disable interrupts */
	spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE |
			SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
			SPI_INTERRUPT_FLAG_TX_COMPLETE;

	module->status = STATUS_ABORTED;
	module->remaining_rx_buffer_length = 0;
	module->remaining_dummy_buffer_length = 0;
	module->remaining_tx_buffer_length = 0;

	module->dir = SPI_DIRECTION_IDLE;
}

#  if CONF_SPI_SLAVE_ENABLE == true || CONF_SPI_MASTER_ENABLE == true
/**
 * \internal
 * Writes a character from the TX buffer to the Data register.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_write(
		struct spi_module *const module)
{
	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw = &(module->hw->SPI);

	/* Write value will be at least 8-bits long */
	uint16_t data_to_send = *(module->tx_buffer_ptr);
	/* Increment 8-bit pointer */
	(module->tx_buffer_ptr)++;

	if (module->character_size == SPI_CHARACTER_SIZE_9BIT) {
		data_to_send |= ((*(module->tx_buffer_ptr)) << 8);
		/* Increment 8-bit pointer */
		(module->tx_buffer_ptr)++;
	}

	/* Write the data to send*/
	spi_hw->DATA.reg = data_to_send & SERCOM_SPI_DATA_MASK;

	/* Decrement remaining buffer length */
	(module->remaining_tx_buffer_length)--;
}
#  endif

#  if CONF_SPI_MASTER_ENABLE == true
/**
 * \internal
 * Writes a dummy character to the Data register.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_write_dummy(
		struct spi_module *const module)
{
	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw = &(module->hw->SPI);

	/* Write dummy byte */
	spi_hw->DATA.reg = dummy_write;

	/* Decrement remaining dummy buffer length */
	module->remaining_dummy_buffer_length--;
}
#  endif

/**
 * \internal
 * Writes a dummy character from the to the Data register.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_read_dummy(
		struct spi_module *const module)
{
	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw = &(module->hw->SPI);
	uint16_t flush = 0;

	/* Read dummy byte */
	flush = spi_hw->DATA.reg;
	UNUSED(flush);

	/* Decrement remaining dummy buffer length */
	module->remaining_dummy_buffer_length--;
}

/**
 * \internal
 * Reads a character from the Data register to the RX buffer.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_read(
		struct spi_module *const module)
{
	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw = &(module->hw->SPI);

	uint16_t received_data = (spi_hw->DATA.reg & SERCOM_SPI_DATA_MASK);

	/* Read value will be at least 8-bits long */
	*(module->rx_buffer_ptr) = received_data;
	/* Increment 8-bit pointer */
	module->rx_buffer_ptr += 1;

	if(module->character_size == SPI_CHARACTER_SIZE_9BIT) {
		/* 9-bit data, write next received byte to the buffer */
		*(module->rx_buffer_ptr) = (received_data >> 8);
		/* Increment 8-bit pointer */
		module->rx_buffer_ptr += 1;
	}

	/* Decrement length of the remaining buffer */
	module->remaining_rx_buffer_length--;
}

/**
 * \internal
 *
 * Handles interrupts as they occur, and it will run callback functions
 * which are registered and enabled.
 *
 * \note This function will be called by the Sercom_Handler, and should
 *       not be called directly from any application code.
 *
 * \param[in]  instance  ID of the SERCOM instance calling the interrupt
 *                       handler.
 */
void _spi_interrupt_handler(
		uint8_t instance)
{
	/* Get device instance from the look-up table */
	struct spi_module *module
		= (struct spi_module *)_sercom_instances[instance];

	/* Pointer to the hardware module instance */
	SercomSpi *const spi_hw = &(module->hw->SPI);

	/* Combine callback registered and enabled masks. */
	uint8_t callback_mask =
			module->enabled_callback & module->registered_callback;

	/* Read and mask interrupt flag register */
	uint16_t interrupt_status = spi_hw->INTFLAG.reg;
	interrupt_status &= spi_hw->INTENSET.reg;

	/* Data register empty interrupt */
	if (interrupt_status & SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY) {
#  if CONF_SPI_MASTER_ENABLE == true
		if ((module->mode == SPI_MODE_MASTER) &&
			(module->dir == SPI_DIRECTION_READ)) {
			/* Send dummy byte when reading in master mode */
			_spi_write_dummy(module);
			if (module->remaining_dummy_buffer_length == 0) {
				/* Disable the Data Register Empty Interrupt */
				spi_hw->INTENCLR.reg
						= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
			}
		}
#  endif

		if (0
#  if CONF_SPI_MASTER_ENABLE == true
		|| ((module->mode == SPI_MODE_MASTER) &&
			(module->dir != SPI_DIRECTION_READ))
#  endif
#  if CONF_SPI_SLAVE_ENABLE == true
		|| ((module->mode == SPI_MODE_SLAVE) &&
			(module->dir != SPI_DIRECTION_READ))
#  endif
		) {
			/* Write next byte from buffer */
			_spi_write(module);
			if (module->remaining_tx_buffer_length == 0) {
				/* Disable the Data Register Empty Interrupt */
				spi_hw->INTENCLR.reg
						= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;

				if (module->dir == SPI_DIRECTION_WRITE &&
						!(module->receiver_enabled)) {
					/* Enable the Data Register transmit complete Interrupt */
					spi_hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
				}
			}
		}
	}

	/* Receive complete interrupt*/
	if (interrupt_status & SPI_INTERRUPT_FLAG_RX_COMPLETE) {
		/* Check for overflow */
		if (spi_hw->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) {
			if (module->dir != SPI_DIRECTION_WRITE) {
				/* Store the error code */
				module->status = STATUS_ERR_OVERFLOW;

				/* End transaction */
				module->dir = SPI_DIRECTION_IDLE;

				spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE |
						SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
				/* Run callback if registered and enabled */
				if (callback_mask & (1 << SPI_CALLBACK_ERROR)) {
					(module->callback[SPI_CALLBACK_ERROR])(module);
				}
			}
			/* Flush */
			uint16_t flush = spi_hw->DATA.reg;
			UNUSED(flush);
			/* Clear overflow flag */
			spi_hw->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF;
		} else {
			if (module->dir == SPI_DIRECTION_WRITE) {
				/* Flush receive buffer when writing */
				_spi_read_dummy(module);
				if (module->remaining_dummy_buffer_length == 0) {
					spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE;
					module->status = STATUS_OK;
					module->dir = SPI_DIRECTION_IDLE;
					/* Run callback if registered and enabled */
					if (callback_mask &
							(1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){
						(module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])(module);
					}
				}
			} else {
				/* Read data register */
				_spi_read(module);

				/* Check if the last character have been received */
				if (module->remaining_rx_buffer_length == 0) {
					module->status = STATUS_OK;
					/* Disable RX Complete Interrupt and set status */
					spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE;
					if(module->dir == SPI_DIRECTION_BOTH) {
						if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSCEIVED)) {
							(module->callback[SPI_CALLBACK_BUFFER_TRANSCEIVED])(module);
						}
					} else if (module->dir == SPI_DIRECTION_READ) {
						if (callback_mask & (1 << SPI_CALLBACK_BUFFER_RECEIVED)) {
							(module->callback[SPI_CALLBACK_BUFFER_RECEIVED])(module);
						}
					}
				}
			}
		}
	}

	/* Transmit complete */
	if (interrupt_status & SPI_INTERRUPT_FLAG_TX_COMPLETE) {
#  if CONF_SPI_SLAVE_ENABLE == true
		if (module->mode == SPI_MODE_SLAVE) {
			/* Transaction ended by master */

			/* Disable interrupts */
			spi_hw->INTENCLR.reg =
					SPI_INTERRUPT_FLAG_TX_COMPLETE |
					SPI_INTERRUPT_FLAG_RX_COMPLETE |
					SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
			/* Clear interrupt flag */
			spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;


			/* Reset all status information */
			module->dir = SPI_DIRECTION_IDLE;
			module->remaining_tx_buffer_length = 0;
			module->remaining_rx_buffer_length = 0;
			module->status = STATUS_OK;

			if (callback_mask &
					(1 << SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE)) {
			(module->callback[SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE])
					(module);
			}

		}
#  endif
#  if CONF_SPI_MASTER_ENABLE == true
		if ((module->mode == SPI_MODE_MASTER) &&
			(module->dir == SPI_DIRECTION_WRITE) && !(module->receiver_enabled)) {
		  	/* Clear interrupt flag */
		 	spi_hw->INTENCLR.reg
					= SPI_INTERRUPT_FLAG_TX_COMPLETE;
			/* Buffer sent with receiver disabled */
			module->dir = SPI_DIRECTION_IDLE;
			module->status = STATUS_OK;
			/* Run callback if registered and enabled */
			if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){
				(module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])
						(module);
			}
		}
#endif
	}

#  ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
#  if CONF_SPI_SLAVE_ENABLE == true
		/* When a high to low transition is detected on the _SS pin in slave mode */
		if (interrupt_status & SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW) {
			if (module->mode == SPI_MODE_SLAVE) {
				/* Disable interrupts */
				spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
				/* Clear interrupt flag */
				spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;

				if (callback_mask & (1 << SPI_CALLBACK_SLAVE_SELECT_LOW)) {
					(module->callback[SPI_CALLBACK_SLAVE_SELECT_LOW])(module);
				}
			}
		}
#  endif
#  endif

#  ifdef FEATURE_SPI_ERROR_INTERRUPT
	/* When combined error happen */
	if (interrupt_status & SPI_INTERRUPT_FLAG_COMBINED_ERROR) {
		/* Disable interrupts */
		spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR;
		/* Clear interrupt flag */
		spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR;

		if (callback_mask & (1 << SPI_CALLBACK_COMBINED_ERROR)) {
			(module->callback[SPI_CALLBACK_COMBINED_ERROR])(module);
		}
	}
#  endif
}
