首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Pinctrl子系统中Pincontroller和client驱动程序的编写

  • 25-03-04 14:01
  • 2552
  • 12030
blog.csdn.net

往期内容

本专栏往期内容:

  1. Pinctrl子系统和其主要结构体引入
  2. Pinctrl子系统pinctrl_desc结构体进一步介绍
  3. Pinctrl子系统中client端设备树相关数据结构介绍和解析
  4. inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体
  5. Pinctrl子系统中client端使用pinctrl过程的驱动分析

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有往期内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序

img

目录

  • 往期内容
  • 1.回顾Pinctrl的三大作用
  • 2.需要做什么
  • 3.硬件功能
  • 4.编写设备树
  • 5.编写Pinctrl驱动程序
    • 5.1 核心:pinctrl_desc
    • 5.2 辅助函数
      • 5.2.1 for_each_child_of_node
      • 5.2.2 of_get_child_count
      • 5.2.3 of_find_property
      • 5.2.4 of_property_read_u32
      • 5.2.5 of_property_read_u32_index
      • 5.2.6 of_property_read_string_index
    • 5.3 代码
    • 5.4 编写测试的client驱动程序
  • 6.调试信息

1.回顾Pinctrl的三大作用

记住pinctrl的三大作用,有助于理解所涉及的数据结构:

  • 引脚枚举与命名(Enumerating and naming)

    • 单个引脚
    • 各组引脚
  • 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能

  • 引脚配置(Configuration):比如上拉、下拉、open drain、驱动强度等

Pinctrl子系统和其主要结构体引入

Pinctrl子系统pinctrl_desc结构体进一步介绍

Pinctrl子系统中client端设备树相关数据结构介绍和解析

2.需要做什么

img

  • pin controller:

    • 创建设备树节点
    • 编写驱动程序
  • 测试:

    • 创建client设备树节点
    • 编写驱动程序

3.硬件功能

假设这个虚拟的pin controller有4个引脚:

img

  • pin0,1,2,3都可以配置为GPIO功能 — function 1
  • pin0,1还可以配置为I2C功能 — function 2
  • pin2,3还可以配置为UART功能 — function 3

4.编写设备树

       Pincontroller:
virtual_pincontroller {
	compatible = "XXX,virtual_pinctrl";
	i2cgrp: i2cgrp {
			functions = "i2c", "i2c";
			groups = "pin0", "pin1";
			configs = <0x11223344  0x55667788>;
	};
};

      client:
virtual_i2c {
	compatible = "XXX,virtual_i2c";
	pinctrl-names = "default";
	pinctrl-0 = <&i2cgrp>;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5.编写Pinctrl驱动程序

5.1 核心:pinctrl_desc

  • 分配pinctrl_desc结构体
  • 设置pinctrl_desc结构体
  • 注册pinctrl_desc结构体

5.2 辅助函数

\Linux-4.9.88\include\linux\of.h?of.h

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.2.1 for_each_child_of_node

#define for_each_child_of_node(parent, child) \
	for (child = of_get_next_child(parent, NULL); child != NULL; \
	     child = of_get_next_child(parent, child))
  • 1
  • 2
  • 3

for_each_child_of_node 是一个宏,用于遍历给定父节点的所有直接子节点。它在遍历每个子节点时会将其赋值给变量 child,供后续操作使用。

参数

  • parent: 指向父节点的指针。
  • child: 用于存储当前遍历的子节点的指针。

说明

  • 该宏循环调用 of_get_next_child 函数,遍历所有直接子节点。
  • 在遍历的过程中,child 会依次指向 parent 的每一个子节点。
  • 使用 for_each_child_of_node 遍历时,用户需要确保释放所有的子节点,避免资源泄露。

5.2.2 of_get_child_count

static inline int of_get_child_count(const struct device_node *np)
{
	struct device_node *child;
	int num = 0;

	for_each_child_of_node(np, child)
		num++;

	return num;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

功能

of_get_child_count 函数用于计算指定设备树节点的直接子节点数量。

参数

  • np: 指向设备树节点的指针。

说明

  • 该函数使用 for_each_child_of_node 宏遍历节点的所有直接子节点,每找到一个子节点,计数器 num 加 1。
  • of_get_child_count 通常用于确定设备树节点的子节点数量,以便在后续操作中做动态分配或判断。

5.2.3 of_find_property

extern struct property *of_find_property(const struct device_node *np,
					 const char *name,
					 int *lenp);
  • 1
  • 2
  • 3

功能

of_find_property 函数用于在指定节点中查找属性。

参数

  • np: 指向设备树节点的指针。
  • name: 要查找的属性名称(字符串)。
  • lenp: 指向一个整数的指针,用于存储找到的属性的长度(以字节为单位)。

说明

  • 该函数适用于检查节点中是否存在某个属性。若 lenp 不为 NULL,将返回属性的长度。
  • of_find_property 常用于从设备树中查找如 compatible、status 等属性。

5.2.4 of_property_read_u32

static inline int of_property_read_u32(const struct device_node *np,
				       const char *propname,
				       u32 *out_value)
{
	return of_property_read_u32_array(np, propname, out_value, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

功能

of_property_read_u32 函数用于读取设备树节点中指定属性的 u32 整数值。

参数

  • np: 指向设备树节点的指针。
  • propname: 要读取的属性名称。
  • out_value: 指向 u32 类型的变量的指针,用于存储读取到的属性值

说明

  • 该函数通过调用 of_property_read_u32_array 实现,从设备树中读取一个 u32 类型的属性值。
  • 如果属性为数组且包含多个值,则只会读取第一个元素。如果需要读取多个值,可以直接调用 of_property_read_u32_array。

5.2.5 of_property_read_u32_index

extern int of_property_read_u32_index(const struct device_node *np,
				       const char *propname,
				       u32 index, u32 *out_value);
  • 1
  • 2
  • 3

功能

of_property_read_u32_index 函数用于读取设备树节点中 u32 类型数组属性的指定索引值。

参数

  • np: 指向设备树节点的指针。
  • propname: 属性名称。
  • index: 数组中的索引。
  • out_value: 指向 u32 变量的指针,用于存储读取到的值。

说明

  • 该函数适用于读取包含多个 u32 元素的数组属性,例如 reg、interrupts 等。
  • of_property_read_u32_index 可以用来精确访问设备树中数组属性的特定元素。

5.2.6 of_property_read_string_index

/**
 * of_property_read_string_index() - Find and read a string from a multiple
 * strings property.
 * @np:		device node from which the property value is to be read.
 * @propname:	name of the property to be searched.
 * @index:	index of the string in the list of strings
 * @out_string:	pointer to null terminated return string, modified only if
 *		return value is 0.
 *
 * Search for a property in a device tree node and retrieve a null
 * terminated string value (pointer to data, not a copy) in the list of strings
 * contained in that property.
 * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
 * property does not have a value, and -EILSEQ if the string is not
 * null-terminated within the length of the property data.
 *
 * The out_string pointer is modified only if a valid string can be decoded.
 */
static inline int of_property_read_string_index(const struct device_node *np,
						const char *propname,
						int index, const char **output)
{
	int rc = of_property_read_string_helper(np, propname, output, 1, index);
	return rc < 0 ? rc : 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

功能

of_property_read_string_index 函数用于读取设备树节点中字符串数组属性的指定索引值。

参数

  • np: 指向设备树节点的指针。
  • propname: 属性名称。
  • index: 要读取的字符串在数组中的索引。
  • output: 指向 const char * 指针的指针,用于存储读取到的字符串地址。

说明

  • 该函数常用于读取如 pinctrl-names 之类的字符串数组属性,能够获取数组属性中的第 index 个字符串。
  • 例如,如果 pinctrl-names 属性中定义了 default, idle 两个状态值,可以使用该函数获取具体的字符串内容。

5.3 代码

?core.h

?virtual_pinctrl_driver.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "core.h"

// 全局变量,保存 pinctrl 设备结构体指针
static struct pinctrl_dev *global_pinctrl_dev;

// 定义一个 pins 数组,描述 GPIO 引脚
static const struct pinctrl_pin_desc pin_descs[] = {
    {0, "pin0", NULL},
    {1, "pin1", NULL},
    {2, "pin2", NULL},
    {3, "pin3", NULL},
};

// 全局配置数组
static unsigned long pin_configs[4];

// 虚拟功能描述结构体
struct virtual_function_desc {
    const char *function_name;
    const char **groups;
    int group_count;
};

// 定义不同功能组的引脚
static const char *function0_groups[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *function1_groups[] = {"pin0", "pin1"};
static const char *function2_groups[] = {"pin2", "pin3"};

// 定义功能描述数组
static struct virtual_function_desc virtual_functions[] = {
    {"gpio", function0_groups, 4},
    {"i2c",  function1_groups, 2},
    {"uart", function2_groups, 2},
};

// 设备树匹配表
static const struct of_device_id virtual_pinctrl_of_match[] = {
    { .compatible = "XXX,virtual_pinctrl", },
    { },
};

// 获取引脚组的数量
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;
}

// 显示引脚调试信息
static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset) {
    seq_printf(s, "%s", dev_name(pctldev->dev)); // 打印设备名称
}

// 将设备树节点映射到 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;

    // 计算 groups 数组的数量
    while (of_property_read_string_index(np, "groups", num_pins, &pin) == 0)
        num_pins++;

    new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);

    // 获取引脚、功能和配置,并填充到新映射中
    for (i = 0; i < num_pins; i++) {
        of_property_read_string_index(np, "groups", i, &pin);
        of_property_read_string_index(np, "functions", i, &function);
        of_property_read_u32_index(np, "configs", i, &config);

        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; // 配置数量为 1
    }

    *map = new_map; // 返回新的映射
    *num_maps = num_pins * 2; // 映射数量

    return 0;
}

// 释放映射结构体
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++;
    }
}

