pinctrl子系统和gpio子系统简化了我们驱动工程师的开发,将芯片的底层操作和驱动分割开来了,底层由bsp工程师完成,他来实现pinctrl子系统和gpio子系统,而我们只需要使用该系统就可完成相关的设备的初始化和配置。pinctrl完成的是引脚的配置,比如引脚的复用,引脚属性(上下拉,电阻大小等等)。gpio完成的是gpio的输入输出功能,给驱动提供接口来控制引脚的输出电平或读取引脚的值。这两者的配置我们只需要在设备树中添加就OK了。
该节点用于定义
pinctrl的节点应该添加到&iomuxc节点下, GPIO5相关的添加到&iomuxc_snvs下
// 基本结构如下:
pinctrl_test: testgrp {
fsl,pins = <
3 /* 设备所使用的 PIN 配置信息 */
>;
};
// eg:
pinctrl_rgb_led:rgb_led {
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
需要添加的属性有:
// eg:
my_rgb_led {
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default";
compatible = "fire,my_rgb_led";
pinctrl-0 = <&pinctrl_rgb_led>;
// 后面的与GPIO子系统有关
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
属性:gpios = <>,属性值有三个,第一个指定使用的引脚属于那一组(格式为&gpio1, &gpio2,…),第二个为引脚号,第三个为一个宏定义,指定什么电平有效(GPIO_ACTIVE_HIGH, GPIO_ACTIVE_LOW) 。
// eg:
my_rgb_led {
...
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请
int gpio_request(unsigned gpio, const char *label)
对申请的gpio进行释放
void gpio_free(unsigned gpio)
设置某个GPIO为输入
int gpio_direction_input(unsigned gpio)
此函数用于设置某个 GPIO 为输出,并且设置默认输出值
int gpio_direction_output(unsigned gpio, int value)
此函数用于获取某个 GPIO 的值(0 或 1)
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
此函数用于设置某个 GPIO 的值
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
用于获取设备树某个属性里面定义了几个 GPIO 信息
int of_gpio_named_count(struct device_node *np, const char *propname)
与上一个函数功能相同,不同的是该函数指定了统计属性为"gpios"
int of_gpio_count(struct device_node *np)
此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
// 根节点下添加
my_rgb_led {
#address-cells = <1>;
#size-cells = <1>;
pinctrl-names = "default";
compatible = "fire,my_rgb_led";
pinctrl-0 = <&pinctrl_rgb_led>;
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
// &iomuxc节点下添加
pinctrl_rgb_led:rgb_led {
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
/* 编写要点
1. 完成平台驱动(platform_driver_register())的注册,probe, remove函数的实现
2. 完成驱动(driver_register())的注册,增加设备节点,实现file_operation结构体的相关函数
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major = 0; // 主设备号
static struct class *led_class;
static int rgb_led_red;
static int rgb_led_green;
static int rgb_led_blue;
static int current_led;
static int led_drv_open(struct inode *node, struct file *file)
{
int minor = iminor(node);
switch(minor) {
case 0:
current_led = rgb_led_red;
break;
case 1:
current_led = rgb_led_green;
break;
case 2:
current_led = rgb_led_blue;
break;
default:
printk("========error: not minor=========\n");
}
printk("current_led: %d\n", current_led);
printk("===============%s===============\n", __FUNCTION__);
return 0;
}
static int led_drv_close(struct inode *node, struct file *file)
{
printk("%s\n", __FUNCTION__);
return 0;
}
static ssize_t led_drv_read(struct file *file, char __user * buf, size_t size, loff_t *offset)
{
printk("%s\n", __FUNCTION__);
return 0;
}
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int minor;
char status;
struct inode *node = file_inode(file);
minor = iminor(node);
copy_from_user(&status, buf, 1);
if (status) {
// 开灯
gpio_direction_output(current_led, 0);
} else {
// 关灯
gpio_direction_output(current_led, 1);
}
printk("%s\n", __FUNCTION__);
return 0;
}
static struct file_operations led_drv = {
.owner = THIS_MODULE, //gcc用法
.open = led_drv_open,
.read = led_drv_read,
.write = led_drv_write,
.release = led_drv_close,
};
static int rgb_led_probe(struct platform_device *pdev)
{
int err;
// 获取资源
struct device_node *rgb_device_node = NULL;
// led节点的父节点
rgb_device_node = of_find_node_by_path("/my_rgb_led");
if (rgb_device_node == NULL) {
printk("%s,%s,%d error: not find /my_reg_led\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
// 获取引脚编号
rgb_led_red = of_get_named_gpio(rgb_device_node, "rgb_led_red", 0);
rgb_led_green = of_get_named_gpio(rgb_device_node, "rgb_led_green", 0);
rgb_led_blue = of_get_named_gpio(rgb_device_node, "rgb_led_blue", 0);
printk("========red_red: %d=========\n", rgb_led_red);
printk("========red_green: %d=========\n", rgb_led_green);
printk("========red_blue: %d=========\n", rgb_led_blue);
gpio_direction_output(rgb_led_red, 1);
gpio_direction_output(rgb_led_green, 1);
gpio_direction_output(rgb_led_blue, 1);
// 完成字符设备的注册
major = register_chrdev(0, "rgb_led", &led_drv);
// 注册设备类
led_class = class_create(THIS_MODULE, "hxdled_class");
err = PTR_ERR(led_class);
if (IS_ERR(led_class)) {
unregister_chrdev(major, "rgb_led");
return -1;
}
// 注册设备节点,/dev/rgb_led%
device_create(led_class, NULL, MKDEV(major, 0), NULL, "led_red");
device_create(led_class, NULL, MKDEV(major, 1), NULL, "led_green");
device_create(led_class, NULL, MKDEV(major, 2), NULL, "led_blue");
printk("=======================%s========================\n", __FUNCTION__);
return 0;
}
static int rgb_led_remove(struct platform_device *dev)
{
// 释放设备节点
device_destroy(led_class, MKDEV(major, 0));
device_destroy(led_class, MKDEV(major, 1));
device_destroy(led_class, MKDEV(major, 2));
// 释放设备类
class_destroy(led_class);
// 释放注册字符设备
unregister_chrdev(major, "rgb_led");
printk("%s\n", __FUNCTION__);
return 0;
}
static const struct of_device_id rgb_led[] = {
{.compatible = "fire,my_rgb_led"},
};
static struct platform_driver rgb_led_platform_driver = {
.probe = rgb_led_probe,
.remove = rgb_led_remove,
.driver = {
.name = "rgb-leds-device-tree",
.owner = THIS_MODULE,
//.remove = rgb_led_remove,
.of_match_table = rgb_led, // match匹配的时候会用到这个列表里面的compatible和设备树里面的compatible对比
}
};
// 平台驱动入口函数
static int __init platform_rgb_led_init(void)
{
int ret = 0;
// 完成平台驱动的注册
ret = platform_driver_register(&rgb_led_platform_driver);
printk("%s\n", __FUNCTION__);
return 0;
}
// 平台驱动出口函数
static void __exit platform_rgb_led_exit(void)
{
// 完成平台驱动的移除
platform_driver_unregister(&rgb_led_platform_driver);
printk("%s\n", __FUNCTION__);
return;
}
module_init(platform_rgb_led_init);
module_exit(platform_rgb_led_exit);
MODULE_LICENSE("GPL");
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fd;
char status;
if (argc != 3) {
printf("Usage %s \n" , argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open error");
return -1;
}
if (strcmp(argv[2], "on") == 0) {
// 开灯
status = 1;
write(fd, &status, 1);
} else if (strcmp(argv[2], "off") == 0) {
// 关灯
status = 0;
write(fd, &status, 1);
} else {
printf("Usage %s \n" , argv[0]);
return -1;
}
close(fd);
return 0;
}
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILE
KERN_DIR = /home/hxd/workdir/ebf_linux_kernel_6ull_depth1/build_image/build
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o led_test led_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f hello_drv_test
obj-m += led_driver.o