Linux核心開發之記憶體與I/O訪問(五)

來源:互聯網
上載者:User

“小王,告訴你一個好訊息,最難理解的部分不知不覺中已經講完了,今天的課程就簡單多了,而且最重要的是咱們的Linux裝置驅動核心理論課也差不多了…”

“最難的部分?已經講完了?我咋沒感覺呢..你講的真是太好了,太通俗易懂了,太..”小王調皮的說。

“切,就你嘴甜,我還不知道你啊,小腦筋..”我白了小王一樣。

   那麼今天呢?今天就講講IO記憶體靜態映射。在將Linux移植到目標電路板中,通常會建立外設IO記憶體物理地址到虛擬位址的靜態映射,這個映射通過在電路板對應的

map_desc結構體數組中添加新的成員來完成,map_desc結構體的定義如下:

struct map_desc{   unsigned long virtual;  //虛擬位址   unsigned long pfn;  //__phys_to_pfn(phy_addr)   unsigned long length;   //大小   unsigned int type;  //類型 }

   將Linux作業系統移植到特定平台上,MACHINE_START到MACHINE_EDN宏之間的定義針對特定電路板而設計,其中的map_io()成員函數完成IO記憶體的靜態映射,最後通過調用cpu—>map_io()建立map_desc數組中實體記憶體和虛擬記憶體的靜態映射關係。

   在一個已經移植好的OS的核心中,驅動工程師完全可以對非常規記憶體地區的IO記憶體(外設控制器寄存器,MCU內部整合的外設控制寄存器等)依照電路板的資源使用方式添加到map_desc數組中。下邊給出SMDK2440記憶體情況定義的map_desc:

SMDK2440記憶體資源情況定義的map_desc#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)S3C24XX_VA_ISA_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)S3C24XX_VA_ISA_BYTE + 0x10000,.pfn= __phys_to_pfn(S3C2410_CS2 + (1<<24)),.length= SZ_4M,.type= MT_DEVICE,}};#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODEstatic 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_LCDCON1_TFT16BPP |  S3C2410_LCDCON1_TFT |  S3C2410_LCDCON1_CLKVAL(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= 0xffffffff,.gpcup= 0x0000ffff,.gpcup_mask= 0xffffffff,.gpdcon= 0xaa84aaa0,.gpdcon_mask= 0xffffffff,.gpdup= 0x0000faff,.gpdup_mask= 0xffffffff,#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 = {&s3c_device_usb,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c,&s3c_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(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));s3c24xx_init_clocks(16934400);s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));s3c24xx_set_board(&smdk2440_board);}static void __init smdk2440_machine_init(void){/* Configure the LEDs (even if we have no LED support)*/s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);s3c2410_gpio_setpin(S3C2410_GPF4, 0);s3c2410_gpio_setpin(S3C2410_GPF5, 0);s3c2410_gpio_setpin(S3C2410_GPF6, 0);s3c2410_gpio_setpin(S3C2410_GPF7, 0);s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);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) & 0xfffc,.boot_params= S3C2410_SDRAM_PA + 0x100,.init_irq= s3c24xx_init_irq,.map_io= smdk2440_map_io,.init_machine= smdk2440_machine_init,.timer= &s3c24xx_timer,MACHINE_END

 

“小王,今天要講的另外一個主題是DMA”我補充道:

  DMA是一種無須CPU的參與就可以讓外設與系統記憶體之間進行雙向資料轉送的硬體機制。使用DMA可以是系統CPU從實際的IO資料轉送過程中擺脫出來,從而大大提

供系統的吞吐率。DMA方式的資料轉送由DMA控制器(DMAC)控制,在傳輸期間,CPU可以並發地執行其他任務,當DMA結束後,DMAC通過中斷通知CPU資料轉送已經結束,然後由CPU執行相應的中斷服務程式進行後續處理。

  說到DMA,就會想到Cache,兩者本身似乎是好不相關的事物。的確,假設DMA針對記憶體的目的地址和Cache緩衝的對象沒有重疊地區,DMA和Cache之間就相安無事,但是,如果有重疊呢,經過DMA操作,Cache緩衝對應的記憶體的資料已經被修改,而CPU本身並不知道,它仍然認為Cache中的資料仍然還是記憶體中的資料,以後訪問Cache映射的記憶體時,它仍然使用陳舊的Cache資料,這就會發生Cache與記憶體之間資料“不一致性”的錯誤。一旦出現這樣的情況,沒有處理好,驅動就將無

法正常運行。那麼怎樣解決呢?最簡單的方法是直接禁止DMA目標位址範圍內記憶體的Cache功能,當然這是犧牲效能的,但卻高可靠。不是嗎,所以這兩者之間究竟怎麼平衡,還真不好解決。

  其實啊,Cache不一致的情況在其他地方也是可能發生的,比如,對於帶MMU功能的ARM處理器,在開啟MMU之前,需要設定Cache無效,TLB也是如此。

  說了,那麼多DMA理論的東西,剩下的來點Linux下DMA編程的東西,當然,這裡也是點一下,具體怎麼操作,我可要賣個關子到下節了。

  在記憶體中用於與外設互動資料的一塊地區被稱作DMA緩衝區,在裝置不支援scatter/gatherCSG,分散/聚集操作的情況下,DMA緩衝區必須是物理上聯絡的。

  對於ISA裝置而言,其DMA操作只能在16MB以下的記憶體進行,因此,在使用kmalloc()和__get_free_pages()及其類似函數申請DMA緩衝區時應使用GFP_DMA標

志,這樣能保證獲得的記憶體是具備DMA能力的。

   核心中定義了__get_free_pages()針對DMA的“捷徑”__get_dma_pages(),它在申請標誌中添加了GFP_DMA,如下所示:

  #define __get_dma__pages(gfp_mask, order)  __get_free_pages((gfp_mask) | GFP_DMA, (order))
  "我不想使用order為參數的申請DMA記憶體,感覺就是怪怪的,那咋辦?"小王抱怨道.
  那?這樣吧,你就用另外一個函數dma_mem_alloc()原始碼如下:
static unsigned long dma_mem_alloc(int size){   int order = get_order(size);    //大小->指數   return __get_dma_pages(GFP_KERNEL, order);}
 
"小王,感覺怎樣,要不咱們下節繼續?看你挺多不懂的地方?"我看到小王那充滿疑惑的眼神。
"好,行,我正有這種打算呢"小王又露出了讓人陶醉的笑容。

 

 

  

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.