LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

LED/GPIO/pinctrl子系统(3):pinctrl子系统概述

关键词:pinctrl、pinmux、pinconf等。

 

pinctrl可以归结为两类设置:一类是功能选择,比如是普通的GPIO或者专用功能引脚;另一类是上拉、下拉、驱动能力、速率等配置。

pinctrl子系统主要完成如下工作:

  • 初始化pin contrroller,并为其每个pin命名和枚举。
  • 处理pinctrl的引脚复用功能。
  • 处理引脚的配置。

下面首先介绍Linux下pinctrl框架和配置;然后分别介绍重要数据结构和API、pinctrl相关DTS、Pin Controller驱动、Client Device驱动;最后介绍pinctrl相关调试接口。

1 pinctrl框架

kernel中pinctrl子系统可以分为三部分:

  • pinctrl core负责pin controller注册以及pin数据维护,并为client device提供pin设置功能。
  • pin controller是pinctrl子系统的provider,包括pin controller的初始化、注册、提供pin数据。
  • client device是使用到pin的设备,是pinctrol子系统的consumer。需要设置不同状态及其对应的pin设置。

1.1 pinctrl debugfs初始化

在启动启动早起创建了pinctrl相关debugfs:

core_initcall(pinctrl_init)
    pinctrl_init_debugfs--创建pinctrl-devices、pinctrl-maps、pinctrl-handles。

当使能pinctrl设备时,创建特定设备的debugfs:

pinctrl_enable
  pinctrl_init_device_debugfs--创建pins/pingroups/gpio-ranges/pinmux/pinconf等调试文件。

2 pinctrl配置

内核支持pinctrl需要如下配置:

Device Drivers
  ->Pin controllers
    ->Debug PINCTRL calls--对pinctrl调用检查和诊断。
    ->STMicroelectronics STMFX GPIO expander pinctrl driver--STM32 pinctrl驱动。

涉及到的文件如下:

drivers/pinctrl/
├── core.c--pinctrl子系统核心。 ├── devicetree.c--处理pinctrl相关的device tree。 ├── pinconf.c--pinctrl子系统core关于pinconf处理。 ├── pinconf-generic.c--从device tree中解析pin通用属性。 ├── pinctrl-utils.c ├── pinmux.c--pinctrl子系统core中关于pinmux处理。 ├── stm32 │   ├── pinctrl-stm32.c--STM32probe函数,以及关于pinconf/pinctrl/pinmux的函数设置。 │   ├── pinctrl-stm32mp157.c--STM32MP157的Pin Controller驱动入口,以及各个pin的名称和功能定义。

3 pinctrl 数据结构和API

3.1 pinctrl数据结构

struct pinctrl_dev是内核中对pin control一类设备的抽象。

struct pinctrl_dev {
    struct list_head node;
    struct pinctrl_desc *desc;--对应的pin controller描述信息。
    struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
    struct radix_tree_root pin_group_tree;
    unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
    struct radix_tree_root pin_function_tree;
    unsigned int num_functions;
#endif
    struct list_head gpio_ranges;
    struct device *dev;
    struct module *owner;
    void *driver_data;
    struct pinctrl *p;
    struct pinctrl_state *hog_default;
    struct pinctrl_state *hog_sleep;
    struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
    struct dentry *device_root;
#endif
};

struct pinctrl_desc用于描述一个pin controller,包括:包含所有引脚描述信息、pinmux操作函数、pin引脚或group配置接口。

struct pinctrl_desc {
    const char *name;--pin controller名称。
    const struct pinctrl_pin_desc *pins;--此pin controller下所有引脚描述符数组。
    unsigned int npins;--上面pins数组大小。
    const struct pinctrl_ops *pctlops;--pin control操作函数集。
    const struct pinmux_ops *pmxops;--pinmux操作函数集。
    const struct pinconf_ops *confops;--pin config操作函数集。
    struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
    unsigned int num_custom_params;
    const struct pinconf_generic_params *custom_params;
    const struct pin_config_item *custom_conf_items;
#endif
    bool link_consumers;--表示此pinctrl是否被consumer使用,并创建连接。
};

struct pinctrl_pin_desc用于描述一个pin:

struct pinctrl_pin_desc {
    unsigned number;--全局唯一的pin编号。
    const char *name;--pin名称。
    void *drv_data;
};

