"Mr. Wang, I want to tell you the good news that the most difficult part of the course has been completed without knowing it. Today's course is much simpler, and the most important thing is that the core theory courses for Linux device drivers are almost the same ..."
"What's the hardest part? Have you finished speaking? I don't think so. You're talking too well. It's so easy to understand, too... "said Xiao Wang.
"Cut, it's sweet for you. I don't know you yet, little brain.
What about today? Today we will talk about static Io memory ing. After Linux is transplanted to the target circuit board, a static ing from the physical IP address of the peripheral I/O memory to the virtual IP address is usually established.
Add new members to the map_desc struct array. The map_desc struct is defined as follows:
StructMap_desc {Unsigned LongVirtual;// Virtual addressUnsigned LongPFN;// _ Phys_to_pfn (phy_addr)Unsigned LongLength;// SizeUnsigned IntType;// Type}
Port the Linux operating system to a specific platform. The definition between machine_start and machine_edn is designed for a specific circuit board. The map_io () member function completes the static ing of Io memory, finally, the static ing between physical memory and virtual memory in the map_desc array is established by calling CPU> map_io.
In the kernel of an OS that has been transplanted, the driver engineer can fully control the IO memory in the unconventional memory area (Peripheral Controller registers, peripheral control registers integrated within the MCU, etc) add to the map_desc array according to the resource usage of the circuit board. The following figure shows the memory usage of smdk2440 in map_desc:
Map_desc defined by smdk2440 memory resources # Include <Linux/kernel. h> # include <Linux/types. h> # include <Linux/interrupt. h> # include <Linux/list. h> # include <Linux/Timer. h> # include <Linux/init. h> # include <Linux/platform_device.h> # include <ASM/Mach/arch. h> # include <ASM/Mach/map. h> # include <ASM/Mach/IRQ. h> # include <ASM/hardware. h> # include <ASM/hardware/iomd. h> # include <ASM/Io. h> # include <ASM/IRQ. h> # include <ASM/mach-types.h> // # Include <ASM/debug-ll.h> # Include <ASM/ARCH/regs-serial.h> # include <ASM/ARCH/regs-gpio.h> # include <ASM/ARCH/regs-lcd.h> # include <ASM/ARCH/idle. h> # include <ASM/ARCH/FB. h> # include" S3C2410. h "# Include"S3C2440. h "# Include" Clock. h "# Include" Devs. h "# Include" CPU. h "# Include" PM. h " Static Struct Map_desc smdk2440_iodesc [] _ initdata = { /* ISA Io space map (memory space selected by A24 )*/ {. Virtual = (u32) s3c24xx_va_isa_word ,. PFN = _ phys_to_pfn (s3c2410_cs2 ),. length = 0x10000 ,. type = mt_device ,},{. virtual = (u32) maid word + 0x10000 ,. PFN = _ phys_to_pfn (s3c2410_cs2 + (1 <24 )),. length = sz_4m ,. type = mt_device ,},{. virtual = (u32) s3c24xx_va_isa_byte ,. PFN = _ phys_to_pfn (s3c2410_cs2 ),. length = 0x10000 ,. type = mt_device ,},{. virtual = (u32) maid + 0x10000 ,. PFN = _ phys_to_pfn (s3c2410_cs2 + (1 <24 )),. length = sz_4m ,. type = mt_device, }};# define ucon s3c2410_ucon_default | usage # define ulcon s3c2410_lcon_cs8 | s3c2410_lcon_pnone | s3c2410_lcon_stopb # define ufcon restart | restart Static Struct S3c2410_uartcfg smdk2440_uartcfgs [] = {[0] = {. hwport = 0 ,. flags = 0 ,. ucon = 0x3c5 ,. ulcon = 0x03 ,. ufcon = 0x51,}, [1] = {. hwport = 1 ,. flags = 0 ,. ucon = 0x3c5 ,. ulcon = 0x03 ,. ufcon = 0x51 ,}, /* IR port */ [2] = {. hwport = 2,. Flags = 0,. ucon = 0x3c5,. ulcon = 0x43,. ufcon = 0x51 ,}}; /* LCD Driver info */ Static Struct S3c2410fb_mach_info smdk2440_ LCD _cfg _ initdata = {. regs = {. lcdcon1 = s3c2410_lcdcon0000tft16bpp | s3c2410_lcdcon0000tft | s3c2410_lcdcon0000clkval (0x04 ),. lcdcon2 = s3c2410_lcdcon2_vbpd (7) | s3c2410_lcdcon2_lineval (319) | s3c2410_lcdcon2_vfpd (6) | s3c2410_lcdcon2_vspw (3 ),. lcdcon3 = s3c2410_lcdcon3_hbpd (19) | s3c2410_lcdcon3_hozval (239) | s3c2410_lcdcon3_hfpd (7 ),. lcdcon4 = s3c2410_lcdcon4_mval (0) | s3c2410_lcdcon4_hspw (3 ),. lcdcon5 = s3c2410_lcdcon5_frm565 | s3c2410_lcdcon5_invvline | s3c2410_lcdcon5_invvframe | s3c2410_lcdcon5_pwren | s3c2410_lcdcon5_hwswp ,},# If 0 /* Currently setup by downloader */ . Gpccon = 0xaa940659 ,. gpccon_mask = 0 xffffffff ,. gpcup = 0x0000ffff ,. gpcup_mask = 0 xffffffff ,. gpdcon = 0xaa84aaa0 ,. gpdcon_mask = 0 xffffffff ,. gpdup = 0x0000faff ,. gpdup_mask = 0 xffffffff, # endif. lpcsel = (0xce6 )&~ 7) | 1 <4 ,. width = 240 ,. height = 320 ,. xres = {. min = 240 ,. max = 240 ,. defval = 240 ,},. yres = {. min = 320 ,. max = 320 ,. defval = 320 ,},. bpp = {. min = 16 ,. max = 16 ,. defval = 16 ,},}; Static Struct Platform_device * smdk2440_devices [] _ initdata ={& initi_device_usb, & initi_device_ LCD, & initi_device_wdt, & initi_device_i2c, & initi_device_iis ,}; Static Struct S3c24xx_board smdk2440_board _ initdata = {. Devices = smdk2440_devices,. devices_count = array_size (smdk2440_devices )}; Static Void _ Init smdk2440_map_io ( Void ) {S3c24xx_init_io (outputs, array_size (smdk2440_iodesc); s3c24xx_init_clocks (16934400); Round (outputs, array_size (blocks); s3c24xx_set_board (& smdk2440_board );} Static Void _ Init smdk2440_machine_init ( Void ){ /* Configure the LEDs (even if we have no led Support )*/ Round (s3c2410_gpf4, success); Round (s3c2410_gpf5, success); Round (s3c2410_gpf6, success); Round (s3c2410_gpf7, success); s3c2410_gpio_setpin (s3c2410_gpf4, 0); Round (s3c2410_gpf5, 0); s3c2410_gpio_setpin (s3c2410_gpf6, 0); s3c2410_gpio_setpin (s3c2410_gpf7, 0); Round (& found); s3c2410_pm_init ();} machine_start (S3C2440 ," Smdk2440 ") /* Maintainer: Ben dooks <ben@fluff.org> */ . Phys_io = s3c2410_pa_uart ,. io_pg_offst = (u32) s3c24xx_va_uart)> 18) & 0 xfffc ,. boot_params = maid + 0x100 ,. init_irq = s3c24xx_init_irq ,. map_io = smdk2440_map_io ,. init_machine = smdk2440_machine_init ,. timer = & s3c24xx_timer, machine_end
"Mr. Wang, another theme to talk about today is DMA," I added:
DMA is a hardware mechanism that enables bidirectional data transmission between peripherals and system memory without the involvement of the CPU. DMA can be used to remove the system CPU from the actual Io data transmission process, thus greatly improving
The throughput of the system. The DMA data transmission mode is controlled by the DMA controller (DMAc). During the transmission period, the CPU can execute other tasks concurrently. After the DMA ends, DMAc notifies the CPU that the data transmission has ended through interruption, then the CPU executes the corresponding service interruptionProgram.
Speaking of DMA, we will think of cache. The two seem to be irrelevant. Indeed, if there is no overlapping area between the target address of the memory and the object cached by the cache, there will be no peace of mind between the DMA and the cache. However, if there is overlap, the DMA Operation will take place, the memory data corresponding to the cache has been modified, but the CPU itself does not know. It still thinks that the data in the cache is still in the memory. When you access the memory mapped by the cache later, it still uses the old cache data, which will cause an "inconsistency" error between the cache and the memory. Once this happens, the driver will not
Method. How can this problem be solved? The simplest method is to directly disable the memory cache function in the target address range of the DMA instance. Of course, this sacrifices performance, but it is highly reliable. No, so how to balance the two is really hard to solve.
In fact, inconsistency in cache may also occur elsewhere. For example, for ARM processors with MMU function, you need to set invalid cache before enabling MMU, as does TLB.
Now, there are so many things about DMA theory, the rest of which is about DMA programming in Linux. Of course, here is also a bit. How can we operate it, I want to sell a token to the next section.
A zone in the memory used for data interaction with peripherals is called a DMA buffer. When the device does not support scatter/gathercsg, the DMA buffer must be physically connected.
For an ISA Device, Its DMA Operation can only be performed in memory below 16 Mb. Therefore, gfp_dma is used when applying for a DMA buffer using kmalloc (), _ get_free_pages (), and similar functions
This ensures that the obtained memory has the DMA capability.
The kernel defines _ get_free_pages () "shortcut" _ get_dma_pages () for DMA. It adds gfp_dma to the application mark, as shown below:
# DEFINE _ get_dma _ pages (gfp_mask, order) _ get_free_pages (gfp_mask) | gfp_dma, (Order ))
"I don't want to use the order parameter to apply for DMA memory. It feels strange. What should I do? "Mr. Wang complained.
So? So, you can use another function dma_mem_alloc ()Source codeAs follows:
Static Unsigned LongDma_mem_alloc (IntSize ){IntOrder = get_order (size );// Size-> IndexReturn_ Get_dma_pages (gfp_kernel, order );}
"How do you feel about Mr. Wang? Do you want to continue in the next section? What do you know? "I saw Wang's confused eyes.
"Okay, okay. I was planning on that." John smiled again.