内核版本:Linux-4.9

在3.x版本内核中platform_device不再静态定义,而是通过device tree来动态生成,例如(arch/arm/mach-s3c24xx/mach-sc2416-dt.c):

[cpp]  view plain  copy
  1. static void __init s3c2416_dt_machine_init(void)  
  2. {  
  3.     of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);  
  4.     s3c_pm_init();  
  5. }  
在s3c2416_dt_machine_init函数中调用of_platform_populate函数,而of_platform_populate函数是of子系统提供的函数,在drivers/of/platform.c中:
[cpp]  view plain  copy
  1. int of_platform_populate(struct device_node *root,  
  2.             const struct of_device_id *matches,  
  3.             const struct of_dev_auxdata *lookup,  
  4.             struct device *parent)  
  5. {  
  6.     struct device_node *child;  
  7.     int rc = 0;  
  8.   
  9.     root = root ? of_node_get(root) : of_find_node_by_path("/");  
  10.     if (!root)  
  11.         return -EINVAL;  
  12.   
  13.     for_each_child_of_node(root, child) {  
  14.         rc = of_platform_bus_create(child, matches, lookup, parent, true);  
  15.         if (rc)  
  16.             break;  
  17.     }  
  18.   
  19.     of_node_put(root);  
  20.     return rc;  
  21. }  
该函数前面的的注释已经说的很明白了,of_platform_populate函数根据device tree生成platform_device。该函数同of_platform_bus_probe函数有点类似,在新的board中最好使用of_platform_populate来代替of_platform_bus_probe函数。

如果传递进来的参数root为NULL,那么需要通过of_find_node_by_path函数找到device tree中的根节点。

得到根节点之后呢,就需要通过这个根节点来遍历device tree中的节点了。得到一个子节点之后,调用of_platform_bus_create函数:
[cpp]  view plain  copy
  1. static int of_platform_bus_create(struct device_node *bus,  
  2.                   const struct of_device_id *matches,  
  3.                   const struct of_dev_auxdata *lookup,  
  4.                   struct device *parent, bool strict)  
  5. {  
  6.     const struct of_dev_auxdata *auxdata;  
  7.     struct device_node *child;  
  8.     struct platform_device *dev;  
  9.     const char *bus_id = NULL;  
  10.     void *platform_data = NULL;  
  11.     int rc = 0;  
  12.   
  13.     /* Make sure it has a compatible property */  
  14.     if (strict && (!of_get_property(bus, "compatible", NULL))) {  
  15.         pr_debug("%s() - skipping %s, no compatible prop\n",  
  16.              __func__, bus->full_name);  
  17.         return 0;  
  18.     }  
  19.   
  20.     auxdata = of_dev_lookup(lookup, bus);  
  21.     if (auxdata) {  
  22.         bus_id = auxdata->name;  
  23.         platform_data = auxdata->platform_data;  
  24.     }  
  25.   
  26.     if (of_device_is_compatible(bus, "arm,primecell")) {  
  27.         /* 
  28.          * Don't return an error here to keep compatibility with older 
  29.          * device tree files. 
  30.          */  
  31.         of_amba_device_create(bus, bus_id, platform_data, parent);  
  32.         return 0;  
  33.     }  
  34.   
  35.     dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);  
  36.     if (!dev || !of_match_node(matches, bus))  
  37.         return 0;  
  38.   
  39.     for_each_child_of_node(bus, child) {  
  40.         pr_debug("   create child: %s\n", child->full_name);  
  41.         rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);  
  42.         if (rc) {  
  43.             of_node_put(child);  
  44.             break;  
  45.         }  
  46.     }  
  47.     of_node_set_flag(bus, OF_POPULATED_BUS);  
  48.     return rc;  
  49. }  
在of_platform_bus_create函数中,首先是需要确定节点是否有"compatible"属性,如果没有"compatible"属性,则直接返回,即不会创建platform设备的。

所幸在mach-sc2416-dt.c中传递进来的lookup参数为NULL,所以of_dev_lookup这部分也就不去看了。

如果"compatible"属性值有"arm,primecell",则会调用of_amba_device_create函数去创建amba_device,这个设备暂时也不知道是一个什么设备,那么这里还是先忽略。

