前言
Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF)。在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台、不同硬件,往往存在着大量的不同的、移植性差的板级描述代码,以达到对这些不同平台和不同硬件特殊适配的需求。但是过多的平台、过的的不同硬件导致了这样的代码越来越多,最终引发了Linux创始人Linus的不满,以及强烈呼吁改变。Device Tree的引入给驱动适配带来了很大的方便,一套完整的Device Tree可以将一个PCB摆在你眼前。Device Tree可以描述CPU,可以描述时钟、中断控制器、IO控制器、SPI总线控制器、I2C控制器、存储设备等任何现有驱动单位。对具体器件能够描述到使用哪个中断,内存映射空间是多少等等。

关于Device Tree的数据结构和详细使用方法,请大家查看宋宝华老师的一篇博客:

http://blog.csdn.net/airk000/article/details/2

1 基于Device Tree机制内核的驱动开发—实例讲解
这个章节,作者来讲讲基于Linux-3.2.X之后使用device tree机制的内核的驱动开发案例。本文的驱动开发案例是作者工作期间亲自写的键盘驱动代码。CPU平台使用的是NXP(freescale)的i.MX6ul。概要信息描述如下:

硬件平台:NXP(freescale)—i.MX6ul

软件开发平台:Ubuntu-12.04

内核版本:Linux-3.14.38

编译环境:yocto project

1.1 基于Device Tree机制的驱动开发—系统如何加载和解析dtb文件
基于Device Tree机制的驱动开发,在驱动当中所使用到的硬件资源都在对应的CPU平台的dts文件上进行配置,然后编译生成dtb文件,放在u-boot分区之后,内核分区之前。这里顺便讲一下,内核是如何解析dtb文件的。其大致过程如下:

系统上电启动之后,u-boot加载dtb,通过u-boot和Linux内核之间的传参操作将dtb文件传给内核,然后内核解析dtb文件,根据device tree中的配置(dtb文件)去初始化设备的CPU管脚、各个外设的状态。device tree中的配置主要是起到了初始化硬件资源的作用,后期可以在驱动中修改设备的硬件资源的状态,比如在device tree中初始化某个GPIO的管脚为上拉状态,可以在驱动加载之后修改这个管脚的状态。

1.2 基于Device Tree机制的驱动开发—dts文件的配置和编译
本节开始以具体的驱动例子讲解如何在驱动开发中配置dts文件。这里使用i.MX6ul平台下的矩阵键盘驱动中使用到的几个GPIO口讲解如何配置dts文件和编译。本次讲解案例用于编译驱动的内核是Linux-3.14.38。首先我们先来看看如何在内核中找到自己相应CPU平台的dts文件:

1.dts文件位于内核的arch/arm/boot/dts/$(board).dts,其中的$(board)指的是对应的CPU平台,比如i.MX6ul平台的dts文件如下:

 imx6ul/linux-3.14.-v2$ vim arch/arm/boot/dts/imx6ul-14x14-evk.dts(部分内容)

 #include <dt-bindings/input/input.h>
