在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);
}