linux pinctrl


原文链接: linux pinctrl

原文地址 https://blog.csdn.net/u012830148/article/details/80609337
https://blog.csdn.net/qq_21353001/article/details/88751773
linux内核使用pinctrl子系统实现操作gpio输出高低电平的实现 https://blog.csdn.net/u010299133/article/details/88646412s

Pinctrl子系统之一了解基础概念:

1.Linux Pinctrl 子系统简介

在许多 soc 内部都包含有 pin 控制器,通过 pin 控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件方面,Linux 内核提供了 pinctrl 子系统,目的是为了统一各 soc 厂商的 pin 脚管理。

2.Linux Pinctrl 子系统提供的功能

    (1)管理系统中所有的可以控制的 pin,在系统初始化的时候,枚举所有可以控制的 pin,并标识这些 pin。

    (2)管理这些 pin 的复用( Multiplexing )。对于 SOC 而言,其引脚除了配置成普通的 GPIO 之外,若干个引脚还可以组成一个 pin group,行程特定的功能。pin control subsystem 要管理所有的 pin group。

    (3)配置这些 pin 的特性。例如使能或关闭引脚上的 pull-up、pull-down 电阻,配置引脚的 driver strength。

3.pinctrl 相关概念

普通 driver 调用 pin control subsystem 的主要目标有两个:

    (1)设定该设备的功能复用。

    (2)设定该 device 对应的 pin 的电气特性。

设定设备的功能复用需要了解两个概念,一个是 function,另外一个是 pin group。function 是功能抽象,对应一个 HW 逻辑 block,例如 SPI0. 虽然给定了具体的 function name,我们并不能确定其使用的 pins 的情况。例如为了设计灵活,芯片内部的 SPI0 的功能引出到 pin group{C6,C7,C8,C9},也可能引出的另外一个 pin group{C22.C23,C24,C25},但毫无疑问,这两个 pin group 不能同时 active,毕竟芯片内部的 SPI0 的逻辑功能电路只有一个,因此只有给出 function selector 以及 function 的 pin group selector 才能进行 function mux 的设定。

此外,由于电源管理的要求,某个 device 可能处于某个电源管理状态,例如 idle 或者 sleep,这时候,属于 device 的所有 pin 就会需要处于另外的状态。

综合上述的需求,就定义了 pin control state 的概念,也就是说设备可能处于非常多的状态中的一个,device driver 可以切换设备处于的状态。为了方便管理 pin control state 就有了 pin control state holder 的概念,用来管理一个设备的所有的 pin control 状态。

综上所述,普通的 driver 调用 pin control subsystem 的接口就是只有三个步骤:

(1)驱动加载或是运行时,获取 pin control state holder 的句柄

(2)设定 pin control 的状态

(3)驱动卸载或是退出时,释放 pin control state holder 的句柄

4. 与 GPIO 子系统的关系

上图显示了 gpio 子系统和 pinctrl 子系统之间的关系,即 pinctrl 子系统实际上也把 gpio 一起管理起来,所有的 gpio 操作也需要透过 pinctrl 子系统来完成,这样,如果一个 pin 已经被申请为 gpio,再通过 pinctrl 子系统申请为某个 function 时就会返回错误。

5. 与统一设备驱动模型的关系

从 "pinctrl 子系统关系图" 中得知,linux kernel 中的统一设备驱动模型也调用了 pinctrl 子系统的功能。linux kernel 中的统一设备驱动模型提供了 driver 和 device 的绑定机制,一旦匹配就会调用 driver 的 probe 函数。

而在调用票 probe 函数之前,驱动模型实际上已经申请过一次 pin 了,(前提是 dts 文件中该驱动节点中有定义名为 “default” 的 pinctrl 配置)。

驱动模型中调用 driver 的 probe 函数的地方:

