系列文章目录
Linux字符设备驱动详解 Linux字符设备驱动详解二(使用设备驱动模型) Linux字符设备驱动详解三(使用class) Linux字符设备驱动详解四(使用自属的xbus驱动总线) Linux字符设备驱动详解五(使用platform虚拟平台总线)
前言
本文主要来自正点原子、野火Linux教程及本人理解,若有侵权请及时联系本人删除。
正文
一、DTS设备树基本语法从上到下
背景
硬件设备中种类逐年递增,板级platform平台设备文件越来越多,这么多的设备如果都要自己写platform_device.c文件,那将需要跟每个外设的寄存器打交道,并且需要很多的platform_device.c文件
设备树简介
- DTS(device tree source):设备树源文件,ASCII 格式
- DTC(device tree compiler):设备树编译工具
- DTB(device tree blob):二进制设备树
设备树使用
uboot负责加载到内存,内核解析使用
设备树源文件
ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts
二进制设备树
pc:ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dtb 开发板:/boot/dtbs/4.19.71-imx-r1/imx6ull-seeed-npi.dtb
设备树编译工具
内核编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
手工编译
./scripts/dtc/dtc -I dts -O dtb -o xxx.dtb arch/arm/boot/dts/xxx.dts
./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb
- -I:指定输入格式
- -O:指定输出格式
- -o:指定输出文件
设备树框架
头文件:
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
主体:
/ {
model = "Seeed i.MX6 ULL NPi Board";
compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
aliases {
pwm0 = &pwm1;
pwm1 = &pwm2;
pwm2 = &pwm3;
pwm3 = &pwm4;
};
chosen {
stdout-path = &uart1;
};
memory {
reg = <0x80000000 0x20000000>;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x14000000>;
linux,cma-default;
};
};
...
};
子节点追加内容
&cpu0 {
dc-supply = <®_gpio_dvfs>;
clock-frequency = <800000000>;
};
&clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <786432000>;
};
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
status = "okay";
};
节点命令
基本方法
node-name@unit-address{
属性1 = …
属性2 = …
属性3= …
子节点…
}
- node-name:指定节点的名称
- “unit-address”用于指定“单元地址”
节点标签
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
}
别名子节点
aliases {
二、DTS设备树基本语法从外到内
常见节点属性
compatible属性: 值类型:字符串
intc: interrupt-controller@a01000 {
compatible = "arm,cortex-a7-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xa01000 0x1000>,
<0xa02000 0x100>;
};
- arm:芯片厂商
- cortex-a7-gic:模块对应的驱动名字
model属性: 值类型:字符串
model = "embedfire i.MX6 ULL NPi Board";
status属性: 值类型:字符串 reg属性: 值类型:一系列《地址、长度》对
ocrams: sram@900000 {
compatible = "fsl,lpm-sram";
reg = <0x900000 0x4000>;
};
- 地址:外设寄存器组的起始地址
- 长度:外设寄存器组的字节长度
#address-cells和#size-cells属性: 值类型:u32
soc {
#address-cells = <1>;
#size-cells = <0>;
compatible = "simple-bus";
interrupt-parent = <&gpc>;
ranges;
ocrams: sram@900000 {
compatible = "fsl,lpm-sram";
reg = <0x900000>;
};
};
- #address-cells :设置子节点中reg地址的数量
- #size-cells:设置子节点中reg地址的长度的数量
linux系统中查看设备树
ls /sys/firmware/devicetree/base
或者
ls /proc/device-tree
添加子节点
test_led{
#address-cells = <1>;
#size-cells = <1>;
rgb_led_red@0x0209C000{
compatible = "fire,rgb_led_red";
reg = <0x0209C000 0x00000020>;
status = "okay";
};
};
三、获取DTS属性信息
节点表示
struct device_node {
const char *name;
const char *type;
phandle phandle;
const char *full_name;
struct fwnode_handle fwnode;
struct property *properties;
struct property *deadprops;
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;
#endif
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
查节点
of_find_node_by_path()函数,根据路径找到节点
struct device_node *of_find_node_by_path(struct device_node *from,const char *path);
参数:
- from:开始查找的节点,NULL表示从根节点开始查找
- path:查找的节点名
返回值: 成功:device_node表示的节点 失败:NULL
of_find_node_by_type()函数,根据“device_type“属性来查找节点,不建议使用
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
of_find_node_by_name()函数,根据"name"属性来查找节点,不建议使用
struct device_node *of_find_node_by_name(struct device_node *from,const char *name);
of_find_compatible_node()函数
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
参数:
- from:开始查找的节点,NULL表示从根节点开始查找
- type:指定 device_type 属性值
- compat:指定 compatible 属性值
返回值: 成功:device_node表示的节点 失败:NULL
查节点的属性值
struct property {
char *name;
int length;
void *value;
struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
struct bin_attribute attr;
#endif
};
of_find_property()函数
查找节点中的属性
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
参数:
- np:device_node表示的节点
- name:查找的属性名字
- lenp:属性值的字节数
返回值: 成功:property表示的属性 失败:NULL
案例:
test_property {
test_name = “hello”;
};
name:“hello” lenp = 6 of_property_read_u32()函数,读取一个32位无符号整数
static inline int of_property_read_u32(const struct device_node *np,const char *propname,
参数:
- np:device_node表示的节点
- propname:查找的属性名字
- out_value:属性值的整数值
返回值: 成功:0 失败:负值 of_property_read_u32_array()函数,读取32位无符号整数数组
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_values,size_t sz)
- np:device_node表示的节点
- name:查找的属性名字
- out_value:读取到的数组值
- sz :要读取的数组元素数量
of_property_read_string()函数,读字符串
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)
参数:
- np:device_node表示的节点
- proname:查找的属性名字
- out_string:读取到的字符串值
返回值: 成功:0 失败:负值
代码示例
以野火设备驱动模型代码为例 get_dts_info.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#define DEV_NAME "get_dts_info"
#define DEV_CNT (1)
static dev_t led_devno;
static struct cdev led_chr_dev;
struct class *led_chrdev_class;
struct device_node *led_device_node;
struct device_node *rgb_led_red_device_node;
struct property *rgb_led_red_property;
int size = 0 ;
unsigned int out_values[18];
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
int error_status = -1;
printk("\n open form device \n");
led_device_node = of_find_node_by_path("/test_led");
if(led_device_node == NULL)
{
printk(KERN_ALERT "\n get led_device_node failed ! \n");
return -1;
}
printk(KERN_ALERT "name: %s",led_device_node->name);
printk(KERN_ALERT "child name: %s",led_device_node->child->name);
rgb_led_red_device_node = of_get_next_child(led_device_node,NULL);
if(rgb_led_red_device_node == NULL)
{
printk(KERN_ALERT "\n get rgb_led_red_device_node failed ! \n");
return -1;
}
printk(KERN_ALERT "name: %s",rgb_led_red_device_node->name);
printk(KERN_ALERT "parent name: %s",rgb_led_red_device_node->parent->name);
rgb_led_red_property = of_find_property(rgb_led_red_device_node,"compatible",&size);
if(rgb_led_red_property == NULL)
{
printk(KERN_ALERT "\n get rgb_led_red_property failed ! \n");
return -1;
}
printk(KERN_ALERT "size = : %d",size);
printk(KERN_ALERT "name: %s",rgb_led_red_property->name);
printk(KERN_ALERT "length: %d",rgb_led_red_property->length);
printk(KERN_ALERT "value : %s",(char*)rgb_led_red_property->value);
error_status = of_property_read_u32_array(rgb_led_red_device_node,"reg",out_values, 2);
if(error_status != 0)
{
printk(KERN_ALERT "\n get out_values failed ! \n");
return -1;
}
printk(KERN_ALERT"0x%08X ", out_values[0]);
printk(KERN_ALERT"0x%08X ", out_values[1]);
return 0;
}
static int led_chr_dev_release(struct inode *inode, struct file *filp)
{
printk("\nrelease\n");
return 0;
}
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.release = led_chr_dev_release,
};
static int __init led_chrdev_init(void)
{
int ret = 0;
printk("led chrdev init\n");
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
led_chrdev_class = class_create(THIS_MODULE, "led_chrdev");
cdev_init(&led_chr_dev, &led_chr_dev_fops);
ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
if(ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
device_create(led_chrdev_class, NULL, led_devno, NULL,
DEV_NAME);
return 0;
add_err:
unregister_chrdev_region(led_devno, DEV_CNT);
alloc_err:
return ret;
}
static void __exit led_chrdev_exit(void)
{
printk("chrdev exit\n");
device_destroy(led_chrdev_class, led_devno);
cdev_del(&led_chr_dev);
unregister_chrdev_region(led_devno, DEV_CNT);
class_destroy(led_chrdev_class);
}
module_init(led_chrdev_init);
module_exit(led_chrdev_exit);
MODULE_LICENSE("GPL");
四、 设备树实现RGB灯驱动
设备树添加节点信息
RGB灯的相关寄存器
rgb_led{
#address-cells = <1>;
#size-cells = <1>;
compatible = "fire,rgb_led";
ranges;
rgb_led_red@0x020C406C{
reg = <0x020C406C 0x00000004
0x020E006C 0x00000004
0x020E02F8 0x00000004
0x0209C000 0x00000004
0x0209C004 0x00000004>;
status = "okay";
};
rgb_led_green@0x020C4074{
reg = <0x020C4074 0x00000004
0x020E01E0 0x00000004
0x020E046C 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
rgb_led_blue@0x020C4074{
reg = <0x020C4074 0x00000004
0x020E01DC 0x00000004
0x020E0468 0x00000004
0x020A8000 0x00000004
0x020A8004 0x00000004>;
status = "okay";
};
};
reg属性内存映射
of_iomap()函数将reg属性值的物理地址转化为虚拟地址
void __iomem *of_iomap(struct device_node *np,
int index)
参数:
- np:device_node表示的节点
- index:通常情况下reg属性包含多段,index 用于指定映射那一段,标号从0开始。
代码示例
以野火设备驱动模型代码为例 dts_led.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#define DEV_NAME "rgb_led"
#define DEV_CNT (1)
struct led_resource
{
struct device_node *device_node;
void __iomem *virtual_CCM_CCGR;
void __iomem *virtual_IOMUXC_SW_MUX_CTL_PAD;
void __iomem *virtual_IOMUXC_SW_PAD_CTL_PAD;
void __iomem *virtual_DR;
void __iomem *virtual_GDIR;
};
static dev_t led_devno;
static struct cdev led_chr_dev;
struct class *class_led;
struct device *device;
struct device_node *rgb_led_device_node;
struct led_resource led_red;
struct led_resource led_green;
struct led_resource led_blue;
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
printk("\n open form driver \n");
return 0;
}
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret,error;
unsigned int register_data = 0;
unsigned char receive_data[10];
unsigned int write_data;
if(cnt>10)
cnt =10;
error = copy_from_user(receive_data, buf, cnt);
if (error < 0)
{
return -1;
}
ret = kstrtoint(receive_data, 16, &write_data);
if (ret) {
return -1;
}
if (write_data & 0x04)
{
register_data = ioread32(led_red.virtual_DR);
register_data &= ~(0x01 << 4);
iowrite32(register_data, led_red.virtual_DR);
}
else
{
register_data = ioread32(led_red.virtual_DR);
register_data |= (0x01 << 4);
iowrite32(register_data, led_red.virtual_DR);
}
if (write_data & 0x02)
{
register_data = ioread32(led_green.virtual_DR);
register_data &= ~(0x01 << 20);
iowrite32(register_data, led_green.virtual_DR);
}
else
{
register_data = ioread32(led_green.virtual_DR);
register_data |= (0x01 << 20);
iowrite32(register_data, led_green.virtual_DR);
}
if (write_data & 0x01)
{
register_data = ioread32(led_blue.virtual_DR);
register_data &= ~(0x01 << 19);
iowrite32(register_data, led_blue.virtual_DR);
}
else
{
register_data = ioread32(led_blue.virtual_DR);
register_data |= (0x01 << 19);
iowrite32(register_data, led_blue.virtual_DR);
}
return cnt;
}
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.write = led_chr_dev_write,
};
static int led_probe(struct platform_device *pdv)
{
int ret = -1;
unsigned int register_data = 0;
printk(KERN_ALERT "\t match successed \n");
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if (rgb_led_device_node == NULL)
{
printk(KERN_ERR "\t get rgb_led failed! \n");
return -1;
}
led_red.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_red");
if (led_red.device_node == NULL)
{
printk(KERN_ERR "\n get rgb_led_red_device_node failed ! \n");
return -1;
}
led_red.virtual_CCM_CCGR = of_iomap(led_red.device_node, 0);
led_red.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_red.device_node, 1);
led_red.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_red.device_node, 2);
led_red.virtual_DR = of_iomap(led_red.device_node, 3);
led_red.virtual_GDIR = of_iomap(led_red.device_node, 4);
register_data = ioread32(led_red.virtual_CCM_CCGR);
register_data |= (0x03 << 26);
iowrite32(register_data, led_red.virtual_CCM_CCGR);
register_data = ioread32(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data &= ~(0xf << 0);
register_data |= (0x05 << 0);
iowrite32(register_data, led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data = ioread32(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = (0x10B0);
iowrite32(register_data, led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = ioread32(led_red.virtual_GDIR);
register_data |= (0x01 << 4);
iowrite32(register_data, led_red.virtual_GDIR);
register_data = ioread32(led_red.virtual_DR);
register_data |= (0x01 << 4);
iowrite32(register_data, led_red.virtual_DR);
led_green.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_green");
if (led_green.device_node == NULL)
{
printk(KERN_ERR "\n get rgb_led_green_device_node failed ! \n");
return -1;
}
led_green.virtual_CCM_CCGR = of_iomap(led_green.device_node, 0);
led_green.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_green.device_node, 1);
led_green.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_green.device_node, 2);
led_green.virtual_DR = of_iomap(led_green.device_node, 3);
led_green.virtual_GDIR = of_iomap(led_green.device_node, 4);
register_data = ioread32(led_green.virtual_CCM_CCGR);
register_data |= (0x03 << 12);
iowrite32(register_data, led_green.virtual_CCM_CCGR);
register_data = ioread32(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data &= ~(0xf << 0);
register_data |= (0x05 << 0);
iowrite32(register_data, led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data = ioread32(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = (0x10B0);
iowrite32(register_data, led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = ioread32(led_green.virtual_GDIR);
register_data |= (0x01 << 20);
iowrite32(register_data, led_green.virtual_GDIR);
register_data = ioread32(led_green.virtual_DR);
register_data |= (0x01 << 20);
iowrite32(register_data, led_green.virtual_DR);
led_blue.device_node = of_find_node_by_name(rgb_led_device_node,"rgb_led_blue");
if (led_blue.device_node == NULL)
{
printk(KERN_ERR "\n get rgb_led_blue_device_node failed ! \n");
return -1;
}
led_blue.virtual_CCM_CCGR = of_iomap(led_blue.device_node, 0);
led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD = of_iomap(led_blue.device_node, 1);
led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD = of_iomap(led_blue.device_node, 2);
led_blue.virtual_DR = of_iomap(led_blue.device_node, 3);
led_blue.virtual_GDIR = of_iomap(led_blue.device_node, 4);
register_data = ioread32(led_blue.virtual_CCM_CCGR);
register_data |= (0x03 << 12);
iowrite32(register_data, led_blue.virtual_CCM_CCGR);
register_data = ioread32(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data &= ~(0xf << 0);
register_data |= (0x05 << 0);
iowrite32(register_data, led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
register_data = ioread32(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = (0x10B0);
iowrite32(register_data, led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
register_data = ioread32(led_blue.virtual_GDIR);
register_data |= (0x01 << 19);
iowrite32(register_data, led_blue.virtual_GDIR);
register_data = ioread32(led_blue.virtual_DR);
register_data |= (0x01 << 19);
iowrite32(register_data, led_blue.virtual_DR);
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if (ret < 0)
{
printk("fail to alloc led_devno\n");
goto alloc_err;
}
led_chr_dev.owner = THIS_MODULE;
cdev_init(&led_chr_dev, &led_chr_dev_fops);
ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
if (ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
class_led = class_create(THIS_MODULE, DEV_NAME);
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
add_err:
unregister_chrdev_region(led_devno, DEV_CNT);
printk("\n error! \n");
alloc_err:
return -1;
}
static const struct of_device_id rgb_led[] = {
{.compatible = "fire,rgb_led"},
{}};
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}};
static int __init led_platform_driver_init(void)
{
int DriverState;
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_ALERT "\tDriverState is %d\n", DriverState);
return 0;
}
static void __exit led_platform_driver_exit(void)
{
iounmap(led_green.virtual_CCM_CCGR);
iounmap(led_green.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_green.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_green.virtual_DR);
iounmap(led_green.virtual_GDIR);
iounmap(led_red.virtual_CCM_CCGR);
iounmap(led_red.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_red.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_red.virtual_DR);
iounmap(led_red.virtual_GDIR);
iounmap(led_blue.virtual_CCM_CCGR);
iounmap(led_blue.virtual_IOMUXC_SW_MUX_CTL_PAD);
iounmap(led_blue.virtual_IOMUXC_SW_PAD_CTL_PAD);
iounmap(led_blue.virtual_DR);
iounmap(led_blue.virtual_GDIR);
device_destroy(class_led, led_devno);
class_destroy(class_led);
cdev_del(&led_chr_dev);
unregister_chrdev_region(led_devno, DEV_CNT);
platform_driver_unregister(&led_platform_driver);
printk(KERN_ALERT "led_platform_driver exit!\n");
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
总结
第三点get_dts_info.c仅为获取DTS属性信息演示代码。
|