高通平台device tree生成platform device的过程(MSM8909)

在arch/arm/kernel/setup.c中,通过customize_machine函数中的machine_desc->init_machine();来调用高通平台的machine初始化代码:
static int __init customize_machine(void)
{
    /*
     * customizes platform devices, or adds new ones
     * On DT based machines, we fall back to populating the
     * machine from the device tree, if no callback is provided,
     * otherwise we would always need an init_machine callback.
     */
    if (machine_desc->init_machine)
        machine_desc->init_machine();
#ifdef CONFIG_OF
    else
        of_platform_populate(NULL, of_default_bus_match_table,
                    NULL, NULL);
#endif
    return 0;
}
arch_initcall(customize_machine);

在arch/arm/mach-msm/board-8909.c中:
DT_MACHINE_START(MSM8909_DT,
    "Qualcomm Technologies, Inc. MSM 8909 (Flattened Device Tree)")
    .map_io = msm8909_map_io,
    .init_machine = msm8909_init,
    .dt_compat = msm8909_dt_match,
    .reserve = msm8909_dt_reserve,
    .smp = &msm8916_smp_ops,
MACHINE_END

经过宏替换,最终变为:
static const struct machine_desc __mach_desc_MSM8909_DT __used __attribute__((__section__(".arch.info.init"))) = {
    .nr        = ~0,
    .name        = "Qualcomm Technologies, Inc. MSM 8909 (Flattened Device Tree)",
    .map_io = msm8909_map_io,
    .init_machine = msm8909_init,
    .dt_compat = msm8909_dt_match,
    .reserve = msm8909_dt_reserve,
    .smp = &msm8916_smp_ops,
}

所以,machine_desc->init_machine();调用的实际是msm8909_init函数:
static void __init msm8909_init(void)
{
    struct of_dev_auxdata *adata = msm8909_auxdata_lookup;

    /*
     * populate devices from DT first so smem probe will get called as part
     * of msm_smem_init.  socinfo_init needs smem support so call
     * msm_smem_init before it.
     */
    of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
    msm_smem_init();

    if (socinfo_init() < 0)
        pr_err("%s: socinfo_init() failed\n", __func__);

    msm8909_add_drivers();
}

通过of_platform_populate函数来生成platform device,具体调用过程: of_platform_populate->of_platform_bus_create->of_platform_device_create_pdata->of_device_add

以高通GPIO为例,在arch/arm/boot/dts/qcom/msm8909-pinctrl.dtsi中关于GPIO的resource描述:
compatible = "qcom,msm-tlmm-8916";
reg = <0x1000000 0x300000>;
在of_platform_device_create_pdata函数中,通过调用of_device_alloc函数来生成platform device,在of_device_alloc函数中,调用of_address_to_resource来解析GPIO的资源:
int of_address_to_resource(struct device_node *dev, int index,
               struct resource *r)
{
    const __be32    *addrp;
    u64        size;
    unsigned int    flags;
    const char    *name = NULL;

    addrp = of_get_address(dev, index, &size, &flags);
    if (addrp == NULL)
        return -EINVAL;

    /* Get optional "reg-names" property to add a name to a resource */
    of_property_read_string_index(dev, "reg-names",    index, &name);

    return __of_address_to_resource(dev, addrp, size, flags, name, r);
}


在of_get_address中,调用of_match_bus来匹配总线:
static struct of_bus *of_match_bus(struct device_node *np)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(of_busses); i++)
        if (!of_busses[i].match || of_busses[i].match(np))
            return &of_busses[i];
    BUG();
    return NULL;
}

最终会匹配到of_busses数组的最后一项,这里的addresses成员刚好是reg,和dtsi中的reg = <0x1000000 0x300000>;匹配。
static struct of_bus of_busses[] = {
    //删除了无关代码
    /* Default */
    {
        .name = "default",
        .addresses = "reg",
        .match = NULL,
        .count_cells = of_bus_default_count_cells,
        .map = of_bus_default_map,
        .translate = of_bus_default_translate,
        .get_flags = of_bus_default_get_flags,
    },
};