static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = 0;
        ......
    /* If using pinctrl, bind pins now before probing */
    ret = pinctrl_bind_pins(dev); //对该device涉及的pin进行pin control相关设定
    if (ret)
        goto probe_failed;
    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
            __func__, dev_name(dev));
        goto probe_failed;
    }
    if (dev->bus->probe) { //下面是真正的probe过程。
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
    driver_bound(dev);
    ret = 1;
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
        .....
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = 0;
        ......
    /* If using pinctrl, bind pins now before probing */
    ret = pinctrl_bind_pins(dev); //对该device涉及的pin进行pin control相关设定
    if (ret)
        goto probe_failed;

    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
            __func__, dev_name(dev));
        goto probe_failed;
    }
    
    if (dev->bus->probe) { //下面是真正的probe过程。
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }
    
    driver_bound(dev);
    ret = 1;
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
        .....
}

pinctrl_bind_pins 函数定义:

#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
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); //获取pinctrl 
    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, //查找这个pin的default状态
                    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);  //设置pin的default状态
    if (ret) {
        dev_dbg(dev, "failed to activate default pinctrl state\n");
        goto cleanup_get;
    }
    return 0;
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); //获取pinctrl 
    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, //查找这个pin的default状态
                    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);  //设置pin的default状态
    if (ret) {
        dev_dbg(dev, "failed to activate default pinctrl state\n");
        goto cleanup_get;
    }
    
    return 0;

从驱动模型的实现中,不难看出在驱动 probe 前,就已经申请到 default 的 pin 配置了,当然 pinctrl 的计数已经 + 1 了。

Driver 的 probe 函数可以通过 devm_pinctrl_get 获得 pinctrl 的句柄,再自行调用 pinctrl_select_state 设置 pin state。

6. 与 device tree 的关系

在 device tree source 文件中可以在驱动节点中定义该驱动需要用到的 pin 配置。

device-node-name{
   //定义该device自己的属性
device-node-name{
   //定义该device自己的属性
    pinctrl-names= "sleep","default","idle";
    pinctrl-0 = &xxx_State_sleep;
    pinctrl-1 = &xxx_state_default;
    pinctrl-2 = &xxx_state_idle;
}
    pinctrl-names= "sleep","default","idle";
    pinctrl-0 = &xxx_State_sleep;
    pinctrl-1 = &xxx_state_default;
    pinctrl-2 = &xxx_state_idle;
}

pinctrl-0 pinctrl-1 pinctrl-2 ..... 表示了该设备的一个个状态,这里我们定义了三个 pinctrl-0 pinctrl-1 pinctrl-2,数字 0、1、2 就是 pinctrl-names 中对应的字符串数组的 index。其中 pinctrl-0 就是 “sleep” 状态,pinctrl-1 就是 “default” 状态,pinctrl-2 就是 “idle” 状态。而 xxx_state_sleep,xxx_state_default,xxx_state_idel 就是驱动具体的 pin 配置项了,需要在 pinctrl 设备节点处定义:

