一、pinctrl子系统简介
在许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。各个厂商soc的pin脚在使用中,都有许多共同的特性,要么配置,要么复用pin脚。所以内核提供了一套代码来管理这些pin,这就是pinctrl子系统。主要实现的功能:
(1)管理系统中所有的可以控制的pin,在系统初始化的时候,枚举所有可以控制的pin,并标识这些pin。 (2)管理这些pin的复用(Multiplexing)。对于SOC而言,其引脚除了配置成普通的GPIO之外,若干个引脚还可以组成一个pin group,行程特定的功能。pin control subsystem要管理所有的pin group。 (3)配置这些pin的特性。例如使能或关闭引脚上的pull-up、pull-down电阻,配置引脚的driver strength。
如图所示的pinctrl逻辑框图,实现pinctrl子系统的功能,core在初始化时,由处理器抽象pin描述,在pinctrl core中枚举所有的pin描述。当我们的驱动层driver使用pinctrl时,会进入pinctrl core会去分析这些pin的枚举,映射,设置,配置pin和pin group。
二、pinctrl 在dts文件中的描述
1、在使用pincrtl时,就涉及pinctrl在设备树中是如何描述的,在dts文件中,将一个处理器所使用的的pin用bank和group来描述。
a、何为bank?
所谓pin bank,就是以引脚名为依据,就是一组GPIO控制器的描述,如s3c2440A这块芯片,阅读它的datasheet就可以知道有9组GPIO,如图所示:
所以我们在dts中就把这9组GPIO枚举为pin bank。这里分析一部分,其他的类似。
gpa: gpa { gpf: gpf {
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;
}; interrupt-controller; #interrupt-cells = <2>; };
如gpa: gpa 这个child node 就是描述GPA这个组,也就是gpa bank.。当然了 gpio-controller;表示这是一个GPIO控制器,有的GPIO控制器也可以是中断控制器,如gpf: gpf,之前的LED实验就用到了。#gpio-cells = <2>;表示使用这个bank的GPIO时,需要用两个32位数去描述。肯定是GPIO number和初始电平。不信可以阅读linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt
b、何为group?
从字面意思理解就是一个类似集群,将不同的pin number组合在一个。group以功能为依据,我们在JZ2440的LCD驱动中,需要使用gpc bank中的(1、2、3、4、8、9、10、11、12、13、14、15)gpd bank中的(0、1、2、3、4、5、6、7、8、9、10、11、12、13、14、15)来读写数据,我们把这些具体pin number组合在一起,我们称之为一个group。同时这些组合在一起的pin number又可能实现不同的功能,就用samsung,pin-function、samsung,pin-val等来描述区分。这样子samsung,pins和samsung,pin-function构成child node。来描述设备使用pinctrl时的具体功能。
lcd_pinctrl: lcd_pinctrl {
samsung,pins = "gpc-8", "gpc-9", "gpc-10", "gpc-11", "gpc-12", "gpc-13", "gpc-14", "gpc-15",
"gpd-0", "gpd-1", "gpd-2", "gpd-3", "gpd-4", "gpd-5", "gpd-6", "gpd-7",
"gpd-8", "gpd-9", "gpd-10", "gpd-11", "gpd-12", "gpd-13", "gpd-14", "gpd-15",
"gpc-1", "gpc-2", "gpc-3", "gpc-4";
samsung,pin-function = <2>;
};
samsung,pins:描述了LCD读写数据所使用的pin number,
samsung,pin-function:将这些GPIO初始值设置为2,具体是什么功能,有datasheet解释。这一个关于初始值的描述并不只是 samsung,pin-function,对于samsung来说,还有如下几种:具体阅读linux-4.19.8\Documentation\devicetree\bindings\pinctrl\samsung-pinctrl.txt
- samsung,pin-val: Initial value of pin output buffer.
- samsung,pin-pud: Pull up/down configuration.
- samsung,pin-drv: Drive strength configuration.
- samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
- samsung,pin-drv-pdn: Drive strength configuration in power down mode.
2、设备节点引用pinctrl
在lcd这个节点中dts文件是如下描述的,省略其他不需要的东西。
fb0: fb@4d000000{
compatible = "jz2440,lcd";
...
pinctrl-names = "default";
pinctrl-0 = <&lcd_pinctrl &lcd_backlight>;
...
}
state:pinctrl-names其实就是设置设备的某种初始状态,比如内核自己定义的"default","init","idel","sleep"状态;也可以是其他自己定义的状态, 比如串口的"flow_ctrl"状态(使用流量控制)。
pinctrl-0 就是正常引用pin config,也就是调用lcd_pinctrl这个group配置LCD读写数据的GPIO。
三、pinctrl子系统涉及的文件
以JZ2440开发板的LCD的dts文件为例,介绍了pinctrl在dts中的几个重要组成部分。接下来真正开始分析pinctrl子系统。
1、pinctrl涉及的主要处理文件
Linux/drivers/pinctrl
core.c core.h //pin control subsystem的core driver
pinctrl-utils.c //pinctrl-utils.h pin control subsystem的一些utility接口函数
pinmux.c pinmux.h //pin control subsystem的core driver(pin muxing部分的代码,也称为pinmux driver)
pinconf.c pinconf.h //pin control subsystem的core driver(pin config部分的代码,也称为pin config driver)
devicetree.c devicetree.h //pin control subsystem的device tree代码
pinctrl-xxxx.c //各种pin controller的low level driver。也就是pin number枚举,如pinctrl-samsung.c
2、pinctrl为其他驱动模块提供的统一接口
linux/include/linux/pinctrl
consumer.h //其他的driver要使用pin control subsystem的下列接口:
a、设置引脚复用功能
b、配置引脚的电气特性
devinfo.h //这是for linux内核的驱动模型模块(driver model)使用的接口。struct device中包括了一个
struct dev_pin_info *pins的成员,这个成员描述了该设备的引脚的初始状态信息,在probe之前,driver model中的core driver在调用driver的probe函数之前会先设定pin state
machine.h //和machine模块的接口。
3、Low level pin controller driver接口
linux/include/linux/pinctrl
pinconf-generic.h /这个接口主要是提供给各种pin controller driver使用的,不是外部接口。
pinconf.h //pin configuration 接口
pinctrl-state.h //pin control state状态定义
pinmux.h //pin mux function接口
四、soc初始化pinctrl
1、在第一节框图中就可以看见,遵循内核的一贯风格,注册。
每个SOC如果使用了pinctrl子系统,那么必然在系统初始化时需要向内核注册一个pinctrl。linux-4.19.8/drivers/pinctrl/core.c 中可以找到注册函数。
struct pinctrl_dev *devm_pinctrl_register(struct device *dev,
struct pinctrl_desc *pctldesc,//SOC对于自己pinctrl的描述
void *driver_data)//soc自己的私有数据
{
struct pinctrl_dev **ptr, *pctldev;
ptr = devres_alloc(devm_pinctrl_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
pctldev = pinctrl_register(pctldesc, dev, driver_data);
if (IS_ERR(pctldev)) {
devres_free(ptr);
return pctldev;
}
*ptr = pctldev;
devres_add(dev, ptr);
return pctldev;
}
分析 struct pinctrl_desc ,看看需要SOC提供些什么东西。
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins;//pin number的描述列表
unsigned int npins;//总共有多少pinctrl_pin_desc
const struct pinctrl_ops *pctlops;//pinctrl全局操作函数
const struct pinmux_ops *pmxops;//pin mux操作函数,也就是group的处理函数
const struct pinconf_ops *confops;//pin config操作函数,也就是处理pin 上拉、下拉等等电气特性
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
};
由结构体分析可以得出,SOC注册时需要枚举pin number,提供初始化这些pin 的函数。在linux-4.19.8/drivers/pinctrl/samsung/pinctrl-samsung.c中调用了devm_pinctrl_register,就来分析分析,底层是如何处理dts文件中这些pin的描述。
pinctrl-samsung.c也是作为一个platfom driver注册到系统中,通过of_match_table匹配注册,当然在dts文件中,肯定需要描述这个节点。一旦匹配成功进入probe函数。分析probe函数:
ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);//获取samsung_pin_ctrl数据,描述了一个 pin contrl
首先获取一个struct samsung_pin_ctrl数据。从函数名字就知道,获取的是具体的物理数据。通过samsung_pinctrl_get_soc_data_for_of_alias函数,获取得到pin control中有多少个bank,以及pinctro的l物理地址。
struct samsung_pin_ctrl {//描述一个pinctrl底层
const struct samsung_pin_bank_data *pin_banks;//pinctrl中包含的pin bank列表
unsigned int nr_banks;//有多少个bank
unsigned int nr_ext_resources;//pin bank数据的物理基地址
const struct samsung_retention_data *retention_data;//保留数据
//中断处理函数
int (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
int (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
void (*suspend)(struct samsung_pinctrl_drv_data *);
void (*resume)(struct samsung_pinctrl_drv_data *);
};
samsung_pinctrl_get_soc_data
ctrl = samsung_pinctrl_get_soc_data_for_of_alias(pdev);//获取当前处理器的pin contrl描述
id = of_alias_get_id(node, "pinctrl");// 通过"pinctrl" 获取id
of_data = of_device_get_match_data(&pdev->dev);
return &(of_data->ctrl[id]);
d->pin_banks = devm_kcalloc(&pdev->dev, d->nr_banks,sizeof(*d->pin_banks), GFP_KERNEL);//为samsung_pinctrl_drv_data的pin bank分配内存
for (i = 0; i < ctrl->nr_banks; ++i, ++bdata, ++bank) {//处理bank 数据
bank->type = bdata->type;
bank->pctl_offset = bdata->pctl_offset;
bank->nr_pins = bdata->nr_pins;
bank->eint_func = bdata->eint_func;
bank->eint_type = bdata->eint_type;
bank->eint_mask = bdata->eint_mask;
bank->eint_offset = bdata->eint_offset;
bank->name = bdata->name;
spin_lock_init(&bank->slock);
/*
d->nr_pins初始值是0,循环完毕时,为总的pin数量
bank->pin_base 每个bank的pin_base都是从上一个bank的bank->nr_pins开始
*/
bank->drvdata = d;
bank->pin_base = d->nr_pins;
d->nr_pins += bank->nr_pins;
bank->eint_base = virt_base[0];
bank->pctl_base = virt_base[bdata->pctl_res_idx];//pinctrl中bank的虚拟地址
}
for_each_child_of_node(node, np) {
if (!of_find_property(np, "gpio-controller", NULL))//每个bank都是一个gpio controller
continue;
bank = d->pin_banks;
for (i = 0; i < d->nr_banks; ++i, ++bank) {
if (!strcmp(bank->name, np->name)) {
bank->of_node = np;//初始化各个bank描述符中的device tree node成员
break;
}
}
}
ret = samsung_pinctrl_register(pdev, drvdata);
2、SOC获取到了硬件信息,也就是pin bank以及pinctrl的地址信息,进入 samsung_pinctrl_register函数,进一步解析这些数据
ctrldesc->pctlops = &samsung_pctrl_ops;
ctrldesc->pmxops = &samsung_pinmux_ops;
ctrldesc->confops = &samsung_pinconf_ops;
SOC提供了三组操作函数,分别是:
a、pinctrl全局操作函数
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev); //该pin controller支持多少个pin group
const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的pin group中pin的信息(该pin group//包括多少个pin,每个pin的ID是什么
unsigned selector, //包括多少个pin,每个pin的ID是什么)
const unsigned **pins,
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
unsigned offset);
int (*dt_node_to_map) (struct pinctrl_dev *pctldev,//分析dts的一个child node中的pin config 引用关系,并保存在pinctrl_map中
struct device_node *np_config,//entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函数。
struct pinctrl_map *map, unsigned num_maps);
};
b、pinctrl复用pin操作函数
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev); //该pin controller支持多少个pin group
const char *(*get_group_name) (struct pinctrl_dev *pctldev,//给定一个selector(index),获取指定的pin group的name
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,//给定一个selector(index)获取指定的pin group中pin的信息(该pin group//包括多少个pin,每个pin的ID是什么
unsigned selector, //包括多少个pin,每个pin的ID是什么)
const unsigned **pins,
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,//debug fs的callback接口
unsigned offset);
int (*dt_node_to_map) (struct pinctrl_dev *pctldev,//分析dts的一个child node中的pin config 引用关系,并保存在pinctrl_map中
struct device_node *np_config,//entry 每一个entry表示一个setting(一个功能复用设定,或者电气特性设定)
struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev, //dt_node_to_map的逆函数。
struct pinctrl_map *map, unsigned num_maps);
};
c、pinctrl 配置pin操作函数
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev,//给定一个pin ID以及config type ID,获取该引脚上指定的type的配置。
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev,//设定一个指定pin的配置
unsigned pin,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,//以pin group为单位,获取pin上的配置信息。
unsigned selector,
unsigned long *config);
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,//以pin group为单位,设定pin group 的特性配置。
unsigned selector,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,//debug接口
const char *arg,
unsigned long *config);
void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
struct seq_file *s,
unsigned offset);
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
struct seq_file *s,
unsigned selector);
void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,//debug接口
struct seq_file *s,
unsigned long config);
};
枚举pin,每一个pin 描述 都是pin-bank name + pin number,如dts文件中"gpc-8", "gpc-9", "gpc-10"等等
//每一个pin 描述 都是pin-bank name + pin number
/* for each pin, the name of the pin is pin-bank name + pin number */
for (bank = 0; bank < drvdata->nr_banks; bank++) {//遍历bank
pin_bank = &drvdata->pin_banks[bank];//取出bank
for (pin = 0; pin < pin_bank->nr_pins; pin++) {//遍历每个bank的所有pin number
sprintf(pin_names, "%s-%d", pin_bank->name, pin);
pdesc = pindesc + pin_bank->pin_base + pin;//一个pin number的描述
pdesc->name = pin_names;
pin_names += PIN_NAME_LENGTH;
}
}
将这些信息保存在struct pinctrl_desc中,ctrldesc->pins = pindesc;//pin描述列表,ctrldesc->npins = drvdata->nr_pins;//pin 数量
接下来分析 samsung_pinctrl_parse_dt(pdev, drvdata);
static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct device *dev = &pdev->dev;
struct samsung_pin_group *groups;
struct samsung_pmx_func *functions;
unsigned int grp_cnt = 0, func_cnt = 0;
groups = samsung_pinctrl_create_groups(dev, drvdata, &grp_cnt);
if (IS_ERR(groups)) {
dev_err(dev, "failed to parse pin groups\n");
return PTR_ERR(groups);
}
functions = samsung_pinctrl_create_functions(dev, drvdata, &func_cnt);
if (IS_ERR(functions)) {
dev_err(dev, "failed to parse pin functions\n");
return PTR_ERR(functions);
}
drvdata->pin_groups = groups;//groups 保存了这个pin contrl 的所有pin number
drvdata->nr_groups = grp_cnt;//总共有多少个pin number
drvdata->pmx_functions = functions;//准确的说是func 绑定的 group,形成的列表
drvdata->nr_functions = func_cnt;//有多少个group
return 0;
}
samsung_pinctrl_create_groups //这里是整理所有ctrldesc->npins中的pin number,按照groups = {"gpc-8", "gpc-9", "gpc-10", ...}的格式,将所有pin number看成是一个group保存。也就是枚举了soc所有的pin number。
samsung_pinctrl_create_functions //通过统计device node中有多少个 配置,如:"samsung,pin-function"、"samsung,pin-val"来统计group,创建struct samsung_pmx_func 来描述这些group和func。也就是dts中设备节点中pin config。
这些group和func数据都是各个soc特有的数据,所以以私有数据传递进pinctrl core,自然也由soc自己来处理。后面会分析到。
如上分析:在struct pinctrl_desc中将pin 信息枚举在了const struct pinctrl_pin_desc *pins;也提供了soc自己的处理函数。那么就可以注册了。 drvdata->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, drvdata);
pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);这个是处理GPIO子系统和pinctrl子系统之间的联系。暂时不分析。
五、drvicer调用pinctrl子系统
soc在初始化时,已经读取dts文件中的pinctrl信息,初始化完毕,进行pinctrl注册。设备驱动时,就可以直接使用这个pinctrl。以LCD驱动为例:
LCD也是通过platform驱动模型进行注册。platform设备模型在匹配时,会调用__driver_attach
__driver_attach
driver_probe_device(drv, dev)
really_probe(dev, drv)
ret = pinctrl_bind_pins(dev); //绑定设备使用pinctrl,获取一个pinctrl_dev handle
dev->pins->p = devm_pinctrl_get(dev);
pinctrl_get(dev);
create_pinctrl
pinctrl_init_done(dev);//配置设备使用的pin
每个platform匹配时,如果使用pinctrl,都需要向pinctrl core获取一个pinctrl handle,使用pin number时,直接使用pinctrl core提供的资源,当然,这些资源是soc初始化注册的。这就形成了一个闭环。接下来分析pinctrl core。
六、pinctrl core分析
首先介绍一下这个核心的结构体,将设备需要的pinctrl信息提取出来,保存在这个申请的handle中。具体如何如何保存信息?保存 了哪些信息,在设备注册接口分析。
struct pinctrl {
struct list_head node;//链表头,设备的pinctrl handle都挂接在这里
struct device *dev;//这个设备
struct list_head states;//设备状态。如idle、sleep等等
struct pinctrl_state *state;//设备的当前状态
struct list_head dt_maps;//map table,设备pin 的配置
struct kref users;
};
1、soc注册接口
分析pinctrl core如何处理soc传递的pinctrl_desc数据。
devm_pinctrl_register
pinctrl_register(pctldesc, dev, driver_data);
pinctrl_init_controller(pctldesc, dev, driver_data);
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);//分配一个pctldev内存,这是pinctrl core核心数据结构
pctldev->owner = pctldesc->owner;//保存soc传递的desc数据
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);//将所有的pin number描述传递
pinctrl_register_one_pin(pctldev, &pins[i]);//注册每一个pin nunmber
pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
pindesc->pctldev = pctldev;
pindesc->name = pin->name;
pindesc->drv_data = pin->drv_data;
radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);//保存在pctldev->pin_desc_tree
pinctrl_enable(pctldev);
list_add_tail(&pctldev->node, &pinctrldev_list); //将这个pctldev保存在链表pinctrldev_list
2、设备驱动调用接口
a、pinctrl core map table
mapping table这个database的建立也是动态的,当第一次调用pinctrl handle的get函数的时候,就会通过调用pinctrl_dt_to_map来建立该device需要的mapping entry。
ret = pinctrl_dt_to_map(p, pctldev);
/* For each defined state ID */
for (state = 0; ; state++) {//pinctrl-0 pinctrl-1 pinctrl-2……表示了该设备的一个个的状态,这里每次循环分析一个pin state。
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);//获取pinctrl-0 pinctrl-1 pinctrl-2……
prop = of_find_property(np, propname, &size);//获取pinctrl-0 pinctrl-1 pinctrl-2……属性指针
kfree(propname);
if (!prop) {
if (state == 0) {
of_node_put(np);
return -ENODEV;
}
break;
}
list = prop->value;//"pinctrl-%d" 的 属性值
size /= sizeof(*list);//多少个32bit数据
/* Determine whether pinctrl-names property names the state */
ret = of_property_read_string_index(np, "pinctrl-names",//获取, "pinctrl-names" 属性的第state个字符串
state, &statename);//statename "default、idle、sleep"
/*
* If not, statename is just the integer state ID. But rather
* than dynamically allocate it and have to free it later,
* just point part way into the property name for the string.
*/
if (ret < 0) {
/* strlen("pinctrl-") == 8 */
statename = prop->name + 8;//如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1 pinctrl-2……中的那个ID取出来作为state name
}
/* For every referenced pin configuration node in it */
for (config = 0; config < size; config++) {//pinctrl-x中含有一个或者多个配置,用phandle引用
phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */
np_config = of_find_node_by_phandle(phandle);//用phandle作为索引,在device tree中找他该phandle表示的那个pin configuration
if (!np_config) {
dev_err(p->dev,
"prop %s index %i invalid phandle\n",
prop->name, config);
ret = -EINVAL;
goto err;
}
/* Parse the node */
ret = dt_to_map_one_config(p, pctldev, statename,//分析一个pin configuration
np_config);
of_node_put(np_config);
if (ret < 0)
goto err;
}
/* No entries in DT? Generate a dummy state table entry */
if (!size) {
ret = dt_remember_dummy_state(p, statename);//如果该设备没有定义pin configuration,那么也要创建一个dummy的pin state。
if (ret < 0)
goto err;
}
}
再看子函数dt_to_map_one_config
dt_to_map_one_config
np_pctldev = of_get_next_parent(np_pctldev);//首先找到该pin configuration node对应的parent node(也就是pin
//controler对应的node),如果找不到或者是root node,则进入出错处理
pctldev = get_pinctrl_dev_from_of_node(np_pctldev);//struct pinctrl_dev
if (pctldev)//一旦找到pin control class device则跳出for循环
break;
ops = pctldev->desc->pctlops;
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);//调用底层的callback函数处理pin configuration
//node。这也是合理的,毕竟很多的pin controller bindings是需要自己解析的
dt_remember_or_free_map(p, statename, pctldev, map, num_maps);//将该pin configuration node的mapping entry信息注册到系统中
以samsung为例,map函数就是soc初始化时提供的ctrldesc->pctlops = &samsung_pctrl_ops;全局函数,samsung_dt_node_to_map
samsung_dt_node_to_map
samsung_dt_subnode_to_map
add_map_mux(map, reserved_maps,num_maps, group, np->full_name); //设备引脚复用table
add_map_configs(dev, map, reserved_maps,num_maps, group, configs,num_configs);//设备引脚配置table
分析到这里,pinctrl 就把这个child node的pinctrl的配置信息保存到了map table中。也就是struct list_head dt_maps;
b、add_setting
这个函数就是把map table中的配置信息,添加到pinctrl handle中。本质就是将pinctrl配置信息保存在struct pinctrl_setting的mux或者configs中。
add_setting
switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_map_to_setting(map, setting);//mux,引脚复用,也就是按照功能使用的一组引脚配置
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_map_to_setting(map, setting);//config,也就是引脚上拉、下拉等电气特性
break;
list_add_tail(&setting->node, &state->settings); //保存在pinctrl handle中
struct pinctrl_setting {
struct list_head node;
enum pinctrl_map_type type;
struct pinctrl_dev *pctldev;
const char *dev_name;
union {
struct pinctrl_setting_mux mux; //mux配置数据
struct pinctrl_setting_configs configs;//config配置数据
} data;
};
c、struct pinctrl
每个使用pinctrl的设备都需要申请一个pinctrl handle。设备节点存在多个state,如idle、sleep等等,前面提到的lcd就使用了一个状态,pinctrl-names = "default";每个状态下又存在多个设置,也就是GPIO配置。前文的lcd使用了两个配置,pinctrl-0 = <&lcd_pinctrl &lcd_backlight>;一个数据读写pinctrl配置,一个背光pinctrl配置。所以soc的设备在申请pinctrl hanle时就形成了一个链表结构。
create_pinctrl
struct pinctrl *p;
p = kzalloc(sizeof(*p), GFP_KERNEL); //分配struct pinctrl并初始化,在使用pinctrl_get()请求pin控件句柄时将获取到
ret = pinctrl_dt_to_map(p, pctldev);//map
add_setting(p, pctldev, map);//分析一个mapping entry,把这个setting的代码加入到handle中
list_add_tail(&p->node, &pinctrl_list); // 把这个新增加的pinctrl handle加入到全局链表中
3、pinctrl_init_done
pinctrl_init_done
pinctrl_select_state(pins->p, pins->default_state);
pinctrl_commit_state(p, state);
/* 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);//mux set
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);//config set
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
goto unapply_new_state;
}
}
七、GPIO子系统与pinctrl子系统的关联
待续。。。
八、总结
pincrtl子系统,就是在soc初始化时枚举所有的gpio。设备注册时,分析mux和configs。mux:设备以功能为依据,所用的一组GPIO;configs:一个GPIO。