一、设备树解析
设备树是描述硬件信息的平台,主要是描述芯片外设的寄存器地址,可以用一张图来描述设备树:
在Linux中主要是以dts文件体现的。该文件中记录着硬件信息,怎么样才能让内核知道硬件信息呢。
首先通过内核编译dts文件,生成dtb文件,一个单板启动时, u-boot 先运行,它的作用是启动内核。 u-boot 会把内核和设备树文件都读入内存,然后启动内核。在启动内核时会把设备树在内存中的地址告诉内核。接着内核解析 dtb 文件,把每一个节点都转换为 device_node 结构体,最后对于某些 device_node 结构体,会被转换为 platform_device 结构体。至于内核怎么解析dtb文件生成device结构体的过程,我们可以不用关心。
什么样节点会生成 platform_device 结构体?具有一下特征:
1.根节点下含有 compatile 属性的子节点;
2.含有特定 compatile 属性的节点的子节点:如果一个节点的 compatile 属性,它的值是这 4 者之一: "simple bus","simple-mfd","isa","arm,amba-bus", 那 么 它 的 子 结 点 ( 需含compatile 属性)也可以转换为 platform_device;
3.总线 I2C、 SPI 节点下的子节点: 不转换为 platform_device
1.设备树属性:
(1)#address-cells:cell表示一个32位的数值,address 要用多少个 32 位数来表示;
(2) #size-cells:size 要用多少个 32 位数来表示;
(3)compatible:该属性表示兼容,如对于一个led,可以兼容多个驱动;根节点下方也有该属性:compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";一般双引号内部为“厂家名,模块名”,有几个逗号表示兼容那些单板;
(4) model:准确地定义这个硬件是什么,这个单板具体型号;
(5) status:状态。具体为okay,disabled,fail,fail-sss,okay表示正常;disabled表示设备不可操作,但是后面可以恢复运行;fail表示发生错误,需要修复;fail-sss表示在前者基础上发送错误信息;
(6) reg:表示寄存器信息。其数值受address-cells和size-cells属性限制;
以下是部分采用上述属性描述的设备节点信息:
2.platform_device 如何与 platform_driver 配对
从设备树转换得来的 platform_device 会被注册进内核里,以后当我们每注册一个platform_driver 时,它们就会两两确定能否配对,如果能配对成功调用 platform_driver 的 probe 函数。
匹配规则借用一张图表示:
其实,主要还是匹配定义的节点中compatible属性是否与platform_driver .driver.of_match_table中compatible属性相同,其它的个人认为并不是特别重要;
3.API函数
列举部分函数:
(1)of_find_node_by_path:查找节点;
(2)of_find_node_by_name:根据名字查找节点
(3)of_property_read_u32_array:读取属性数据,以数组形式缓存
(4)of_iomap:相当于ioremap函数
驱动led例子;
在设备树中添加节点,并通过make dtbs进行编译:
myboardled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "myboard_led";
reg = < 0X02290014 0x04 /*SW_MUX_SNVS_TAMPER3_BASE*/
0X020AC004 0x04 /*GPIO5_GDIR_BASE*/
0X020AC000 0x04 >; /*GPIO5_DR_BASE*/
};
platform_driver编写和之前没什么太大差异:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/property.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR;
static volatile unsigned int *GPIO5_DR;
static int major = 0;
static struct class *led_class;
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
char val;
int ret;
/* copy_from_user : get data from app */
ret = copy_from_user(&val, buf, 1);
/* to set gpio register: out 1/0 */
if (val)
{
/* set gpio to let led on */
*GPIO5_DR &= ~(1<<3);
}
else
{
/* set gpio to let led off */
*GPIO5_DR |= (1<<3);
}
return 1;
}
static int led_drv_open (struct inode *node, struct file *file)
{
/* enable gpio5
* configure gpio5_io3 as gpio
* configure gpio5_io3 as output
*/
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
*GPIO5_GDIR |= (1<<3);
return 0;
}
static struct file_operations led_fileoperation = {
.open = led_drv_open,
.write = led_drv_write,
.owner = THIS_MODULE,
};
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
int ret = 0;
int err = 0;
uint32_t regdata[6];
struct property *proper;
struct device_node* np = pdev->dev.of_node;
/* 1、获取compatible属性内容 */
proper = of_find_property(np, "compatible", NULL);
if(proper == NULL)
{
printk("compatible property find failed\r\n");
}
else
{
printk("compatible = %s\r\n", (char*)proper->value);
}
//2.获取led资源
ret = of_property_read_u32_array(np, "reg", regdata, 6);
if(ret < 0)
{
printk("reg property read failed!\r\n");
}
//3.完成寄存器映射
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = of_iomap(np,0);
GPIO5_GDIR = of_iomap(np,1);
GPIO5_DR = of_iomap(np,2);
//4.创建字符设备
major = register_chrdev(0, "100ask_led", &led_fileoperation); /* /dev/100ask_led */
led_class = class_create(THIS_MODULE, "100ask_led_class");
err = PTR_ERR(led_class);
if (IS_ERR(led_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "led");
return -1;
}
device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0,1,... */
return 0;
}
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
iounmap(GPIO5_GDIR);
iounmap(GPIO5_DR);
unregister_chrdev(major, "100ask_led");
device_destroy(led_class, MKDEV(major, 0));
class_destroy(led_class);
return 0;
}
static const struct of_device_id ask100_led[] = {
{.compatible = "myboard_led"},
};
static struct platform_driver led_driver = {
.probe = chip_demo_gpio_probe,
.remove = chip_demo_gpio_remove,
.driver = {
.name = "mydevice",
.of_match_table = ask100_led,
},
};
static int __init leddriver_init(void)
{
int ret = 0;
ret = platform_driver_register(&led_driver);
if (!ret)
printk("platform_driver_register error\n");
return 0;
}
static void __exit leddriver_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&led_driver);
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");