【Linux】Pinctrl子系统

1. Pinctrl子系统

        许多SoC内部都包含pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件上,Linux内核的Pinctrl驱动可以操作pin控制器为我们完成如下工作:

  • 枚举并且命名pin控制器可控制的所有引脚;
  • 提供引脚复用的能力;
  • 提供配置引脚的能力,如驱动能力、上下拉、开漏等。

1.1 pinctrl和引脚

        在特定SoC的pinctrl驱动中,我们需要定义引脚。假设有一个PGA封装的芯片的引脚排布如下所示:

         在pinctrl驱动初始化的时候,需要向pinctrl子系统注册一个pinctrl_desc描述符,该描述符的pins成员包含了所有引脚的列表。可以通过如下代码来注册这个pin控制器并命名它的所有引脚。

#include <linux/pinctrl/pinctrl.h>

const struct pinctrl_pin_desc foo_pins[] = {
    PINCTRL_PIN(0, "A8"),
    PINCTRL_PIN(1, "B8"),
    PINCTRL_PIN(2, "C8"),
    ...
    PINCTRL_PIN(61, "F1"),
    PINCTRL_PIN(62, "G1"),
    PINCTRL_PIN(63, "H1"),
};

static struct pinctrl_desc foo_desc = {
    .name = "foo",
    .pins = foo_pins,
    .npins = ARRAY_SIZE(foo_pins),
    .maxpin = 63,
    .owner = THIS_MODULE,
};

int __init foo_probe(void)
{
    struct pinctrl_dev *pctl;
    pctl = pinctrl_register(&foo_desc, <PARENT>, NULL)
}

1.1.1  pinctrl_dev 

struct pinctrl_dev {
	struct list_head node;//用于将当前引脚控制器设备加入全局引脚控制器链表。
	struct pinctrl_desc *desc;//指向引脚控制器描述符的指针,描述符包含了引脚控制器的详细信息,比如可管理的引脚和复用功能等。
	struct radix_tree_root pin_desc_tree;
    //使用 Radix 树来存储该引脚控制器的每个引脚的描述符,Radix 树是一种高效的内存数据结构,用于快速检索引脚信息。
    
	struct list_head gpio_ranges;
    //一个链表,存储了该引脚控制器负责管理的 GPIO 范围(GPIO ranges),这些范围是在运行时添加的。
    
	struct device *dev;
    //对应的设备指针,表示该引脚控制器设备。
    
	struct module *owner;//模块所有者,用于引用计数,防止模块在使用过程中被卸载。
    
	void *driver_data;
    //驱动数据,用于驱动程序在注册到引脚控制器子系统时存储特定的驱动信息。
    
	struct pinctrl *p;//表示该设备的 pinctrl_get() 结果,用于关联引脚控制和设备。
	struct pinctrl_state *hog_default;//表示该设备默认状态下被 "hog" 的引脚状态,"hog" 是指在设备初始化时强占一些引脚。
	struct pinctrl_state *hog_sleep;//表示该设备进入睡眠状态时被 "hog" 的引脚状态。
	struct mutex mutex;//一个互斥锁,用于在每次执行引脚控制器相关操作时确保线程安全。
#ifdef CONFIG_DEBUG_FS
	struct dentry *device_root;//该设备在 debugfs 中的根目录指针,用于调试文件系统。
#endif
};

1.1.2  pinctrl_desc 

struct pinctrl_desc {
	const char *name; //引脚控制器的名称,用于标识该控制器。
	const struct pinctrl_pin_desc *pins; //一个指向引脚描述符数组的指针。每个引脚描述符(pinctrl_pin_desc)描述了该控制器管理的引脚。
	unsigned int npins;//引脚描述符数组的大小,表示控制器管理的引脚数量,通常通过 ARRAY_SIZE() 来计算数组大小。
	const struct pinctrl_ops *pctlops;//引脚控制操作,这个下面会将---作用1
	const struct pinmux_ops *pmxops;//引脚复用操作,这个下面会将---作用2
	const struct pinconf_ops *confops;//引脚配置操作(如设置引脚的电压、电流、上拉/下拉等)---作用3
	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
};

1.2 引脚组(Pin Group)

        在pinctrl子系统中,支持将一组引脚绑定为同一功能。假设{0,8,16,24}这一组引脚承担SPI的功能,而{24,25}这一组承担I2C接口功能。在驱动的代码中,需要体现这个分组关系,并且为这些分组实现pinctrl_ops的成员函数get_groups_count()、get_group_name()和get_group_pins(),将pinctrl_ops填充到foo_desc 中,如下:

