S3C2410/2440 等 ARM SoC 採用 Linux Device Model 的 Platform Bus 機制管理片上外設。本文詳細解析 Platform Bus 註冊、平台裝置定義、資源描述、板級資訊傳遞、MACHINE_START 巨集、s3c24xx_set_board() 以及 platform_device_register() 完整流程。
struct device platform_bus = {
.bus_id = "platform",
};
struct bus_type platform_bus_type = {
.name = "platform",
.match = platform_match,
.suspend = platform_suspend,
.resume = platform_resume,
};
int __init platform_bus_init(void) {
device_register(&platform_bus);
return bus_register(&platform_bus_type);
}
// 板級裝置陣列
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
// 單一裝置範例:NAND 控制器
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND,
.flags = IORESOURCE_MEM,
}
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
resource 陣列描述裝置佔用的記憶體、IRQ、DMA 等硬體資源。
static struct s3c24xx_board smdk2410_board __initdata = {
.devices = smdk2410_devices,
.devices_count = ARRAY_SIZE(smdk2410_devices),
};
static void __init smdk2410_map_io(void) {
s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));
s3c24xx_init_clocks(0);
s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));
s3c24xx_set_board(&smdk2410_board); // 關鍵:將板級資訊傳給核心
}
MACHINE_START(SMDK2410, "SMDK2410")
.phys_ram = S3C2410_SDRAM_PA,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = smdk2410_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
展開後產生 struct machine_desc __mach_desc_SMDK2410,放入 .arch.info.init 區段,供啟動時偵測機器型號使用。
static struct s3c24xx_board *board;
void s3c24xx_set_board(struct s3c24xx_board *b) {
board = b;
if (b->clocks_count) {
struct clk **ptr = b->clocks;
for (int i = b->clocks_count; i > 0; i--, ptr++)
s3c24xx_register_clock(*ptr);
}
}
將板級資訊存入全域變數 board,稍後架構初始化時使用。
static int __init s3c_arch_init(void) {
// ... CPU 初始化 ...
if (board) {
struct platform_device **ptr = board->devices;
for (int i = 0; i < board->devices_count; i++, ptr++) {
int ret = platform_device_register(*ptr);
if (ret)
printk(KERN_ERR "failed to add board device %s\n", (*ptr)->name);
}
}
return 0;
}
arch_initcall(s3c_arch_init);
int platform_device_register(struct platform_device *pdev) {
if (!pdev) return -EINVAL;
// 關聯父裝置
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
// 設定 bus_id
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
return device_add(&pdev->dev); // 加入裝置模型,觸發驅動匹配
}
至此,平台裝置正式掛載到 Platform Bus,等待同名 platform_driver 的 probe() 被呼叫,完成裝置-驅動綁定。