一个设备会用到一个或多个引脚,这些引脚可以归为一组group。这些引脚可以复用为某种特定功能:function。

pinmux_ops用来描述那组group引脚复用为哪个function功能。

pinconf_ops可以用来描述配置信息:上来、下拉等等。

struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);--获取pin controller的group个数。
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,
                       unsigned selector);--获取selector(group序号)对应的名称。
    int (*get_group_pins) (struct pinctrl_dev *pctldev,
                   unsigned selector,
                   const unsigned **pins,
                   unsigned *num_pins);--获取group下pin列表。
    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);
};

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);--选择mux的一个功能。
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
                    struct pinctrl_gpio_range *range,
                    unsigned offset);--选择mux中的GPIO功能。
    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);--设置GPIO的input/output。
    bool strict;
};

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);--配置单个pin的configuration。
    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);--配置一个group的pin的configuration。
    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);
};

struct pinctrl维护每个设备pinctrl的各种状态;struct pinctrl_state表示设备的一个pinctrl状态。

struct pinctrl {
    struct list_head node;
    struct device *dev;
    struct list_head states;
    struct pinctrl_state *state;
    struct list_head dt_maps;
    struct kref users;
};

struct pinctrl_state {
    struct list_head node;
    const char *name;
    struct list_head settings;
};

3.2 pinctrl API

pinctrl的注册函数需要提供一个初始化好的struct pinctrl_desc结构体,返回一个struct pinctrl_dev。并将struct pinctrl_dev加入到pinctrldev_list全剧链表中。

extern int pinctrl_register_and_init(struct pinctrl_desc *pctldesc,
                     struct device *dev, void *driver_data,
                     struct pinctrl_dev **pctldev);
extern int pinctrl_enable(struct pinctrl_dev *pctldev);

extern struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
                struct device *dev, void *driver_data);

extern void pinctrl_unregister(struct pinctrl_dev *pctldev);

extern int devm_pinctrl_register_and_init(struct device *dev,
                struct pinctrl_desc *pctldesc,
                void *driver_data,
                struct pinctrl_dev **pctldev);

extern struct pinctrl_dev *devm_pinctrl_register(struct device *dev,
                struct pinctrl_desc *pctldesc,
                void *driver_data);

extern void devm_pinctrl_unregister(struct device *dev,
                struct pinctrl_dev *pctldev);

 pinctrl的注册函数简要流程如下:

devm_pinctrl_register_and_init--带资源管理的版本。
  pinctrl_register_and_init--注册pin controller到pinctrl子系统,并初始化。
    pinctrl_init_controller
      pinctrl_register_pins
        pinctrl_register_one_pin
          pin_desc_get
          radix_tree_insert

pinctrl_get()用于从struce device中获取pinctrl状态列表struct pinctrl。然后通过pinctrl_pinctrl_state()函数设置使用某种状态。对于低功耗通过pinctrl_pm_select_default_state()/pinctrl_pm_select_sleep_state()/pinctrl_pm_select_idle_state()根据低功耗状态切换pinctrl状态。

extern struct pinctrl * __must_check pinctrl_get(struct device *dev);
extern void pinctrl_put(struct pinctrl *p);

extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
extern void devm_pinctrl_put(struct pinctrl *p);
extern struct pinctrl_state * __must_check pinctrl_lookup_state(
                            struct pinctrl *p,
                            const char *name);
extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
extern int pinctrl_pm_select_default_state(struct device *dev);
extern int pinctrl_pm_select_sleep_state(struct device *dev);
extern int pinctrl_pm_select_idle_state(struct device *dev);

4 pinctrl DTS(Pin Controller & Client Device)

关于pin的详细配置更多参考《pinctrl-bindings.txt》。

pinctrl节点存放pin-controller配置信息,以及相关pin配置:

pinctrl: pin-controller@50002000 {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "st,stm32mp157-pinctrl";
    ranges = <0 0x50002000 0xa400>;--所有pin的寄存器起始地址和范围。
    interrupt-parent = <&exti>;--父中断为exti。
    st,syscfg = <&exti 0x60 0xff>;
    hwlocks = <&hsem 0 1>;
    pins-are-numbered;

    gpioa: gpio@50002000 {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
        reg = <0x0 0x400>;--根据address-cells/size-cells可知,第一个是寄存器偏移地址,第二个是寄存器地址大小。
        clocks = <&rcc GPIOA>;
        st,bank-name = "GPIOA";
        status = "disabled";
    };

    gpiob: gpio@50003000 {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
        reg = <0x1000 0x400>;
        clocks = <&rcc GPIOB>;
        st,bank-name = "GPIOB";
        status = "disabled";
    };
};