#include <linux/pinctrl/pinctrl.h>

struct foo_group{
    const char *name;
    const unsigned int *pins;
    const unsigned num_pins;
};

static const unsigned int spi0_pins[] = {0, 8, 16, 24};
static const unsigned int i2c0_pins[] = {24, 25};

static const struct foo_group foo_groups[] = {
    {
        .name = "spi0_grp",
        .pins = spi0_pins,
        .num_pins = ARRAY_SIZE(spi0_pins),
    },
    {
        .name = "i2c0_pins",
        .pins = i2c0_pins,
        .num_pins = ARRAY_SIZE(i2c0_pins),
    },
};

static int fool_get_groups_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(foo_groups);
}

static const *foo_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
    return foo_groups[selector].name;
}

static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
                              unsigned ** const pins,
                              unsigned *cont num_pins)
{
    *pins = (unsigned *)foo_groups[selector].pins;
    *num_pins = foo_groups[selector].num_pins;
    return 0;
}

static struct pinctrl_ops foo_pctrl_ops = {
    .get_groups_count = fool_get_groups_count,
    .get_group_name = foo_get_group_name,
    .get_group_pins = foo_get_group_pins,
};

static struct pinctrl_desc foo_desc = {
    ...
    .pctlops = &foo_pctrl_ops,
}

        get_groups_count()成员函数用于告知pinctrl子系统该SoC中合法的被选引脚有多少个,而get_group_name() 则提供引脚组的名字,get_group_pins()提供引脚组的引脚表。在设备驱动调用pinctrl通用API使能某一组引脚的对应功能时,pinctrl子系统的核心层会调用上述回调函数。

        1.2.1 pinctrl_ops 

        struct pinctrl_ops 结构体定义了全局引脚控制操作(pin control operations),这些操作需要由引脚控制器驱动程序实现。该结构体中的函数指针代表不同的操作接口,允许驱动程序通过这些接口与引脚控制子系统进行交互

struct pinctrl_ops {
    /*  返回当前已注册的引脚组(pin group)数量。该函数用于获取系统中引脚组的总数。 */
	int (*get_groups_count) (struct pinctrl_dev *pctldev);
    /* 返回特定引脚组的名称。通过组选择器 selector 作为参数,驱动可以获取某个引脚组的名称。 */
	const char *(*get_group_name) (struct pinctrl_dev *pctldev,
				       unsigned selector);
    /* 返回与特定组选择器对应的引脚数组,并通过参数 pins 和 num_pins 返回该数组的指针和引脚数量。此函数用于查询引脚组包含的具体引脚。 */
	int (*get_group_pins) (struct pinctrl_dev *pctldev,
			       unsigned selector,
			       const unsigned **pins,
			       unsigned *num_pins);

    /* 可选的 debugfs 显示挂钩,用于在调试文件系统(debugfs)中展示每个引脚的设备相关信息。它可以在调试时提供关于某个引脚的详细信息。 */
	void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
			  unsigned offset);

    /* 解析设备树(Device Tree)中的 “pin 配置节点” 并为其创建映射表项(mapping table entries)。这些映射表项通过 map 和 num_maps 输出参数返回。该函数是可选的,适用于支持设备树的引脚控制器驱动。 */
	int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
			       struct device_node *np_config,
			       struct pinctrl_map **map, unsigned *num_maps);

    /*  释放通过 dt_node_to_map 创建的映射表项。顶层的 map 指针需要释放,同时还要释放映射表项中动态分配的成员。该函数是可选的,对于不支持设备树的引脚控制器驱动,可以忽略此函数。 */
	void (*dt_free_map) (struct pinctrl_dev *pctldev,
			     struct pinctrl_map *map, unsigned num_maps);
};

 1.3  引脚配置

        设备驱动有时候需要配置引脚,譬如可能把引脚设置为高阻或者三态,或通过某阻值将引脚上拉/下拉以确保默认状态下引脚的电平状态。在驱动中可以自定义相应板级引脚配置API的细节,譬如某设备可能通过如下代码将某引脚上拉:

#include <linux/pinctrl/consumer.h>

ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP);

        其中的PLATFORM_X_PULL_UP由特定的pinctrl驱动定义。在特定的pinctrl驱动中,需要实现完成这些配置所需要的回调函数(pinctrl_desc的confops成员函数)

#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>

