参考资料:
Linux 4.x内核:
- Documentation\pinctrl.txt
- Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt
- arch/arm/boot/dts/imx6ull-14x14-evk.dts
- arch/arm/boot/dts/100ask_imx6ull-14x14.dts
- drivers\pinctrl\freescale\pinctrl-imx6ul.c
- drivers\pinctrl\freescale\pinctrl-imx.c
一、回顾Pinctrl的三大作用
pinctrl的三大作用:
- 引脚枚举与命名(Enumerating and naming)
- 单个引脚
- 各组引脚
- 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
- 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度
Pinctrl驱动程序的核心是构造一个pinctrl_desc结构体:
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
};
1.1 作用1:描述、获得引脚
分为2部分:
- 描述、获得单个引脚的信息
- 描述、获得某组引脚的信息
struct pinctrl_desc {
const char *name;
const struct pinctrl_pin_desc *pins; //单个引脚
unsigned int npins; //单个引脚个数
const struct pinctrl_ops *pctlops; //各组引脚
//....
};
struct pinctrl_ops {
int (*get_groups_count) (struct pinctrl_dev *pctldev);
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset);
int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
struct device_node *np_config,
struct pinctrl_map **map, unsigned *num_maps);
void (*dt_free_map) (struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps);
};
1.2 作用2:引脚复用
用来把某组引脚(group)复用为某个功能(function)。
struct pinctrl_desc {
//...
const struct pinmux_ops *pmxops;
//...
};
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
int (*get_functions_count) (struct pinctrl_dev *pctldev);
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,
unsigned selector,
const char * const **groups,
unsigned *num_groups);
int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,
unsigned group_selector);
int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset);
void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset);
int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset,
bool input);
bool strict;
};
1.3 作用3:引脚配置
用来配置:某个引脚(pin)或某组引脚(group)。
struct pinctrl_desc {
//...
const struct pinconf_ops *confops;
//...
};
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
bool is_generic;
#endif
int (*pin_config_get) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *config);
int (*pin_config_set) (struct pinctrl_dev *pctldev,
unsigned pin,
unsigned long *configs,
unsigned num_configs);
int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *config);
int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
unsigned selector,
unsigned long *configs,
unsigned num_configs);
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);
};
二、硬件功能
假设这个虚拟的pin controller有4个引脚:

