gpio子系统和pinctrl子系统

转自https://blog.csdn.net/shiyongyue/article/details/75103470

前言

随着内核的发展,linux驱动框架在不断的变化。很早很早以前,出现了gpio子系统,后来又出现了pinctrl子系统。在网上很难看到一篇讲解这类子系统的文章。就拿gpio操作来说吧,很多时候都是简单的调用gpio子系统提供的api,然后根据sdk说明文档写明的gpio号传参数,至于里面的工作过程对于驱动工程师而言就像个黑盒子。当我们自己设计的板子和demo板有很大变动时,问题就出现了。首先遇到的是怎么配置pin(是基于设备树还是不基于设备树,基于设备树的话,怎么修改设备树关于pinctrl部分的内容,里面各个字段什么意思,怎么改),然后是在哪里配置pin(内核部分有哪些需要相应修改,还是不需要一点修改呢),接着就是怎么调试等等。我想只有清楚了尽量多的gpio子系统和pinctrl子系统细节,才会更快更好的完成这些工作。有些平台的实现没有使用内核提供的pinctrl子系统,而是继续采用在内核提供pinctrl子系统前自己实现的那套机制来pinmux操作,如omap,有些平台则基于pinctrl子系统来实现pinmux、pinconf的控制。本文以gpio子系统为入口慢慢深入,最后分析pinctrl子系统。

gpio子系统

gpio子系统帮助我们管理整个系统gpio的使用情况,同时通过sys文件系统导出了调试信息和应用层控制接口。它内部实现主要提供了两类接口,一类给bsp工程师,用于注册gpio chip(也就是所谓的gpio控制器驱动),另一部分给驱动工程师使用,为驱动工程师屏蔽了不同gpio chip之间的区别,驱动工程师调用的api的最终操作流程会导向gpio对应的gpio chip的控制代码,也就是bsp的代码。

gpio子系统核心实现分析
  gpio子系统的内容在drivers/gpio文件夹下,主要文件有:
    devres.c
    gpiolib.c
    gpiolib-of.c
    gpiolib-acpi.c
    gpio-xxx.c
  devres.c是针对gpio api增加的devres机制的支持,devres机制讲解请参考另一篇博文,gpiolib.c是gpio子系统的核心实现,gpiolib-of.c是对设备树的支持,gpiolib-acpi.c和acpi相关,不分析(acpi还未深入了解),最后情景分析的时候,会找一个平台的gpio-xxx.c来分析。
  从驱动工程师使用的api开始分析吧!也分两代,legacy的api主要会用到的接口有(现在推荐采用新的,基于描述符的api):
gpio_request、gpio_free
gpio_direction_input、gpio_direction_output
gpio_to_irq
gpio_export

一般的流程:

//请求一个/一组gpio
gpio_request/devm_gpio_request、gpio_request_one/devm_gpio_request_one、gpio_request_array   ---------<1>  
...
//设置gpio方向为输入/输出
gpio_direction_input或者gpio_direction_output                     ---------<2>  
...
//将该gpio通过sys文件系统导出,应用层可以通过文件操作gpio
gpio_export                                                                                                             ---------<3>  
...
//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平
gpio_get_value、gpio_set_value                                                                           ---------<4>  
...
//将gpio转为对应的irq,然后注册该irq的中断handler
request_irq(gpio_to_irq(gpio_num)...)                                                                   ---------<5>  
...
//释放请求的一个或者一组gpio
gpio_free/devm_gpio_free、gpio_free_array                                                       ---------<6>  
...

下面一个个来分析吧!
  <1> 以gpio_request为例,gpio_request_one、gpio_request_array是它的扩展,devm_为前缀的是gpio devres机制的实现。
int gpio_request(unsigned gpio, const char *label) 参数为gpio号和为该gpio指定的表签名,具体gpio号是多少,可以通过sdk开发包说明文档查看,或者查看设备树文件,再或者基于bank数量推算(当然,这样可能不准),实在没办法的话,瞄一眼gpio chip驱动的代码吧!gpio_request主要做了以下动作:
  1. 检查是否已经被申请,没有的话,标记为已申请
  2. 填充label到该pin数据结构,用于debug
  3. 如果chip driver提供了request回调,调用它
  4. 如果chip driver提供了get_direction回调,调用它,通过它更新pin数据结构,标明gpio方向
  gpio_request_one多一个flags参数,通过该参数,可以指定GPIOF_OPEN_DRAIN、GPIOF_OPEN_SOURCE、GPIOF_DIR_IN、GPIOF_EXPORT等标志,如果指定了GPIOF_DIR_IN,那么后面就不需要自己再额外调用gpio_direction_input或者gpio_direction_output了,如果指定了GPIOF_EXPORT,后面就不需要自己调用gpio_export了。
  gpio_request_array是对gpio_request_one的封装,用于处理同时申请多个gpio的情形。
  
  <2> gpio_direction_input或者gpio_direction_output用来设置该gpio为输入还是输出,它们主要是回调gpio chip driver提供的direction_input或者direction_output来设置该gpio寄存器为输入、输出。
  
  <3> gpio_export主要用于调试,它会将该gpio的信息通过sys文件系统导出,这样应用层可以直接查看状态、设置状态等。
  
  <4> gpio_get_value或者gpio_set_value和input、output类似,如果为输入,获取该gpio的值,如果为输出,设置该gpio的值,内部也是调用gpio chip driver提供的get、set。
  <5> gpio_to_irq用于获取该gpio对应的中断号,这个需要设备树里的该gpio节点描述使用哪个中断号(并不是所有的gpio都可以触发中断的)。它里面的实现就是回调gpio chip driver提供的to_irq。

<6> gpio_free就不用说了啦,gpio_request的逆操作。
  
要使用以上接口,需要#include <linux/gpiolib.h> ,且还有一组用于允许睡眠场景的api没有给出,更多相关的说明可以参考Documentation/gpio/gpio-legacy.txt

上面的分析没有深入的代码层,之所以没有深入分析,是因为打算放到后面分析基于描述符api时啦!其实,legacy gpio 大部分api就是基于描述符api来实现的。最新的,基于描述符一般的流程:

...
//请求第一个/指定某一个gpio desc,该返回值用于后面的操作
gpiod_get/devm_gpiod_get、gpiod_get_index/devm_gpiod_get_index   ---------<1>  
...
//设置gpio方向为输入/输出
gpiod_direction_input或者gpiod_get_direction                 ---------<2>  
...
//将该gpio通过sys文件系统导出,应用层可以通过文件操作gpio
gpiod_export                                                ---------<3>  
...
//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平
gpiod_get_value或者gpiod_set_value                           ---------<4>  
...
//将gpio转为对应的irq,然后注册该irq的中断handler
request_irq(gpiod_to_irq(gpio_desc)...)                     ---------<5>  
...
//释放请求的一个或者一组gpio
gpiod_put/devm_gpiod_put                                    ---------<6>  
...

还是下面一个个来分析吧!
  <1> gpiod_get内部的处理和gpio_request一样,不过输入参数变为char *con_id,这个需要从设备树文件里查看到,除此之外,我们还可以在设备树文件里添加参数(GPIO_ACTIVE_LOW、GPIO_OPEN_DRAIN、GPIO_OPEN_SOURCE)来触发该接口内部设置gpio,具体的参数格式和具体的gpio chip driver有关,一般可以在/Documentation/devicetree/bindings/gpio里找到对应平台的。举个例子:

        row-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH     /* Bank1, pin25 */          
                 &gpio1 26 GPIO_ACTIVE_HIGH       /* Bank1, pin26 */              
                 &gpio1 27 GPIO_ACTIVE_HIGH>;     /* Bank1, pin27 */ 

struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) 对应上面的设备树描述,con_id就是row了,获取的也会是第一个设置,即gpio1 25 GPIO_ACTIVE_HIGH,如果想获取第二个设置,我们得通过gpiod_get_index,并将输入参数idx设置为1。

<2> gpiod_direction_input和gpio_direction_input功能一样,实际上gpio_direction_input仅仅简单封装了gpiod_direction_input,不再描述。
  
  <3> gpiod_export 同上
  
  <4> gpiod_get_value 同上
  
  <5> gpiod_to_irq 同上
  
  <6> gpiod_put 同上

要使用以上接口,需要#include ,且还有一组用于允许睡眠场景的api没有给出,更多相关的说明可以参考Documentation/gpio/consumer.txt

下面简单分析下gpio子系统内部实现:
  1> gpio chip driver的初始化
gpio子系统提供了两层接口,一层给上层驱动工程师调用,一层给下层bsp工程师调用。上层使用前,当然先得bsp工程师完成对应的动作。先看一张网上的截图:
gpio子系统bsp工程师通过gpiochip_add将gpio chip添加到gpio子系统中,下面就分析它:

int gpiochip_add(struct gpio_chip *chip)  
{
    unsigned long   flags;
    int     status = 0;
    unsigned    id;
    int     base = chip->base;
 
    //如果指定了base,也就是指定了启示gpio号,需要校验下chip的所有gpio是否有效
    //这里会用到ARCH_NR_GPIOS宏,它可以在配置的时候,通过CONFIG_ARCH_NR_GPIO修改,
    //否则采用默认值256
    if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
            && base >= 0) {
        status = -EINVAL;
        goto fail;
    }
 
    spin_lock_irqsave(&gpio_lock, flags);
 
    //如果没有指定base,那么需要基于该chip的gpio数量在系统支持的gpio范围里找一段区间给该chip
    if (base < 0) {
        base = gpiochip_find_base(chip->ngpio);
        if (base < 0) {
            status = base;
            goto unlock;
        }
        chip->base = base;
    }
 
    //到这里的时候,说明一切正常,把它加入到全局的gpiochip链表中去吧,注意,加入的时候会基于base排序
    //这也保证了gpiochip_find_base的实现
    status = gpiochip_add_to_list(chip);
 
    //如果加入成功,最后一步就是初始化该chip对应的那些gpio了
    if (status == 0) {
        chip->desc = &gpio_desc[chip->base];
 
        for (id = 0; id < chip->ngpio; id++) {
            struct gpio_desc *desc = &chip->desc[id];
            //将该chip对应的那些gpio对应的数据结构desc初始化,指向拥有它的chip
            desc->chip = chip;
 
            /* REVISIT:  most hardware initializes GPIOs as
             * inputs (often with pullups enabled) so power
             * usage is minimized.  Linux code should set the
             * gpio direction first thing; but until it does,
             * and in case chip->get_direction is not set,
             * we may expose the wrong direction in sysfs.
             */
            //如果chip driver没有指定chip->direction_input,意味着不是输入,那就设置为输出咯
            desc->flags = !chip->direction_input
                ? (1 << FLAG_IS_OUT)
                : 0;
        }
    }
 
    spin_unlock_irqrestore(&gpio_lock, flags);
 
