
#include "common.h"
#include "sysinit.h"
#include "dma.h"

/*********************************************************************************************/   
/*********************************************************************************************/   
/* Use Always enabled DMA channels to transfer data, memory to memory
*
*   Input:  DMA pointer         (&DMA0 or &DMA1)
*           dma mux channel     (0-15)
*           dma source channel  (54-63) always enabled source 
*
*   Return: N/A
*
*   Description:
*
*   General DMA Config:
*
*     - MUX0 + MUX1 => DMA0
*     - MUX2 + MUX3 => DMA1
*
*
*   Always Enabled DMA Channels Config:
*
*     - DMA0/MUX0 OR DMA1/MUX3  (source channels 54-63)  ** Using this by default **
*     - DMA1/MUX2 OR DMA0/MUX1  (source channels 54-63)
*     *** Do NOT use more than one DMAx/MUXx configuration with the same channel source ! ***
*     *** For example, if using DMA0/MUX0 source 54, do NOT use DMA0/MUX1 source 54 ***
*     
*********************************************************************************************/   
/*********************************************************************************************/   
void dma_data_xfer(DMA_Type *ptrDMA, uint8_t dma_mux_ch0, uint8_t dma_source_ch, uint32_t source_addr, uint32_t destination_addr, uint32_t num_bytes)
{  
  uint32_t i;
  DMAMUX_Type *ptrDMAMUX;
  uint8_t dma_ch0, dma_ch1;
  
  /* setup globals that may be used in this test */
  //dma0_int_flag = 0;
  //dma1_int_flag = 0;

  /***********************************************************************/
  /* Make sure parameters passed in are valid */
  /* Each DMA has two MUX's.  Each MUX has 16 channels Max */
  /***********************************************************************/
  if (dma_mux_ch0 >= 16)
  {
    printf("\nError!  DMA mux channel needs to be 0-15 for correct DMA Mux connectivity! Exiting...\n");
    return;
  }
  else if ((ptrDMA != DMA0) && (ptrDMA != DMA1))
  {
    printf("\nWrong DMA pointer passed in!  Exiting...\n");
    return;
  }           
  
  if ((dma_source_ch > 63) || (dma_source_ch < 54))
  {
    printf("\nError!  Need to use always enabled DMA channel 54-63!  You tried: %d", dma_source_ch);
  }
  /***********************************************************************/
  /* End parameter checking */
  /***********************************************************************/
  

  /*************************************************************/
  /* Assign DMA MUX pointer */
  /* Here we are assuming DMA0/MUX0 and DMA1/MUX3 combination */
  /*************************************************************/
  if(ptrDMA == DMA0)
  {
    ptrDMAMUX = DMAMUX0;
    dma_ch0 = dma_mux_ch0;
    printf("Using DMA_0 and DMAMUX_0\n");
    printf("DMA_0 channel %d linked to DMA_0 channel %d\n", dma_ch0, dma_ch1);    
  }
  else //if (ptrDMA == &DMA1)
  {
    ptrDMAMUX = DMAMUX3;
    dma_ch0 = dma_mux_ch0 + 16;   //mux3, upper 16 channels of DMA
    printf("Using DMA_1 and DMAMUX_3\n");  
    printf("DMA_1 channel %d linked to DMA_1 channel %d\n", dma_ch0, dma_ch1); 
  }  
  
  /* Reset DMA channel priority */
  for (i = 0; i < 32; i++)
  {    
    /* These are setup by default, left for reference but don't update until we have to */
    /* set priority register 0-31 to lowest priority, later configure the channels we want */  
    //setup_dma_priority(ptrDMA, i /*channel*/, i /*priority*/);  /* 0=lowest priority */ 
  }
   
  /* Enable DMA and DMA mux clocks if not already */
  ENABLE_DMAx_CLOCKS;
  ENABLE_DMA_MUXx_CLOCKS;  
   
  /****************************************************************
  * 
  * PDB:  DMA0 (MUX0) / DMA1 (MUX3):  source 22
  *
  * dma_ch0 triggers dma_ch1 to run with major link setup
  *
  ******************************************************************/    
  /* Setup DMA MUX with source and enable it */
  /* DMA MUX are channels 0-15, then TCD for actual DMA depends on which MUX we are using */
  ptrDMAMUX->CHCFG[dma_mux_ch0] = DMAMUX_CHCFG_SOURCE(dma_source_ch);
  ptrDMAMUX->CHCFG[dma_mux_ch0] |= DMAMUX_CHCFG_ENBL_MASK;
  //ptrDMAMUX->CHCFG[dma_mux_ch1] = DMAMUX_CHCFG_SOURCE(dma_source_ch);
  //ptrDMAMUX->CHCFG[dma_mux_ch1] |= DMAMUX_CHCFG_ENBL_MASK;
 
  /* Assign group priority - group0 and group1 need to be different if using fixed priority mode ! */
  ptrDMA->CR = 0x00000300;
  
  /* Enable errors to interrupt */
  /* Only use if interrupt handler is setup and interrupts are enabled */
  //ptrDMA->SEEI.R = dma_ch0;
  //ptrDMA->SEEI.R = dma_ch1; 
  
  /* init dma channels to 0 to a known state before setup */
  init_dma_tcd(ptrDMA, dma_ch0);
  //init_dma_tcd(ptrDMA, dma_ch1);    //if using second channel for linking

  
  /* Setup source and destination for data transfer */
  /* One byte transfers */
  // Initialize dma channel for adc0
  ptrDMA->TCD[dma_ch0].DADDR = destination_addr;  			/* Destination Address */
  ptrDMA->TCD[dma_ch0].SADDR = source_addr;   				/* Source Address */
  ptrDMA->TCD[dma_ch0].NBYTES_MLNO = num_bytes;           	/* Num bytes */
  ptrDMA->TCD[dma_ch0].ATTR = 0x0; 							/* DSIZE is lower 3 bits -- 0=one byte, 2=one word (32bits) */
  ptrDMA->TCD[dma_ch0].ATTR |= (0x0 << 8);					/* SSIZE - bits 10:8 */
  ptrDMA->TCD[dma_ch0].SOFF = 0x1;							/* increment source address by 1 */
  ptrDMA->TCD[dma_ch0].DOFF = 0x1;							/* 0 = don't increment, 0x1 = one byte, 0x4 = one word */
  ptrDMA->TCD[dma_ch0].CITER_ELINKNO = 0x1;					/* Current iteration */
  ptrDMA->TCD[dma_ch0].BITER_ELINKNO = 0x1;					/* Starting Major iteration count */
  ptrDMA->TCD[dma_ch0].SLAST = -num_bytes;					/* Source last address adjustment */
  ptrDMA->TCD[dma_ch0].DLAST_SGA = -num_bytes;				/* Destination last address adjustment */
  //ptrDMA->TCD[dma_ch1].WORD7.B.INTMAJOR = 1;          	/* DMA interrupt on major loop iteration completion */
  //ptrDMA->TCD[dma_ch0].CSR = ((dma_ch1 << 8) | 0x0020);	/* use if linking to another channel */
  

 
  /***********************/
  /* START TRANSFER HERE */
  /* set START bit - normally if doing manual dma transfer we would use this */
  /***********************/
  ptrDMA->SSRT = dma_ch0;
 
  
  /*********************************/
  /* Wait for Channel to complete */
  /*********************************/
  while (ptrDMA->TCD[dma_ch0].CSR & DMA_CSR_DONE_MASK){};

  /* Data should be transferred at this point */
  
  /* Clear Enable request */
  ptrDMA->CERQ = dma_ch0;

  /* Clear Error request */
  ptrDMA->CEEI = dma_ch0;
  //ptrDMA->CEEI.R = dma_ch1; 
  
  /* Halt DMA */
  ptrDMA->CR |= DMA_CR_HALT_MASK;
  
  /* Disconnect channels */
  ptrDMAMUX->CHCFG[dma_mux_ch0] = 0;
  //ptrDMAMUX->CHCFG[dma_mux_ch1].R = 0;  
  
  /* Clear TCD channels */
  init_dma_tcd(ptrDMA, dma_ch0);
  //init_dma_tcd(ptrDMA, dma_ch1);    
  
  /* dummy wait - set bkpt if desired */
  for(i=0; i <= 100; i++){} 
}