#include "imx6ul.dtsi" / {
model = "Freescale i.MX6 UltraLite NewLand Board";
compatible = "fsl,imx6ul-14x14-evk", "fsl,imx6ul"; chosen {
stdout-path = &uart1;
}; memory {
reg = <0x80000000 0x20000000>;
}; pxp_v4l2 {
compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
status = "okay";
}; keyboard {
compatible = "max-keypad";
pinctrl-names = "default";
pinctrl- = <&pinctrl_keypad>;
in-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //key_in0
<&gpio2 GPIO_ACTIVE_HIGH>, //key_in1
<&gpio2 GPIO_ACTIVE_HIGH>; //key_in2 out-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //key_out0
<&gpio2 GPIO_ACTIVE_HIGH>, //key_out1
<&gpio2 GPIO_ACTIVE_HIGH>, //key_out2
<&gpio4 GPIO_ACTIVE_HIGH>, //key_out3
<&gpio4 GPIO_ACTIVE_HIGH>; //key_out4
status = "okay";
};
}; &cpu0 {
arm-supply = <&reg_arm>;
soc-supply = <&reg_soc>;
}; &clks {
assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
assigned-clock-rates = <>;
}; &tsc {
pinctrl-names = "default";
pinctrl- = <&pinctrl_tsc>;
status = "okay";
xnur-gpio = <&gpio1 >;
measure_delay_time = <0xffff>;
pre_charge_time = <0xfff>;
}; &gpmi {
pinctrl-names = "default";
pinctrl- = <&pinctrl_gpmi_nand_1>;
status = "okay";
nand-on-flash-bbt;
}; &lcdif {
pinctrl-names = "default";
pinctrl- = <&pinctrl_lcdif_dat
&pinctrl_lcdif_ctrl>;
lcd_reset = <&gpio3 GPIO_ACTIVE_HIGH>;
display = <&display0>;
status = "okay"; display0: display {
bits-per-pixel = <>;
bus-width = <>; display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <>;
hactive = <>;
vactive = <>;
hfront-porch = <>;
hback-porch = <>;
hsync-len = <>;
vback-porch = <>;
vfront-porch = <>;
vsync-len = <>; hsync-active = <>;
vsync-active = <>;
de-active = <>;
pixelclk-active = <>;
};
};
};
}; &iomuxc {
pinctrl-names = "default";
pinctrl- = <&pinctrl_uart1>;
imx6ul-evk {
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
}; pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0
>;
}; pinctrl_lcdif_dat: lcdifdatgrp {
fsl,pins = <
MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
>;
}; pinctrl_lcdif_ctrl: lcdifctrlgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_WR_RWN 0x79
MX6UL_PAD_LCD_ENABLE__LCDIF_RD_E 0x79
MX6UL_PAD_LCD_HSYNC__LCDIF_RS 0x79
MX6UL_PAD_LCD_RESET__LCDIF_CS 0x79
/* used for lcd reset */
MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x79
>;
}; pinctrl_keypad: keypadgrp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x70a0
MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x70a0
MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x70a0
MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x70a0
MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x70a0
MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x70a0
MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x70a0
MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x70a0
>;
}; pinctrl_gpmi_nand_1: gpmi-nand- {
fsl,pins = <
MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0xb0b1
MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0xb0b1
MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0xb0b1
MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0xb0b1
MX6UL_PAD_NAND_CE1_B__RAWNAND_CE1_B 0xb0b1
MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0xb0b1
MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0xb0b1
MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0xb0b1
MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0xb0b1
MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0xb0b1
MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0xb0b1
MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0xb0b1
MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0xb0b1
MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0xb0b1
MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0xb0b1
>;
};
};
};

2.根据自己的开发需求配置dts文件,本文矩阵键盘驱动所使用到的GPIO管脚资源为:gpio2-2、gpio2-3、gpio2-4、gpio2-5、gpio2-6、gpio2-7、gpio4-25、gpio4-26。dts文件配置如下:

~/yangfile/imx6ul/linux-3.14.38-v2$ vim arch/arm/boot/dts/imx6ul-newland.dts

2.1 在dts文件中添加一个设备节点,比如我们是矩阵键盘驱动,那么就添加一个名为”keyboard“的设备节点;

2.2 compatible属性用于of_find_node_compatible函数获取设备节点用的,这个函数的通过”max-keypad“字符串去遍历device tree,查找匹配的设备节点;

2.3 pinctrl-0 = <&pinctrl_keypad>主要用于说明设备硬件资源在哪里获取,比如这里就是到iomuxc里面去获取IO资源