#ifdef CONFIG_PINCTRL
    //这里在配置了pinctrl的时候,会初始化它,后面会用到
    INIT_LIST_HEAD(&chip->pin_ranges);
#endif
 
    //初始化设备树相关的信息,后面会详细讲一下这部分
    of_gpiochip_add(chip);
    //acpi方式的先忽略吧
    acpi_gpiochip_add(chip);
 
    if (status)
        goto fail;
 
    //将该gpiochip导出到sys,用于调试和应用层直接操作
    status = gpiochip_export(chip);
    if (status)
        goto fail;
 
    pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
        chip->base, chip->base + chip->ngpio - 1,
        chip->label ? : "generic");
 
    return 0;
 
unlock:  
    spin_unlock_irqrestore(&gpio_lock, flags);
fail:  
    /* failures here can mean systems won't boot... */
    pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
        chip->base, chip->base + chip->ngpio - 1,
        chip->label ? : "generic");
    return status;
}

还是以zynq平台为例,看看输入参数struct gpio_chip *chip的初始化过程:

chip->label = "zynq_gpio";  
chip->owner = THIS_MODULE;  
chip->dev = &pdev->dev;  
chip->get = zynq_gpio_get_value;  
chip->set = zynq_gpio_set_value;  
chip->request = zynq_gpio_request;  
chip->free = zynq_gpio_free;  
chip->direction_input = zynq_gpio_dir_in;  
chip->direction_output = zynq_gpio_dir_out;  
chip->to_irq = zynq_gpio_to_irq;  
chip->dbg_show = NULL;  
chip->base = 0;        /* default pin base */  
chip->ngpio = ZYNQ_GPIO_NR_GPIOS;  
chip->can_sleep = 0;  

get、set、request、free、direction_input、direction_output、to_irq这几个回调应该很清楚了,也知道是哪几个接口调用的它们了吧,另外几个关键的参数base和ngpio在上面的gpiochip_add里应该也已经清楚了它们的重要作用了吧!

of_gpiochip_add会处理gpio chip设备树相关的东西:

void of_gpiochip_add(struct gpio_chip *chip)  
{
    if ((!chip->of_node) && (chip->dev))
        chip->of_node = chip->dev->of_node;
 
    if (!chip->of_node)
        return;
 
    //如果没有指定of_xlate,那给一个默认的吧!of_xlate用于解析设备树里gpio属性
    //不同的soc可能需要不同的解析方法,但是如果没有什么特别,那就用默认的解析吧
    //当前设备树的节点里支持两种格式的属性,一种是xxx-gpios,另一种就直接是gpios
    //如前文中用到的row-gpios就是一种
    if (!chip->of_xlate) {
        chip->of_gpio_n_cells = 2;
        chip->of_xlate = of_gpio_simple_xlate;
    }
 
    //这一步就是与pinctrl子系统打交道啦!后面会简单分析下它都做了什么 
    //等到讲pinctrl子系统的时候就更加清楚了
    of_gpiochip_add_pin_range(chip);
    //增加该节点引用计数
    of_node_get(chip->of_node);
}

关于of_gpiochip_add_pin_range,看起来很复杂,其实也就那样啦_

static void of_gpiochip_add_pin_range(struct gpio_chip *chip)  
{
    struct device_node *np = chip->of_node;
    struct of_phandle_args pinspec;
    struct pinctrl_dev *pctldev;
    int index = 0, ret;
    const char *name;
    static const char group_names_propname[] = "gpio-ranges-group-names";
    struct property *group_names;
 
    if (!np)
        return;
 
    //查找该gpiochip里的gpio-ranges-group-names属性
    group_names = of_find_property(np, group_names_propname, NULL);
 
    for (;; index++) {
        //提取该gpiochip设备树信息里的gpio-ranges属性,按3个字段为一组解析,解析后
        //放到pinspec中,这个gpio-ranges可能存在多组,因此用index来控制,一组一组来处理
        //举个例子:
        //gpio0: gpio@ffc40000 {                                                      
        //    compatible = "renesas,gpio-r8a7778", "renesas,gpio-rcar";               
        //    reg = <0xffc40000 0x2c>;                                                
        //    interrupt-parent = <&gic>;                                              
        //    interrupts = <0 103 IRQ_TYPE_LEVEL_HIGH>;                               
        //    #gpio-cells = <2>;                                                      
        //    gpio-controller;                                                        
        //    gpio-ranges = <&pfc 0 0 32>;                                            
        //    #interrupt-cells = <2>;                                                 
        //    interrupt-controller;                                                   
        //}; 
        //gpio-ranges属性是gpio子系统规定的属性,更详细的信息参考                
        //Documentation/devicetree/bindings/gpio/gpio.txt        
        ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3,
                index, &pinspec);
        if (ret)
            break;
 
        //获取到pinspec.np(也就是pinctrl对应的节点)对应的pctldev
        //注意,这个时候的gpiochip属于pinctrl的一个client
        pctldev = of_pinctrl_get(pinspec.np);
        if (!pctldev)
            break;
 
        //这部分的解析也是Documentation/devicetree/bindings/gpio/gpio.txt里有详细
        //说明的,如果最后一个参数为0,表示代表一个gpio group,否则,第一个参数意思是gpio号起始值
        //第二个参数意思是与之对应的pin号的起始值,最后一个参数表示连续多少各
        if (pinspec.args[2]) {
            if (group_names) {//这里仅仅是校验,如果不是表示gpio group,那么对应的
                //gpio-ranges-group-names属性里对应的字段应该为""
                ret = of_property_read_string_index(np,
                        group_names_propname,
                        index, &name);
                if (strlen(name)) {
                    pr_err("%s: Group name of numeric GPIO ranges must be the empty string.\n",
                        np->full_name);
                    break;
                }
            }
            /* npins != 0: linear range */
            //到这里说明确定不是gpio group啦,那就将它们加入到pinctrl子系统管理起来吧
            //,这里后面分析pinctrl子系统的时候再回过头来分析它
            ret = gpiochip_add_pin_range(chip,
                    pinctrl_dev_get_devname(pctldev),
                    pinspec.args[0],
                    pinspec.args[1],
                    pinspec.args[2]);
            if (ret)
                break;
        } else {
            /* npins == 0: special range */
            //校验,为pin gourp时,gpio 子系统要求第一个参数必须要0
            if (pinspec.args[1]) {
                pr_err("%s: Illegal gpio-range format.\n",
                    np->full_name);
                break;
            }
 
            //如果是pin group,那么gpio-ranges-group-names属性必须要有,通过它来指定
            //那个group
            if (!group_names) {
                pr_err("%s: GPIO group range requested but no %s property.\n",
                    np->full_name, group_names_propname);
                break;
            }
 
            //获取gpio-ranges-group-names属性里第index对应的字串(也就是组名)
            ret = of_property_read_string_index(np,
                        group_names_propname,
                        index, &name);
            if (ret)
                break;
 
            if (!strlen(name)) {
                pr_err("%s: Group name of GPIO group range cannot be the empty string.\n",
                np->full_name);
                break;
            }
 
            //一切ok,按group将它们加入到pinctrl子系统管理起来吧,这里后面分析pinctrl子系统的
            //时候再回过头来分析它
            ret = gpiochip_add_pingroup_range(chip, pctldev,
                        pinspec.args[0], name);
            if (ret)
                break;
        }
    }
}

总结一下,gpiochip_add总共做了以下主要事情:

  1. 将chip添加到全局的gpio chip链表中,用于gpio chip冲突处理和gpio管理
  2. 将该gpio chip对应的那段gpio都初始化
  3. 初始化设备树相关的信息,用于后面的属性解析及向pinctrl子系统同步下
  4. 导出到sys

还有一点需要补充,从这里我们也会发现pinctrl由gpio子系统调用了,驱动工程师以及bsp工程师都不用关心,多好啊!后面会再次看到,大部分都是内核的通用部分代码处理了pinctrl,驱动工程师以及bsp工程师仍然不用关心,只需要关心设备树里pinctrl相关的部分,不过我们也得知其所以然啊,不然该动那些和pinctrl相关的总是心理没底啊!!!和bsp驱动工程师相关的部分就一个函数,没其他的了。不过准备chip里面的那些字段也够bsp工程师忙一阵子了吧_

和bsp驱动工程师相关的部分就一个函数,没其他的了。不过准备chip里面的那些字段也够bsp工程师忙一阵子了吧_

下面开始分析驱动工程师调用的gpio_request的过程,它的核心实现是调用gpiod_request,gpiod_get和gpiod_get_index的核心实现处理也调用了gpiod_request,但还做了一些其他事情,如解析gpios或者xxx-gpios属性获取设备树里指定的flags以及通过指定的gpio号获取到对应的desc(在上面的gpiochip_add过程中,我们有看到了desc的初始化),当然解析的过程会用到gpiochip_add里说过的of_xlate

if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {  
    dev_dbg(dev, "using device tree for GPIO lookup\n");
    desc = of_find_gpio(dev, con_id, idx, &flags);
} else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) {
    dev_dbg(dev, "using ACPI for GPIO lookup\n");
    desc = acpi_find_gpio(dev, con_id, idx, &flags);
}

以及根据设备树里指定的flags设置desc,比如是否低电平有效,是否开漏输出等

if (flags & GPIO_ACTIVE_LOW)  
    set_bit(FLAG_ACTIVE_LOW, &desc->flags);
if (flags & GPIO_OPEN_DRAIN)  
    set_bit(FLAG_OPEN_DRAIN, &desc->flags);
if (flags & GPIO_OPEN_SOURCE)  
    set_bit(FLAG_OPEN_SOURCE, &desc->flags);

gpio_request里面做的事情前面已经说的很清楚了,下面结合代码再回顾下:

static int gpiod_request(struct gpio_desc *desc, const char *label)  
{
    int status = -EPROBE_DEFER;
    struct gpio_chip *chip;
 
    //检查desc是否有效,gpio_request会根据传入的gpio号在全局的desc里定位到desc
    //gpiod_get和gpiod_get_index则是通过解析设备树信息,提取里面的gpio号,然后再转换
    if (!desc) {
        pr_warn("%s: invalid GPIO\n", __func__);
        return -EINVAL;
    }
 
    //获取该desc的拥有者,即gpio chip(这个初始化在gpiochip_add里已经分析过了)
    chip = desc->chip;
    if (!chip)
        goto done;
 
    if (try_module_get(chip->owner)) {//增加下chip的引用
        status = __gpiod_request(desc, label);//核心动作都是在__gpiod_request里完成,就不再跟进去分析了^_^
        if (status < 0)
            module_put(chip->owner);
    }
 
done:  
    if (status)
        gpiod_dbg(desc, "%s: status %d\n", __func__, status);
 
    return status;
}

总结一下,驱动工程师用gpio_request等api请求指定的gpio,这些gpio其实都是由bsp工程师调用gpiochip_add添加的,gpio_request会标记它,防止被不同的模块重复引用该gpio,当然也会告诉下gpio chip(如果chip想要被通知的话)

2> gpio_to_irq/gpiod_to_irq的过程分析
通过前面的分析,应该对gpio子系统有了一个比较完全的了解吧!其他的api应该也都能理解(猜测到会做什么),这里在对和gpio相关的中断分析下

gpio_to_irq只是简单的对gpiod_to_irq进行封装,主要看gpiod_to_irq:

int gpiod_to_irq(const struct gpio_desc *desc)  
{
    struct gpio_chip    *chip;
    int         offset;
 
    if (!desc)
        return -EINVAL;
    chip = desc->chip;
    //获取该gpio号对应于该chip的offset,由于chip的起始号不一定就开始与系统全局desc的起点
      //而该chip的处理又都是基于0开始的,所以得转一下啦
    offset = gpio_chip_hwgpio(desc);
    //调用芯片驱动提供的to_irq,如果chip driver不支持中断,那么to_irq应该就是空咯,说明不支持
    //从这里应该也清楚的知道了gpio号与中断号的对应关系是由chip driver处理的
    //驱动工程师用的gpio号都是全局的,bsp工程师用的gpio号都是局部的
    return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
}

还是看一下zynq的to_irq的实现吧!说到这里,会牵涉到中断子系统(貌似我还没写过中断子系统的文章),如果不太清楚,就跳过吧!

static int zynq_gpio_to_irq(struct gpio_chip *chip, unsigned offset)  
{
    return irq_find_mapping(irq_domain, offset);
}

看起来貌似很简单吧!其实这也要归功于中断子系统的功劳啦!一般有中断控制器功能的设备会在该设备驱动里调用irq_domain_add_xxx接口来注册一个irq domain,然后会调用irq_create_mapping来创建irq号与硬件号的对应关系,里面会分配期望的irq desc,分配的时候,该中断控制器对应的中断号就确定了,然后会绑定该irq号与传入的硬件号。这就为后面的irq_find_mapping提供了支持,通过硬件号获取对应的irq号。这里再多说一句,大部分gpio chip同时也是一个中断控制器,写bsp的苦逼们不仅要gpiochip_add还要irq_domain_add等等操作,于是乎,gpio子系统解救他们来了,提供了一个接口gpiochip_irqchip_add,这接口完成后和gpio中断相关的所有事情,于是乎,bsp驱动工程师们也只需要调用两个接口了_多么美好!

pinctrl子系统

pinctrl子系统的内容在drivers/pinctrl文件夹下,主要文件有(建议先看看pinctrl内核文档Documentation/pinctrl.txt):
    core.c
    devicetree.c
    pinconf.c
    pinmux.c
    pinctrl-xxx.c

core.c为pinctrl的核心代码,实现了pinctrl框架,pinmux.c和pinconf.c基于core实现了对pinmux和pinconf的支持,pinctrl-xxx.c为厂商相关的pinctrl实现(又是苦逼的bsp工程师_),当然有些厂商还未采用pinctrl机制,因此就没有对应的实现。最后说一句,pinctrl的实现不许用我们在驱动里调用任何它提供的api,所有的pinctrl动作都是在通用内核代码里完成了,对于驱动工程师是透明的。驱动工程师只需要通过设备树文件就能掌控整个系统的pin管理了,后面分析的过程会证实这一点。
  pinctrl在代码层级只与bsp工程师有关,他们需要调用pinctrl api pinctrl_register注册。
  pinctrl子系统框架对于驱动工程师,只需要通过设备树文件就可以起到配置整个系统pin的目的。有几个概念先理一下,功能和组,功能就是指uart、i2c、spi等这些,组是pin的集合,我们都知道现在的soc的pin中,经常会遇到一个功能可以由不同的pin集合(即组)配置,当然同一时间只能选一个pin集合,因此,当我们要用某个功能的时候,需要告诉它func以及哪一组。下面开始分析pinctrl_register:

struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,  
                    struct device *dev, void *driver_data)
{
    struct pinctrl_dev *pctldev;
    int ret;
 
    if (!pctldesc)
        return NULL;
    if (!pctldesc->name)
        return NULL;
 
    //一般只有pinctrl chip driver需要调用pinctrl_register,pctldev就是软件上pinctrl的抽象
    pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
    if (pctldev == NULL) {
        dev_err(dev, "failed to alloc struct pinctrl_dev\n");
        return NULL;
    }
 
    /* Initialize pin control device struct */
    //初始化一些成员,后面会遇到它们的
    pctldev->owner = pctldesc->owner;
    pctldev->desc = pctldesc;
    pctldev->driver_data = driver_data;
    //pin_desc_tree用于存放所有的pin信息,由后面即将分析的pinctrl_register_pins来填充
    //所有pin信息来源于输入参数pctldesc,也就是说每个pinctrl chip driver的实现者需要告诉pinctrl
    //子系统该pinctrl chip所有的pin信息
    INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
    //这个由gpio子系统填充信息,还记得of_gpiochip_add_pin_range吧^_^最后总结的时候再结合gpio子系统一起看看这部分
    INIT_LIST_HEAD(&pctldev->gpio_ranges);
    pctldev->dev = dev;
    mutex_init(&pctldev->mutex);
 
    /* check core ops for sanity */
    //pinctrl_ops是pinctrl chip driver必须要实现的一组回调集合,后面在用到它里面的api时再详细讲解
    if (pinctrl_check_ops(pctldev)) {
        dev_err(dev, "pinctrl ops lacks necessary functions\n");
        goto out_err;
    }
 
    /* If we're implementing pinmuxing, check the ops for sanity */
    //如果提供了pinmux ops,检查下是否合法
    if (pctldesc->pmxops) {
        if (pinmux_check_ops(pctldev))
            goto out_err;
    }
 
    /* If we're implementing pinconfig, check the ops for sanity */
    //如果提供了pinconf ops,检查下是否合法
    if (pctldesc->confops) {
        if (pinconf_check_ops(pctldev))
            goto out_err;
    }
 
    /* Register all the pins */
    dev_dbg(dev, "try to register %d pins ...\n",  pctldesc->npins);
    //第一个核心操作,后面详细分析    ---------> 1
    ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
    if (ret) {
        dev_err(dev, "error during pin registration\n");
        pinctrl_free_pindescs(pctldev, pctldesc->pins,
                      pctldesc->npins);
        goto out_err;
    }
 
    mutex_lock(&pinctrldev_list_mutex);
    //将pctldev加入到全局链表
    list_add_tail(&pctldev->node, &pinctrldev_list);
    mutex_unlock(&pinctrldev_list_mutex);
 
    //这是第二个核心操作,往往pinctrl设备本身也需要做一些配置,这个函数就是用于处理这个功能---------> 2
    pctldev->p = pinctrl_get(pctldev->dev);
 
    if (!IS_ERR(pctldev->p)) {
        //如果pinctrl设备提供了default状态,设置为default状态
        pctldev->hog_default =
            pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
        if (IS_ERR(pctldev->hog_default)) {
            dev_dbg(dev, "failed to lookup the default state\n");
        } else {
            //设置为default状态
            if (pinctrl_select_state(pctldev->p,
                        pctldev->hog_default))
                dev_err(dev,
                    "failed to select default state\n");
        }
 
        //如果pinctrl设备提供了sleep状态,获取它,以后再用
        pctldev->hog_sleep =
            pinctrl_lookup_state(pctldev->p,
                            PINCTRL_STATE_SLEEP);
        if (IS_ERR(pctldev->hog_sleep))
            dev_dbg(dev, "failed to lookup the sleep state\n");
    }
 
    //和调试相关,先忽略吧
    pinctrl_init_device_debugfs(pctldev);
 
    return pctldev;
 
out_err:  
    mutex_destroy(&pctldev->mutex);
    kfree(pctldev);
    return NULL;
}

总结一下,pinctrl_register主要做了以下工作:

  1. 分配pctldev数据结构,并添加到全局链表pinctrldev_list中
  2. 填充pctldev,根据pctldesc里的pin信息注册所有的pin信息到pctldev里的pin_desc_tree管理起来,
  3. 如果该pinctrl对应的设备树里有描述它自己的pin配置信息,那么解析它,并设置为default状态。这一部分是任何一个用到pinctrl设备都会进行的动作(解析、设置状态)
  4. 初始化调试相关的东西

下面先看看pinctrl_register_pins的过程:

static int pinctrl_register_pins(struct pinctrl_dev *pctldev,  
                 struct pinctrl_pin_desc const *pins,
                 unsigned num_descs)
{
    unsigned i;
    int ret = 0;
 
    for (i = 0; i < num_descs; i++) {
        //遍历传入的所有pin的数据结构,一个个处理它们
        //pinctrl driver会传入所有的pin管脚及对应的名称
        ret = pinctrl_register_one_pin(pctldev,
                           pins[i].number, pins[i].name);
        if (ret)
            return ret;
    }
 
    return 0;
}
 
static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,  
                    unsigned number, const char *name)
{
    struct pin_desc *pindesc;
 
    //查看是否已经存在了
    pindesc = pin_desc_get(pctldev, number);
    if (pindesc != NULL) {
        pr_err("pin %d already registered on %s\n", number,
               pctldev->desc->name);
        return -EINVAL;
    }
 
    //分配一个pinctrl子系统用于管理pin的数据结构
    pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
    if (pindesc == NULL) {
        dev_err(pctldev->dev, "failed to alloc struct pin_desc\n");
        return -ENOMEM;
    }
 
    /* Set owner */
    //指定该pin的拥有者
    pindesc->pctldev = pctldev;
 
    /* Copy basic pin info */
    if (name) {
        //如果指定了名字,那么好吧,就用你了
        pindesc->name = name;
    } else {
        //如果没有指定名字,用默认的格式组合一个
        pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", number);
        if (pindesc->name == NULL) {
            kfree(pindesc);
            return -ENOMEM;
        }
        pindesc->dynamic_name = true;
    }
 
    //将该pin添加到pctldev里管理起来
    radix_tree_insert(&pctldev->pin_desc_tree, number, pindesc);
    pr_debug("registered pin %d (%s) on %s\n",
         number, pindesc->name, pctldev->desc->name);
    return 0;
}