继续,调用of_platform_device_create_pdata函数:
[cpp]  view plain  copy
  1. static struct platform_device *of_platform_device_create_pdata(  
  2.                     struct device_node *np,  
  3.                     const char *bus_id,  
  4.                     void *platform_data,  
  5.                     struct device *parent)  
  6. {  
  7.     struct platform_device *dev;  
  8.   
  9.     if (!of_device_is_available(np) ||  
  10.         of_node_test_and_set_flag(np, OF_POPULATED))  
  11.         return NULL;  
  12.   
  13.     dev = of_device_alloc(np, bus_id, parent);  
  14.     if (!dev)  
  15.         goto err_clear_flag;  
  16.   
  17.     of_dma_configure(&dev->dev);  
  18.     dev->dev.bus = &platform_bus_type;  
  19.     dev->dev.platform_data = platform_data;  
  20.   
  21.     /* We do not fill the DMA ops for platform devices by default. 
  22.      * This is currently the responsibility of the platform code 
  23.      * to do such, possibly using a device notifier 
  24.      */  
  25.   
  26.     if (of_device_add(dev) != 0) {  
  27.         platform_device_put(dev);  
  28.         goto err_clear_flag;  
  29.     }  
  30.   
  31.     return dev;  
  32.   
  33. err_clear_flag:  
  34.     of_node_clear_flag(np, OF_POPULATED);  
  35.     return NULL;  
  36. }  
可以看到,这个函数才是真正创建platform_device的。

首先调用of_device_is_available函数,这个函数主要是用于检测"status"属性的,如果没有"status"属性,那还好说直接返回true。如果有"status"属性,而它的值又不是"okay"或"ok",那么不好意思,返回false,否则还是返回true。所以"status"属性就是用来检测是否可用(有点拗口,其实就是用来确认是否需要创建platform设备)。

"status"属性检测完毕了,则要调用of_device_alloc函数来为platform_device分配内存了。
[cpp]  view plain  copy
  1. struct platform_device *of_device_alloc(struct device_node *np,  
  2.                   const char *bus_id,  
  3.                   struct device *parent)  
  4. {  
  5.     struct platform_device *dev;  
  6.     int rc, i, num_reg = 0, num_irq;  
  7.     struct resource *res, temp_res;  
  8.   
  9.     dev = platform_device_alloc("", -1);  
  10.     if (!dev)  
  11.         return NULL;  
  12.   
  13.     /* count the io and irq resources */  
  14.     while (of_address_to_resource(np, num_reg, &temp_res) == 0)  
  15.         num_reg++;  
  16.     num_irq = of_irq_count(np);  
  17.   
  18.     /* Populate the resource table */  
  19.     if (num_irq || num_reg) {  
  20.         res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);  
  21.         if (!res) {  
  22.             platform_device_put(dev);  
  23.             return NULL;  
  24.         }  
  25.   
  26.         dev->num_resources = num_reg + num_irq;  
  27.         dev->resource = res;  
  28.         for (i = 0; i < num_reg; i++, res++) {  
  29.             rc = of_address_to_resource(np, i, res);  
  30.             WARN_ON(rc);  
  31.         }  
  32.         if (of_irq_to_resource_table(np, res, num_irq) != num_irq)  
  33.             pr_debug("not all legacy IRQ resources mapped for %s\n",  
  34.                  np->name);  
  35.     }  
  36.   
  37.     dev->dev.of_node = of_node_get(np);  
  38.     dev->dev.parent = parent;  
  39.   
  40.     if (bus_id)  
  41.         dev_set_name(&dev->dev, "%s", bus_id);  
  42.     else  
  43.         of_device_make_bus_id(&dev->dev);  
  44.   
  45.     return dev;  
  46. }  
调用platform中的platform_device_alloc函数来分配内存。

内存申请了之后,还会对platform_device做一些初始化,例如IO、中断资源等等。首先是调用of_address_to_resource和of_irq_count去计算io和中断资源的个数(有注释说明)。
[cpp]  view plain  copy
  1. int of_address_to_resource(struct device_node *dev, int index,  
  2.                struct resource *r)  
  3. {  
  4.     const __be32    *addrp;  
  5.     u64     size;  
  6.     unsigned int    flags;  
  7.     const char  *name = NULL;  
  8.   
  9.     addrp = of_get_address(dev, index, &size, &flags);  
  10.     if (addrp == NULL)  
  11.         return -EINVAL;  
  12.   
  13.     /* Get optional "reg-names" property to add a name to a resource */  
  14.     of_property_read_string_index(dev, "reg-names", index, &name);  
  15.   
  16.     return __of_address_to_resource(dev, addrp, size, flags, name, r);  
  17. }  
