以高通平台为例,会在kernel/arch/arm/mach-msm下的相应的board-xxx.c文件里边用 DT_MACHINE_START()这个宏定义一系列的芯片。以高通8916为例: 在kernel/arch/arm/mach-msm/board-8916.c文件里定义了
//当然下面使用哪个要看一下。 DT_MACHINE_START(MSM8916_DT, "Qualcomm Technologies, Inc. MSM 8916 (Flattened Device Tree)") .map_io = msm8916_map_io, .init_machine = msm8916_init, .dt_compat = msm8916_dt_match, .reserve = msm8916_dt_reserve, .smp = &msm8916_smp_ops, MACHINE_END //下面还定了很多其他名字的,比如msm8939。但最终会根据dt_compat,也就是msm8916_dt_match的名字找到相应的dts文件。比如 static const char *msm8916_dt_match[] __initconst = { "qcom,msm8916", "qcom,apq8016", NULL }; 这个会找到dts文件里边的对应名字的compatible。比如: / { ... compatible = "qcom,msm8916"; ... }; ....在.init_machine对应的msm8916_init()函数里,会查找到设备所有的platform设备进行初始化。
static void __init msm8916_init(void){ ... of_platform_populate(NULL, of_default_bus_match_table, adata, NULL); ... } const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", },//搜一下simple-bus #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ }; //搜一下上面的comatible,比如“simple-bus”可以在类似msm8916.dtsi文件里边找到 &soc { //包含在&soc大括号里边的设备应该是都会被初始化为platform_device?? //只有platform设备才需要device tree初始化! #address-cells = <1>; #size-cells = <1>; ranges = <0 0 0 0xffffffff>; compatible = "simple-bus"; //下面定义了一堆platform设备对应的。 //of_platform_populate函数会根据下面定义的内容,生成相应的platform_device。然后在注册platform_driver的时候,如果名字和定义的一样,就可以进行初始化platform设备了。 //...... tsens: tsens@4a8000 { compatible = "qcom,msm8916-tsens"; reg = <0x4a8000 0x2000>, <0x5c000 0x1000>; reg-names = "tsens_physical", "tsens_eeprom_physical"; interrupts = <0 184 0>; interrupt-names = "tsens-upper-lower"; qcom,sensors = <5>; qcom,slope = <3200 3200 3200 3200 3200>; qcom,sensor-id = <0 1 2 4 5>; }; //....在找到simple-bus并初始化&soc下面所有的platform device之后,就可以在 sys/devices/soc.0下面找到所有的device节点。比如拿上面tsens为例会根据device tree设定的名字platform device id等给设备取名字。
/sys/devices/soc.0/4a8000.tsens而且platform device创建的时候在platform_device_add函数中,dev.bus都会默认设置成platform_bus_type 所以上面4a8000.tsens也会在/sys/bus/platform/devices下面生成一样的节点,并被symlink。
/sys/bus/platform/devices # ls -l lrwxrwxrwx root root 2015-01-13 18:30 4a8000.tsens -> ../../../devices/soc.0/4a8000.tsensplatform设备的名字,通常是@后面的名字.加上:后面的名字。如果什么都没有,就名字加上platform id。 但也有例外,确切的device tree节点名字读一下uevent节点就可以知道。
cpubw: qcom,cpubw@0 {}就会生成/sys/devices/soc.0/0.qcom,cpubw qcom,armbw-pm {}就会生成/sys/devices/soc.0/qcom,armbw-pm.32当然&soc{}这个有效果的前提也是需要包含在整个device tree的根目录下。
/ { model = "Qualcomm Technologies, Inc. MSM8916"; compatible = "qcom,msm8916"; qcom,msm-id = <206 0>, <248 0>, <249 0>, <250 0>; interrupt-parent = <&intc>; chosen { bootargs = "sched_enable_hmp=1"; }; aliases { sdhc1 = &sdhc_1; /* SDC1 eMMC slot */ sdhc2 = &sdhc_2; /* SDC2 SD card slot */ /* smdtty devices */ smd0 = &smdtty_ds; smd1 = &smdtty_apps_fm; smd2 = &smdtty_apps_riva_bt_acl; smd3 = &smdtty_apps_riva_bt_cmd; smd4 = &smdtty_mbalbridge; smd5 = &smdtty_apps_riva_ant_cmd; smd6 = &smdtty_apps_riva_ant_data; smd7 = &smdtty_data1; smd8 = &smdtty_data4; smd11 = &smdtty_data11; smd21 = &smdtty_data21; smd36 = &smdtty_loopback; //spi0 = &spi_0; /* SPI0 controller device */ i2c0 = &i2c_0; /* I2C0 controller device */ i2c1 = &i2c_1; /* I2C1 controller device */ i2c5 = &i2c_5; /* I2C5 controller device */ i2c6 = &i2c_6; /* I2C6 NFC qup6 device */ i2c4 = &i2c_4; /* I2C4 controller device */ }; cpus { #address-cells = <1>; #size-cells = <0>; CPU0: cpu@0 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0>; enable-method = "qcom,arm-cortex-acc"; qcom,acc = <&acc0>; next-level-cache = <&L2_0>; L2_0: l2-cache { compatible = "arm,arch-cache"; cache-level = <2>; power-domain = <&l2ccc_0>; }; }; CPU1: cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x1>; enable-method = "qcom,arm-cortex-acc"; qcom,acc = <&acc1>; next-level-cache = <&L2_0>; }; CPU2: cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x2>; enable-method = "qcom,arm-cortex-acc"; qcom,acc = <&acc2>; next-level-cache = <&L2_0>; }; CPU3: cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x3>; enable-method = "qcom,arm-cortex-acc"; qcom,acc = <&acc3>; next-level-cache = <&L2_0>; }; }; soc: soc { };//没有这个,platform设备相关的&soc{}包含的都是不会被初始化的!!! };具体的platform驱动在找到对应的compatible的名字之后,就可以添加设备驱动。 这个过程中会在/sys/bus/platform/driver目录下生成与驱动的名字对应名字的节点。 比如下面定义的platform driver在被找到对应的platform device之后会生成 相应的节点。
static struct platform_driver mdm_driver = { .probe = mdm_probe, .driver = { .name = "ext-mdm", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mdm_dt_match), }, }; 生成的节点是: /sys/bus/platform/drivers/ext-mdm现在platform device都是从device tree里边读出并进行platform device的初始化。 以前是对应platform device和platform driver的名字的话,现在是对比device相关的compatible的内容了。 像下面i2c-versatile驱动中,在i2c_versatile_match.compatible的字符串,如果与某个&soc{}里边的compatible内容一致,就回去调用相应的probe函数去初始化platform。
drivers/i2c/busses/i2c-versatile.c文件里边可以看到像下面的基本的platform设备的定义以及函数调用
static const struct of_device_id i2c_versatile_match[] = { { .compatible = "arm,versatile-i2c", }, {}, }; MODULE_DEVICE_TABLE(of, i2c_versatile_match); static struct platform_driver i2c_versatile_driver = { .probe = i2c_versatile_probe, .remove = i2c_versatile_remove, .driver = { .name = "versatile-i2c", .owner = THIS_MODULE, .of_match_table = i2c_versatile_match, }, }; static int __init i2c_versatile_init(void) { return platform_driver_register(&i2c_versatile_driver); } static void __exit i2c_versatile_exit(void) { platform_driver_unregister(&i2c_versatile_driver); } subsys_initcall(i2c_versatile_init); module_exit(i2c_versatile_exit); MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:versatile-i2c");/sys/bus/platform/devices/versatile-i2c.0/ modalias ,uevent这几个文件,power,subsystem文件夹
/sys/devices/platform/versatile-i2c.0/ modalias ,uevent这几个文件,power,subsystem文件夹
#######################################################
int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } struct bus_type platform_bus_type= { .name = "platform", .dev_attrs =platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; static struct device_attribute platform_dev_attrs[] = { __ATTR_RO(modalias), __ATTR_NULL, };这里的platform_bus_type会用bus_register()先注册一下。
int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //kobj名字设定为platform if (retval) goto out; priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); if (retval) goto out; retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } INIT_LIST_HEAD(&priv->interfaces); __mutex_init(&priv->mutex, "subsys mutex", key); klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0; bus_attrs_fail: remove_probe_files(bus); bus_probe_files_fail: kset_unregister(bus->p->drivers_kset); bus_drivers_fail: kset_unregister(bus->p->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: kset_unregister(&bus->p->subsys); out: kfree(bus->p); bus->p = NULL; return retval; }待续….