下面开始分析第二个核心部分pinctrl_get,注意,这部分是任何一个用到pinctrl设备都会进行的动作(解析、设置状态),所以还必须弄清楚它,它主要的作用就是通过解析该设备的pinctrl信息生成一个pinctrl数据结构,用于管理该设备的pin信息,如有哪些状态、每个状态有哪些设置(设置包括pinmux和pinconf两种,有些设备只用需要pinmux,有些需要pinmux和pinconf)

struct pinctrl *pinctrl_get(struct device *dev)  
{
    struct pinctrl *p;
 
    if (WARN_ON(!dev))
        return ERR_PTR(-EINVAL);
 
    /*
     * See if somebody else (such as the device core) has already
     * obtained a handle to the pinctrl for this device. In that case,
     * return another pointer to it.
     */
    //如果已经有其他模块get了,那么pinctrl肯定已经创建好了,直接返回吧
    p = find_pinctrl(dev);
    if (p != NULL) {
        dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
        kref_get(&p->users);
        return p;
    }
 
    //否则,创建一个pinctrl用于管理该设备本身的pin信息
    return create_pinctrl(dev);
}

继续看解析的过程,通过看懂这部分,我们应该就很清楚设备树里需要怎么配置,怎么对整个系统的pin配置起作用的

static struct pinctrl *create_pinctrl(struct device *dev)  
{
    struct pinctrl *p;
    const char *devname;
    struct pinctrl_maps *maps_node;
    int i;
    struct pinctrl_map const *map;
    int ret;
 
    /*
     * create the state cookie holder struct pinctrl for each
     * mapping, this is what consumers will get when requesting
     * a pin control handle with pinctrl_get()
     */
    p = kzalloc(sizeof(*p), GFP_KERNEL);
    if (p == NULL) {
        dev_err(dev, "failed to alloc struct pinctrl\n");
        return ERR_PTR(-ENOMEM);
    }
    p->dev = dev;
    //每个需要管理的设备都会有对应的pinctrl,每个设备也会有多个状态,如default、sleep等等(内核
    //默认定义了一些,自己也可以随意定义),每个状态又有可能有多种设置。这个需要自己慢慢理解^_^
    //这里的states成员就是用于存放所有的状态的
    INIT_LIST_HEAD(&p->states);
    //这里的dt_maps就是用于存放所有的设置的
    INIT_LIST_HEAD(&p->dt_maps);
 
    //又是一个复杂的函数,后面分析,它主要用于解析设备树里的信息,生成该设备对应的maps(设置)
    ret = pinctrl_dt_to_map(p);
    if (ret < 0) {
        kfree(p);
        return ERR_PTR(ret);
    }
 
    devname = dev_name(dev);
 
    mutex_lock(&pinctrl_maps_mutex);
    /* Iterate over the pin control maps to locate the right ones */
    //遍历所有的的设置,这里遍历的是全局的maps链表,因为它要用到
    //pinctrl_map结构,而p->dt_maps里的不是该类型
    for_each_maps(maps_node, i, map) {
        /* Map must be for this device */
        //检查是否属于俺的设置
        if (strcmp(map->dev_name, devname))
            continue;
 
        //将该设置加入到pinctrl中,也许有人会奇怪,前面的dt_maps不是已经包含了该设备的所有设置了么,
        //其实这里会对每个设置做进一步处理,然后放入到p中,后面分析
        ret = add_setting(p, map);
        /*
         * At this point the adding of a setting may:
         *
         * - Defer, if the pinctrl device is not yet available
         * - Fail, if the pinctrl device is not yet available,
         *   AND the setting is a hog. We cannot defer that, since
         *   the hog will kick in immediately after the device
         *   is registered.
         *
         * If the error returned was not -EPROBE_DEFER then we
         * accumulate the errors to see if we end up with
         * an -EPROBE_DEFER later, as that is the worst case.
         */
        if (ret == -EPROBE_DEFER) {
            pinctrl_free(p, false);
            mutex_unlock(&pinctrl_maps_mutex);
            return ERR_PTR(ret);
        }
    }
    mutex_unlock(&pinctrl_maps_mutex);
 
    if (ret < 0) {
        /* If some other error than deferral occured, return here */
        pinctrl_free(p, false);
        return ERR_PTR(ret);
    }
 
    kref_init(&p->users);
 
    /* Add the pinctrl handle to the global list */
    mutex_lock(&pinctrl_list_mutex);
    //将每个设备用于控制pin的结构也放到一个全局链表中
    list_add_tail(&p->node, &pinctrl_list);
    mutex_unlock(&pinctrl_list_mutex);
 
    return p;
}

先总结下create_pinctrl:

  1. 创建一个pinctrl,将它加入到全局的pinctrl链表
  2. 解析该设备的说有设备树信息,将解析的状态挂到states里,解析的设置挂到dt_maps(当然,设置同时也挂到全局的maps里去了)

实在不想贴代码了,不过不贴又不好解释清楚_ 继续上pinctrl_dt_to_map吧,它就是实现了上面总结的第二点:

int pinctrl_dt_to_map(struct pinctrl *p)  
{
    struct device_node *np = p->dev->of_node;
    int state, ret;
    char *propname;
    struct property *prop;
    const char *statename;
    const __be32 *list;
    int size, config;
    phandle phandle;
    struct device_node *np_config;
 
    /* CONFIG_OF enabled, p->dev not instantiated from DT */
    if (!np) {
        if (of_have_populated_dt())
            dev_dbg(p->dev,
                "no of_node; not parsing pinctrl DT\n");
        return 0;
    }
 
    /* We may store pointers to property names within the node */
    of_node_get(np);
 
    /* For each defined state ID */
    for (state = 0; ; state++) {
        /* Retrieve the pinctrl-* property */
        //pinctrl子系统规定了几个属性,如pinctrl-n,用于指定一个状态对应的设置,从0开始        
        propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
        //查找pinctrl-n属性
        prop = of_find_property(np, propname, &size);
        kfree(propname);
        if (!prop)
            break;
        //value对应的就是该状态对应的设置(可能有多个),后面会处理它
        list = prop->value;
        size /= sizeof(*list);
 
        /* Determine whether pinctrl-names property names the state */
        //读pinctrl-names属性,也属于pinctrl子系统规定的属性,用于指定每个状态的名字,一一对应的
        ret = of_property_read_string_index(np, "pinctrl-names",
                            state, &statename);
        /*
         * If not, statename is just the integer state ID. But rather
         * than dynamically allocate it and have to free it later,
         * just point part way into the property name for the string.
         */
        if (ret < 0) {
            /* strlen("pinctrl-") == 8 */
            //如果美誉pinctrl-names属性,那么状态名就是index
            statename = prop->name + 8;
        }
 
        /* For every referenced pin configuration node in it */
        //一个一个处理设置
        for (config = 0; config < size; config++) {
            //第一个成员规定为配置节点(属于pinctrl的子节点)的引用,因此通过它可以找到该配置节点
            phandle = be32_to_cpup(list++);
 
            /* Look up the pin configuration node */
            np_config = of_find_node_by_phandle(phandle);
            if (!np_config) {
                dev_err(p->dev,
                    "prop %s index %i invalid phandle\n",
                    prop->name, config);
                ret = -EINVAL;
                goto err;
            }
 
            /* Parse the node */
            //找到对应的配置节点了,那么就解析那个配置节点到该设备的这个状态的这个设置中吧,后面继续贴 哎
            ret = dt_to_map_one_config(p, statename, np_config);
            of_node_put(np_config);
            if (ret < 0)
                goto err;
        }
 
        /* No entries in DT? Generate a dummy state table entry */
        if (!size) {
            ret = dt_remember_dummy_state(p, statename);
            if (ret < 0)
                goto err;
        }
    }
 
    return 0;
 
err:  
    pinctrl_dt_free_maps(p);
    return ret;
}

继续看dt_to_map_one_config:

static int dt_to_map_one_config(struct pinctrl *p, const char *statename,  
                struct device_node *np_config)
{
    struct device_node *np_pctldev;
    struct pinctrl_dev *pctldev;
    const struct pinctrl_ops *ops;
    int ret;
    struct pinctrl_map *map;
    unsigned num_maps;
 
    /* Find the pin controller containing np_config */
    np_pctldev = of_node_get(np_config);
    for (;;) {
        //找该节点的父节点,就是pinctrl设备啦,我们得通过它获取pctldev,毕竟只有它才有啊
        np_pctldev = of_get_next_parent(np_pctldev);
        if (!np_pctldev || of_node_is_root(np_pctldev)) {
            dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",
                np_config->full_name);
            of_node_put(np_pctldev);
            /* OK let's just assume this will appear later then */
            return -EPROBE_DEFER;
        }
        pctldev = get_pinctrl_dev_from_of_node(np_pctldev);
        if (pctldev)//拿到就跳出
            break;
        /* Do not defer probing of hogs (circular loop) */
        if (np_pctldev == p->dev->of_node) {
            of_node_put(np_pctldev);
            return -ENODEV;
        }
    }
    of_node_put(np_pctldev);
 
    /*
     * Call pinctrl driver to parse device tree node, and
     * generate mapping table entries
     */
    ops = pctldev->desc->pctlops;
    //这里就用到了pinctrl_register注册时pctlops里的dt_node_to_map回调函数了
    if (!ops->dt_node_to_map) {
        dev_err(p->dev, "pctldev %s doesn't support DT\n",
            dev_name(pctldev->dev));
        return -ENODEV;
    }
    //调用它,靠它来解析出这个配置节点,毕竟格式只有对应的pinctrl driver最清楚
    ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
    if (ret < 0)
        return ret;
 
    /* Stash the mapping table chunk away for later use */
    //将解析出来的设置添加到pctldev的dt_maps中,也会加到全局的maps中啦,这里就不再深入分析了,自己都觉得太啰嗦了
    return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
}

继续看add_setting:

