/*
 * Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2023 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"

#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_spi.h"
#include "fsl_inputmux.h"
#include "fsl_iopctl.h"
#include "fsl_dma.h"
/*
 * Pins used in this application:
 *
 * P0_6  [D:O] -  J30.2: GPO - SYNCHRO indicator (active high)
 * P0_15 [D:O] -  JS6.2: GPO - TEST indicator (active high)
 * P0_16 [D:O] -  JS5.2: GPO - AUX0 indicator (test parameters indicator
 *
 * P1_3  [D:O] - JP26.4: SPI5_CLK
 * P1_4  [D:I] - JP26.3: SPI5_MISO
 * P1_5  [D:O] - JP26.2: SPI5_MOSI
 * P1_6  [D:O] - JP26.1: SPI5_SS0
 *
 * P2_29 [D:O] - J18.1 : CLKOUT, main clock/1
 *
 * P2_15 [D:O] - J47.10: GPO - error indicator (active high)
 *
 */

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define IOCON_IN_NOPUPD(x)	\
	(IOPCTL_PIO_FSEL(x) 	|	\
	 IOPCTL_PIO_PUPDENA(0)	| IOPCTL_PIO_IBENA(1)	| IOPCTL_PIO_FULLDRIVE(0)	| \
     IOPCTL_PIO_AMENA(0)	| IOPCTL_PIO_ODENA(0)	| IOPCTL_PIO_IIENA(0))

// FC5 SPI
#define SPI_SS_PORT		1
#define SPI_SS_PIN		6
#define	SPI_SS_FUNC		1

#define SPI_CLK_PORT	1
#define SPI_CLK_PIN		3
#define	SPI_CLK_FUNC	1

#define SPI_MOSI_PORT	1
#define SPI_MOSI_PIN	5
#define	SPI_MOSI_FUNC	1

#define SPI_MISO_PORT	1
#define SPI_MISO_PIN	4
#define	SPI_MISO_FUNC	1

#define CLKOUT_PORT		2
#define	CLKOUT_PIN		29
#define	CLKOUT_FUNC		5

#ifdef DEBUG_MODE
gpio_pin_config_t gpio_config =
 {
	kGPIO_DigitalOutput,
	0,
};

/* SYNCHRO indicates when the the program starts.
 * TEST indicates when a test starts/stops.
 * AUX0 indicates when a DMA0/SPI5 interrupt is generated.
 * ERROR indicates when data received are different to data sent.
 */

#define	SYNCHRO_PORT	0
#define	SYNCHRO_PIN		6
#define SYNCHRO_FUNC	0
#define	TEST_PORT		0
#define	TEST_PIN		15
#define TEST_FUNC		0
#define	AUX0_PORT		0
#define	AUX0_PIN		16
#define AUX0_FUNC		0
#define ERROR_PORT		2
#define	ERROR_PIN		15
#define ERROR_FUNC		0
#endif

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
volatile uint32_t sw_gate, sw_gate_count;
volatile uint32_t main_loop_count;

void delay_sw(uint32_t count);
void init_mcu(void);
void init_spi(void);
void init_dma(void);

void prepare_txrx_data(uint32_t count, uint8_t scramble_pattern, uint8_t *pnt_tx_array, uint8_t *pnt_rx_array);
void test_spi_txrx(uint8_t *pnt_tx_array, uint8_t *pnt_rx_array);
void spi_fifowr_preset(uint32_t fifowr_addr, uint32_t fifowr32bit);
void verify_txrx_data(uint32_t count, uint8_t *pnt_tx_array, uint8_t *pnt_rx_array);


// DMA section
//============

// RT500 specific number of DMA0 requests
#define DEMO_DMA_CH_PERI_REQ	37
enum demo_dma_main_descriptor_enum
{
	dma_desc_spi_rx_main = 10,	// this one must be @ index 10 to match SPI5 Rx request
};
struct DEMO_DESCRIPTOR_TYPE_T
{
	uint32_t xfercfg;
	uint32_t src_addr;
	uint32_t des_addr;
	uint32_t link;
};
__attribute__((aligned(1024))) volatile struct DEMO_DESCRIPTOR_TYPE_T demo_dma_main_descriptor[DEMO_DMA_CH_PERI_REQ];