- pin0,1,2,3都可以配置为GPIO功能
- pin0,1还可以配置为I2C功能
- pin2,3还可以配置为UART功能
三、编写Pinctrl驱动程序要做什么
- 编写pinctroller的设备树
- 编写使用pinctroller的client设备树
virtual_pincontroller {
compatible = "100ask,virtual_pinctrl";
i2cgrp: i2cgrp {
functions = "i2c", "i2c";
groups = "pin0", "pin1";
configs = <0x11223344 0x55667788>;
};
};
virtual_i2c {
compatible = "100ask,virtual_i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2cgrp>;
};
- 编写Pinctroller驱动程序
- 编写client驱动程序
四、编写Pinctroller驱动程序
4.1 核心pinctrl_desc
分配、设置、注册pinctrl_desc结构体
4.2 辅助函数
include/linux/of.h
for_each_child_of_node
of_get_child_count
of_find_property
of_property_read_u32
of_property_read_u32_index
of_property_read_string_index
4.3 编写pinctroller驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include "core.h"
static struct pinctrl_dev *g_pinctrl_dev;
static const struct pinctrl_pin_desc pins[] = {
{ 0, "pin0", NULL }, //描述4个引脚,有num,名字,私有数据
{ 1, "pin1", NULL },
{ 2, "pin2", NULL },
{ 3, "pin3", NULL },
};
static unsigned long g_configs[4];
struct virtual_functions_desc {
const char *func_name;
const char **groups;
int num_groups;
};
static const char *function_group0[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *function_group1[] = {"pin0", "pin1"};
static const char *function_group2[] = {"pin2", "pin3"};
static struct virtual_functions_desc g_funcs_des[] = { //不同的复用功能对应的引脚
{ "gpio", function_group0, 4 },
{ "i2c", function_group1, 2 },
{ "uart", function_group2, 2 },
};
//从引脚信息中获取引脚个数
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
return pctldev->desc->npins;
}
//直接返回引脚的名字
static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
return pctldev->desc->pins[selector].name;
}
//获取组的所有引脚信息
static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
const unsigned **pins, unsigned *npins)
{
if(selector >= pctldev->desc->npins)
return -EINVAL;
*pins = &pctldev->desc->pins[selector].number; //引脚组的序号
*npins = 1;
return 0;
}
//打印debug信息
static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset)
{
seq_printf(s, "%s", dev_name(pctldev->dev));
}
/*
i2cgrp: i2cgrp {
functions = "i2c", "i2c";
groups = "pin0", "pin1";
configs = <0x11223344 0x55667788>;
};
one pin ===> two pinctrl_map (one for mux, one for config)
*/
//把设备树中的节点转换成pinctrl_map
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
int i;
int num_pins = 0;
const char *pin;
const char *function;
unsigned int config;
struct pinctrl_map *new_map;
unsigned long *configs;
/* 1.确定引脚个数 */
while(1) {
if(of_property_read_string_index(np, "groups", num_pins, &pin) == 0)
num_pins++;
else
break;
}
new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);
for(i=0;i<num_pins;i++) {
/* 2.取出引脚 */
of_property_read_string_index(np, "functions", i, &function);
of_property_read_string_index(np, "groups", i, &pin);
of_property_read_u32_index(np, "configs", i, &config);
/* 3.存入pinctrol_map */
configs = kmalloc(sizeof(*configs), GFP_KERNEL);
/* 设置复用值 */
new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;
new_map[i*2].data.mux.function= function;
new_map[i*2].data.mux.group = pin;
/* 设置配置值 */
new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN;
new_map[i*2+1].data.configs.group_or_pin = pin;
new_map[i*2+1].data.configs.configs = configs;
configs[0] = config;
new_map[i*2+1].data.configs.num_configs = 1;
}
*map = new_map;
*num_maps = num_pins * 2;
return 0;
}
//释放pinctrl_map
static void virtual_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map,
unsigned num_maps)
{
while(num_maps) {
if(map->type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(map->data.configs.configs);
kfree(map);
map++;
}
}
//把每个引脚作为一组
static const struct pinctrl_ops virtual_pinctrl_ops = {
.get_groups_count = virtual_get_groups_count,
.get_group_name = virtual_get_group_name,
.get_group_pins = virtual_get_group_pins,
.pin_dbg_show = virtual_pin_dbg_show,
.dt_node_to_map = virtual_dt_node_to_map,
.dt_free_map = virtual_dt_free_map,
};
//可以复用的功能种类
static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(g_funcs_des);
}
//指定序号中,可以复用的功能名字
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev, unsigned selector)
{
return g_funcs_des[selector].func_name;
}
//获得这个功能下,支持的组的信息
static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
const char *const **groups,
unsigned * const num_groups)
{
*groups = g_funcs_des[selector].groups;
*num_groups = g_funcs_des[selector].num_groups;
return 0;
}
//对指定组设置为指定功能
static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
printk("set %s as %s\n", pctldev->desc->pins[group].name,
g_funcs_des[selector].func_name);
return 0;
}
//复用功能的结构体
static const struct pinmux_ops virtual_pmx_ops = {
.get_functions_count = virtual_pmx_get_funcs_count,
.get_function_name = virtual_pmx_get_func_name,
.get_function_groups = virtual_pmx_get_groups,
.set_mux = virtual_pmx_set,
};
//获取引脚的配置值
static int virtual_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin_id,
unsigned long *config)
{
*config = g_configs[pin_id];
return 0;
}
//设置引脚的配置
static int virtual_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin_id,
unsigned long *configs, unsigned num_configs)
{
if(num_configs != 1)
return -EINVAL;
g_configs[pin_id] = *configs;
printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs);
return 0;
}
//调试信息
static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}
static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin_id)
{
seq_printf(s, "0x%lx", g_configs[pin_id]);
}
//配置引脚功能
static const struct pinconf_ops virtual_pinconf_ops = {
.pin_config_get = virtual_pinconf_get,
.pin_config_set = virtual_pinconf_set,
.pin_config_dbg_show = virtual_pinconf_dbg_show,
.pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};
static int virtual_pinctrl_probe(struct platform_device *pdev)
{
struct pinctrl_desc *pinctrl;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 1.分配pinctrl_desc */
pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
/* 2.设置pinctrl_desc */
pinctrl->name = dev_name(&pdev->dev);
pinctrl->owner = THIS_MODULE;
/* b.1 pins and group */
pinctrl->pins = pins;
pinctrl->npins = ARRAY_SIZE(pins); //多少个引脚
pinctrl->pctlops = &virtual_pinctrl_ops; //组的信息
/* b.2 pins mux */
pinctrl->pmxops = &virtual_pmx_ops; //设置引脚的复用
/* b.3 pins config */
pinctrl->confops = &virtual_pinconf_ops; //设备引脚配置
/* 3.注册pinctrl_desc */
g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pinctrl, NULL);
return 0;
}
static int virtual_pinctrl_remove(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static struct of_device_id virtual_pinctrl_of_match[] = {
{ .compatible = "100ask,virtual_pinctrl", }, //通过这个值来查找设备树节点
{ },
};
static struct platform_driver virtual_pinctrl_driver = {
.probe = virtual_pinctrl_probe,
.remove = virtual_pinctrl_remove,
.driver = {
.name = "100ask_virtual_pinctrl",
.of_match_table = of_match_ptr(virtual_pinctrl_of_match),
}
};
/* 1.入口函数 */
static int __init virtual_pinctrl_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return platform_driver_register(&virtual_pinctrl_driver);
}
/* 2. 出口函数 */
static void __exit virtual_pinctrl_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&virtual_pinctrl_driver);
}
module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);
MODULE_LICENSE("GPL");
五、编写client驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
static int virtual_client_probe(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int virtual_client_remove(struct platform_device *pdev)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static struct of_device_id virtual_client_of_match[] = {
{ .compatible = "100ask,virtual_i2c", },
{ },
};
static struct platform_driver virtual_client_driver = {
.probe = virtual_client_probe,
.remove = virtual_client_remove,
.driver = {
.name = "100ask_virtual_client",
.of_match_table = of_match_ptr(virtual_client_of_match),
}
};
/* 1.入口函数 */
static int __init virtual_client_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return platform_driver_register(&virtual_client_driver);
}
/* 2. 出口函数 */
static void __exit virtual_client_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&virtual_client_driver);
}
module_init(virtual_client_init);
module_exit(virtual_client_exit);
MODULE_LICENSE("GPL");
六、调试
加载了virtual_pinctrl_driver.ko驱动后,可以在开发板的/sys/kernel/debug/pinctrl/目录下,
每一个pinctroller都有一个目录,比如virtual_pincontroller。里面有很多文件,作用如下:
| Pinctrl虚拟文件 | 作用 | 解释 |
|---|---|---|
| pins | 单个引脚信息 | |
| pingroups | 引脚的组信息 | |
| pinmux-pins | 单个引脚的复用信息 | |
| pinmux-functions | function下的group(支持该function的group) | |
| pinconf-pins | 单个引脚的配置 | |
| pinconf-groups | 引脚组的配置 | |
| pinconf-config | 可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值 |
root@npi:/mnt/home/picture/virtual_pinctrl# insmod virtual_pinctrl_driver.ko
root@npi:/mnt/home/picture/virtual_pinctrl# insmod virtual_pinctrl_client.ko
-
单个引脚信息
-
引脚的组信息
root@npi:/sys/kernel/debug/pinctrl/virtual_pincontroller# cat pingroups
registered pin groups:
group: pin0
pin 0 (pin0)
group: pin1
pin 1 (pin1)
group: pin2
pin 2 (pin2)
group: pin3
pin 3 (pin3)
- 单个引脚的复用信息
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-pins
Pinmux settings per pin
Format: pin (name): mux_owner gpio_owner hog?
pin 0 (pin0): virtual_i2c (GPIO UNCLAIMED) function i2c group pin0
pin 1 (pin1): virtual_i2c (GPIO UNCLAIMED) function i2c group pin1
pin 2 (pin2): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 3 (pin3): (MUX UNCLAIMED) (GPIO UNCLAIMED)
- function下的goup(支持该function的group)
root@npi:/sys/kernel/debug/pinctrl/virtual_pincontroller# cat pinmux-functions
function: gpio, groups = [ pin0 pin1 pin2 pin3 ]
function: i2c, groups = [ pin0 pin1 ]
function: uart, groups = [ pin2 pin3 ]
- 单个引脚的配置
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (pin0): 0x11223344
pin 1 (pin1): 0x55667788
pin 2 (pin2): 0x0
pin 3 (pin3): 0x0
- 引脚组的配置
root@npi:/mnt/home/picture/virtual_pinctrl# cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-groups
Pin config settings per pin group
Format: group (name): configs
0 (pin0): 0x11223344
1 (pin1): 0x55667788
2 (pin2): 0x0
3 (pin3): 0x0
- 修改配置值内核源码
drivers\pinctrl\pinconf.c
pinconf_dbg_config_write
如果pin controller驱动程序中的pinconf_ops提供了pin_config_dbg_parse_modify函数, 就可以通过pinconf-config文件修改某个pin或某个group的配置值。
# 格式: modify <config> <devicename> <state> <pin_name|group_name> <newvalue>
echo "modify config_pin virtual_i2c default pin0 0xaabb" > /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-config
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinconf-config
本文详细介绍了Linux内核的pinctrl功能及其在虚拟pincontroller驱动程序中的实现,包括引脚枚举、复用和配置,以及如何编写设备树和驱动程序实例。涵盖了关键概念如pinctrl_desc、pinmux和pinconf操作,并展示了如何在硬件功能和实际应用中操作。
606

被折叠的 条评论
为什么被折叠?



