/* * This file is part of FH8620 BSP for RT-Thread distribution. * * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. * All rights reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Visit http://www.fullhan.com to get contact with Fullhan. * * Change Logs: * Date Author Notes */ /***************************************************************************** * Include Section * add all #include here *****************************************************************************/ //#include "drivers/fh_dma.h" #include "fh_dma.h" #include "mmu.h" #include "drivers/dma.h" #include #include #include #include "fh_arch.h" #include "mmu.h" #include "fh_def.h" /***************************************************************************** * Define section * add all #define here *****************************************************************************/ //#define DMA_DEBUG #ifdef DMA_DEBUG #define FH_DMA_DEBUG(fmt, args...) \ rt_kprintf(fmt,##args); #else #define FH_DMA_DEBUG(fmt, args...) #endif #define DMA_REG_BASE (0xEE000000) #define DMA_CONTROLLER_NUMBER (1) #define WORK_QUEUE_STACK_SIZE 512 #define WORK_QUEUE_PRIORITY 12 #define TEST_PER_NO (10) #define DESC_MAX_SIZE (20) /********************************* * * copy from the linux core start * *********************************/ //this is the ip reg offset....don't change!!!!!!! #define DW_DMA_MAX_NR_CHANNELS 8 /* * Redefine this macro to handle differences between 32- and 64-bit * addressing, big vs. little endian, etc. */ #define DW_REG(name) rt_uint32_t name; rt_uint32_t __pad_##name /* Hardware register definitions. */ struct dw_dma_chan_regs { DW_REG(SAR); /* Source Address Register */ DW_REG(DAR); /* Destination Address Register */ DW_REG(LLP); /* Linked List Pointer */ rt_uint32_t CTL_LO; /* Control Register Low */ rt_uint32_t CTL_HI; /* Control Register High */ DW_REG(SSTAT); DW_REG(DSTAT); DW_REG(SSTATAR); DW_REG(DSTATAR); rt_uint32_t CFG_LO; /* Configuration Register Low */ rt_uint32_t CFG_HI; /* Configuration Register High */ DW_REG(SGR); DW_REG(DSR); }; struct dw_dma_irq_regs { DW_REG(XFER); DW_REG(BLOCK); DW_REG(SRC_TRAN); DW_REG(DST_TRAN); DW_REG(ERROR); }; struct dw_dma_regs { /* per-channel registers */ struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; /* irq handling */ struct dw_dma_irq_regs RAW; /* r */ struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ DW_REG(STATUS_INT); /* r */ /* software handshaking */ DW_REG(REQ_SRC); DW_REG(REQ_DST); DW_REG(SGL_REQ_SRC); DW_REG(SGL_REQ_DST); DW_REG(LAST_SRC); DW_REG(LAST_DST); /* miscellaneous */ DW_REG(CFG); DW_REG(CH_EN); DW_REG(ID); DW_REG(TEST); /* optional encoded params, 0x3c8..0x3 */ }; /* Bitfields in CTL_LO */ #define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ #define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ #define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) #define DWC_CTLL_DST_INC_MODE(n) ((n)<<7) #define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ #define DWC_CTLL_DST_DEC (1<<7) #define DWC_CTLL_DST_FIX (2<<7) #define DWC_CTLL_SRC_INC_MODE(n) ((n)<<9) #define DWC_CTLL_SRC_INC (0<<9) /* SAR update/not */ #define DWC_CTLL_SRC_DEC (1<<9) #define DWC_CTLL_SRC_FIX (2<<9) #define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ #define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) #define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ #define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ #define DWC_CTLL_FC(n) ((n) << 20) #define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ #define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ #define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ #define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ /* plus 4 transfer types for peripheral-as-flow-controller */ #define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ #define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ #define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ #define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ /* Bitfields in CTL_HI */ #define DWC_CTLH_DONE 0x00001000 #define DWC_CTLH_BLOCK_TS_MASK 0x00000fff /* Bitfields in CFG_LO. Platform-configurable bits are in */ #define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ #define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ #define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ #define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ #define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ #define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ #define DWC_CFGL_MAX_BURST(x) ((x) << 20) #define DWC_CFGL_RELOAD_SAR (1 << 30) #define DWC_CFGL_RELOAD_DAR (1 << 31) /* Bitfields in CFG_HI. Platform-configurable bits are in */ #define DWC_CFGH_DS_UPD_EN (1 << 5) #define DWC_CFGH_SS_UPD_EN (1 << 6) /* Bitfields in SGR */ #define DWC_SGR_SGI(x) ((x) << 0) #define DWC_SGR_SGC(x) ((x) << 20) /* Bitfields in DSR */ #define DWC_DSR_DSI(x) ((x) << 0) #define DWC_DSR_DSC(x) ((x) << 20) /* Bitfields in CFG */ #define DW_CFG_DMA_EN (1 << 0) #define DW_REGLEN 0x400 /* Platform-configurable bits in CFG_HI */ #define DWC_CFGH_FCMODE (1 << 0) #define DWC_CFGH_FIFO_MODE (1 << 1) #define DWC_CFGH_PROTCTL(x) ((x) << 2) #define DWC_CFGH_SRC_PER(x) ((x) << 7) #define DWC_CFGH_DST_PER(x) ((x) << 11) /* Platform-configurable bits in CFG_LO */ #define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ #define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) #define DWC_CFGL_LOCK_CH_XACT (2 << 12) #define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */ #define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14) #define DWC_CFGL_LOCK_BUS_XACT (2 << 14) #define DWC_CFGL_LOCK_CH (1 << 15) /* channel lockout */ #define DWC_CFGL_LOCK_BUS (1 << 16) /* busmaster lockout */ #define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */ #define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */ #define lift_shift_bit_num(bit_num) (1<(b))?(a):(b)) #define dw_readl(dw, name) \ __raw_readl(&(((struct dw_dma_regs *)dw->regs)->name)) #define dw_writel(dw, name, val) \ __raw_writel((val), &(((struct dw_dma_regs *)dw->regs)->name)) #define dw_readw(dw, name) \ __raw_readw(&(((struct dw_dma_regs *)dw->regs)->name)) #define dw_writew(dw, name, val) \ __raw_writew((val), &(((struct dw_dma_regs *)dw->regs)->name)) #define CHANNEL0 (lift_shift_bit_num(0)) #define CHANNEL1 (lift_shift_bit_num(1)) #define CHANNEL2 (lift_shift_bit_num(2)) #define CHANNEL3 (lift_shift_bit_num(3)) #define channel_set_bit(dw, reg, mask) \ dw_writel(dw, reg, ((mask) << 8) | (mask)) #define channel_clear_bit(dw, reg, mask) \ dw_writel(dw, reg, ((mask) << 8) | 0) /**************************************************************************** * ADT section * add definition of user defined Data Type that only be used in this file here ***************************************************************************/ struct dw_dma{ //vadd void *regs; //padd rt_uint32_t paddr; rt_uint32_t irq; rt_uint32_t channel_max_number; #define CONTROLLER_STATUS_CLOSED (0) #define CONTROLLER_STATUS_OPEN (1) rt_uint32_t controller_status; #define FH81_DMA_INIT_NOT_YET (0) #define FH81_DMA_INIT_ALREADY (1) rt_uint32_t init; rt_uint32_t id; char *name; rt_uint32_t channel_work_done; }; struct dma_channel { #define CHANNEL_STATUS_CLOSED (0) #define CHANNEL_STATUS_OPEN (1) #define CHANNEL_STATUS_IDLE (2) #define CHANNEL_STATUS_BUSY (3) rt_uint32_t channel_status; //open, busy ,closed rt_uint32_t desc_trans_size; //isr will set it complete. struct rt_completion transfer_completion; //add lock,when set the channel.lock it struct rt_semaphore channel_lock; //struct rt_mutex lock; //rt_enter_critical(); rt_list_t queue; //active transfer now!!! struct dma_transfer *active_trans; #define SINGLE_TRANSFER (0) #define CYCLIC_TRANSFER (1) #define DEFAULT_TRANSFER SINGLE_TRANSFER rt_uint32_t open_flag; // //new add para... rt_uint32_t desc_total_no; rt_uint32_t free_index; rt_uint32_t used_index; rt_uint32_t desc_left_cnt; rt_uint32_t allign_malloc; struct dw_lli *base_lli; }; struct fh81_dma{ //core use ,this must be the first para!!!! struct rt_dma_device parent; //myown struct dw_dma dwc; //channel obj struct dma_channel dma_channel[FH81_MAX_CHANNEL]; //struct rt_workqueue* isr_workqueue; //struct rt_work *isr_work; }; #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = rt_list_entry((head)->next, typeof(*pos), member), \ n = rt_list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = rt_list_entry(n->member.next, typeof(*n), member)) /****************************************************************************** * Function prototype section * add prototypes for all functions called by this file,execepting those * declared in header file *****************************************************************************/ /***************************************************************************** * Global variables section - Exported * add declaration of global variables that will be exported here * e.g. * int8_t foo; ****************************************************************************/ /***************************************************************************** * static fun; *****************************************************************************/ static rt_err_t init (struct rt_dma_device *dma); static rt_err_t control (struct rt_dma_device *dma, int cmd, void *arg); static void rt_fh_dma_cyclic_stop(struct dma_transfer *p); static void rt_fh_dma_cyclic_start(struct dma_transfer *p); static void rt_fh_dma_cyclic_prep(struct fh81_dma * fh81_dma_p,struct dma_transfer *p); static void rt_fh_dma_cyclic_free(struct dma_transfer *p); static struct rt_dma_ops fh81_dma_ops = { init, control }; /***************************************************************************** * Global variables section - Local * define global variables(will be refered only in this file) here, * static keyword should be used to limit scope of local variable to this file * e.g. * static uint8_t ufoo; *****************************************************************************/ static struct fh81_dma fh81_dma_controller[DMA_CONTROLLER_NUMBER] = {0}; /* function body */ /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static rt_uint32_t allign_func(rt_uint32_t in_addr,rt_uint32_t allign_size){ return (in_addr + allign_size-1) & (~(allign_size - 1)); } struct dw_lli * get_desc(struct fh81_dma *p_dma,struct dma_transfer *p_transfer,rt_uint32_t lli_size){ struct dw_lli * ret_lli; rt_uint32_t free_index; rt_uint32_t allign_left; rt_uint32_t totoal_desc; rt_uint32_t actual_get_desc; rt_uint32_t totoal_free_desc; totoal_free_desc = p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt; free_index = p_dma->dma_channel[p_transfer->channel_number].free_index; totoal_desc = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; allign_left = totoal_desc - free_index; //check first.. if(totoal_free_desc < lli_size){ rt_kprintf("not enough desc to get...\n"); rt_kprintf("get size is %d,left is %d\n",lli_size,totoal_free_desc); return RT_NULL; } //rt_kprintf("get desc in...\n"); //rt_kprintf("lli size is %d\n",lli_size); if(lli_size > allign_left){ //if allign desc not enough...just reset null.... if((totoal_free_desc - allign_left) < lli_size){ rt_kprintf("not enough desc to get...\n"); rt_kprintf("app need size is %d, totoal left is %d, allign left is %d\n",lli_size,totoal_free_desc,allign_left); rt_kprintf("from head to get desc size is %d, actual get is %d\n",(totoal_free_desc - allign_left),(allign_left +lli_size)); return RT_NULL; } else{ actual_get_desc = allign_left +lli_size; free_index = 0; } } //ret_lli = &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index]; ret_lli = &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index]; // rt_kprintf("get desc base index addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[0]); // rt_kprintf("get desc free index addr:%08x\n",(rt_uint32_t)ret_lli); // rt_kprintf("get desc request size:%08x\n",lli_size); // rt_kprintf("get desc total size:%08x\n",p_dma->dma_channel[p_transfer->channel_number].desc_total_no); // rt_kprintf("one desc size is:%08x\n",sizeof( struct dw_lli)); p_dma->dma_channel[p_transfer->channel_number].free_index += actual_get_desc; //rt_kprintf("get desc free index addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[p_dma->dma_channel[p_transfer->channel_number].free_index]); p_dma->dma_channel[p_transfer->channel_number].free_index %= p_dma->dma_channel[p_transfer->channel_number].desc_total_no; p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt -= actual_get_desc; p_transfer->lli_size = lli_size; p_transfer->actual_lli_size = actual_get_desc; return ret_lli; } rt_uint32_t put_desc(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ struct dw_lli * ret_lli; rt_uint32_t used_index; rt_uint32_t lli_size; //rt_kprintf("put desc in...\n"); used_index = p_dma->dma_channel[p_transfer->channel_number].used_index; lli_size = p_transfer->actual_lli_size; p_dma->dma_channel[p_transfer->channel_number].used_index += lli_size; p_dma->dma_channel[p_transfer->channel_number].used_index %= p_dma->dma_channel[p_transfer->channel_number].desc_total_no; p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt += lli_size; p_transfer->lli_size = 0; p_transfer->actual_lli_size = 0; return 0; } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static rt_err_t init (struct rt_dma_device *dma){ //init the clk table struct fh81_dma *my_own = (struct fh81_dma *)dma->parent.user_data; FH_DMA_DEBUG("my_own value:0x%x\n",(rt_uint32_t)my_own); //check the user data RT_ASSERT(my_own != RT_NULL); return RT_EOK; } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static void handle_dma_open(struct fh81_dma *p_dma){ rt_uint32_t i; struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; dw_writel(temp_dwc, CFG, 1); p_dma->dwc.controller_status = CONTROLLER_STATUS_OPEN; } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static void handle_dma_close(struct fh81_dma *p_dma){ rt_uint32_t i; struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; //take lock for(i=0;idwc.channel_max_number;i++){ rt_sem_take(&p_dma->dma_channel[i].channel_lock, RT_WAITING_FOREVER); channel_clear_bit(temp_dwc, CH_EN, lift_shift_bit_num(i)); p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_CLOSED; } dw_writel(temp_dwc, CFG, 0); p_dma->dwc.controller_status = CONTROLLER_STATUS_CLOSED; //release lock for(i=0;idwc.channel_max_number;i++){ rt_sem_release(&p_dma->dma_channel[i].channel_lock); } //destroy the workqueue.. //rt_workqueue_destroy(p_dma->isr_workqueue); } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ #define CHANNEL_REAL_FREE (0) #define CHANNEL_NOT_FREE (1) static rt_uint32_t check_channel_real_free(struct fh81_dma *p_dma,rt_uint32_t channel_number){ struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; rt_uint32_t ret_status; RT_ASSERT(channel_number < p_dma->dwc.channel_max_number); ret_status = dw_readl(temp_dwc, CH_EN); if(ret_status & lift_shift_bit_num(channel_number)){ //the channel is still busy!!!error here //FH_DMA_DEBUG("auto request channel error\n"); return CHANNEL_NOT_FREE; } return CHANNEL_REAL_FREE; } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static rt_err_t handle_request_channel(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ rt_uint32_t i; struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; rt_err_t ret_status = RT_EOK; //handle if auto check channel... if(p_transfer->channel_number == AUTO_FIND_CHANNEL){ //check each channel lock,find a free channel... for(i=0;idwc.channel_max_number;i++){ ret_status = rt_sem_trytake(&p_dma->dma_channel[i].channel_lock); if(ret_status == RT_EOK){ break; } } if(i < p_dma->dwc.channel_max_number){ ret_status = check_channel_real_free(p_dma,i); if(ret_status!= CHANNEL_REAL_FREE){ FH_DMA_DEBUG("auto request channel error\n"); RT_ASSERT(ret_status == CHANNEL_REAL_FREE); } //caution : channel is already locked here.... p_transfer->channel_number = i; //bind to the controller. //p_transfer->dma_controller = p_dma; p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_OPEN; } else return -RT_ENOMEM; } // request channel by user else{ // RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); ret_status = rt_sem_take(&p_dma->dma_channel[p_transfer->channel_number].channel_lock, RT_TICK_PER_SECOND*50); if(ret_status != RT_EOK) return -RT_ENOMEM; //rt_enter_critical(); ret_status = check_channel_real_free(p_dma,p_transfer->channel_number); if(ret_status!= CHANNEL_REAL_FREE){ FH_DMA_DEBUG("user request channel error\n"); RT_ASSERT(ret_status == CHANNEL_REAL_FREE); } //bind to the controller //p_transfer->dma_controller = p_dma; p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_OPEN; //rt_exit_critical(); } //malloc desc for this one channel... //fix me.... p_dma->dma_channel[p_transfer->channel_number].allign_malloc = (rt_uint32_t) rt_malloc( (p_dma->dma_channel[p_transfer->channel_number].desc_total_no * sizeof(struct dw_lli)) + CACHE_LINE_SIZE); if(!p_dma->dma_channel[p_transfer->channel_number].allign_malloc){ //release channel rt_kprintf("[dma]: no mem to malloc channel%d desc..\n",p_transfer->channel_number); p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_CLOSED; rt_sem_release(&p_dma->dma_channel[p_transfer->channel_number].channel_lock); return -RT_ENOMEM; } p_dma->dma_channel[p_transfer->channel_number].base_lli = (struct dw_lli *) allign_func( p_dma->dma_channel[p_transfer->channel_number].allign_malloc, CACHE_LINE_SIZE); FH_DMA_DEBUG("dma desc addr is %x\n",(rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli); //t1 = (UINT32)rt_malloc(GMAC_TX_RING_SIZE * sizeof(Gmac_Tx_DMA_Descriptors) + CACHE_LINE_SIZE); if(!p_dma->dma_channel[p_transfer->channel_number].base_lli){ FH_DMA_DEBUG("request desc failed..\n"); RT_ASSERT(p_dma->dma_channel[p_transfer->channel_number].base_lli != RT_NULL); } if((rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli % 32){ rt_kprintf("malloc is not cache allign.."); } //rt_memset((void *)dma_trans_desc->first_lli, 0, lli_size * sizeof(struct dw_lli)); rt_memset((void *) p_dma->dma_channel[p_transfer->channel_number].base_lli, 0, p_dma->dma_channel[p_transfer->channel_number].desc_total_no * sizeof(struct dw_lli)); p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; p_dma->dma_channel[p_transfer->channel_number].free_index = 0; p_dma->dma_channel[p_transfer->channel_number].used_index = 0; return RT_EOK; } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static rt_uint32_t handle_release_channel(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ rt_uint32_t i; struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; rt_uint32_t ret_status; //rt_enter_critical(); ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status; RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); if(ret_status == CHANNEL_STATUS_CLOSED){ FH_DMA_DEBUG("release channel error,reason: release a closed channel!!\n"); RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED); } channel_clear_bit(temp_dwc, CH_EN, lift_shift_bit_num(p_transfer->channel_number)); rt_sem_release(&p_dma->dma_channel[p_transfer->channel_number].channel_lock); //p_transfer->dma_controller = RT_NULL; p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_CLOSED; p_dma->dma_channel[p_transfer->channel_number].open_flag = DEFAULT_TRANSFER; //rt_exit_critical(); //release this channel malloc mem... //fix me..... rt_free((void *)p_dma->dma_channel[p_transfer->channel_number].allign_malloc); p_dma->dma_channel[p_transfer->channel_number].allign_malloc = RT_NULL; p_dma->dma_channel[p_transfer->channel_number].base_lli = RT_NULL; p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; p_dma->dma_channel[p_transfer->channel_number].free_index = 0; p_dma->dma_channel[p_transfer->channel_number].used_index = 0; return RT_EOK; } static rt_uint32_t cal_lli_size(struct dma_transfer *p_transfer){ RT_ASSERT(p_transfer != RT_NULL); RT_ASSERT(p_transfer->dma_controller != RT_NULL); RT_ASSERT(p_transfer->src_width <= DW_DMA_SLAVE_WIDTH_32BIT); rt_uint32_t lli_number = 0; rt_uint32_t channel_max_trans_per_lli = 0; channel_max_trans_per_lli = p_transfer->dma_controller->dma_channel[p_transfer->channel_number].desc_trans_size; lli_number = (p_transfer->trans_len % channel_max_trans_per_lli) ? 1:0; lli_number += p_transfer->trans_len / channel_max_trans_per_lli; return lli_number; } static void dump_lli(struct dw_lli *p_lli){ FH_DMA_DEBUG("link_mem padd:0x%x\n sar:0x%x\n dar:0x%x\n llp:0x%x\n ctllo:0x%x\n ctlhi:0x%x\n sstat:0x%x\n dstat:0x%x\n", (rt_uint32_t)p_lli,p_lli->sar, p_lli->dar, p_lli->llp, p_lli->ctllo, p_lli->ctlhi,p_lli->sstat,p_lli->dstat); } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static void handle_single_transfer(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ rt_uint32_t i; struct dw_dma *temp_dwc; temp_dwc = &p_dma->dwc; volatile rt_uint32_t ret_status; rt_list_t *p_controller_list; rt_uint32_t lli_size,max_trans_size; struct dw_lli *p_lli = RT_NULL; struct dma_transfer *dma_trans_desc; struct dma_transfer *_dma_trans_desc; rt_uint32_t temp_src_add; rt_uint32_t temp_dst_add; rt_uint32_t trans_total_len = 0; rt_uint32_t temp_trans_size = 0; //rt_uint32_t dma_channl_no = 0; RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); RT_ASSERT(p_transfer->dma_number < DMA_CONTROLLER_NUMBER); RT_ASSERT(&fh81_dma_controller[p_transfer->dma_number] == p_dma); //when the dma transfer....the lock should be 0!!!! //or user may not request the channel... RT_ASSERT(p_dma->dma_channel[p_transfer->channel_number].channel_lock.value == 0); ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status; if(ret_status == CHANNEL_STATUS_CLOSED){ FH_DMA_DEBUG("transfer error,reason: use a closed channel..\n"); RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED); } p_transfer->dma_controller = p_dma; rt_list_init(&p_transfer->transfer_list); max_trans_size = p_transfer->dma_controller->dma_channel[p_transfer->channel_number].desc_trans_size; //add transfer to the controller's queue list //here should insert before and handle after....this could be a fifo... rt_list_insert_before(&p_dma->dma_channel[p_transfer->channel_number].queue , &p_transfer->transfer_list); p_controller_list = &p_dma->dma_channel[p_transfer->channel_number].queue; //here the driver could make a queue to cache the transfer and kick a thread to handle the queue~~~ //but now,this is a easy version...,just handle the transfer now!!! list_for_each_entry_safe(dma_trans_desc, _dma_trans_desc, p_controller_list, transfer_list) { //the dma controller could see the active transfer ..... p_transfer->dma_controller->dma_channel[p_transfer->channel_number].active_trans = dma_trans_desc; trans_total_len = p_transfer->trans_len; //handle desc //step1:cal lli size... lli_size = cal_lli_size(dma_trans_desc); //step2:malloc lli_size mem //dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(lli_size * sizeof(struct dw_lli)); dma_trans_desc->first_lli = get_desc(p_dma,p_transfer,lli_size); //not enough mem.. if(dma_trans_desc->first_lli == RT_NULL){ FH_DMA_DEBUG("transfer error,reason: not enough mem..\n"); RT_ASSERT(dma_trans_desc->first_lli != RT_NULL); } //bug here.... rt_memset((void *)dma_trans_desc->first_lli, 0, lli_size * sizeof(struct dw_lli)); p_lli = dma_trans_desc->first_lli; //warnning!!!!must check if the add is 32bits ally... RT_ASSERT(((rt_uint32_t)p_lli & 0x03) == 0); RT_ASSERT(dma_trans_desc->dst_inc_mode <=DW_DMA_SLAVE_FIX); RT_ASSERT(dma_trans_desc->src_inc_mode <=DW_DMA_SLAVE_FIX); //step3: set the mem.. for(i=0;idst_inc_mode){ case DW_DMA_SLAVE_INC: temp_dst_add = dma_trans_desc->dst_add + i * max_trans_size * (1<dst_width); break; case DW_DMA_SLAVE_DEC: temp_dst_add = dma_trans_desc->dst_add - i * max_trans_size * (1<dst_width); break; case DW_DMA_SLAVE_FIX: temp_dst_add = dma_trans_desc->dst_add; break; } switch(dma_trans_desc->src_inc_mode){ case DW_DMA_SLAVE_INC: temp_src_add = dma_trans_desc->src_add + i * max_trans_size * (1<src_width); break; case DW_DMA_SLAVE_DEC: temp_src_add = dma_trans_desc->src_add - i * max_trans_size * (1<src_width); break; case DW_DMA_SLAVE_FIX: temp_src_add = dma_trans_desc->src_add ; break; } p_lli[i].sar = temp_src_add; p_lli[i].dar = temp_dst_add; //para ctl temp_trans_size = (trans_total_len / max_trans_size)? max_trans_size : (trans_total_len % max_trans_size); trans_total_len -= temp_trans_size; RT_ASSERT(dma_trans_desc->dst_width <=DW_DMA_SLAVE_WIDTH_32BIT); RT_ASSERT(dma_trans_desc->src_width <=DW_DMA_SLAVE_WIDTH_32BIT); RT_ASSERT(dma_trans_desc->dst_msize <=DW_DMA_SLAVE_MSIZE_256); RT_ASSERT(dma_trans_desc->src_msize <=DW_DMA_SLAVE_MSIZE_256); RT_ASSERT(dma_trans_desc->fc_mode <=DMA_P2P); p_lli[i].ctllo = DWC_CTLL_INT_EN|DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width)|DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) |DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode)|DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) |DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize)|DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize)|DWC_CTLL_FC(dma_trans_desc->fc_mode) |DWC_CTLL_DMS(0)|DWC_CTLL_SMS(0); //block size p_lli[i].ctlhi = temp_trans_size; if(trans_total_len > 0){ p_lli[i].llp = (rt_uint32_t)&p_lli[i+1]; p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; } //flush cache to mem mmu_clean_invalidated_dcache((rt_uint32_t)&p_lli[i],sizeof(struct dw_lli)); dump_lli(&p_lli[i]); } //clear the isr status //set the dma config reg //clear cfg reload reg //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); //ret_status &= ~(DWC_CFGL_RELOAD_SAR|DWC_CFGL_RELOAD_DAR); dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,0); //set the first link add //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].LLP); ret_status = 0; ret_status = (rt_uint32_t)&p_lli[0]; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].LLP,ret_status); //set link enable //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_LO); ret_status = 0; ret_status =DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_LO,ret_status); dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_HI,0); //set handshaking RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING); RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING); if(dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING){ ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); ret_status |= DWC_CFGL_HS_DST; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); } else{ ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); ret_status &= ~DWC_CFGL_HS_DST; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); } if(dma_trans_desc->src_hs == DMA_SW_HANDSHAKING){ ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); ret_status |= DWC_CFGL_HS_SRC; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); } else{ ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); ret_status &= ~DWC_CFGL_HS_SRC; dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); } //only hw handshaking need this.. switch(dma_trans_desc->fc_mode){ case DMA_M2M: break; case DMA_M2P: //set dst per... RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); //clear 43 ~ 46 bit ret_status &= ~0x7800; ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per); dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); //DWC_CFGH_SRC_PER break; case DMA_P2M: //set src per... RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); //clear 39 ~ 42 bit ret_status &= ~0x780; ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per); dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); break; case DMA_P2P: //set src and dst.. RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); ret_status &= ~0x7800; ret_status &= ~0x780; ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) | DWC_CFGH_DST_PER(dma_trans_desc->dst_per); dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); break; default: break; } dma_trans_desc->dma_controller->dma_channel[dma_trans_desc->channel_number].channel_status = CHANNEL_STATUS_BUSY; //enable isr... channel_set_bit(temp_dwc, MASK.XFER, lift_shift_bit_num(dma_trans_desc->channel_number)); channel_set_bit(temp_dwc, MASK.ERROR, lift_shift_bit_num(dma_trans_desc->channel_number)); //close channel_clear_bit(temp_dwc, MASK.BLOCK, lift_shift_bit_num(dma_trans_desc->channel_number)); dw_writel(temp_dwc, CLEAR.XFER, 1<<(dma_trans_desc->channel_number)); dw_writel(temp_dwc, CLEAR.BLOCK, 1<<(dma_trans_desc->channel_number)); dw_writel(temp_dwc, CLEAR.SRC_TRAN, 1<<(dma_trans_desc->channel_number)); dw_writel(temp_dwc, CLEAR.DST_TRAN, 1<<(dma_trans_desc->channel_number)); dw_writel(temp_dwc, CLEAR.ERROR, 1<<(dma_trans_desc->channel_number)); ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); FH_DMA_DEBUG("cfg_hi value:0x%x\n",ret_status); ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); FH_DMA_DEBUG("cfg_low value:0x%x\n",ret_status); ret_status = dw_readl(temp_dwc, MASK.BLOCK); FH_DMA_DEBUG("mask block value:0x%x\n",ret_status); ret_status = dw_readl(temp_dwc, MASK.XFER); FH_DMA_DEBUG("mask xfer value:0x%x\n",ret_status); if(dma_trans_desc->prepare_callback){ dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para); } //enable the channle to transfer channel_set_bit(temp_dwc, CH_EN, lift_shift_bit_num(dma_trans_desc->channel_number)); } } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ static rt_err_t control (struct rt_dma_device *dma, int cmd, void *arg){ struct fh81_dma *my_own = (struct fh81_dma *)dma->parent.user_data; rt_uint32_t i; struct dw_dma *dwc; dwc = &my_own->dwc; rt_err_t ret = RT_EOK; struct dma_transfer *p_dma_transfer = (struct dma_transfer *)arg; //FH_DMA_DEBUG("p_dma_transfer value:0x%x\n",(rt_uint32_t)p_dma_transfer); RT_ASSERT(my_own != RT_NULL); RT_ASSERT(dwc != RT_NULL); switch(cmd){ case RT_DEVICE_CTRL_DMA_OPEN: //open the controller.. handle_dma_open(my_own); break; case RT_DEVICE_CTRL_DMA_CLOSE: //close the controller.. handle_dma_close(my_own); break; case RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL: //request a channel for the user RT_ASSERT(p_dma_transfer != RT_NULL); ret = handle_request_channel(my_own,p_dma_transfer); break; case RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL: //release a channel RT_ASSERT(p_dma_transfer != RT_NULL); ret = handle_release_channel(my_own,p_dma_transfer); break; case RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER: //make a channel to transfer data. RT_ASSERT(p_dma_transfer != RT_NULL); //check if the dma channel is open,or return error. my_own->dma_channel[p_dma_transfer->channel_number].open_flag = SINGLE_TRANSFER; handle_single_transfer(my_own,p_dma_transfer); //then wait for the channel is complete.. //caution that::we should be in the "rt_enter_critical()"when set the dma to work. break; case RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE: RT_ASSERT(p_dma_transfer != RT_NULL); my_own->dma_channel[p_dma_transfer->channel_number].open_flag = CYCLIC_TRANSFER; rt_fh_dma_cyclic_prep(my_own,p_dma_transfer); break; case RT_DEVICE_CTRL_DMA_CYCLIC_START: rt_fh_dma_cyclic_start(p_dma_transfer); break; case RT_DEVICE_CTRL_DMA_CYCLIC_STOP: rt_fh_dma_cyclic_stop(p_dma_transfer); break; case RT_DEVICE_CTRL_DMA_CYCLIC_FREE: rt_fh_dma_cyclic_free(p_dma_transfer); break; default: break; } return ret; } static void rt_fh81_dma_isr(int irq, void *param) { RT_ASSERT(irq == DMAC_IRQn); rt_uint32_t isr_channel_x,i,error,isr_channel_b; struct fh81_dma *my_own = (struct fh81_dma *)param; struct dw_dma *dwc; struct dma_transfer *p_transfer; dwc = &my_own->dwc; //p_transfer = //rt_kprintf("dma isr get in~~~\n"); error = dw_readl(dwc,STATUS.ERROR); if(error != 0){ FH_DMA_DEBUG("dma isr error!!!!\n"); RT_ASSERT(error == RT_NULL); } isr_channel_x = dw_readl(dwc,STATUS.XFER); isr_channel_b = dw_readl(dwc,STATUS.BLOCK); //for single check the transfer status //check which channel... for(i=0;idwc.channel_max_number;i++){ if(my_own->dma_channel[i].open_flag == SINGLE_TRANSFER){ if(isr_channel_x & 1<dma_channel[i].active_trans; if(p_transfer->complete_callback){ p_transfer->complete_callback(p_transfer->complete_para); } p_transfer->dma_controller->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_IDLE; //here is a bug...do not free here //rt_free(p_transfer->first_lli); put_desc(my_own,p_transfer); rt_list_remove(&p_transfer->transfer_list); } } else if(my_own->dma_channel[i].open_flag == CYCLIC_TRANSFER){ if(isr_channel_b & 1<dma_channel[i].active_trans; dw_writel(dwc, CLEAR.BLOCK, 1<<(p_transfer->channel_number)); if(p_transfer->complete_callback){ p_transfer->complete_callback(p_transfer->complete_para); } } } } } /***************************************************************************** * Description: * add funtion description here * Parameters: * description for each argument, new argument starts at new line * Return: * what does this function returned? *****************************************************************************/ const char *channel_lock_name[FH81_MAX_CHANNEL] = { "channel_0_lock", "channel_1_lock", "channel_2_lock", "channel_3_lock", }; rt_err_t fh81_dma_register(struct fh81_dma * fh81_dma_p, char * dma_name){ rt_uint32_t i; RT_ASSERT(fh81_dma_p != RT_NULL); RT_ASSERT(dma_name != RT_NULL); //RT_ASSERT(fh81_dma_p->dwc.init != FH81_DMA_INIT_ALREADY); if(fh81_dma_p->dwc.init == FH81_DMA_INIT_ALREADY) return 0; struct rt_dma_device *rt_dma; rt_dma = &fh81_dma_p->parent; rt_dma->ops = &fh81_dma_ops; //soc para set fh81_dma_p->dwc.name = dma_name; fh81_dma_p->dwc.regs =(void *)DMA_REG_BASE; fh81_dma_p->dwc.paddr = DMA_REG_BASE; fh81_dma_p->dwc.irq = DMAC_IRQn; fh81_dma_p->dwc.channel_max_number = FH81_MAX_CHANNEL; fh81_dma_p->dwc.controller_status = CONTROLLER_STATUS_CLOSED; fh81_dma_p->dwc.init = FH81_DMA_INIT_ALREADY; fh81_dma_p->dwc.id = 0; //channel set for(i=0;idma_channel[i].channel_status = CHANNEL_STATUS_CLOSED; fh81_dma_p->dma_channel[i].desc_total_no = DESC_MAX_SIZE; //rt_completion_init(&(fh81_dma_p->dma_channel[i].transfer_completion)); rt_list_init(&(fh81_dma_p->dma_channel[i].queue)); fh81_dma_p->dma_channel[i].desc_trans_size = FH81_CHANNEL_MAX_TRANSFER_SIZE; rt_sem_init(&fh81_dma_p->dma_channel[i].channel_lock, channel_lock_name[i], 1, RT_IPC_FLAG_FIFO); } //isr rt_hw_interrupt_install(fh81_dma_p->dwc.irq, rt_fh81_dma_isr, (void *)fh81_dma_p, "dma_isr"); rt_hw_interrupt_umask(fh81_dma_p->dwc.irq); return rt_hw_dma_register(rt_dma,dma_name,RT_DEVICE_FLAG_RDWR,fh81_dma_p); } static void rt_fh_dma_cyclic_stop(struct dma_transfer *p){ struct fh81_dma *my_own = p->dma_controller; struct dw_dma *dwc; dwc = &my_own->dwc; channel_clear_bit(dwc, CH_EN, 1<<(p->channel_number)); } static void rt_fh_dma_cyclic_start(struct dma_transfer *p){ struct fh81_dma *my_own = p->dma_controller; struct dw_dma *dwc; dwc = &my_own->dwc; volatile uint32_t ret_status; struct dw_lli *p_lli = RT_NULL; p_lli = p->first_lli; //32bit ally RT_ASSERT(((uint32_t)p_lli & 0x03) == 0); dw_writel(dwc, CLEAR.XFER, 1<<(p->channel_number)); dw_writel(dwc, CLEAR.BLOCK, 1<<(p->channel_number)); dw_writel(dwc, CLEAR.ERROR, 1<<(p->channel_number)); //enable isr channel_set_bit(dwc, MASK.BLOCK, lift_shift_bit_num(p->channel_number)); //disable isr channel_clear_bit(dwc, MASK.XFER, lift_shift_bit_num(p->channel_number)); ret_status = dw_readl(dwc,CHAN[p->channel_number].CFG_LO); ret_status &= ~(DWC_CFGL_RELOAD_SAR|DWC_CFGL_RELOAD_DAR); dw_writel(dwc,CHAN[p->channel_number].CFG_LO,ret_status); //set the first link add ret_status = dw_readl(dwc,CHAN[p->channel_number].LLP); ret_status = (uint32_t)&p_lli[0]; dw_writel(dwc,CHAN[p->channel_number].LLP,ret_status); //set link enable //ret_status = dw_readl(dwc,CHAN[p->channel_number].CTL_LO); ret_status =DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; dw_writel(dwc,CHAN[p->channel_number].CTL_LO,ret_status); //clear ctl_hi dw_writel(dwc,CHAN[p->channel_number].CTL_HI,0); //enable channle channel_set_bit(dwc, CH_EN, 1<<(p->channel_number)); } static void rt_fh_dma_cyclic_prep(struct fh81_dma * fh81_dma_p,struct dma_transfer *p) { //bind the controller to the transfer p->dma_controller = fh81_dma_p; //bind active transfer fh81_dma_p->dma_channel[p->channel_number].active_trans = p; //p_transfer->dma_controller->dma_channel[p_transfer->channel_number].active_trans = dma_trans_desc; struct fh81_dma *my_own = p->dma_controller; struct dw_dma *dwc; dwc = &my_own->dwc; volatile uint32_t ret_status; struct dw_lli *p_lli = RT_NULL; uint32_t periods,i; uint32_t temp_src_add; uint32_t temp_dst_add; uint32_t buf_len = p->trans_len; uint32_t period_len = p->period_len; struct dma_transfer * dma_trans_desc = p; //check first... RT_ASSERT(buf_len % period_len == 0); //cal the periods... periods = buf_len / period_len; //get desc.... //dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(periods * sizeof(struct dw_lli)); dma_trans_desc->first_lli = get_desc(fh81_dma_p,dma_trans_desc,periods); if(dma_trans_desc->first_lli == RT_NULL){ FH_DMA_DEBUG("transfer error,reason: not enough mem..\n"); RT_ASSERT(dma_trans_desc->first_lli != RT_NULL); } rt_memset((void *)dma_trans_desc->first_lli, 0, periods * sizeof(struct dw_lli)); p_lli = dma_trans_desc->first_lli; RT_ASSERT(((uint32_t)p_lli & 0x03) == 0); RT_ASSERT(dma_trans_desc->dst_inc_mode <=DW_DMA_SLAVE_FIX); RT_ASSERT(dma_trans_desc->src_inc_mode <=DW_DMA_SLAVE_FIX); //step3: set the mem.. for(i=0;idst_inc_mode){ case DW_DMA_SLAVE_INC: temp_dst_add = dma_trans_desc->dst_add + i * period_len * (1<dst_width); break; case DW_DMA_SLAVE_DEC: temp_dst_add = dma_trans_desc->dst_add - i * period_len * (1<dst_width); break; case DW_DMA_SLAVE_FIX: temp_dst_add = dma_trans_desc->dst_add; break; } switch(dma_trans_desc->src_inc_mode){ case DW_DMA_SLAVE_INC: temp_src_add = dma_trans_desc->src_add + i * period_len * (1<src_width); break; case DW_DMA_SLAVE_DEC: temp_src_add = dma_trans_desc->src_add - i * period_len * (1<src_width); break; case DW_DMA_SLAVE_FIX: temp_src_add = dma_trans_desc->src_add ; break; } p_lli[i].sar = temp_src_add; p_lli[i].dar = temp_dst_add; //para ctl RT_ASSERT(dma_trans_desc->dst_width <=DW_DMA_SLAVE_WIDTH_32BIT); RT_ASSERT(dma_trans_desc->src_width <=DW_DMA_SLAVE_WIDTH_32BIT); RT_ASSERT(dma_trans_desc->dst_msize <=DW_DMA_SLAVE_MSIZE_256); RT_ASSERT(dma_trans_desc->src_msize <=DW_DMA_SLAVE_MSIZE_256); RT_ASSERT(dma_trans_desc->fc_mode <=DMA_P2P); p_lli[i].ctllo = DWC_CTLL_INT_EN|DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width)|DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) |DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode)|DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) |DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize)|DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize)|DWC_CTLL_FC(dma_trans_desc->fc_mode) |DWC_CTLL_DMS(0)|DWC_CTLL_SMS(0); //block size p_lli[i].ctlhi = period_len; p_lli[i].llp = (uint32_t)&p_lli[i+1]; p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; //flush cache to mem mmu_clean_invalidated_dcache((uint32_t)&p_lli[i],sizeof(struct dw_lli)); dump_lli(&p_lli[i]); } //make a ring here p_lli[periods -1 ].llp = (uint32_t)&p_lli[0]; mmu_clean_invalidated_dcache((uint32_t)&p_lli[periods -1 ],sizeof(struct dw_lli)); //parse the handshake RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING); RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING); //dst handshake dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,0); ret_status = 0; if(dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING){ ret_status |= DWC_CFGL_HS_DST; } else{ ret_status &= ~DWC_CFGL_HS_DST; } dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); //src handshake ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); if(dma_trans_desc->src_hs == DMA_SW_HANDSHAKING){ ret_status |= DWC_CFGL_HS_SRC; } else{ ret_status &= ~DWC_CFGL_HS_SRC; } dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); //only hw handshaking need this.. switch(dma_trans_desc->fc_mode){ case DMA_M2M: break; case DMA_M2P: //set dst per... RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); //clear 43 ~ 46 bit ret_status &= ~0x7800; ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per); dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); //DWC_CFGH_SRC_PER break; case DMA_P2M: //set src per... RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); //clear 39 ~ 42 bit ret_status &= ~0x780; ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per); dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); break; case DMA_P2P: //set src and dst.. RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); ret_status &= ~0x7800; ret_status &= ~0x780; ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) | DWC_CFGH_DST_PER(dma_trans_desc->dst_per); dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); break; default: break; } dma_trans_desc->dma_controller->dma_channel[dma_trans_desc->channel_number].channel_status = CHANNEL_STATUS_BUSY; if(dma_trans_desc->prepare_callback){ dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para); } } static void rt_fh_dma_cyclic_free(struct dma_transfer *p){ struct fh81_dma *my_own = p->dma_controller; struct dw_dma *dwc; dwc = &my_own->dwc; volatile uint32_t ret_status; struct dw_lli *p_lli = RT_NULL; p_lli = p->first_lli; //close channel first.. channel_clear_bit(dwc, CH_EN, 1<<(p->channel_number)); //check if close really while (dw_readl(dwc, CH_EN) & 1<<(p->channel_number)); dw_writel(dwc, CLEAR.XFER, 1<<(p->channel_number)); dw_writel(dwc, CLEAR.BLOCK, 1<<(p->channel_number)); dw_writel(dwc, CLEAR.ERROR, 1<<(p->channel_number)); //rt_free(p->first_lli); put_desc(my_own,p); } void rt_fh_dma_init(void){ fh81_dma_register(&fh81_dma_controller[0],"fh81_dma"); }