Linux pinctrl子系统学习(一)

Linux pinctrl子系统学习(一)

1 Pinctrl子系统介绍

  • 众所周知,ARM SoC提供了十分丰富的硬件接口,而接口物理上的表现就是一个个的pin(或者叫做pad, finger等)。为了实现丰富的硬件功能,SoC的pin需要实现复用功能,即单独的pin需要提供不同功能,例如,pin0既可以作为GPIO,可以也用于i2c的SCL,通过pin相关的复用寄存器来切换不同的功能。除此之外,软件还可以通过寄存器配置pin相关的电气特性,例如,上拉/下拉、驱动能力、开漏等。

  • Linux kernel 3.0之前的内核,对于pin的功能配置都是通过目标板的配置文件(arch/arm/mach-*)来初始化的,这种配置方式比较繁琐,十分容易出现问题(例如,pin的功能配置冲突)。所以,Linux kernel 3.0之后,实现了DT的板级配置信息管理机制,大大改善了对于pin的配置方式,随之一起实现的就是pinctrl子系统。

  • pinctrl子系统主要负责以下功能:
    1、通过DTS配置的pin的功能;
    2、对于pin实现复用功能;
    3、配置pin的电器特性,例如,上拉/下拉、驱动能力、开漏等。;

  • 可见,pinctrl子系统地位相当于kernel全局的pin管理中心,kernel中所有需要pin资源的驱动、子系统都需要通过pinctrl子系统来申请、配置、释放。对于pin的操作来说,pinctrl子系统十分重要的。

2 Pinctrl子系统的框架

在这里插入图片描述

  • 驱动分层最终目的:消除复杂性。框架如上图所示,可以分为三层,分别为最顶层consumer,即各种需要使用到pin的一些功能外设,如spi,i2c,sdio等等。中间层为pinctrl-core,是完成底层驱动与上层之间的连接。而pinctrl-driver为最底层,直接控制相关寄存器。

2.1 pinctrl-core

  • pinctrl-core抽象层主要的功能就是为Consumer提供访问pin的能力,即driver配置pin复用能、配置引脚的电气特性。

  • 其实,对于pinctrl-core抽象层如何为上层提供服务完全不需要关心,此过程由Linux内核完成。那么,pinctrl-core如何完成与pinctrl-driver连接呢? pinctrl-core与pinctrl-driver是通过pin controller descriptor进行通信的。该结构定义如下:

      /**
       * struct pinctrl_desc - pin controller descriptor, register this to pin
       * control subsystem
       * @name: name for the pin controller
       * @pins: an array of pin descriptors describing all the pins handled by
       *  this pin controller
       * @npins: number of descriptors in the array, usually just ARRAY_SIZE()
       *  of the pins field above
       * @pctlops: pin control operation vtable, to support global concepts like
       *  grouping of pins, this is optional.
       * @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
       * @confops: pin config operations vtable, if you support pin configuration in
       *  your driver
       * @owner: module providing the pin controller, used for refcounting
       */
      struct pinctrl_desc {
      
          /*pinctrl-driver属性*/
          const char *name;		
          const struct pinctrl_pin_desc *pins;                                                                                                                                                                            
          unsigned int npins;
      
          /*pinctrl-drive抽象接口*/
          const struct pinctrl_ops *pctlops;
          const struct pinmux_ops  *pmxops;
          const struct pinconf_ops *confops;
          struct module *owner;
      };
    

2.2 pinctrl-driver

  • 实际上在bsp层,我们需要做的就是定义一个pinctrl_desc结构体,并将此结构体注册到pinctrl子系统中去,上层即可连接到我们所写的bsp层驱动。
  • 在编写一个pinctrl_desc结构体之前,首先要清楚一下几个概念:pin,pin groups,pin configuration和pin multiplexing。

2.2.1 pin

  • kernel的pin controller子系统要想管理好系统的pin资源,第一个要搞明白的问题就是:系统中到底有多少个pin?用软件语言来表述就是:要把系统中所有的pin描述出来,并建立索引。这由上面struct pinctrl_desc结构中pins和npins来完成。

  • 对pinctrl core来说,它只关心系统中有多少个pin,并使用自然数为这些pin编号,后续的操作,都是以这些编号为操作对象。至于编号怎样和具体的pin对应上,完全是pinctrl driver自己的事情。
    因此,pinctrl driver需要根据实际情况,将系统中所有的pin组织成一个struct pinctrl_pin_desc类型的数组,该类型的定义为:

      /** 
      * struct pinctrl_pin_desc - boards/machines provide information on their 
        * pins, pads or other muxable units in this struct 
        * @number: unique pin number from the global pin number space 
        * @name: a name for this pin 
        * @drv_data: driver-defined per-pin data. pinctrl core does not touch this 
        */ 
      struct pinctrl_pin_desc { 
              unsigned number; 
              const char *name; 
              void *drv_data; 
      };
    

