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.cdevm_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;
}