linux gpio子系统与pinctrl子系统
一、pinctrl与gpio子系统概述
1.1 gpio子系统
主要负责读/写引脚值、设置引脚为输入或输出模式、中断等基本操作。
1.2 pinctrl子系统
管理所有可控制的引脚,包括引脚的复用功能、电气特性等。
1.2.1 pinctrl子系统的主要功能包括
- 引脚复用:大多数引脚都可以通过配置寄存器来选择复用成不同的功能。
- 引脚配置:包括设置引脚的上拉/下拉电阻、速度、驱动能力等电气特性。
1.2.2与gpio子系统的关系
部分soc gpio驱动会调用pinctrl系统接口设置pin脚状态,gpio控制器下gpio-ranges属性会引用都pinctrl设备节点,同步gpio-ranges到pinctrl dev的gpio_ranges链表
二、pinctrl子系统
2.1 pinctrl节点设备树配置
rk:
pinctrl: pinctrl {
compatible = "rockchip,rk3568-pinctrl";
rockchip,grf = <&grf>;
rockchip,pmu = <&pmugrf>;
#address-cells = <2>;
#size-cells = <2>;
ranges;gpio0: gpio@fdd60000 {
compatible = "rockchip,gpio-bank";
reg = <0x0 0xfdd60000 0x0 0x100>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 0 32>;
interrupt-controller;
#interrupt-cells = <2>;
};......
ti:
main_pmx0: pinctrl@11c000 {
compatible = "pinctrl-single";//通用pinctrl驱动
/* Proxy 0 addressing */
reg = <0x0 0x11c000 0x0 0x2b4>;
#pinctrl-cells = <1>;
pinctrl-single,register-width = <32>;
pinctrl-single,function-mask = <0xffffffff>;
};......
&main_pmx0 {
......gpio_main_pins_default: gpio-main-pins-default {
pinctrl-single,pins = <
J721E_IOPAD(0x18c, PIN_INPUT, 7) /* (V23) RGMII6_RX_CTL.GPIO0_98 */
J721E_IOPAD(0x1a4, PIN_INPUT, 7) /* (W26) RGMII6_RXC.GPIO0_104 */
>;
};......
1、pinctrl驱动为soc厂商单独驱动
2、通用pinctrl驱动,compatible="pinctrl-single",引脚节点属性用pinctrl-single,pins进行配置
2.2 pinctrl子系统注册过程
2.2.1 注册过程
1、准备struct pinctrl_desc数据结构,pinctrl_pin_desc数据类里面会存所有的pin脚,包括每个pin脚的名字,每个pin脚在该pin controller的编号,npins指示多少个pin脚,pctlops,pmxops,confops提供相应的操作函数。
/**
* 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
* @num_custom_params: Number of driver-specific custom parameters to be parsed
* from the hardware description
* @custom_params: List of driver_specific custom parameters to be parsed from
* the hardware description
* @custom_conf_items: Information how to print @params in debugfs, must be
* the same size as the @custom_params, i.e. @num_custom_params
*/
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins;
unsigned int npins;
const struct pinctrl_ops *pctlops;
const struct pinmux_ops *pmxops;
const struct pinconf_ops *confops;
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
};
/**
* struct pinctrl_dev - pin control class device
* @node: node to include this pin controller in the global pin controller list
* @desc: the pin controller descriptor supplied when initializing this pin
* controller
* @pin_desc_tree: each pin descriptor for this pin controller is stored in
* this radix tree
* @pin_group_tree: optionally each pin group can be stored in this radix tree
* @num_groups: optionally number of groups can be kept here
* @pin_function_tree: optionally each function can be stored in this radix tree
* @num_functions: optionally number of functions can be kept here
* @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
* ranges are added to this list at runtime
* @dev: the device entry for this pin controller
* @owner: module providing the pin controller, used for refcounting
* @driver_data: driver data for drivers registering to the pin controller
* subsystem
* @p: result of pinctrl_get() for this device
* @hog_default: default state for pins hogged by this device
* @hog_sleep: sleep state for pins hogged by this device
* @mutex: mutex taken on each pin controller specific action
* @device_root: debugfs root for this device
*/
struct pinctrl_dev {
struct list_head node;
struct pinctrl_desc *desc;
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
};
ctrldesc->name = "rockchip-pinctrl";
ctrldesc->owner = THIS_MODULE;
ctrldesc->pctlops = &rockchip_pctrl_ops;
ctrldesc->pmxops = &rockchip_pmx_ops;
ctrldesc->confops = &rockchip_pinconf_ops;
pindesc = devm_kcalloc(&pdev->dev,
info->ctrl->nr_pins, sizeof(*pindesc),
GFP_KERNEL);
if (!pindesc)
return -ENOMEM;
ctrldesc->pins = pindesc;
ctrldesc->npins = info->ctrl->nr_pins;
pdesc = pindesc;
for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {
pin_bank = &info->ctrl->pin_banks[bank];
for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
pdesc->number = k;
pdesc->name = kasprintf(GFP_KERNEL, "%s-%d",
pin_bank->name, pin);
pdesc++;
}
}
2、将pinctrl_desc数据结构,私有结构info传递给devm_pinctrl_register进行注册,会返回一个struct pinctrl_dev,所有信息填充在pinctrl_dev,可以将pinctrl_dev看作是pin controller的一个实例。
info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
2.2.2 devm_pinctrl_register函数说明
devm_pinctrl_register---->pinctrl_register------->pinctrl_init_controller
|
|
----->pinctrl_enable
static struct pinctrl_dev *pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev,
void *driver_data)
1、分配一个pinctrl_dev
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
2、填充pinctrl_dev,前面传递过来的描述pin conroller的数据结构pinctrl_desc,pinctrl的驱动的私有数据结构都填充到pinctrl_dev,dev赋值为设备树pinctrl节点设备
/* Initialize pin control device struct */
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
.....
pctldev->dev = dev;
3、初始化三个基数树,两个链表,
pin_desc_tree:存放描述pin的数据结构struct pin_desc,pctldesc->pins也存放有数组方式存放的描述pin的struct pinctrl_pin_desc
pin_group_tree:存放struct group_desc的基数树,某个功能的pin组信息,map转setting的时候,枚举比对name,拿到group的全局编号,私有驱动可以自己的方式存放
pin_function_tree:存放struct function_desc,某个功能的描述信息,map转setting的时候,枚举比对name,拿到func的全局编号,私有驱动可以自己的方式存放
node:挂载到全局变量pinctrldev_list,创建pinctrl的时候会通过pin脚节点查找父目录pinctrl dev节点,枚举pinctrldev_list,比对of_node来查找pin脚节点对应的pinctrl dev
gpio_ranges:struct pinctrl_gpio_range信息挂载链表
list_add_tail(&pctldev->node, &pinctrldev_list);
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL);
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
INIT_RADIX_TREE(&pctldev->pin_function_tree, GFP_KERNEL);
#endif
INIT_LIST_HEAD(&pctldev->gpio_ranges);
INIT_LIST_HEAD(&pctldev->node);
4、将pctldesc->pins以数组存放的描述pin的数据结构struct pinctrl_pin_desc以基数树的形式再存到pctldev->pin_desc_tree中
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
......
static int pinctrl_register_pins(struct pinctrl_dev *pctldev,
const struct pinctrl_pin_desc *pins,
unsigned num_descs)
{
unsigned i;
int ret = 0;
for (i = 0; i < num_descs; i++) {
ret = pinctrl_register_one_pin(pctldev, &pins[i]);
if (ret)
return ret;
}
return 0;
}
......
static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
const struct pinctrl_pin_desc *pin)
{
struct pin_desc *pindesc;
pindesc = pin_desc_get(pctldev, pin->number);
if (pindesc) {
dev_err(pctldev->dev, "pin %d already registered\n",
pin->number);
return -EINVAL;
}
pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
if (!pindesc)
return -ENOMEM;
/* Set owner */
pindesc->pctldev = pctldev;
/* Copy basic pin info */
if (pin->name) {
pindesc->name = pin->name;
} else {
pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", pin->number);
if (!pindesc->name) {
kfree(pindesc);
return -ENOMEM;
}
pindesc->dynamic_name = true;
}
pindesc->drv_data = pin->drv_data;
radix_tree_insert(&pctldev->pin_desc_tree, pin->number, pindesc);
pr_debug("registered pin %d (%s) on %s\n",
pin->number, pindesc->name, pctldev->desc->name);
return 0;
}
2.2.3 各个数据结构关系