2.2.2 Pin groups

  • 在SoC系统中,有时需要将很多pin组合在一起,以实现特定的功能,例如SPI接口、I2C接口等。因此pin controller需要以group为单位,访问、控制多个pin,这就是pin groups。相应地,pinctrl-driver需要提供一些机制,来获取系统中到底有多少groups、每个groups包含哪些pins、等等。

  • 在struct pinctrl_ops中抽象出回调函数,用来获取pin groups相关信息,如下:

      struct pinctrl_ops { 
              int (*get_groups_count) (struct pinctrl_dev *pctldev); 
              const char *(*get_group_name) (struct pinctrl_dev *pctldev, 
                                              unsigned selector); 
              int (*get_group_pins) (struct pinctrl_dev *pctldev, 
                                     unsigned selector, 
                                     const unsigned **pins, 
                                     unsigned *num_pins); 
              void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, 
                                unsigned offset); 
              int (*dt_node_to_map) (struct pinctrl_dev *pctldev, 
                                     struct device_node *np_config, 
                                     struct pinctrl_map **map, unsigned *num_maps); 
              void (*dt_free_map) (struct pinctrl_dev *pctldev, 
                                   struct pinctrl_map *map, unsigned num_maps); 
      };
    
  • get_groups_count,获取系统中pin groups的个数,后续的操作,将以相应的索引为单位(类似数组的下标,个数为数组的大小)。

  • get_group_name,获取指定group(由索引selector指定)的名称。

  • get_group_pins,获取指定group的所有pins(由索引selector指定),结果保存在pins(指针数组)和num_pins(指针)中。

  • dt_node_to_map用于将device tree中的pin state信息转换为pin map,consumer driver在需要的时候,可以调用pinctrl_get/devm_pinctrl_get接口。pinctrl subsystem在pinctrl get的过程中,解析consumer device的dts node,找到相应的pin state,进行调用pinctrl driver提供的dt_node_to_map API,解析pin state并转换为pin map。

注:pin state

  • consumer在某一状态下(如工作状态、休眠状态、等等),所使用的pin(pin group)、的function和configuration,是唯一确定的。就是说pin(pin group)以及相应的function和configuration的组合,可以确定一个设备的一个“状态”。consumer可以调用pinctrl subsystem提供的API(例如pinctrl_select_state),使自己的某个pin state生效。pinctrl subsystem进而调用pinctrl driver提供的各种回调函数,配置pin controller的硬件。
    在设备树下定义的pin_state:
    在这里插入图片描述
    在这里插入图片描述

2.2.3 Pin configuration

SoC中的管脚有些属性可以配置,例如上拉、下拉、高阻、驱动能力等。pinctrl subsystem使用pin configuration来封装这些功能,具体体现在struct pinconf_ops数据结构中,如下:

struct pinconf_ops { 
#ifdef CONFIG_GENERIC_PINCONF 
         bool is_generic; 
#endif 
        int (*pin_config_get) (struct pinctrl_dev *pctldev, 
                               unsigned pin, 
                               unsigned long *config); 
        int (*pin_config_set) (struct pinctrl_dev *pctldev, 
                               unsigned pin, 
                                unsigned long *configs, 
                                unsigned num_configs); 
        int (*pin_config_group_get) (struct pinctrl_dev *pctldev, 
                                      unsigned selector, 
                                      unsigned long *config); 
        int (*pin_config_group_set) (struct pinctrl_dev *pctldev, 
                                      unsigned selector, 
                                      unsigned long *configs, 
                                     unsigned num_configs); 
        int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, 
                                            const char *arg, 
                                            unsigned long *config); 
        void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev, 
                                      struct seq_file *s, 
                                      unsigned offset); 
        void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, 
                                            struct seq_file *s, 
                                            unsigned selector); 
        void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev, 
                                             struct seq_file *s, 
                                             unsigned long config); 
};
  • pin_config_get,获取指定pin当前配置,保存在config指针中(配置的具体含义,只有pinctrl driver自己知道,下同)。
  • pin_config_set,设置指定pin的配置(可以同时配置多个config,具体意义要由相应pinctrl driver解释)。
  • pin_config_group_get、pin_config_group_set,获取或者设置指定pin group的配置项。剩下的是一些debug用的api。

2.2.4 Pin multiplexing

  • 为了照顾不同类型的产品、不同的应用场景,SoC中的很多管脚可以配置为不同的功能,例如A2和B5两个管脚,既可以当作普通的GPIO使用,又可以配置为I2C0的的SCL和SDA,也可以配置为UART5的TX和RX,这称作管脚的复用(pin multiplexing,简称为pinmux)。kernel pinctrl subsystem使用struct pinmux_ops来抽象pinmux有关的操作,如下:

      struct pinmux_ops { 
               int (*request) (struct pinctrl_dev *pctldev, unsigned offset); 
              int (*free) (struct pinctrl_dev *pctldev, unsigned offset); 
              int (*get_functions_count) (struct pinctrl_dev *pctldev); 
              const char *(*get_function_name) (struct pinctrl_dev *pctldev, 
                                                 unsigned selector); 
              int (*get_function_groups) (struct pinctrl_dev *pctldev, 
                                        unsigned selector, 
                                        const char * const **groups, 
                                        unsigned *num_groups); 
              int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector, 
                              unsigned group_selector); 
              int (*gpio_request_enable) (struct pinctrl_dev *pctldev, 
                                          struct pinctrl_gpio_range *range, 
                                           unsigned offset); 
              void (*gpio_disable_free) (struct pinctrl_dev *pctldev, 
                                         struct pinctrl_gpio_range *range, 
                                          unsigned offset); 
              int (*gpio_set_direction) (struct pinctrl_dev *pctldev, 
                                         struct pinctrl_gpio_range *range, 
                                          unsigned offset, 
                                         bool input); 
              bool strict; 
      };
    
  • get_functions_count,获取系统中function的个数。

  • get_function_name,获取指定function的名称。

  • get_function_groups,获取指定function所占用的pin group(可以有多个)。

  • set_mux,将指定的pin group(group_selector)设置为指定的function(func_selector)。

  • request,检查某个pin是否已作它用,用于管脚复用时的互斥(避免多个功能同时使用某个pin而不知道,导致奇怪的错误)。

  • free,request的反操作。

  • strict,为true时,说明该pin controller不允许某个pin作为gpio和其它功能同时使用。

发布了47 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u013836909/article/details/94207781