首先调用of_get_address获取地址信息。
[cpp]  view plain  copy
  1. const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,  
  2.             unsigned int *flags)  
  3. {  
  4.     const __be32 *prop;  
  5.     unsigned int psize;  
  6.     struct device_node *parent;  
  7.     struct of_bus *bus;  
  8.     int onesize, i, na, ns;  
  9.   
  10.     /* Get parent & match bus type */  
  11.     parent = of_get_parent(dev);  
  12.     if (parent == NULL)  
  13.         return NULL;  
  14.     bus = of_match_bus(parent);  
  15.     bus->count_cells(dev, &na, &ns);  
  16.     of_node_put(parent);  
  17.     if (!OF_CHECK_ADDR_COUNT(na))  
  18.         return NULL;  
  19.   
  20.     /* Get "reg" or "assigned-addresses" property */  
  21.     prop = of_get_property(dev, bus->addresses, &psize);  
  22.     if (prop == NULL)  
  23.         return NULL;  
  24.     psize /= 4;  
  25.   
  26.     onesize = na + ns;  
  27.     for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)  
  28.         if (i == index) {  
  29.             if (size)  
  30.                 *size = of_read_number(prop + na, ns);  
  31.             if (flags)  
  32.                 *flags = bus->get_flags(prop);  
  33.             return prop;  
  34.         }  
  35.     return NULL;  
  36. }  
首先是找到它的parent,然后根据parent去找bus。
[cpp]  view plain  copy
  1. static struct of_bus *of_match_bus(struct device_node *np)  
  2. {  
  3.     int i;  
  4.   
  5.     for (i = 0; i < ARRAY_SIZE(of_busses); i++)  
  6.         if (!of_busses[i].match || of_busses[i].match(np))  
  7.             return &of_busses[i];  
  8.     BUG();  
  9.     return NULL;  
  10. }  
of_busses定义如下:
[cpp]  view plain  copy
  1. static struct of_bus of_busses[] = {  
  2. #ifdef CONFIG_OF_ADDRESS_PCI  
  3.     /* PCI */  
  4.     {  
  5.         .name = "pci",  
  6.         .addresses = "assigned-addresses",  
  7.         .match = of_bus_pci_match,  
  8.         .count_cells = of_bus_pci_count_cells,  
  9.         .map = of_bus_pci_map,  
  10.         .translate = of_bus_pci_translate,  
  11.         .get_flags = of_bus_pci_get_flags,  
  12.     },  
  13. #endif /* CONFIG_OF_ADDRESS_PCI */  
  14.     /* ISA */  
  15.     {  
  16.         .name = "isa",  
  17.         .addresses = "reg",  
  18.         .match = of_bus_isa_match,  
  19.         .count_cells = of_bus_isa_count_cells,  
  20.         .map = of_bus_isa_map,  
  21.         .translate = of_bus_isa_translate,  
  22.         .get_flags = of_bus_isa_get_flags,  
  23.     },  
  24.     /* Default */  
  25.     {  
  26.         .name = "default",  
  27.         .addresses = "reg",  
  28.         .match = NULL,  
  29.         .count_cells = of_bus_default_count_cells,  
  30.         .map = of_bus_default_map,  
  31.         .translate = of_bus_default_translate,  
  32.         .get_flags = of_bus_default_get_flags,  
  33.     },  
  34. };  
在of_match_bus函数中,如果前面的bus不匹配,则使用默认的"default" bus,注意它的addresses字段为"reg"。

回到of_get_address函数中,调用of_get_property函数去读取哪个属性呢,就是前面的addresses值的属性,即reg属性,所以reg属性就是用来定义io地址地址信息的。而io地址的长度是通过of_get_address中的of_read_number去读取完成的,最后返回这个io地址。

回到of_address_to_resource函数中,在得到这个io地址之后,调用__of_address_to_resource函数将io地址转换成struct resource资源信息。