之后of_get_address会通过*flags = bus->get_flags(prop);来设置resource的标志位,实际上就是调用上述of_bus of_busses的get_flags成员指向的地址,即of_bus_default_get_flags,而函数直接返回IORESOURCE_MEM标志:
static unsigned int of_bus_default_get_flags(const __be32 *addr)
{
    return IORESOURCE_MEM;
}

of_get_address会通过prop = of_get_property(dev, bus->addresses, &psize);来返回const __be32 *prop;指针给addrp变量,最后通过调用__of_address_to_resource函数来设置resource的start和end地址以及flags标志:
static int __of_address_to_resource(struct device_node *dev,
        const __be32 *addrp, u64 size, unsigned int flags,
        const char *name, struct resource *r)
{
    u64 taddr;

    if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
        return -EINVAL;
    taddr = of_translate_address(dev, addrp);
    if (taddr == OF_BAD_ADDR)
        return -EINVAL;
    memset(r, 0, sizeof(struct resource));
    if (flags & IORESOURCE_IO) {
        unsigned long port;
        port = pci_address_to_pio(taddr);
        if (port == (unsigned long)-1)
            return -EINVAL;
        r->start = port;
        r->end = port + size - 1;
    } else {
         r->start = taddr;
        r->end = taddr + size - 1;

    }
    r->flags = flags;
    r->name = name ? name : dev->full_name;

    return 0;
}

platform device生成之后,就可以调用platform_get_resource(pdev, IORESOURCE_MEM, 0);来获得GPIO的基地址和长度,具体代码可以参考 drivers/pinctrl/pinctrl-msm-tlmm.c文件中的msm_tlmm_probe:
static int msm_tlmm_probe(struct platform_device *pdev)
{
    const struct of_device_id *match;
    struct msm_tlmm_desc *tlmm_desc;
    int irq, ret;
    struct resource *res;
    int i;
    const struct msm_pintype_data **pintype_data;
    struct device_node *node = pdev->dev.of_node;

    match = of_match_node(msm_tlmm_dt_match, node);
    if (IS_ERR(match))
        return PTR_ERR(match);
    else if (!match)
        return -ENODEV;
    tlmm_desc = devm_kzalloc(&pdev->dev, sizeof(*tlmm_desc), GFP_KERNEL);
    if (!tlmm_desc) {
        dev_err(&pdev->dev, "Alloction failed for tlmm desc\n");
        return -ENOMEM;
    }
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "cannot find IO resource\n");
        return -ENOENT;
    }
    tlmm_desc->base = devm_ioremap(&pdev->dev, res->start,
                            resource_size(res));

    if (IS_ERR(tlmm_desc->base))
        return PTR_ERR(tlmm_desc->base);
    tlmm_desc->irq = -EINVAL;
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res) {
        irq = res->start;
        ret = devm_request_irq(&pdev->dev, irq, msm_tlmm_handle_irq,
                            IRQF_TRIGGER_HIGH,
                            dev_name(&pdev->dev),
                            tlmm_desc);
        if (ret) {
            dev_err(&pdev->dev, "register for irq failed\n");
            return ret;
        }
        tlmm_desc->irq = irq;
    }
    pintype_data = (const struct msm_pintype_data **)match->data;
    for (i = 0; i < MSM_PINTYPE_MAX; i++)
        tlmm_pininfo[i].pintype_data = pintype_data[i];
    tlmm_desc->pintypes = tlmm_pininfo;
    tlmm_desc->num_pintypes = ARRAY_SIZE(tlmm_pininfo);
    return msm_pinctrl_probe(pdev, tlmm_desc);
}

你可能感兴趣的:(高通平台device tree生成platform device的过程(MSM8909))