jinsheng

  • 57 主题
  • 122 帖子
  • 1221 积分
  • 身份:LV4 高级技术员
  • E币:1079

【转载】Pinctrl子系统之一了解基础概念

2020-9-9 22:07:27 显示全部楼层
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",


  •         .....


  • }

pinctrl_bind_pins函数定义:



  • #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;

从驱动模型的实现中,不难看出在驱动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自己的属性



  •     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子系统在加载时,会调用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;


  • };

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


  • };

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


  • };

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


  • };

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


  • }

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


  • };

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*/


  • }

表示“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>;


  • };

表示将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 {


  •                 };




而个驱动引用定义在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驱动定义的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。

转载于:https://blog.csdn.net/u012830148/article/details/80609337


最新评论

楼层直达:
我要评论
0
10
广告
关闭 热点推荐上一条 /6 下一条
快速回复 返回列表