本文根据网上资料理解、汇总、整理得到。
1.首先定义某个硬件(以mt6622蓝牙芯片为例)的资源结构体。
static struct mt6622_platform_data mt6622_platdata = {
.power_gpio =
{
.io = RK30_PIN3_PC7,
.enable = GPIO_HIGH,
.iomux = {
.name = NULL,
},
},
.reset_gpio =
{
.io = RK30_PIN3_PD1,
.enable = GPIO_LOW,
.iomux = {
.name = NULL,
},
},
.irq_gpio =
{
.io = RK30_PIN6_PA7,
.enable = GPIO_HIGH,
.iomux = {
.name = NULL,
},
}
};
2.将上一步的资源结构体作为platform设备的私有数据,这里是赋值给了platform_device结构体内部的struct device dev,还有一种做法(应该更常见)是把硬件资源信息赋于platform_device结构体内部的struct resource * resource指针变量。
static struct platform_device device_mt6622 = {
.name = "mt6622",
.id = -1,
.dev = {
.platform_data = &mt6622_platdata,
},
};
3.用上一步的platform设备结构体填充platform结构体数组以备统一注册。
static struct platform_device *devices[] __initdata = {
&rk29_device_backlight,
&device_fb,
&device_ion,
&rk29_device_vibrator,
&rk29_device_gpio_leds,
&irda_device,
&rk29sdk_wifi_device,
&rk30_device_modem,
&rk29_device_mu509,
&rk29_device_mw100,
&rk29_device_mt6229,
&rk30_device_sew868,
&rk30_device_adc_battery,
&device_rfkill_rk,
&device_mt6622,
&pwm_regulator_device[0],
};
4.在板级初始化函数中统一注册platform device结构体,rk30是SOC的名字。
static void __init machine_rk30_board_init(void)
{
.........................
//板级相关的硬件资源信息都在这里注册。
//申请电源启动引脚,如果被占用会申请失败
gpio_request(POWER_ON_PIN, "poweronpin");
//设置电源引脚为输出引脚
gpio_direction_output(POWER_ON_PIN, GPIO_HIGH);
//给电源管理的系统关闭函数赋函数指针
pm_power_off = rk30_pm_power_off;
//注册I2C的板级信息
rk30_i2c_register_board_info();
//注册SPI的板级信息
spi_register_board_info(board_spi_devices, ARRAY_SIZE(board_spi_devices));
//注册platform的板级信息
platform_add_devices(devices, ARRAY_SIZE(devices));
//初始化USB的检测引脚
board_usb_detect_init(RK30_PIN6_PA3);
......
}
5.板级设备函数machine_rk30_board_init( )被赋值给struct machine_desc的变量,以备在板子启动时候调用该注册函数。
在arch\arm\下的mach或plat开头的文件和文件夹中。
MACHINE_START(RK30, "RK30board")
.boot_params = PLAT_PHYS_OFFSET + 0x800,
.fixup = rk30_fixup,
.reserve = &rk30_reserve,
.map_io = rk30_map_io,
.init_irq = rk30_init_irq,
.timer = &rk30_timer,
.init_machine = machine_rk30_board_init,//不同电路板用不同函数名标识
MACHINE_END
其中MACHINE_START、MACHINE_END都是定义的宏,两个宏一起定义了一个类型为struct machine_desc的变量。由宏定义可知struct machine_desc被放置在__section__(".arch.info.init")段。
其中的nr值很关键,它会与bootloader传来的nr做匹配,来获取具体特定板级(即为什么是rk30,而不是rk40 ) 相关硬件资源描述信息。
接下来看内核启动过程中是如何注册特定板级的信息,注册的过程也可以看作是内核获取相关板级硬件信息的过程,大致的调用流程:
start_kernel()--->setup_arch()--->setup_machine_tags(),在此函数中已获得板级资源相关的struct machine_desc变量--->do_initcalls()--->customize_machine()--->machine_rk30_board_init()
首先使用mdesc = setup_machine_fdt(__atags_pointer)获取mdesc,__atags_pointer是uboot传递的参数存放位置,这里等于0x80000100,后边会解释。setup_machine_fdt()起作用的话,需要uboot传递的参数是设备树device tree的存放方式,我们实际使用的是tags_list方式,所以这里返回值为NULL。所以是通过mdesc = setup_machine_tags(machine_arch_type)获得的machine desc,machine_arch_type是uboot传递的machid。
在setup_machine_tags( )中有如下代码:
for_each_machine_desc(p)
{
if (nr == p->nr)
{
printk("Machine: %s\n", p->name);
mdesc = p;
break;
}
}
nr= machid=MACH_TYPE_AM335XEVM=3589是uboot传递过来的,而内核中的MACH_TYPE_AM335XEVM, 在/include/generated/mach-types.h中定义,这个文件是动态产生的。如何确定是哪个machine_desc?从machine_desc存放的段中,选择机器码与从uboot传递过来的机器号nr比较,相等即得到了与板级相关的struct machine_desc。
接下来看这个struct machine_desc类型的变量如何被调用的。
do_initcalls()执行下面的链接脚本指定的段中的函数,这其中有接下来要调用的customize_machine(),在arch/arm/kernel/vmlinux.lds中,
下面先贴上customize_machine()函数,
在include/linux/init.h中:
经过宏定义处理,最后customize_machine()被链接到了 ".initcall3.init" 段,所以会被 do_initcalls()执行。
在驱动程序中获取硬件资源信息:
以上就是内核如何注册板级硬件信息过程,以及驱动如何获取相关信息(均未使用设备树)。