然后是中断资源。
[cpp]  view plain  copy
  1. int of_irq_count(struct device_node *dev)  
  2. {  
  3.     struct of_phandle_args irq;  
  4.     int nr = 0;  
  5.   
  6.     while (of_irq_parse_one(dev, nr, &irq) == 0)  
  7.         nr++;  
  8.   
  9.     return nr;  
  10. }  
  11.   
  12. int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)  
  13. {  
  14.     struct device_node *p;  
  15.     const __be32 *intspec, *tmp, *addr;  
  16.     u32 intsize, intlen;  
  17.     int i, res = -EINVAL;  
  18.   
  19.     pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);  
  20.   
  21.     /* OldWorld mac stuff is "special", handle out of line */  
  22.     if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)  
  23.         return of_irq_parse_oldworld(device, index, out_irq);  
  24.   
  25.     /* Get the reg property (if any) */  
  26.     addr = of_get_property(device, "reg", NULL);  
  27.   
  28.     /* Try the new-style interrupts-extended first */  
  29.     res = of_parse_phandle_with_args(device, "interrupts-extended",  
  30.                     "#interrupt-cells", index, out_irq);  
  31.     if (!res)  
  32.         return of_irq_parse_raw(addr, out_irq);  
  33.   
  34.     /* Get the interrupts property */  
  35.     intspec = of_get_property(device, "interrupts", &intlen);  
  36.     if (intspec == NULL)  
  37.         return -EINVAL;  
  38.   
  39.     intlen /= sizeof(*intspec);  
  40.   
  41.     pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);  
  42.   
  43.     /* Look for the interrupt parent. */  
  44.     p = of_irq_find_parent(device);  
  45.     if (p == NULL)  
  46.         return -EINVAL;  
  47.   
  48.     /* Get size of interrupt specifier */  
  49.     tmp = of_get_property(p, "#interrupt-cells", NULL);  
  50.     if (tmp == NULL)  
  51.         goto out;  
  52.     intsize = be32_to_cpu(*tmp);  
  53.   
  54.     pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);  
  55.   
  56.     /* Check index */  
  57.     if ((index + 1) * intsize > intlen)  
  58.         goto out;  
  59.   
  60.     /* Copy intspec into irq structure */  
  61.     intspec += index * intsize;  
  62.     out_irq->np = p;  
  63.     out_irq->args_count = intsize;  
  64.     for (i = 0; i < intsize; i++)  
  65.         out_irq->args[i] = be32_to_cpup(intspec++);  
  66.   
  67.     /* Check if there are any interrupt-map translations to process */  
  68.     res = of_irq_parse_raw(addr, out_irq);  
  69.  out:  
  70.     of_node_put(p);  
  71.     return res;  
  72. }  
我们看在of_irq_parse_one函数中,是查找的"interrupts"属性值。

回到of_device_alloc函数,还是通过前面的of_address_to_resource函数将io地址资源赋值给平台设备,通过of_irq_to_resource_table函数将中断号转换成中断资源信息并赋值给平台设备。
[cpp]  view plain  copy
  1. int of_irq_to_resource_table(struct device_node *dev, struct resource *res,  
  2.         int nr_irqs)  
  3. {  
  4.     int i;  
  5.   
  6.     for (i = 0; i < nr_irqs; i++, res++)  
  7.         if (!of_irq_to_resource(dev, i, res))  
  8.             break;  
  9.   
  10.     return i;  
  11. }  
  12.   
  13. int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)  
  14. {  
  15.     int irq = irq_of_parse_and_map(dev, index);  
  16.   
  17.     /* Only dereference the resource if both the 
  18.      * resource and the irq are valid. */  
  19.     if (r && irq) {  
  20.         const char *name = NULL;  
  21.   
  22.         memset(r, 0, sizeof(*r));  
  23.         /* 
  24.          * Get optional "interrupt-names" property to add a name 
  25.          * to the resource. 
  26.          */  
  27.         of_property_read_string_index(dev, "interrupt-names", index,  
  28.                           &name);  
  29.   
  30.         r->start = r->end = irq;  
  31.         r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));  
  32.         r->name = name ? name : of_node_full_name(dev);  
  33.     }  
  34.   
  35.     return irq;  
  36. }  
我们可以看出在of_device_alloc函数中除了为平台设备分配内存之外,还为平台设备找到了io地址资源和中断资源。

回到of_platform_device_create_pdata函数中,平台设备已经申请好了,然后对平台设备继续进行赋值操作,例如平台设备的总线赋值为平台总线,平台设备的私有数据赋值为platform_data,最后调用of_device_add函数将平台设备注册到内核中。

总结,涉及到的属性有:
"compatible"	必须
"status"	可选属性
"reg"		io资源
"interrupts"	中断资源
Logo

更多推荐