IMX6ULL學習篇——實戰:使用設備樹/Pinctl-gpio子系統驅動LED
前言
? 經過層層考驗,我們即將接近現代的LED驅動的解決方案了。那就是使用最現代的方式開發一個簡單的GPIO驅動外設。
? 如果您忘記了設備樹的相關內容,請自行到筆者的上一篇博客復習
修改我們的設備樹
? 我們現在修改我們的設備樹,在根節點下,直接添加一個myled節點,我們做了如下的添加
myled {#address-cells = <1>;#size-cells = <1>;compatible = "charlie-led";status = "okay";reg = <0x020C406C 0x040x020E0068 0x040x020E02F4 0x040x0209C000 0x040x0209C004 0x04>;};
myled
:節點名稱,表示這是一個LED設備。#address-cells = <1>
:- 指定子節點中"reg"屬性的地址字段用1個32位數字表示。
#size-cells = <1>
:- 指定子節點中"reg"屬性的大小字段用1個32位數字表示。
compatible = "charlie-led"
:- 驅動兼容性標識,表示這個設備與"charlie-led"驅動程序兼容。
status = "okay"
:- 設備狀態,表示這個設備處于啟用狀態。
reg
屬性:- 定義了5組寄存器地址和大小,每組包含:
- 第一個數字是寄存器地址
- 第二個數字是寄存器區域大小(這里是0x04表示4字節)
- 具體地址:
- 0x020C406C - 大小為0x04
- 0x020E0068 - 大小為0x04
- 0x020E02F4 - 大小為0x04
- 0x0209C000 - 大小為0x04
- 0x0209C004 - 大小為0x04
- 定義了5組寄存器地址和大小,每組包含:
然后重新編譯我們的設備樹文件
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
? 將得到的設備樹放置到我們的tftp文件夾目錄下,之后使用這個新的設備樹啟動我們的Linux內核
? 啟動成功后,在設備樹的文件夾下:
cd /proc/device-tree/
/sys/firmware/devicetree/base # ls
#address-cells model
#size-cells myled
aliases name
backlight pxp_v4l2
chosen regulators
clocks reserved-memory
compatible soc
cpus sound
interrupt-controller@00a01000 spi4
memory
/sys/firmware/devicetree/base # cd myled/
/sys/firmware/devicetree/base/myled # ls
#address-cells compatible reg
#size-cells name status
? 可用看到我們的myled和下面的東西,這里都被讀取到了。下面就是直接對設備樹進行讀取和編程。
方式1:使用讀取的方式直接拿到寄存器的值
? 我們下面一個辦法就是直接通過設備樹,拿到LED外設GPIO寄存器的值
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <asm/mach/map.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("charliechen<charliechen114514@demo.com>");#define LED_DEV_NAME ("charlies_led")
#define LED_DEV_N (1)
/* LED Physical Address See the manual
*/
#define CCM_CCGR1_BASE (0x020C406C)
#define GPIO1_IOLED_BASE (0x020E0068)
#define GPIO1_IOPAD_BASE (0x020E02F4)
#define GPIO1_IODR_BASE (0x0209C000)
#define GPIO1_GDIR_BASE (0x0209C004)/* mappings of the io phe*/
static void* __iomem LED_CCGR1;
static void* __iomem LEDBASE;
static void* __iomem LEDPAD_BASE;
static void* __iomem LEDDR_BASE;
static void* __iomem LEDGDIR_BASE;/* we use a device struct */
typedef struct __myled {struct cdev char_dev_handle;dev_t dev_id;int major;int minor;struct class* led_class_handle; struct device* led_handle_device;// node infostruct device_node* node;
}MyLED;static MyLED myled;/* operations cached */
static char operations_cached[20];static void __led_turn_on(void)
{u32 val = 0;val = readl(LEDDR_BASE);val &= ~(1 << 3);writel(val, LEDDR_BASE);
}static void __led_turn_off(void)
{u32 val = 0;val = readl(LEDDR_BASE);val |= (1 << 3);writel(val, LEDDR_BASE);
}static u8 __fetch_led_status(void)
{u32 val = 0;val = readl(LEDDR_BASE);return !(val & (1 << 3));
}static void __enable_led_mappings(u32 regdata[10])
{int val = 0;pr_info("Ready to mappings the registers...\n");// LED_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);// LEDBASE = ioremap(GPIO1_IOLED_BASE, 4);// LEDPAD_BASE = ioremap(GPIO1_IOPAD_BASE, 4);// LEDDR_BASE = ioremap(GPIO1_IODR_BASE, 4);// LEDGDIR_BASE = ioremap(GPIO1_GDIR_BASE, 4);LED_CCGR1 = ioremap(regdata[0], regdata[1]);LEDBASE = ioremap(regdata[2], regdata[3]);LEDPAD_BASE = ioremap(regdata[4], regdata[5]);LEDDR_BASE = ioremap(regdata[6], regdata[7]);LEDGDIR_BASE = ioremap(regdata[8], regdata[9]);pr_info("mappings the registers done!\n");pr_info("LED_CCGR1 ioremap to: %p\n", LED_CCGR1);pr_info("LEDBASE ioremap to: %p\n", LEDBASE);pr_info("LEDPAD_BASE ioremap to: %p\n", LEDPAD_BASE);pr_info("LEDDR_BASE ioremap to: %p\n", LEDDR_BASE);pr_info("LEDGDIR_BASE ioremap to: %p\n", LEDGDIR_BASE);pr_info("initialize the led registers\n");val = readl(LED_CCGR1);// clear the bitsval &= ~(3 << 26);val |= (3 << 26);writel(val, LED_CCGR1);writel(0x5, LEDBASE);writel(0x10B0, LEDPAD_BASE);val = readl(LEDGDIR_BASE);val |= 1 << 3;writel(val, LEDGDIR_BASE);pr_info("operations of led is accessable!\n");
}static void __disable_led_mappings(void)
{__led_turn_off();pr_info("set the led turning off...\n");pr_info("set the led turning off done!\n");pr_info("Ready to unmappings the registers...\n"); iounmap(LED_CCGR1);iounmap(LEDBASE);iounmap(LEDPAD_BASE);iounmap(LEDDR_BASE);iounmap(LEDGDIR_BASE);pr_info("unmappings the registers done\n");
}static ssize_t led_read(struct file* filp, char* buffer, size_t count, loff_t* ppos)
{const char* status = "opened";int ret = 0;pr_info("\nled device is reading!\n");if(!__fetch_led_status()){status = "closed";} ret = copy_to_user(buffer, status, strlen(status));if(ret < 0){pr_warn("Copy to the user failed\n");return -EFAULT;}return 0;
}static ssize_t led_write(struct file* filp,const char* buffer, size_t count, loff_t* ppos)
{int check = 0;pr_info("\nled device is ready writing!\n");check = copy_from_user(operations_cached, buffer, count);if(check < 0){pr_warn("Can not copy from user!\n");return -EFAULT;}if(!strcmp(operations_cached, "open")){__led_turn_on();}else if(!strcmp(operations_cached, "close")){__led_turn_off();}else{pr_warn("Can not find the indications operations!\n""check the business: %s", operations_cached);}return 0;
}static int led_open(struct inode* inode, struct file* filp)
{pr_info("\nled device is opened!\n");return 0;
}static int led_close(struct inode* inode, struct file* filp)
{pr_info("\nled device is released!\n");return 0;
}static const struct file_operations led_ops = {.owner = THIS_MODULE,.open = led_open,.release = led_close,.write = led_write,.read = led_read
};/* register the device */
static int __init led_init(void)
{int ret = 0;u32 regdata[10];const char* compatible;const char* status_str;// fetch platform treemyled.node = of_find_node_by_path("/myled");if(myled.node == NULL){pr_warn("can not find the myled\n");return -EINVAL;}else{pr_info("find the myled in device tree!\n");}ret = of_property_read_string(myled.node, "status", &status_str);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("status=%s\n", status_str);}ret = of_property_read_string(myled.node, "compatible", &compatible);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("compatible=%s\n", status_str);} ret = of_property_read_u32_array(myled.node, "reg", regdata, 10);if(ret < 0){pr_info("failed to fetch the reg device info!\n");return -EINVAL;}else{u8 i = 0;for(i = 0; i < 10; i+=2){pr_info("reg data: %#X %#X\n", regdata[i], regdata[i + 1]);}}// create the led deviceif(myled.major){myled.dev_id = MKDEV(myled.major, 0);ret = register_chrdev_region(myled.dev_id, LED_DEV_N, LED_DEV_NAME);}else{ret = alloc_chrdev_region(&myled.dev_id, 0, LED_DEV_N, LED_DEV_NAME);myled.major = MAJOR(myled.dev_id);myled.minor = MINOR(myled.dev_id);}if(ret < 0){pr_warn("Error in registering the device! Failed to register the region\n");goto failed_register_chrdev_region;}pr_info("Success in registering the device major=%d, minor=%d\n",myled.major, myled.minor);myled.char_dev_handle.owner = THIS_MODULE;cdev_init(&(myled.char_dev_handle), &led_ops);ret = cdev_add(&(myled.char_dev_handle), myled.dev_id, LED_DEV_N);if(ret < 0){pr_warn("Failed to add chardev into the kernel_list!\n");goto failed_add_chardev;}pr_info("Prepare to create the device class file...\n");myled.led_class_handle = class_create(THIS_MODULE, LED_DEV_NAME);if(IS_ERR(myled.led_class_handle)){// class creation failed, rollback!pr_warn("Failed to create the led class handle...\n");goto failed_create_class;}myled.led_handle_device = device_create(myled.led_class_handle, NULL, myled.dev_id, "%s", LED_DEV_NAME);if(IS_ERR(myled.led_handle_device)){pr_warn("Failed to create the led device ...\n");goto failed_create_device;}__enable_led_mappings(regdata);return 0;failed_create_device:class_destroy(myled.led_class_handle);
failed_create_class:cdev_del(&(myled.char_dev_handle));
failed_add_chardev:unregister_chrdev_region(myled.dev_id, LED_DEV_N);
failed_register_chrdev_region:return -EIO;
}static void __exit led_deinit(void)
{pr_info("Ready to remove the hook of the device operating file\n");device_destroy(myled.led_class_handle, myled.dev_id);class_destroy(myled.led_class_handle);pr_info("Ready to release the char dev handle\n");cdev_del(&(myled.char_dev_handle));pr_info("Ready to unhook the device region\n");unregister_chrdev_region(myled.dev_id, LED_DEV_N);__disable_led_mappings();pr_info("Successfully unregister the device\n");
}module_init(led_init);
module_exit(led_deinit);
? 中間很長,但是只修改了led初始化的部分:
// fetch platform treemyled.node = of_find_node_by_path("/myled");if(myled.node == NULL){pr_warn("can not find the myled\n");return -EINVAL;}else{pr_info("find the myled in device tree!\n");}ret = of_property_read_string(myled.node, "status", &status_str);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("status=%s\n", status_str);}ret = of_property_read_string(myled.node, "compatible", &compatible);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("compatible=%s\n", status_str);} ret = of_property_read_u32_array(myled.node, "reg", regdata, 10);if(ret < 0){pr_info("failed to fetch the reg device info!\n");return -EINVAL;}else{u8 i = 0;for(i = 0; i < 10; i+=2){pr_info("reg data: %#X %#X\n", regdata[i], regdata[i + 1]);}}
? 讀取設備樹,第一件事情就是把節點找到了,然后這里,我們只是嘗試讀取設備樹上的葉子節點的屬性,這樣來測試我們的設備樹的節點讀取是否成功,真正設計到驅動LED本身的,還是of_property_read_u32_array
讀取我們的寄存器的值,之后的事情就很簡單了,我們直接使用這些地址進行映射。
? 編譯下載得到:
/module_test # insmod led.ko
find the myled in device tree!
status=okay
compatible=charlie-led
reg data: 0X20C406C 0X4
reg data: 0X20E0068 0X4
reg data: 0X20E02F4 0X4
reg data: 0X209C000 0X4
reg data: 0X209C004 0X4
Success in registering the device major=249, minor=0
Prepare to create the device class file...
Ready to mappings the registers...
mappings the registers done!
LED_CCGR1 ioremap to: f42c406c
LEDBASE ioremap to: f42e0068
LEDPAD_BASE ioremap to: f42e02f4
LEDDR_BASE ioremap to: a08e6000
LEDGDIR_BASE ioremap to: a08ee004
initialize the led registers
operations of led is accessable!
/module_test # ./chrdev_application /dev/charlies_led write openled device is opened!
args: 4
led device is ready writing!user process the write issue: o
led device is released!
pen
/module_test # ./chrdev_application /dev/charlies_led write closeled device is opened!
args: 4
led device is ready writing!user process the write issue: c
led device is released!
lose
/module_test # rmmod led.ko
Ready to remove the hook of the device operating file
Ready to release the char dev handle
Ready to unhook the device region
set the led turning off...
set the led turning off done!
Ready to unmappings the registers...
unmappings the registers done
Successfully unregister the device
使用工具函數of_iomap直接映射
? 上面的方法還是很麻煩,我們可不可以直接一步到位呢?當然可以,使用of_iomap函數可以一步到位
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("charliechen<charliechen114514@demo.com>");#define LED_DEV_NAME ("charlies_led")
#define LED_DEV_N (1)
/* LED Physical Address See the manual
*/
#define CCM_CCGR1_BASE (0x020C406C)
#define GPIO1_IOLED_BASE (0x020E0068)
#define GPIO1_IOPAD_BASE (0x020E02F4)
#define GPIO1_IODR_BASE (0x0209C000)
#define GPIO1_GDIR_BASE (0x0209C004)/* mappings of the io phe*/
static void* __iomem LED_CCGR1;
static void* __iomem LEDBASE;
static void* __iomem LEDPAD_BASE;
static void* __iomem LEDDR_BASE;
static void* __iomem LEDGDIR_BASE;/* we use a device struct */
typedef struct __myled {struct cdev char_dev_handle;dev_t dev_id;int major;int minor;struct class* led_class_handle; struct device* led_handle_device;struct device_node* node;
}MyLED;static MyLED myled;/* operations cached */
static char operations_cached[20];static void __led_turn_on(void)
{u32 val = 0;val = readl(LEDDR_BASE);val &= ~(1 << 3);writel(val, LEDDR_BASE);
}static void __led_turn_off(void)
{u32 val = 0;val = readl(LEDDR_BASE);val |= (1 << 3);writel(val, LEDDR_BASE);
}static u8 __fetch_led_status(void)
{u32 val = 0;val = readl(LEDDR_BASE);return !(val & (1 << 3));
}static void __enable_led_mappings(void)
{int val = 0;pr_info("Ready to mappings the registers...\n");LED_CCGR1 = of_iomap(myled.node, 0);LEDBASE = of_iomap(myled.node, 1);LEDPAD_BASE = of_iomap(myled.node, 2);LEDDR_BASE = of_iomap(myled.node, 3);LEDGDIR_BASE = of_iomap(myled.node, 4);// LED_CCGR1 = ioremap(regdata[0], regdata[1]);// LEDBASE = ioremap(regdata[2], regdata[3]);// LEDPAD_BASE = ioremap(regdata[4], regdata[5]);// LEDDR_BASE = ioremap(regdata[6], regdata[7]);// LEDGDIR_BASE = ioremap(regdata[8], regdata[9]);pr_info("mappings the registers done!\n");pr_info("LED_CCGR1 ioremap to: %p\n", LED_CCGR1);pr_info("LEDBASE ioremap to: %p\n", LEDBASE);pr_info("LEDPAD_BASE ioremap to: %p\n", LEDPAD_BASE);pr_info("LEDDR_BASE ioremap to: %p\n", LEDDR_BASE);pr_info("LEDGDIR_BASE ioremap to: %p\n", LEDGDIR_BASE);pr_info("initialize the led registers\n");val = readl(LED_CCGR1);// clear the bitsval &= ~(3 << 26);val |= (3 << 26);writel(val, LED_CCGR1);writel(0x5, LEDBASE);writel(0x10B0, LEDPAD_BASE);val = readl(LEDGDIR_BASE);val |= 1 << 3;writel(val, LEDGDIR_BASE);pr_info("operations of led is accessable!\n");
}static void __disable_led_mappings(void)
{__led_turn_off();pr_info("set the led turning off...\n");pr_info("set the led turning off done!\n");pr_info("Ready to unmappings the registers...\n"); iounmap(LED_CCGR1);iounmap(LEDBASE);iounmap(LEDPAD_BASE);iounmap(LEDDR_BASE);iounmap(LEDGDIR_BASE);pr_info("unmappings the registers done\n");
}static ssize_t led_read(struct file* filp, char* buffer, size_t count, loff_t* ppos)
{const char* status = "opened";int ret = 0;pr_info("\nled device is reading!\n");if(!__fetch_led_status()){status = "closed";} ret = copy_to_user(buffer, status, strlen(status));if(ret < 0){pr_warn("Copy to the user failed\n");return -EFAULT;}return 0;
}static ssize_t led_write(struct file* filp,const char* buffer, size_t count, loff_t* ppos)
{int check = 0;pr_info("\nled device is ready writing!\n");check = copy_from_user(operations_cached, buffer, count);if(check < 0){pr_warn("Can not copy from user!\n");return -EFAULT;}if(!strcmp(operations_cached, "open")){__led_turn_on();}else if(!strcmp(operations_cached, "close")){__led_turn_off();}else{pr_warn("Can not find the indications operations!\n""check the business: %s", operations_cached);}return 0;
}static int led_open(struct inode* inode, struct file* filp)
{pr_info("\nled device is opened!\n");return 0;
}static int led_close(struct inode* inode, struct file* filp)
{pr_info("\nled device is released!\n");return 0;
}static const struct file_operations led_ops = {.owner = THIS_MODULE,.open = led_open,.release = led_close,.write = led_write,.read = led_read
};/* register the device */
static int __init led_init(void)
{int ret = 0;u32 regdata[10];const char* compatible;const char* status_str;// fetch platform treemyled.node = of_find_node_by_path("/myled");if(myled.node == NULL){pr_warn("can not find the myled\n");return -EINVAL;}else{pr_info("find the myled in device tree!\n");}ret = of_property_read_string(myled.node, "status", &status_str);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("status=%s\n", status_str);}ret = of_property_read_string(myled.node, "compatible", &compatible);if(ret < 0){pr_warn("can not fetch the status node info");return EINVAL;}else{pr_info("compatible=%s\n", status_str);} ret = of_property_read_u32_array(myled.node, "reg", regdata, 10);if(ret < 0){pr_info("failed to fetch the reg device info!\n");return -EINVAL;}else{u8 i = 0;for(i = 0; i < 10; i+=2){pr_info("reg data: %#X %#X\n", regdata[i], regdata[i + 1]);}}// create the led deviceif(myled.major){myled.dev_id = MKDEV(myled.major, 0);ret = register_chrdev_region(myled.dev_id, LED_DEV_N, LED_DEV_NAME);}else{ret = alloc_chrdev_region(&myled.dev_id, 0, LED_DEV_N, LED_DEV_NAME);myled.major = MAJOR(myled.dev_id);myled.minor = MINOR(myled.dev_id);}if(ret < 0){pr_warn("Error in registering the device! Failed to register the region\n");goto failed_register_chrdev_region;}pr_info("Success in registering the device major=%d, minor=%d\n",myled.major, myled.minor);myled.char_dev_handle.owner = THIS_MODULE;cdev_init(&(myled.char_dev_handle), &led_ops);ret = cdev_add(&(myled.char_dev_handle), myled.dev_id, LED_DEV_N);if(ret < 0){pr_warn("Failed to add chardev into the kernel_list!\n");goto failed_add_chardev;}pr_info("Prepare to create the device class file...\n");myled.led_class_handle = class_create(THIS_MODULE, LED_DEV_NAME);if(IS_ERR(myled.led_class_handle)){// class creation failed, rollback!pr_warn("Failed to create the led class handle...\n");goto failed_create_class;}myled.led_handle_device = device_create(myled.led_class_handle, NULL, myled.dev_id, "%s", LED_DEV_NAME);if(IS_ERR(myled.led_handle_device)){pr_warn("Failed to create the led device ...\n");goto failed_create_device;}__enable_led_mappings();return 0;failed_create_device:class_destroy(myled.led_class_handle);
failed_create_class:cdev_del(&(myled.char_dev_handle));
failed_add_chardev:unregister_chrdev_region(myled.dev_id, LED_DEV_N);
failed_register_chrdev_region:return -EIO;
}static void __exit led_deinit(void)
{pr_info("Ready to remove the hook of the device operating file\n");device_destroy(myled.led_class_handle, myled.dev_id);class_destroy(myled.led_class_handle);pr_info("Ready to release the char dev handle\n");cdev_del(&(myled.char_dev_handle));pr_info("Ready to unhook the device region\n");unregister_chrdev_region(myled.dev_id, LED_DEV_N);__disable_led_mappings();pr_info("Successfully unregister the device\n");
}module_init(led_init);
module_exit(led_deinit);
/module_test # insmod led.ko
find the myled in device tree!
status=okay
compatible=charlie-led
reg data: 0X20C406C 0X4
reg data: 0X20E0068 0X4
reg data: 0X20E02F4 0X4
reg data: 0X209C000 0X4
reg data: 0X209C004 0X4
Success in registering the device major=249, minor=0
Prepare to create the device class file...
Ready to mappings the registers...
mappings the registers done!
LED_CCGR1 ioremap to: f42c406c
LEDBASE ioremap to: f42e0068
LEDPAD_BASE ioremap to: f42e02f4
LEDDR_BASE ioremap to: a08e6000
LEDGDIR_BASE ioremap to: a08ee004
initialize the led registers
operations of led is accessable!
/module_test # ./chrdev_application /dev/charlies_led write openled device is opened!
args: 4
led device is ready writing!user process the write issue: o
led device is released!
pen
/module_test # ./chrdev_application /dev/charlies_led write closeled device is opened!
args: 4
led device is ready writing!user process the write issue: c
led device is released!
lose
/module_test # rmmod led.ko
Ready to remove the hook of the device operating file
Ready to release the char dev handle
Ready to unhook the device region
set the led turning off...
set the led turning off done!
Ready to unmappings the registers...
unmappings the registers done
Successfully unregister the device
使用pinctl和gpio子系統——LED驅動
? 對于如何初始化LED燈所使用的GPIO,整個過程可以分為幾個步驟。
首先需要修改設備樹,添加相應的節點,其中最重要的是設置reg屬性,這個屬性包含了GPIO相關的寄存器信息。接下來要獲取reg屬性中的IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03和IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03這兩個寄存器地址,并初始化它們,這兩個寄存器用于設置GPIO1_IO03這個引腳的復用功能、上下拉、速度等參數。
在完成這一步后,由于GPIO1_IO03這個引腳已經被復用為GPIO功能,因此還需要設置GPIO1_IO03相關的GPIO寄存器,也就是GPIO1_DR和GPIO1_GDIR這兩個寄存器。簡單來說,第二步完成了對GPIO1_IO03引腳的初始化,設置其復用功能等屬性,比如將其設置為GPIO功能;
而第三步則完成了對GPIO本身的初始化,設置其輸入輸出方向等。這個流程與STM32的開發類似,都是先設置引腳的復用功能、速度、上下拉等屬性,再配置對應的GPIO功能。實際上,大多數32位SOC的引腳配置都遵循這個模式,因此Linux內核專門針對引腳配置開發了pinctrl子系統,針對GPIO配置開發了gpio子系統。我們綜合應用起來,就能開發大部分的GPIO驅動的外設了。
? 要使用pinctrl 子系統,我們需要在設備樹里面設置 PIN 的配置信息,畢竟 pinctrl 子系統要根據你提供的信息來配置 PIN 功能,一般會在設備樹里面創建一個節點來描述 PIN 的配置信息。我們需要做的就是打開 imx6ull.dtsi 文件,找到一個叫做 iomuxc 的節點,我們實際上會向這里追加數據,但是不在這個文件加,而是在我們的自己的板子配置文件上加。
? 對于IMX6ULL,這個板子的配置實際上寫在了iomuxc節點下的imx6ul-evk下,我們繼續追加
pinctrl_charliesled: ledgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0>;};
? 控制組加好了之后,我們就可以聲明我們的GPIO設備的LED節點的信息
charliesled {#address-cells = <1>;#size-cells = <1>;compatible = "charlies-gpioled";pinctrl-names = "default";pinctrl-0 = <&pinctrl_charliesled>;led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;status = "okay";};
? 但是你需要注意的是——因為我們現在拿到的是原廠的板子,這個GPIO很有可能已經被復用了,因此,你需要做的是搜索一下板子上使用這個GPIO的設備,看看他是不是用在了自己的板子上
- 檢查pinctrl 設置。
- 如果這個 PIN 配置為 GPIO 的話,檢查這個 GPIO 有沒有被別的外設使用。
? 我們就是需要做這兩個事情.檢查發現:
pinctrl_tsc: tscgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0/* MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 */MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0>;};
和
&tsc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_tsc>;/* xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; */measure-delay-time = <0xffff>;pre-charge-time = <0xfff>;status = "okay";
};
? 一次出現了一些不該不限的東西,這些是我們使用的板子上沒有的,可以安全的注釋掉.
? 隨后編譯上傳我們的設備樹,查看是否正常.
使用GPIO驅動子系統為我們化簡驅動
我們下面就來看看我們的Linux操作系統給我們提供了啥花活。gpio_request
函數用于申請一個GPIO管腳,其原型為int gpio_request(unsigned gpio, const char *label)
,其中gpio參數是要申請的GPIO標號(可通過of_get_named_gpio
從設備樹獲取),label是為該GPIO指定的名稱,返回0表示申請成功。當不再需要使用某個GPIO時,應調用void gpio_free(unsigned gpio)
函數進行釋放。
要配置GPIO方向,可以使用gpio_direction_input
和gpio_direction_output
函數。int gpio_direction_input(unsigned gpio)
將指定GPIO設置為輸入模式,而int gpio_direction_output(unsigned gpio, int value)
則將其設置為輸出模式并設置初始輸出值(value參數),兩者都返回0表示成功。
對于GPIO值的讀寫操作,gpio_get_value
宏(實際調用__gpio_get_value
函數)用于讀取GPIO的當前值,其原型為int __gpio_get_value(unsigned gpio)
,返回非負值表示讀取到的電平狀態。gpio_set_value
宏(對應__gpio_set_value
函數)用于設置GPIO輸出電平,原型為void __gpio_set_value(unsigned gpio, int value)
,無返回值。這些函數是GPIO子系統中最常用的API,涵蓋了GPIO的申請、釋放、方向配置及電平操作等基本功能。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("charliechen<charliechen114514@demo.com>");#define LED_OFF_VALUE (1)
#define LED_ON_VALUE (0)
#define LED_CNT (1)
#define MY_LED_NAME ("charliesled")static struct charlies_led_dev {dev_t devid;struct cdev led_cdev;struct class* led_class;struct device* led_device;int led_major;int led_minor;struct device_node* device_node;int led_gpio_n;
}charliesled_dev;static void set_led_status(int value)
{int ret = 0;ret = gpio_direction_output(charliesled_dev.led_gpio_n, value);if(ret < 0){pr_warn("Can not set the led as turning off!\n");}return;
}static int led_open(struct inode *inode, struct file *filp){filp->private_data = &charliesled_dev; return 0;}static ssize_t led_read(struct file* filp, char* buffer, size_t count, loff_t* ppos){int ret = 0;const char* status = "opened";int gpio_current_value = 0;pr_info("Triggering the read gpio status of led!\n");gpio_current_value = gpio_get_value(charliesled_dev.led_gpio_n);if(gpio_current_value == LED_OFF_VALUE){status = "closed";}ret = copy_to_user(buffer, status, strlen(status));if(ret < 0){pr_warn("Copy to the user failed\n");return -EFAULT;}return 0;}/* operations cached */
static char operations_cached[20];
static ssize_t led_write(struct file* filp,const char* buffer, size_t count, loff_t* ppos)
{int check = 0;struct charlies_led_dev *dev = filp->private_data;pr_info("\nled device is ready writing!\n");check = copy_from_user(operations_cached, buffer, count);if(check < 0){pr_warn("Can not copy from user!\n");return -EFAULT;}if(!strcmp(operations_cached, "open")){gpio_set_value(dev->led_gpio_n, LED_ON_VALUE); }else if(!strcmp(operations_cached, "close")){gpio_set_value(dev->led_gpio_n, LED_OFF_VALUE); }else{pr_warn("Can not find the indications operations!\n""check the business: %s", operations_cached);}memset(operations_cached, 0, 20);return 0;
}static int led_release(struct inode *inode, struct file *filp)
{pr_info("Led device is released!\n");return 0;
}static struct file_operations led_op = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,
};static int fetch_from_device_tree(void)
{charliesled_dev.device_node = of_find_node_by_path("/charliesled");if(!charliesled_dev.device_node){pr_warn("Can not find the device node!\n");return -EINVAL;}else{pr_info("we have found the target node!\n");}charliesled_dev.led_gpio_n = of_get_named_gpio(charliesled_dev.device_node, "led-gpio", 0);if(charliesled_dev.led_gpio_n < 0){pr_warn("Can not find the led-gpio property!\n");}pr_info("successfully fetch the led-gpio: %d\n", charliesled_dev.led_gpio_n);pr_info("default we turn off the led");set_led_status(LED_ON_VALUE);return 0;
}static int init_device(void)
{if(charliesled_dev.led_major){// the led device has been defined the valuecharliesled_dev.devid = MKDEV(charliesled_dev.led_major, 0);register_chrdev_region(charliesled_dev.devid, LED_CNT, MY_LED_NAME);}else{alloc_chrdev_region(&charliesled_dev.devid, 0, LED_CNT, MY_LED_NAME);charliesled_dev.led_major = MAJOR(charliesled_dev.devid);charliesled_dev.led_minor = MINOR(charliesled_dev.devid);}pr_info("Device number requires succuss: major-id: %d, minor-id: %d\n", charliesled_dev.led_major, charliesled_dev.led_minor);charliesled_dev.led_cdev.owner = THIS_MODULE;cdev_init(&charliesled_dev.led_cdev, &led_op);cdev_add(&charliesled_dev.led_cdev, charliesled_dev.devid, LED_CNT);pr_info("cdev init and add success!\n");charliesled_dev.led_class = class_create(THIS_MODULE, MY_LED_NAME);if(IS_ERR(charliesled_dev.led_class)){pr_info("class creation failed\n");return PTR_ERR(charliesled_dev.led_class);}pr_info("class creation success!\n");charliesled_dev.led_device = device_create(charliesled_dev.led_class, NULL, charliesled_dev.devid, NULL, MY_LED_NAME);if(IS_ERR(charliesled_dev.led_device)){pr_info("device creation failed\n");return PTR_ERR(charliesled_dev.led_device);}pr_info("device creation success!\n");return 0;
}static int __init led_init(void)
{int ret;pr_info("Fetching from the device tree!\n");if(fetch_from_device_tree()){pr_warn("The fetch failed!\n");return -EINVAL;}pr_info("Successfully fetch from the device tree!\n");ret = init_device();pr_info("Device init finished!\n");return ret;
}static void __exit led_exit(void)
{pr_info("LED Driver Exiting... turn off the device!\n");set_led_status(LED_OFF_VALUE);pr_info("Erase the device and classes...\n");device_destroy(charliesled_dev.led_class, charliesled_dev.devid);class_destroy(charliesled_dev.led_class);pr_info("Erase the cdev and release the dev id\n");cdev_del(&charliesled_dev.led_cdev);unregister_chrdev_region(charliesled_dev.devid, LED_CNT);pr_info("Module exit!\n");
}module_init(led_init);
module_exit(led_exit);
? 現在我們可以測試,結果照常,這里就不展示了
再試一次:BEEP驅動
? 還是一樣,我們第一步是編寫pinctl的group
pinctrl_charliesbeep: beepgrp {fsl,pins = <MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0>;};
? 然后下一步是
charliesbeep {#address-cells = <1>;#size-cells = <1>;compatible = "charlies-gpiobeep";pinctrl-names = "default";pinctrl-0 = <&pinctrl_charliesbeep>;led-gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;status = "okay"; };
? 經過我們的檢查,實際上沒有&gpio5 1和MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01的沖突,咱們直接用就好了,下一步就是稍微小小的修改一下上一個LED的驅動即可:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("charliechen<charliechen114514@demo.com>");#define BEEPOFF_VALUE (1)
#define BEEPON_VALUE (0)
#define BEEPCNT (1)
#define MY_BEEPNAME ("charliesbeep")static struct charlies_beep_dev {dev_t devid;struct cdev beep_cdev;struct class* beep_class;struct device* beep_device;int beep_major;int beep_minor;struct device_node* device_node;int beep_gpio_n;
}charliesbeep_dev;static void set_beep_status(int value)
{int ret = 0;ret = gpio_direction_output(charliesbeep_dev.beep_gpio_n, value);if(ret < 0){pr_warn("Can not set the led as turning off!\n");}return;
}static int beep_open(struct inode *inode, struct file *filp){filp->private_data = &charliesbeep_dev; return 0;}static ssize_t beep_read(struct file* filp, char* buffer, size_t count, loff_t* ppos){int ret = 0;const char* status = "opened";int gpio_current_value = 0;pr_info("Triggering the read gpio status of led!\n");gpio_current_value = gpio_get_value(charliesbeep_dev.beep_gpio_n);if(gpio_current_value == BEEPOFF_VALUE){status = "closed";}ret = copy_to_user(buffer, status, strlen(status));if(ret < 0){pr_warn("Copy to the user failed\n");return -EFAULT;}return 0;}/* operations cached */
static char operations_cached[20];
static ssize_t beep_write(struct file* filp,const char* buffer, size_t count, loff_t* ppos)
{int check = 0;struct charlies_beep_dev *dev = filp->private_data;pr_info("\nled device is ready writing!\n");check = copy_from_user(operations_cached, buffer, count);if(check < 0){pr_warn("Can not copy from user!\n");return -EFAULT;}if(!strcmp(operations_cached, "open")){gpio_set_value(dev->beep_gpio_n, BEEPON_VALUE); }else if(!strcmp(operations_cached, "close")){gpio_set_value(dev->beep_gpio_n, BEEPOFF_VALUE); }else{pr_warn("Can not find the indications operations!\n""check the business: %s", operations_cached);}memset(operations_cached, 0, 20);return 0;
}static int beep_release(struct inode *inode, struct file *filp)
{pr_info("Led device is released!\n");return 0;
}static struct file_operations beep_op = {.owner = THIS_MODULE,.open = beep_open,.read = beep_read,.write = beep_write,.release = beep_release,
};static int fetch_from_device_tree(void)
{charliesbeep_dev.device_node = of_find_node_by_path("/charliesbeep");if(!charliesbeep_dev.device_node){pr_warn("Can not find the device node!\n");return -EINVAL;}else{pr_info("we have found the target node!\n");}charliesbeep_dev.beep_gpio_n = of_get_named_gpio(charliesbeep_dev.device_node, "led-gpio", 0);if(charliesbeep_dev.beep_gpio_n < 0){pr_warn("Can not find the led-gpio property!\n");}pr_info("successfully fetch the led-gpio: %d\n", charliesbeep_dev.beep_gpio_n);pr_info("default we turn off the led");set_beep_status(BEEPOFF_VALUE);return 0;
}static int init_device(void)
{if(charliesbeep_dev.beep_major){// the led device has been defined the valuecharliesbeep_dev.devid = MKDEV(charliesbeep_dev.beep_major, 0);register_chrdev_region(charliesbeep_dev.devid, BEEPCNT, MY_BEEPNAME);}else{alloc_chrdev_region(&charliesbeep_dev.devid, 0, BEEPCNT, MY_BEEPNAME);charliesbeep_dev.beep_major = MAJOR(charliesbeep_dev.devid);charliesbeep_dev.beep_minor = MINOR(charliesbeep_dev.devid);}pr_info("Device number requires succuss: major-id: %d, minor-id: %d\n", charliesbeep_dev.beep_major, charliesbeep_dev.beep_minor);charliesbeep_dev.beep_cdev.owner = THIS_MODULE;cdev_init(&charliesbeep_dev.beep_cdev, &beep_op);cdev_add(&charliesbeep_dev.beep_cdev, charliesbeep_dev.devid, BEEPCNT);pr_info("cdev init and add success!\n");charliesbeep_dev.beep_class = class_create(THIS_MODULE, MY_BEEPNAME);if(IS_ERR(charliesbeep_dev.beep_class)){pr_info("class creation failed\n");return PTR_ERR(charliesbeep_dev.beep_class);}pr_info("class creation success!\n");charliesbeep_dev.beep_device = device_create(charliesbeep_dev.beep_class, NULL, charliesbeep_dev.devid, NULL, MY_BEEPNAME);if(IS_ERR(charliesbeep_dev.beep_device)){pr_info("device creation failed\n");return PTR_ERR(charliesbeep_dev.beep_device);}pr_info("device creation success!\n");return 0;
}static int __init beep_init(void)
{int ret;pr_info("Fetching from the device tree!\n");if(fetch_from_device_tree()){pr_warn("The fetch failed!\n");return -EINVAL;}pr_info("Successfully fetch from the device tree!\n");ret = init_device();pr_info("Device init finished!\n");return ret;
}static void __exit beep_exit(void)
{pr_info("LED Driver Exiting... turn off the device!\n");set_beep_status(BEEPOFF_VALUE);pr_info("Erase the device and classes...\n");device_destroy(charliesbeep_dev.beep_class, charliesbeep_dev.devid);class_destroy(charliesbeep_dev.beep_class);pr_info("Erase the cdev and release the dev id\n");cdev_del(&charliesbeep_dev.beep_cdev);unregister_chrdev_region(charliesbeep_dev.devid, BEEPCNT);pr_info("Module exit!\n");
}module_init(beep_init);
module_exit(beep_exit);
? 完事!