三、消费者使用pinctrl方法
前面章节讲的是pinctrl控制器怎么注册到系统,控制器的引脚数量,引脚分组,引脚的复用功能、电气特性寄存器信息,操作函数等注册到系统中,本章节说明作为消费者,其他驱动或内核代码怎么使用pinctrl配置复用功能及电气特性,通过使用来了解pinctrl架构为什么这样设计。
3.1 设备树中配置pinctrl处理过程
3.1.1 要使用pinctrl配置引脚,怎么配置设备树
要使用pinctrl配置引脚的复用功能及电气特性,需要在要使用的设备节点里加上属性pinctrl-0、 pinctrl-1等, 命名pinctrl-names = "default","......"等,pinctrl-0取名对应pinctrl-names的第一个字符串,pinctrl-1取名对应pinctrl-names第二个字符串,如果没有"init"名的pinctrl配置,那么会在driver_probe_device--->really_probe--->pinctrl_bind_pins调用时配置成"default"的pinctrl的配置。
pinctrl-x所引用的pins group设备节点必须在pinctrl设备节点里面,pins group设备节点怎么样写取决于pinctrl驱动,解析pins group设备节点是调用驱动的dt_node_to_map函数处理的
pins脚功能处理
&main_pmx0 {
......
main_i2c0_pins_default: main-i2c0-pins-default {
pinctrl-single,pins = <
J721E_IOPAD(0x220, PIN_INPUT_PULLUP, 0) /* (AC5) I2C0_SCL */
J721E_IOPAD(0x224, PIN_INPUT_PULLUP, 0) /* (AA5) I2C0_SDA */
>;
};......
}
&main_i2c0 {
pinctrl-names = "default";
pinctrl-0 = <&main_i2c0_pins_default>;
clock-frequency = <400000>;
};
3.1.2 配置完后,内核是怎么处理的
设备注册的时候
device_add--->bus_probe_device--->device_initial_probe--->__device_attach--->__device_attach_driver--->driver_probe_device--->really_probe--->pinctrl_bind_pins
driver注册的时候
driver_register--->bus_add_driver--->driver_attach--->__driver_attach--->device_driver_attach--->driver_probe_device--->really_probe--->pinctrl_bind_pins
在driver或device注册过程中,匹配到合适的设备或驱动调用probe的函数里会调用pinctrl_bind_pins,设备节点里的配置的pinctrl-x主要在这里进行自动引脚配置。
int pinctrl_bind_pins(struct device *dev)
1、struct device结构体有个struct dev_pin_info *pins变量,用来存放设备节点的pinctrl信息的,第一步先获取pinctrl
struct dev_pin_info {
struct pinctrl *p;
struct pinctrl_state *default_state;
struct pinctrl_state *init_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};
......
/**
* struct pinctrl - per-device pin control state holder
* @node: global list node
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
* this device, if any
* @users: reference count
*/
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;
};
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;
dev->pins->p = devm_pinctrl_get(dev);
struct pinctrl *devm_pinctrl_get(struct device *dev)
1.1、先看看是不是已经存在p = find_pinctrl(dev),先从全局链表pinctrl_list查找,以p->dev == dev作匹配条件
1.2、没找到创建一个pinctl
return create_pinctrl(dev, NULL);
static struct pinctrl *create_pinctrl(struct device *dev,struct pinctrl_dev *pctldev)
1.2.1 分配一个pinctrl,并初始化一些变量
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return ERR_PTR(-ENOMEM);
p->dev = dev;
INIT_LIST_HEAD(&p->states);
INIT_LIST_HEAD(&p->dt_maps);
1.2.2 解析设备节点的pinctrl设置,并用struct pinctrl_map数据结构保存信息
ret = pinctrl_dt_to_map(p, pctldev);
int pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev)
1.2.2.1 循环查找pinctrl-%d,直到of_find_property失败
/* For each defined state ID */
for (state = 0; ; state++) {
/* Retrieve the pinctrl-* property */
propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
prop = of_find_property(np, propname, &size);
kfree(propname);
if (!prop) {
if (state == 0) {
of_node_put(np);
return -ENODEV;
}
break;
}
1.2.2.2 枚举pinctrl-x所有引用的句柄,调用dt_to_map_one_config进行解析pin脚节点
for (config = 0; config < size; config++) {
phandle = be32_to_cpup(list++);
/* Look up the pin configuration node */
np_config = of_find_node_by_phandle(phandle);
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,
np_config);
static int dt_to_map_one_config(struct pinctrl *p, struct pinctrl_dev *hog_pctldev,const char *statename,struct device_node *np_config)
1.2.2.2.1 查找pins节点的父节点,一步步往父节点查,直到找到pinctrl dev节点,以pctldev->dev->of_node == np进行判断
/* Find the pin controller containing np_config */
np_pctldev = of_node_get(np_config);
for (;;) {
if (!allow_default)
allow_default = of_property_read_bool(np_pctldev,
"pinctrl-use-default");
np_pctldev = of_get_next_parent(np_pctldev);
list_for_each_entry(pctldev, &pinctrldev_list, node)
if (pctldev->dev->of_node == np) {
mutex_unlock(&pinctrldev_list_mutex);
return pctldev;
}
1.2.2.2.2 调用pinctrl_dev的pinctrl_desc的pctlops函数,真正的开始解析设备树pin脚节点,转换成struct pinctrl_map。
/*
* Call pinctrl driver to parse device tree node, and
* generate mapping table entries
*/
ops = pctldev->desc->pctlops;
if (!ops->dt_node_to_map) {
dev_err(p->dev, "pctldev %s doesn't support DT\n",
dev_name(pctldev->dev));
return -ENODEV;
}
ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
1.2.2.2.3 把struct pinctrl_map信息进一步填充并存下来,一是以struct pinctrl_dt_map存下来,挂载到pinctrl的dt_maps链表;另一个是存入struct pinctrl_maps,挂载到全局链表pinctrl_maps
/* Initialize common mapping table entry fields */
for (i = 0; i < num_maps; i++) {
const char *devname;
devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
if (!devname)
goto err_free_map;
map[i].dev_name = devname;
map[i].name = statename;
if (pctldev)
map[i].ctrl_dev_name = dev_name(pctldev->dev);
}
/* Remember the converted mapping table entries */
dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
if (!dt_map)
goto err_free_map;
dt_map->pctldev = pctldev;
dt_map->map = map;
dt_map->num_maps = num_maps;
list_add_tail(&dt_map->node, &p->dt_maps);
1.2.3 扫描1.2.2.2.3存入的pinctrl_maps链表,比对当前设备节点的名字与map的dev_name是否匹配,匹配说明是当前设备节点配置的pinctrl。
/* Iterate over the pin control maps to locate the right ones */
for_each_maps(maps_node, i, map) {
/* Map must be for this device */
if (strcmp(map->dev_name, devname))
continue;
/*
* If pctldev is not null, we are claiming hog for it,
* that means, setting that is served by pctldev by itself.
*
* Thus we must skip map that is for this device but is served
* by other device.
*/
if (pctldev &&
strcmp(dev_name(pctldev->dev), map->ctrl_dev_name))
continue;
1.2.4 map转换成struct pinctrl_setting,然后存到pinctrl数据结构下
ret = add_setting(p, pctldev, map);
static int add_setting(struct pinctrl *p, struct pinctrl_dev *pctldev,const struct pinctrl_map *map)
1.2.4.1 pinctrl_setting是挂载在state->settings下,state是挂载在p->states,所有要增加setting就要先查找到state,以map->name查找,这个name就是pinctrl-names配置的名字解析过程赋值给map->name的,找不到就创建一个state,然后挂载到p->states,
struct pinctrl_state *state;
struct pinctrl_setting *setting;
int ret;
state = find_state(p, map->name);
if (!state)
state = create_state(p, map->name);
if (IS_ERR(state))
return PTR_ERR(state);
1.2.4.2 pinctrl_setting赋值,type, dev_name,pctldev变量赋值
setting->type = map->type;
if (pctldev)
setting->pctldev = pctldev;
else
setting->pctldev =
get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (!setting->pctldev) {
kfree(setting);
/* Do not defer probing of hogs (circular loop) */
if (!strcmp(map->ctrl_dev_name, map->dev_name))
return -ENODEV;
/*
* OK let us guess that the driver is not there yet, and
* let's defer obtaining this pinctrl handle to later...
*/
dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
map->ctrl_dev_name);
return -EPROBE_DEFER;
}
setting->dev_name = map->dev_name;
1.2.4.3 填充个pinmux或者pinconf配置。mux配置填充func, group的全局编号(setting->data.mux.func,setting->data.mux.group),conf配置填充setting->data.configs.group_or_pin,setting->data.configs.num_configs,setting->data.configs.configs = map->data.configs.configs。
switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_map_to_setting(map, setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_map_to_setting(map, setting);
break;
default:
ret = -EINVAL;
break;
}
2、获取default_state,init_state,sleep_state,idle_state这些pinctrl_state,pinctrl_lookup_state查找方法是从struct pinctrl中的states链表找出对应名字的state。
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
......
3、有"init" pinctrl配置则选定"init"引脚配置,否则选定"default" pinctrl配置
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state);
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
3.1 判断pinctrl当前的状态p->state与要设置的状态state是否一致,一致再调用pinctrl_commit_state
/**
* pinctrl_select_state() - select/activate/program a pinctrl state to HW
* @p: the pinctrl handle for the device that requests configuration
* @state: the state handle to select/activate/program
*/
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
{
if (p->state == state)
return 0;
return pinctrl_commit_state(p, state);
}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)
3.1.1 如果之前已经p->state设置过,那么要对&p->state->settings下的所有setting的pin脚如果是mux设置类型,那要进行pinmux_disable_setting,主要是对存放在pctldev->pin_desc_tree的struct pin_desc的mux_usecount,mux_owner ,mux_setting变量进行free设置。
if (!gpio_range) {
/*
* A pin should not be freed more times than allocated.
*/
if (WARN_ON(!desc->mux_usecount))
return NULL;
desc->mux_usecount--;
if (desc->mux_usecount)
return NULL;
}
/*
* If there is no kind of request function for the pin we just assume
* we got it by default and proceed.
*/
if (gpio_range && ops->gpio_disable_free)
ops->gpio_disable_free(pctldev, gpio_range, pin);
else if (ops->free)
ops->free(pctldev, pin);
if (gpio_range) {
owner = desc->gpio_owner;
desc->gpio_owner = NULL;
} else {
owner = desc->mux_owner;
desc->mux_owner = NULL;
desc->mux_setting = NULL;
}
3.1.2 枚举state->settings下的所有配置进行配置
/* 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);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
goto unapply_new_state;
}
/* Do not link hogs (circular dependency) */
if (p != setting->pctldev->p)
pinctrl_link_add(setting->pctldev, p->dev);
}
int pinmux_enable_setting(const struct pinctrl_setting *setting)
3.1.2.1 获取setting->data.mux.group的pins编号及数量num_pins
if (pctlops->get_group_pins)
ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
&pins, &num_pins);
3.1.2.2 枚举所有pins,然后调用pin_request,pin_request:1.判断是不是被别的设备节点占用设置过,是的话返回错误,2、mux_owner,mux_usecount变量进行设置,3、status=ops->request/status=0
/* Try to allocate all pins in this group, one by one */
for (i = 0; i < num_pins; i++) {
ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
3.1.2.3 设置当前pin_desc的mux_setting
/* Now that we have acquired the pins, encode the mux setting */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
dev_warn(pctldev->dev,
"could not get pin desc for pin %d\n",
pins[i]);
continue;
}
desc->mux_setting = &(setting->data.mux);
}
3.1.2.4 真正进行mux配置
ret = ops->set_mux(pctldev, setting->data.mux.func,
setting->data.mux.group);
3.1.3 赋值p->state为设置的state
p->state = state;
3.2 驱动中主动调用进行引脚配置
调用pinctrl相关api,进行相关引脚配置,相关接口位于linux/pinctrl/consumer.h
1.部分soc gpio驱动会调用以下驱动进行pinctrl设置。
/* External interface to pin control */
extern bool pinctrl_gpio_can_use_line(unsigned gpio);
extern int pinctrl_gpio_request(unsigned gpio);
extern void pinctrl_gpio_free(unsigned gpio);
extern int pinctrl_gpio_direction_input(unsigned gpio);
extern int pinctrl_gpio_direction_output(unsigned gpio);
extern int pinctrl_gpio_set_config(unsigned gpio, unsigned long config);
2.驱动中使用pinctrl_lookup_state,pinctrl_select_state可以设置设备树中配置好的某种pin脚状态
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 struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
extern void devm_pinctrl_put(struct pinctrl *p);
extern int pinctrl_select_default_state(struct device *dev);
static inline struct pinctrl * __must_check pinctrl_get_select(
struct device *dev, const char *name);
四、gpio子系统
4.1 gpio驱动
gpio驱动一些操作函数在驱动文件中,系统框架代码在接口代码drivers/gpio/gpiolib.c,我们来看一看soc的gpio驱动怎么写的
1、gpio控制器设备节点
1、soc gpio控制器
main_gpio0: gpio@600000 {
compatible = "ti,j721e-gpio", "ti,keystone-gpio";
reg = <0x0 0x00600000 0x0 0x100>;
gpio-controller;
#gpio-cells = <2>;
interrupt-parent = <&main_gpio_intr>;
interrupts = <256>, <257>, <258>, <259>,
<260>, <261>, <262>, <263>;
interrupt-controller;
#interrupt-cells = <2>;
ti,ngpio = <128>;
ti,davinci-gpio-unbanked = <0>;
power-domains = <&k3_pds 105 TI_SCI_PD_EXCLUSIVE>;
clocks = <&k3_clks 105 0>;
clock-names = "gpio";
};2、挂在i2c下的外部gpio控制器
&i2c2 {
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";pca9534: gpio@20 {
compatible = "nxp,pca9534";
reg = <0x20>;
gpio-controller;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pca9534>;
interrupt-parent = <&gpio1>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
#gpio-cells = <2>;
wakeup-source;/* USB 3.0 OTG (usbotg1) / SATA port switch, set to USB 3.0 */
usb3-sata-sel-hog {
gpio-hog;
gpios = <4 GPIO_ACTIVE_HIGH>;
output-low;
line-name = "usb3_sata_sel";
};......
2、驱动处理步骤,先准备一个私有结构体,里面含有结构体struct gpio_chip chip
struct davinci_gpio_controller {
struct gpio_chip chip;
struct irq_domain *irq_domain;
/* Serialize access to GPIO registers */
spinlock_t lock;
void __iomem *regs[MAX_REGS_BANKS];
int gpio_unbanked;
int irqs[MAX_INT_PER_BANK];
};
3、填充 struct gpio_chip chip,一些gpio信息和gpio操作函数
chips->chip.label = dev_name(dev);
chips->chip.direction_input = davinci_direction_in;
chips->chip.get = davinci_gpio_get;
chips->chip.direction_output = davinci_direction_out;
chips->chip.set = davinci_gpio_set;
chips->chip.ngpio = ngpio;
chips->chip.base = pdata->no_auto_base ? pdata->base : -1;
#ifdef CONFIG_OF_GPIO
chips->chip.of_gpio_n_cells = 2;
chips->chip.parent = dev;
chips->chip.of_node = dev->of_node;
chips->chip.request = gpiochip_generic_request;
chips->chip.free = gpiochip_generic_free;
#endif
4、创建struct gpio_device *gdev,注册到gpio系统
ret = devm_gpiochip_add_data(dev, &chips->chip, chips);
int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key)
1、分配一个struct gpio_device *gdev,填充gdev,给gdev赋值,如gpio_chip, devname,dev.of_node,分配id,为每个gpio分配一个struct gpio_desc, 未指定起始编号则分配起始编号,最开始注册gpio设备起始编号为int base = ARCH_NR_GPIOS - ngpio
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
#ifdef CONFIG_GPIO_CDEV
/* debounce period in microseconds */
unsigned int debounce_period_us;
#endif
};
2、将gdev加入链表gpio_devices,编号最大的放在链表头
ret = gpiodev_add_to_list(gdev);
3、处理gpio chip设备树相关的东西,解析设备树gpio-ranges属性,gpio-range同步添加到pinctrl设备,挂载到pctldev->gpio_ranges链表上,of_gpiochip_scan_gpios扫描gpio控制器下的独占脚即gpio控制器设备节点下的child节点gpio-hog属性,gpios属性配置的gpio,gpio请求并配置引脚输入或输出
/**
* struct gpio_pin_range - pin range controlled by a gpio chip
* @node: list for maintaining set of pin ranges, used internally
* @pctldev: pinctrl device which handles corresponding pins
* @range: actual range of pins controlled by a gpio controller
*/
struct gpio_pin_range {
struct list_head node;
struct pinctrl_dev *pctldev;
struct pinctrl_gpio_range range;
};
ret = of_gpiochip_add(gc);
1、赋值gpio属性参数格式,及从设备树获取gpio编号函数,通用编号获取函数设定设备树指定的gpio编号参数对应gdev->descs数组的索引,gdev->descs[gpio编号参数]即可获取到struct gpio_desc
if (!chip->of_xlate) {
chip->of_gpio_n_cells = 2;
chip->of_xlate = of_gpio_simple_xlate;
}
2、初始化gpio_chip数据结构的valid_mask变量,将保留的gpio脚有效位清除
of_gpiochip_init_valid_mask(chip);
3、扫描gpio-ranges属性
ret = of_gpiochip_add_pin_range(chip);
4、处理gpio控制器占用的引脚
ret = of_gpiochip_scan_gpios(chip);
4、gpio驱动valid_mask设置,设置gpio dev的gpio_desc flags变量输入输出默认属性,加入驱动中单独设置的gpio_pin_range
for (i = 0; i < gc->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
if (gc->get_direction && gpiochip_line_is_valid(gc, i)) {
assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->get_direction(gc, i));
} else {
assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->direction_input);
}
}
......
ret = gpiochip_add_pin_ranges(gc);
5、注册gpio控制器字符设备(应用层可以用来操作gpio),注册gpio控制器到sysfs(调试,应用层操作gpio)
ret = gpiochip_setup_dev(gdev);
......
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
int ret;
ret = gcdev_register(gdev, gpio_devt);
if (ret)
return ret;
ret = gpiochip_sysfs_register(gdev);
if (ret)
goto err_remove_device;
/* From this point, the .release() function cleans up gpio_device */
gdev->dev.release = gpiodevice_release;
dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
return 0;
err_remove_device:
gcdev_unregister(gdev);
return ret;
}
4.2 内核中使用gpio
gpio消费者无需关系gpio驱动具体实现方法,使用gpio系统提供的gpio接口来使用gpio
接口代码drivers/gpio/gpiolib.c,通过接口代码实现我们来理解gpio驱动为什么要那么写及框架辑
先说一下内核中使用gpio的步骤
4.2.1 gpio使用步骤
接口头文件在linux/gpio/consumer.h
4.2.1.1 通过gpio编号调用,gpio编号设备树中*-gpio/*-gpios属性指定
1、获取gpio编号(获取gpio也是先通过of_get_named_gpiod_flags获取到gpiod,然后再转换成gpio),获取flags
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
of_get_named_gpio_flags(phynode, "reset-gpios", 0, &(data->cfg[i].gpio_flags));
2、gpio_request请求占有gpio,或者用gpio_request_one直接请求,配置输入输出一次性调用完,配置输出功能是直接输出高低电平
int gpio_request(unsigned gpio, const char *label)
/**
* gpio_request_one - request a single GPIO with initial configuration
* @gpio: the GPIO number
* @flags: GPIO configuration as specified by GPIOF_*
* @label: a literal description string of this GPIO
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
3、配置输入输出,输出值是电平高低,没有逻辑转换值
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
4、获取输入/设置输出值,这里接口都是gpio电平高低,没有逻辑转换
static inline int gpio_get_value(unsigned int gpio)
{
return __gpio_get_value(gpio);
}
static inline void gpio_set_value(unsigned int gpio, int value)
{
__gpio_set_value(gpio, value);
}
/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
* the GPIO is constant and refers to some always-present controller,
* giving direct access to chip registers and tight bitbanging loops.
*/
static inline int __gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
static inline void __gpio_set_value(unsigned gpio, int value)
{
return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}
4.2.1.2 通过gpio_desc调用接口
1、获取gpio_desc,并进行输入输出设置
devm_gpiod_get_index_optional/devm_gpiod_get_index/gpiod_get_index/gpiod_get_index_optional
这几个函数是嵌套关系,功能主要是通过设备节点指定的gpio进行初始化并使能/不使能,主要做了以下几个动作
a、从设备节点中获取*-gpios/*-gpio属性,第一个参数为gpio控制器节点的引用,从gpio_devices链表中通过比对设备of_node拿到gpio_chip, 获取gpio编号(直接提取或转换,看of_xlate函数是不是单独提供的,这个编号属于gpio控制器内的局部编号,不是整个系统的编号,全局编号chip->base + gpio编号),获取flags,以获取到的gpio编号为索引从gdev->descs数组获取到gpio_desc
说明:还有一种方法是不从设备树获取gpio,驱动中注册gpio设备消费者,通过调用gpiod_add_lookup_table实现
flags值,gpios/gpio属性的第三个值
enum gpio_lookup_flags {GPIO_ACTIVE_HIGH = (0 << 0),
GPIO_ACTIVE_LOW = (1 << 0),
GPIO_OPEN_DRAIN = (1 << 1),
GPIO_OPEN_SOURCE = (1 << 2),
GPIO_PERSISTENT = (0 << 3),
GPIO_TRANSITORY = (1 << 3),
GPIO_PULL_UP = (1 << 4),
GPIO_PULL_DOWN = (1 << 5),
GPIO_LOOKUP_FLAGS_DEFAULT = GPIO_ACTIVE_HIGH | GPIO_PERSISTENT,
};
b、调用gpiod_request,这个函数的作用1、desc->flags FLAG_REQUESTED是否置位,置位说明占用返回错误,未置为则置为该位表示占用,2、调用gpio_chip的request函数指针,这里面判断如果存在pin_ranges则会调用pin_request,3、设定gpio当前方向
/*
* If a connection label was passed use that, else attempt to use
* the device name as label
*/
ret = gpiod_request(desc, con_id ? con_id : devname);
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
*/
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
struct gpio_chip *gc = desc->gdev->chip;
int ret;
unsigned long flags;
unsigned offset;
if (label) {
label = kstrdup_const(label, GFP_KERNEL);
if (!label)
return -ENOMEM;
}
spin_lock_irqsave(&gpio_lock, flags);
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
desc_set_label(desc, label ? : "?");
ret = 0;
} else {
kfree_const(label);
ret = -EBUSY;
goto done;
}
if (gc->request) {
/* gc->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
offset = gpio_chip_hwgpio(desc);
if (gpiochip_line_is_valid(gc, offset))
ret = gc->request(gc, offset);
else
ret = -EINVAL;
spin_lock_irqsave(&gpio_lock, flags);
if (ret < 0) {
desc_set_label(desc, NULL);
kfree_const(label);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
}
if (gc->get_direction) {
/* gc->get_direction may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_get_direction(desc);
spin_lock_irqsave(&gpio_lock, flags);
}
done:
spin_unlock_irqrestore(&gpio_lock, flags);
return ret;
}
c、根据从设备树获取到的lookupflags参数设置desc->flags相关位,包括低有效,开漏,开源,上下拉,接口传过来的参数flags如果设置了in/out相关功能则设置输入输出,特别说明这里传过来的flag,是表示有效或无效化pin脚,不是直接高低电平,会经过是不是低有效判断转换成高低电平
/**
* Optional flags that can be passed to one of gpiod_* to configure direction
* and output value. These values cannot be OR'd.
*/
enum gpiod_flags {
GPIOD_ASIS = 0,
GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,
GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_DIR_VAL,
GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_OPEN_DRAIN,
GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_OPEN_DRAIN,
};
......
ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
gpiod_direction_output
a、对开漏,开源,推挽进行设置gpio_set_config
b、上下拉进行设置gpio_set_config(desc, bias)//驱动里头gc->set_config可能位空,上下拉也可能在pin里指定
c、调用最后的设置输出并输出值gpiod_direction_output_raw_commit,这个函数里头会调用驱动direction_output函数指针进行设置寄存器,如输入输出方向寄存器,设置输出高低电平寄存器
2、控制gpio输出/获取gpio pin脚输入
gpiod_set_value_cansleep/gpiod_set_value:输出gpio逻辑值,value为逻辑有效值;
gpiod_get_value_cansleep/gpiod_get_value:获取gpio逻辑值,返回值为逻辑有效值;
gpiod_set_raw_value/gpiod_set_raw_value_cansleep:输出gpio电平高低,value为gpio实际高低电平;
gpiod_get_raw_value/gpiod_get_raw_value_cansleep:获取gpio引脚电平,返回值为引脚高低电平;
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
void gpiod_set_value(struct gpio_desc *desc, int value)
int gpiod_get_value(const struct gpio_desc *desc)
/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
int gpiod_get_raw_value(const struct gpio_desc *desc)
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
五、上层应用使用gpio
5.1 通过/sys/class/gpio/操作gpio
1、指令操作
1. 导出 /sys/class/gpio# echo 203 > export
2. 设置方向 /sys/class/gpio/gpio203# echo out > direction
3. 查看方向 /sys/class/gpio/gpio203# cat direction
4. 设置输出 /sys/class/gpio/gpio203# echo 1 > value
5. 查看输出值 /sys/class/gpio/gpio203# cat value
6. 取消导出 /sys/class/gpio# echo 203 > unexport
2、代码编写
输出高低电平
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#define GPIO_INDEX "42"
static char gpio_path[75];
int gpio_init(char *name)
{
int fd;
sprintf(gpio_path, "/sys/class/gpio/gpio%s", name);
//查询文件是否存在
if (access("gpio_path", F_OK)){
//只读方式打开
fd = open("/sys/class/gpio/export", O_WRONLY);
//打开文件失败
if(fd < 0)
return 1 ;
write(fd, name, strlen(name));
close(fd);
//初始化gpio使用的引脚为输出模式
sprintf(gpio_path, "/sys/class/gpio/gpio%s/direction", name);
fd = open(gpio_path, O_WRONLY);
if(fd < 0)
return 2;
write(fd, "out", strlen("out"));
close(fd);
}
return 0;
}
//向unexport文件写入编号,取消导出
int gpio_deinit(char *name)
{
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if(fd < 0)
return 1;
write(fd, name, strlen(name));
close(fd);
return 0;
}
//输出高电平
int gpio_high(char *name)
{
int fd;
sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
fd = open(gpio_path, O_WRONLY);
if(fd < 0){
printf("open gpio%s wrong\n",name);
return -1;
}
if(2 != write(fd, "1", sizeof("1")))
printf("wrong set \n");
close(fd);
return 0;
}
//输出低电平
int gpio_low(char *name)
{
int fd;
sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
fd = open(gpio_path, O_WRONLY);
if(fd < 0){
printf("open gpio%s wrong\n",name);
return -1;
}
if(2 != write(fd, "0", sizeof("0")))
printf("wrong set \n");
close(fd);
return 0;
}
int main(int argc, char *argv[])
{
char buf[10];
int res;
/* 校验传参 */
if (2 != argc) {
printf( "usage: %s <PinNum>\n",argv[0]);
return -1;
}
res = gpio_init(argv[1]);
if(res){
printf("gpio init error,code = %d",res);
return 0;
}
while(1){
printf("Please input the value : 0--low 1--high q--exit\n");
scanf("%10s", buf);
switch (buf[0]){
case '0':
gpio_low(argv[1]);
break;
case '1':
gpio_high(argv[1]);
break;
case 'q':
gpio_deinit(argv[1]);
printf("Exit\n");
return 0;
default:
break;
}
}
return 0;
}
获取gpio电平
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename = "/sys/devices/platform/apb/10010000.pinctrl/gpio/gpio66/value";
char gpio_in_value;if (access(filename, F_OK))
system("echo 66 > /sys/class/gpio/export");fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}read(fd, &gpio_in_value, 1);
printf("gpio in value:%d.\r\n", gpio_in_value);lseek(fd, 0, SEEK_SET);
read(fd, &gpio_in_value, 1);
printf("gpio in value:%d.\r\n", gpio_in_value);retvalue = close(fd);
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
gpio应用层中断
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>int main(int argc, char *argv[])
{
char buff[1024];
int gpio_id;
struct pollfd fds[1];
int gpio_fd = open("/sys/class/gpio/gpio508/value", O_RDONLY);
if (gpio_fd == -1)
printf("gpio open");
fds[0].fd = gpio_fd;
fds[0].events = POLLPRI;
int ret = read(gpio_fd, buff, 10);
if (ret == -1)
printf("read");while (1)
{
ret = poll(fds, 1, -1);
if (ret == -1)
printf("poll");if (fds[0].revents & POLLPRI)
{
ret = lseek(gpio_fd, 0, SEEK_SET);
if (ret == -1)
printf("lseek");
ret = read(gpio_fd, buff, 10);
if (ret == -1)
printf("read");
printf("get interrupt\n");
}
}
}
5.2 通过/dev/gpiochipX字符设备操作gpio
参见kernel/tools/gpio,有控制输入输出,获取中断的例子
5.3 通过写寄存器操作gpio
先对寄存器地址进行mmap,再进行操作寄存器
六、pinctrl调试
CONFIG_DEBUG_FS pinctrl debugfs配置开关
/sys/kernel/debug/pinctrl/底下的几个文件说明
pinctrl-devices:pinctrl控制器设备对应的驱动是否有pmxops,confops

