嵌入式Linux设备树下的 platform 驱动

Linux 系统中 platform 平台框架包括总线、 设备和驱动, 其中总线不用我们去操心, Linux 内核中会自动管理, 我们只需要关系设备和驱动如何实现。 在不支持设备树的内核中, 我们需要分别实现platform_device 和 platform_driver, 其中 platform_device 是在平台文件中实现的。 在支持设备树的内核中,我们就不用实现 platform_device 了, 而是在设备树文件中添加设备信息。 下面看一下在设备树文件中添加设备信息。
在之前关于设备树语法的章节中, 我们学习了如何在根节点“/” 下去添加一个设备节点信息。 其中最重要的就是 compatible 属性值, compatible 属性使用来和驱动进行匹配的。 下面是本实验用到的设备的设备节点:
嵌入式Linux设备树下的 platform 驱动_第1张图片

在编写驱动以前, 有一个地方需要注意一下, 我们在加载 driver.ko 之前, 一定要在开发板上已经成功地添加了 test 的节点, 你可以在 linux 系统里面查看到你添加的节点,  查看到 test 节点的 comtabile 属性的值为 test1234, 如下图所示:


 

Platform 驱动程序
driver.c 文件

#include  //初始化头文件
#include  //最基本的文件, 支持动态添加和卸载模块。
#include  //platform 平台设备相关的头文件

/**
* @description: platform 驱动的 probe 函数, 当驱动与设备匹配以后此函数就会执行
* @param {*}pdev : platform 设备
* @return {*}0, 成功;其他负值,失败
*/
int beep_probe(struct platform_device *pdev)
{
    //匹配成功以后, 进入到 probe 函数
    printk("beep_probe\n");
    return 0;
} 

/**
* @description: platform 驱动的 remove 函数, 移除 platform 驱动的时候此函数会执行
* @param {structplatform_device} *pdev: platform 设备
* @return {*} 0, 成功;其他负值,失败
*/
int beep_remove(struct platform_device *pdev)
{
    printk("beep_remove\n");
    return 0;
} 

const struct platform_device_id beep_idtable = {
.name = "beep_test",
};

//与设备树的 compatible 匹配
const struct of_device_id of_match_table_test[] = {
{
    .compatible = “test1234”},
    {},
};

struct platform_driver beep_driver = {
    //3. 在 beep_driver 结构体中完成了 beep_probe 和 beep_remove
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "beep_test",
        //接下来我们改一下驱动, 让他来匹配设备树里面 test 的节点, of_match_table 优先级高id_table
       //设置 platform 驱动匹配表
       .of_match_table = of_match_table_test},
       //4 .id_table 的优先级要比 driver.name 的优先级要高, 优先与.id_table 进行匹配
       .id_table = &beep_idtable
};


static int beep_driver_init(void)
{
      //1.我们看驱动文件要从 init 函数开始看
     int ret = 0;
     //2.在 init 函数里面注册了 platform_driver
     ret = platform_driver_register(&beep_driver);
     if (ret < 0)
     {
          printk("platform_driver_register error \n");
     } 

     //打印平台设备驱动注册成功
     printk("platform_driver_register ok \n");
     return 0;
} 

static void beep_driver_exit(void)
{
    platform_driver_unregister(&beep_driver);
    printk("gooodbye! \n");
} 

module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");

保存 driver.c 文件, 编译 driver.c 为驱动模块,加载刚刚编译好的 driver.ko, 如下图所示:

cd /mnt/nfs/imx6ull/25driver/
insmod driver.ko

如上图所示, 已经匹配成功进入到 probe 函数中。 如果没有进入 probe 函数, 可能出现匹配不成功的原因是 1 device 或者设备树根本没有加到我们系统里面 2 名字不一样导致匹配不成功。

获取资源
进入了 probe 函数, 可以在 probe 函数中获取资源, 如下所示:

//匹配成功以后, 进入到 probe 函数
int beep_probe(struct platform_device *pdev){
    printk("beep_probe\n");
    /*********************方法一: 直接获取节点**************************/
    printk("node name is %s\n",pdev->dev.of_node->name);
    return 0;
}

编译驱动, 然后加载驱动后, 如下图所示:

嵌入式Linux设备树下的 platform 驱动_第2张图片

如上图所示, 加载驱动以后, 设备树上的节点和驱动程序匹配成功, 进入了 probe 函数, 并打印了节点的名字。

通过of 操作函数来获取我们的设备资源, 修改 driver.c 为如下所示:

int beep_probe(struct platform_device *pdev)
{ //匹配成功以后, 进入到 probe 函数
    printk("beep_probe\n");
    /*********************方法一: 直接获取节点**************************/
    //printk("node name is %s\n",pdev->dev.of_node->name);
    /*********************方法二: 通过函数获取硬件资源**************************/
    //获得设备节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
         printk("of_find_node_by_path is error \n");
         return -1;
    } 
    //获取 reg 属性
    ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 2);
    if (ret < 0)
    {
         printk("of_property_read_u32_array is error \n");
         return -1;
    } 
    printk("out_values[0] is 0x%08x\n", out_values[0]);
    printk("out_values[1] is 0x%08x\n", out_values[1]);
    return 0;
}

编译驱动, 然后加载驱动后, 如下图所示:
嵌入式Linux设备树下的 platform 驱动_第3张图片
如上图所示, 我们已经成功的获得设备树里面的 reg 属性。
获取节点属性
 修改 driver.c 如下所示:

int beep_probe(struct platform_device *pdev)
{ //匹配成功以后, 进入到 probe 函数
    printk("beep_probe\n");

    ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 2);
    if (ret < 0)
    {
         printk("of_property_read_u32_array is error \n");
         return -1;
    } 
    printk("out_values[0] is 0x%08x\n", out_values[0]);
    printk("out_values[1] is 0x%08x\n", out_values[1]);
    return 0;
}

编译驱动, 然后加载驱动后, 如下图所示:
嵌入式Linux设备树下的 platform 驱动_第4张图片

 如上图所示, 可以直接通过节点获取到 reg 属性的值。

映射物理地址
现在我们已经拿到了寄存器的地址, 接下来可以注册杂项设备或者字符设备, 我们先将获取到的物理地址映射为虚拟地址, 修改 driver.c 代码如下:

int beep_probe(struct platform_device *pdev)
{
    printk("beep_probe\n");

    ret = of_property_read_u32_array(pdev->dev.of_node, "reg", out_values, 2);
    if (ret < 0)
    {
        printk("of_property_read_u32_array is error \n");
        return -1;
    }  
    printk("out_values[0] is 0x%08x\n", out_values[0]);
    printk("out_values[1] is 0x%08x\n", out_values[1]);
    /*********************映射物理地址**************************/
    vir_gpio_dr = of_iomap(pdev->dev.of_node, 0);
    if (vir_gpio_dr == NULL)
    {
       printk("of_iomap is error \n");
       return -1;
    } 
    return 0;
}

编译驱动, 然后加载驱动后, 如下图所示:
嵌入式Linux设备树下的 platform 驱动_第5张图片

如上图所示, 物理地址已经映射为虚拟地址, 接下来可以注册字符设备和杂项设备, 流程和我们前面学习到的内容是一模一样的。
 

你可能感兴趣的:(嵌入式,设备树,linux)