基于tiny4412的Linux内核移植 -- 设备树的展开

发布时间:2023-06-21  

平台简介

开发板:tiny4412ADK + S700 + 4GB Flash

要移植的内核版本:Linux-4.4.0 (支持device tree)

u-boot版本:友善之臂自带的 U-Boot 2010.12 (为支持uImage启动,做了少许改动)

busybox版本:busybox 1.25

交叉编译工具链: arm-none-linux-gnueabi-gcc

      (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))

摘要

    在Linux引入设备树之后,将原来写在代码中的大量的硬件信息全部移到了设备树中,然后在Linux启动的时候会解析设备树,利用解析到的硬件信息构造device,然后注册到相应的bus上,如果有对应的driver,则会调用driver的probe函数。那么这个过程是怎么进行的?Linux系统有各种device,如对于platform子系统来说有platform_device、对于I2C子系统有i2c_client、对于SPI子系统来说有spi_device,那么这些device是怎么跟设备树关联起来的呢?

   在分析的过程中参考了下面的几篇博文:

    http://www.wowotech.net/device_model/dt-code-analysis.html

       http://www.wowotech.net/comm/i2c_overview.html

    http://www.wowotech.net/comm/i2c_provider.html

这几篇博文讲的非常好,下面一篇是之前总结的:

         http://www.cnblogs.com/pengdonglin137/p/4495056.html

内核文档:

     Documentation/devicetree/booting-without-of.txt

     Documentation/devicetree/usage-model.txt

官方文档:

     Power_ePAPR_APPROVED_v1.1.pdf

正文

  设备树的populate过程大致有如下几个阶段(下文中“节点”与“device node”可以理解为一个意思):

一、根据设备树创建device node链表

start_kernel

    ---> setup_arch

            ---> unflatten_device_tree

在u-boot引导内核的时候,会将设备树在物理内存中的物理起始地址(存放在寄存器r2中)传递给Linux内核,然后Linux内核在函数unflatten_device_tree中会解析设备树镜像,并利用扫描到的信息创建由device node构成的链表,全局变量of_root指向链表的根节点,设备树的每个节点都会有一个struct device_node与之对应。

二、遍历device node链表,创建并注册platform_device

start_kernel

    ---> rest_init

            ---> kernel_init

                    ---> kernel_init_freeable

                            ---> do_basic_setup

                                    ---> do_initcalls

在do_initcalls函数中,kernel会依次执行各个initcall函数,在这个过程中,会调用 customize_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.

     */

    of_iommu_init();

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

这样就可调用到exynos_dt_machine_init:


static void __init exynos_dt_machine_init(void)

{

    ......

 

    of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

}

    在of_platform_populate中会调用of_platform_bus_create ---> of_platform_device_create_pdata,完成platform_device的创建和注册。那么Linux系统是怎么知道哪些device node要注册为platform_device,哪些是用于i2c_client,哪些是用于spi_device?不知道你有没有注意到调用of_platform_populate的时候给它传递了一个参数of_default_bus_match_table,它的定义如下:


const struct of_device_id of_default_bus_match_table[] = {

    { .compatible = "simple-bus", },

    { .compatible = "simple-mfd", },

#ifdef CONFIG_ARM_AMBA

    { .compatible = "arm,amba-bus", },

#endif /* CONFIG_ARM_AMBA */

    {} /* Empty terminated list */

};

    是这个意思:如果某个device node的compatible属性的值与数组of_default_bus_match_table中的任意一个元素的compatible的值match(但是对于compatible属性的值是arm,primecell的节点有些特殊,它是单独处理的),那么这个device node的child device node(device_node的child成员变量指向的是这个device node的子节点,也是一个链表)仍旧会被注册为platform_device。


of_platform_populate:


   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("/");  // 找到root device node

  10:     if (!root)

  11:         return -EINVAL;

  12:  

  13:     for_each_child_of_node(root, child) { // 遍历root device node的child device node

  14:         rc = of_platform_bus_create(child, matches, lookup, parent, true);

  15:         if (rc) {

  16:             of_node_put(child);

  17:             break;

  18:         }

  19:     }

  20:     of_node_set_flag(root, OF_POPULATED_BUS);

  21:  

  22:     of_node_put(root);

  23:     return rc;

  24: }

of_platform_bus_create :


   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))) { // 这样可以把chosen、aliases、memory等没有compatible属性的节点排除在外

  15:         pr_debug("%s() - skipping %s, no compatible propn",

  16:              __func__, bus->full_name);

  17:         return 0;

  18:     }

  19:  

  20:     auxdata = of_dev_lookup(lookup, bus);  // tiny4412给lookup传递的是NULL

  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); // 根据device node创建 platform_device并注册

  36:     if (!dev || !of_match_node(matches, bus)) // 判断是不是需要继续遍历这个device node下的child device node

  37:         return 0;

  38:  

  39:     for_each_child_of_node(bus, child) { // 遍历这个device node下的child device node,将child device node也注册为platform_device

  40:         pr_debug("   create child: %sn", 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: }

三、注册其他设备

I2C设备的注册

下面说一下i2c_client是如何注册的。先看下面一张图(来自蜗窝科技):

e37c4da1dcb2d9b5a190002b60b3923620160214140115

下面是从http://www.wowotech.net/comm/i2c_overview.html摘抄的一段话:

1)platform bus(/sys/bus/platform)是驱动工程师常见的bus,用于挂载和CPU通过系统总线连接的各类外设。在I2C framework中,I2C控制器直接从属于platform bus,我们在linux kernel中常说的I2C driver,都是指I2C controller driver,都是以platform driver的形式存在,当然,对应的控制器是platform device。

2)与此同时,kernel抽象出I2C bus(/sys/bus/i2c),用于挂载和I2C controller通过I2C总线连接的各个I2C slave device。

3)比较特殊的地方是,I2C core使用一个虚拟实体----I2C adapter,抽象I2C controller有关的功能(主要是数据的收发),I2C adapter也挂载在I2C bus上。

4)I2C adapter和I2C slave device都挂载在I2C bus上,就可以方便的进行Master(I2C adapter)和Slave之间的匹配操作,并通过I2C core提供的统一接口,访问I2C salve device,进行数据的收发。

    我们知道,i2c控制器在i2c驱动模型中被抽象为i2c_adapter,但是i2c控制器驱动实际上是在platform_bus上,所以i2c控制器对应的是platform_device,因此会在上面调用of_platform_populate时注册,然后i2c控制器驱动的probe函数会被调用。以tiny4412开发板为例,在drivers/i2c/busses/i2c-s3c2410.c的probe函数中调用注册adapter的函数接口:i2c_add_numbered_adapter ---> i2c_add_adapter ---> i2c_register_adapter ---> of_i2c_register_devices,在函数of_i2c_register_devices中会遍历这个adapter对应的device node的child device node,这些child device node对应的就是挂载i2c bus上的板级外设的硬件信息(这些板级外设使用I2C接口跟SOC通信),如 MMA7660。然后调用of_i2c_register_device,这个函数根据每个child device node的信息构造i2c_board_info,并调用i2c_new_device,在i2c_new_device中会创建并注册i2c_client,注册i2c_client的时候如果找到了对应的设备驱动程序(如 MMA7660的驱动程序),设备驱动程序的probe函数就会被调动。

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>