static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)  
{
    struct pinctrl_state *state;
    struct pinctrl_setting *setting;
    int ret;
    //前面只是解析出了所有的设置,这里就将所有的设置按状态归类起来,如果状态还没创建,就创建一个
    state = find_state(p, map->name);
    if (!state)
        state = create_state(p, map->name);
    if (IS_ERR(state))
        return PTR_ERR(state);
 
    if (map->type == PIN_MAP_TYPE_DUMMY_STATE)
        return 0;
 
    //分配一个设置数据结构
    setting = kzalloc(sizeof(*setting), GFP_KERNEL);
    if (setting == NULL) {
        dev_err(p->dev,
            "failed to alloc struct pinctrl_setting\n");
        return -ENOMEM;
    }
 
    //设置的类型
    setting->type = map->type;
 
    //设置所属的pctldev
    setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
    if (setting->pctldev == NULL) {
        kfree(setting);
        /* Do not defer probing of hogs (circular loop) */
        if (!strcmp(map->ctrl_dev_name, map->dev_name))
            return -ENODEV;
        /*
         * OK let us guess that the driver is not there yet, and
         * let's defer obtaining this pinctrl handle to later...
         */
        dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
            map->ctrl_dev_name);
        return -EPROBE_DEFER;
    }
 
    //设置名字
    setting->dev_name = map->dev_name;
 
    switch (map->type) {//根据设置的类型处理设置,因为设置可以表示mux功能,也可以表示conf功能
    case PIN_MAP_TYPE_MUX_GROUP://如果是mux功能的设置,调用mux模块处理
        ret = pinmux_map_to_setting(map, setting);
        break;
    case PIN_MAP_TYPE_CONFIGS_PIN:
    case PIN_MAP_TYPE_CONFIGS_GROUP://如果是mux功能的设置,调用conf模块处理
        ret = pinconf_map_to_setting(map, setting);
        break;
    default:
        ret = -EINVAL;
        break;
    }
    if (ret < 0) {
        kfree(setting);
        return ret;
    }
 
    //将设置放入状态链表归类
    list_add_tail(&setting->node, &state->settings);
 
    return 0;
}

下面分别分析pinmux_map_to_setting和pinconf_map_to_setting,先pinmux_map_to_setting,它是和pinmux相关,对应pinmux.c文件,里面也会用到pinmux_ops:

int pinmux_map_to_setting(struct pinctrl_map const *map,  
              struct pinctrl_setting *setting)
{
    struct pinctrl_dev *pctldev = setting->pctldev;
    const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
    char const * const *groups;
    unsigned num_groups;
    int ret;
    const char *group;
    int i;
    //如果在register的时候没有指定pinmux_ops,那么该函数什么都不做,出错返回
    if (!pmxops) {
        dev_err(pctldev->dev, "does not support mux function\n");
        return -EINVAL;
    }
    //现在就是pinmux_ops作用的时候啦!里面会以从0开始的索引不停的调用
    //pinmux_ops里的get_function_name来获取对应的名字,然后和前面解析设备树过程解析出来的名字做匹配
    //直到找到或到末尾,返回该索引。这个索引与功能之间的关系由pinctrl bsp实现者负责
    ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
    if (ret < 0) {
        dev_err(pctldev->dev, "invalid function %s in map table\n",
            map->data.mux.function);
        return ret;
    }
    //保存该索引
    setting->data.mux.func = ret;
 
    //调用pmxops的get_function_groups获取该索引对应的组(可能存在多个,前面已经说过,一个功能可以由多个组实现,同一时间只能选一个组)
    ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
                      &groups, &num_groups);
    if (ret < 0) {
        dev_err(pctldev->dev, "can't query groups for function %s\n",
            map->data.mux.function);
        return ret;
    }
    if (!num_groups) {
        dev_err(pctldev->dev,
            "function %s can't be selected on any group\n",
            map->data.mux.function);
        return -EINVAL;
    }
    //如果设备树里有直接指定组,那么就会以指定的组为默认选择
    if (map->data.mux.group) {
        bool found = false;
        group = map->data.mux.group;
        //当然,也还是要校验下,组是否有效
        for (i = 0; i < num_groups; i++) {
            if (!strcmp(group, groups[i])) {
                found = true;
                break;
            }
        }
        if (!found) {
            dev_err(pctldev->dev,
                "invalid group \"%s\" for function \"%s\"\n",
                group, map->data.mux.function);
            return -EINVAL;
        }
    } else {
        //如果没有指定,那么就用第一个组咯
        group = groups[0];
    }
 
    //根据选定的组,获取该组的信息,返回的是该组对应的索引,这里会调用pmxops的get_group_name,操作
    //过程和前面的pinmux_func_name_to_selector类似
    ret = pinctrl_get_group_selector(pctldev, group);
    if (ret < 0) {
        dev_err(pctldev->dev, "invalid group %s in map table\n",
            map->data.mux.group);
        return ret;
    }
    //保存该组索引
    setting->data.mux.group = ret;
 
    return 0;
}

继续pinconf_map_to_setting吧,它是和pinconf相关,对应pinconf.c文件,但里面还没用pinconf_ops,后面才会用到:

int pinconf_map_to_setting(struct pinctrl_map const *map,  
              struct pinctrl_setting *setting)
{
    struct pinctrl_dev *pctldev = setting->pctldev;
    int pin;
 
    switch (setting->type) {//该设置到底是什么类型,是pinctrl driver回调dt_node_to_map里解析的
    //配置有两种类型,一种是一个pin一个pin的配置,一种是将一些pin的配置组合为一个组,指定某个组就会采用那个组里的所有的pin的配置
    case PIN_MAP_TYPE_CONFIGS_PIN:
        //根据设备树里指定的pin名字获取它对应的pin号
        pin = pin_get_from_name(pctldev,
                    map->data.configs.group_or_pin);
        if (pin < 0) {
            dev_err(pctldev->dev, "could not map pin config for \"%s\"",
                map->data.configs.group_or_pin);
            return pin;
        }
        //将该设置对应的pin号保存起来
        setting->data.configs.group_or_pin = pin;
        break;
    case PIN_MAP_TYPE_CONFIGS_GROUP:
        //根据设备树指定的pin组获取它对应的group号
        pin = pinctrl_get_group_selector(pctldev,
                     map->data.configs.group_or_pin);
        if (pin < 0) {
            dev_err(pctldev->dev, "could not map group config for \"%s\"",
                map->data.configs.group_or_pin);
            return pin;
        }
        //将该设置对应的group号保存起来
        setting->data.configs.group_or_pin = pin;
        break;
    default:
        return -EINVAL;
    }
 
    //保存所有其他用于配置的信息
    setting->data.configs.num_configs = map->data.configs.num_configs;
    setting->data.configs.configs = map->data.configs.configs;
 
    return 0;
}

现在都仅仅是分析了pinmux_map_to_setting和pinconf_map_to_setting,具体它们的作用我们在后面才能看的出来,所以继续分析吧!到这里pinctrl_get分析完了,执行完pinctrl_get,就意味着该设备的所有和pin相关的设备树信息已经解析完成,并生成了用于管理、配置的数据结构,为以后的其他api提供了支持。其他驱动一般不会直接调用pinctrl_get,而是调用它的变体devm_pinctrl_get或者pinctrl_get_select来初始化设备。devm_pinctrl_get就不用说了啦,pinctrl_get_select类似与pinctrl_register调用pinctrl_get及它后的那段代码的结合,不仅调用了pinctrl_get,还根据输入参数让设备处于指定的状态。通过pinctrl_select_state来让设备处于指定的状态,下面开始分析它,通过分析它,应该就清楚了前面各种填充的作用啦!

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)  
{
    struct pinctrl_setting *setting, *setting2;
    struct pinctrl_state *old_state = p->state;
    int ret;
    //如果当前就是该状态,直接返回成功
    if (p->state == state)
        return 0;
 
    //如果之前有设置过状态,那需要做一些额外处理
    if (p->state) {
        /*
         * The set of groups with a mux configuration in the old state
         * may not be identical to the set of groups with a mux setting
         * in the new state. While this might be unusual, it's entirely
         * possible for the "user"-supplied mapping table to be written
         * that way. For each group that was configured in the old state
         * but not in the new state, this code puts that group into a
         * safe/disabled state.
         */
        list_for_each_entry(setting, &p->state->settings, node) {
            bool found = false;
            if (setting->type != PIN_MAP_TYPE_MUX_GROUP)
                continue;
            list_for_each_entry(setting2, &state->settings, node) {
                if (setting2->type != PIN_MAP_TYPE_MUX_GROUP)
                    continue;
                if (setting2->data.mux.group ==
                        setting->data.mux.group) {
                    found = true;
                    break;
                }
            }
            if (!found)
                pinmux_disable_setting(setting);
        }
    }
 
    p->state = NULL;
 
    /* Apply all the settings for the new state */
    //
    list_for_each_entry(setting, &state->settings, node) {
    //遍历该设备的该状态下的所有设置,一个个设置上去
        switch (setting->type) {
        case PIN_MAP_TYPE_MUX_GROUP://如果该设置是mux设置,那么调用pinmux_enable_setting,这里面
            //就用到了前面填充的信息
            ret = pinmux_enable_setting(setting);
            break;
        case PIN_MAP_TYPE_CONFIGS_PIN:
        case PIN_MAP_TYPE_CONFIGS_GROUP://如果该设置是conf设置,那么调用pinconf_apply_setting,
            //这里面就用到了前面填充的信息
            ret = pinconf_apply_setting(setting);
            break;
        default:
            ret = -EINVAL;
            break;
        }
 
        if (ret < 0) {
            goto unapply_new_state;
        }
    }
 
    p->state = state;
 
    return 0;
 
unapply_new_state:  
    dev_err(p->dev, "Error applying setting, reverse things back\n");
 
    list_for_each_entry(setting2, &state->settings, node) {
        if (&setting2->node == &setting->node)
            break;
        /*
         * All we can do here is pinmux_disable_setting.
         * That means that some pins are muxed differently now
         * than they were before applying the setting (We can't
         * "unmux a pin"!), but it's not a big deal since the pins
         * are free to be muxed by another apply_setting.
         */
        if (setting2->type == PIN_MAP_TYPE_MUX_GROUP)
            pinmux_disable_setting(setting2);
    }
 
    /* There's no infinite recursive loop here because p->state is NULL */
    if (old_state)
        pinctrl_select_state(p, old_state);
 
    return ret;
}

pinmux_enable_setting当然处于pinmux.c中,根据前面填充的setting->data.mux.group获取该组的pin信息,然后以pin号为参数循环回调ops->request,最后回调ops->enable。

