1.DMA(Direct Memory Access)

Direct memory access , DMA There is no need for transmission CPU Direct control of transmission , Through hardware for RAM 、I/O The device opens up a path for direct data transmission , Can make CPU Greatly improved the efficiency of .

Learning so much about driving , It's not hard to launch DMA The writing routine of :

1) register DMA interrupt , Allocate buffer
2) Register character device , And provides a collection of file operations fops
-> 2.1)file_operations Set in the DMA Hardware related operations , To start up DMA
Because we use the character device test method to test , But this example just uses the copy between two addresses to demonstrate DMA The role of , So it's written as a character device

2. Before writing the driver , First, how to allocate the release buffer 、DMA Related register introduction 、 Use DMA interrupt

2.1 stay linux in , Distribution releases DMA buffer , The following functions are commonly used

1)

/* This function only forbids cache buffer , Keep the write buffer , That is to write data to the registered physical area , It will also be updated to the corresponding virtual cache */void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); // Distribute DMA Buffer zone // The return value is : Applied for DMA The virtual address of the buffer , if NULL, Distribution failure , Need to release , Avoid memory leaks // The parameters are as follows : //*dev: The pointer , Fill in here 0, There is no content in the buffer of this application //size: The size of the assigned address ( Byte units ) //*handle: The physical starting address applied to //gfp: Allocated memory parameters , The logo is defined in <linux/gfp.h>, The common signs are as follows : //GFP_ATOMIC     Used to allocate memory from code other than interrupt handling and process context .  Never sleep . //GFP_KERNEL     Normal allocation of kernel memory .  Maybe sleep . //GFP_USER       Used to allocate memory for user space pages ;  It may sleep .

2)

/* This function forbids cache Cache and write forbidden buffer , So that CPU Read and write address and DMA Read and write the same address content */void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);         // Distribute DMA Buffer zone , The return values and parameters are the same as the functions above 

3)

dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);   // Release DMA cache , And dma_alloc_writecombine() Corresponding //size: Release length //cpu_addr: Virtual address ,//handle: Physical address 

4)

dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)    // Release DMA cache , And dma_alloc_coherent () Corresponding //size: Release length //cpu_addr: Virtual address ,//handle: Physical address 

(PS: dma_free_writecombine() In fact, that is dma_free_conherent(), It's just using #define It's just a rename .)

And the memory allocation we used before kmalloc() function , It can't be used in DMA On , Because the allocated memory is not continuous in physical address ( Continuous on virtual address ).

2.2 that 2440 How to start the development board DMA, First look 2440 Of DMA register

(PS: Actually these DMA Related registers , stay linux Samsung in the kernel has been packaged , Can be called directly , But it was very troublesome , It's better to set the register directly , You can refer to : http://blog.csdn.net/mirkerson/article/details/6632273)

2.2.1 2440 Support 4 Passageway DMA controller

among 4 Passageway DMA Peripheral request source , As shown in the figure below ( adopt DCONn The register of [26:24] To set up )

 Insert picture description here
(PS: If the request source is on the system bus , Just set up DCONn The register of [23]=0 that will do )

2.2.2 And each channel can handle the following 4 In this case :

1) The source and target are on the system bus ( such as : Two physical memory addresses )
2) When the target is on the peripheral bus , The source is on the system bus ( Peripherals refer to : A serial port , Timer ,I2C,I2S etc. )
3) When the target is on the system bus , The source is on the peripheral bus
4) The source and destination are on the peripheral bus

2.2.3 DMA There are two working modes ( adopt DCONn The register of [28] To set up )

Query mode :

When DMA request XnXDREQ Low power level , be DMA The data will be transmitted all the time , until DMA Ask for higher , Just stop

Handshake mode :

When DMA request XnXDREQ When there is a falling edge trigger , be DMA It will transmit data once
 Insert picture description here

2.2.4 DMA There are two transmission modes ( adopt DCONn The register of [31] To set up )

Unit transmission :

During transmission , Every time , Read 1 Time , Write 1 Time .( As shown in the figure above )

Sudden 4 transmission :

During transmission , Every time , Read 4 Time , Then write 4 Time ( As shown in the figure below )
 Insert picture description here

2.2.5 2440 Medium DMA The register is shown in the figure below :

 Insert picture description here
share 4 Register of two channels , And the register contents of each channel are consistent , So we take DMA passageway 0 For example :

1)DISRC0 Initial source register

[30:0] : Deposit DMA The base address of the source

2)DISRCC0 Initial source control register

[1] : Source location selection ,0: The source is on the system bus , 1: The source is on the peripheral bus

[0] : Source address selection ,0: The source address increases automatically during transmission , 1: The source address is fixed

3)DIDST0 Initial target register

[30:0] : Set up DMA The base address of the destination

4)DIDSTC0 Initial target control register

[2] : Interrupt time selection , 0: When DMA Transmission count =0, An immediate interruption 1: Send interrupt after auto loading ( That is to say, the count is 0, Then reload the count )

[1] : Destination location selection , 0: The purpose is on the system bus , 1: The purpose is on the peripheral bus

