设备树解析 & i2c设备模型

这篇具有很好参考价值的文章主要介绍了设备树解析 & i2c设备模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、基础概念

1、总线

2、手机启动流程

1、MTK启动流程

2、高通启动流程的差别

3、设备树解析

1、设备树相关

2、设备树解析

4、 i2c 设备初始化流程


1、基础概念

1、总线

总线是连接多个设备或者接入点的数据传输通路。

老的电脑主机的都有PCI插槽类似现在的内存条,可以插声卡、网卡、视频采集卡等,是PC的万用插槽。这些设备与主机的通信就是走的PCI总线,但后来带宽跟不上,被淘汰了。

总线的英文为Bus,公共汽车线路,连接的设备是公交站,传输的数据包就是乘客。每个乘客都要知道自己从哪站上,到哪站下,然后等到站的时候就下去进入另一个设备进行处理。

公交车需要调度室,对应总线那就是控制器。

现阶段的SoC包括ARM,集成了大量的内总线:PCIE、USB、I2C、spi等,这些总线控制器已经集成到芯片内部,通过芯片引脚来连接外围器件。

2、手机启动流程

1、MTK启动流程

参考了:MTK bootloader 启动过程_mtk hypervisor_bobuddy的博客-CSDN博客

如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux

要完全将这张图看懂,还是需要一些功夫的。

首先需要了解的是这几种存储介质的区别:

首先要介绍一下,NOR Flash,ISRAM,NAND Flash,DRAM,我们需要回忆的是:Flash VS RAM,Flash是非易失性存储器,也即掉电数据不丢失,而RAM则相反。

Flash :NOR(或非)  VS  NAND(与非)这两者最大的区别是,CPU可以直接运行NOR上存储的程序,不需要额外的控制电路,而NAND不可以直接运行,并且其是按块进行访问的,比如其中一个典型的是eMMC,一般每块512字节。

RAM: RAM有两大类,一种称为静态RAM(Static RAM/SRAM),SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲。

另一种称为动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。

总结一下,读取速度排名 :SRAM > DRAM ≈ NOR Flash > NAND Flash,还有价格因素,SRAM比DRAM贵很多,NOR也比NAND贵很多。

现在就能理解为什么Boot Code要和LK、Kernel等img分开存放了。也能理解CPU内cache和内存的区别了。

然后还需要了解的是Boot Code、Pre-loader、ATF、LK、Kernel、Ramdisk分别是什么程序,有什么作用。

Boot Code:位于Boot ROM(NOR flash)可直接执行,可片上线性寻址,掉电不消失,这段程序可以初始化NAND,一般采用单线模式(类似串口协议)进行初始化,同时也初始化ISRAM(stack空间),在NAND初始化后将NAND上的Pre-loader加载到ISRAM(Internal SRAM)进行运行。

Pre-loader :可编程BootLoader,初始化硬件,UART,GPIO,DRAM,TIMER,RTC,PMIC 等等,建立起最基本的运行环境,最重要的就是初始化DRAM,加载image到内存,另外还需要创建C程序运行环境。

ATF:(Arm Trust Firmware)就是运行在EL3上的一个monitor。实现了REE(normal world)和TEE(secure world)的切换,在开机引导中负责Trusted OS的引导。ATF 包含了 service router、PSCI、Interrupt Handler 等功能, 该项目是开源的, 相关代码可以参

考 GitHub:GitHub - ARM-software/arm-trusted-firmware: Read-only mirror of Trusted Firmware-A 。arm-trusted-firmware会有几个阶段需要进行,分别叫做 BL1、BL2、BL31、BL32、BL33,这部分就不细说了。

LK:Little Kernel,也是一个开源项目,是一个小型操作系统,继续完成硬件初始化,使能MMU,设置启动模式,根据MAGIC()匹配dtb,加载并引导内核,这里先要知道boot.img 中包括 boot header, kernel,ramdisk,second stage ,device tree,LK需要解析boot.img并加载kernel和dtb。具体参考:https://www.cnblogs.com/ant-man/p/9102055.html

Kernel: 负责程序调度、各种硬件资源的管理,为应用程序提供基本的运行环境。其第一个执行代码为head.S ,第一个C代码为start_kernel。

Ramdisk:安卓根文件系统,ramdisk.img是编译后生成的/out/target/product/“generic”/root目录下经过打包压缩而成的。主要是存放android启动后第一个用户进程init可执行文件和init.*.rc等相关启动脚本以及sbin目录下的adbd工具。