以UART为例,涉及到不同状态的pin配置如下:

&pinctrl {
    uart4_pins_a: uart4-0 {
        pins1 {--一个或一系列pin的配置,下面可以是pins/group/pinmux的一种。
            pinmux = <STM32_PINMUX('G', 11, AF6)>; /* UART4_TX */
            bias-disable;--关闭pin bias。
            drive-push-pull;
            slew-rate = <0>;--设置slew rate。
        };
        pins2 {
            pinmux = <STM32_PINMUX('B', 2, AF8)>; /* UART4_RX */
            bias-disable;
        };
    };

    uart4_idle_pins_a: uart4-idle-0 {
        pins1 {
            pinmux = <STM32_PINMUX('G', 11, ANALOG)>; /* UART4_TX */
        };
        pins2 {
            pinmux = <STM32_PINMUX('B', 2, AF8)>; /* UART4_RX */
            bias-disable;
        };
    };

    uart4_sleep_pins_a: uart4-sleep-0 {
        pins {
            pinmux = <STM32_PINMUX('G', 11, ANALOG)>, /* UART4_TX */
                 <STM32_PINMUX('B', 2, ANALOG)>; /* UART4_RX */
        };
    };
};

以uart4为例,其dts配置如下:

uart4: serial@40010000 {
    compatible = "st,stm32h7-uart";
    reg = <0x40010000 0x400>;
    interrupts-extended = <&exti 30 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&rcc UART4_K>;
    resets = <&rcc UART4_R>;
    wakeup-source;
    power-domains = <&pd_core>;
    dmas = <&dmamux1 63 0x400 0x5>,
           <&dmamux1 64 0x400 0x1>;
    dma-names = "rx", "tx";
    status = "disabled";
};

对uart4使用的pin进行如下配置:

&uart4 {
    pinctrl-names = "default", "sleep", "idle", "no_console_suspend";--指定状态名称列表,依次对应pinctrl-0、pinctrl-1等。
    pinctrl-0 = <&uart4_pins_a>;--phandle句柄列表,每一个指向一个pin配置节点。
    pinctrl-1 = <&uart4_sleep_pins_a>;
    pinctrl-2 = <&uart4_idle_pins_a>;
    pinctrl-3 = <&uart4_pins_a>;
    /delete-property/dmas;--删除dmas节点。
    /delete-property/dma-names;--删除dma-names节点。
    status = "okay";
};

可以将pinctrl配置分为两部分:provider和consumer。

pinctrl节点作为consumer,各外设作为consumer使用pinctrl配置。

5 Pin Controller驱动(STM32MP157D)

pinctrl驱动初始化级别为arch_initcall():

  • 根据stm32mp157_match_data为每个pin创建struct pinctrl_pin_desc。
  • 初始化struct pinctrl_desc成员。
  • 根据struct pinctrl_desc将pin controller注册到pinctrl子系统中。
  • 注册gpio。
stm32mp157_pinctrl_driver
  ->stm32_pctl_probe--当和stm32mp157_pctrl_match中compatible匹配之后,调用probe函数。
    ->devm_kcalloc--根据传入的数据stm32mp157_match_data为每个pin创建数组。
    ->stm32_pctrl_create_pins_tab--初始化struct stm32_desc_pin。
    ->devm_kcalloc--为每个pin创建struct pinctrl_pin_desc数组。
    ->初始化struct pinctrl_desc:confops/pctlops/pmxops函数集;创建的struct pinctrl_pin_desc数组和个数。
    ->devm_pinctrl_register--将代表当前pinctrl controller的struct pinctrl_desc注册到pinctrl子系统中。
    ->stm32_gpiolib_register_bank--pinctrl子节点如果是gpio-controller,则注册到GPIO子系统。
      ->pinctrl_add_gpio_range--为当前pinctrl注册一组GPIO。
      ->gpiochip_add_data

 使用stm32mp157_match_data作为驱动的私有数据传入probe函数:

static struct stm32_pinctrl_match_data stm32mp157_match_data = {
    .pins = stm32mp157_pins,
    .npins = ARRAY_SIZE(stm32mp157_pins),
};
static const struct of_device_id stm32mp157_pctrl_match[] = {
    {
        .compatible = "st,stm32mp157-pinctrl",
        .data = &stm32mp157_match_data,
    },
    { }
};

Pin Controller驱动编写有两大部分:一是各个Pin功能定义;另一个是pinctrl_ops/pinmux_ops/pinconf的函数集。

6 Client Device对pin配置

每一个struct device都有一个pins成员,指向设备的pinctrl:

struct device {
...
#ifdef CONFIG_PINCTRL
    struct dev_pin_info    *pins;
#endif...
};

使用某个具体pinctrl配置,是在外设驱动probe之前通过pinctrl_bind_pins做如下工作:

  1. 从设备节点读取pinctrl信息。
  2. 查找default/init状态。
  3. 将当前pin状态配置为default/init。
bus_for_each_drv--遍历bus下所有driver。
  ->__device_attach_driver
    ->driver_probe_device--driver和device匹配后,开始probe。
      ->really_probe/really_probe_debug
        ->pinctrl_bind_pins--在正式probe之前,处理设备的pinctrl节点。
          ->devm_pinctrl_get--获取当前设备的struct pinctrl。
            ->pinctrl_get
              ->find_pinctrl
              ->create_pinctrl
                ->pinctrl_dt_to_map
                ->add_setting
          ->pinctrl_lookup_state--从pinctrl句柄中分别查找pinctrl-names为default(默认配置)/init(probe时配置)/idle(runtime suspend时配置)/sleep(suspend时配置)的pinctrl状态。
          ->pinctrl_select_state--此时处于probe阶段,如果有init状态配置则进行配置;否则使用default状态配置。
            ->pinctrl_commit_state
              ->pinctrl_commit_state
                ->pinmux_enable_setting--PIN_MAP_TYPE_MUX_GROUP
                  ->pin_request
                  ->ops->set_mux
                ->pinconf_apply_setting--PIN_MAP_TYPE_CONFIGS_PIN/PIN_MAP_TYPE_CONFIGS_GROUP
                  ->ops->pin_config_set--PIN_MAP_TYPE_CONFIGS_PIN
                  ->ops->pin_config_group_set--PIN_MAP_TYPE_CONFIGS_GROUP
      ->dev->bus->probe/drv->probe--调用bus或者driver的probe函数。
      ->pinctrl_init_done--将pinctrl状态切换到default状态。

以stm32串口驱动为例:初始化时设置为default_state;睡眠时设置为sleep_state;退出睡眠设置为default_state。

stm32_serial_driver
  stm32_serial_pm_ops
    stm32_serial_suspend
      pinctrl_pm_select_sleep_state
        pinctrl_pm_select_state--设置为sleep_state。
    stm32_serial_resume
      pinctrl_pm_select_default_state
        pinctrl_pm_select_state--设置为default_state。

7 pinctrl相关sysfs

/sys/kernel/debug/pinctrl/
|-- pinctrl-devices--当前使能的Pin Controller名称,以及是否支持pinmux/pinconf。
|-- pinctrl-handles--当前被外设使用的pinctrl信息。按照不同外设不同状态显示详细信息。
|-- pinctrl-maps--遍历pinctrl_maps,显示每个pin所属的设备、状态、Pin Controller、Group、Function。
|-- soc:pin-controller-z@54004000
|   |-- gpio-ranges--不同GPIO组范围及其对应的pin。
|   |-- pinconf-groups--每个group的configuration显示。
|   |-- pinconf-pins--每个pin的configuration信息显示。
|   |-- pingroups--显示所有注册的pin group及其属下pin。
|   |-- pinmux-functions--不同functions下的group列表。
|   |-- pinmux-pins--当前pin选择的mux功能和对应的group号。
|   `-- pins--所有注册的pin列表。
`-- soc:pin-controller@50002000
    |-- gpio-ranges
    |-- pinconf-groups
    |-- pinconf-pins
    |-- pingroups
    |-- pinmux-functions
    |-- pinmux-pins
    `-- pins

参考文档《Linux驱动之Pinctrl子系统》《一文搞懂 | Linux pinctrl/gpio子系统》。

posted on 2024-01-07 23:59  ArnoldLu  阅读(131)  评论(0编辑  收藏  举报

导航