设备树

dtb板级信息文件,大势所趋,一定要学

 如何确定要编译哪个DTS文件:查看linux下的arch/arm/boot/dts/Makefile

 DTS语言有属于自己的语法:以树形来描述设备信息

包括外部的灯、按键、传感器,内部的中断控制器,IIC、 SPI、 RAM 、晶振、芯片等

学习文件:imx6ull-alientek-emmc.dts、Devicetree SpecificationV0.2.pdf、Power_ePAPR_APPROVED_v1.12.pdf三个文件

结构:一级子节点,二级子节点,三级子节点。。。

@后面的绝对地址 一般都是外设寄存器的起始地址

/dts-v1/;#include.h
#include .dtsi/{model "Freescale i.MX6 ULL 14x14 EVK Board";compatible "fsl,imx6ull-14x14-evk","fsl,imx6ull";chosen{};memory{//一级子节点};reserved-memory{};backlight{};pxp_v412{};
};

通过查看设备树可以获得例如IIC的频率和地址 才能写IIC设备的驱动文件,还要看mag3110的驱动手册

imx6ull-alientek-emmc.dts中添加自定义节点

可以在 /proc/device-tree/中查看

 特殊设备树aliases:作用 起别名

gpio0 = &gpio1

gpio1 = &gpio2

设备树的属性

compatible 属性
        compatible 属性也叫做“兼容性”属性,这是非常重要的一个属性!compatible 属性的值是
一个字符串列表,compatible 属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要
使用的驱动程序,compatible 属性的值格式如下所示:
"manufacturer,model"
        其中 manufacturer 表示厂商,model 一般是模块对应的驱动名字。
model 属性
        model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字什么的,比
如:
model = "wm8960-audio";

#address-cells #size-cells 属性(影响的是子节点 而不是一级本节点)
        这两个属性的值都是无符号 32 位整形,#address-cells 和#size-cells 这两个属性可以用在任
何拥有子节点的设备中,用于描述子节点的地址信息。
#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),

#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。

#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性

都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:
reg = <address1 length1 address2 length2 address3 length3……>
reg 属性
reg 属性前面已经提到过了,reg 属性的值一般是(address,length)对。reg 属性一般用于描
述设备地址空间资源信息,一般都是某个外设的寄存器地址范围信息,比如在 imx6ull.dtsi 中有
如下内容

ranges 属性
        ranges属性值可以为空或者按照(child-bus-address,parent-bus-address,length)格式编写的数字矩阵,ranges 是一个地址映射/转换表,ranges 属性每个项目由子地址、父地址和地址空间长度
这三部分组成:
        child-bus-address:子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
        parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物 理地址所占用的字长。
        length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。
如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换,

=========================================================================

根节点/下面的compatible属性

Linux 内核会通过根 节点的 compoatible 属性查看是否支持此设备,如果支持的话设备就会启动 Linux 内核
当 Linux 内 核 引 入 设 备 树 以 后 就 不 再 使 用 MACHINE_START 了 , 而 是 换 为 了
DT_MACHINE_START。DT_MACHINE_START 也定义在文件 arch/arm/include/asm/mach/arch.h
里面,定义如下:

绑定信息文档

        设备树是用来描述板子上的设备信息的,不同的设备其信息不同,反映到设备树中就是属性不同。那么我们在设备树中添加一个硬件对应的节点的时候从哪里查阅相关的说明呢?在Linux 内核源码中有详细的.txt 文档描述了如何添加节点,
这些.txt 文档叫做绑定文档,路径为: Linux 源码目录/Documentation/devicetree/bindings,

Linux内核的OF操作函数~(驱动如何获取到设备树中节点信息)

重点在于如何使用of函数 1 获取节点 2 获取节点属性值

        设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,
我们在编写驱动的时候需要获取到这些信息。比如设备树使用 reg 属性描述了某个外设的寄存
器地址为 0X02005482,长度为 0X400,我们在编写驱动的时候需要获取到 reg 属性的