/* Init all TCD words first to 0 */
void init_dma_tcd(DMA_Type *ptrDMA, int dma_ch)
{  
	ptrDMA->TCD[dma_ch].SADDR = 0;
	ptrDMA->TCD[dma_ch].SOFF = 0;
	ptrDMA->TCD[dma_ch].ATTR = 0;
	ptrDMA->TCD[dma_ch].NBYTES_MLNO = 0;
	ptrDMA->TCD[dma_ch].SLAST = 0;
	ptrDMA->TCD[dma_ch].DADDR = 0;
	ptrDMA->TCD[dma_ch].DOFF = 0;
	ptrDMA->TCD[dma_ch].CITER_ELINKNO = 0;
	ptrDMA->TCD[dma_ch].DLAST_SGA = 0;
	ptrDMA->TCD[dma_ch].CSR = 0;
	ptrDMA->TCD[dma_ch].BITER_ELINKNO = 0;
}




  /********************************************************************************************/
  /*
  *
  *   Example TCD setup which loads new delay values to PDB from an array, links to second channel
  *   Kept for reference!!
  *
  *
  **************************************************************************************************/
#if 0 
  // Initialize dma channel for adc0
  ptrDMA->TCD[dma_ch0].WORD4.B.DADDR = (uint32_t)&(PDB0_CH0DLY0);  //tcd.daddr = (uint32_t)&(PDB0_CH0DLY0);
  ptrDMA->TCD[dma_ch0].WORD0.B.SADDR = (uint32_t)&(dly0[0]);       //tcd.saddr = (uint32_t)&(dly0[0]);
  ptrDMA->TCD[dma_ch0].WORD2.B.NBYTES = 8;                         //tcd.nbytes = 8;
  ptrDMA->TCD[dma_ch0].WORD1.B.DSIZE = 0x2;                        //tcd.tcdAttr = DMA_ATTR_DSIZE(0x2)|DMA_ATTR_SSIZE(0x2) ; // set source and destination to 32 bit
  ptrDMA->TCD[dma_ch0].WORD1.B.SSIZE = 0x2; 
  ptrDMA->TCD[dma_ch0].WORD1.B.SOFF = 0x4;                           //tcd.soff =0x4;
  ptrDMA->TCD[dma_ch0].WORD5.B.DOFF = 0x4;                          //tcd.doff =0x4;
  ptrDMA->TCD[dma_ch0].WORD5.B.CITER = 0x1;                         //tcd.citer = 0x1;   
  ptrDMA->TCD[dma_ch0].WORD7.B.BITER = 0x1;                         //tcd.biter = 0x1;
  ptrDMA->TCD[dma_ch0].WORD3.B.SLAST = 0x0;                         //tcd.slast = 0;
  ptrDMA->TCD[dma_ch0].WORD6.B.DLASTSGA = -8;                       //tcd.dlast_sga = -8; 
  ptrDMA->TCD[dma_ch0].WORD7.R |= ((dma_ch1 << 8) | 0x0020);         //tcd.csr = 0x120;  //major link to dma_ch1  
       
  // Initialize ch1 for adc1 - triggered at end of major loop by dma_ch0
 
  /* future exansion */
  ptrDMA->TCD[dma_ch1].WORD4.B.DADDR = (uint32_t)&(PDB0_CH1DLY0);
  ptrDMA->TCD[dma_ch1].WORD0.B.SADDR = (uint32_t)&(dly1[0]);
  ptrDMA->TCD[dma_ch1].WORD2.B.NBYTES = 8;
  ptrDMA->TCD[dma_ch1].WORD1.B.DSIZE = 0x2;
  ptrDMA->TCD[dma_ch1].WORD1.B.SSIZE = 0x2;
  ptrDMA->TCD[dma_ch1].WORD1.B.SOFF = 0x4;                           //tcd.soff =0x4;
  ptrDMA->TCD[dma_ch1].WORD5.B.DOFF = 0x4;                          //tcd.doff =0x4;
  ptrDMA->TCD[dma_ch1].WORD5.B.CITER = 0x1;                         //tcd.citer = 0x1;   
  ptrDMA->TCD[dma_ch1].WORD7.B.BITER = 0x1;                         //tcd.biter = 0x1;
  ptrDMA->TCD[dma_ch1].WORD3.B.SLAST = 0x0;                    //tcd.slast = 0;
  ptrDMA->TCD[dma_ch1].WORD7.B.INTMAJOR = 1;   /*** Major loop complete will interrupt DMA, then LDOK will be written to PDB to load next delay values */                                               
  ptrDMA->TCD[dma_ch1].WORD6.B.DLASTSGA = -8;                       //tcd.dlast_sga = -8; 
#endif
