理解设备树下的platform驱动编写

platform驱动框架分为总线、设备和驱动,驱动程序开发不需要管理platform驱动框架下的总线,只需关注设备和驱动的实现。而在有设备树的Linux内核下,设备的描述被放到了设备树中,我们不编写platform_device,只需编写platform_driver。

设备树创建设备节点描述设备信息,主要设置compatible属性值,用来匹配驱动。

复制代码
gpioled {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "atkalpha-led";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_led>;
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
    status = "okay";
};

compatible属性值要与platform_driver结构体中的struct device_driver driver下的of_match_table属性表中的值一致。

复制代码
static const struct of_device_id  leds_of_match[] = {
    { .compatible = "atkalpha-led" },
    { /* Sentinel */ }
};

MODULE_DEVICE_TABLE(of, leds_of_match);

static struct platform_driver leds_platform_driver = {
    .driver = {
        .name   = "imx6ul-led",
        .of_match_table = leds_of_match,
    },
    .probe = leds_porbe,
    .remove = leds_remove,
};

MODULE_DEVICE_TABLE声明leds_of_match这个设备匹配表。

platform_driver编写

例如驱动一个led,首先在leddev设备结构体增加LED设备节点node和LED灯GPIO标号led0,它们将分别在设备树文件中得到gpioled节点和led-gpio属性。

复制代码
struct leddev_dev {
    dev_t devid;
    ...
    struct device_node *node;
    int led0;
};

设备树文件已声明了gpio资源,led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>,可调用基于整数编号的Legacy API函数gpio_set_value()来控制gpio电平。

复制代码
void led0_switch(u8 sta)
{
    if (sta == LEDON )
        gpio_set_value(leddev.led0, 0);
    else if (sta == LEDOFF)
        gpio_set_value(leddev.led0, 1);
}

由于是在非设备树的驱动程序上修改,而设备树文件已经提供了硬件资源的信息,所以led_probe函数删除platform_get_resource()获取资源的代码,删除配置gpio代码 ,增加访问设备树获取节点属性的代码。

复制代码
static int led_probe(struct platform_device *dev)
{
    
    ...
    
    leddev.node = of_find_node_by_path("/gpioled");
    if (leddev.node == NULL) {
        printk("gpioled node nost find!\r\n");
        return -EINVAL;
    }

    leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
    if (leddev.led0 < 0) {
        printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    
    gpio_request(leddev.led0, "led0");
    gpio_direction_output(leddev.led0, 1);
    return 0;
}

leddev.node和leddev.led0分别从设备树文件里读取到,"gpioled","led-gpio"分别是设备树文件中的节点和属性。

在结构体platform_driver中增加.of_match_table = led_of_match

复制代码
static const struct of_device_id led_of_match[] = {
    { .compatible = "atkalpha-led" },
    { /* Sentinel */ }
};

static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ul-led",
        .of_match_table = led_of_match,
    },
    .probe = led_probe,
    .remove = led_remove,
};

.compatible 的属性值"atkalpha-gpioled"一定要和dts文件中的compatible一致。

设置这个 platform 驱动的name为"imx6ul-led",因此,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为"imx6u-led"的文件。

ledderiver驱动程序写好后,运行后有如下错误:

/lib/modules/4.1.15 # depmod

/lib/modules/4.1.15 # modprobe leddriver.ko

led driver and device has matched!

gpioled node not find!

imx6ul-led: probe of gpioled failed with error -22

经过排查,是下面的代码中了一个"/"。

错误代码:

leddev.node = of_find_node_by_path("gpioled");

应该修改成:

leddev.node = of_find_node_by_path("/gpioled");

重新编译运行,无报错,功能正常。

参考资料

  1. I.MX6U 嵌入式 Linux 驱动开发指南 V2.0.1 正点原子

  2. Linux设备驱动开发 详解:基于最新的Linux 4.0内核

相关推荐
小义_1 小时前
Linux 补充
linux·服务器
晴天的雨.9921 小时前
【Linux】Shell 命令以及运行原理+权限管理
linux·运维·服务器
djjjx.1 小时前
Linux工具篇 (六):GDB 调试从底层逻辑到高阶实战
linux·运维·服务器·gdb
coward912 小时前
RK3576 双千兆 GMAC 驱动加载异常问题排查与总结
linux
幼儿园蛋小黄2 小时前
从设备树到 ARM 驱动:嵌入式 Linux 驱动开发学习总结
linux·arm开发·驱动开发
dishugj2 小时前
HANA集群计划内切换与维护操作
java·linux·数据库
爱吃香菜的初学者4 小时前
十四.Linux——消息队列
linux·运维·开发语言
Apipi*11 小时前
30天速通Linux 第六章信号及信号处理
linux·运维·信号处理
天启HTTP12 小时前
爬虫防封核心原理:IP轮换机制与风控规避逻辑
linux·服务器·网络·爬虫·tcp/ip