全志_基于dts设备树驱动开发

基于设备树开发


平台:A64
源码:Linux3.10

路径:Linux/arch/arm(arm64)/boot/dts/    ***.dts    Makefile    //A64 是64位,选择arm64


Linux2.6 之前没有dts(arch/arm/plat-xxx和arch/arm/mach-xxx存在大量垃圾代码)

dts:结点+属性(name=value)
dts---(dtc)--->dtb


DT
DTS
DTSI
OF:open firmware 开源硬件
FDT
DTB
DTC

语法:
1.根节点    /{
2.节点-子节点    <名称>[@<设备地址>]    cpu@1 []可省,每个节点都需要一个compatible属性
3.属性
compatible属性    作用:匹配    compatible = "jk,leds";
#address-cells和#size-cells属性    作用:地址占cell个数和长度占cell个数    #address-cells = <2> #size-cells = <1> 描述子节点
reg属性    作用:地址    reg = <address1 length1 [address2 length2][address3 length3]>
中断信息属性--interrupts    中断号
#interrupt-cells    中断控制器字节属性

添加节点:
node@12345{
compatible = "jk,leds";
reg = <0x1111 0x111>



1.内核启动会加载dtb文件,加载信息到系统
2.节点位置:ls proc/device-tree    (设备树节点信息)

 

 


static int__init dt_drv_init(void)
{
/*
在代码中获取整个节点信息



*/
//1.先获取节点
of_find_node_by_path("/text@123");

//2.获取节点中属性
of_find_property();



return 0;
}


static void__exit dt_drv_init(void)
{


}

 

 

module_init(dt_drv_init);
module_exit(de_drv_exit);
modules_license("GPL");

 


#gpio-cells = <6>”表示在设备树里描述使用一个gpio口需要提供6个指定的参数.

gpio = <&pio 1 1 1 1 1 0>;
| | | | | | | |-------------------表示有效电平
| | | | | | |----------------------上下拉, 0关闭功能, 1上拉, 2下拉, 3保留
| | | | | |--------------------------驱动力,电流等级(0 - 3),级别越高,输出电流越大
| | | | |------------------------------gpio功能类型(复用类型),0输入, 1输出, 6和外部中断,7关闭功能(具体查手册)
| | | |----------------------------------pin bank 内偏移(即组内第几个io口).
| | |--------------------------------------哪组gpio, PA(0),PB(1),PC(2),PD(3),PE(4),PF(5),PG(6),PH(7),PI(8),PJ(9),PK(10),PL(11)
| |-------------------------------------------指向哪个gpio控制器, pio / r_pio(PL组)
|--------------------------------------------------属性名字(随便命名)


1.两个GPIO控制器    r_pio pio

___________________________________________________________________________________

获取设备树里设备节点的gpio口信息:

#include <linux/of_gpio.h>

//只需一个函数即可
int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags);

/*
功能:函数用于获取指定名称的 gpio 信息
np: 需要查找 GPIO 的节点;
propname: GPIO 信息的属性名字;
index:    属性 propname 中属性值的索引(表示获取属性里的第几个值);    
flags: 存放 gpio 的 flags;
返回值: 成功:返回 gpio 编号, flags 存放 gpio 配置信息;失败:返回 null
*/

 


//其中flags一定得注意,按文档里的说明应就是一个int类型的值,但根本就不能为int参数(会导致kernel panic),
//通过阅读内核里的代码得出, flags的参数应为struct gpio_config类型. 定义在下面文件:

"include/linux/sys_config.h"
struct gpio_config {
u32 gpio; /* gpio global index, must be unique */
u32 mul_sel; /* multi sel val: 0 - input, 1 - output... */
u32 pull; /* pull val: 0 - pull up/down disable, 1 - pull up... */
u32 drv_level; /* driver level val: 0 - level 0, 1 - level 1... */
u32 data; /* data val: 0 - low, 1 - high, only vaild when mul_sel is input/output */
};


/* 节点 */
struct device_node {
const char *name; /* 节点中属性为name的值 */
const char *type; /* 节点中属性为device_type的值 */
char    *full_name; /* 节点的名字,在device_node结构体后面放一个字符串,full_name指向它 */
struct    property *properties; /* 链表,连接该节点的所有属性 */
struct    device_node *parent; /* 指向父节点 */
struct    device_node *child; /* 指向孩子节点 */
struct    device_node *sibling; /* 指向兄弟节点 */
};

/* 属性 */
struct property {
char    *name; /* 属性的名字,指向设备树文件的string block */
int    length; /* 属性名字的字节数 */
void    *value; /* 属性的值,指向struct block */
struct property *next; /* 链表,连接下一个属性 */
};

 

Linux 系统为 device tree 提供了标准的 API 接口:
(OF:    API)

1.    unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
/*
功能:函数用于获取中断号
dev: 要解析中断号的设备;
index: dts 源文件中节点 interrupt 属性值索引
返回值:解析成功返回中断号,失败:返回 0
*/

2.    void __iomem *of_iomap(struct device_node *np, int index);
/*
功能:函数用于获取映射内存
np: 映射内存的设备节点;
index: dts 源文件中节点 interrupt 属性值索引;
返回值:映射成功 IO memory 的虚拟地址,失败:返回 NULL
*/


3.    static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value)
/*
功能:函数用于获取节点中的属性值
np: 获取属性值的节点
propname: 属性名称;
out_value:属性值
返回值: 映射成功 IO memory 的虚拟地址,失败:返回 NULL
*/


