初始化
以高通平台为例,会在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.tsens
platform设备的名字,通常是@后面的名字.加上:后面的名字。如果什么都没有,就名字加上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
以i2c-versatile驱动为例,说明platform设备名字匹配,初始化和生成节点等
现在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;
}
待续….