// pinctrl 操作结构体
static const struct pinctrl_ops virtual_pctrl_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(virtual_functions);
}

// 获取功能名称
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev,
                                              unsigned selector) {
    return virtual_functions[selector].function_name;
}

// 获取功能组
static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
                                   const char * const **groups,
                                   unsigned * const num_groups) {
    *groups = virtual_functions[selector].groups; // 返回功能组
    *num_groups = virtual_functions[selector].group_count; // 返回功能组数量

    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, virtual_functions[selector].function_name);
    return 0;
}

// pinmux 操作结构体
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 = pin_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; // 错误处理
    
    pin_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", pin_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", pin_configs[pin_id]);
}

// pinconf 操作结构体
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,
};

// probe 函数,用于初始化 pinctrl
static int virtual_pinctrl_probe(struct platform_device *pdev) {
    struct pinctrl_desc *pinctrl_desc;
    
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    // 分配 pinctrl_desc 结构体
    pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pinctrl_desc), GFP_KERNEL);
    
    // 初始化 pinctrl_desc 结构体
    pinctrl_desc->name = dev_name(&pdev->dev);
    pinctrl_desc->owner = THIS_MODULE;
    
    // 设置引脚描述和数量
    pinctrl_desc->pins = pin_descs;
    pinctrl_desc->npins = ARRAY_SIZE(pin_descs);

    pinctrl_desc->pctlops = &virtual_pctrl_ops; // 设置 pinctrl 操作
    pinctrl_desc->pmxops = &virtual_pmx_ops; // 设置 pinmux 操作
    pinctrl_desc->confops = &virtual_pinconf_ops; // 设置 pinconf 操作
    
    // 注册 pinctrl 设备
    global_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pinctrl_desc, NULL);
    
    return 0;
}

// remove 函数,清理 pinctrl
static int virtual_pinctrl_remove(struct platform_device *pdev) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

