文章目錄
Linux核心升級LCD驅動的更換(開發板)
關鍵字
核心升級 ,更換驅動 ,LCD
概 述
本文給出了將一個已有的LCD驅動添加進一個新的linux核心中的方法
一. 概述
本文搜集整理了Linux系統編譯時間的主要配置選項(make config)的詳細說明,供Linux裁剪特別是裝置驅動和模組功能增刪時使用參考。需要注意的是,每個版本linux版本的config各選項意義,命名等都可能有所差異。
1、 修改核心根目錄config檔案
文本方式開啟config檔案,將LCD的幾個編譯開關添加進Frame buffer hardware drivers這一項目下,之後在make menuconfig中就可以看到。
#
# Frame buffer hardware drivers
#
CONFIG_FB_SIDSA=y
CONFIG_FB_TCM=y
# CONFIG_FB_SDRAM is not set
CONFIG_TFT_AT91=y
CONFIG_FB_SIDSA_DEFAULT_BPP=16
# CONFIG_FB_SIDSA_DEBUG is not set
在控制台顯示驅動項目中將linux logo顯示的編譯開關開啟,logo顯示16色或其他均可。
#
# Console display driver support
#
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
CONFIG_LOGO_LINUX_VGA16=y
添加好上述config選項後,使用make menuconfig開啟該config檔案,即可看到這些選項,一一進行確認:
LCD的配置項位於drivers->graphices support下 分別將frame buffer和logo的選項選中,才能使用邏輯屏和logo圖片。Bootup logo中可選黑白圖,16色圖或256色圖其中一種均可。
Frame buffer的裝置驅動選中SIDSA控制器,目前邏輯屏比較小為QVGA,資料來源選為TCM等選項。開發板具有內建大約1M多的SRAM,當邏輯屏比較大時,應選SDRAM.
2、 添加屏驅動源碼到工程目錄
Linux的屏驅動一般位於drivers/video 目錄下,目前AT91SAM926開發板使用的LCD驅動是sidsafb.c和sidsafb.h,at91sam9261_lcdc.h三個檔案,在標準的linux2.6.25上是沒有的,從開發板源碼包中複製過來放到該目錄下(還要進行一些代碼修改,後面的步驟會講到)。
3、修改邏輯屏驅動模組的Makefile檔案
開啟drivers/video目錄下的Makefile檔案,在其中添加sidsafb.o檔案的編譯選項,依賴於編譯開關CONFIG_FB_SIDSA,也可使用檔案比較的方式和開發板同名makefile比較,將藍色方框部分複製到你的makefile
4、修改模組的Kconfig檔案
開啟drivers/video目錄下的Kconfig檔案,添加FB_SIDSA, FB_TCM等幾個編譯開關的配置,也可使用檔案比較子將開發板源碼包下的同名Kconfig檔案比較,將藍色方框部分的幾個編譯開關選項複製添加到你的drivers/video/Kconfig中,如:
5、原始碼的修改
移植驅動時有兩種工作方式,一種是所有驅動同時一併移植,這樣做的好處是各驅動模組公用檔案如寄存器表,相關宏的標頭檔放置的比較合理,移植完後清晰,缺點是交叉錯誤多,調試工作量大,一兩個人無法完成。另一種是單個模組串列一一移植,好處是調試比較好調,缺點是移植完代碼可能比較亂。綜合一下,個人認為驅動整合移植時先移植所有公用的標頭檔,公用的功能函數和宏,不添加具體模組,編譯出一個基本版本,在此版本基礎上在分頭移植各個模組比較合理。
單驅動模組移植時,總結下來原始碼的修改主要包括幾個部分:
a) 驅動的依賴關係整理清楚,若本驅動還依賴其他驅動,則還需要先移植添加其他驅動模組
b) 理清所添加驅動對外部變數,函數,宏的參考關聯性,去除編譯錯誤。
c) 修改虛擬位址->物理地址的映射函數為當前平台的映射函數,使寄存器map表映射正確。
d) 修改驅動的註冊和載入部分。
具體改動如下:(此部分改動已有改好的版本在FTP,列舉只是為了說明問題和給出方法)
● 模組依賴關係和編譯錯誤的整理
由於本開發板的屏是RGB屏,所以實際驅動是由AT91所帶的LCDC控制器來完成,因此屏的驅動主要工作是配置LCDC和管理Frame buffer兩部分。LCDC依賴於另外兩個模組:IO和CLOCK(即代碼中的PIO和PMC模組),IO用來控制LCDC的電源,CLOCK用來給出LCDC時序的時基。所以先要添加PIO和PMC部分的代碼.為了不至於要掛載整個PIO和PMC模組,開發板的BSP將一些簡單的功能獨立出來作為函數供其他模組調用。
從開發板源碼中找到PIO的寄存器定義相關宏,拷貝出來添加到sidsafb.c頭部或sidsafb.h,不嫌麻煩的話也可另建標頭檔,PIO的寄存器主要是這些宏:
#define PIO_PER (0x0000) /**< PIO Enable Register */
#define PIO_PDR (0x0004) /**< PIO Disable Register */
#define PIO_PSR (0x0008) /**< PIO Status Register */
#define PIO_OER (0x0010) /**< Output Enable Register */
#define PIO_ODR (0x0014) /**< Output Disable Registerr */
#define PIO_OSR (0x0018) /**< Output Status Register */
#define PIO_IFER (0x0020) /**< Input Filter Enable Register */
………
同樣添加PMC模組的寄存器宏,主要是這些:
#define PMC_SCER (0x0000) /**< System Clock Enable Register */
#define PMC_SCDR (0x0004) /**< System Clock Disable Register */
#define PMC_SCSR (0x0008) /**< System Clock Status Register */
#define PMC_PCER (0x0010) /**< Peripheral Clock Enable Register */
#define PMC_PCDR (0x0014) /**< Peripheral Clock Disable Register */
#define PMC_PCSR (0x0018) /**< Peripheral Clock Status Register */
#define PMC_MOR (0x0020) /**< Main Oscillator Register */
……..
添加各模組寄存器讀寫的宏
#define readreg_pmc(offset) readl(AT91C_VA_BASE_PMC + offset)
#define writereg_pmc(value, offset) writel(value, (AT91C_VA_BASE_PMC + offset))
有了這些宏,對IO和CLOCK的操作函數基本就可以加進來編譯,屏驅動中引用到的操作函數主要是:
at91_gpio_set_level
at91_gpio_periph_enable
at91_gpio_configure
at91_device_pio_setup
at91_enable_periph_clock
at91_disable_system_clock
at91_enable_system_clock
at91_lcdc_clock_enable
at91_lcdc_power_up
● 虛擬位址-物理地址轉換函式的修改
去除上述產生的所有編譯錯誤,將其他一些零碎的未定義的對象添加進來,基本就可以編譯通過了。但還不能運行,一般地講,兩個linux版本的虛擬位址到物理地址的轉換關係是不一致的,而驅動對寄存器的訪問都是通過虛擬位址進行,因此要使驅動能在新的linux核心上運行,必須使用該linux的虛擬位址-物理地址轉換公式,擷取到正確的寄存器虛擬位址。
Linux系統的裝置記憶體映射都映射到系統空間(0xc00000000-0xFFFFFFFF)的固定映射區之內,映射方式是線性、連續的,因此裝置的物理地址和虛擬位址只相差一個常數位移量,當實體記憶體小於896M時,高端映射區未用,固定映射區的首地址一般就是緊接在vmalloc區的後面,不同的版本不同的晶片體系中vmalloc區的尾地址定義有所不同,在vmalloc.h中定義:
Linux 2.6.15-AT91定義為
#define VMALLOC_END (0xFF000000 - 0x00200000)
Linux 2.6.25-AT91定義為
#define VMALLOC_END (AT91_VIRT_BASE & PGDIR_MASK)
在Linux2.6.15-AT91體系中,物理地址到虛擬位址的轉換相差的常數依賴於VMALLOC_END(/include/asm-arm/arch-at91sam9261/hardware.h):
#define AT91C_IO_PHYS_BASE 0xFFFFF000
#define AT91C_IO_VIRT_BASE VMALLOC_END
/* Convert a physical IO address to virtual IO address */
#define AT91_IO_P2V(x) ((x) - AT91C_IO_PHYS_BASE + AT91C_IO_VIRT_BASE)
即物理地址轉換為虛擬位址時,虛擬位址=物理地址-(0xFFFFF000- VMALLOC_END)
在Linux2.6.25-AT91體系中,IO物理地址到物理地址轉換公式為:
#define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE)
但其物理基地址AT91_IO_PHYS_BASE和虛擬基地址AT91_IO_VIRT_BASE和Linux2.6.15版本均不同,(由於對其的長度不同,因此IO物理基地址可能不同版本都不同,但具體到任意一個寄存器的物理地址,最終換算出來基地址+OFFSET都是一樣的),因此寄存器的訪問,必須採用當前體系的基地址,當前體系的OFFSET,當前體系的映射方式AT91_IO_P2V,才能得到當前體系下對應的正確的虛擬位址。
代碼中將舊的映射函數AT91_IO_P2V屏蔽不用,採用新版本同名的AT91_IO_P2V映射函數,並將所有使用到的寄存器基地址,位移量修改為當前體系的值,此處只用到LCDC,PIO,PMC三片寄存器,將其基地址改為目前的版本定義值:
#define AT91C_VA_BASE_PIOA AT91_IO_P2V(AT91C_BASE_PIOA)
#define AT91C_VA_BASE_PMC AT91_IO_P2V(AT91C_BASE_PMC)
改為
#define AT91C_VA_BASE_PIOA AT91_IO_P2V(AT91_PIOA)
#define AT91C_VA_BASE_PMC AT91_IO_P2V(AT91_PMC)
至此,在當前體系下寄存器的虛擬位址就可以正確地得到了,使用虛擬位址去配置寄存器,才不會產生分頁錯,data abort之類地記憶體錯誤,
● 裝置註冊和初始化的修改
裝置的註冊和初始化一般位於arch/arm/對應體系目錄下,使用AT91晶片時,主要是at91sam9261.c,at91sam9261_devices.c和board-sam9261ek.c三個代碼檔案和一些.h檔案,第一個at91sam9261.c負責總的調用和定義,第二個at91sam9261_devices.c負責主要片內控制器的定義,初始化,其中LCDC就包含在這裡面。第三個board-sam9261ek.c負責片外板上裝置的定義和初始化,同一款晶片不同的板子不同的硬體在這裡區別。
在at91sam9261_devices.c中找到LCD的定義和初始化,標準Linux2.6.25使用的是CONFIG_FB_ATMEL,將其改為當前開發板對應的代碼,主要是RGB屏時序的定義,FB的位置地區,和普通裝置device不一樣,LCDC屬於平台標配裝置,其類型為platform_device,需要定義對DMA的使用等,將其裝置名稱改為sidsafb.c檔案中定義的"sidsa-lcdc",這樣啟動時才能找到"sidsa-lcdc"並將其掛載。這樣,LCD驅動就移植完畢,編譯通過,燒寫後啟動即可亮屏,如果想修改logo圖片,到drivers/video/logo目錄下修改圖片,或直接修改其對應的數組。
LCDC的裝置是:
static struct platform_device at91_lcdc_device = {
.name = "sidsa-lcdc",
.id = 0,
.num_resources = ARRAY_SIZE(lcdc_resources),
.resource = lcdc_resources,
.dev = {
.dma_mask = &lcdc_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &lcdc_data,
},
};
調試過程中可在關鍵的函數中使用printk添加一些列印語句,如:
init/main.c中的start_kernel函數是核心啟動的第一個函數,在這裡進行各項初始化,可在其中加一些列印觀察情況;
at91sam9260.c和at91sam9261_devices.c中進行晶片裝置的載入,可加一些列印,觀察哪些裝置載入成功,哪些載入失敗。
觀察所加的裝置"sidsa-lcdc”是否被正常掛載,可在裝置的初始化函數sidsafb_init中添加列印,觀察是否進入其中初始化,且註冊裝置driver_register時返回的值是否正確等。
編譯時間,要使用AT91-SAM926的config檔案,由於每個板子的記憶體基地址和長度不一樣,因此要在config中定義,也可以在代碼中定義:(include/asm-arm/arch-at91/hardware.h)
#define AT91_SDRAM_BASE 0x20000000
屏基本上可以亮了,但還有個bug,就是顯示完logo後之後一會兒就滅掉了,這是因為Linux2.6.25版本上的預設BSP時鐘使用方式和現有開發板不一致,LCD定義的CLOCK有衝突,將如下CLK定義宏暫時登出即可正常顯示:
//#define AT91_PMC_HCK1(1 << 17) /* AHB Clock (LCD) [AT91SAM9261 only] */
//lingzj remove
#define AT91_PMC_HCK1 (1 << 1) /* AHB Clock (LCD) [AT91SAM9261 only] */
—— 完 ——