linux驱动开发—基于Device tree机制的驱动编写
前言
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 = <®_arm>;
soc-supply = <®_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机制的驱动编写的更多相关文章
- 【转载】Linux设备树(Device Tree)机制
		
转:Linux设备树(Device Tree)机制 目录 1. 设备树(Device Tree)基本概念及作用2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DT ...
 - 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 ...
 - [转] Linux 3.10 ARM Device Tree 的初始化
		
[转] Linux 3.10 ARM Device Tree 的初始化 本文代码均来自标准 linux kernel 3.10,可以到这里下载 https://www.kernel.org/ ...
 - 转:Linux设备树(Device Tree)机制
		
目录 1. 设备树(Device Tree)基本概念及作用 2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DTB 2.4. Bootloader 3. 设备树中d ...
 - Linux 内核中的 Device Mapper 机制
		
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
 - [dts]Device Tree机制
		
转自:http://blog.csdn.net/machiner1/article/details/47805069 ------------------Based on linux 3.10.24 ...
 - [dts]Device Tree机制【转】
		
转自:https://www.cnblogs.com/aaronLinux/p/5496559.html 转自:http://blog.csdn.net/machiner1/article/detai ...
 - [转] Linux 内核中的 Device Mapper 机制
		
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
 - 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
		
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
 
随机推荐
- Airtest Project的探索和使用
			
Airtest使用参考博文: https://testerhome.com/topics/12391 1. 安装Python 3 2. 安装pip: 安装方法参考另外一篇随笔 pip3部署: C:\U ...
 - 我对 前端 Js 开发方式 架构方向 的 一些看法
			
有 网友 提到 : “复杂的页面,一个页面加载的模块多,各种异步请求,页面渲染,jquery链式编程操作dom数过于频繁.现在的前台越来越复杂,逻辑臃肿.” 哎, 所以 我说, 要改成用 同步调用 . ...
 - 30-seconds-of-css
			
你可以再30秒或者更短的时间内读懂的有用的CSS代码片段的精选. github地址 不过代码不在github上面 官网地址 上面有详细的介绍和演示 下面是我读到的一些个人认为比较实用的片段 1. 等宽 ...
 - nginx 镜像使用说明
			
nginx 镜像说明 目录 说明 /etc/nginx nginx安装目录 /usr/share/nginx/html nginx网站资源存放的目录 运行nginx容器,相关命令: 命令 说明 doc ...
 - hdfs  例子
			
package hadoop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; ...
 - 在CentOS6.8系统上安装MySQL5.7(转)
			
mysql-57">如何在CentOS 6.8系统上安装MySQL 5.7? 一.检查系统上是否已经安装MySQL 命令: ? 1 2 3 4 5 [root@localhost ~] ...
 - ubuntu网络配置及端口名修改
			
一.网络配置文件 buntu系统进行网络配置有的时候用图形界面不起作用,这种情况下可以直接修改某些启动脚本或配置文件 Ubuntu系统进行网络配置涉及到几个配置文件1./etc/network/int ...
 - 读取配置文件的C语言接口实现
			
在一些场合,需要对一些配置文件进行读取,去设置软件的参数,自己实现了一些接口函数,以供以后使用. ConfigFile.c #include <stdio.h> #include < ...
 - OpenWrt实现802.11s组网模式
			
参考 http://www.docin.com/p-277067204.html 无线网卡wlan0正常后,输入一下命令 iw dev wlan0 interface add mesh_iface t ...
 - 用DLL实现插件的简单演示
			
这是DLL的代码 library MyDll; uses SysUtils, Dialogs, Classes; procedure ShowInfo(info:PChar);stdcall; beg ...