做网站需要简介,做三方网站多少钱,陕西网站建设网络公司,wordpress 安装中文往期内容 本专栏往期内容#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析#xff1a;imx_pinctrl_soc_info结构体Pinctrl子系统中c…往期内容 本专栏往期内容 Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析imx_pinctrl_soc_info结构体Pinctrl子系统中client端使用pinctrl过程的驱动分析Pinctrl子系统中Pincontroller和client驱动程序的编写GPIO子系统层次与数据结构详解-CSDN博客GPIO子系统中Controller驱动源码分析 input子系统专栏 专栏地址input子系统input角度I2C触摸屏驱动分析和编写一个简单的I2C驱动程序 – 末片有往期内容观看顺序 I2C子系统专栏 专栏地址IIC子系统具体芯片的IIC控制器驱动程序分析i2c-imx.c-CSDN博客 – 末篇有往期内容观看顺序 总线和设备树专栏 专栏地址总线和设备树设备树与 Linux 内核设备驱动模型的整合-CSDN博客 – 末篇有往期内容观看顺序 目录 往期内容前言1.编写虚拟的GPIO控制器的驱动程序1.1 硬件功能1.2 编写设备树文件1.3 代码编写 2.GPIO子系统与Pinctrl子系统的交互2.1 使用GPIO前应该设置Pinctrl2.2 GPIO和Pinctrl的映射关系2.2.1 示例2.2.2 数据结构 2.3 GPIO调用Pinctrl的过程2.4 编程_GPIO使用Pinctrl2.4.1 做什么2.4.2 编程2.4.3 注意 3.GPIO子系统的sysfs接口3.1 驱动程序3.2 常用的sysfs文件3.2.1 有哪些GPIO控制器3.2.2 每个GPIO控制器的详细信息3.2.3 查看GPIO使用情况3.2.4 通过sysfs使用GPIO1. 确定GPIO编号2. 导出/设置方向/读写值 前言
Linux 4.x内核文档
Linux-4.9.88\Documentation\gpiodrivers-on-gpio.txtgpio.txtgpio-legacy.txtsysfs.txtboard.txtconsumer.txtdriver.txtLinux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txtgpio.txtLinux-4.9.88\drivers\gpio\gpio-mxc.cgpio-mxc.cLinux-4.9.88\arch\arm\boot\dts\imx6ull.dtsi
本文主要讲解了如何在Linux 4.9.88内核中为虚拟GPIO控制器编写驱动程序并展示了GPIO和Pinctrl子系统之间的交互方式。假设该虚拟GPIO控制器有4个引脚然后在设备树中为其设置相应的设备节点和引脚配置。通过代码示例展示了如何实现GPIO的输入、输出功能包括GPIO的值读取与设置。文介绍了GPIO控制器与Pinctrl的关系以及两者的映射机制解释了gpio_pin_range和pinctrl_gpio_range结构体的作用并提供了GPIO子系统中调用Pinctrl的流程示例。
1.编写虚拟的GPIO控制器的驱动程序
1.1 硬件功能
假设这个虚拟的GPIO Controller有4个引脚 1.2 编写设备树文件
修改arch/arm/boot/dts/100ask_imx6ull-14x14.dts添加如下代码
/ {gpio_virt: virtual_gpiocontroller {compatible example,virtual_gpio;gpio-controller;#gpio-cells 2;ngpios 4;};myled {compatible XXX,leddrv;led-gpios gpio_virt 2 GPIO_ACTIVE_LOW;};
};1.3 代码编写
代码编写virtual_gpio_driver.c
#include linux/module.h
#include linux/err.h
#include linux/init.h
#include linux/io.h
#include linux/mfd/syscon.h
#include linux/of.h
#include linux/of_device.h
#include linux/of_address.h
#include linux/gpio/consumer.h
#include linux/gpio/driver.h
#include linux/slab.h
#include linux/regmap.h// 定义一个全局的GPIO芯片结构体指针
static struct gpio_chip *g_virtual_gpio_chip;// 用于模拟GPIO状态的全局变量
static int g_gpio_status 0;// 设备树匹配结构匹配支持的设备
static const struct of_device_id virtual_gpio_of_match[] {{ .compatible example,virtual_gpio, },{ },
};// 设置GPIO为输出模式的函数实现
static int virtual_gpio_set_direction_output(struct gpio_chip *chip, unsigned offset, int value) {printk(Setting GPIO pin %d as output %s\n, offset, value ? high : low);return 0; // 成功返回0
}// 设置GPIO为输入模式的函数实现
static int virtual_gpio_set_direction_input(struct gpio_chip *chip, unsigned offset) {printk(Setting GPIO pin %d as input\n, offset);return 0; // 成功返回0
}// 获取GPIO当前值的函数实现
static int virtual_gpio_get_value(struct gpio_chip *chip, unsigned offset) {int value (g_gpio_status (1 offset)) ? 1 : 0;printk(Reading GPIO pin %d, current value %d\n, offset, value);return value; // 返回当前值
}// 设置GPIO值的函数实现
static void virtual_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) {printk(Setting GPIO pin %d to %d\n, offset, value);if (value) {g_gpio_status | (1 offset); // 设置为高电平} else {g_gpio_status ~(1 offset); // 设置为低电平}
}// 设备探测函数
static int virtual_gpio_probe(struct platform_device *pdev) {int ret;unsigned int ngpios;printk(Probing virtual GPIO driver: %s, line: %d\n, __FUNCTION__, __LINE__);// 分配gpio_chip结构体内存g_virtual_gpio_chip devm_kzalloc(pdev-dev, sizeof(*g_virtual_gpio_chip), GFP_KERNEL);// 配置gpio_chip的相关信息g_virtual_gpio_chip-label pdev-name;g_virtual_gpio_chip-direction_output virtual_gpio_set_direction_output;g_virtual_gpio_chip-direction_input virtual_gpio_set_direction_input;g_virtual_gpio_chip-get virtual_gpio_get_value;g_virtual_gpio_chip-set virtual_gpio_set_value;g_virtual_gpio_chip-parent pdev-dev; // 设置父设备g_virtual_gpio_chip-owner THIS_MODULE; // 设置模块所有者// 读取ngpios属性g_virtual_gpio_chip-base -1; // 默认未分配基地址ret of_property_read_u32(pdev-dev.of_node, ngpios, ngpios);g_virtual_gpio_chip-ngpio ngpios; // 设置GPIO数量// 注册gpio_chipret devm_gpiochip_add_data(pdev-dev, g_virtual_gpio_chip, NULL);return ret; // 返回结果
}// 设备移除函数
static int virtual_gpio_remove(struct platform_device *pdev) {printk(Removing virtual GPIO driver: %s, line: %d\n, __FUNCTION__, __LINE__);return 0; // 成功返回0
}// 定义platform_driver
static struct platform_driver virtual_gpio_driver {.probe virtual_gpio_probe,.remove virtual_gpio_remove,.driver {.name example_virtual_gpio,.of_match_table of_match_ptr(virtual_gpio_of_match),},
};// 初始化函数
static int __init virtual_gpio_init(void) {printk(Initializing virtual GPIO driver: %s, line: %d\n, __FUNCTION__, __LINE__);return platform_driver_register(virtual_gpio_driver); // 注册platform_driver
}// 清理函数
static void __exit virtual_gpio_exit(void) {printk(Exiting virtual GPIO driver: %s, line: %d\n, __FUNCTION__, __LINE__);platform_driver_unregister(virtual_gpio_driver); // 注销platform_driver
}// 模块初始化和清理宏
module_init(virtual_gpio_init);
module_exit(virtual_gpio_exit);// 模块许可证
MODULE_LICENSE(GPL);of_property_read_u32of.h
2.GPIO子系统与Pinctrl子系统的交互
Linux-4.9.88\drivers\gpio\gpio-74x164.cgpio-74x164.c
2.1 使用GPIO前应该设置Pinctrl
假设使用这个虚拟的GPIO Controller的pinA来控制LED 要使用pinA来控制LED首先要通过Pinctrl子系统把它设置为GPIO功能然后才能设置它为输出引脚、设置它的输出值。
所以在设备树文件里应该添加Pinctrl的内容
virtual_pincontroller {compatible XXX,virtual_pinctrl;myled_pin: myled_pin {functions gpio; // 设置引脚功能为GPIOgroups pin0; // 指定该引脚所在的组configs 0x11223344; // 配置该引脚的其他特性};
};gpio_virt: virtual_gpiocontroller {compatible XXX,virtual_gpio;gpio-controller; // 标识这是一个GPIO控制器#gpio-cells 2; // GPIO单元的数量ngpios 4; // GPIO引脚数量
};myled {compatible XXX,leddrv; // LED驱动led-gpios gpio_virt 0 GPIO_ACTIVE_LOW; // 指定使用的GPIO引脚pinctrl-names default; // 指定Pinctrl的名称pinctrl-0 myled_pin; // 指定使用的Pinctrl设置
};许多芯片并不需要在设备树中显式地将引脚配置为GPIO功能。这是因为某些芯片例如STM32MP157在内部实现上已经将GPIO功能与引脚复用整合在一起。因此Pinctrl的使用在某些情况下是虚拟的它与GPIO密切相关。
2.2 GPIO和Pinctrl的映射关系
2.2.1 示例 左边的Pinctrl支持8个引脚在Pinctrl的内部编号为0~7 图中有2个GPIO控制器 GPIO0内部引脚编号为03假设在GPIO子系统中全局编号为100103GPIO1内部引脚编号为03假设在GPIO子系统中全局编号为104107 假设我们要使用pin1_1应该这样做 根据GPIO1的内部编号1可以换算为Pinctrl子系统中的编号5使用Pinctrl的函数把第5个引脚配置为GPIO功能
2.2.2 数据结构
gpio_pin_range 结构体定义了 GPIO 控制器控制的引脚范围和对应的 pinctrl 设备允许 GPIO 控制器管理一个 pinctrl 设备的引脚范围。
/** * struct gpio_pin_range - GPIO 控制器所控制的引脚范围* node: 连接到范围列表的链表节点用于内部维护 pin range 的集合。* pctldev: 指向管理相应引脚的 pinctrl 设备pinctrl_dev的指针* 该设备将处理引脚复用、方向控制等功能。* range: 由 GPIO 控制器控制的引脚的实际范围包含引脚的起始位置、数量等信息。*/
struct gpio_pin_range {struct list_head node; // 列表头维护 GPIO 范围的链表结构struct pinctrl_dev *pctldev; // 与 GPIO 控制器关联的 pinctrl 设备struct pinctrl_gpio_range range; // 由 GPIO 控制器控制的引脚范围信息
};node这是一个链表节点通常用于将多个 gpio_pin_range 链接成一个链表以便管理和迭代多个引脚范围。pctldev指向 pinctrl 设备的指针pinctrl 设备负责管理 GPIO 引脚的复用和配置。range定义了 GPIO 控制器管理的实际引脚范围包含引脚基地址和数量等信息。 pinctrl_gpio_range 结构体描述了由 GPIO 控制器所处理的 GPIO 引脚范围为每个 GPIO 控制器提供一个编号空间的子范围。
/*** struct pinctrl_gpio_range - 每个 GPIO 控制器提供的 GPIO 编号范围* node: 用于内部使用的链表节点连接到范围列表中* name: 此 GPIO 控制器范围的名称用于标识 GPIO 控制器* id: 该范围的芯片 ID用于在范围内区分 GPIO 控制器* base: GPIO 范围的基地址偏移用于计算 GPIO 引脚编号* pin_base: 如果 pins 数组为 NULL此字段表示 GPIO 引脚范围的基引脚编号* pins: 指向引脚枚举数组的指针该数组列出 GPIO 范围中的所有引脚* npins: GPIO 范围内的引脚数量包含基引脚编号* gc: 可选指针指向 gpio_chip 结构体该结构体用于 GPIO 控制器的基本信息*/
struct pinctrl_gpio_range {struct list_head node; // 内部链表节点连接到 GPIO 范围列表const char *name; // 范围名称标识 GPIO 控制器unsigned int id; // GPIO 范围 ID唯一标识该范围unsigned int base; // GPIO 编号范围的基地址偏移用于计算引脚编号unsigned int pin_base; // 引脚范围的基引脚编号如果 pins 数组为 NULL 使用此基数unsigned const *pins; // 引脚数组的指针列出该 GPIO 范围内的所有引脚unsigned int npins; // GPIO 范围中的引脚数量包括基引脚编号struct gpio_chip *gc; // 可选的 gpio_chip 指针用于指向 GPIO 控制器信息
};node内部链表节点用于管理 pinctrl_gpio_range 的链表结构。name范围的名称通常是 GPIO 控制器的名称用于标识该范围。id范围 ID唯一标识该 GPIO 范围用于在控制器中区分不同的 GPIO 范围。baseGPIO 编号范围的基地址偏移量通过该偏移量确定 GPIO 控制器管理的具体引脚编号。pin_base基引脚编号表示该 GPIO 范围的起始引脚通常在 pins 数组为空时使用。pins指向引脚编号数组的指针列出该 GPIO 范围内的所有 GPIO 引脚。npins范围内的引脚数量包括基引脚编号。gc可选的指向 gpio_chip 结构体的指针用于与 GPIO 控制器相关联提供 GPIO 控制器的基本信息。
2.3 GPIO调用Pinctrl的过程
GPIO子系统中的request函数用来申请某个GPIO引脚
它会导致Pinctrl子系统中的这2个函数之一被调用pmxops-gpio_request_enable或pmxops-request
调用关系如下
gpiod_get //获取GPIO描述符的主函数通常用于申请和配置GPIO引脚。gpiod_get_index desc of_find_gpio(dev, con_id, idx, lookupflags);//查找设备树中指定的GPIO。ret gpiod_request(desc, con_id ? con_id : devname); //用于确认请求并进行最终设置。ret gpiod_request_commit(desc, label); //如果GPIO芯片结构体中的request指针非空它会调用芯片的request函数。if (chip-request) {ret chip-request(chip, offset);}编写GPIO驱动程序时所设置chip-request函数一般直接调用gpiochip_generic_request它导致Pinctrl把引脚复用为GPIO功能。
gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)pinctrl_request_gpio(chip-gpiodev-base offset) //该函数负责将引脚配置为GPIO功能ret pinctrl_get_device_gpio_range(gpio, pctldev, range); // gpio是引脚的全局编号/* Convert to the pin controllers number space */pin gpio_to_pin(range, gpio);ret pinmux_request_gpio(pctldev, range, pin, gpio);ret pin_request(pctldev, pin, owner, range);Pinctrl子系统中的pin_request函数就会把引脚配置为GPIO功能
static int pin_request(struct pinctrl_dev *pctldev,int pin, const char *owner,struct pinctrl_gpio_range *gpio_range) {const struct pinmux_ops *ops pctldev-desc-pmxops;// 检查是否有用于请求引脚的操作if (gpio_range ops-gpio_request_enable) {// 请求并启用单个GPIO引脚status ops-gpio_request_enable(pctldev, gpio_range, pin);} else if (ops-request) {// 如果有通用请求函数则调用status ops-request(pctldev, pin);} else {// 默认情况下不做任何操作status 0;}return status; // 返回请求状态
}2.4 编程_GPIO使用Pinctrl
2.4.1 做什么
如果不想在使用GPIO引脚时在设备树中设置Pinctrl信息
如果想让GPIO和Pinctrl之间建立联系
需要做这些事情
表明GPIO和Pinctrl间的联系
在GPIO设备树中使用gpio-ranges来描述它们之间的联系
GPIO系统中有引脚号Pinctrl子系统中也有自己的引脚号2个号码要建立映射关系在GPIO设备树中使用如下代码建立映射关系
// 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12
gpio-ranges pinctrlA 0 128 12; 解析这些联系
在GPIO驱动程序中解析跟Pinctrl之间的联系处理gpio-ranges:
这不需要我们自己写代码注册gpio_chip时会自动调用
int gpiochip_add_data(struct gpio_chip *chip, void *data)status of_gpiochip_add(chip);status of_gpiochip_add_pin_range(chip);of_gpiochip_add_pin_rangefor (;; index) {ret of_parse_phandle_with_fixed_args(np, gpio-ranges, 3,index, pinspec);pctldev of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev// 增加映射关系 /* npins ! 0: linear range */ret gpiochip_add_pin_range(chip,pinctrl_dev_get_devname(pctldev),pinspec.args[0],pinspec.args[1],pinspec.args[2]);编程
在GPIO驱动程序中提供gpio_chip-request在Pinctrl驱动程序中提供pmxops-gpio_request_enable或pmxops-request 2.4.2 编程
GPIO控制器编程
在编写GPIO驱动程序时需要关注以下几个方面 设置请求函数 确保在GPIO芯片结构体中正确设置chip-request指针通常指向gpiochip_generic_request。这样可以确保引脚被正确复用为GPIO功能。 理解设备树 如果GPIO引脚来自设备树确保在设备树中正确描述GPIO的相关属性和配置以便gpiod_get和相关函数能够正确查找和请求GPIO。 错误处理 在每个调用中检查返回值确保处理潜在的错误避免因引脚请求失败而导致驱动不稳定。 功能分离 在实现请求和释放引脚时确保保持代码的清晰和可读尽量将复杂逻辑分离到辅助函数中以便于维护。 gpio_chip中提供request函数 c chip-request gpiochip_generic_request; virtual_gpio_driver.c 参考 virtual_gpio_driver0.c Pinctrl编程
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,.gpio_request_enable virtual_pmx_gpio_request_enable,
};virtual_pinctrl_driver.ccore.h
led
virtual_pinctrl_client.c参考 》 leddrv.c
ledtest.c
2.4.3 注意
IMX6ULL使用GPIO时必须设置Pinctrl如果不设置只有那些默认就是GPIO功能的引脚可以正常使用。
原因
GPIO控制器的设备树中没有gpio-rangesPinctrl驱动中并没有提供pmxops-gpio_request_enable或pmxops-requestgpio_chip结构体中direction_input、direction_output并没有配置引脚为GPIO功能
3.GPIO子系统的sysfs接口
Linux-4.9.88\drivers\gpio\gpiolib-sysfs.cgpiolib-sysfs.c
3.1 驱动程序
gpiolib-sysfs.c
3.2 常用的sysfs文件
3.2.1 有哪些GPIO控制器
/sys/bus/gpio/devices目录下列出了所有的GPIO控制器如下表示有11个GPIO控制器
/sys/bus/gpio/devices/gpiochip0
/sys/bus/gpio/devices/gpiochip1
/sys/bus/gpio/devices/gpiochip2
/sys/bus/gpio/devices/gpiochip3
/sys/bus/gpio/devices/gpiochip4
/sys/bus/gpio/devices/gpiochip5
/sys/bus/gpio/devices/gpiochip6
/sys/bus/gpio/devices/gpiochip7
/sys/bus/gpio/devices/gpiochip8
/sys/bus/gpio/devices/gpiochip9
/sys/bus/gpio/devices/gpiochip103.2.2 每个GPIO控制器的详细信息
/sys/class/gpio/gpiochipXXX下有这些信息
/sys/class/gpio/gpiochip508]# ls -1
base // 这个GPIO控制器的GPIO编号
device
label // 名字
ngpio // 引脚个数
power
subsystem
uevent3.2.3 查看GPIO使用情况
cat /sys/kernel/debug/gpio3.2.4 通过sysfs使用GPIO
如果只是简单的引脚控制(比如输出、查询输入值)可以不编写驱动程序。
但是涉及中断的话就需要编写驱动程序了。
1. 确定GPIO编号
查看每个/sys/class/gpio/gpiochipXXX目录下的label确定是你要用的GPIO控制器也称为GPIO Bank。
根据它名字gpiochipXXX就可以知道基值是XXX。
基值加上引脚offset就是这个引脚的编号。
2. 导出/设置方向/读写值
举例
echo 509 /sys/class/gpio/export
echo out /sys/class/gpio/gpio509/direction
echo 1 /sys/class/gpio/gpio509/value
echo 509 /sys/class/gpio/unexportecho 509 /sys/class/gpio/export
echo in /sys/class/gpio/gpio509/direction
cat /sys/class/gpio/gpio509/value
echo 509 /sys/class/gpio/unexport