Linux驱动开发-设备树

ops/2025/3/14 8:03:40/

Linux驱动开发-设备树

  • 一,设备树简介
    • 1. 设备树
    • 2. DTS、DTB和DTC
  • 二,DTS语法
    • 1.设备节点
      • 1.1 字符串型
      • 1.2 32位无符号整数
      • 1.3 字符串列表
    • 2.设备属性
      • 2.1 兼容性 compatible
      • 2.2 model
      • 2.3 status
      • 2.4 reg
      • 2.5 #address-cells 和#size-cells 属性
  • 三,设备树常用的OF操作函数
    • 1.操作函数
    • 2.将led寄存器信息放到设备树中
      • 2.1创建led节点

一,设备树简介

1. 设备树

  设备树:设备按照树这种结构,来描述板子上的设备信息的文件,目的是和linux内核分离开,用专属的文件格式来描述,这个文件称为设备树(.dts)。主干是系统总线,分支有GPIO控制器,SPI控制器,IIC控制器等,然后再分支,比如IIC控制器又分为IIC1和IIC2,然后继续分支,IIC1上搭载具体的设备。
在这里插入图片描述

2. DTS、DTB和DTC

  dts:设备树源码文件,里面显示的是设备之间的关系网,从大类继续往下慢慢细分,最终到具体设备。
  dtb:将dts文件编译后得到的二进制文件,类似于将.c编译成.o文件这种。
  dtsi:类似于c语言中的头文件,由下面的dts代码中 #include <imx6ull.dtsi>,这个dtsi文件描述的是内部的外设信息(CPU架构,主频,外设寄存器的地址范围),相当于把芯片内部的共有属性都提取到一个文件夹中,然后对于不同的板子再有不同dts文件(芯片和板子不一样,针对这个alpha开发板,芯片就是这个内部小‘板子’,板子是整个带有网口等等设备的大板子)。
  dtc:编译所需要的工具。

二,DTS语法

1.设备节点

  每个设备都是一个节点,称为设备节点,比如代码14行的 / 称为根节点,imx6ull.dtsi和imx6ull-alientek-emc.dts都有根节点,上面也说明了这俩文件的关系,因此相当于俩根节点内容合并成一个根节点。

1.1 字符串型

 model = "Freescale i.MX6 ULL 14x14 EVK Board";

1.2 32位无符号整数

 reg = <0x80000000 0x20000000>;

1.3 字符串列表

compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

2.设备属性

2.1 兼容性 compatible

  格式:“厂商,模块对应的驱动的名字”,如果这个兼容性设置有两个,比如compatible = “fsl,imx6ul-evk-wm8960”,“fsl,imx-audio-wm8960”;会先去使用第一个兼容值在linux内核找查找,看看能不能找到对应的驱动文件,找不到就用第二个值去查找。

2.2 model

  字符串,用于描述设置模块信息,比如名字,model = “wm8960-audio”。

2.3 status

  字符串,设备的状态信息,一般有okay和disabled比较常用。

2.4 reg

  reg用于描述这个设备地址空间信息。

2.5 #address-cells 和#size-cells 属性

  无符号32位整形,用于任何拥有子节点的设备中,描述子节点的地址信息。对于节点的reg属性:reg = <address1 length1 address2 length2 address3 length3……>,其中 address 是起始地址,length 是地址长度,#address-cells 表明 address 这个数据所占用的字长,#size-cells 表明 length 这个数据所占用的字长。字长意思就是有几个
比如父节点设置 #address-cells = <1>,#size-cells<1>,那么子节点的address1 为1,length1 为1,一一对应的关系。

  在I2C1中添加自己的设备wwwyyyyyttt,247-251行代码中,添加完成后,在板子上通过串口查看:在这里插入图片描述