pinctrl@e01b0000{
      pinctrl-names = "default";
      pinctrl-0 = <&state_default>;
      state_default:pinctrl_default{
      };
      xxx_state_sleep:xxx_sleep{
         xxx_mfp{
            actions,groups = "fmp1_3_1spi0_ss","mfp1_3_1_spi0_miso","mfp1_5_4";
            actions,function = "spi0";
         };
       };
pinctrl@e01b0000{
      pinctrl-names = "default";
      pinctrl-0 = <&state_default>;

      state_default:pinctrl_default{
      };

      xxx_state_sleep:xxx_sleep{
         xxx_mfp{
            actions,groups = "fmp1_3_1spi0_ss","mfp1_3_1_spi0_miso","mfp1_5_4";
            actions,function = "spi0";
         };
       };

Pinctrl 子系统在加载时,会调用 pinctrl_dt_to_map 函数将 dts 文件中有关 pinctrl 的配置项解析出来,并根据 dts 各驱动节点对 pinctrl 的引用关系,将 phandle 挂到各个驱动的 device tree 子节点,各个驱动就可以通过自己的 dev 句柄获得 pinctrl 的配置了。

7. 与主控驱动的关系

在 kernel 的 machine driver 中,需要将主控的 pinctrl 相关硬件操作形象成一个符合 linux pinctrl 子系统规范的结构 pinctrl_desc,并调用 pinctrl_register 注册到 pinctrl 子系统中,这样 pinctrl 子系统就可以将上层行为转换成具体的硬件操作了:

struct pinctrl_desc {
    const char *name;
    struct pinctrl_pin_desc const *pins; //描述每个pin的信息为何。
    unsigned int npins; //表示可以控制多少个pin。pins和npins这两个成员就确定了一个pin controller所能控制的引脚信息。
    const struct pinctrl_ops *pctlops;//全局的控制函数
    const struct pinmux_ops *pmxops;//复用引脚的相关的操作函数
    const struct pinconf_ops *confops; //用来配置引脚特性(如pull-up/pull-down)
    struct module *owner;
};
struct pinctrl_desc {
    const char *name;
    struct pinctrl_pin_desc const *pins; //描述每个pin的信息为何。
    unsigned int npins; //表示可以控制多少个pin。pins和npins这两个成员就确定了一个pin controller所能控制的引脚信息。
    const struct pinctrl_ops *pctlops;//全局的控制函数
    const struct pinmux_ops *pmxops;//复用引脚的相关的操作函数
    const struct pinconf_ops *confops; //用来配置引脚特性(如pull-up/pull-down)
    struct module *owner;
};

pctlops 成员 callback 函数说明:

struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);  //该pin controller支持多少个pin group
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
                       unsigned selector);
    int (*get_group_pins) (struct pinctrl_dev *pctldev, //给定一个selector(index)获取指定的pin group中pin的信息(该pin group
                   unsigned selector,,                                  //包括多少个pin,每个pin的ID是什么)
                   const unsigned **pins,
                   unsigned *num_pins);
    void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
              unsigned offset);
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,   //分析一个pin configuration node并把分析结果保存成mapping table              
                   struct device_node *np_config,     //entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
                   struct pinctrl_map **map, unsigned *num_maps);
    void (*dt_free_map) (struct pinctrl_dev *pctldev,     //dt_node_to_map的逆函数。
                 struct pinctrl_map *map, unsigned num_maps);
};
struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);  //该pin controller支持多少个pin group
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
                       unsigned selector);
    int (*get_group_pins) (struct pinctrl_dev *pctldev, //给定一个selector(index)获取指定的pin group中pin的信息(该pin group
                   unsigned selector,,                                  //包括多少个pin,每个pin的ID是什么)
                   const unsigned **pins,
                   unsigned *num_pins);
    void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
              unsigned offset);
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,   //分析一个pin configuration node并把分析结果保存成mapping table              
                   struct device_node *np_config,     //entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
                   struct pinctrl_map **map, unsigned *num_maps);
    void (*dt_free_map) (struct pinctrl_dev *pctldev,     //dt_node_to_map的逆函数。
                 struct pinctrl_map *map, unsigned num_maps);
};

pmxops 成员 callback 函数说明:

struct pinmux_ops {
    int (*request) (struct pinctrl_dev *pctldev, unsigned offset);//pinctrl子系统进行具体的复用设定之前需要调用该函数,主要用来请底层的driver判断某个引脚的复用设定是否ok
    int (*free) (struct pinctrl_dev *pctldev, unsigned offset);//request的逆函数,调用request函数请求占用了某些pin的资源,调用free可以释放这些资源。
    int (*get_functions_count) (struct pinctrl_dev *pctldev);//返回pin controller支持的function的数目。
    const char *(*get_function_name) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定function的name。
                      unsigned selector);
    int (*get_function_groups) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的function 的pin group信息。
                  unsigned selector,
                  const char * const **groups,
                  unsigned * const num_groups);
    int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, //enable一个function当然要给我出function selector和pin group的selector
               unsigned group_selector);
    void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector,//enable的逆函数
             unsigned group_selector);
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,//request并且enable一个单独的gpio pin
                    struct pinctrl_gpio_range *range,
                    unsigned offset);
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev,//gpio_request_free的逆函数
                   struct pinctrl_gpio_range *range,
                   unsigned offset);
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev,//设定gpio方向的回调函数
                   struct pinctrl_gpio_range *range,
                   unsigned offset,
                   bool input);
};
struct pinmux_ops {
    int (*request) (struct pinctrl_dev *pctldev, unsigned offset);//pinctrl子系统进行具体的复用设定之前需要调用该函数,主要用来请底层的driver判断某个引脚的复用设定是否ok
    int (*free) (struct pinctrl_dev *pctldev, unsigned offset);//request的逆函数,调用request函数请求占用了某些pin的资源,调用free可以释放这些资源。
    int (*get_functions_count) (struct pinctrl_dev *pctldev);//返回pin controller支持的function的数目。
    const char *(*get_function_name) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定function的name。
                      unsigned selector);
    int (*get_function_groups) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的function 的pin group信息。
                  unsigned selector,
                  const char * const **groups,
                  unsigned * const num_groups);
    int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, //enable一个function当然要给我出function selector和pin group的selector
               unsigned group_selector);
    void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector,//enable的逆函数
             unsigned group_selector);
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,//request并且enable一个单独的gpio pin
                    struct pinctrl_gpio_range *range,
                    unsigned offset);
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev,//gpio_request_free的逆函数
                   struct pinctrl_gpio_range *range,
                   unsigned offset);
    int (*gpio_set_direction) (struct pinctrl_dev *pctldev,//设定gpio方向的回调函数
                   struct pinctrl_gpio_range *range,
                   unsigned offset,
                   bool input);
};

confops 成员的 callback 函数说明:

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
    bool is_generic;
#endif
    int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
                   unsigned pin,
                   unsigned long *config);
    int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
                   unsigned pin,
                   unsigned long config);
    int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev,
                    unsigned pin,
                    unsigned long *configs,
                    unsigned num_configs);
    int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
                     unsigned selector,
                     unsigned long *config);
    int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
                     unsigned selector,
                     unsigned long config);
    int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev,
                      unsigned selector,
                      unsigned long *configs,
                      unsigned num_configs);
    int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, //debug接口
                       const char *arg,
                       unsigned long *config);
    void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,  //debug接口
                     struct seq_file *s,
                     unsigned offset);
    void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, //debug接口
                       struct seq_file *s,
                       unsigned selector);
    void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
                        struct seq_file *s,
                        unsigned long config);
};
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
    bool is_generic;
#endif
    int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
                   unsigned pin,
                   unsigned long *config);
    int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
                   unsigned pin,
                   unsigned long config);
    int (*pin_config_set_bulk) (struct pinctrl_dev *pctldev,
                    unsigned pin,
                    unsigned long *configs,
                    unsigned num_configs);
    int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
                     unsigned selector,
                     unsigned long *config);
    int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
                     unsigned selector,
                     unsigned long config);
    int (*pin_config_group_set_bulk) (struct pinctrl_dev *pctldev,
                      unsigned selector,
                      unsigned long *configs,
                      unsigned num_configs);
    int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, //debug接口
                       const char *arg,
                       unsigned long *config);
    void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,  //debug接口
                     struct seq_file *s,
                     unsigned offset);
    void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, //debug接口
                       struct seq_file *s,
                       unsigned selector);
    void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
                        struct seq_file *s,
                        unsigned long config);
};

7.dts 文件中的 pinctrl 关键词表