2.4 iomuxc设备节点里面定义了CPU所有的IO资源,包括每个IO口的初始化状态都定义好了,比如:MX6UL_PAD_ENET1_RX_EN_GPIO2_IO02  0x70a0,这里的MX6UL_PAD_ENET1_RX_EN_GPIO2_IO02宏表示的是GPIO2-2这个IO口的寄存器组(IO复用寄存器、IO方向控制寄存器、IO输入输出值设置寄存器),0x70a0这个值根据自己的驱动开发需求,查阅CPU手册定义,不唯一。

 keyboard {
compatible = "max-keypad";
pinctrl-names = "default";//这个设置成默认default就可以了,没什么特别要求
pinctrl- = <&pinctrl_keypad>;//到iomuxc里面去获取相应IO资源的初始化状态
in-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //“in-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的
<&gpio2 GPIO_ACTIVE_HIGH>, //GPIO_ACTIVE_HIGH:逻辑高电平有效
<&gpio2 GPIO_ACTIVE_HIGH>; //key_in2 out-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //“out<span style="font-family: Arial, Helvetica, sans-serif;">-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的</span>
<&gpio2 GPIO_ACTIVE_HIGH>, //key_out1
<&gpio2 GPIO_ACTIVE_HIGH>, //key_out2
<&gpio4 GPIO_ACTIVE_HIGH>, //key_out3
<&gpio4 GPIO_ACTIVE_HIGH>; //key_out4
status = "okay";//使能要使用的gpio资源
};
};
&iomuxc {
pinctrl-names = "default";
pinctrl- = <&pinctrl_uart1>;
。。。。。。。。
pinctrl_keypad: keypadgrp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x70a0
MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x70a0
MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x70a0
MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x70a0
MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x70a0
MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x70a0
MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x70a0
MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x70a0
>;
};

3.编译dts文件,在内核根目录下执行以下命令:

~/yangfile/imx6ul/linux-3.14.38-v2$ make ARCH=arm CROSS_COMPILE=arm-linux-gcc imx6ul-newland.dtb

(这里的arm-Linux-gcc只是个代表交叉编译器的标识,具体的根据实际情况而定)

4.将配置、编译后的dtb文件烧录到设备flash(或者SD卡)的dtb分区中。

2 驱动代码中如何注册dts文件中的设备
接触了device tree机制的驱动开发后,其实device tree机制就是Linux-2.6.x中的platform 总线机制的优化版本。OK,我们来说说基于device tree机制的驱动开发中注册设备的过程,这里以我写的矩阵键盘驱动代码的设备注册过程为例:
1.在probe函数中调用of_get_**或者of_find_**函数从dtb中获取设备资源:

 static int max_keypad_probe(struct platform_device *pdev)
{
int i,ret;
struct device *dev;
struct device_node *dev_node = NULL; //add by zengxiany dev = &pdev->dev;
。。。。。。
//省略部分代码
dev_node = of_find_compatible_node(NULL,NULL,"fsl,imx6ul-gpio");
if(!of_device_is_compatible(dev_node,"fsl,imx6ul-gpio"))
{
printk("get keypad device node error!\n");
return -EINVAL;
}
dev_node = of_find_compatible_node(dev_node,NULL,"max-keypad");
if(!of_device_is_compatible(dev_node,"max-keypad"))
{
printk("failure to find max-keypad device node!\n");
return -EINVAL;
} for(i=; i< KEYPAD_ROWS; i++)
{
gpio_map_rowkey[i] = of_get_named_gpio(dev_node,"in-gpios",i);
set_key_input(gpio_map_rowkey[i]);
} for(i=; i< KEYPAD_COLS; i++)
{
gpio_map_colkey0[i] = of_get_named_gpio(dev_node,"out-gpios",i);
set_key_input(gpio_map_colkey0[i]);
}
}

2.在init函数中注册设备:

 //add by zengxiany for platform device register