[0] : Destination address selection , 0: The destination address is automatically increased during transmission , 1: The destination address is fixed

5)DCON0 Control register

[31] : Working mode selection , 0: Query mode 1: Handshake mode ( When the source is on a peripheral , Try to choose handshake mode )

[30] : Interrupt request (DREQ)/ Interrupt response (DACK) Selection of synchronous clock based on FPGA , 0:PCLK Sync 1:HCLK Sync

(PS: If there's a device in HCLK On , This bit should be set to 1, such as :(SDRAM) Memory array , On the contrary, when these devices are PCLK On , It should be set to 0, such as :ADC,IIS,I2C,UART)

[29] : DMA Transmit count interrupt enable / prohibit 0: No interruptions 1: When the transmission is complete , The interrupt

[28] : Transmission mode selection , 0: Unit transmission 1: Sudden 4 transmission

[27] : Transport service mode

0: Single service mode , such as : Yes 2 individual DMA request , They will be executed once in sequence ( Unit transmission / Sudden 4 transmission ) Stop after , And then until the next time DMA request , Start another cycle again .

1: Full service mode , It refers to the DMA If there is a request , Will occupy DMA Bus , All the time , If there is anything else in the meantime DMA request , Only wait transfer count TC by 0, To do something else DMA request

[26:24] : DMA Peripheral request source selection

[23] : Software / Hardware request source selection 0: Software requests 1: Hardware request ( You also need to set [26:24] To select a peripheral source )

[22] : Reload switch options by 0 that will do

[21:20] : Transfer data size by 00(8 position ) that will do

[19:0] : Set up DMA Count of transfers TC

6)DSTAT0 Status register

[21:20] : DMA state 00: Free 01: busy

[19:0] : Transfer count current value CURR_TC by 0 Indicates the end of transmission

7)DCSRC0 Current source register

[30:0] : Deposit DMA Current source base address

8)DCDST0 Current target register

[30:0] : Deposit DMA The current destination base address

9)DMASKTRIG0 Trigger mask register

[2] : stop it STOP This person writes 1, Stop at once DMA The current transmission

[1] : DMA Channel enable 0: close DMA The passage of 0( prohibit DMA request ) 1: Turn on DMA The passage of 0( Turn on DMA request )

[0] : Software request triggers 1: Start a software request DMA, Only DCONn[23]=0 and DMASKTRIGn[1]=1 It works ,DMA When transmitting , This bit is cleared automatically 0

2.3 Let's start with linux register DMA interrupt

First ,DMA There can only be one source per channel of - > Purpose , So enter the command cat /proc/interrupts , find DMA3 Interrupt not used

So in linux Use in :

request_irq(IRQ_DMA3, s3c_dma_irq, NULL, "s3c_dma", 1);// s3c_dma_irq: Interrupt service function , Register here DMA3 Interrupt service function //NULL: Interrupt generation type ,  Unwanted , So fill in NULL//1: When it means interruption , Pass in the parameters of the interrupt function , There is no need to fill in this section 1, Remember not to fill in 0, Otherwise, the registration fails 

3. Next , Let's write a DMA Character device driver for

Steps are as follows :

