1 基于Platform总线的驱动开发流程
·定义初始化platform bus
·定义各种platform devices
·注册各种platform devices
·定义相关platform driver
·注册相关platform driver
·操作相关设备
基于Mx53QSB为例,实现流程如下:
1.1 初始化platform_bus
初始化代码在kernel_imx/drivers/base/platform.c中#L28,
struct device platform_bus = {
.init_name = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);
int __init platform_bus_init(void) #L1020
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
该函数创建了一个名为“platform”的设备,后续platform的设备都会以此为parent,在sysfs中表示为:所有platform类型的设备都会添加在platform_bus所代表的目录下,即/sys/devices/platform。
Platform_bus必须在系统注册任何platform driver和platform device之前初始化,实现如下:
Kernel_imx/drivers/base/Init.c #L20
/**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();
devices_init();
buses_init();
classes_init();
firmware_init();
hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
}
start_kernel(init/main.c#L539) >>rest_init(init/main.c#L429)>>kenel_init(init/main.c#L866)>>do_basic_setup(init/Main.c#L797)>>driver_init>>platform_bus_init
platform driver 和platform devices 的初始化是在do_initcalls(do_basic_setup(init/Main.c#L806处调用))中进行的的。
1.2 定义platform_device
Kernel_imx/arch/arm/mach-mx5/Devices.c #L649中定义了系统的资源,大部分板级资源都在这里集中定义。
static struct resource mxci2c1_resources[] = {
{
.start = I2C1_BASE_ADDR,
.end = I2C1_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_I2C1,
.end = MXC_INT_I2C1,
.flags = IORESOURCE_IRQ,
},
};
static struct resource mxci2c2_resources[] = {
{
.start = I2C2_BASE_ADDR,
.end = I2C2_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_I2C2,
.end = MXC_INT_I2C2,
.flags = IORESOURCE_IRQ,
},
};
static struct resource mxci2c3_resources[] = {
{
.start = I2C3_BASE_ADDR,
.end = I2C3_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_I2C3,
.end = MXC_INT_I2C3,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device mxci2c_devices[] = {
{
.name = "imx-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(mxci2c1_resources),
.resource = mxci2c1_resources,
},
{
.name = "imx-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(mxci2c2_resources),
.resource = mxci2c2_resources,
},
{
.name = "imx-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(mxci2c3_resources),
.resource = mxci2c3_resources,
},
};
设备名称为imx-i2c,.id说明设备的序号,共有三个, mxci2c1_resources,mxci2c2_resources,mxci2c3_resources分别对应设备的资源,资源包括I2c控制器的寄存器空间和中断信息。
1.3 注册platform_device
定义了platflat_device后,需要添加到系统中,就可以调用函数mxc_register_device。
Kernel_imx/arch /arm/plat-mxc.c#L108
int __init mxc_register_device(struct platform_device *pdev, void *data)
{
int ret;
pdev->dev.platform_data = data;
ret = platform_device_register(pdev);
if (ret)
pr_debug("Unable to register platform device '%s': %d/n",
pdev->name, ret);
return ret;
}
在mxc_board_init()函数中,调用mxc_register_device对一些外设进行注册。
static void __init mxc_board_init(void)
{
mxc_ipu_data.di_clk[0] = clk_get(NULL, "ipu_di0_clk");
mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");
mxc_ipu_data.csi_clk[0] = clk_get(NULL, "ssi_ext1_clk");
mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_xtal_clk");
clk_put(mxc_spdif_data.spdif_core_clk);
mxcsdhc3_device.resource[2].start = IOMUX_TO_IRQ_V3(SD3_CD);
mxcsdhc3_device.resource[2].end = IOMUX_TO_IRQ_V3(SD3_CD);
mxc_cpu_common_init();
mx53_loco_io_init();
mxc_register_device(&mxc_dma_device, NULL);
mxc_register_device(&mxc_wdt_device, NULL);
mxc_register_device(&mxci2c_devices[0], &mxci2c_data);
mxc_register_device(&mxci2c_devices[1], &mxci2c_data);
mx53_loco_init_da9052();
mxc_register_device(&mxc_rtc_device, NULL);
mxc_register_device(&mxc_ipu_device, &mxc_ipu_data);
mxc_register_device(&mxc_ldb_device, &ldb_data);
mxc_register_device(&mxc_tve_device, &tve_data);
mxc_register_device(&mxcvpu_device, &mxc_vpu_data);
mxc_register_device(&gpu_device, &z160_revision);
mxc_register_device(&mxcscc_device, NULL);
mxc_register_device(&mxc_dvfs_core_device, &dvfs_core_data);
mxc_register_device(&busfreq_device, &bus_freq_data);
mxc_register_device(&mxc_iim_device, &iim_data);
mxc_register_device(&mxc_pwm2_device, NULL);
mxc_register_device(&mxc_pwm1_backlight_device, &mxc_pwm_backlight_data);
mxc_register_device(&mxcsdhc1_device, &mmc1_data);
mxc_register_device(&mxcsdhc3_device, &mmc3_data);
mxc_register_device(&mxc_ssi1_device, NULL);
mxc_register_device(&mxc_ssi2_device, NULL);
mxc_register_device(&mxc_alsa_spdif_device, &mxc_spdif_data);
mxc_register_device(&ahci_fsl_device, &sata_data);
mxc_register_device(&mxc_fec_device, &fec_data);
/* ASRC is only available for MX53 TO2.0 */
if (cpu_is_mx53_rev(CHIP_REV_2_0) >= 1) {
mxc_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk");
clk_put(mxc_asrc_data.asrc_core_clk);
mxc_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk");
clk_set_rate(mxc_asrc_data.asrc_audio_clk, 1190000);
clk_put(mxc_asrc_data.asrc_audio_clk);
mxc_register_device(&mxc_asrc_device, &mxc_asrc_data);
}
i2c_register_board_info(0, mxc_i2c0_board_info,
ARRAY_SIZE(mxc_i2c0_board_info));
i2c_register_board_info(1, mxc_i2c1_board_info,
ARRAY_SIZE(mxc_i2c1_board_info));
mxc_register_device(&mxc_sgtl5000_device, &sgtl5000_data);
mx5_usb_dr_init();
mx5_set_host1_vbus_func(mx53_loco_usbh1_vbus);
mx5_usbh1_init();
mxc_register_device(&mxc_v4l2_device, NULL);
mxc_register_device(&mxc_v4l2out_device, NULL);
mxc_register_device(&mxc_android_pmem_device, &android_pmem_data);
mxc_register_device(&mxc_android_pmem_gpu_device, &android_pmem_gpu_data);
mxc_register_device(&usb_mass_storage_device, &mass_storage_data);
mxc_register_device(&usb_rndis_device, &rndis_data);
mxc_register_device(&android_usb_device, &android_usb_data);
loco_add_device_buttons();
}
mxc_board_init定义在Mx53_LOCO板子的MACHINE_START中。
MACHINE_START(MX53_LOCO, "Freescale MX53 LOCO Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.fixup = fixup_mxc_board,
.map_io = mx5_map_io,
.init_irq = mx5_init_irq,
.init_machine = mxc_board_init,
.timer = &mxc_timer,
MACHINE_END
利用mxc_register_device将系统资源注册进系统,在此之前platform bus 需要初始化成功,否则无法将platform devices挂接到platform bus上。为了保证platform drvier初始化时,相关platform资源已经注册进系统,mxc_board_init需要很早执行,而其作为init_machine时,将优先于系统所有驱动的初始化。
调用顺序如下:start_kernel(init/main.c#L539) >>setup_arch(arch/arm/kernel/setup.c#L670)>> init_machine(arch/arm/kernel/setup.c#L742)>> customize_machine(arch/arm/kernel/setup.c#L663)>>arch_initcall(arch/arm/kernel/setup.c#L670)
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
unwind_init();