static struct of_device_id max_keypad_of_match[] = {
{ .compatible = "max-keypad", },
{ },
}; static struct platform_driver max_keypad_device_driver = {
.probe = max_keypad_probe,
.remove = max_keypad_remove,
.driver = {
.name = "max-keypad",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(max_keypad_of_match),
}
};
static int __init keypad_module_init(void)
{
int ret;
ret = platform_driver_register(&max_keypad_device_driver);//modify by zengxiany
if(ret < )
{
printk("max_keypad_device driver init error!\n");
return -ENODEV;
}
return ;
} static void __exit keypad_module_exit(void)
{
platform_driver_unregister(&max_keypad_device_driver);
}

OK,这样就完成了设备的注册

linux驱动开发—基于Device tree机制的驱动编写的更多相关文章

  1. 【转载】Linux设备树(Device Tree)机制

    转:Linux设备树(Device Tree)机制   目录 1. 设备树(Device Tree)基本概念及作用2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DT ...

  2. The Linux usage model for device tree data

    Linux and the Device Tree The Linux usage model for device tree data Author: Grant Likely grant.like ...

  3. [转] Linux 3.10 ARM Device Tree 的初始化

    [转] Linux 3.10 ARM Device Tree 的初始化 本文代码均来自标准 linux kernel 3.10,可以到这里下载 https://www.kernel.org/     ...

  4. 转:Linux设备树(Device Tree)机制

    目录 1. 设备树(Device  Tree)基本概念及作用 2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DTB 2.4. Bootloader 3. 设备树中d ...

  5. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  6. [dts]Device Tree机制

    转自:http://blog.csdn.net/machiner1/article/details/47805069 ------------------Based on linux 3.10.24 ...

  7. [dts]Device Tree机制【转】

    转自:https://www.cnblogs.com/aaronLinux/p/5496559.html 转自:http://blog.csdn.net/machiner1/article/detai ...

  8. [转] Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  9. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

随机推荐

  1. 将数组A中的内容和数组B中的内容进行交换。(数组一样大)

    将两个数组中的内容相互交换,必须是两个数组的内容一样大小. 思路: 结合两个整型变量之间的交换,同样可以用于内容一样大的数组.用异或关系相互交换. #include<stdio.h> in ...

  2. 基于.NET平台常用的框架整理 【转载】

    [转载] http://www.cnblogs.com/hgmyz/p/5313983.html 自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个 ...

  3. 如何优化JavaScript的构造函数

    首先看一个构造函数User,我们在调用User创建一个实例的的时候,一般都是要写上new操作符的.在这里说明一下,如果使用new关键字调用构造函数,那么构造函数里面的this总是是指向一个全新的对象( ...

  4. Use swig + lua quick guide

    软件swigwin3    用于生成c的lua包装lua5.2源代码 步骤进入目录G:\sw\swigwin-3.0.12\Examples\lua\arrays执行 SWIG -lua     ex ...

  5. input 修改placeholder颜色

    一般来说input的placeholder是不能修改的,但是现在浏览器利用伪类实现了对placeholder的修改 input::-webkit-input-placeholder { // cher ...

  6. 单页面应用(SPA)重新部署后,正在浏览的页面如何更新缓存?

    当单页面的系统在重新部署更新时,此时正在浏览网页,并且已经在网页内的用户,始终会使用老的js与css文件,一直在使用已经缓存了的静态资源. 所有的缓存问题焦点都在index.html上,只要index ...

  7. Linux 安装MySql启动Can't locate Data/Dumper.pm in @INC

    通过RPM包CentOS7 安装MySQL的时候提示“Can't locate Data/Dumper.pm in @INC (@INC contains: /usr/local/lib64/perl ...

  8. RedHat6.5安装Spark集群

    版本号: RedHat6.5   RHEL 6.5系统安装配置图解教程(rhel-server-6.5) JDK1.8      http://blog.csdn.net/chongxin1/arti ...

  9. shopnc-setNcCookie-后台验证码

    function setNcCookie($name, $value, $expire='3600', $path='', $domain='.a.cn', $secure=false){ if (e ...

  10. linux新手非常有用的20个命令

    引用:http://www.oschina.net/translate/useful-linux-commands-for-newbies 1. ls命令 ls命令是列出目录内容(List Direc ...