4.    static inline int of_property_read_string_index(struct device_node *np, const char *propname, const char **output)
/*
功能:函数用于获取节点中属性值
np: 获取属性值的节点
propname: 属性名称;
output: 存放返回字符串;
返回值: 取值成功,返回 0
*/


5.    static inline int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output)
/*
功能:函数用于获取节点中属性值
np: 获取属性值的节点
propname: 属性名称;
index: 索引配置在 dts 中属性为 propname 的值;
output:    存放返回字符串
返回值: 取值成功,返回 0
*/


6.    extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
/*
功能:函数用于获取指定名称的节点
from: 开始查找节点,如果为NULL,则从根节点开始;
name: 节点名字;
返回值: 成功:得到节点的结构体首地址;失败:NUL
*/


7.    extern struct device_node *of_find_node_by_name(struct device_node *from, const char *type);
/*
功能:函数用于获取指定 device_type 的节点
from: 开始查找节点,如果为NULL,则从根节点开始;
type: 节点名字;
返回值: 成功:得到节点的结构体首地址;失败:NUL
*/


8.    extern struct device_node *of_find_node_by_path(const char *path);
/*
功能:函数用于获取指定路径的节点
path: 通过指定路径查找节点;
返回值: 成功:得到节点的结构体首地址;失败:NUL
*/


9.    int of_get_named_gpio_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags)
/*
功能:函数用于获取指定名称的 gpio 信息
np: 需要查找 GPIO 的节点;
propname: GPIO 信息的属性;
index:    属性 propname 中属性值的索引;    
flags: 存放 gpio 的 flags;
返回值: 成功:返回 gpio 编号, flags 存放 gpio 配置信息;失败:返回 null
*/

 

___________________________________________________________________________________


获取到int类型的gpio口后,就可以使用linux/gpio.h里的gpio口操作函数:

#include <linux/gpio.h> //里面声明io口的操作函数

int gpio_request(unsigned gpio, const char *label); //每个io只能被请求一次,可防止多个驱动来控制同一个IO口
void gpio_free(unsigned gpio);    //释放已请求的io口
int gpio_direction_input(unsigned gpio); //作输入功能, gpio用于指定具体哪个io口
int gpio_direction_output(unsigned gpio, int value); //作输出功能,并根据value的值输出高低电平
int gpio_get_value(unsigned gpio); //获取指定IO口的电平
void gpio_set_value(unsigned gpio, int value); //设置IO口的电平为value(0/1)
int gpio_to_irq(unsigned gpio); //根据io口,获取到它对应的中断号(io口大都有外部中断功能)
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)


___________________________________________________________________________________

设备树里的描述:

jkbuzzer {
compatible = "jk,buzzer";
gpios = <&pio 3 24 1 1 1 1>; 
};

jkleds {
compatible = "jk,leds";
gpios = <&r_pio 11 10 1 1 1 1>, <&r_pio 11 12 1 1 1 1>;
};


//PH8
text {
compatible = "text_";
gpios = <&pio 7 9 1 1 1 0>;
};

 


___________________________________________________________________________________

代码测试:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sys_config.h>
#include <linux/delay.h>

int myprobe(struct platform_device *pdev)
{
struct device_node *nd = pdev->dev.of_node;
int gpio; 
struct gpio_config config;


printk("gpio count:%d
", of_gpio_named_count(nd, "gpios"));
gpio = of_get_named_gpio_flags(nd, "gpios", 0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(gpio))
printk("gpio isn't valid
");
if (gpio_request(gpio, pdev->name) < 0)
printk("gpio request failed %d
", gpio);

gpio_direction_output(gpio, 1);
msleep(3000);
gpio_direction_input(gpio);
gpio_free(gpio);
return 0;
}

int myremove(struct platform_device *pdev)
{
printk("in myremove ...
");
return 0;
}

struct of_device_id ids[] = {
{.compatible = "jk,buzzer"},
{},
};

struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,

.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,

.of_match_table = ids,
},
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");

 

 

代码测试:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/sys_config.h>
#include <linux/delay.h>

int myprobe(struct platform_device *pdev)
{
struct device_node *nd = pdev->dev.of_node;
int gpio, n, i; 
struct gpio_config config;


n = of_gpio_named_count(nd, "gpios");
for (i = 0; i < n ; i++)
{
gpio = of_get_named_gpio_flags(nd, "gpios", i, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(gpio))
printk("gpio isn't valid
");
if (gpio_request(gpio, pdev->name) < 0)
printk("gpio request failed %d
", gpio);

gpio_direction_output(gpio, 1);
msleep(3000);
gpio_direction_input(gpio);
gpio_free(gpio);
}
return 0;
}

int myremove(struct platform_device *pdev)
{
printk("in myremove ...
");
return 0;
}

struct of_device_id ids[] = {
{.compatible = "jk,leds"},
{},
};

struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,

.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,

.of_match_table = ids,
},
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");

 

 

 

 

 

 

 

优质博客:
https://blog.csdn.net/jklinux/article/details/82391923
http://www.pianshen.com/article/520912458/
https://www.linuxidc.com/Linux/2017-02/140818.htm

 
原文地址:https://www.cnblogs.com/panda-w/p/11759151.html