再去看上面的流程图就很明了了。

2、高通启动流程的差别

高通平台的启动流程稍有不同,但是每个阶段做的工作基本是一致的,只是具体实现和叫法不一样。大致来说,都是先PBL(对应boot code)——SBL(对应pre-loader)——UEFI(对应 LK, XBL——ABL)——Kernel。

另外因为硬件不同每种芯片启动流程还有些不一样。参考:高通平台常用缩写:高通平台常用缩写_hlos operation_lalalalala的博客-CSDN博客

UEFI:英特尔开发的项目,针对X86启动的,是C语言开发的,采用了分层思想和,模块化设计。包含了上电时序、驱动实现、网络配置、类shell环境的建立等功能。

高通在MSM8998上引入了UEFI,用来代替LK。高通UEFI由XBL和ABL两部分组成。XBL负责芯片驱动及充电等核心应用功能。ABL包括芯片无关的应用如fastboot。LK的设备驱动都放在了XBL核心,Linux加载启动及fastboot等功能组件则作为独立的UEFI应用存在。

引入UEFI一个显而易见的好处是,初始化硬件部分的代码可以复用,XBL和SBL都能用,另外UEFI的可寻址范围更大,扩展性强,可裁剪性、等优点。

3、设备树解析

1、设备树相关

设备树主要是针对ARM平台的硬件配置而开发设计出来的,在引入设备树之前,关于硬件配置的代码直接写在内核中,后来由于硬件的型号、管脚越来越多,导致内核越来越臃肿,于是需要一个额外的image来负责存储配置硬件信息。

这样也是进一步地将硬件与软件进行解耦了。

直观地说,在内核中看到的dts 、dtsi即为我们编写的设备树文件,而dtb(device tree blob)、dtbo则为编译后的设备树文件,dtb.img、dtbo.img则为将整个系统dtb文件打包后的文件。

如果我们要编写设备树文件,建议参考官方文档:Device Tree Usage - eLinux.org, 进一步了解参考Linux文档:Linux and the Devicetree — The Linux Kernel documentation

一般需要了解的基础内容包括:根节点、属性 、compatible、reg等,具体就不细讲了这方面参考资料很多。

需要补充几点的是:

一个大括号{ } 编译后对应一个设备节点,另外我们看到源码中很多dts文件,很多文件都有根节点,其实有很多是引用,编译器会进行整合,最终编译出来的dtb.img只有一个根节点,整个系统为一个树状结构。

另外需要知道几个设备树相关的文件,可以打开adb shell进行对照查看。         

进入/sys/firmware目录后便可看到二个文件,一个是devicetree文件夹,另一个是fdt(原始dtb文件,可以用hexdump -C fdt 将其打印出来查看就会发现里面的数据和dtb文件是一致的)。

/sys/firmware/devicetree:以目录结构呈现的dtb文件。 根节点对应base目录,每一个节点对应一个目录, 每一个属性对应一个文件

/sys/devices/platform:系统中所有的platform_device, 有来自设备树的, 也有来有.c文件中注册的。对于来自设备树的platform_device, 可以进入 /sys/devices/platform/<设备名>/of_node 查看它的设备树属性(例如进入/sys/devices/platform/led/后若发现该目录下有of_node节点,就表明该platform_device来自设备树)

2、设备树解析

简单了解设备树后,看看设备树在内核中是如何解析的:

如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux

 设备树的解析就是要将dtb中的fdt(扁平设备树)最终解析成platform_device,这里面主要分为两个步骤,第一步是将fdt解析成device_node,第二步是将device_node转换为platform_device,这两步对应上图中start_kernel的两个箭头。调用流程以4.19版的kernel为基准。

首先需要看的是device_node结构体:

struct device_node{

    const char *name;           //节点名称 节点的name属性转换而来

    const char *type;           //节点类型 节点的device_type转换而来 比如 cpu soc memory serial等

    phandle phandle;            //由节点的phandle和"linux,phandle"属性转换而来,自引用的节点标记

    const char *full_name;      // 从“/”开始,表示该node的full path

    struct fwnode_handle fwnode;

    struct property *properties;    //这是一个设备树节点的属性链表,属性可能有很多种,比如:"interrupts","timer","hwmods"等等

    struct property *deadprops;     //如果需要删除某些属性,kernel并非真的删除,而是挂入到deadprops的列表