static int foo_pin_config_get(struct pinctrl_dev *pctldev,
                              unsigned offset,
                              unsigned long *config)
{
    struct my_conftype conf;

    ...find setting for pin 0 offset ...
    *config = (unsigned long)conf;
}

static int foo_pin_config_set(struct pinctrl_dev *pctldev,
                              unsigned offset,
                              unsigned long config)
{
    struct my_conftype *conf = (struct my_conftype *)config;

    switch(config){
        case PLATFORM_X_PULL_UP:
            ...
    }
}

static struct pinconf_ops foo_pconf_ops = {
    .pin_config_get = foo_pin_config_get,
    .pin_config_set = foo_pin_config_set,
};

static struct pinctrl_desc foo_desc = {
    ...
    .confops = &foo_pconf_ops,
}

         foo_pin_config_get()、foo_pin_config_set()针对单个引脚的配置。

1.3.1 pinconf_ops 

        struct pinconf_ops 结构体定义了引脚配置操作,用于支持引脚配置功能的驱动程序实现。它包含一系列函数指针,这些函数用于获取和设置单个引脚及引脚组的配置。

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
    /* 如果引脚控制器希望使用通用接口,设置该标志以告诉框架其为通用接口。 */
	bool is_generic;
#endif
    /*  获取特定引脚的配置。如果请求的配置在该控制器上不可用,返回 -ENOTSUPP;如果可用但被禁用,返回 -EINVAL。 */
	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);
    /*  配置引脚组内所有引脚的设置。与 pin_config_set 类似,但作用于整个引脚组 */
	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);
};

1.4 引脚复用(pinmux) 

        在pinctrl驱动中可处理引脚复用,它定义了功能(FUNCTIONS),驱动可以设置某功能的使能或者禁止。各个功能联合起来组成一个一维数组,譬如{spi0,i2c0,mmc0}就描述了3个不同的功能。

        一个特定的功能总是要求由一些引脚组来完成,引脚组的数量可以为1个或者多个。假设对前面的SoC而言,引脚分组如下:

        假设I2C功能由{A5,B5}引脚组成,而在定义引脚描述的pinctrl_pin_desc结构体实例foo_pins的时候,将他们的序号定义为了{24,25};而 SPI功能则可以由{A8,A7,A6,A5}和{G4,G3,G2,G1},即{0,8,16,24}和{38,26,54,62}两个引脚组完成。

        据说,由功能和引脚组的组合就可以决定一组引脚在系统的作用,因此在设置某组引脚的作用时,pinctrl的核心层会将功能的序号以及引脚组的序号传递给底层pinctrl驱动相关的回调函数。

        在整个系统中,驱动或板级代码调用pinmux相关的API获取引脚后,会形成一个pinctrl、使用引脚的设备、功能、引脚组的映射关系,假设在某一电路板上,将让spi0设备使用pinctrl0的fspi0功能以及gspi0引脚组,让i2c设备使用pinctrl0的fi2c功能和gi2c引脚组,我们将得到如下的映射关系:

{
    "map-spi0", spi0, pinctrl0, fspi0, gspi0},
    "map-i2c0", i2c0, pinctrl0, fi2c0, gi2c0}
}

        在特定pinctrl驱动中pinmux相关的代码主要处理如何使能/禁止某一功能{功能,引脚组}的组合,譬如,当spi0设备申请pinctrl0的fspi0功能和gspi0引脚组以便将gspi0引脚组配置为SPI接口时,相关的回调函数被组织进一个pinmux_ops结构体中,而该结构体的实例最终成为前文pinctrl_desc的pmxops成员,代码如下:

#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>

struct foo_group {
    const char *name;
    const unsigned int *pins;
    const unsigned num_pins;
};

static const unsigned spi0_0_pins[] = {0, 8, 16, 24};
static const unsigned spi0_1_pins[] = {38, 46, 54, 62};
static const unsigned i2c0_pins[] = {24, 25};
static const unsigned mmc0_1_pins[] = {56, 57};
static const unsigned mmc0_2_pins[] = {58, 59};
static const unsigned mmc0_3_pins[] = {60, 61, 62, 63};