0X02005482 和 0X400 这两个值,然后初始化外设。Linux 内核给我们提供了一系列的函数来获
取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资
料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中
        驱动要想获取到设备树节点内容,首先要找到节点
与查找节点有关的 OF 函数有 5 个,我们依次来看一下。

查找父/子节点的 OF 函数

 

提取属性值的 OF 函数

        节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内
核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,

 

#include <linux/types.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ide.h>#include <linux/init.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/gpio.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of.h>#include <linux/of_address.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名     : dtsled.c作者        : 左忠凯版本     : V1.0描述        : LED驱动文件。其他        : 无论坛       : www.openedv.com日志     : 初版V1.0 2019/7/9 左忠凯创建***************************************************************/#define DTSLED_CNT           1           /* 设备号个数 */#define DTSLED_NAME          "dtsled"  /* 名字 */#define LEDOFF                  0           /* 关灯 */#define LEDON                   1           /* 开灯 *//* 映射后的寄存器虚拟地址指针 */static void __iomem *IMX6U_CCM_CCGR1;static void __iomem *SW_MUX_GPIO1_IO03;static void __iomem *SW_PAD_GPIO1_IO03;static void __iomem *GPIO1_DR;static void __iomem *GPIO1_GDIR;/* dtsled设备结构体 */struct dtsled_dev{dev_t devid;         /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;      /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */};struct dtsled_dev dtsled;  /* led设备 *//** @description        : LED打开/关闭* @param - sta   : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return           : 无*/void led_switch(u8 sta){u32 val = 0;if(sta == LEDON) {val = readl(GPIO1_DR);val &= ~(1 << 3);   writel(val, GPIO1_DR);}else if(sta == LEDOFF) {val = readl(GPIO1_DR);val|= (1 << 3);  writel(val, GPIO1_DR);} }/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/static int led_open(struct inode *inode, struct file *filp){filp->private_data = &dtsled; /* 设置私有数据 */return 0;}/** @description     : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){return 0;}/** @description     : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];       /* 获取状态值 */if(ledstat == LEDON) { led_switch(LEDON);      /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;}/** @description        : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/static int led_release(struct inode *inode, struct file *filp){return 0;}/* 设备操作函数 */static struct file_operations dtsled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release =  led_release,};/** @description : 驱动出口函数* @param       : 无* @return       : 无*/static int __init led_init(void){u32 val = 0;int ret;u32 regdata[14];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点:alphaled of_find_node_by_path函数 */dtsled.nd = of_find_node_by_path("/alphaled");if(dtsled.nd == NULL) { /* 失败  */printk("alphaled node nost find!\r\n");return -EINVAL;} else {printk("alphaled node find!\r\n");}/* 2、获取compatible属性内容 获取节点属性值 */proper = of_find_property(dtsled.nd, "compatible", NULL);if(proper == NULL) {printk("compatible property find failed\r\n");} else {    /* 成功则使用变量承接属性值  */printk("compatible = %s\r\n", (char*)proper->value);}/* 3、获取status属性内容 */ret = of_property_read_string(dtsled.nd, "status", &str);if(ret < 0){printk("status read failed!\r\n");} else {printk("status = %s\r\n",str);}/* 4、获取reg属性内容 */ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);if(ret < 0) {printk("reg property read failed!\r\n");} else {u8 i = 0;printk("reg data:\r\n");for(i = 0; i < 10; i++)printk("%#X ", regdata[i]);printk("\r\n");}/* 初始化LED */#if 0/* 1、寄存器地址映射 *//* 所有的寄存器信息被取值以后都保存再regdata[]数组中,所以可以操作数据取值  */IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);GPIO1_DR = ioremap(regdata[6], regdata[7]);GPIO1_GDIR = ioremap(regdata[8], regdata[9]);#else/* 这里就是与第三个实验不一样的地方,他不是自己写在.c里的,而是从设备树里取值*//* 可以使用of_iomap直接完成 设备节点 到 虚拟地址的内存映射,而不需要数据取值但一定要注意是 of_iomap 获得的是reg属性的值  */IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);GPIO1_DR = of_iomap(dtsled.nd, 3);GPIO1_GDIR = of_iomap(dtsled.nd, 4);#endif/* 2、使能GPIO1时钟 */val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26);    /* 清楚以前的设置 */val |= (3 << 26);   /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 3、设置GPIO1_IO03的复用功能,将其复用为*    GPIO1_IO03,最后设置IO属性。*/writel(5, SW_MUX_GPIO1_IO03);/*寄存器SW_PAD_GPIO1_IO03设置IO属性*bit 16:0 HYS关闭*bit [15:14]: 00 默认下拉*bit [13]: 0 kepper功能*bit [12]: 1 pull/keeper使能*bit [11]: 0 关闭开路输出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 110 R0/6驱动能力*bit [0]: 0 低转换率*/writel(0x10B0, SW_PAD_GPIO1_IO03);/* 4、设置GPIO1_IO03为输出功能 */val = readl(GPIO1_GDIR);val &= ~(1 << 3);    /* 清除以前的设置 */val |= (1 << 3);    /* 设置为输出 */writel(val, GPIO1_GDIR);/* 5、默认关闭LED */val = readl(GPIO1_DR);val |= (1 << 3);    writel(val, GPIO1_DR);/* 注册新版字符设备驱动 一共分两步 注册设备号 和 添加设备节点*//* 1、创建设备号 */if (dtsled.major) {      /*  定义了设备号 */dtsled.devid = MKDEV(dtsled.major, 0);register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);} else {                      /* 没有定义设备号 */alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);    /* 申请设备号 */dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 查看作用 */dtsled.minor = MINOR(dtsled.devid);   /* 获取分配号的次设备号 查看作用*/}printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor); /* 2、新的字符设备号注册方法初始化cdev在linux中使用cdev结构体表示一个字符设备,include/linux/cdev.h */dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);/* 3、添加一个cdev */cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);//* 通过mdev来实现设备文件节点的自动创建与删除 *//* 自动创建设备节点 *//* 4、创建类 */dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {return PTR_ERR(dtsled.class);}/* 5、创建设备 */dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {return PTR_ERR(dtsled.device);}return 0;}/** @description : 驱动出口函数* @param       : 无* @return       : 无*/static void __exit led_exit(void){/* 取消映射 */iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/* 注销字符设备驱动 */cdev_del(&dtsled.cdev);/*  删除cdev */unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 *////*摧毁创建的类创建时先创建类再创建设备摧毁时先摧毁设备再摧毁类*/device_destroy(dtsled.class, dtsled.devid);class_destroy(dtsled.class);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("zuozhongkai");
#include "stdio.h"#include "unistd.h"#include "sys/types.h"#include "sys/stat.h"#include "fcntl.h"#include "stdlib.h"#include "string.h"/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名     : ledApp.c作者        : 左忠凯版本     : V1.0描述        : chrdevbase驱测试APP。其他       : 无使用方法  :./ledtest /dev/led  0 关闭LED./ledtest /dev/led  1 打开LED     论坛      : www.openedv.com日志     : 初版V1.0 2019/1/30 左忠凯创建***************************************************************/#define LEDOFF  0#define LEDON  1/** @description      : main主程序* @param - argc   : argv数组元素个数* @param - argv    : 具体参数* @return            : 0 成功;其他 失败*/int main(int argc, char *argv[]){int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);    /* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;}