// platform driver 结构体
static struct platform_driver virtual_pinctrl_driver = {
    .probe      = virtual_pinctrl_probe,
    .remove     = virtual_pinctrl_remove,
    .driver     = {
        .name   = "XXX,virtual_pinctrl",
        .of_match_table = of_match_ptr(virtual_pinctrl_of_match),
    }
};

// 初始化函数
static int __init virtual_pinctrl_init(void) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    // 注册 platform driver
    return platform_driver_register(&virtual_pinctrl_driver);
}

// 清理函数
static void __exit virtual_pinctrl_exit(void) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    // 注销 platform driver
    platform_driver_unregister(&virtual_pinctrl_driver);
}

// 模块入口和出口
module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);

MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287

img

5.4 编写测试的client驱动程序

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 设备树匹配表,匹配虚拟 I2C 设备
static const struct of_device_id virtual_i2c_of_match[] = {
    { .compatible = "XXX,virtual_i2c", },
    { },
};

// probe 函数,在设备被检测到时调用
static int virtual_i2c_probe(struct platform_device *pdev) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息
    return 0; // 返回 0 表示成功
}

// remove 函数,在设备被移除时调用
static int virtual_i2c_remove(struct platform_device *pdev) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息
    return 0; // 返回 0 表示成功
}

// 定义平台驱动结构体
static struct platform_driver virtual_i2c_driver = {
    .probe      = virtual_i2c_probe, // 设备探测函数
    .remove     = virtual_i2c_remove, // 设备移除函数
    .driver     = {
        .name   = "100ask_virtual_client", // 驱动名称
        .of_match_table = of_match_ptr(virtual_i2c_of_match), // 设备树匹配表
    }
};

/* 1. 模块初始化函数 */
static int __init virtual_i2c_init(void) {    
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息
    // 注册平台驱动
    return platform_driver_register(&virtual_i2c_driver);
}

/* 2. 模块清理函数 */
static void __exit virtual_i2c_exit(void) {
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); // 输出当前文件和函数信息
    // 注销平台驱动
    platform_driver_unregister(&virtual_i2c_driver);
}

// 模块入口和出口
module_init(virtual_i2c_init);
module_exit(virtual_i2c_exit);

// 模块许可证
MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

6.调试信息

开发板的/sys/kernel/debug/pinctrl/目录下,每一个pin controller都有一个目录,比如virtual_pincontroller。
里面有很多文件,作用如下:

Pinctrl的虚拟文件作用
pins单个引脚信息
pingroups引脚的组信息
pinmux-pins单个引脚的复用信息
pinmux-functionsfunction下的group(支持该function的group)
pinconf-pins单个引脚的配置
pinconf-groups引脚组的配置
pinconf-config可以通过写它修改指定设备、指定状态下、指定(组)引脚的config值
  • 单个引脚信息
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pins
registered pins: 4
pin 0 (pin0) virtual_pincontroller
pin 1 (pin1) virtual_pincontroller
pin 2 (pin2) virtual_pincontroller
pin 3 (pin3) virtual_pincontroller
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 引脚的组信息
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pingroups
registered pin groups:
group: pin0
pin 0 (pin0)

group: pin1
pin 1 (pin1)

group: pin2
pin 2 (pin2)

group: pin3
pin 3 (pin3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 单个引脚的复用信息
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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • function下的group(支持该function的group)
cat /sys/kernel/debug/pinctrl/virtual_pincontroller/pinmux-functions
function: gpio, groups = [ pin0 pin1 pin2 pin3 ]
function: i2c, groups = [ pin0 pin1 ]
function: uart, groups = [ pin2 pin3 ]
  • 1
  • 2
  • 3
  • 4
  • 单个引脚的配置
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 引脚组的配置
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 修改配置值
    内核源码:
drivers\pinctrl\pinconf.c
    pinconf_dbg_config_write
  • 1
  • 2

如果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
  • 1
  • 2
  • 3
  • 4
文章知识点与官方知识档案匹配,可进一步学习相关知识
C技能树首页概览220410 人正在系统学习中
注:本文转载自blog.csdn.net的憧憬一下的文章"https://blog.csdn.net/caiji0169/article/details/143473255"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top