static const struct foo_group foo_groups[] = {
    {
        .name = "spi0_0_grp",
        .pins = spi0_0_pins,
        .num_pins = ARRAY_SIZE(spi0_0_pins),
    },
    {
        .name = "spi0_1_grp",
        .pins = spi0_1_pins,
        .num_pins = ARRAY_SIZE(spi0_1_pins),
    },
    {
        .name = "i2c0_grp",
        .pins = i2c0_pins,
        .num_pins = ARRAY_SIZE(i2c0_pins),
    },
    {
        .name = "mmc0_1_grp",
        .pins = mmc0_1_pins,
        .num_pins = ARRAY_SIZE(mmc0_1_pins),
    },
    {
        .name = "mmc0_2_grp",
        .pins = mmc0_2_pins,
        .num_pins = ARRAY_SIZE(mmc0_2_pins),
    },
    {
        .name = "mmc0_3_grp",
        .pins = mmc0_3_pins,
        .num_pins = ARRAY_SIZE(mmc0_3_pins),
    },
};


static int fool_get_groups_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(foo_groups);
}

static const *foo_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
    return foo_groups[selector].name;
}

static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
                              unsigned ** const pins,
                              unsigned *cont num_pins)
{
    *pins = (unsigned *)foo_groups[selector].pins;
    *num_pins = foo_groups[selector].num_pins;
    return 0;
}

static struct pinctrl_ops foo_pctrl_ops = {
    .get_groups_count = fool_get_groups_count,
    .get_group_name = foo_get_group_name,
    .get_group_pins = foo_get_group_pins,
};

struct foo_pmx_func {
    const char *name;
    const char * const *groups;
    const unsigned num_groups;
};

static const char * const spi0_groups[] = {"spi0_0_grp", "spi0_1_grp"};
static const char * const i2c0_groups[] = {"i2c0_grp"};
static const char * const mmc0_groups[] = {"mmc0_1_grp", "mmc0_2_grp", "mmc0_3_grp"};

static const struct foo_pmx_func foo_functions[] = {
    {
        .name = "spi0",
        .groups = spi0_groups,
        .num_groups = ARRAY_SIZE(spi0_groups),
    },
    {
        .name = "i2c0",
        .groups = i2c0_groups,
        .num_groups = ARRAY_SIZE(i2c0_groups),
    },
    {
        .name = "mmc0",
        .groups = mmc0_groups,
        .num_groups = ARRAY_SIZE(mmc0_groups),
    }
};

int foo_get_functions_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(foo_functions);
}

const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
{
    return foo_functions[selector].name;
}

int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
                   const char * const **groups,
                   unsigned * const num_groups)
{
    *groups = foo_functions[selector].groups;
    *num_groups = foo_functions[selector].num_groups;

    return 0;
}

int foo_enable(struct pinctrl_dev *pctldev, unsigned selector,
               unsigned groups)
{
    u8 regbit = (1 << selector + groups);
    writeb(readb(NUX)|regbit, MUX);
    return 0;
}

int foo_disable(struct pinctrl_dev *pctldev, unsigned selector,
               unsigned groups)
{
    u8 regbit = (1 << selector + groups);
    writeb(readb(NUX)&~regbit, MUX);
    return 0;
}

struct pinmux_ops foo_pmxops = {
    .get_functions_count = foo_get_functions_count,
    .get_functions_name = foo_get_fname,
    .get_functions_groups = foo_get_groups,
    .enable = foo_enable,
    .disbale = foo_disable,
};

static struct pinctrl_desc foo_desc = {
    ...
    .pctlops = &foo_pctrl_ops,
    .pmxops = &foo_pmxops
};

        具体的pinctrl、使用引脚的设备、功能、引脚组的映射关系,可以在板文件中通过定义pinctrl_map结构体的实例来展开,如:

static struct pinctrl_map __initdata mapping[] = {
    PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", NULL, "i2c0")
};

        又由于1个功能可由两个不同的引脚组实现,所有对于同一个功能可能形成有两个引脚组的pinctrl_map:

static struct pinctrl_map __initdata mapping[] = {
    PIN_MAP_MUX_GROUP("foo-spi.0", "spi0-pos-A", "pinctrl-foo", "spi0_0_grp", "spi0"),
    PIN_MAP_MUX_GROUP("foo-spi.0", "spi0-pos-B", "pinctrl-foo", "spi0_1_grp", "spi0")
};

        其中调用的PIN_MAP_MUX_GROUP是一个快捷宏,用于赋值pinctrl_map的各个成员:

#define PIN_MAP_MUX_GROUP(dev, state, pinctrl, grp, func)		\
	{								\
		.dev_name = dev,					\
		.name = state,						\
		.type = PIN_MAP_TYPE_MUX_GROUP,				\
		.ctrl_dev_name = pinctrl,				\
		.data.mux = {						\
			.group = grp,					\
			.function = func,				\
		},							\
	}

        当然,这种映射关系最好是在设备树中通过节点的属性进行,具体的节点属性的定义依赖于具体的pinctrl驱动,最终在pinctrl驱动中通过pinctrl_ops的结构体的.dt_node_to_map()成员函数读出属性并建立映射表。

        在运行时,我们可以通过类似的API去查找并设置位置A的引脚组以行驶SPI接口的功能:

p = devm_pinctrl_get_select(dev, "spi0-pos-A")

        对于“default”状态下的引脚配置,驱动一般不需要完成devm_pinctrl_get_select(dev, "default")的调用,如下:

peri-iobg {
    uart@b0060000{
        pinctrl-names = "default";
        pinctrl-0 = <&uart1_pins_a>
    }
};

        由于pinctrl-name都是“default”的,所以pinctrl核实际会自动做类似devm_pinctrl_get_select(dev, "default")的动作的

1.4.1 pinmux_ops 

        struct pinmux_ops 结构体定义了引脚复用(pinmux)操作,主要用于实现支持引脚复用功能的引脚控制器驱动程序。该结构体提供了一系列回调函数,允许引脚控制器子系统与引脚复用功能进行交互。

struct pinmux_ops {
    /* 核心功能调用,用于判断特定的引脚是否可以被用于复用。在实际选择复用设置前,核心会调用该函数来请求引脚。驱动程序可以通过返回负值拒绝该请求。 */
	int (*request) (struct pinctrl_dev *pctldev, unsigned offset);

    /* 与 request() 相反,释放被请求的引脚。这是在引脚复用完成后释放引脚的回调函数。 */
	int (*free) (struct pinctrl_dev *pctldev, unsigned offset);

    /* 返回当前引脚复用驱动中可选择的复用功能数量 */
	int (*get_functions_count) (struct pinctrl_dev *pctldev);

    /* 根据复用选择器 selector 返回某个复用功能的名称。核心通过调用此函数来确定应将某个设备映射到哪个复用设置。 */
	const char *(*get_function_name) (struct pinctrl_dev *pctldev,
					  unsigned selector);

    /* 根据功能选择器 selector 返回与该复用功能相关的引脚组名称数组,并通过 groups 和 num_groups 返回受影响的组及其数量。可以与 pinctrl_ops 中的函数结合使用以获取受影响的引脚。 */
	int (*get_function_groups) (struct pinctrl_dev *pctldev,
				  unsigned selector,
				  const char * const **groups,
				  unsigned *num_groups);

    /* 启用特定的复用功能与引脚组关联。该函数根据 func_selector 选择特定的复用功能,并根据 group_selector 选择一组引脚进行复用设置。驱动程序不需要检测复用冲突,系统会自动处理引脚冲突。 */
	int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
			unsigned group_selector);

    /* 请求并启用某个引脚的 GPIO 模式。如果引脚控制器支持每个引脚单独复用为 GPIO 模式,则需要实现此函数。参数包括 GPIO 范围和在该范围内的引脚偏移量。 */
	int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
				    struct pinctrl_gpio_range *range,
				    unsigned offset);

    /* 释放之前启用的 GPIO 复用设置,与 gpio_request_enable 作用相反。 */
	void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset);

    /*  对于需要引脚复用的 GPIO 控制器,当 GPIO 配置为输入或输出时可能需要不同的设置。该函数允许控制引脚方向(输入或输出)。 */
	int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
				   struct pinctrl_gpio_range *range,
				   unsigned offset,
				   bool input);

    /* 如果为真,则不允许引脚同时作为 GPIO 和复用其他功能使用。在批准引脚请求之前,会严格检查 gpio_owner 和 mux_owner 以防止冲突。 */
	bool strict;
};

2. Pinctrl子系统中client端

        在设备树中,我们使用pinctrl-name 和pinctrl-x描述客户端使用的引脚组和引脚状态,在设备树解析中,这两个属性被转换为一个struct dev_pin_info结构体,如下:

        每个device结构体里都有一个dev_pin_info结构体,用来保存设备的pinctrl信息,只不过这个pinctrl信息是经过转换的,节点–>mapping–>setting:

dev_pin_info : 

