Linux Kernel Porting CPU是什麼?
Linux kernel發展至今,除了X86,代碼包中默認支持很多流行的CPU,arch\arm\下可以看到已支持的ARM 架構的CPU。 但是, Linux kernel代碼包不能做到支持每個CPU, 但是系統提供了一套完整的interface,讓廠商可以更容易的在linux kernel上移植自家的CPU, 讓系統跑起來。
Porting CPU 涉及的重要的點
1. IO 地址映射
IO地址(如GPIO地址)需要被映射爲虛擬地址,內核通過訪問虛擬地址去訪問IO。虛擬地址段建議處於第4G的頂部。
比如某32位處理器某GPIO物理地址是0x25000000, 內核中不能用物理地址去訪問,value = *(volatile)(unsigned int *)0x25000000內核認爲是非法的地址訪問。 正確的訪問方法是,首先0x25000000 映射到第4G空間的一個虛擬地址,如0xf4000000,
通過虛擬地址訪問GPIO,value = *(volatile)(unsigned int *)0xf4000000是合法的。
2. 中斷系統初始化,重寫操作函數
Linux是一個開源的os, 支持X86,ARM等架構。對於中斷,先對其抽象,然後設計出一套通用的框架,即中斷子系統。對外,爲驅動開發者提供統一的內核API;對內,子系統必須提供接口支持和具體的硬件操作相關聯,常用函數指針來實現。所以具體的CPU,都需要重寫操作函數,中斷系統才能正確的操作中斷相關的寄存器。
3. 註冊時鐘源
linux 新版中有時鐘源的概念,總線時鐘多被抽象爲時鐘源,如果CPU核的clock, IIC,SPI的時鐘可以抽象爲時鐘源。
4. CPU頻率調節,重寫操作函數
頻率調節是CPU一個重要的功能,是通過設置reg實現的,需要重寫操作函數實現設置reg的具體方法
Porting CPU 涉及的重要的數據結構
1. IO 地址映射
(1) 填充關鍵數據結構
struct map_desc {
unsigned long virtual; //首個IO 物理地址對應的虛擬地址
unsigned long pfn; //首個IO 物理地址page frame num
unsigned long length; //映射總長度
unsigned int type;
};
(2) 註冊 map_desc數據結構
iotable_init(struct map_desc *io_desc, int nr)
(1) map_desc可描述地址連續的IO地址空間,即一段IO地址
(2) 內核空間可以通過virtual訪問 IO
2. 中斷系統初始化,設置回調函數
(1) 填充關鍵數據結構 struct irq_chip
根據CPU設計一貫方法看,下面的四個回調函數是最小集合
函數指針 | 功能描述 |
void(*irq_ack)(...) | clear irq |
void(*irq_mask)(...) | mask irq |
void(*irq_unmask)(...) | unmask irq |
(2) 註冊函數
irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip, irq_flow_handler_t handle)
中斷號和中斷操作函數,中斷handle三者相關聯,並註冊。完成註冊後,調用set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 系統才認爲irq是激活的
3. 註冊時鐘源
(1) 關鍵數據結構struct clk_lookup
(2) 向系統註冊struct clk_lookup
clkdev_add(struct clk_lookup *cl) //添加時鐘源 clk_lookup
(1) struct clk結構的定義請另外參考,不做說明。 需要什麼數據結構,就定義什麼,然後填充,linux 開發方法大多如此
4. 設置 CPU頻率調節回調函數
(1) 填充關鍵數據結構struct cpufreq_driver
下面的項是必須要填充(所謂必須,也未必,只是通常都會)的:
struct module *owner;
u8 flags
char name[cpufreq_name_len];
int (*init) (...)
int(*verify) (...)
unsigned int (*get) (...)
int (*target) (...)
unsigned int (*get) (...)
(2) 向系統註冊數據結構struct cpufreq_driver
int cpufreq_register_driver(struct cpufreq_driver *driver_data)