pinctrl-maps:枚举maps_node下的所有map,其实就是所有states下的所有setting

pinctrl-handles:枚举pinctrl_list,显示该pinctrl的设备节点,当前使用的哪个pinctrl-x配置,也就是当前的pinctrl_state,然后枚举p->states,枚举state->settings,显示所有states下的setting

pinctrl设备信息注册到debugfs:
pinctrl_enable会调用pinctrl_init_device_debugfs,这里会创建目录pinctrl_dev->dev名字-pinctrl_desc名字,一般情况下等于设备名-驱动名,下面对创建的文件说明
pins:枚举所有pin脚pctldev->desc->npins,显示pin number,pin名字,gpio_ranges信息,调用驱动pin_dbg_show显示其他信息(pinctrl-single驱动显示:寄存器地址 寄存器值 驱动名)

pingroups:枚举pingroups,显示pin group用的pin 编号与名字

gpio-ranges:显示pctrldev->gpio_ranges信息,gpio范围对应的pin范围
pinmux-pins:枚举所有pin脚,显示所有pin脚被哪个节点占用,功能,pin组

pinmux-functions:显示所有function及function下的pin组

七、gpio调试
相关调试的配置开关
CONFIG_GPIO_CDEV //控制gpio的字符设备/dev/gpiochip%d
CONFIG_DEBUG_FS // /sys/kernel/debug/gpio配置开关
CONFIG_GPIO_SYSFS /sys/class/gpio 配置开关
八、参考文章
Linux下通过sysfs方式控制GPIO(/sys/class/gpio)_sysfs gpio-CSDN博客
学习笔记之Linux的GPIO控制_linux gpio-CSDN博客
更多推荐
所有评论(0)