pinconf_apply_setting当然处于pinconf.c中,根据前面填充的group_or_pin、configs、num_configs以及type分别回调pin_config_set和pin_config_group_set。

最后补充下,本文描述的都是基于设备树方式的pinctrl处理,其实也可以通过pinctrl_register_mappings调用静态添加所有的设置,只是不常用该方式而已。

基于s3c6410情景分析

打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410为例看看pinctrl输入参数的初始化过程(最开始的zynq平台的pin配置貌似是通过bitstreams来的,内核层没看到有关配置pin的代码,不过最新的zynq代码里加入了pinctrl,但我手上的恰好的较早其的zynq代码,所以这里以三星的代码为例子),不过这里贴的代码有点多(尽量将无关的代码删掉),耐心的看吧_.(对于JZ2440,其驱动流程基本一致,完全可以类比)
bsp驱动工程师的角度:

driver/pinctrl/samsung/pinctrl-samsung.c

static int samsung_pinctrl_probe(struct platform_device *pdev)  
{
    ...
    ...
    ...
    //解析pinctrl信息,后面分析
    ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);
    drvdata->ctrl = ctrl;
    drvdata->dev = dev;
 
    ...
    ...
    ...
    //向gpio子系统注册(三星有用gpio子系统)
    ret = samsung_gpiolib_register(pdev, drvdata);
    if (ret)
        return ret;
 
    //向pinctrl子系统注册
    ret = samsung_pinctrl_register(pdev, drvdata);
    if (ret) {
        samsung_gpiolib_unregister(pdev, drvdata);
        return ret;
    }
 
    ...
    ...
    ...
 
    return 0;
}

先贴下6410 pinctrl设备树信息(arch/arm/boot/dts/s3c64xx.dtsi):

aliases {  
    i2c0 = &i2c0;                                                           
    pinctrl0 = &pinctrl0;                                                   
}; 
 
pinctrl0: pinctrl@7f008000 {  
    compatible = "samsung,s3c64xx-pinctrl";                             
    reg = <0x7f008000 0x1000>;                                          
    interrupt-parent = <&vic1>;                                         
    interrupts = <21>;                                                  
 
    pctrl_int_map: pinctrl-interrupt-map {                              
        interrupt-map = <0 &vic0 0>,                                    
                <1 &vic0 1>,                                            
                <2 &vic1 0>,                                            
                <3 &vic1 1>;                                            
        #address-cells = <0>;                                           
        #size-cells = <0>;                                              
        #interrupt-cells = <1>;                                         
    };                                                                  
 
    wakeup-interrupt-controller {                                       
        compatible = "samsung,s3c64xx-wakeup-eint";                     
        interrupts = <0>, <1>, <2>, <3>;                                
        interrupt-parent = <&pctrl_int_map>;                            
    };                                                                  
};  

下面边看代码边对照上面的设备树描述,看看解析过程:

static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(  
                struct samsung_pinctrl_drv_data *d,
                struct platform_device *pdev)
{
    int id;
    const struct of_device_id *match;
    struct device_node *node = pdev->dev.of_node;
    struct device_node *np;
    struct samsung_pin_ctrl *ctrl;
    struct samsung_pin_bank *bank;
    int i;
 
    //获取pinctrl的alias id,其实就是上面的pinctrl0了
    id = of_alias_get_id(node, "pinctrl");
    if (id < 0) {
        dev_err(&pdev->dev, "failed to get alias id\n");
        return NULL;
    }
    //获取该节点对应的match
    match = of_match_node(samsung_pinctrl_dt_match, node);
    //通过id找到对应的pinctrl,因为三星的有些soc是存在多个pinctrl的,
    //也就是说pinctrl0,pinctrl1等等同时存在,这里就是获取第id个,对于6410,就一个
    //struct samsung_pin_ctrl s3c64xx_pin_ctrl[] = {
    //    {
    //        /* pin-controller instance 1 data */
    //        .pin_banks    = s3c64xx_pin_banks0,
    //        .nr_banks    = ARRAY_SIZE(s3c64xx_pin_banks0),
    //        .eint_gpio_init = s3c64xx_eint_gpio_init,
    //        .eint_wkup_init = s3c64xx_eint_eint0_init,
    //        .label        = "S3C64xx-GPIO",
    //    },
    //};
    对于exynos5420,就存在多个啦:
    //struct samsung_pin_ctrl exynos5420_pin_ctrl[] = {
    //    {
    //        /* pin-controller instance 0 data */
    //        .pin_banks    = exynos5420_pin_banks0,
    //        .nr_banks    = ARRAY_SIZE(exynos5420_pin_banks0),
    //        .geint_con    = EXYNOS_GPIO_ECON_OFFSET,
    //        .geint_mask    = EXYNOS_GPIO_EMASK_OFFSET,
    //        .geint_pend    = EXYNOS_GPIO_EPEND_OFFSET,
    //        .weint_con    = EXYNOS_WKUP_ECON_OFFSET,
    //        .weint_mask    = EXYNOS_WKUP_EMASK_OFFSET,
    //        .weint_pend    = EXYNOS_WKUP_EPEND_OFFSET,
    //        .svc        = EXYNOS_SVC_OFFSET,
    //        .eint_gpio_init = exynos_eint_gpio_init,
    //        .eint_wkup_init = exynos_eint_wkup_init,
    //        .label        = "exynos5420-gpio-ctrl0",
    //    }, {
    //        /* pin-controller instance 1 data */
    //        .pin_banks    = exynos5420_pin_banks1,
    //        .nr_banks    = ARRAY_SIZE(exynos5420_pin_banks1),
    //        .geint_con    = EXYNOS_GPIO_ECON_OFFSET,
    //       .geint_mask    = EXYNOS_GPIO_EMASK_OFFSET,
    //        .geint_pend    = EXYNOS_GPIO_EPEND_OFFSET,
    //        .svc        = EXYNOS_SVC_OFFSET,
    //        .eint_gpio_init = exynos_eint_gpio_init,
    //        .label        = "exynos5420-gpio-ctrl1",
    //    },
    //    ...
    //    ...
    //    ...
    //};
    ctrl = (struct samsung_pin_ctrl *)match->data + id;
    //提取pin ctrl里的banks信息,这里就是ARRAY_SIZE(s3c64xx_pin_banks0)
    bank = ctrl->pin_banks;
    //遍历每一个bank,填充相应的信息
    for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
        spin_lock_init(&bank->slock);
        bank->drvdata = d;
        //设置bank的pin base
        bank->pin_base = ctrl->nr_pins;
        //更新ctrl->nr_pins,即该pin ctrl的pin数量,在后面的注册时会用到该成员
        ctrl->nr_pins += bank->nr_pins;
    }
 
    //遍历该节点的每一个子节点,上面的s3c64xx.dtsi文件末尾有一个
    //#include "s3c64xx-pinctrl.dtsi" 语句,s3c64xx-pinctrl.dtsi里
    //的信息是对当前节点pinctrl0的补充,内容如下:
    //&pinctrl0 {                                                                     
    ///*                                                                          
    // * Pin banks                                                                
    // */                                                                         
    //
    //gpa: gpa {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};                                                                          
    //
    //gpb: gpb {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};                                                                          
 
    //gpc: gpc {                                                                  
    //    gpio-controller;                                                        
    //    #gpio-cells = <2>;                                                      
    //    interrupt-controller;                                                   
    //    #interrupt-cells = <2>;                                                 
    //};          
    //...
    //...
    //...
    //hsi_bus: hsi-bus {                                                          
    //    samsung,pins = "gpk-0", "gpk-1", "gpk-2", "gpk-3",                      
    //            "gpk-4", "gpk-5", "gpk-6", "gpk-7";                             
    //    samsung,pin-function = <3>;                                             
    //    samsung,pin-pud = <PIN_PULL_NONE>;                                      
    //};     
    //}
    //这里就是处理这些子节点
    for_each_child_of_node(node, np) {
        //如果该子节点没有gpio-controller属性,跳过处理,这里处理的是bank
        //只和gpio有关,所以跳过不关心的
        if (!of_find_property(np, "gpio-controller", NULL))
            continue;
        bank = ctrl->pin_banks;
        for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
            if (!strcmp(bank->name, np->name)) {
                //将bank对应到它自己的设备节点
                bank->of_node = np;
                break;
            }
        }
    }
 
    ctrl->base = pin_base;
    pin_base += ctrl->nr_pins;
 
    return ctrl;
}

填充完必要的信息,就开始注册了,先看pinctrl的注册吧!注意,传入的参数drvdata是已经经过前面的解析填入了很多信息的

static int samsung_pinctrl_register(struct platform_device *pdev,  
                    struct samsung_pinctrl_drv_data *drvdata)
{
    struct pinctrl_desc *ctrldesc = &drvdata->pctl;
    struct pinctrl_pin_desc *pindesc, *pdesc;
    struct samsung_pin_bank *pin_bank;
    char *pin_names;
    int pin, bank, ret;
 
    //初始化pinctrl_desc,register的时候要用
    ctrldesc->name = "samsung-pinctrl";
    ctrldesc->owner = THIS_MODULE;
    //这个ops是必须要的,里面的几个函数前面也都用到了,主要有
    //get_groups_count、dt_node_to_map、get_group_pins
    ctrldesc->pctlops = &samsung_pctrl_ops;
    //这个是pinctrl chip driver根据自己平台的特性,可选的支持的
    //主要有request、get_functions_count、get_function_groups、
    //enable,和gpio相关的还有额外几个gpio_request_enable、gpio_disable_free、gpio_set_direction
    ctrldesc->pmxops = &samsung_pinmux_ops;
    //这个是pinctrl chip driver根据自己平台的特性,可选的支持的
    //主要有pin_config_get、pin_config_set、pin_config_group_get、pin_config_group_set
    ctrldesc->confops = &samsung_pinconf_ops;
 