linux驱动开发学习2 设备树相关推荐

  1. Linux驱动开发中与设备树相关的6种debug方法

    整理出了6种驱动开发时与设备注册.设备树相关的调试方法,彼此间没有优先级之分,每种方法不一定是最优解,但可以作为一种debug查找问题的手段,快速定位问题原因.例如在芯片验证时,不同时钟频率下系统启动 ...

  2. Linux驱动开发:字符设备驱动开发实战

    Linux驱动开发:字符设备驱动开发实战 一.工程创建 VSCode 创建工程,设置 C/C++ 配置,导入 linux kernel 源码目录,方便 vscode 写代码自动补全,vscode 配置 ...

  3. 驱动开发基础知识——设备树

    BSP开发工程师[原来BSP就是那些被指臃肿的文件啊 BSP的出生 Linux经过不断的发展,原先嵌入式系统的三层结构逐步演化成为一种四层结构. 这个新增加的中间层次位于操作系统和硬件之间,包含了系统 ...

  4. 一、linux驱动开发-8.2-设备树下platform驱动

    一.简介 之前没用设备树的时候,是分别编写并注册platform_device和platform_driver来表示设备和驱动,如果使用设备树,设备的描述就放在设备树中,因此只需要实现platform ...

  5. Linux驱动开发学习笔记【12】:Linux自带LED灯驱动

    目录 一.内核自带LED驱动使能 二.内核自带LED驱动分析 三.内核自带LED驱动使用 一.内核自带LED驱动使能 在Linux内核中,已经自带了LED灯的驱动程序,使用的就是platform平台驱 ...

  6. Linux驱动开发 15 块设备驱动框架

    CAN         I.MX6ULL 带有两个 CAN 控制器: FlexCAN1 和 FlexCAN2 , NXP 官方的 EVK 开发板这两个 CAN 接口都用到了,因此 NXP 官方的设备树 ...

  7. 一、linux驱动开发-8.1-platform设备驱动

    一.驱动的分离与分层 1.1.驱动的分隔与分离 通过驱动的分隔,也就是将主机驱动和设备驱动分隔开来,通过总线就行匹配,当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配 的 ...

  8. Linux驱动开发学习笔记【8】:Linux中断系统

    目录 一.Linux内核中断处理过程 1.1.裸机中断 1.2.linux中断 二.linux中断的上半部和下半部 2.1 软中断 2.2 tasklet 2.3 工作队列 2.4 中断线程化 三.设 ...

  9. linux驱动私有数据,linux驱动开发之字符设备--私有数据和container_of

    前言 驱动开发中通常为设备定义一个设备相关的设备结构体,其包含该设备的cdev .私有数据.信号量.irq等这些信息. 驱动开发中通常将文件的私有数据private_data指向设备结构体,在read ...

最新文章

  1. 推荐8个值得每天一看的网站,值得收藏起来!
  2. CCF201403-1 相反数(100分)
  3. Linux下部署开源版“禅道”项目管理系统
  4. PM——【1】维护功能位置主数据
  5. 慎用AXIS2(续)
  6. 音频处理十:( mp3 与wav 格式的相互转换)
  7. 工作184:自定义事件
  8. 获取客户端的IP地址
  9. python distance matrix_Python 矩阵转置的几种方法小结
  10. SCRUM的五个事件
  11. STM32标准库与HAL库中的Mode和Pull设置
  12. matlab画图不显示中文_[过时] [LaTeX 使用] 升级 macOS 10.15 后 ctex 文档不显示中文的临时方案...
  13. 【DDD】领域驱动设计实践 —— Application层实现
  14. Scratch编程与美术:制作美丽的烟花!
  15. 微信扫描二维码快速登录网站
  16. 成都榆熙:做拼多多电商如何优化用户消费体验?
  17. Algorithm保姆级笔记(基础+提高+Top1+杂题+Top2+蓝桥杯)
  18. 进军数字货币新业务,500.com能否“涅槃”重生?
  19. set_xscale 表示x轴缩放比例,一张图明明白白
  20. 计算机视觉大型攻略 —— SLAM(2) Graph-based SLAM(基于图优化的算法)

热门文章

  1. Flutter Icons 图标库 国内镜像
  2. 用scoop代替chocolatey做Windows包管理器
  3. PyQt5 如何改变各控件的叠置顺序(有遮盖情况)
  4. html点击阅读全文,【css】html+css给文章页,做阅读全文
  5. 设计中的手写识别(输入法)思路 -- 上篇
  6. 复选框如何实现单选框效果
  7. 在一页纸上打印8页PPT讲义的方法
  8. clickhouse 数据TTL使用
  9. hdfs删文件夹报错
  10. 成都java薪资待遇怎么样?还值得学习吗?