#define TEST_CPOL		0
#define TEST_CPHA		0

// demo example: this SPI does not need more than 5 descriptors (transfers up to count of 5*1024 = 5120)
// define a stand-alone set of descriptors for this SPI to use
#define GP_SPI_DESCRIPTOR_COUNT	5
__attribute__((aligned(16))) volatile struct DEMO_DESCRIPTOR_TYPE_T demo_gp_spi_descriptor[GP_SPI_DESCRIPTOR_COUNT];
#define GP_SPI_RX_DMAPRIO		2

volatile uint8_t tx_data[GP_SPI_DESCRIPTOR_COUNT*1024], rx_data[GP_SPI_DESCRIPTOR_COUNT*1024];
volatile uint32_t tx_len, rx_len, tx_spi_first_control, tx_spi_last_control;
volatile uint32_t spi_tx_transfer_count, spi_isr_count;
volatile uint32_t dma0_isr_count;

spi_master_config_t masterConfig;
/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * @brief Main function
 */
int main(void)
{
    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    init_mcu();
    init_spi();
    init_dma();

#ifdef DEBUG_MODE
    // synchro pulse - use this to trigger the scope/logic
    GPIO_PortClear(GPIO, SYNCHRO_PORT, 1 << SYNCHRO_PIN);
    delay_sw(200);
    GPIO_PortSet(GPIO, SYNCHRO_PORT, 1 << SYNCHRO_PIN);
    delay_sw(200);
    GPIO_PortClear(GPIO, SYNCHRO_PORT, 1 << SYNCHRO_PIN);
    delay_sw(200);
#endif

    for(uint32_t i = 1; i < 28; i ++)
    {
    	// test case 1: transfer 63 bytes, 1 descriptor, use the general purpose routine
        prepare_txrx_data(63, 0, (uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
        test_spi_txrx((uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
        verify_txrx_data(63, (uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
        delay_sw(50);
    }

    // test case 3: transfer 3500 bytes, multiple descriptors, use the general purpose routine
    prepare_txrx_data(3500, 0, (uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
    test_spi_txrx((uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
    verify_txrx_data(3500, (uint8_t *)&tx_data[0], (uint8_t *)&rx_data[0]);
    delay_sw(50);

    main_loop_count = 0;
    while(1)
    {
    	main_loop_count++;
    }

    return 0;
}

void delay_sw(uint32_t count)
{
	while(count != 0)
	{
		count--;
	}

	return;
}

void init_mcu(void)
{
	CLOCK_EnableClock(kCLOCK_InputMux);
	RESET_PeripheralReset(kINPUTMUX_RST_SHIFT_RSTn);

	CLOCK_AttachClk(kMAIN_CLK_to_CLKOUT);
	CLOCK_SetClkDiv(kCLOCK_DivClockOut, 0);

	IOPCTL_PinMuxSet(IOPCTL, CLKOUT_PORT, CLKOUT_PIN, IOCON_IN_NOPUPD(CLKOUT_FUNC));

#ifdef DEBUG_MODE
    // prime the test indicators
    //==========================
	GPIO_PortInit(GPIO, SYNCHRO_PORT);
	GPIO_PortInit(GPIO, ERROR_PORT);

	GPIO_PinInit(GPIO, SYNCHRO_PORT, SYNCHRO_PIN, &gpio_config);
	IOPCTL_PinMuxSet(IOPCTL, SYNCHRO_PORT, SYNCHRO_PIN, IOCON_IN_NOPUPD(SYNCHRO_FUNC));

	GPIO_PinInit(GPIO, TEST_PORT, TEST_PIN, &gpio_config);
	IOPCTL_PinMuxSet(IOPCTL, TEST_PORT, TEST_PIN, IOCON_IN_NOPUPD(TEST_FUNC));

	GPIO_PinInit(GPIO, AUX0_PORT, AUX0_PIN, &gpio_config);
	IOPCTL_PinMuxSet(IOPCTL, AUX0_PORT, AUX0_PIN, IOCON_IN_NOPUPD(AUX0_FUNC));

	GPIO_PinInit(GPIO, ERROR_PORT, ERROR_PIN, &gpio_config);
	IOPCTL_PinMuxSet(IOPCTL, ERROR_PORT, ERROR_PIN, IOCON_IN_NOPUPD(ERROR_FUNC));
#endif

	return;
}


// basic SPI setup common for all scenarios
void init_spi(void)
{
	CLOCK_SetFRGClock(&(const clock_frg_clk_config_t){5, kCLOCK_FrgMainClk, 255, 0});
	CLOCK_AttachClk(kFRG_to_FLEXCOMM5);
	CLOCK_EnableClock(kCLOCK_Spi5);

    RESET_PeripheralReset(kFC5_RST_SHIFT_RSTn);

    // select FC5 as SPI
    FLEXCOMM5->PSELID = FLEXCOMM_PSELID_PERSEL(2);

    // FC5_SCK @ ...
    IOPCTL_PinMuxSet(IOPCTL, SPI_SS_PORT, SPI_SS_PIN, IOCON_IN_NOPUPD(SPI_SS_FUNC));
    IOPCTL_PinMuxSet(IOPCTL, SPI_CLK_PORT, SPI_CLK_PIN, IOCON_IN_NOPUPD(SPI_CLK_FUNC));
    IOPCTL_PinMuxSet(IOPCTL, SPI_MOSI_PORT, SPI_MOSI_PIN, IOCON_IN_NOPUPD(SPI_MOSI_FUNC));
    IOPCTL_PinMuxSet(IOPCTL, SPI_MISO_PORT, SPI_MISO_PIN, IOCON_IN_NOPUPD(SPI_MISO_FUNC));

    SPI_MasterGetDefaultConfig(&masterConfig);
    masterConfig.baudRate_Bps = CLOCK_GetFlexcommClkFreq(5) / 3;
    SPI_MasterInit(SPI5, &masterConfig, CLOCK_GetFlexcommClkFreq(5));
    SPI_EnableInterrupts(SPI5, kSPI_TxLvlIrq);

	// prepare interrupt
	NVIC_DisableIRQ(FLEXCOMM5_IRQn);
	NVIC_SetPriority(FLEXCOMM5_IRQn, 2);
	spi_isr_count = 0;

	return;
}

void init_dma(void)
{
	uint32_t i_loc;

	// DMA, INPUTMUX
    CLOCK_EnableClock(kCLOCK_Dmac0);
    RESET_PeripheralReset(kDMAC0_RST_SHIFT_RSTn);

    CLOCK_EnableClock(kCLOCK_InputMux);
    RESET_PeripheralReset(kINPUTMUX_RST_SHIFT_RSTn);

    DMA0->SRAMBASE = (uint32_t)&demo_dma_main_descriptor[0];
    DMA0->CTRL = 1;

    INPUTMUX_EnableSignal(INPUTMUX, kINPUTMUX_Flexcomm5RxToDmac0Ch10RequestEna, true);

    // the following SPI descriptor details do not change
	DMA0->CHANNEL[dma_desc_spi_rx_main].CFG =
		1<<0	|	// peripheral req enabled
		0<<1	|	// hw trigger not used
		0<<6	|	// no burst transfers
		GP_SPI_RX_DMAPRIO<<16;	// priority =...

	for (i_loc = 0; i_loc != GP_SPI_DESCRIPTOR_COUNT; i_loc++)
	{
		// read data from the FIFORD
		demo_gp_spi_descriptor[i_loc].src_addr = (uint32_t)&SPI5->FIFORD;

		// prepare links for the list
		if (i_loc != (GP_SPI_DESCRIPTOR_COUNT-1))
		{
			demo_gp_spi_descriptor[i_loc].link = (uint32_t)&demo_gp_spi_descriptor[i_loc+1];
		}
	}

	// prepare INTA
	// disable DMA0 IRQ, clear (any pending) ch flag @ DMA0, clear pending IRQ @ NVIC
	// enable DMA ch interrupt @ DMA0, set DMA0 IRQ priority and enable IRQ @ NVIC
	NVIC_DisableIRQ(DMA0_IRQn);
	DMA0->COMMON[0].INTA = 1<<dma_desc_spi_rx_main;
	NVIC_ClearPendingIRQ(DMA0_IRQn);
	DMA_EnableChannelInterrupts(DMA0, dma_desc_spi_rx_main);
	NVIC_SetPriority(DMA0_IRQn, 2);
	NVIC_EnableIRQ(DMA0_IRQn);
	dma0_isr_count = 0;

	return;
}

// prepare test data
void prepare_txrx_data(uint32_t count, uint8_t scramble_pattern,
					   uint8_t *pnt_tx_array, uint8_t *pnt_rx_array)
{
	uint32_t i_loc;

	if (count == 0)
	{
		count = 1;
	}
	else if (count > GP_SPI_DESCRIPTOR_COUNT*1024)
	{
		count = GP_SPI_DESCRIPTOR_COUNT*1024;
	}

	tx_spi_first_control =
			SPI_FIFOWR_LEN(8-1) 	|	// len
			SPI_FIFOWR_TXIGNORE(0)	|	// TXIGNORE
			SPI_FIFOWR_RXIGNORE(0)	|	// RXIGNORE
			SPI_FIFOWR_EOF(1)		|	// EOF
			SPI_FIFOWR_EOT(0)		|	// EOT
			SPI_FIFOWR_TXSSEL3_N(1)	|	// TXSSEL3
			SPI_FIFOWR_TXSSEL2_N(1)	|	// TXSSEL2
			SPI_FIFOWR_TXSSEL1_N(1)	|	// TXSSEL1
			SPI_FIFOWR_TXSSEL0_N(0)	|	// TXSSEL0
			SPI_FIFOWR_TXDATA(0); // TXDATA

	tx_spi_last_control =
			SPI_FIFOWR_LEN(8-1) 	|	// len
			SPI_FIFOWR_TXIGNORE(0)	|	// TXIGNORE
			SPI_FIFOWR_RXIGNORE(0)	|	// RXIGNORE
			SPI_FIFOWR_EOF(1)		|	// EOF
			SPI_FIFOWR_EOT(1)		|	// EOT
			SPI_FIFOWR_TXSSEL3_N(1)	|	// TXSSEL3
			SPI_FIFOWR_TXSSEL2_N(1)	|	// TXSSEL2
			SPI_FIFOWR_TXSSEL1_N(1)	|	// TXSSEL1
			SPI_FIFOWR_TXSSEL0_N(0)	|	// TXSSEL0
			SPI_FIFOWR_TXDATA(0); // TXDATA

	// prepare to send len info
	tx_len = count;

	// prepare data, get ready for data
	for (i_loc = 0; i_loc != count; i_loc++)
	{
		*pnt_tx_array = (i_loc & 0xFF) ^ scramble_pattern;
		pnt_tx_array++;

		*pnt_rx_array = 0;
		pnt_rx_array++;
	}

	// adjust the last one
	*(pnt_tx_array-1) = ((i_loc-1) & 0xFF) ^ scramble_pattern;

	return;
}

// general purpose txrx code handling as many DMA descriptors as needed due to the transfer count
void test_spi_txrx(uint8_t *pnt_tx_array, uint8_t *pnt_rx_array)
{
	uint32_t spi_rx_count_received_loc, spi_rx_count_left_loc;
	uint32_t spi_rx_descriptor_index_loc, spi_rx_descriptor_count_loc;

#ifdef DEBUG_MODE
	GPIO_PortSet(GPIO, TEST_PORT, 1 << TEST_PIN);
#endif

    // disable DMA ch, disable SPI Rx DMA request
	DMA_DisableChannel(DMA0, dma_desc_spi_rx_main);
    SPI5->FIFOCFG =
       	SPI_FIFOCFG_ENABLETX(1)	|	// enable TxFIFO
       	SPI_FIFOCFG_ENABLERX(1)	|	// enable RxFIFO
		SPI_FIFOCFG_DMARX(0)	|	// disable Rx DMA
		SPI_FIFOCFG_EMPTYTX(0)	|	// release Tx FIFO reset
		SPI_FIFOCFG_EMPTYRX(0);		// release Rx FIFO reset

    spi_fifowr_preset((uint32_t)&SPI5->FIFOWR, tx_spi_first_control);

    // send two byte command
    SPI5->FIFOWR =
    	SPI_FIFOWR_LEN(8-1) 	|	// len
    	SPI_FIFOWR_TXIGNORE(0)	|	// TXIGNORE
    	SPI_FIFOWR_RXIGNORE(0)	|	// RXIGNORE
    	SPI_FIFOWR_EOF(1)		|	// EOF
    	SPI_FIFOWR_EOT(0)		|	// EOT
    	SPI_FIFOWR_TXSSEL3_N(1)	|	// TXSSEL3
    	SPI_FIFOWR_TXSSEL2_N(1)	|	// TXSSEL2
    	SPI_FIFOWR_TXSSEL1_N(1)	|	// TXSSEL1
    	SPI_FIFOWR_TXSSEL0_N(0)	|	// TXSSEL0
    	SPI_FIFOWR_TXDATA((tx_len>>0) & 0xFF); // TXDATA

    SPI5->FIFOWR =
    	SPI_FIFOWR_LEN(8-1) 	|	// len
    	SPI_FIFOWR_TXIGNORE(0)	|	// TXIGNORE
    	SPI_FIFOWR_RXIGNORE(0)	|	// RXIGNORE
    	SPI_FIFOWR_EOF(1)		|	// EOF
    	SPI_FIFOWR_EOT(0)		|	// EOT
    	SPI_FIFOWR_TXSSEL3_N(1)	|	// TXSSEL3
    	SPI_FIFOWR_TXSSEL2_N(1)	|	// TXSSEL2
    	SPI_FIFOWR_TXSSEL1_N(1)	|	// TXSSEL1
    	SPI_FIFOWR_TXSSEL0_N(0)	|	// TXSSEL0
    	SPI_FIFOWR_TXDATA((tx_len>>8) & 0xFF); // TXDATA

    // wait for for 2 bytes to be received, extract rx_len info
    while(((SPI_GetStatusFlags(SPI5) >> SPI_FIFOSTAT_RXLVL_SHIFT) & 0x1F) != 2);
    rx_len = SPI_ReadData(SPI5) & 0xFF;
    rx_len |= (SPI_ReadData(SPI5) & 0xFF) << 8;

    // enable Rx DMA from the SPI
    SPI5->FIFOCFG =
       	SPI_FIFOCFG_ENABLETX(1)	|	// enable TxFIFO
       	SPI_FIFOCFG_ENABLERX(1)	|	// enable RxFIFO
		SPI_FIFOCFG_DMARX(1)	|	// enable Rx DMA
		SPI_FIFOCFG_EMPTYTX(0)	|	// release Tx FIFO reset
		SPI_FIFOCFG_EMPTYRX(0);		// release Rx FIFO reset

	// prepare DMA configuration/descriptors
	spi_rx_count_received_loc = 0;
	spi_rx_count_left_loc = rx_len;
	spi_rx_descriptor_index_loc = 0;

	do
	{
		if (spi_rx_count_left_loc > 1024)
		{
			spi_rx_descriptor_count_loc = 1024;
		}
		else
		{
			spi_rx_descriptor_count_loc = spi_rx_count_left_loc;
		}

		spi_rx_count_received_loc += spi_rx_descriptor_count_loc;
		spi_rx_count_left_loc     -= spi_rx_descriptor_count_loc;


		demo_gp_spi_descriptor[spi_rx_descriptor_index_loc].des_addr =
			(uint32_t)&pnt_rx_array[spi_rx_count_received_loc-1];

		demo_gp_spi_descriptor[spi_rx_descriptor_index_loc].xfercfg =
			1<<0 	|	// valid configuration
			1<<1 	|	// link/reload
			1<<2	|	// sw trigger
			0<<3	|	// do not clear trigger at the end
			0<<4 	|	// no int A at the end
			0<<5	|	// no int B at the end
			0<<8 	|	// 8-bit transfers
			0<<12	|	// src: +0
			1<<14	|	// dst: +1
			(spi_rx_descriptor_count_loc-1)<<16;	//transfer count...

		// if not done, add a descriptor
		if (spi_rx_count_left_loc != 0)
		{
			spi_rx_descriptor_index_loc++;
		}
	}
	while(spi_rx_count_left_loc != 0);

	// make sure INTA not active, update the last descriptor:
	// do not link, clear trigger at descriptor end, enable int A at the end,
	// update main descriptor & XFERCFG
	DMA0->COMMON[0].INTA = 1<<dma_desc_spi_rx_main;
	demo_gp_spi_descriptor[spi_rx_descriptor_index_loc].xfercfg =
		(demo_gp_spi_descriptor[spi_rx_descriptor_index_loc].xfercfg & ~(1<<1)) |
		1<<3 | 1<<4;

	demo_dma_main_descriptor[dma_desc_spi_rx_main].src_addr = demo_gp_spi_descriptor[0].src_addr;
	demo_dma_main_descriptor[dma_desc_spi_rx_main].des_addr = demo_gp_spi_descriptor[0].des_addr;
	demo_dma_main_descriptor[dma_desc_spi_rx_main].link     = demo_gp_spi_descriptor[0].link;
	DMA0->CHANNEL[dma_desc_spi_rx_main].XFERCFG             = demo_gp_spi_descriptor[0].xfercfg;

	// let the DMA ch run
	DMA_EnableChannel(DMA0, dma_desc_spi_rx_main);

	// let SPI Tx run
	spi_tx_transfer_count = 0;
	NVIC_ClearPendingIRQ(FLEXCOMM5_IRQn);
	NVIC_EnableIRQ(FLEXCOMM5_IRQn);

    // wait for the DMA A int
    while((DMA0->COMMON[0].ENABLESET & (1<<dma_desc_spi_rx_main)) != 0);

#ifdef DEBUG_MODE
    GPIO_PortClear(GPIO, TEST_PORT, 1 << TEST_PIN);
#endif
	return;
}

// this routine prepares the SPI by writing into the FIFOWR control upper half-word
// fifowr32bit matches the 32-bit FIFOWR content
void spi_fifowr_preset(uint32_t fifowr_addr, uint32_t fifowr32bit)
{
	uint32_t fifowr_control_addr;
	uint16_t fifowr_control_16bit;

	fifowr_control_addr = (fifowr_addr & 0xFFFFFFFC)+2;

	fifowr_control_16bit = (uint16_t)((fifowr32bit>>16) & 0xFFFF);

	*((uint16_t*)fifowr_control_addr) = fifowr_control_16bit;

	return;
}

void verify_txrx_data(uint32_t count, uint8_t *pnt_tx_array, uint8_t *pnt_rx_array)
{
	uint32_t i_loc, error_count_loc;

	error_count_loc = 0;
	for (i_loc = 0; i_loc != count; i_loc++)
	{
		if ((((*pnt_tx_array)^(*pnt_rx_array)) & 0xFF) != 0)
		{
			error_count_loc++;
		}

		pnt_tx_array++;
		pnt_rx_array++;
	}

	if (error_count_loc != 0)
	{
#ifdef DEBUG_MODE
		GPIO_PortSet(GPIO, ERROR_PORT, 1 << ERROR_PIN);
	    for (i_loc = 0; i_loc != error_count_loc; i_loc++)
	    {
	    	GPIO_PortClear(GPIO, ERROR_PORT, 1 << ERROR_PIN);
	    	GPIO_PortSet(GPIO, ERROR_PORT, 1 << ERROR_PIN);
	    }
	    GPIO_PortClear(GPIO, ERROR_PORT, 1 << ERROR_PIN);
#endif
	}

	return;
}

// simple DMA isr: clears flag generating the interrupt, disables the channel
void DMA0_IRQHandler(void)
{
#ifdef DEBUG_MODE
	GPIO_PortSet(GPIO, AUX0_PORT, 1 << AUX0_PIN);
	GPIO_PortClear(GPIO, AUX0_PORT, 1 << AUX0_PIN);
	GPIO_PortSet(GPIO, AUX0_PORT, 1 << AUX0_PIN);
#endif

    dma0_isr_count++;

    // clear flag, disable ch
    if (DMA0->COMMON[0].INTA & (1<<dma_desc_spi_rx_main))
    {
    	DMA0->COMMON[0].INTA = 1<<dma_desc_spi_rx_main;
    	DMA_DisableChannel(DMA0, dma_desc_spi_rx_main);
    }
#ifdef DEBUG_MODE
    GPIO_PortClear(GPIO, AUX0_PORT, 1 << AUX0_PIN);
#endif

	return;
}

// this SPI TxFIFO driven isr runs only when the TxFIFO is empty
// number of vacant slots in the RxFIFO will determine the max number of TxFIFO writes
void FLEXCOMM5_IRQHandler(void)
{
	uint32_t rx_fifo_vacancy_loc;

#ifdef DEBUG_MODE
	GPIO_PortSet(GPIO, AUX0_PORT, 1 << AUX0_PIN);
#endif

    spi_isr_count++;

    if ((SPI_GetStatusFlags(SPI5) & SPI_FIFOSTAT_TXEMPTY_MASK) != 0)
	{	// TXFIFO empty, proceed

    	// step 1: find out is there any room in the RxFIFO for data to be received?
    	// step 2: if room available, adjust by 1 for potentially an incoming entry
    	// step 3: write as many entries as possible/available to the TxFIFO without causing RxFIFO overflow
    	rx_fifo_vacancy_loc = 8 - ((SPI_GetStatusFlags(SPI5) >> SPI_FIFOSTAT_RXLVL_SHIFT) & 0x1F);

        if (rx_fifo_vacancy_loc != 0)
        {	// RX FIFO not full, proceed
        	rx_fifo_vacancy_loc--;

            while((rx_fifo_vacancy_loc != 0) && (spi_tx_transfer_count != tx_len))
           	{	// loop while room left in RX FIFO and not all data sent
            	if (spi_tx_transfer_count != (tx_len-1))
            	{	// not the last byte
               	    SPI5->FIFOWR = (uint32_t)tx_data[spi_tx_transfer_count];
            	}
            	else
            	{	// the last byte
               	    SPI5->FIFOWR = tx_spi_last_control | ((uint32_t)tx_data[spi_tx_transfer_count]);
            	}
           	    spi_tx_transfer_count++;
           	    rx_fifo_vacancy_loc--;
           	    if (spi_tx_transfer_count == tx_len)
           	    {
           	    	NVIC_DisableIRQ(FLEXCOMM5_IRQn);
           	    }// end of not all data sent
            } // end of RX FIFO not full AND not all data sent
        }// end of RX FIFO not full
	}// end of TX FIFO empty

#ifdef DEBUG_MODE
	GPIO_PortClear(GPIO, AUX0_PORT, 1 << AUX0_PIN);
#endif

	return;
}
