Linux pinctrl子系统学习

引用
1、Linux pinctrl子系统学习(一)
2、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子系统十分重要的。

3 Pinctrl子系统与consumer关系

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

  • 而在调用probe函数时,consumer就会调用pinctrl子系统里的回调函数,进行pin的申请。consumer的probe函数可以通过devm_pinctrl_get获得pinctrl的句柄,再自行调用pinctrl_select_state设置pin state。

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和其它功能同时使用。

3.1 设备树中的pin state

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

device-node-name{
   //定义该device自己的属性
    pinctrl-names= "sleep","default";
    pinctrl-0 = &xxx_state_sleep;
    pinctrl-1 = &xxx_state_default;
}
  • pinctrl-0 pinctrl-1 …表示了该设备的一个个状态,这里我们定义了两个pinctrl-0 pinctrl-1,数字0、1就是pinctrl-names中对应的字符串数组的index。其中pinctrl-0就是“sleep”状态,pinctrl-1就是“default”状态。而xxx_state_sleep,xxx_state_default,xxx_state_idel就是驱动具体的pin配置项了,需要在pinctrl设备节点处定义:
pinctrl@e01b0000{
        xxx_state_ default:xxx_ default {
                pins = "PC10", "PC11", "PC12", "PC13", "PC14", "PC15";
                function = "spi0";
           };
  };

3.2 pinctrl关键词表

3.2.1 pin命名表

pin的命名遵循芯片datasheet上的命名。
在dts中使用关键词“pins”(不同厂商不同)后跟名字数组来定义需要使用哪些pin,
如:

spi0_0: spi0 {
        pins = "PC10", "PC11", "PC12", "PC13", "PC14", "PC15";
        function = "spi0";
    }
3.2.2 MUX pin-function

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

pio0_0: gpio0 {
        pins = "PE0", "PE1", "PE2", "PE3";
        function = "gpio";
    }
    spi2_0: spi2 {
        pins = "PE0", "PE1", "PE2", "PE3";
        function = "spi2";
    };
3.2.3 Mux Group命名表

Mux Group的意义在于,将一些具有相同功能的pin作为一个集合group管理起来,方便consumer在需要配置多个管脚时,更加的简洁方便。
在dts中使用关键词“groups”后跟mux Group名字数组来定义需要使用哪些Mux group,如:

sd0 {
   groups = "mfp2_8_7";
   function = "sd0";
};
3.2.4 MUX Group-function

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

jtag {
    groups ="mfp2_8_7";
    function = "jtag";
};
3.2.5 Pin drive

对于某些pin可以设置pin的驱动能力(即供电能力),可以通过配置pin的等级对pin的驱动能力进行配置。
在dts中使用关键词“xxx,drive”后跟驱动能力等级来pin用哪种驱动能力,如:

i2c0_0: i2c0
 {
                pins = "PB0", "PB1";
                function = "i2c0";
                amicro,drive = <xxx_PINCTRL_12_MA>;
                amicro,pull = <xxx_PINCTRL_PULL_UP>;
};
3.2.6 Pin Pull Up/Down

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

i2c0_0: i2c0 {
                pins = "PB0", "PB1";
                function = "i2c0";
                amicro,drive = <xxx_PINCTRL_12_MA>;
                amicro,pull = <xxx_PINCTRL_PULL_UP>;
            };

表示将i2c0这两个pin上拉。
#define xxx_PINCTRL_NO_PULL 0
#define xxx_PINCTRL_PULL_UP 1
#define xxx_PINCTRL_PULL_DOWN 2

3.3 v3s的pin信息表

通过分析pinctrl-sunxi-v3s.c可以看到,v3s的所有pin信息被保存在了一个类型为自定义结构体xxx_pinctrl_desc的数组sunxi_v3s_pinctrl_data中。

static struct sunxi_pinctrl_desc sunxi_v3s_pinctrl_data = {
    .pins   = sunxi_v3s_pins,                /*单个pin的信息表*/
    .npins  = ARRAY_SIZE(sunxi_v3s_pins),  /*pin的总数*/
    .banks  = sunxi_v3s_banks,              /*banks的分组信息*/
    .nbanks = ARRAY_SIZE(sunxi_v3s_banks), /*banks分组数量*/
    .nirqs  = 16,                            /*外部中断线数量*/
};

单个pin的信息被集合到了一个类型为自定义结构体sunxi_desc_pin的sunxi_v3s_pins数组中去,分析结构体成员可以看看到底保存了什么内容。

struct sunxi_desc_pin {
struct pinctrl_pin_desc    pin;          /*pinctrl通用的结构体,保存pin的编号,名字,以及pinctrl设备私有数据,内核可以识别*/
struct sunxi_desc_function *functions;   /*am780自定义结构体,与功能有关*/
};
struct sunxi_desc_function {
        const char    *name;                   /*功能名字*/
        u8        muxval;                      /*datasheet上复用功能编号*/
};

可以看到,v3s的pin信息表所保存的数据包括
1、 pin的编号,如PA0的编号为0,PB0编号为16
2、 pin的名字,如“PA0”,“PE14”
3、 复用功能映射编号,从datasheet中获取,如PA0:gpio为0,adc1为2,spi0为3
这些信息的作用是与各自平台的pinctrl子系统的pinconf和pinmux这些底层回调函数有关,最终的目的是通过这些信息来操作相关寄存器(最底层操作)。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值