1) register DMA interrupt , Allocate two DMA buffer ( Source 、 Purpose )
2) Register character device , And provides a collection of file operations fops
-> 2.1) adopt ioctl Of cmd To judge whether to use DMA Start a copy between two addresses , Or a direct copy between two addresses
-> 2.2) if DMA start-up , Is set DMA The related hardware , And start the DMA transmission

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/irq.h>   #include <asm/irq.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/dma-mapping.h>#define  S3C_DMA_SIZE   512*1024          //DMA Transmission length    512KB#define NORMAL_COPY     0                 // A normal copy between two addresses #define DMA_COPY        1                 // Between two addresses DMA Copy /* Function declaration */static DECLARE_WAIT_QUEUE_HEAD(s3c_dma_queue);          // Declare the waiting queue static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags);
  /*
   *  Define the interrupt event flag
   * 0: Enter the waiting queue         1: Exit the waiting queue
   */ static int s3c_dma_even=0;static unsigned char   *source_virt;            // Source virtual address static unsigned int     source_phys;            // Source physical address static unsigned char *dest_virt;              // Destination virtual address static unsigned int   dest_phys;              // Destination virtual address /*DMA3 register */struct  S3c_dma3_regs{unsigned int disrc3    ;          //0x4b0000c0unsigned int disrcc3   ;                    unsigned int didst3    ;                    unsigned int didstc3   ;               unsigned int dcon3     ;                unsigned int dstat3    ; unsigned int dcsrc3    ; unsigned int dcdst3    ;        unsigned int dmasktrig3;        //0x4b0000e0};
 static volatile struct S3c_dma3_regs   *s3c_dma3_regs;/* Character device operation */static struct file_operations  s3c_dma_fops={.owner  = THIS_MODULE,.ioctl     = s3c_dma_ioctl,};/* Interrupt service function */static irqreturn_t  s3c_dma_irq (int irq, void *dev_id)   {s3c_dma_even=1;                             // Exit the waiting queue wake_up_interruptible(&s3c_dma_queue);      // Wake up the   interrupt return IRQ_HANDLED;}/*ioctl function */static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long flags){int i;memset(source_virt, 0xAA, S3C_DMA_SIZE);          memset(dest_virt, 0x55, S3C_DMA_SIZE);   
    switch(cmd){case NORMAL_COPY:                           // Normal copy
             for(i=0;i<S3C_DMA_SIZE;i++) dest_virt[i] =  source_virt[i]; if(memcmp(dest_virt, source_virt, S3C_DMA_SIZE)==0)   { printk("NORMAL_COPY OK\n");return 0; } else{ printk("NORMAL_COPY ERROR\n");   return -EAGAIN;}             
            case DMA_COPY:                               //DMA Copy s3c_dma_even=0;     // Enter the waiting queue /* Set up DMA register , Start once DMA transmission  *//*  The physical address of the source  */s3c_dma3_regs->disrc3      = source_phys;      /*  The source is located in AHB Bus ,  Source address increment  */  
        s3c_dma3_regs->disrcc3     = (0<<1) | (0<<0);/*  The physical address of the destination  */s3c_dma3_regs->didst3      = dest_phys;      /*  The purpose is to AHB Bus ,  Destination address increment  */s3c_dma3_regs->didstc3     = (0<<2) | (0<<1) | (0<<0);     /*  To interrupt , Single transmission , Software triggers , */s3c_dma3_regs->dcon3=(1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(S3C_DMA_SIZE<<0);  // Start once DMA transmission s3c_dma3_regs->dmasktrig3  = (1<<1) | (1<<0);     
        wait_event_interruptible(s3c_dma_queue, s3c_dma_even);    // Go to sleep , wait for DMA When the transmission is interrupted, it exits if(memcmp(dest_virt, source_virt, S3C_DMA_SIZE)==0){ printk("DMA_COPY OK\n");return 0; }else{   printk("DMA_COPY ERROR\n"); return -EAGAIN;   }  break;}return 0;}static unsigned int major;static struct class *cls;static int s3c_dma_init(void){/*1.1  register DMA3  interrupt   */if(request_irq(IRQ_DMA3, s3c_dma_irq,NULL, "s3c_dma",1)) {printk("Can't    request_irq   \"IRQ_DMA3\"!!!\n ");return -EBUSY;}/*1.2  Allocate two DMA buffer ( Source 、 Purpose )*/source_virt=dma_alloc_writecombine(NULL,S3C_DMA_SIZE, &source_phys, GFP_KERNEL);if(source_virt==NULL)       
   {printk("Can't  dma_alloc   \n ");return -ENOMEM;
   }
    dest_virt=dma_alloc_writecombine(NULL,S3C_DMA_SIZE, &dest_phys, GFP_KERNEL);if(dest_virt==NULL)       
   {printk("Can't  dma_alloc   \n ");return -ENOMEM;
   }
    /*2. Register character device , And provides a collection of file operations fops*/major=register_chrdev(0, "s3c_dma",&s3c_dma_fops);cls= class_create(THIS_MODULE, "s3c_dma");class_device_create(cls, NULL,MKDEV(major,0), NULL, "s3c_dma");s3c_dma3_regs=ioremap(0x4b0000c0, sizeof(struct S3c_dma3_regs));return 0;  }static void s3c_dma_exit(void){iounmap(s3c_dma3_regs);class_device_destroy(cls, MKDEV(major,0));class_destroy(cls);dma_free_writecombine(NULL, S3C_DMA_SIZE, dest_virt, dest_phys);dma_free_writecombine(NULL, S3C_DMA_SIZE, source_virt, source_phys);   free_irq(IRQ_DMA3, 1);}module_init(s3c_dma_init);module_exit(s3c_dma_exit);MODULE_LICENSE("GPL");

3.2 The application test procedure is as follows :

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>#include <string.h>/* ./dma_test NORMAL
 * ./dma_test DMA
 */#define NORMAL_COPY     0               // A normal copy between two addresses #define DMA_COPY        1              // Between two addresses DMA Copy void print_usage(char *name){printf("Usage:\n");printf("%s <NORMAL | DMA>\n", name);}int main(int argc, char **argv){int fd,i=30;
     if (argc != 2){print_usage(argv[0]);return -1;}fd = open("/dev/s3c_dma", O_RDWR);if (fd < 0){printf("can't open /dev/s3c_dma\n");return -1;}if (strcmp(argv[1], "NORMAL") == 0){while (i--) // Call driven ioctl(),30 Time {ioctl(fd, NORMAL_COPY);}}else if (strcmp(argv[1], "DMA") == 0){while (i--) // Call driven ioctl(),30 Time {ioctl(fd, DMA_COPY);}}else{print_usage(argv[0]);return -1;}return 0;     }

4. test run

Input ./dma_test NORMAL & , Use CPU Normal copy , You can see that it takes up most of the resources , Input ls No response :
 Insert picture description here
Input ./dma_test DMA & , Use DMA Copy , Input ls There was an immediate response , And thus released CPU The pressure of the :
 Insert picture description here