GPIO控制器驱动的核心
GPIO控制器驱动的核心就是创建struct gpio_chip对象,并初始化其中内容,然后利用devm_gpiochip_add_data或gpiochip_add_data注册GPIO控制器驱动(它会在内部创建并注册一个struct gpio_devic对象)
//注册GPIO控制器驱动
int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip, void *data)
int gpiochip_add_data(struct gpio_chip *chip, void *data)
//注销GPIO控制器驱动
void gpiochip_remove(struct gpio_chip *chip)
驱动编写
GPIO控制器需要与pinctrl控制器驱动关联,这里会用到6.3 虚拟pinctrl驱动中的虚拟pinctrl驱动,并且还需要在 pinctrl 控制器驱动的 pinmux_ops 中实现 gpio_request_enable 或 request 函数、gpio_disable_free 或 free函数、pinctrl_gpio_direction_input 函数、 pinctrl_gpio_direction_output 函数。
设备树编写
设备树包括三个部分,分别是虚拟pinctr控制器l的节点、虚拟GPIO控制器节点设备、虚拟LED设备节点
//虚拟pinctrl的节点
virtual_pinctrl {
compatible = "atk,virtual_pinctrl";
//描述设备引脚的节点,节点内容通过dt_node_to_map解析到pinctrl_map中
pinctrl_i2c: i2cgrp {
functions = "af1", "af5";
groups = "pin0", "pin1";
configs = <0x1122 0x3344>;
};
};
//虚拟GPIO节点
virt_gpio: virtual_gpio {
compatible = "atk,virtual_gpio";
gpio-controller;
#gpio-cells = <2>;
ngpios = <6>;
gpio-ranges = <&virt_pinctrl 0 0 6>;
};
//虚拟LED节点
virt_led {
compatible = "atk,led";
led-gpios = <&virt_gpio 0 GPIO_ACTIVE_HIGH>;
status = "okay";
labe = "virtual";
};
驱动代码编写
编写一个GPIO控制器驱动主要包括以下步骤
- 实现GPIO操作函数
- 创建并初始化struct gpio_chip对象
- 利用struct gpio_chip对象注册GPIO控制器驱动
#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>
//记录IO输入输出
static uint32_t direction = 0;
//记录IO电平状态
static uint32_t output = 0;
//获取GPIO方向,0输出,1输入
static int virtual_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
printk("get pin direction %d, it's direction = %s\n", offset, (direction & (0x01 << offset)) ? "output" : "input");
return (direction & (0x01 << offset)) ? 0 : 1;
}
//设置GPIO为输入
static int virtual_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
printk("set pin %d as input\n", offset);
direction &= ~(0x01 << offset);
return 0;
}
//设置GPIO为输出,并输出高电平或者低电平
static int virtual_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
printk("set pin %d as output %s\n", offset, value ? "high" : "low");
direction |= 0x01 << offset;
if(value)
output |= 0x01 << offset;
else
output &= ~(0x01 << offset);
return 0;
}
//获取GPIO电平状态
static int virtual_gpio_get(struct gpio_chip *chip, unsigned offset)
{
printk("get pin value %d, it's value = %s\n", offset, (output & (0x01 << offset)) ? "high" : "low");
return (output & (0x01 << offset)) ? 1 : 0;
}
//设置GPIO电平状态
static void virtual_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
printk("set pin value %d as %s\n", offset, value ? "high" : "low");
if(value)
output |= 0x01 << offset;
else
output &= ~(0x01 << offset);
}
//配置 GPIO
static int virtual_gpio_set_config(struct gpio_chip *chip, unsigned offset, unsigned long config)
{
printk("set pin %d config %ld\n", offset, config);
return 0;
}
//设备和驱动匹配成功执行
static int virtual_probe(struct platform_device *pdev)
{
int result;
uint32_t value;
struct gpio_chip *gpio_chip;
printk("%s\n", __FUNCTION__);
/* 分配gpio_chip */
gpio_chip = devm_kzalloc(&pdev->dev, sizeof(struct gpio_chip), GFP_KERNEL);
if(!gpio_chip) {
printk("alloc gpio_chip failed\r\n");
return -ENOMEM;
}
/* 设置gpio_chip */
gpio_chip->parent = &pdev->dev;
gpio_chip->owner = THIS_MODULE;
gpio_chip->label = pdev->name;
/* 设置函数,这里request直接调用gpiochip_generic_request,free直接调用gpiochip_generic_free */
gpio_chip->request = gpiochip_generic_request;
gpio_chip->free = gpiochip_generic_free;
gpio_chip->get_direction = virtual_gpio_get_direction;
gpio_chip->direction_input = virtual_gpio_direction_input;
gpio_chip->direction_output = virtual_gpio_direction_output;
gpio_chip->get = virtual_gpio_get;
gpio_chip->set = virtual_gpio_set;
gpio_chip->set_config = virtual_gpio_set_config,
/* 设置base、ngpio值,base==-1表示自动分配 */
gpio_chip->base = -1;
result = of_property_read_u32(pdev->dev.of_node, "ngpios", &value);
if(result < 0)
{
printk("get ngpios failed\r\n");
return result;
}
gpio_chip->ngpio = value;
/* 注册gpio_chip */
return devm_gpiochip_add_data(&pdev->dev, gpio_chip, NULL);;
}
//设备或驱动卸载时执行
static int virtual_remove(struct platform_device *pdev)
{
printk("%s\n", __FUNCTION__);
return 0;
}
/* 匹配列表,用于设备树和平台驱动匹配 */
static const struct of_device_id virtual_of_match[] = {
{.compatible = "atk,virtual_gpio"},
{ /* Sentinel */ }
};
/* 平台驱动 */
static struct platform_driver virtual_drv = {
.driver = {
.name = "virtual_gpio",
.owner = THIS_MODULE,
.pm = NULL,
.of_match_table = virtual_of_match,
},
.probe = virtual_probe,
.remove = virtual_remove,
};
static int __init virtual_drv_init(void)
{
int result;
printk("%s\r\n", __FUNCTION__);
//注册平台驱动
result = platform_driver_register(&virtual_drv);
if(result != 0)
printk("add virtual driver failed\r\n");
return result;
}
static void __exit virtual_drv_exit(void)
{
printk("%s\r\n", __FUNCTION__);
//注销平台驱动
platform_driver_unregister(&virtual_drv);
}
module_init(virtual_drv_init);
module_exit(virtual_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("virtual_gpio_controller");
测试代码编写
测试代码使用使用Linux的GPIO驱动中的基于GPIO子系统的LED驱动即可
上机测试
- 修改设备树,然后编译设备树,并用新的设备树启动
- 在这里下载代码并进行编译,然后拷贝到目标板
- 依次执行命令insmod virtual_pinctrl.ko、insmod virtual_gpio.ko、insmod led.ko,加载虚拟pinctrl驱动、虚拟GPIO驱动、LED驱动。
- 执行命令echo 0 > /dev/virtual_led和echo 1 > /dev/virtual_led,通过调试输出可以发现LED驱动已经成功调用到GPIO驱动的接口
注意:卸载时必须按led.ko、virtual_gpio.ko、virtual_pinctrl.ko的顺序卸载,否则可能会报内存错误