7.1 pin 命名表

    pin 的命名遵循 IC spec 上的命名,以下命名表以每个 pin 默认的功能命名,但实际使用中各个 pin 的功能会随着配置发生变化。

    在 dts 中使用关键词 “actions,pins”(不同厂商不同,这是炬芯的)后跟名字数组来定义需要使用哪些 pin,如:

i2c0_over_uart0_pull_up{
        actions,pins = “P_UART0_RX”,“P_UART0_TX”;
        actions,pull = <2>;
}
i2c0_over_uart0_pull_up{
        actions,pins = “P_UART0_RX”,“P_UART0_TX”;
        actions,pull = <2>;
}

7.2 MUX Group-Function 表

  7.2.1 Mux Group 命名表

    pin group 按照寄存器的名字和 bit 来命名。比如:MFP_CTL1 bit22:21, 定义了 10 个 pin 的 mux:P_LVDS_OEP,P_LVDS_OEN,P_LVDS_ODP,P_LVDS_ODN,P_LVDS_OCP,P_LVDS_OCN,P_LVDS_OBP,P_LVDS_OBN,P_LVDS_OAP,P_LVDS_OAN。该 pin group 的名字为 mfp1_22_21.

    有些 mfp 寄存器的 cell 中,设置某一个值会将多个 pin 配置为不同的功能。那么这个 cell 中的 pin 就不能归为同一个 pin group。需按情况拆解开,那么拆解开的 pin group 的名字还会加上一些后缀。

    比如:根据 IC SPEC,mfp1 bit[31:29] 可控制 3 根 pin:P_KS_IN0,P_KS_IN1,P_KS_IN2. 该 cell 的 0x11 选项会将这 2 跟 pin 分别定义为 pwm0,pwm1,pwm4.

那么就将其分拆为 3 个 group:分别为 “mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in2”、“mfp1_31_29_ks_in0”。这几个 mfp cell 分割以后,他们之间仍然存在硬件上的互斥关系,比如将 mfp1_31_29_ks_in2 的 function 选为 pwm0,mfp1_31_29_ks_in1 的 function 选为 pwm1,可以工作。但是将 mfp1_31_29_ks_in2 的 function 选为 pwm0,mfp1_31_29_ks_in1 的 function 选为 jtag,就会返回错误。

    在 pin group 的命名表中还会看到 “xxx_dummy” 的命名,这种 pin group 在 pinmux 设置中可能会用到。这些 pin group 只有一种 mux 功能,所以在 mfp 寄存器中不会表示,但是这些 pin 可能和 gpio 复用,申请这些 pin 有助于发现它和 gpio 存在的潜在复用错误。

在 dts 中使用关键词 “actions,groups” 后跟 mux Group 名字数组来定义需要使用哪些 Mux group,如:

sd0_mfp_cmd_clk{

     actions,groups = "mfp2_8_7","mfp2_6_5";

     actions,function = "sd0";

};

7.2.2MUX Group-function

    Mux Group 所控制的 pin,可以通过设置 Mux Function 的方式改变 IC 内部连通性,使得同一组 pin 用作不同的功能,例如:

sd0_mfp_cmd_clk{
    actions,groups ="mfp2_8_7","mfp2_6_5";
    actions,function = "jtag";
};
sd0_mfp_cmd_clk{
    actions,groups ="mfp2_8_7","mfp2_6_5";
    actions,function = "jtag";
};

7.2.3 Drive Group

paddrv 使用的配置值完全等同于 IC spec 中关于 PAD_DRVx 寄存器的定义。

对于某些 pin 可以设置 pin 的驱动能力(即供电能力),可以通过配置 driver group 的等级对 pin 的驱动能力进行配置。

在 dts 中使用关键词 “actions,paddrv” 后跟驱动能力等级来定义 “actions,groups” 指定 drive group 所代表的 pin 组使用哪种驱动能力,如:

sd0_d0_d3_cmd_clk_paddrv{
 actions,groups="paddrv1_19_18","paddrv1_17_16";
 actions,paddrv = "1" /*level 1, range :0 ~ 3*/
}
sd0_d0_d3_cmd_clk_paddrv{
 actions,groups="paddrv1_19_18","paddrv1_17_16";
 actions,paddrv = "1" /*level 1, range :0 ~ 3*/
}

表示 “paddrv1_19_18” 所代表的 P_SD0_CMD 和 paddrv1_17_16 所代表的 P_SD0_CLK, 使用驱动能力 1 来提升数据传输稳定性。

7.2.4 Pin Pull Up/Down

在 dts 中使用关键词 “actions,pull” 后跟上下拉数据定义 “actions,pin” 指定的 pin 组使用哪种上下拉,如:

sd0_pull_d0_d3_cmd{
   actions,pins = "P_SD0_CMD","P_SD0_CLK";
   actions,pull = <1>; 
};
sd0_pull_d0_d3_cmd{
   actions,pins = "P_SD0_CMD","P_SD0_CLK";
   actions,pull = <1>; 
};

表示将 P_SD0_CMD 和 P_SD0_CLK 这两个 pin 下拉。

actions,pull = <0>, 表示上下拉功能关闭

actions,pull = <1>, 表示下拉

actions,pull = <2>, 表示上拉

7.2.5 GPIO-PIN

pin 除了可以复用作各种功能外,还可以配置成 GPIO 使用,pinctrl 子系统将 GPIO 子系统也管理起来了,因此申请 GPIO 的时候会去检查该 gpio 所对应的 pin 是否已经被其他驱动申请作其他功能了。如果已经被申请则申请时会报错,反之亦然。

8 使用 dts 描述 pinctrl 配置

8.1dts 中 pinctrl 配置方法

所有的 pinctrl-state 都定义在 pin controller device 节点中:

在 kernel/arch/arm64/boot/dts/s700_pinctrl.dtsi 中