/*dts文件*/
/*2  * Copyright (C) 2016 Freescale Semiconductor, Inc.3  *4  * This program is free software; you can redistribute it and/or modify5  * it under the terms of the GNU General Public License version 2 as6  * published by the Free Software Foundation.7  */8 9 /dts-v1/;10 11 #include <dt-bindings/input/input.h>12 #include "imx6ull.dtsi"13 14 / {15     model = "Freescale i.MX6 ULL 14x14 EVK Board";16     compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";17 18     chosen {19         stdout-path = &uart1;20     };21 22     memory {23         reg = <0x80000000 0x20000000>;24     };25 26     reserved-memory {27         #address-cells = <1>;28         #size-cells = <1>;29         ranges;30 31         linux,cma {32             compatible = "shared-dma-pool";33             reusable;34             size = <0x14000000>;35             linux,cma-default;36         };37     };38 39     backlight {40         compatible = "pwm-backlight";41         pwms = <&pwm1 0 5000000>;42         brightness-levels = <0 4 8 16 32 64 128 255>;43         default-brightness-level = <6>;44         status = "okay";45     };46 47     pxp_v4l2 {
......50     };regulators {
......86     };87 88     sound {
......
123     };
124 
125     spi4 {
......
148 };228 &i2c1 {
229     clock-frequency = <100000>;
230     pinctrl-names = "default";
231     pinctrl-0 = <&pinctrl_i2c1>;
232     status = "okay";
233 
234     mag3110@0e {
235         compatible = "fsl,mag3110";
236         reg = <0x0e>;
237         position = <2>;
238     };
239 
240     fxls8471@1e {
241         compatible = "fsl,fxls8471";
242         reg = <0x1e>;
243         position = <0>;
244         interrupt-parent = <&gpio5>;
245         interrupts = <0 8>;
246     };
247     wwwyyyyyttt@3e{
248         compatiable = "fsl,wwwyyyyyttt";
249         reg = <0x3e>;
250         position = <0>;
251     };
252 };

在这里插入图片描述  图片中显示的驱动,和dtsi中的内容也有不同,dtsi中的驱动更多,但是在板子运行后少了,因为有的找不到,因此在板子运行时就不显示了。比如这个caam@2140000。追加设备在dts文件中,一般不要在dtsi文件。

	/*这段是dtsi的代码*/aips2: aips-bus@02100000 {compatible = "fsl,aips-bus", "simple-bus";#address-cells = <1>;#size-cells = <1>;reg = <0x02100000 0x100000>;ranges;crypto: caam@2140000 {....};};usbotg1: usb@02184000 {.....};usbotg2: usb@02184200 {.....};usbmisc: usbmisc@02184800 {.....};fec1: ethernet@02188000 {.....};sim1: sim@0218c000 {.....};usdhc1: usdhc@02190000 {compatible = "fsl,imx6ul-usdhc", "fsl,imx6sx-usdhc";reg = <0x02190000 0x4000>;interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_USDHC1>,<&clks IMX6UL_CLK_USDHC1>,<&clks IMX6UL_CLK_USDHC1>;clock-names = "ipg", "ahb", "per";bus-width = <4>;status = "disabled";};usdhc2: usdhc@02194000 {compatible = "fsl,imx6ul-usdhc", "fsl,imx6sx-usdhc";reg = <0x02194000 0x4000>;interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_USDHC2>,<&clks IMX6UL_CLK_USDHC2>,<&clks IMX6UL_CLK_USDHC2>;clock-names = "ipg", "ahb", "per";bus-width = <4>;status = "disabled";};adc1: adc@02198000 {compatible = "fsl,imx6ul-adc", "fsl,vf610-adc";reg = <0x02198000 0x4000>;interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_ADC1>;num-channels = <2>;clock-names = "adc";status = "disabled";};/*在dts中添加在i2c1中的设备wwwyyyyyyttt*/i2c1: i2c@021a0000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";reg = <0x021a0000 0x4000>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_I2C1>;status = "disabled";};.....
};

三,设备树常用的OF操作函数

1.操作函数

  设备树就相当于一个文件,内核想要获取设备树中设备信息,因此要借助内核中的函数即OF函数,设备树中,设备类似按节点的方式放到设备树上,因此内核要获取设备树上设备信息要先获取这个设备节点,然后利用相关OF函数获取其他的属性。