    struct device_node *parent;     //父节点   组成一颗树

    struct device_node *child;

    struct device_node *sibling;

#if defined(CONFIG_OF_OBJ)

    struct kobject kobj;            //用于在/sys目录下生成相应用户文件

#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

}

将dtb转换成device_node的具体流程如下:需要注意的是下图是arm32位的流程,64位的代码有一些不一样,比如就没有machine_desc相关内容。

如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux

总结一下就是:

1、在setup_machine_fdt内进行了设备树的初次扫描,获取了一些总览信息,验证设备树地址信息,获取cmdline作为启动参数、读取根节点信息等;

2、arm_memlock_init 为设备树的相关内存需求保留内存空间;

3、unflatten_device_tree是真正进行设备树解析的函数,主要填充节点的工作在populate_node内完成。

转换为device_node后,就需要考虑如何转换为platform_device,如何挂载到platform总线上。

这里需要理解一下platform总线,platform总线对应的是在SoC内集成的独立外设控制器和挂接在SoC内存空间的外设,被称为虚拟总线,也就对应之前说的SoC内总线。

首先需要明确的是,不是所有的device_node都会进行转换,所以就有了以下的转换条件:

1.具有compatible属性;2.一般只对根节点的一级子节点进行转换,二级子节点不转换,除非是一级子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus",

这几种是arm的片上总线,挂接在上面的设备也被称为platform_device;

没有被转换为platform_device的节点怎么处理由其父节点的platform_driver来决定。

转换的过程:首先要看下面platform_device结构体的内容

struct platform_device {

    const char  *name;          //设备名称,该名称在注册时,会拷贝到dev.init_name中

    int     id;                 //用于标识设备的ID,在设备与驱动匹配的时候,如果遇到同名的设备,需要用到

    bool        id_auto;

    struct device   dev;        //platform_device 其核心逻辑还是一个device,

    u32     num_resources;      //

    struct resource *resource;  //是主要进行转换的内容,描述设备的资源

    const struct platform_device_id *id_entry;

    char *driver_override; /* Driver name to force a match */

    /* MFD cell pointer */

    struct mfd_cell *mfd_cell;

    /* arch specific additions */

    struct pdev_archdata    archdata;

};

这里需要了解initcall机制:

在start_kernel函数的最后会调用rest_init函数,掉用到do_initcalls会去按顺序加载initcall宏定义的函数。

之后的流程如下(只介绍关键步骤):

如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux

4、 i2c 设备初始化流程

介绍i2c设备注册匹配流程

i2c_client

i2c_adaptor

i2c_driver

介绍相关函数以及使用 如 i2c_master_send,i2c_master_recv,i2c_add_driver,i2c_del_driver,i2c_device_id

attr i2c_transfer  algorithm 等

以及相关

从使用中去理解背后原理。

如何使用设备树注册i2c设备,Linux,驱动开发,c++,linux

platform_driver的注册是通过module_platform_driver宏函数来进行的:

然后通过module_driver来添加注册和注销的函数(采用名字加init,exit的形式),最终还是调用module_init宏进行注册;

#define module_init(x)  __initcall(x)

#define __initcall(fn)  device_initcall(fn)

#define device_initcall(fn)  __define_initcall(fn,6)

这个初始化在arch_initcall之后;

i2c_driver、spi_driver、usb_driver、pci_driver的注册也是走module_driver这个流程,所以说这些驱动地位对等。

针对于i2c节点, i2c控制器, 它会被转换为platform_device, 在内核需要编写对应的platform_driver程序,在实现probe函数的时候,需要调用
i2c_add_numbered_adapter函数,该函数会在i2c总线上注册适配器(adapter),并且将设备树中的i2c子节点转换成i2c_client

Kernel-设备驱动注册 | Rocky_Ansi Blog

设备树的compatible的节点兼容性

Device Tree(三):代码分析文章来源地址https://www.toymoban.com/news/detail-768187.html

