從CPU連出來一把線:數據總線、地址總線、控制總線,這把線上掛著N個接口,有相同的,有不同的,名字叫做存儲器接口、中斷控制接口、DMA接口、並行接口、串行接口、AD接口……一個設備要想接入,就用自己的接口和總線上的某個匹配接口對接……於是總線上出現了各種設備:內存、硬盤,鼠標、鍵盤,顯示器……
外設都是通過讀寫設備上的寄存器來進行的,外設寄存器也稱為“I/O端口”,而IO端口有兩種編址方式:獨立編址和統一編制。
統一編址:外設接口中的IO寄存器(即IO端口)與主存單元一樣看待,每個端口占用一個存儲單元的地址,將主存的一部分劃出來用作IO地址空間。
統一編址也稱為“I/O內存”方式,外設寄存器位於“內存空間”(很多外設有自己的內存、緩沖區,外設的寄存器和內存統稱“I/O空間”)。
獨立編址(單獨編址):IO地址與存儲地址分開獨立編址,I/O端口地址不占用存儲空間的地址范圍,這樣,在系統中就存在了另一種與存儲地址無關的IO地址,CPU也必須具有專用與輸入輸出操作的IO指令(IN、OUT等)和控制邏輯。
IO端口:當一個寄存器或者內存位於IO空間時;
IO內存:當一個內存或者寄存器位於內存空間時;
在linux使用platform_driver_register()注冊 platform_driver 時, 需要在 platform_driver 的probe() 裡面知道設備的中斷號, 內存地址等資源。
這些資源的描述信息存放在 resource數據結構中, 相同的資源存放在一個樹形樹形數據結構中, 通過父節點, 兄弟節點, 子節點相連。 比如中斷資源, IO端口資源, IO內存資源, DMA資源有不同資源樹。
Linux使用 struct resource來描述一個resouce
struct resource{
resource_size_t start; //資源范圍的開始
resource_size_t end; //資源范圍的結束
constchar *name;
//資源擁有者名
unsignedlong flags; //資源屬性標識
struct resource *parent, *sibling, *child; //資源樹的父節點, 兄弟節點, 字節點指針
};
resource_size_t 由系統決定為uint32_t 或uint64_t 。
在platform機制裡,使用platform_get_resource()來獲取指定的資源類型。
//比如獲取想獲取中斷號,
irq = platform_get_irq(pdev, 0);
intplatform_get_irq(struct platform_device
*dev, unsigned intnum)
{
struct resource *r =
platform_get_resource(dev,IORESOURCE_IRQ, num);
return r ? r->start: -ENXIO;
}
EXPORT_SYMBOL_GPL(platform_get_irq);
platform_get_irq() //會返回一個start, 即可用的中斷號。
//之後便可使用request_irq()來注冊中斷服務函數。
//再比如想要獲取IO內存資源:
struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM,
0);
即可得到一個IO內存資源節點指針, 包括了地址的開始,結束地址等, 該IO內存的長度可用resource_size() 來獲取, 但這段資源只是一個描述, 想真正使用這段IO內存, 還要經過先申請, 再映射的過程。
例如可使用devm_request_mem_region()申請出使用這段IO內存, 再使用ioremap() 將其映射出來, 供用戶空間使用。
devm_request_mem_region(&pdev->dev, res_mem->start, resource_size(res_mem), res_mem->name))
addr_start = ioremap(res_mem->start, resource_size(res_mem));
ioremap() 的返回值即為該資源的虛擬地址。
IO內存的資源是在設備樹源(DeviceTree Source)文件(以.dts結尾)裡給出的,.dts文件就是用來描述目標板硬件信息的,在uboot啟動後, 使用uboot提供的特定API將其獲取出來, 如fdt_getprop(), fdt_path_offset(),這些API包含在uboot 的頭文件
gpio:gpio-controller@1070000000800 {
#gpio-cells
=<2>;
compatible ="cavium,octeon-3860-gpio";
reg =<0x107000x000008000x00x100>;
gpio-controller;
根據其描述, 可知道gpio控制器的IO內存起始地址為:0x107900000800, 長度為0x100.
即從 0x107900000800 到0x1079000008ff.
在目標板裡使用 cat /proc/iomem可以看到:
1070000000800-10700000008ff: /soc@0/gpio-controller@1070000000800
關於i2c 的描述:
twsi0: i2c@1180000001000{
#address-cells
=<1>;
#size-cells
=<0>;
compatible ="cavium,octeon-3860-twsi";
reg =<0x118000x000010000x00x200>;
interrupts =<045>;
clock-rate =<100000>;
IO內存起始地址為: 0x118000001000, 長度為0x200.
從 0x118000001000 到0x1180000011ff.
在目標板裡使用 cat /proc/iomem可以看到:
1180000001000-11800000011ff: /soc@0/i2c@1180000001000