    //下面这部分也是pinctrl chip driver根据自己平台的特性必须填充的,用于表示该pinctrl chip
    //所有的pin信息
    pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
            drvdata->ctrl->nr_pins, GFP_KERNEL);
    if (!pindesc) {
        dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
        return -ENOMEM;
    }
    ctrldesc->pins = pindesc;
    ctrldesc->npins = drvdata->ctrl->nr_pins;//该成员就是samsung_pin_ctrl填充的
 
    //填充pin号
    /* dynamically populate the pin number and pin name for pindesc */
    for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
        pdesc->number = pin + drvdata->ctrl->base;//该成员也是由samsung_pin_ctrl填充的
 
    //分配空间,用于填充pin名字
    /*
     * allocate space for storing the dynamically generated names for all
     * the pins which belong to this pin-controller.
     */
    pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *
                    drvdata->ctrl->nr_pins, GFP_KERNEL);
    if (!pin_names) {
        dev_err(&pdev->dev, "mem alloc for pin names failed\n");
        return -ENOMEM;
    }
 
    /* for each pin, the name of the pin is pin-bank name + pin number */
    for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
        pin_bank = &drvdata->ctrl->pin_banks[bank];
        for (pin = 0; pin < pin_bank->nr_pins; pin++) {
            //填充pin的名字,注意这里的格式,设备树里的命名就得按照该格式,即bank名字+pin号
            sprintf(pin_names, "%s-%d", pin_bank->name, pin);
            pdesc = pindesc + pin_bank->pin_base + pin;
            pdesc->name = pin_names;
            pin_names += PIN_NAME_LENGTH;
        }
    }
 
    //到现在,离注册需要的条件就剩function和group的填充了,其实它们不是pinctrl子系统要求的,
    //但是回调函数的实现依赖这些,因此需要解析设备树信息来填充它们,后面会详细分析该函数
    ret = samsung_pinctrl_parse_dt(pdev, drvdata);
    if (ret)
        return ret;
 
    //一切准备好后,就注册了
    drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
    if (!drvdata->pctl_dev) {
        dev_err(&pdev->dev, "could not register pinctrl driver\n");
        return -EINVAL;
    }
 
    //
    for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
        pin_bank = &drvdata->ctrl->pin_banks[bank];
        pin_bank->grange.name = pin_bank->name;
        pin_bank->grange.id = bank;
        pin_bank->grange.pin_base = pin_bank->pin_base;
        pin_bank->grange.base = pin_bank->gpio_chip.base;
        pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
        pin_bank->grange.gc = &pin_bank->gpio_chip;
        pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
    }
 
    return 0;
}

samsung_pinctrl_parse_dt分析:

static int samsung_pinctrl_parse_dt(struct platform_device *pdev,  
                    struct samsung_pinctrl_drv_data *drvdata)
{
    ...
    //获取pinctrl设备的子节点数量,前面已经讲过有哪些子节点了,不再重复
    grp_cnt = of_get_child_count(dev_np);
    if (!grp_cnt)
        return -EINVAL;
 
    //根据获取的数量,分配空间,每个配置节点对应于一个group(pin的集合)
    groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
    if (!groups) {
        dev_err(dev, "failed allocate memory for ping group list\n");
        return -EINVAL;
    }
    grp = groups;
 
    //根据获取的数量,分配空间,每个配置节点对应的功能
    functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
    if (!functions) {
        dev_err(dev, "failed to allocate memory for function list\n");
        return -EINVAL;
    }
    func = functions;
 
    //遍历每一个子节点,一个个处理
    /*
     * Iterate over all the child nodes of the pin controller node
     * and create pin groups and pin function lists.
     */
    for_each_child_of_node(dev_np, cfg_np) {
        u32 function;
        //检查samsung,pins属性
        if (!of_find_property(cfg_np, "samsung,pins", NULL))
            continue;
 
        //将samsung,pins属性里面指定的名字列表转换为pin号列表
        //,这里面会用到前面samsung_pinctrl_get_soc_data填充的信息来匹配
        ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
                    &drvdata->pctl, &pin_list, &npins);
        if (ret)
            return ret;
 
        //下面就是构成一个pin group了,注意pin组的名字
        //,是配置节点名+GROUP_SUFFIX,GROUP_SUFFIX为-grp
        /* derive pin group name from the node name */
        gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
                    GFP_KERNEL);
        if (!gname) {
            dev_err(dev, "failed to alloc memory for group name\n");
            return -ENOMEM;
        }
        sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
 
        grp->name = gname;
        grp->pins = pin_list;
        grp->num_pins = npins;
        of_property_read_u32(cfg_np, "samsung,pin-function", &function);
        grp->func = function;
        grp++;
 
        if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
            continue;
 
        //如果存在samsung,pin-function属性,那么构建一个功能名
        //,功能名组合方式是配置节点名+FUNCTION_SUFFIX,FUNCTION_SUFFIX为-mux
        /* derive function name from the node name */
        fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
                    GFP_KERNEL);
        if (!fname) {
            dev_err(dev, "failed to alloc memory for func name\n");
            return -ENOMEM;
        }
        sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
 
        func->name = fname;
        func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
        if (!func->groups) {
            dev_err(dev, "failed to alloc memory for group list "
                    "in pin function");
            return -ENOMEM;
        }
        func->groups[0] = gname;
        func->num_groups = 1;
        func++;
        func_idx++;
    }
 
    //存储下解析的数据信息
    drvdata->pin_groups = groups;
    drvdata->nr_groups = grp_cnt;
    drvdata->pmx_functions = functions;
    drvdata->nr_functions = func_idx;
 
    return 0;
}

下面通过分析各个ops,来进一步理解下上面几个函数所起的作用:

static const struct pinctrl_ops samsung_pctrl_ops = {  
    .get_groups_count   = samsung_get_group_count,
    .get_group_name     = samsung_get_group_name,
    .get_group_pins     = samsung_get_group_pins,
    .dt_node_to_map     = samsung_dt_node_to_map,
    .dt_free_map        = samsung_dt_free_map,
};
static const struct pinmux_ops samsung_pinmux_ops = {  
    .get_functions_count    = samsung_get_functions_count,
    .get_function_name  = samsung_pinmux_get_fname,
    .get_function_groups    = samsung_pinmux_get_groups,
    .enable         = samsung_pinmux_enable,
    .disable        = samsung_pinmux_disable,
    //由pinmux_gpio_direction间接调用,最开始应该是gpio子系统
    //的gpio_pin_direction_input、gpio_pin_direction_output触发
    .gpio_set_direction = samsung_pinmux_gpio_set_direction,
};
static const struct pinconf_ops samsung_pinconf_ops = {  
    .pin_config_get     = samsung_pinconf_get,
    .pin_config_set     = samsung_pinconf_set,
    .pin_config_group_get   = samsung_pinconf_group_get,
    .pin_config_group_set   = samsung_pinconf_group_set,
};

从上面一路分析下路来,我们应该知道dt_node_to_map是最先调用的,其次是get_functions_count、get_function_name、get_function_groups、get_groups_count、get_group_name、get_group_pins、request(三星pinmux_ops没有实现它)、enable、pin_config_set、pin_config_group_set所以我打算就按这个顺序进行分析。

调用dt_node_to_map的时候,从前文应该很清楚了吧,就是在某一个设备(pinctrl本身也算是一个设备,不过从前文贴出来的pinctrl0里,我没发现有pinctrl-xxx的属性,也就是说不需要对它做任何pin ctrl)用pinctrl_get请求解析自己设备树信息的时候,说的更准确点的话,就是解析该设备里某一个状态的某一个配置(一个状态可能需要多个配置来完成)的时候。下面用某一个子设备的设备树信息为例子,对应文件s3c6410-smdk6410.dts

#define PIN_PULL_NONE   0  
 
&uart0 {                                                                        
    pinctrl-names = "default";                                                  
    pinctrl-0 = <&uart0_data>, <&uart0_fctl>;                                   
    status = "okay";                                                            
};
uart0_data: uart0-data {  
    samsung,pins = "gpa-0", "gpa-1";                                        
    samsung,pin-function = <2>;                                             
    samsung,pin-pud = <PIN_PULL_NONE>;                                      
};       
uart0_fctl: uart0-fctl {  
    samsung,pins = "gpa-2", "gpa-3";                                        
    samsung,pin-function = <2>;                                             
    samsung,pin-pud = <PIN_PULL_NONE>;                                      
};
 
//下面部分是uart0的其他信息,和本文关心的pinctrl无关,之所以也列出来,只是不想让读者对这部分有误解
uart0: serial@7f005000 {  
    compatible = "samsung,s3c6400-uart";                                
    reg = <0x7f005000 0x100>;                                           
    interrupt-parent = <&vic1>;                                         
    interrupts = <5>;                                                   
    clock-names = "uart", "clk_uart_baud2",                             
            "clk_uart_baud3";                                           
    clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,                
            <&clocks SCLK_UART>;                                        
    status = "disabled";                                                
};  

对应的解析代码如下,从前文描述应该清楚,期望回调函数返回该设备该状态该配置下的所有设置信息(可能只存在mux设置,也可能同时存在mux和conf设置),而上面的设备树里的uart0只有一个状态,default,对应的配置有两个,一个是uart0_data,一个是uart0_fctl,它们都是对配置节点的引用,配置节点都是pinctrl节点下的子节点,下面看代码吧:

static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,  
            struct device_node *np, struct pinctrl_map **maps,
            unsigned *nmaps)
{
...
    //检查该节点(第一次调用应该是uart0_data节点,第二次调用应该是uart0_fctl节点啦)
    //含有多少个自己定义的属性,包括:
    //{ "samsung,pin-pud", PINCFG_TYPE_PUD },
    //{ "samsung,pin-drv", PINCFG_TYPE_DRV },
    //{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
    //{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },    
    /* count the number of config options specfied in the node */
    for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
        if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
            cfg_cnt++;
    }
 
    /*
     * Find out the number of map entries to create. All the config options
     * can be accomadated into a single config map entry.
     */
    //如果有,那么说明需要继续后面的conf操作
    if (cfg_cnt)
        map_cnt = 1;
    //如果存在samsung,pin-function属性,那么不仅要做后面的操作,还需要额外做一些mux操作
    if (of_find_property(np, "samsung,pin-function", NULL))
        map_cnt++;
    if (!map_cnt) {
        dev_err(dev, "node %s does not have either config or function "
                "configurations\n", np->name);
        return -EINVAL;
    }
 
    //分配空间
    /* Allocate memory for pin-map entries */
    map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
    if (!map) {
        dev_err(dev, "could not alloc memory for pin-maps\n");
        return -ENOMEM;
    }
    *nmaps = 0;
 
    //从前面的分析应该清楚了组名的格式,下面就是根据配置节点名构建一个格式,然后到系统
    //里找对应的信息
    /*
     * Allocate memory for pin group name. The pin group name is derived
     * from the node name from which these map entries are be created.
     */
    gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
    if (!gname) {
        dev_err(dev, "failed to alloc memory for group name\n");
        goto free_map;
    }
    sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
 
    /*
     * don't have config options? then skip over to creating function
     * map entries.
     */
    if (!cfg_cnt)
        goto skip_cfgs;
 
    //根据前面获取的数量来分配配置节点空间
    /* Allocate memory for config entries */
    cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
    if (!cfg) {
        dev_err(dev, "failed to alloc memory for configs\n");
        goto free_gname;
    }
 
    //将已经定义的,属于自己定义列表里面的属性值提取出来,对应于我们这里,都是PIN_PULL_NONE
    /* Prepare a list of config settings */
    for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
        u32 value;
        if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
            cfg[cfg_cnt++] =
                PINCFG_PACK(pcfgs[idx].cfg_type, value);
    }
 
    //创建设置信息,如设置名字,类型,以及多少个conf操作,每一个conf值
    /* create the config map entry */
    map[*nmaps].data.configs.group_or_pin = gname;
    map[*nmaps].data.configs.configs = cfg;
    map[*nmaps].data.configs.num_configs = cfg_cnt;
    map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
    *nmaps += 1;
 
