| 
					
				 | 
			
			
				@@ -0,0 +1,398 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "scsi_accel_dma.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <AzulSCSI_log.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <gd32f20x_timer.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <gd32f20x_rcu.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <assert.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#ifdef SCSI_ACCEL_DMA_AVAILABLE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define DMA_BUF_SIZE 256 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define DMA_BUF_MASK (DMA_BUF_SIZE - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint8_t *app_buf; // Buffer provided by application 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t dma_buf[DMA_BUF_SIZE]; // Buffer of data formatted for GPIO BOP register 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t dma_idx; // Write index to DMA buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t dma_fillto; // Point up to which DMA buffer is available for refilling 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t timer_buf; // Control value for timer SWEVG register 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t bytes_app; // Bytes available in application buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t bytes_dma; // Bytes (words) written so far to DMA buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t scheduled_dma; // Bytes (words) that DMA data count was last set to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint8_t *next_app_buf; // Next buffer from application after current one finishes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t next_app_bytes; // Bytes in next buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} g_scsi_dma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+enum scsidma_state_t { SCSIDMA_IDLE = 0, SCSIDMA_WRITE, SCSIDMA_READ }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static volatile scsidma_state_t g_scsi_dma_state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void scsi_accel_dma_init() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma_state = SCSIDMA_IDLE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rcu_periph_clock_enable(SCSI_TIMER_RCU); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // DMA Channel A: data copy 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // GPIO DMA copies data from memory buffer to GPIO BOP register. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // The memory buffer is filled by interrupt routine. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dma_parameter_struct gpio_dma_config = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_width = DMA_PERIPHERAL_WIDTH_32BIT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_addr = 0, // Filled before transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_width = DMA_MEMORY_WIDTH_32BIT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .number = DMA_BUF_SIZE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .priority = DMA_PRIORITY_ULTRA_HIGH, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_inc = DMA_PERIPH_INCREASE_DISABLE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_inc = DMA_MEMORY_INCREASE_ENABLE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .direction = DMA_MEMORY_TO_PERIPHERAL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // DMA Channel B: timer update 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Timer DMA causes update event to restart timer after 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // GPIO DMA operation is done. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dma_parameter_struct timer_dma_config = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_addr = (uint32_t)&TIMER_SWEVG(SCSI_TIMER), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_width = DMA_PERIPHERAL_WIDTH_32BIT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_addr = (uint32_t)&g_scsi_dma.timer_buf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_width = DMA_MEMORY_WIDTH_32BIT, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .number = DMA_BUF_SIZE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .priority = DMA_PRIORITY_HIGH, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .periph_inc = DMA_PERIPH_INCREASE_DISABLE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .memory_inc = DMA_PERIPH_INCREASE_DISABLE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .direction = DMA_MEMORY_TO_PERIPHERAL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB, &timer_dma_config); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    NVIC_SetPriority(SCSI_TIMER_DMACHB_IRQn, 2); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    NVIC_EnableIRQ(SCSI_TIMER_DMACHB_IRQn); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.timer_buf = TIMER_SWEVG_UPG; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Timer is used to toggle the request signal based on external trigger input. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // OUT_REQ is driven by timer output. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 1. On timer update event, REQ is set low. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 2. When ACK goes low, timer counts and OUT_REQ is set high. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //    Simultaneously a DMA request is triggered to write next data to GPIO. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 3. When ACK goes high, a DMA request is triggered to cause timer update event. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //    The DMA request priority is set so that 2. always completes before it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CTL0(SCSI_TIMER) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CAR(SCSI_TIMER) = 65535; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_PSC(SCSI_TIMER) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_DMAINTEN(SCSI_TIMER) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CHCTL1(SCSI_TIMER) = 0x6074; // CH2 as fast PWM output, CH3 as DMA trigger 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CHCTL2(SCSI_TIMER) = TIMER_CHCTL2_CH2NEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CCHP(SCSI_TIMER) = TIMER_CCHP_POEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CH2CV(SCSI_TIMER) = 1; // REQ is low until ACK goes low 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CH3CV(SCSI_TIMER) = 2; // Reset timer after ACK goes high & previous DMA is complete 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    scsi_accel_dma_stopWrite(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // For debug 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpio_init(GPIOE, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Select whether OUT_REQ is connected to timer or GPIO port 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void scsi_dma_gpio_config(bool enable_timer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (enable_timer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gpio_init(SCSI_OUT_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        GPIO_BOP(GPIOE) = GPIO_PIN_0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gpio_init(SCSI_OUT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        GPIO_BC(GPIOE) = GPIO_PIN_0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Convert input bytes into BOP values in the DMA buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void refill_dmabuf() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check how many bytes we have available from the application 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check amount of free space in DMA buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (count > max) count = max; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (count == 0) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t *dst = g_scsi_dma.dma_buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t pos = g_scsi_dma.dma_idx; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t end = pos + count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.dma_idx = end; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.bytes_dma += count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (pos + 4 <= end) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        uint32_t input = *(uint32_t*)src; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        src += 4; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 0) & 0xFF]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 8) & 0xFF]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 16) & 0xFF]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 24) & 0xFF]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (pos + 4 <= end) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[*src++]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Start DMA transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void start_dma() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Disable channels while configuring 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CTL0(SCSI_TIMER) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Set new buffer address and size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // CHA / Data channel is in circular mode and always has DMA_BUF_SIZE buffer size. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // CHB / Update channel limits the number of data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.scheduled_dma += dma_to_schedule; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Clear pending DMA events 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_DMAINTEN(SCSI_TIMER) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Clear and enable interrupt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHB); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_FTFIE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Enable channels 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Make sure REQ is initially high 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CHCTL1(SCSI_TIMER) = 0x6050; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CHCTL1(SCSI_TIMER) = 0x6074; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Enable timer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CNT(SCSI_TIMER) = 16; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Generate first events 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH3G; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Stop DMA transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void stop_dma() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma_state = SCSIDMA_IDLE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Convert new data from application buffer to DMA buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+extern "C" void SCSI_TIMER_DMACHA_IRQ() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // azdbg("DMA irq A, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //             DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //             TIMER_CNT(SCSI_TIMER)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t intf = DMA_INTF(SCSI_TIMER_DMA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const uint32_t half_flag = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const uint32_t full_flag = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (intf & half_flag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (intf & full_flag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            azlog("ERROR: SCSI DMA overrun: ", intf, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               " bytes_app: ", g_scsi_dma.bytes_app, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               " bytes_dma: ", g_scsi_dma.bytes_dma, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               " dma_idx: ", g_scsi_dma.dma_idx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               " sched_dma: ", g_scsi_dma.scheduled_dma); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stop_dma(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else if (intf & full_flag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Fill DMA buffer with data from current application buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    refill_dmabuf(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check if we are at the end of the application buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Switch to next buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.app_buf = g_scsi_dma.next_app_buf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.bytes_dma = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.next_app_buf = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        g_scsi_dma.next_app_bytes = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refill_dmabuf(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Check if enough data is available to continue DMA transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+extern "C" void SCSI_TIMER_DMACHB_IRQ() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // azdbg("DMA irq B, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //             DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    //             TIMER_CNT(SCSI_TIMER)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t intf = DMA_INTF(SCSI_TIMER_DMA); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (intf & DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (g_scsi_dma.bytes_app > g_scsi_dma.scheduled_dma) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                __disable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                refill_dmabuf(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                __enable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Update the total number of bytes available for DMA 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            g_scsi_dma.scheduled_dma += dma_to_schedule; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // No more data available 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            stop_dma(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __disable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (g_scsi_dma_state == SCSIDMA_WRITE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.bytes_app) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Combine with currently running request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            g_scsi_dma.bytes_app += count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            count = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Combine with queued request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            g_scsi_dma.next_app_bytes += count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            count = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else if (!g_scsi_dma.next_app_buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Add as queued request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            g_scsi_dma.next_app_buf = (uint8_t*)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            g_scsi_dma.next_app_bytes = count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            count = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __enable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check if the request was combined 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (count == 0) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (g_scsi_dma_state != SCSIDMA_IDLE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // Wait for previous request to finish 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        scsi_accel_dma_finishWrite(resetFlag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (*resetFlag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    azdbg("Starting DMA write of ", (int)count, " bytes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    scsi_dma_gpio_config(true); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma_state = SCSIDMA_WRITE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.app_buf = (uint8_t*)data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.dma_idx = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.dma_fillto = DMA_BUF_SIZE; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.bytes_app = count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.bytes_dma = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.scheduled_dma = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.next_app_buf = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_scsi_dma.next_app_bytes = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    refill_dmabuf(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_dma(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+bool scsi_accel_dma_isWriteFinished(const uint8_t* data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check if everything has completed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (g_scsi_dma_state == SCSIDMA_IDLE) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Check if this data item is still in queue. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __disable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bool finished = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (data >= g_scsi_dma.app_buf + g_scsi_dma.bytes_dma && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data < g_scsi_dma.app_buf + g_scsi_dma.bytes_app) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        finished = false; // In current transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    else if (data >= g_scsi_dma.next_app_buf && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        finished = false; // In queued transfer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    __enable_irq(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return finished; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void scsi_accel_dma_stopWrite() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stop_dma(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    scsi_dma_gpio_config(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void scsi_accel_dma_finishWrite(volatile int *resetFlag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    uint32_t start = millis(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if ((uint32_t)(millis() - start) > 5000) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            azlog("scsi_accel_dma_finishWrite() timeout, DMA counts ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                TIMER_CNT(SCSI_TIMER)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            *resetFlag = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    scsi_accel_dma_stopWrite(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#endif 
			 |