到了这里,关于设备树解析 & i2c设备模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux在应用层上使用I2C

    通常情况下i2c读写一般是在kernel中使用,但是在应用层上一样可以使用。在应用上可以通过读写/dev/i2c-x这个节点从而控制i2c接口进行读写数据。 通常一个SOC有多个I2C控制器,假设有这个SOC有3个控制器,我们会在/dev目录下看到i2c-0、i2c-1、i2c-2,计数从0开始。 1.首先使用的时

    2024年02月02日
    浏览(48)
  • alsa音频pcm设备之i2c调试

    i2cdetect 列举 I2C bus i2cdetect -l ls /dev/i2c* 列出I2C bus i2c-7 上面连接的所有设备,并得到i2c设备地址 i2cdetect -y 7 发现i2c设备的位置显示为UU或表示设备地址的数值,UU表示设备在driver中被使用. I2cdump i2c设备大量register的值 i2cdump -y 7 0x40 I2cset设置i2c设备某个register的值 i2cset -y 7 0x40 0x0

    2024年02月08日
    浏览(39)
  • QEMU学习(五):I2C设备仿真及驱动开发

            I2C 是很常用的一个串行通信接口,用于连接各种外设、传感器等器件, 本章我们来学习一下如何在QEMU里仿真I2C设备及 Linux 下开发 I2C 接口器件驱动。 下面是标准的设备添加结构,我们使用的是常见的at_24c系列设备来做I2C的通信,详细代码请看qemuhwnvrameeprom_

    2024年02月08日
    浏览(123)
  • I2C知识大全系列四 —— I2C驱动之Linux下的I2C

    Linux 系统定义了 I2C 驱动体系结构。在 Linux 系统中, I2C 驱动由三部分组成,即 I2C 核心 、 I2C 总线驱动 和 I2C 设备驱动 。这三部分相互协作,形成了非常通用、可适应性很强的 I2C 框架。 I2C核心 I2C 核心提供了 I2C 总线驱动 和 设备驱动 的 注册 、 注销方法 , I2C 通信方法

    2024年02月07日
    浏览(58)
  • <Linux开发> linux开发工具-之-I2C TOOLS工具使用

    <Linux开发> linux开发工具-之-I2C TOOLS工具使用 <Android开发> Android开发工具- 之-I2C TOOLS工具使用 <Linux开发>驱动开发 -之- Linux I2C 驱动 在笔者的另一篇文章 <Android开发> Android开发工具- 之-I2C TOOLS工具使用讲解过,如何在android上使用I2C TOOLS工具。本文主要是分析如何在

    2024年02月16日
    浏览(53)
  • TP驱动——I2C驱动,细节分析——dts设备树

    TP驱动程序,是指带屏幕设备的触屏功能调用的驱动程序。TP外设与主板之间的关系如下框图: 产品常用的屏幕单元为液晶显示屏幕,TP的触摸功能也是一同集成到一起的,通过控制芯片以及外围电路、i2c总线与主板进行通信。 TP驱动的功能逻辑: 当有触点发生时,TP芯片向主

    2024年02月04日
    浏览(43)
  • Linux Kernel 4.19+内核使用GPIO模拟I2C的方法

    1.修改内核配置文件,使内核支持GPIO模拟I2C 2.对应的dts里面增加GPIO模拟I2C的设备树 编译后烧写,会发现/dev下多了一个i2c总线,多出来的那个就是了。

    2024年02月16日
    浏览(51)
  • 【科普贴】I2C接口详解——偏硬件解析

    I2C最早是Philips公司开发的一款简单的双向总线,实现有效的IC控制。把这个总线写成Inter IC 简称IIC或I2C(PS:说白了2就是两个“I”的意思。) I2C总线一些特征: 1、 只有两根线分别是串行数据线(SDA),串行时钟线(SCL)。 2、 每个连接到总线的器件有唯一一个地址。 3、

    2024年02月07日
    浏览(49)
  • Linux I2C 驱动实验

    目录 一、Linux I2C 驱动简介 1、I2C 总线驱动 2、I2C 设备驱动 1、 i2c_client 结构体 2、 i2c_driver 结构体 二、硬件分析 三、设备树编写 1、pinctrl_i2c1 2、在 i2c1 节点追加 ap3216c 子节点 3、验证 四、 代码编写 1、makefile 2、ap3216c.h  3、ap3216c.c ①、头文件 ②、驱动出入口  ③、 i2c驱动

    2024年02月08日
    浏览(54)
  • 【I2C】基于Linux移植i2c-tool工具

    i2c-tool 工具下载地址: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/ 因为我这里需要将i2c-tool移植到imx6ull嵌入式平台,所以编译时肯定需要重新指定GCC。查看i2c-tool根目录下的 Makefile 文件,默认为系统GCC工具编译: 在ubuntu编译i2c-tool源码之前,首先设置imx6ull嵌入式平台的交

    2024年02月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包