/ {
        pinctrl@e01b0000 {
                compatible = "actions,s700-pinctrl";
                reg = <0 0xe01b0000 0 0x1000>;
                pinctrl-names = "default";
                pinctrl-0 = <&state_default>;
                clocks = <&clock CLK_GPIO>;
                clock-names = "mfp";
                state_default: pinctrl_default {
                };
/ {

        pinctrl@e01b0000 {
                compatible = "actions,s700-pinctrl";
                reg = <0 0xe01b0000 0 0x1000>;
    
                pinctrl-names = "default";
                pinctrl-0 = <&state_default>;
                clocks = <&clock CLK_GPIO>;
                clock-names = "mfp";
    
                state_default: pinctrl_default {
                };

而个驱动引用定义在 pin controller device 节点中的子节点,即 pinctrl-state 节点。

各驱动使用如下方法引用 pinctrl-state 节点:

pinctrl-N:描述该设备需要使用的 pin 的一个状态(pin state),相当于上述的 state。N 的数值必须从 0 开始顺序递增。pinctrl-N 属性的值为 pin configuration nodes 的 phandle。pinctrl-N 属性引用的 pin configuration nodes 必须是 pin controller device node 的直接子节点。

pinctrl-names:为每个 pin state 定义一个名字。每个名字顺序对应一个 pin state。比如 pinctrl-0 的名字为 “default”,pinctrl-1 的名字对应“idle”。若不能指定 pinctrl-names 属性,这样的话,pin state 的名字就是该属性的“N” 字符。比如 pinctrl-0 的名字为字符“0”

mmc@e0330000{
      pinctrl-names = "pinctrl_mmc0","share_uart2_5";  
      pinctrl-0 = <&mm0_pinctrl_state>;
      pinctrl-1 = <&mmc_share_uart_state>;
}
mmc@e0330000{
      pinctrl-names = "pinctrl_mmc0","share_uart2_5";  
      pinctrl-0 = <&mm0_pinctrl_state>;
      pinctrl-1 = <&mmc_share_uart_state>;
}

上面的例子中,mmc 驱动定义的 pinctrl-state 有两个,其中 mmc0_state_default 是定义 pin controller device  node 下的直接子节点,表示 sd0 的 pin group 作为 sd 功能使用,而 mmc_share_uart_state 则表示作为 serial 功能使用,其中 mmc_state_default 对应的 pinctrl-names 属性为 default,而 mmc_share_uart_state 对应的 pinctrl-names 属性为 share_uart2_5。

8.2 pin mapping database 的建立

pin controller device 节点配置好后,内核是如何获取到 pin mapping database 并使用的呢?

通过查看 pinctrl 子系统代码,了解到 pin mapping database 建立有两种情况:

一种情况是主控驱动调用 pinctrl_register 注册 machine 的 struct pinctrl_desc 结构到 pinctrl 子系统时,由 pinctrl_register 调用 pinctrl_get 创建。

    另一种就是各驱动在加载时由统一设备驱动模型在 driver 和 device 绑定时调用 devm_pinctrl_get 转而调用 pinctrl_get 创建。

下一篇中通过代码来具体分析 pinctrl_get。

###Linux内核中的pinctrl子系统应用实例

由于近期在做一个项目用到了pinctrl子系统,但是对pinctrl子系统了解又不是很多,所以遇到了麻烦,但是找度娘发现很少有同行对pinctrl的具体用法做出说明,所以只能自己去搞了,在经过一段时间对Linux内核源码的折腾,最终搞定,并将我所应用的实例给展示一下,希望对大家有所帮助。

关于pinctrl是什么,为什么要用pinctrl,源码深度剖析我在这就不赘述了,有位博友总结的非常好,大家可以参考http://www.wowotech.net/sort/gpio_subsystem。

下面我介绍一下如何去使用内核中的pinctrl子系统以device tree设备树为例,当你需要控制某些pin的时候,你首先要在devicetree中去按照pinctrl的规则去描述它,然后才能在driver中去使用:

案例1:

xxx这个设备要用到gpg0_1这个pin的TE_DECON_INT功能,并分别将这两个状态取了个名字turnon_tes和turnoff_tes.这个名字是随便起的。重点是看pinctrl-0和pinctrl-1,根据示例,它们分别引用了disp_teson和disp_tesoff这两个节点。

xxx {

....
pinctrl-names = "turnon_tes", "turnoff_tes";
pinctrl-0 = <&disp_teson>;
pinctrl-1 = <&disp_tesoff>;

};
两个重要的属性必须有:pins 和 pin-function分别是pin的名字和要把pin配置成什么功能,还有gpg0属于pinctrl_2,所以这个地方引用的是pinctrl_2而不是其他。

&disp_teson_pinctrl { //#define disp_teson_pinctrl pinctrl_2

disp_teson: disp_teson {
    samsung,pins = disp_teson_pin;          //#define disp_teson_pin    "gpg0-1"
    samsung,pin-function = <disp_teson_con>;//#define disp_teson_con    2 -- 对应0x2 = TEDECON_INT
};

};
&disp_tesoff_pinctrl {

disp_tesoff: disp_tesoff {
    samsung,pins = disp_tesoff_pin;          //#define disp_teson_pin        "gpg0-1"
    samsung,pin-function = <disp_tesoff_con>;//#define disp_teson_con         0
};

};

那么driver如何去操作这个pin呢?首先需要大家熟悉几个内核的API:

  1. 获取一个pinctrl句柄,参数是dev是包含这个pin的device结构体即xxx这个设备的device


/**

  • struct devm_pinctrl_get() - Resource managed pinctrl_get()
  • @dev: the device to obtain the handle for
    *
  • If there is a need to explicitly destroy the returned struct pinctrl,
  • devm_pinctrl_put() should be used, rather than plain pinctrl_put().
    */
    struct pinctrl *devm_pinctrl_get(struct device *dev)


  1. 获取这个pin对应pin_state(引脚状态-turnon_tes/turnoff_tes)
    /**
    • pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
    • @p: the pinctrl handle to retrieve the state from
    • @name: the state name to retrieve
      */
      struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)
  2. 设置引脚为为某个stata -- turnon_tes/turnoff_tes

/**

  • pinctrl_select_state() - select/activate/program a pinctrl state to HW
  • @p: the pinctrl handle for the device that requests configuration
  • @state: the state handle to select/activate/program
    */
    int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
    具体操作:


/* 获取pin control state holder 的句柄 /
pinctrl = devm_pinctrl_get(dev);
/
得到名字为turnon_tes和turnoff_tes对应的pin state */
struct pinctrl_state * turnon_tes = pinctrl_lookup_state(pinctrl, "turnon_tes");
struct pinctrl_state * turnoff_tes = pinctrl_lookup_state(pinctrl, "turnoff_tes");
/* 设置名字为turnon_tes这个pinctrl对应引脚(gpg0-1)的pin state,即gpg0_1对应的寄存器位域设置为2 */
pinctrl_select_state(pinctrl, turnon_tes)。
经过以上操作,gpg_1引脚对应的con寄存器的对应的位域被配置成2,即0x2 = TE_DECON_INT功能。同意,根据此方法也可以设置turnoff_tes的状态。

案例2 -- 一个背光灯device需要使用pwm的输出pin:
device tree:
背光系统中要用到gpd2_4这个pin的TOUT_0功能和gpd4_3这个pin的输出功能并输出1,需要在backlight这个node中做以下描述,这两个pin只有一个状态(pwm-on),同样,这个名字也是可以随便起的。bl_pwm_ctrl和bl_pwm_en_ctrl分别是对这两个pin的描述。

backlight {

...
...
pinctrl-names = "pwm-on";
pinctrl-0 = <&bl_pwm_ctrl @bl_pwm_en_ctrl>;

};
/* 这个和上面一样,就不多说了 */
&bl_pwm_ctrl_pinctrl{ //#define bl_pwm_ctrl_pinctrl pinctrl_2

bl_pwm_ctrl: bl_pwm_ctrl {
    samsung,pins = bl_pwm_ctrl_pin;           //#define bl_pwm_ctrl_pin   "gpd2-4"
    samsung,pin-function = <bl_pwm_ctrl_con>; //#define bl_pwm_ctrl_con     2
    samsung,pin-pud = <bl_pwm_ctrl_pull>;     //#define bl_pwm_ctrl_pull        3
    samsung,pin-drv = <bl_pwm_ctrl_drv>;      //#define bl_pwm_ctrl_drv     0
};

};
这个描述比上面多了个pin-val,因为这个引脚不仅要配置成输出功能,还要输出1,所以pin-val = 1。
&bl_pwm_en_ctrl_pinctrl{

bl_pwm_en_ctrl: bl_pwm_en_ctrl {
    samsung,pins = bl_pwm_en_ctrl_pin;           //#define bl_pwm_en_ctrl_pin     "gpd4-3"
    samsung,pin-function = <bl_pwm_en_ctrl_con>; //#define bl_pwm_en_ctrl_con      1
    samsung,pin-val = <1>;
    samsung,pin-pud = <bl_pwm_en_ctrl_pull>;
    samsung,pin-drv = <bl_pwm_en_ctrl_drv>;
};

};
driver的操作:
在backlight的driver的probe中:

struct pinctrl * p = devm_pinctrl_get(&pdev->dev);
struct pinctrl_state * default_state = pinctrl_lookup_state(p, "pwm-on");
pinctrl_select_state(p, default_state);

执行完以上操作,可以发现gpd2_4引脚被配置成了TOUT_0功能,gpd4_3引脚被配置成为了输出功能,并且输出1(高电平)。
以上就是pinctrl子系统的应用实例。如果有解释不太正确的地方请指教。
————————————————
版权声明:本文为CSDN博主「hanp_linux」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hanp_linux/article/details/72818437

`