struct dev_pin_info {
	struct pinctrl *p; //指针类型,一个pinctrl,比如设备树中client定义了pinctrl-0= ;那么其对应的pinctrl信息就存在这
    //这是设备的引脚控制句柄,用于管理和操作设备的引脚(pins)。通过它,驱动程序可以使用 pinctrl API 来设置和切换引脚的状态。
	struct pinctrl_state *default_state;
    //设备的默认引脚状态。如果设备成功查找到默认状态,这里会保存该状态。在设备初始化或者运行期间,这个状态可能会被激活。
	struct pinctrl_state *init_state;
    //设备在驱动探测(probe)时的初始引脚状态。如果在驱动加载时找到这个状态,它将被设置为设备的初始状态。
#ifdef CONFIG_PM
	struct pinctrl_state *sleep_state;
    //设备进入系统休眠(suspend)时的引脚状态。在电源管理(Power Management,PM)启用的情况下,当设备进入休眠时,这个状态会被应用,以确保引脚处于低功耗或其他适当的状态。
	struct pinctrl_state *idle_state;
    //设备进入空闲(即运行时挂起,runtime suspend)时的引脚状态。在设备处于空闲状态时,会应用此状态。它允许设备进入空闲时节省功耗的状态。
#endif
};

1.1 pinctrl和pinctrl_state

        先看pinctrl, 是 Linux 内核中每个设备的引脚控制状态管理器。它负责管理和切换设备的引脚配置状态(state),并且可以存储从设备树(Device Tree)解析的引脚映射表。这个结构体对于设备驱动程序而言,是引脚控制系统的一部分,用于确保设备的引脚配置在系统的不同状态下能够正确应用 :

假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?

这需要通过设备树来确定:

  • 分析设备树,找到pin controller,就是根据client处定义的pinctrl-0/1所对应到Pincontroller的设备树节点
  • 对于每个状态,比如default、init,去分析pin controller中的设备树节点
    • 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map
    • 这些pinctrl_map放在pinctrl.dt_maps链表中
    • 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表

1.2 client端调用Pinctrl

really_probe //dd.c
    pinctrl_bind_pins  //pinctrl.c

        devm_pinctrl_get

                devm_pinctrl_get

                        create_pinctrl

                                pinctrl_dt_to_map/* 通过ops->dt_node_to_map把设备树资源转换为pinctrl_map */

                                add_setting /* 将pinctrl_map 转换为pinctrl_setting  */
        pinctrl_select_state  //core.c
            /* Apply all the settings for the new state */
            list_for_each_entry(setting, &state->settings, node) {
                switch (setting->type) {
                case PIN_MAP_TYPE_MUX_GROUP:  /*使用(复用)引脚*/
                    ret = pinmux_enable_setting(setting);  //pinmux.c   // 4.2.1
                            ret = ops->set_mux(...);
                break;
                case PIN_MAP_TYPE_CONFIGS_PIN: /*配置引脚*/
                case PIN_MAP_TYPE_CONFIGS_GROUP:
                    ret = pinconf_apply_setting(setting);
                            ret = ops->pin_config_group_set(...);  // 4.2.1
                    break;
                default:
                    ret = -EINVAL;
                break;
            }        

                 

引用\[1\]: pinctrl子系统Linux内核中的一个框架,用于统一管理不同系统芯片的引脚控制和配置。在pinctrl子系统中,有一个主要的结构体struct pinctrl_dev,用于表示一个引脚控制器设备。一般系统只会有一个struct pinctrl_dev实例。通过调用pinctrl_register_pins函数,可以为每个引脚分配一个独立的struct pin_desc结构体,并进行相应的赋值。struct pin_desc是pinctrl子系统用来管理每个引脚的最小单元。 引用\[2\]: 在pinctrl子系统中,引脚控制器并不只有一个,而是可以有多个。在设备树中,含有pinctrl-names和pinctrl-0属性的节点会拥有一个struct pinctrl结构体。pinctrl-names和pinctrl-0属性中的内容指定了要控制的引脚。有时候会遇到多个pinctrl-names和pinctrl-0属性,可以通过这种方式来配置不同的引脚控制。 引用\[3\]: pinctrl子系统的目的是为了统一各种不同的系统芯片中的引脚管理。在ARM的各种SOC芯片中,一个引脚可以被复用为不同功能的引脚,例如GPIO、SPI、I2C、UART等。pinctrl子系统的引入正是为了解决这种复用问题,使得不同SOC厂商的引脚管理能够在Linux内核中得到统一。 #### 引用[.reference_title] - *1* *3* [Linux pinctrl子系统框架流程详解(基于Kernel 3.16,arm,设备树)](https://blog.csdn.net/ZHONGkunjia/article/details/89873417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于LinuxPinctrl子系统框架源码分析](https://blog.csdn.net/qq_42017846/article/details/127795402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值