/***
 *** This Example has no copyright and can be used by anyone.
 *** The following example is based on Device File Package
 *** required to compile the macro definitions used.
 *** The Device File Package is avaiblable by downloading Atmel Studio 7.
 ***/

/*** Define SPI Channels ***/
#define SPI_TX_CHANNEL      1
#define SPI_RX_CHANNEL      0

/*** Create DMA descriptor in global variable (128-bit aligned) ***/
volatile __attribute__((__aligned__(128))) DmacDescriptor descriptor_SPI[2];
volatile __attribute__((__aligned__(128))) DmacDescriptor write_back_descriptor_SPI[2];

/*** DMA RX Descriptor Initialization step by step
 *** - Validate the Descriptor
 *** - No Event out generated by the DMA
 *** - Channel will be disabled if it is the last block 
 ***   transfer in the transaction and block interrupt
 *** - 8-bit bus transfer (BYTE)
 *** - No increment from the source address (SRCINC=0)
 *** - Enable Increment from the destination address (DSTINC=1)
 *** - Step size settings apply to the Destination address
 *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1
 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core
 ***/
void DMA_RX_descriptor_init(void)
{
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.VALID = 1;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val;    
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;    
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.SRCINC = 0;                        
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.DSTINC = 1;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1;

 /*** Set source address and destination address 
  *** source address is the SPI Data register (SERCOM0->SPI.DATA)
  *** destination address in the RAM is the rx buffer (spi_rx_buffer[SPI_DATA_SIZE])
  ***/
 descriptor_SPI[SPI_RX_CHANNEL].SRCADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA));
 descriptor_SPI[SPI_RX_CHANNEL].DSTADDR.reg = (uint32_t) (&(spi_rx_buffer[SPI_DATA_SIZE]));

 /*** Set next transfer descriptor_SPI_TX address ***/
 descriptor_SPI[SPI_RX_CHANNEL].DESCADDR.reg = (uint32_t) (&descriptor_SPI[SPI_TX_CHANNEL]);
}

/*** DMA RX Descriptor Initialization step by step
 *** - Validate the Descriptor
 *** - No Event out generated by the DMA
 *** - Channel will be disabled if it is the last block 
 ***   transfer in the transaction and block interrupt
 *** - 8-bit bus transfer (BYTE)
 *** - Enable Increment from the  the source address (SRCINC=1)
 *** - No increment from the destination address (DSTINC=0)
 *** - Step size settings apply to the Source address
 *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1
 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core
 ***/
void DMA_TX_descriptor_init(void)
{
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.VALID = 1;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.SRCINC = 1;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.DSTINC = 0;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_SRC_Val;    
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1;

 /*** Set source address and destination address 
  *** Source address in the RAM is the spi Output buffer (spi_output_data[SPI_DATA_SIZE])
  *** Destination address is the SPI Data register (SERCOM0->SPI.DATA)
  ***/
 descriptor_SPI[SPI_TX_CHANNEL].SRCADDR.reg = (uint32_t) (&(spi_output_data[SPI_DATA_SIZE]));
 descriptor_SPI[SPI_TX_CHANNEL].DSTADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA));

 /*** Set next transfer address to NULL ***/
 descriptor_SPI[SPI_TX_CHANNEL].DESCADDR.reg = NULL;
}

/*** DMA SPI TX and RX Channels initialization ***/
void DMA_init_SPI(void)
{
 /***
  *** Setup descriptor base address 
  *** and write back section base address 
  ***/
 DMAC->BASEADDR.reg = (uint32_t)descriptor_SPI;
 DMAC->WRBADDR.reg = (uint32_t)write_back_descriptor_SPI;

 /***
  *** Disable DMAC 
  *** Enable all priority level at the same time 
  *** Round Robin Arbitrer Scheme selected
  ***/
 DMAC->CTRL.reg &= ~(DMAC_CTRL_DMAENABLE);
 DMAC->CTRL.reg |= DMAC_CTRL_LVLEN(0xf);    
 DMAC->PRICTRL0.bit.RRLVLEN0 = 1;

 /***
  *** Configure The TX SPI DMA Channel:
  *** DMAC Channel 1 is used
  *** Disable the channel before configuring it
  ***/
 DMAC->CHID.reg = DMAC_CHID_ID(SPI_TX_CHANNEL);
 DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;

 /***
  *** Configure TX SPI DMA Channel: (continue...)
  *** DMA is used in STANDBY, therefore needs RUNSTDBY 
  *** Channel Priority Level = 0
  *** SERCOM0 (SPI) is used as source to trigger the DMA TX
  *** The trigger action is done for every DMA BEAT
  ***/
 DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY;
 DMAC->CHCTRLB.reg = (DMAC_CHCTRLB_LVL(0x0)|    
                      DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_TX)| 
                      DMAC_CHCTRLB_TRIGACT_BEAT);

 /*** Enabling DMA channel TX ***/
 DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
 while(DMAC->CHSTATUS.bit.BUSY);  

 /***
  *** Configure RX SPI DMA Channel:
  *** DMAC Channel 0 is used
  *** Disable the channel before configuring it
  ***/
 DMAC->CHID.reg = DMAC_CHID_ID(SPI_RX_CHANNEL);    
 DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;    

 /***
  *** Configure RX SPI DMA Channel: (continue...)
  *** DMA is used in STANDBY, therefore needs RUNSTDBY 
  *** Channel Priority Level = 0
  *** SERCOM0 (SPI) is used as source to trigger the DMA RX
  *** The trigger action is done for every DMA BEAT
  ***/
  DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY;
  DMAC->CHCTRLB.reg = ( DMAC_CHCTRLB_LVL(0x0)|    
                        DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_RX)|
                        DMAC_CHCTRLB_TRIGACT_BEAT );

 /*** enabling DMA interrupt on RX transfer completed ***/
 DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;        

 /*** Enabling DMA channel RX ***/
 DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
 while(DMAC->CHSTATUS.bit.BUSY);  

 /*** Enabling interrupt at core side ***/
 NVIC_EnableIRQ(DMAC_0_IRQn);

 /*** Finally Enable the DMA 
  *** (Enable Protected)
  ***/
 DMAC->CTRL.reg |= (DMAC_CTRL_DMAENABLE) ;    
}