skip_cfgs:  
    /* create the function map entry */
    if (of_find_property(np, "samsung,pin-function", NULL)) {
        //如果存在samsung,pin-function属性,说明有mux的需求,处理它
        //这里是构建功能名,和前面初始化的时候一致
        fname = kzalloc(strlen(np->name) + FSUFFIX_LEN, GFP_KERNEL);
        if (!fname) {
            dev_err(dev, "failed to alloc memory for func name\n");
            goto free_cfg;
        }
        sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
 
        //填充mux操作需要的信息,如哪一个设备,哪一个功能
        map[*nmaps].data.mux.group = gname;
        map[*nmaps].data.mux.function = fname;
        map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
        *nmaps += 1;
    }
 
    *maps = map;
    return 0;
...
}

samsung_get_functions_count,它用于获取功能的总数量drvdata->nr_functions,前面已经分析过初始化这个的过程,所以这里就不再分析。samsung_pinmux_get_fname从已经初始化的数据结构里拿出对应索引上的name,name就是由配置节点名+-mux后缀构成。pinctrl_get的过程(pinmux_map_to_setting),会以map->data.mux.function为参数调用samsung_pinmux_get_fname获取该功能对应的索引来初始化setting->data.mux.func,然后在用samsung_pinmux_get_groups获取的组信息里,用前面解析出来的map[*nmaps].data.mux.group作为输入参数,获取该组的索引来初始化setting->data.mux.group。最后在pinctrl_select_state的时候,会通过上面的信息并结合最开始初始化的一些数据结构进行mux和conf操作。pinconf_map_to_setting的操作类似,不再重复。在pinctrl_select_state的时候samsung_pinmux_enable和samsung_pinconf_set有可能会触发,这里就不再继续分析了,但还是贴出代码吧!

/* enable a specified pinmux by writing to registers */
static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,  
                    unsigned group)
{
    samsung_pinmux_setup(pctldev, selector, group, true);
    return 0;
}
 
static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,  
                    unsigned group, bool enable)
{
    struct samsung_pinctrl_drv_data *drvdata;
    const unsigned int *pins;
    struct samsung_pin_bank *bank;
    void __iomem *reg;
    u32 mask, shift, data, pin_offset, cnt;
    unsigned long flags;
 
    drvdata = pinctrl_dev_get_drvdata(pctldev);
    pins = drvdata->pin_groups[group].pins;
 
    /*
     * for each pin in the pin group selected, program the correspoding pin
     * pin function number in the config register.
     */
    for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
        struct samsung_pin_bank_type *type;
 
        pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base,
                &reg, &pin_offset, &bank);
        type = bank->type;
        mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
        shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
        if (shift >= 32) {
            /* Some banks have two config registers */
            shift -= 32;
            reg += 4;
        }
 
        spin_lock_irqsave(&bank->slock, flags);
 
        data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
        data &= ~(mask << shift);
        if (enable)
            data |= drvdata->pin_groups[group].func << shift;
        writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
 
        spin_unlock_irqrestore(&bank->slock, flags);
    }
}

/* set the pin config settings for a specified pin */
static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,  
                unsigned long *configs, unsigned num_configs)
{
    int i, ret;
 
    for (i = 0; i < num_configs; i++) {
        ret = samsung_pinconf_rw(pctldev, pin, &configs[i], true);
        if (ret < 0)
            return ret;
    } /* for each config */
 
    return 0;
}
 
/* set or get the pin config settings for a specified pin */
static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,  
                unsigned long *config, bool set)
{
    struct samsung_pinctrl_drv_data *drvdata;
    struct samsung_pin_bank_type *type;
    struct samsung_pin_bank *bank;
    void __iomem *reg_base;
    enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
    u32 data, width, pin_offset, mask, shift;
    u32 cfg_value, cfg_reg;
    unsigned long flags;
 
    drvdata = pinctrl_dev_get_drvdata(pctldev);
    pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, &reg_base,
                    &pin_offset, &bank);
    type = bank->type;
 
    if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type])
        return -EINVAL;
 
    width = type->fld_width[cfg_type];
    cfg_reg = type->reg_offset[cfg_type];
 
    spin_lock_irqsave(&bank->slock, flags);
 
    mask = (1 << width) - 1;
    shift = pin_offset * width;
    data = readl(reg_base + cfg_reg);
 
    if (set) {
        cfg_value = PINCFG_UNPACK_VALUE(*config);
        data &= ~(mask << shift);
        data |= (cfg_value << shift);
        writel(data, reg_base + cfg_reg);
    } else {
        data >>= shift;
        data &= mask;
        *config = PINCFG_PACK(cfg_type, data);
    }
 
    spin_unlock_irqrestore(&bank->slock, flags);
 
    return 0;
}

/* set the pin config settings for a specified pin group */
static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev,  
            unsigned group, unsigned long *configs,
            unsigned num_configs)
{
    struct samsung_pinctrl_drv_data *drvdata;
    const unsigned int *pins;
    unsigned int cnt;
 
    drvdata = pinctrl_dev_get_drvdata(pctldev);
    pins = drvdata->pin_groups[group].pins;
 
    for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++)
        samsung_pinconf_set(pctldev, pins[cnt], configs, num_configs);
 
    return 0;
}

驱动工程师的角度

一般会用到的接口:
devm_pinctrl_get
pinctrl_lookup_state
pinctrl_select_state

操作gpio时,会用到的接口:
pinctrl_request_gpio
pinctrl_gpio_direction_input
pinctrl_gpio_direction_output

还有一些额外变体,懒得贴了

下面以gpio方式的api为例子继续分析,这样也好与文章最开始的gpio子系统结合起来理解!pinctrl_request_gpio在驱动里,主要有两类会用到它,一类是gpio子系统的实现者,即gpio-xxx.c那些文件,另一类是pinctrl的实现者,即pinctrl-xxx.c那些文件。它们在注册gpio chip时,将pinctrl_request_gpio作为gpio chip里request,这样间接将pinctrl操作交给gpio子系统自动完成。从gpio子系统分析可知,request的调用是在gpio_request或者gpiod_get间接触发。看一下pinctrl_request_gpio做了些什么:

int pinctrl_request_gpio(unsigned gpio)  
{
    struct pinctrl_dev *pctldev;
    struct pinctrl_gpio_range *range;
    int ret;
    int pin;
    //这里会通过gpio来取得该gpio对应的pctldev和range,还记得分析gpiochip_add时的
    //of_gpiochip_add_pin_range吧,这里就用到了它add的信息
    ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
    if (ret) {
        if (pinctrl_ready_for_gpio_range(gpio))
            ret = 0;
        return ret;
    }
 
    mutex_lock(&pctldev->mutex);
 
    /* Convert to the pin controllers number space */
    //有了range就好办了啦,它里面有gpio与pin号的对应关系,当然这关系是最开始从设备树里解析过来的
    pin = gpio_to_pin(range, gpio);
 
    //有了所有信息调用pinmux_request_gpio进一步request吧
    ret = pinmux_request_gpio(pctldev, range, pin, gpio);
 
    mutex_unlock(&pctldev->mutex);
 
    return ret;
}

继续pinmux_request_gpio:

int pinmux_request_gpio(struct pinctrl_dev *pctldev,  
            struct pinctrl_gpio_range *range,
            unsigned pin, unsigned gpio)
{
    const char *owner;
    int ret;
 
    /* Conjure some name stating what chip and pin this is taken by */
    owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
    if (!owner)
        return -EINVAL;
    //pin_request之前分析的时候有看到调用过,不过这次gpio的时候会传入range,导致它的
    //调用流程会有所不同,里面会触发pinmux_ops的gpio_request_enable回调,而不是request回调
    ret = pin_request(pctldev, pin, owner, range);
    if (ret < 0)
        kfree(owner);
 
    return ret;
}

最后看看设备驱动模型中pinctrl的影子,在bus_probe_device的时候,会调用device_attach,而device_attach里会调用__device_attach去attach,在匹配成功后,会调用driver_probe_device,它会导致really_probe的调用来进行驱动的probe,最终会导致pinctrl_bind_pins调用,这个函数会pinctrl_get并设置设备的初始状态,这个过程不需要驱动额外做任何事情,多么巧妙啊

int pinctrl_bind_pins(struct device *dev)  
{
    int ret;
 
    dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
    if (!dev->pins)
        return -ENOMEM;
 
    dev->pins->p = devm_pinctrl_get(dev);
    if (IS_ERR(dev->pins->p)) {
        dev_dbg(dev, "no pinctrl handle\n");
        ret = PTR_ERR(dev->pins->p);
        goto cleanup_alloc;
    }
 
    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
                    PINCTRL_STATE_DEFAULT);
    if (IS_ERR(dev->pins->default_state)) {
        dev_dbg(dev, "no default pinctrl state\n");
        ret = 0;
        goto cleanup_get;
    }
 
    ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
    if (ret) {
        dev_dbg(dev, "failed to activate default pinctrl state\n");
        goto cleanup_get;
    }
...
}

总结
  通过对gpio子系统和pinctrl子系统的分析,应该对这两个系统有了大致的概念了吧.gpio子系统让驱动工程师不用关心底层gpio chip的具体实现,让bsp工程师不用关心上层驱动工程师的使用方式。pinctrl子系统帮我们管理了pin信息,包括了pin的mux和conf,同时也透明的处理了与gpio子系统的关联以及设备模型的关联。

猜你喜欢

转载自blog.csdn.net/zhexingsunba/article/details/86520485