📄 dma.c
字号:
/* * ApOS (Another Project software for s3c2410) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Copyright caiyuqing * * this file define the functions of the DMA block * 本文件实现了对DMA模块进行操作的函数 */#include "../include/s3c2410/s3c2410.h"#include "../include/s3c2410/dma.h"#include "../include/kernel/irq.h"extern irq_ptr irq_rotunie[32];/* * DMA global object,any functions of the dma control is base it. * DMA全局对象,整个系统的DMA操作(初始化,设置等等)都是基于这个对象的 */struct dma_ctrl_object dma_ctrl_object;/* * system can get the status of a dma opteration througth * thease four global variable * 四个全局变量,通过他们系统可以知道dma操作是否已经完成 */char sys_dma0_done;char sys_dma1_done;char sys_dma2_done;char sys_dma3_done;/* * irq for chanel0 * 0号通道dma中断服务例程 */void dma0_irq(struct cpu_registers *regs);/* * initialize the DMA object * 初始化 dma_ctrl_object 对象 */void dma_ctrl_obj_init(struct dma_ctrl_object *dco){ //0通道对象初始化 dco->chanel0.disrc =&rDISRC0; dco->chanel0.disrcc =&rDISRCC0; dco->chanel0.didst =&rDIDST0; dco->chanel0.didstc =&rDIDSTC0; dco->chanel0.dcon =&rDCON0; dco->chanel0.dstat =&rDSTAT0; dco->chanel0.dcsrc =&rDCSRC0; dco->chanel0.dcdst =&rDCDST0; dco->chanel0.dmasktrig =&rDMASKTRIG0; dco->chanel0.tc_opt =1; //1通道对象初始化 dco->chanel1.disrc =&rDISRC1; dco->chanel1.disrcc =&rDISRCC1; dco->chanel1.didst =&rDIDST1; dco->chanel1.didstc =&rDIDSTC1; dco->chanel1.dcon =&rDCON1; dco->chanel1.dstat =&rDSTAT1; dco->chanel1.dcsrc =&rDCSRC1; dco->chanel1.dcdst =&rDCDST1; dco->chanel1.dmasktrig =&rDMASKTRIG1; dco->chanel1.tc_opt =1; //2通道对象初始化 dco->chanel2.disrc =&rDISRC2; dco->chanel2.disrcc =&rDISRCC2; dco->chanel2.didst =&rDIDST2; dco->chanel2.didstc =&rDIDSTC2; dco->chanel2.dcon =&rDCON2; dco->chanel2.dstat =&rDSTAT2; dco->chanel2.dcsrc =&rDCSRC2; dco->chanel2.dcdst =&rDCDST2; dco->chanel2.dmasktrig =&rDMASKTRIG2; dco->chanel2.tc_opt =1; //3通道对象初始化 dco->chanel3.disrc =&rDISRC3; dco->chanel3.disrcc =&rDISRCC3; dco->chanel3.didst =&rDIDST3; dco->chanel3.didstc =&rDIDSTC3; dco->chanel3.dcon =&rDCON3; dco->chanel3.dstat =&rDSTAT3; dco->chanel3.dcsrc =&rDCSRC3; dco->chanel3.dcdst =&rDCDST3; dco->chanel3.dmasktrig =&rDMASKTRIG3; dco->chanel3.tc_opt =1; irq_rotunie[INT_DMA0] =&dma0_irq;}/* * Select one between Demand mode and Handshake mode. * */static void set_ack_mode(struct dma_chanel *dma_chanel, char hs_mode){ *dma_chanel->dcon&=~(1<<DMD_HS_OFFSET); *dma_chanel->dcon|=(hs_mode<<DMD_HS_OFFSET);}/* * Select DREQ/DACK synchronization. * */static void set_sync_mode(struct dma_chanel *dma_chanel, char sync_mode){ *dma_chanel->dcon&=~(1<<SYNC_OFFSET); *dma_chanel->dcon|=(sync_mode<<SYNC_OFFSET);}/* * Enable/Disable the interrupt setting for CURR_TC * */static void enable_int(struct dma_chanel *dma_chanel, char int_enable){ *dma_chanel->dcon&=~(1<<INT_OFFSET); *dma_chanel->dcon|=(int_enable<<INT_OFFSET);}/* * Select the transfer size of an atomic transfer * */static void set_transfer_size(struct dma_chanel *dma_chanel, char transfer_size){ (*dma_chanel->dcon)&=~(1<<TSZ_OFFSET); (*dma_chanel->dcon)|=(transfer_size<<TSZ_OFFSET);}/* * Select the service mode between Single service mode and Whole * service mode. * */static void set_service_size(struct dma_chanel *dma_chanel, char serve_mode){ *dma_chanel->dcon&=~(1<<SERVMODE_OFFSET); *dma_chanel->dcon|=(serve_mode<<SERVMODE_OFFSET);}/* * Select DMA request source for each DMA. * */static void set_hardware_src(struct dma_chanel *dma_chanel, char hw_src_sel){ *dma_chanel->dcon&=~(0x7<<HWSRCSEL_OFFSET); *dma_chanel->dcon|=(hw_src_sel<<HWSRCSEL_OFFSET);}/* * Select the DMA source between software (S/W request mode) and * hardware (H/W request mode). * */static void set_triger_src(struct dma_chanel *dma_chanel, char sw_or_hw){ *dma_chanel->dcon&=~(1<<SWHW_SEL_OFFSET); *dma_chanel->dcon|=(sw_or_hw<<SWHW_SEL_OFFSET);}/* * Set the reload on/off option * */static void reload_enable(struct dma_chanel *dma_chanel, char reload){ *dma_chanel->dcon&=~(1<<RELOAD_OFFSET); *dma_chanel->dcon|=(reload<<RELOAD_OFFSET);}/* * Data size to be transferred * */static void set_data_size(struct dma_chanel *dma_chanel, char data_size){ *dma_chanel->dcon&=~(0x3<<DSZ_OFFSET); *dma_chanel->dcon|=(data_size<<DSZ_OFFSET);}/* * setup the DMA chanel. * 对dma通道的属性进行设置,比如设置触发模式,触发源 */int setup_dma_chanel(struct dma_option* opt){ char chanel=opt->ch; struct dma_ctrl_object *dma_obj =&dma_ctrl_object; struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+chanel; /* * 0号通道用于内存之间的数据传递 * 2号通道用于sdi的数据传递 */ if(chanel==0||chanel==2) return -1; *dma_chanel->disrcc=((opt->src_loc)<<LOC_OFFSET)|((opt->src_inc)<<INC_OFFSET); *dma_chanel->didstc=((opt->dest_loc)<<LOC_OFFSET)|((opt->dest_inc)<<INC_OFFSET); set_ack_mode(dma_chanel,opt->hs_mode); set_sync_mode(dma_chanel,opt->sync_mode); enable_int(dma_chanel,opt->int_enable); set_transfer_size(dma_chanel,opt->transfer_size); set_service_size(dma_chanel,opt->serve_mode); set_hardware_src(dma_chanel,opt->hw_src_sel); set_triger_src(dma_chanel,opt->sw_or_hw); reload_enable(dma_chanel,opt->reload); set_data_size(dma_chanel,opt->data_size); irq_rotunie[INT_DMA0+chanel]=opt->interrupt_routine; switch(opt->transfer_size) { case 0: dma_chanel->tc_opt*=1; break; case 1: dma_chanel->tc_opt*=4; break; } switch(opt->data_size) { case 0: dma_chanel->tc_opt*=1; break; case 1: dma_chanel->tc_opt*=2; break; case 2: dma_chanel->tc_opt*=4; break; } return chanel;}/* * get options of the DMA chanel ask . * 获得指定的DMA通道的属性 */void get_dma_option(int chanel,struct dma_option* opt){ struct dma_ctrl_object *dma_obj =&dma_ctrl_object; struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+chanel; opt->src_loc =((*dma_chanel->disrcc)>>LOC_OFFSET)&0x01; opt->src_inc =(*dma_chanel->disrcc)&0x01; opt->dest_loc =((*dma_chanel->didstc)>>LOC_OFFSET)&0x01; opt->dest_inc =(*dma_chanel->didstc)&0x01; opt->hs_mode =((*dma_chanel->dcon)>>DMD_HS_OFFSET)&0x01; opt->sync_mode =((*dma_chanel->dcon)>>SYNC_OFFSET)&0x01; opt->int_enable =((*dma_chanel->dcon)>>INT_OFFSET)&0x01; opt->transfer_size =((*dma_chanel->dcon)>>TSZ_OFFSET)&0x01; opt->serve_mode =((*dma_chanel->dcon)>>SERVMODE_OFFSET)&0x01; opt->hw_src_sel =((*dma_chanel->dcon)>>HWSRCSEL_OFFSET)&0x07; opt->sw_or_hw =((*dma_chanel->dcon)>>SWHW_SEL_OFFSET)&0x01; opt->reload =((*dma_chanel->dcon)>>RELOAD_OFFSET)&0x01; opt->data_size =((*dma_chanel->dcon)>>DSZ_OFFSET)&0x03;}/* * very important,if an dma operation is trigger(except chanel 0), * this function should be called.It deal the DMA operation. * 很重要的一个函数,这个函数是每个DMA通道(除0号通道)被触发后进行DMA操作的函数 * * chanel: chanel ID 通道号 * src_addr: source address 源地址 * dest_addr: destination address 目标地址 * len: data length(byts) 数据长度(字节) */int do_dma(int chanel,unsigned int* src_addr,unsigned int* dest_addr,int len){ /* * get the DMA global object * 获得系统DMA对象 */ struct dma_ctrl_object *dma_obj =&dma_ctrl_object; /* * get the DMA chanel object * 获得DMA通道对象 */ struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+chanel; /* * get the transfer count * 获取DMA操作的传送次数 */ unsigned int tc =(len+dma_chanel->tc_opt-1)/(dma_chanel->tc_opt); switch(chanel) { case DMA_CHANEL_0: return -1; case DMA_CHANEL_1: /* * unmask DMA1 int,set the sys_dma1_done to 0 indicate * that DMA operation will be deal. * 开启DMA1中断,将sys_dmaX_done设置为0表示一个DMA操作将开始 */ irq_mask(INT_DMA1,IRQ_UNMASK); sys_dma1_done=0; break; case DMA_CHANEL_2://same as above irq_mask(INT_DMA2,IRQ_UNMASK); sys_dma2_done=0; break; case DMA_CHANEL_3://same as above irq_mask(INT_DMA3,IRQ_UNMASK); sys_dma3_done=0; break; } /* * set the DMA operation's source address,destination address and * the transfer count(tc) * 设置DMA操作的源地址src_addr,目标地址dest_addr,传送次数tc */ *dma_chanel->disrc=src_addr; *dma_chanel->didst=dest_addr; *dma_chanel->dcon|=tc; //ready? while((*dma_chanel->dstat)&0xfffff!=0); //DMA on, SW_TRIG if((*dma_chanel->dcon&(1<<SWHW_SEL_OFFSET))==0) { //该通道为软触发 *dma_chanel->dmasktrig=(1<<ON_OFF_OFFSET)|(1<<SW_TRIG_OFFSET); printk("dma soft trig turn on\n"); } else { //该通道为硬触发 *dma_chanel->dmasktrig=(0<<STOP_OFFSET)|(1<<ON_OFF_OFFSET)|0; printk("dma hard trig turn on\n"); } return chanel;}/* * we use chanel0 to perform the data transfer between memory and memory * 本函数使用DMA的0号通道对内存间的数据传送 * src_addr: source address 源地址 * dest_addr: destination address 目标地址 * len: data length(byts) 数据长度(字节) */int dma_mem_transfer(unsigned int* src_addr,unsigned int* dest_addr,int len){ struct dma_ctrl_object *dma_obj =&dma_ctrl_object; struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+0; unsigned int tc=(len); irq_mask(INT_DMA0,IRQ_UNMASK); sys_dma0_done=0; /* * LOC: 0 address increment * INC: 0 AHB */ *dma_chanel->disrc=src_addr; *dma_chanel->disrcc=(0<<LOC_OFFSET)|(0<<INC_OFFSET); *dma_chanel->didst=dest_addr; *dma_chanel->didstc=(0<<LOC_OFFSET)|(0<<INC_OFFSET); /* * DMD_HS: 1 Handshake mode. * * SYNC: 1 DREQ and DACK are synchronized to HCLK. * 由于是内存间的数据传递,数据源设备是内存,应该同步于HCLK * 所以将SYNC位设置为1 * 若数据源设备是属于外部总线的,则必须同步于PCLK,必须将该 * 位设置为0 * * INT: 1 interrupt request is generated when all the transfer is done. * 当该位为1时,则DMA完成(既CURR_TC变为0)之后相应的DMA中断将被触发 * * TSZ: 0 an union transfer is performed. * DMA设备每次占用总线时传递数据的次数,0是传递1次后释放总线, * 1是传递4次后再释放总线 * * SERVMODE:1 Whole service mode. * * SWHW_SEL:0 S/W request mode. * DMA触发方式的选择,0软件触发,1为硬件触发。 * 当设置为硬件触发之后我们必须设置HWSRCSEL位 * 为4个通道配置触发源 * * RELOAD: 1 DMA channel (DMA REQ) is turned off when a current value of * transfer count becomes 0. * 当设置为1时,则一旦某通道完成了DMA操作(既CURR_TC变为0),该通道 * 将被马上关闭。(既DMASKTRIG寄存器的ON_OFF位被置0) * 若设置为0时,则完成DMA操作之后的通道并不关闭(既DMASKTRIG寄存器 * 的ON_OFF位还是1),它会紧接着处理新的请求(如果有的话) * * DSZ: 00 BYTE. * DMA每次传送数据的字节数 * * TC: tc 当前DMA请求总共需要传递多少次数据(必须占用多少次总线) * 一个DMA操作传递的数据字节数实际上是 DSZ*TSZ*TC * */ *dma_chanel->dcon=(1<<DMD_HS_OFFSET)|(1<<SYNC_OFFSET)| (1<<INT_OFFSET)|(0<<TSZ_OFFSET)|(1<<SERVMODE_OFFSET)| (0<<SWHW_SEL_OFFSET)|(1<<RELOAD_OFFSET)|(BYTE<<DSZ_OFFSET)| (tc); //ready? while((*dma_chanel->dstat)&0xfffff!=0); /* * DMA on, SW_TRIG * 开启DMA通道,触发一个DMA信号 */ *dma_chanel->dmasktrig=(1<<ON_OFF_OFFSET)|(1<<SW_TRIG_OFFSET); return tc;}dma_stop(int chanel){ struct dma_ctrl_object *dma_obj =&dma_ctrl_object; struct dma_chanel *dma_chanel =((struct dma_chanel *)dma_obj)+chanel; *dma_chanel->dmasktrig=(1<<STOP_OFFSET);}/* * irq for dam chanel0 * dma chanel0 中断服务例程 */void dma0_irq(struct cpu_registers *regs){ /* * clean the interrupt pin and mask the interrupt,set * the sys_dmaX_done to 1,indecate that the DMA opteration * is complete. * * 清除中断信号并屏蔽该中断,将sys_dmaX_done * 设置为0表示一个DMA操作已经完成 */ clean_src_pnd(INT_DMA0); clean_int_pnd(INT_DMA0); irq_mask(INT_DMA0,IRQ_MASK); sys_dma0_done=1;}int dma_tranfer_done(){ return sys_dma0_done;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -