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)