• 【Device Tree】Kernel中的gpio driver在DTS下是如何初始化的


    1. 前言

    在这篇文章仅仅是简单展示下一个gpio key driver是通过dts中定义的数据进行相关key的硬件初始化的,只是截图了部分相关的dts文件内容和驱动代码.

    使用的平台:
    MT8167
    Android O
    Kernel: 4.4

    2. DTS中的gpio_keys 节点说明

    gpio_keys {
                    compatible = "gpio-keys";
                    pinctrl-names = "default";
                    pinctrl-0 = <&gpio_keys_default>;
    
                    lens_cover {
                            label = "lens_cover";
                            gpios = <&pio 10 GPIO_ACTIVE_LOW>;
                            linux,code = <KEY_F1>;
                            linux,input-type = <EV_KEY>;
                    };
    
            };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    关键属性的解释:

    1. 在Device Tree的Spec中有对于compatible的描述:
    2.3.1. compatible
    Property name: compatible
    Value type: <stringlist>
    Description:
    The compatible property value consists of one or more strings that define the specific programming model for the device. This list of strings should be used by a client program for device driver selection. The property value consists of a concatenated list of null terminated strings, from most specific to most general. They allow a device to express its compatibility with a family of similar devices, potentially allowing a single device driver to match against several devices.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    简单理解: 在这个案例中的compatible 是用于对应的driver和gpio_keys 节点定义设备的匹配,具体的原理在后面的文章中会讲述。

    1. gpio_keys下的子节点lens_cover解释
    lens_cover {
                            label = "lens_cover";
                            # gpios这一属性定义了引脚和有效的电平,这在对应的驱动文件中会解析使用,进行gipo的初始化
                            gpios = <&pio 10 GPIO_ACTIVE_LOW>;    
                            # linux,code 这一属性定义的是按键触发时对应需要上报的应用层的key code                  
                            linux,code = <KEY_F1>;
                            # linux,input-type这一属性定义了lens_cover这一按键注册到input时的按键类型
                            linux,input-type = <EV_KEY>;
                    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    另外对于gpio_keys 这一节点 你可能还会有疑问? 为什么还会有一个子节点lens_cover。
    在这个案例中lens_cover要定义的是镜头盖的拨动开关(可以理解为一个按键),对于gpio_keys这一大类来说它下面可能会包含多种类型的按键,所以这里就将lens_cover定义为了gpio_keys的子节点。

    3. gpio drirver 加载对应的dts数据进行硬件初始化

    首先看下gpio_keys_probe中调用了gpio_keys_get_devtree_pdata进行对应device data的获取, gpio driver是如何识别到gpio_key driver所对应的dts 节点 在后续文章中会详细说明。

    // kernel-4.4/drivers/input/keyboard/gpio_keys.c
    static int gpio_keys_probe(struct platform_device *pdev)
    {
            struct device *dev = &pdev->dev;
            const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
            struct gpio_keys_drvdata *ddata;
            struct input_dev *input;
            size_t size;
            int i, error;
            int wakeup = 0;
    
            if (!pdata) {
                    /*
                    在gpio_keys调用gpio_keys_probe进行驱动的初始化时,首先需要获取到driver所对应的platform data,在未支持dts前 platform data是固化在代码中的,而在支持dts后是通过读取的对应dts中定义的device data进行相关pdata的赋值。
                    */
                    pdata = gpio_keys_get_devtree_pdata(dev);
                    if (IS_ERR(pdata))
                            return PTR_ERR(pdata);
            }
      ...
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在gpio_keys_get_devtree_pdata中会将加载到内存的dts数据转换为paltform_data, 对于系统是如何将dts转换为驱动可以解析的内容, 在后续文章中会详细说明。

    /*
     * Handlers for alternative sources of platform_data
     */
    
    #ifdef CONFIG_OF
    /*
     * Translate OpenFirmware node properties into platform_data
     */
    static struct gpio_keys_platform_data *
    gpio_keys_get_devtree_pdata(struct device *dev)
    {
            struct device_node *node, *pp;
            struct gpio_keys_platform_data *pdata;
            struct gpio_keys_button *button;
            int error;
            int nbuttons;
            int i;
            // 这里所赋值的就是dts中定义的gpio_keys 节点
            node = dev->of_node;
            if (!node)
                    return ERR_PTR(-ENODEV);
            //获取gpio_keys 下有多少个子节点,在这个案例只存在lens_cover 这一个设备
            nbuttons = of_get_child_count(node);
            if (nbuttons == 0)
                    return ERR_PTR(-ENODEV);
    
            pdata = devm_kzalloc(dev,
                                 sizeof(*pdata) + nbuttons * sizeof(*button),
                                 GFP_KERNEL);
            if (!pdata)
                    return ERR_PTR(-ENOMEM);
    
            pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
            pdata->nbuttons = nbuttons;
    
            pdata->rep = !!of_get_property(node, "autorepeat", NULL);
            i = 0;
            // 这里就是循环去读取子节点中的属性
            // 在这里就只是处理了lens_cover 
            for_each_child_of_node(node, pp) {
                    enum of_gpio_flags flags;
    
                    button = &pdata->buttons[i++];
    
                    button->gpio = of_get_gpio_flags(pp, 0, &flags);
                    if (button->gpio < 0) {
                            error = button->gpio;
                            if (error != -ENOENT) {
                                    if (error != -EPROBE_DEFER)
                                            dev_err(dev,
                                                    "Failed to get gpio flags, error: %d\n",
                                                    error);
                                    return ERR_PTR(error);
                            }
                    } else {
                            button->active_low = flags & OF_GPIO_ACTIVE_LOW;
                    }
    
                    button->irq = irq_of_parse_and_map(pp, 0);
    
                    if (!gpio_is_valid(button->gpio) && !button->irq) {
                            dev_err(dev, "Found button without gpios or irqs\n");
                            return ERR_PTR(-EINVAL);
                    }
                    // 这里对应到了lens_cover 中的linux,code = <KEY_F1>;
                    if (of_property_read_u32(pp, "linux,code", &button->code)) {
                            dev_err(dev, "Button without keycode: 0x%x\n",
                                    button->gpio);
                            return ERR_PTR(-EINVAL);
                    }
                    // 这里对应到了lens_cover 中的label = "lens_cover";
                    button->desc = of_get_property(pp, "label", NULL);
                    // 这里对应到了lens_cover 中的linux,input-type = <EV_KEY>;
                    if (of_property_read_u32(pp, "linux,input-type", &button->type))
                            button->type = EV_KEY;
    
                    button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
                                     /* legacy name */
                                     of_property_read_bool(pp, "gpio-key,wakeup");
    
                    button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
    
                    if (of_property_read_u32(pp, "debounce-interval",
                                             &button->debounce_interval))
                            button->debounce_interval = 5;
            }
            if (pdata->nbuttons == 0)
                    return ERR_PTR(-EINVAL);
    
            return pdata;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    通过上述的DTS中定义的节点(device node)与对应的驱动gpio_key的部分代码,可以更容易的理解:
    所谓的dts实质上只是一个定义device data的规范,然后对应的驱动基于这个规范去读取加载到内存中的device data进行一系列的初始化动作。

    对于Android的kernel是如何加载dts文件?driver是如何匹配到对应的device data的?在后续的文章中会详细的描述。

    4. 相关链接

    理解设备树基本概念

  • 相关阅读:
    Linux 安装subversion+mod_dav_svn,搭建subversion(svn)服务器
    【算法训练营】最近公共祖先+求最大连续bit数
    积分商城小程序的作用是什么
    opengl灯光基础:2.1 光照基础知识
    端到端智能音箱
    算法之神奇的斐波那契数列(Fibonacci sequence)
    C/C++微实践 - 发现圆周率
    Python if-for-while使用
    12.JVM
    面试官:你讲下如何设计支持千万级别的短链?
  • 原文地址:https://blog.csdn.net/love131452098/article/details/124904436