Linux ARM 驅動筆記之一 : 準備
Linux ARM 驅動筆記之一 : 準備
轉載請註明:http://blog.csdn.net/zkwsr/article/details/12510439
本文以Linux ARMSPI裝置驅動為例記錄Linux驅動的開發中的一些套路和準備.本文內容拋開具體程式碼,描述相關的準備知識,描述書本中很少提及的內容.
1 驅動編寫的套路
Linux 驅動編寫歸根揭底,涉及到底部,就是設定暫存器, 從設定暫存器的角度來看,Linux驅動編寫分為兩種套路:
1) 直接操控暫存器: 這種方式寫驅動程式碼結構簡單,直接設定控制暫存器即可,但是對程式設計師的硬體知識要求比較高, 一個Arm的Datasheet看上3-5遍,才有可能寫出個正確的驅動.(從微控制器寫過程式的程式設計師轉到linux驅動,開始容易寫這種程式碼.)
注意事項:
volatile 這個關鍵字, 有些時候必須加上.否則變數不會及時更新,導致程式出錯.
管腳地址示例:
a) 使用核心設定
定義:S3C2410_GPB(0)//S3C2410 CPU GPIO_B第 0個管腳,linux核心提供了這個對應的巨集.
設定/賦值 : s3c2410_gpio_setpin(S3C2410_GPB(0),0);
b) 直接使用暫存器地址
#define PMC_PCER 0xFFFFFC10 //sam9G15 PMC暫存器.
#definev_uint32_t volatile uint32_t
//根據暫存器的位數設定位元組長度.uint32_t, uint16_t
v_uint32_t *pmc_pcer;
//ioremap地址對映
pmc_pcer =(uint32_t *)ioremap(PMC_PCER, 4);
* pmc_pcer = (1u<< 5); //設定暫存器值第的值
通過直接操控暫存器就可完成裝置的驅動, 但是不建議使用這種方法.
2) 不直接操控暫存器: 通過Linux核心提供的驅動框架寫程式,速度快,相對於直接控制暫存器,對硬體知識要求不那麼高了.在編寫個規範和可靠性方面,按linux驅動框架編寫會更好.驅動架構分為:控制器驅動層/Linux驅動核心層/裝置驅動層. 下面以SPI驅動為例介紹.(其他型別裝置類似)
SPI驅動架構圖:
SPI控制器驅動層:每種處理器平臺都有自己的控制器驅動, 屬於平臺移植相關,一般廠商都會提供到linux核心中. (編譯核心時將之編譯進去,或者選擇為module,編譯後將之insmod)
//這樣程式設計師就不用去過多瞭解硬體控制器部分的細節了.
SPI核心層:Linux SPI的核心部分,裡面定義了相關的資料結構, 用於向上(SPI裝置)提供統一的介面.提供了spi驅動相關資料結構和操作函式.
// linux-2.6.39/drivers/spi/spi.c
SPI裝置驅動(裝置協議驅動):針對不同裝置的特定驅動.利用SPI核心層提供的介面編寫協議驅動. //這個驅動是程式設計師需要開發的驅動.
2 控制器驅動模組的載入:
載入了控制器驅動後, spicore的功能才會正常使用,spidevice 才可以正常執行.
Linux核心編譯時有 [*] [M] [ ] 三個選項:
[*] : 選中編入核心,啟動時會載入.
[ M ] : Module 編譯成模組,手動載入.
[ ] : 不選.
#make menuconfig //進行選擇
#make zImage/uImage/…. 各種形式的核心映象
#make modules //編譯[M]選中的模組,然後在相關路徑下找到, copy出來使用.
// 下圖編譯後module為 linux-2.6.39/drivers/spi/atmel_spi.ko
// 和自己寫的module檔案一樣使用 #insmod.
3 設備註冊形式:
根據裝置的不同存在形式進行程式碼的對應編寫.裝置的存在形式分為板載裝置和動態載入裝置.
1) 板載裝置:裝置啟動時載入的裝置, 固定在板子/機器上的裝置,不進行熱插拔.
linux-2.6.39/arch/arm/mach-at91/board-sam9x5cm.c
// 在spi board_info中新增裝置資訊, 啟動時通過spi core 的__init函式spi_register_board_info()函式進行初始化.
api_register_board_info() <-- at91_add_device_spi()
//載入spi devide 時會呼叫. //在深挖就找不到了,暫時放在這.
2) 動態載入裝置: 載入裝置驅動時載入上去的裝置.
動態裝置新增例子:
152 spi_master =spi_busnum_to_master(SPI_BUS);
153 spi_device =spi_alloc_device(spi_master);
154 pdev =bus_find_device_by_name(spi_device->dev.bus, NULL, buff);
155 if (pdev) {
156 spi_dev_put(spi_device);
157 } else {
158 status =spi_add_device(spi_device);
159 }
160
161 put_device(&spi_master->dev);
其他的驅動也有類似的方法.
4 裝置驅動編寫方法:
關於裝置驅動的寫法,沒有捷徑.多讀程式碼,多寫程式碼.裝置驅動起初最好的方法就是—看核心原始碼,裝置驅動最好的例子. 以spi驅動為例:
~/linux-2.6.39/drivers/spi/spidev.c就是第一個需要看的例子.參照這個例子就能寫出spi裝置驅動. 當然,spidev.c提供的僅僅是一種方式.還有其他的方式—internet資源模仿. 例如:git,github,…. 最終寫出漂亮的驅動.
5 驅動的makefile
1 ifneq ($(KERNELRELEASE),)
2 #module name not same any file
3 MODULE_NAME := EEPROM
4 #module with init and exit
5 RESMAIN_CORE_OBJS :=eeprom.o
6 RESMAIN_GLUE_OBJS := twi.o
7 $(MODULE_NAME)-objs := $(RESMAIN_CORE_OBJS) $(RESMAIN_GLUE_OBJS)
8 obj-m := $(MODULE_NAME).o
9else
10 PWD=$(shell pwd)
11 KERNEL_SRC=~/linux-2.6.39
12default:
13 # -C input kernel src -M reback thismakefile
14 #$(MAKE) -C $(KERNEL_SRC) M=$(PWD)LDDINC=$(PWD) modules
15 $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules
16clean:
17 rm *.o *.ko #and other generated code
18endif
6 關於中斷號
寫中斷函式的時候,需要這樣一個函式:
intrequest_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *devname, void*dev_id);
其中 unsignedint irq, 是中斷號,這個中斷號如何選取,怎麼能和板子上的某個GPIO引腳對應上呢?下面介紹一下查詢中斷號的方法.
首先看CPUdatasheet,有類似下圖的一張表格,列出了0-31個號碼(具體CPU不同,可能編號數目不同 16 32 64…).
內部中斷: 這32是CPU的內部中斷 (外設中斷), 這32箇中斷是由各外設產生的—外設—這裡應該理解成CPU的外設控制器(這樣和外部裝置就能區別開來了).這32箇中斷對應的中斷編號是(0-31),對外是沒有引腳的.(記住,這些中斷是沒辦法和真實的外部裝置用線連起來的, 所以長說為內部中斷, 所謂內部是CPU內部,相對於外部而言的).這些內部中斷普通的驅動程式設計師是不需要去處理的,核心控制器驅動提供者已經做好了相關的工作,僅需要呼叫相關的處理函式即可.
外部中斷:所謂的外部中斷,是在裝置程式設計時長用到的中斷, GPIO_X_Y //GPIO的X控制器第Y個引腳.這個編號從32-255,根據GPIO的引腳數量而定.那GPIO的引腳的中斷線時怎麼編號的呢? 是以32為基數,向上累加的.具體使用可使用相關的標頭檔案對應的定義即可.
下面舉例說明:
例如SAM9G15晶片的 GPIO_D_21引腳對應的中斷號,在linux-2.6.39/arch/arm/mach-at91/include/mach/gpio.h中定義
#define AT91_PIN_PD21 (PIN_BASE + 0x60 + 21) //32+96+21=149
//即GPIO_D_21號引腳對應的中斷編號是 149.
//使用時多使用核心提供的定義,不要使用純數字.
//那麼149顯然不在其上圖0-31箇中斷號中, linux核心為了後續編寫程式方便提供了對映.
//將149對應到了PIOD的暫存器上的第21位.然後進行處理.
//對於上層的程式設計師只需關心GPIO引腳對應的中斷編號即可.