整体实现:

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  // 包含 register_chrdev_region 的定义
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>static int __init dtsof_init(void)
{int ret = 0,i=0;struct device_node *bl_nd = NULL;/*设置返回值*/struct property  *bl_property = NULL;//字符串类型const char * str;u32 shu_value;//数值类型u32 kkkmmmmm[8]={0};u32 num;/*1.找节点,路径/backlight*/bl_nd = of_find_node_by_path("/backlight");if(bl_nd == NULL){printk("CCCCC\r\n");ret = -EINVAL;goto fail_find;}/*2.获取属性*/bl_property = of_find_property(bl_nd,"compatible",NULL);if(bl_property == NULL){ret = -EINVAL;goto fail_find;        }else printk("compatible = %s\r\n",(char*)bl_property->value);ret = of_property_read_string(bl_nd,"status",&str);if(ret < 0){goto fail_rs;}else printk("status = %s\r\n",str);/*3.读取数类型*/ret = of_property_read_u32(bl_nd,"default-brightness-level",&shu_value);if(ret<0){goto fail_shu;}else printk("u32 default-brightness-level =%d\r\n",shu_value);/*4.获取数组类型*/num = of_property_count_elems_of_size(bl_nd,"brightness-levels", sizeof(u32));if(ret<0){printk("NO!\r\n");goto fail_duoshu;}else printk("brightness-levels is  = %d 个\r\n",num);// /*申请内存*/// kkkmmmmm = kmalloc(num*sizeof(u32),GFP_KERNEL);// if(!kkkmmmmm)// {//     printk("malloc memory error \r\n");//     ret = -EINVAL;//     goto fail_memory;// }/*获取数组*/ret = of_property_read_u32_array(bl_nd,"brightness-levels",kkkmmmmm,num);if(ret<0){goto fail_11;}else{for(i=0;i<num;i++){printk("brightness-levels[%d] =%d\r\n",i,kkkmmmmm[i]);}}// kfree(kkkmmmmm);return 0;fail_11://fail_memory:fail_duoshu:fail_shu:fail_rs:fail_find:return ret;}static void __exit dtsof_exit(void)
{}/*模块入口出口*/
module_init(dtsof_init);
module_exit(dtsof_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");

  这段利用的是数组kkkmmmmm,如果利用指针的话要先在内核中用kmalloc函数开辟内存空间,不然会有问题。因为用数组的话,相当于在内存中地址已经给出了,of_property_read_u32_array(const struct device_node *np,const char *propname,
u32 *out_values, size_t sz)放得到的out_values数据就行。如果用指针类型,u32 *kkkmmmmm,要用kmalloc函数,当 kmalloc 函数成功执行时,它会返回一个指向所分配内存块起始地址的指针,然后这个指针会被赋值给 kkkmmmmm。这意味着 kkkmmmmm 现在指向了一个确定的、新分配的内存地址。然后将得到的数据out_values放到这个内存中。不利用kmalloc函数的话,得到的数据无法用这个指针指向。

2.将led寄存器信息放到设备树中

2.1创建led节点

  位置暂且放在根目录下:
在这里插入图片描述### 2.2 函数实现

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  // 包含 register_chrdev_region 的定义
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>#define DEVICE_NAME "dtsofled"
// /*相关寄存器对应地址*/
// #define CCM_CCGR1_BASE          (0X020C406C)
// #define SW_MUX_GPIO1_IO03_BASE   (0X020E0068)
// #define SW_PAD_GPIO1_IO03_BASE   (0X020E02F4)
// #define GPIO1_DR_BASE            (0X0209C000)
// #define GPIO1_GDIR_BASE          (0X0209C004)/*虚拟地址指针*/
static void __iomem  *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;/*注册和创建节点用第结构体*/
struct dtsled_dev{struct  class *class;//创建类struct device *device;//创建节点  struct cdev dtsofled_cdev;//字符设备dev_t device_hao;//设备号        int major;//主设备号int minor;//次设备号};
struct dtsled_dev dtsled;static void led_chioce(unsigned char on_and_off)
{static int register_led = 0;if(on_and_off == 0)//开{register_led = readl(GPIO1_DR); register_led &=~(1<<3);writel(register_led,GPIO1_DR);         }else {          //关 register_led = readl(GPIO1_DR); register_led |=(1<<3);writel(register_led,GPIO1_DR);}
}
static int dtsofled_open(struct inode *inode, struct file *file)
{return 0;}
static int dtsofled_close(struct inode *inode, struct file *file)
{return 0;
}
static ssize_t dtsofled_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{return 0;
}
static ssize_t dtsofled_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{unsigned char writebuff[1] = {0};int ret = 0;ret = __copy_from_user(writebuff,buf,count);led_chioce(writebuff[0]);return 0;
}
const struct  file_operations dtsofled_fops = {.owner = THIS_MODULE,.read = dtsofled_read,.write = dtsofled_write,.open = dtsofled_open,.release = dtsofled_close,
};
static int __init dtsofled_init(void)
{int ret=0,num=0,i=0;int register_result = 0;struct device_node *led_nd = NULL;//节点struct property *led_property =NULL;//属性u32 led_reg[10]={0};//reg/*查询设备树信息*//*找节点*/led_nd = of_find_node_by_path("/alphaled");printk("OK node \r\n");/*获取属性*/led_property = of_find_property(led_nd,"status",NULL);printk("led compatible = %s\r\n",(char*)led_property->value);/*获取reg值*/num = of_property_count_elems_of_size(led_nd,"reg",sizeof(u32));//reg里面几个值of_property_read_u32_array(led_nd,"reg",led_reg,num);for(i=0;i<10;i++){printk("led reg[%d] = %x\r\n",i,led_reg[i]);  }/*寄存器第地址映射*//*1.将物理地址*_BASE和虚拟地址联系起来*/CCM_CCGR1 = ioremap(led_reg[0],led_reg[1]);//右边是实际物理地址,左边是虚拟地址SW_MUX_GPIO1_IO03 = ioremap(led_reg[2],led_reg[3]);SW_PAD_GPIO1_IO03 = ioremap(led_reg[4],led_reg[5]);GPIO1_DR = ioremap(led_reg[6],led_reg[7]);GPIO1_GDIR = ioremap(led_reg[8],led_reg[9]);/*2.初始化*//*2.1 时钟初始化*/register_result = readl(CCM_CCGR1);register_result |=(3<<26);writel(register_result,CCM_CCGR1);/*2.2复用初始化*/writel(5,SW_MUX_GPIO1_IO03);/*2.3电器属性初始化*/writel(0x10b0,SW_PAD_GPIO1_IO03);/*2.4设置为输出模式*/register_result = readl(GPIO1_GDIR);   register_result |= (1<<3);writel(register_result,GPIO1_GDIR);/*2.5控制亮*/register_result = readl(GPIO1_DR); register_result &=~(1<<3);writel(register_result,GPIO1_DR); /*注册字符设备  设备号 注册函数 *//*1.设备号,如果设置了设备号,还要用杂感注册函数,主要就是为了让内核承认这个设号*/if(dtsled.major)//如果设置了主设备号{dtsled.device_hao = MKDEV(dtsled.major,0);ret = register_chrdev_region(dtsled.device_hao, 1,"dtsofled");}else {ret = alloc_chrdev_region(&dtsled.device_hao, 0, 1, "dtsofled");}printk("major = %d,minor = %d\r\n",MAJOR(dtsled.device_hao),MINOR(dtsled.device_hao));/*2.注册函数*/dtsled.dtsofled_cdev.owner = THIS_MODULE;cdev_init(&dtsled.dtsofled_cdev,&dtsofled_fops);//将字符设备和注册函数关联其来cdev_add(&dtsled.dtsofled_cdev,dtsled.device_hao,1);//添加到内核中/*3.设备节点,即主动在/dev/下创建这个节点*/dtsled.class = class_create(THIS_MODULE,DEVICE_NAME);//类创建dtsled.device = device_create(dtsled.class,NULL,dtsled.device_hao,NULL,DEVICE_NAME);return 0;
}
static void __exit dtsofled_exit(void)
{int register_result = 0;/*控制灭*/register_result = readl(GPIO1_DR); register_result |=(1<<3);writel(register_result,GPIO1_DR); /*1.取消虚拟地址映射*/iounmap(CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);printk("exit in linux\r\n");//注销cdev_del(&dtsled.dtsofled_cdev);//先解除和注册函数的关系unregister_chrdev_region(dtsled.device_hao,1);//解除和设备号的关系device_destroy(dtsled.class,dtsled.device_hao);//摧毁设备class_destroy(dtsled.class);//摧毁类
}/*注册驱动和卸载驱动*/
module_init(dtsofled_init);
module_exit(dtsofled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");

http://www.ppmy.cn/ops/165260.html

相关文章

linux学习(五)(服务器审查,正常运行时间负载,身份验证日志,正在运行的服务,评估可用内存)

服务器审查 在 Linux 中审查服务器的过程包括评估服务器的性能、安全性和配置&#xff0c;以确定需要改进的领域或任何潜在问题。审查的范围可以包括检查安全增强功能、检查日志文件、审查用户帐户、分析服务器的网络配置以及检查其软件版本。 Linux 以其稳定性和安全性而闻名…

Qt状态更新设计

在动平衡仪的使用过程中&#xff0c;配置参数的动态切换&#xff08;如切换传感器通道、切换单位&#xff09;需要实时反映在界面显示和计算方法上。为了实现这种动态响应&#xff0c;可以采用 观察者模式&#xff08;Observer Pattern&#xff09; 和 事件驱动架构&#xff08…

The Rust Programming Language 学习 (四)

结构体 struct&#xff0c;或者 structure&#xff0c;是一个自定义数据类型&#xff0c;允许你命名和包装多个相关的值&#xff0c;从而形成一个有意义的组合。如果你熟悉一门面向对象语言&#xff0c;struct 就像对象中的数据属性。 定义并实例化结构体 和元组一样&#x…

hive 中各种参数

在 Apache Hive 中&#xff0c;参数&#xff08;也称为配置属性&#xff09;用于控制 Hive 的行为和性能。这些参数可以优化查询性能、调整资源使用、解决数据倾斜问题等。Hive 参数通常分为以下几类&#xff1a; 1. 查询执行参数 这些参数用于控制查询的执行方式和性能。 1.…

死锁的产生以及如何避免

死锁的产生与避免 一、死锁的产生原因二、典型死锁场景示例三、死锁的检测与诊断1. 使用工具检测死锁2. 日志分析 四、死锁的避免策略1. 破坏“请求与保持”条件2. 破坏“不可剥夺”条件3. 破坏“循环等待”条件4. 使用超时机制5. 减少锁的粒度 五、最佳实践总结六、总结 一、死…

20250311-vue-Props1

Props 声明 一个组件需要显式声明它所接受的 props&#xff0c;这样 Vue 才能知道外部传入的哪些是 props&#xff0c;哪些是透传 attribute。 props 需要使用 props 选项来定义&#xff1a; export default {props: [foo],created() {// props 会暴露到 this 上console.log…

C++设计模式-观察者模式:从基本介绍,内部原理、应用场景、使用方法,常见问题和解决方案进行深度解析

一、基本介绍 1.1 模式定义与核心思想 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了对象间一对多的依赖关系。当被观察对象&#xff08;Subject&#xff09;状态改变时&#xff0c;所有依赖它的观察者&#xff08;Observer&…

DeepSeek搭配Excel,制作自定义按钮,实现办公自动化!

今天跟大家分享下我们如何将DeepSeek生成的VBA代码&#xff0c;做成按钮&#xff0c;将其永久保存在我们的Excel表格中&#xff0c;下次遇到类似的问题&#xff0c;直接在Excel中点击按钮&#xff0c;就能10秒搞定&#xff0c;操作也非常的简单. 一、代码准备 代码可以直接询问…