【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. 在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.

简单理解: 在这个案例中的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>;
                };

另外对于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);
        }
  ...
  }

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

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

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

4. 相关链接

理解设备树基本概念

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值