文件路径:kernel\arch\arm\mach-tcc803x\board-tcc803x.c
struct of_dev_auxdata {
char *compatible;
resource_size_t phys_addr;
char *name;
void *platform_data;
};
#define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \
{ .compatible = _compat, .phys_addr = _phys, .name = _name, \
.platform_data = _pdata }
static struct of_dev_auxdata tcc803x_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("telechips,vioc-fb", TCC803X_PA_VIOC, "tccfb", NULL),
OF_DEV_AUXDATA("telechips,nand-v8", TCC803X_PA_NFC,"tcc_nand", NULL),
OF_DEV_AUXDATA("telechips,tcc-ehci", TCC_PA_EHCI0, "tcc-ehci.0", NULL),
OF_DEV_AUXDATA("telechips,tcc-ehci", TCC_PA_EHCI1, "tcc-ehci.1", NULL),
OF_DEV_AUXDATA("telechips,tcc-ohci", TCC_PA_OHCI0, "tcc-ohci.0", NULL),
OF_DEV_AUXDATA("telechips,tcc-ohci", TCC_PA_OHCI1, "tcc-ohci.1", NULL),
OF_DEV_AUXDATA("telechips,dwc_otg", TCC_PA_DWC_OTG,"dwc_otg", NULL),
OF_DEV_AUXDATA("telechips,i2c", TCC803X_PA_I2C0, "i2c.0", NULL),
OF_DEV_AUXDATA("telechips,i2c", TCC803X_PA_I2C1, "i2c.1", NULL),
OF_DEV_AUXDATA("telechips,i2c", TCC803X_PA_I2C2, "i2c.2", NULL),
OF_DEV_AUXDATA("telechips,i2c", TCC803X_PA_I2C3, "i2c.3", NULL),
OF_DEV_AUXDATA("telechips,tcc-sdhc", TCC803X_PA_SDHC0, "tcc-sdhc.0", NULL),
OF_DEV_AUXDATA("telechips,tcc-sdhc", TCC803X_PA_SDHC1, "tcc-sdhc.1", NULL),
OF_DEV_AUXDATA("telechips,tcc-sdhc", TCC803X_PA_SDHC2, "tcc-sdhc.2", NULL),
OF_DEV_AUXDATA("telechips,wmixer_drv", 0, "wmixer0", NULL),
OF_DEV_AUXDATA("telechips,wmixer_drv", 1, "wmixer1", NULL),
OF_DEV_AUXDATA("telechips,scaler_drv", 1, "scaler1", NULL),
OF_DEV_AUXDATA("telechips,scaler_drv", 3, "scaler3", NULL),
#ifdef CONFIG_TCC_ATTACH
OF_DEV_AUXDATA("telechips,attach_drv", 0, "attach0", NULL),
OF_DEV_AUXDATA("telechips,attach_drv", 1, "attach1", NULL),
#endif
OF_DEV_AUXDATA("telechips,tcc_wdma", 0, "wdma", NULL),
OF_DEV_AUXDATA("telechips,tcc_overlay", 0, "overlay", NULL),
{},
};
const struct of_device_id of_default_bus_match_table[] = {
{ .compatible = "simple-bus", },
{ .compatible = "simple-mfd", },
{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
static void __init tcc803x_dt_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table,
tcc803x_auxdata_lookup, NULL);
}
of_platform_populate函数从设备树的根节点开始,为每个子节点是平台设备的节点创建平台设备。populate:填充,即根据设备树的设备节点参数填充平台设备。
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
//root为null,所以root= of_find_node_by_path("/")
root = root ? of_node_get(root) : of_find_node_by_path("/");
//遍历设备树的"/"节点的所有子节点
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
of_node_set_flag(root, OF_POPULATED_BUS);
return rc;
}
of_platform_bus_create函数:创建根节点下的某个子节点以及该子节点下的所有子节点对应的平台设备。
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) {
return 0;
}
//确保该设备节点之前没有被填充过
if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
return 0;
}
//查看lookup数组里有没有数组元素与该设备节点的compatile和内存空间首地址匹配,
//如果有匹配,返回该数组元素
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
//如果设备节点是在lookup数组里,是带有平台数据的,
//否则没有平台数据
platform_data = auxdata->platform_data;
}
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
//递归创建bus节点的子节点对应的平台设备
for_each_child_of_node(bus, child) {
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
//设置标志,表示该节点填充完
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc;
}
of_dev_lookup函数:
static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *lookup,
struct device_node *np)
{
const struct of_dev_auxdata *auxdata;
struct resource res;
int compatible = 0;
if (!lookup)
return NULL;
auxdata = lookup;
//查看lookup数组里有没有数组元素与该设备节点的compatile和内存空间首地址匹配,
//如果有匹配,返回该数组元素
for (; auxdata->compatible; auxdata++) {
//of_device_is_compatible函数返回0,表示没有匹配上,
//返回正整数,表示匹配上。
if (!of_device_is_compatible(np, auxdata->compatible))
continue;
compatible++;
if (!of_address_to_resource(np, 0, &res))
if (res.start != auxdata->phys_addr)
continue;
return auxdata;
}
if (!compatible)
return NULL;
/* Try compatible match if no phys_addr and name are specified */
auxdata = lookup;
for (; auxdata->compatible; auxdata++) {
if (!of_device_is_compatible(np, auxdata->compatible))
continue;
if (!auxdata->phys_addr && !auxdata->name) {
pr_debug("%pOF: compatible match\n", np);
return auxdata;
}
}
return NULL;
}
of_device_is_compatible函数:
int of_device_is_compatible(const struct device_node *device,
const char *compat)
{
unsigned long flags;
int res;
raw_spin_lock_irqsave(&devtree_lock, flags);
res = __of_device_is_compatible(device, compat, NULL, NULL);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return res;
}
__of_device_is_compatible函数:
static int __of_device_is_compatible(const struct device_node *device,
const char *compat, const char *type, const char *name)
{
struct property *prop;
const char *cp;
int index = 0, score = 0;
//compatible属性匹配优先级最高,所以对应的score分数最大,其次是type,再其次是name。
if (compat && compat[0]) {
prop = __of_find_property(device, "compatible", NULL);
//获取"compatible"节点属性的字符串值,一般只有一个字符串
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
//score:匹配度的相对分数,正整数,分数越高,越匹配
//如果是0,表示没有匹配上
score = INT_MAX/2 - (index << 2);
break;
}
}
if (!score)
return 0;
}
//一般type设置为NULL,就是不进行type匹配
if (type && type[0]) {
if (!device->type || of_node_cmp(type, device->type))
return 0;
score += 2;
}
//一般name设置为NULL,就是不进行name匹配
if (name && name[0]) {
if (!device->name || of_node_cmp(name, device->name))
return 0;
score++;
}
return score;
}
of_platform_device_create_pdata函数:
static struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct platform_device *dev;
//设备节点的status属性有,且不是"okay"或者"ok"状态,那么返回,不会创建平台设备。
if (!of_device_is_available(np) || of_node_test_and_set_flag(np, OF_POPULATED))
return NULL;
//平台设备对应的结构体的内存分配
dev = of_device_alloc(np, bus_id, parent);
//总线类型为平台总线类型
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
//添加设备
if (of_device_add(dev) != 0) {
......
}
return dev;
}
of_device_is_available函数:
bool of_device_is_available(const struct device_node *device)
{
unsigned long flags;
bool res;
raw_spin_lock_irqsave(&devtree_lock, flags);
res = __of_device_is_available(device);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return res;
}
__of_device_is_available函数:
static bool __of_device_is_available(const struct device_node *device)
{
const char *status;
int statlen;
if (!device)
return false;
//如果没有status属性,没问题
status = __of_get_property(device, "status", &statlen);
if (status == NULL)
return true;
//如果有status属性,且设置成"okay"或者"ok",没问题
if (statlen > 0) {
if (!strcmp(status, "okay") || !strcmp(status, "ok"))
return true;
}
//否则有问题
return false;
}