理解设备树下的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内核

相关推荐
逆风的蚂蚁4 分钟前
linux服务器性能调整怎么落地?我把 CPU 调度、I/O 优先级和内核参数这几个旋钮都拧了一遍
linux·运维·服务器
沫璃染墨4 小时前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html
AI_Lab015 小时前
宝塔用了两年,有没有必要换成轻量方案?
linux·运维·服务器
by209996 小时前
Linux GDB 调试与 CGDB 使用
linux·运维·服务器·经验分享
星恒随风6 小时前
Linux网络编程:应用层协议究竟是什么?从网络计算器到TCP Socket抽象基类完整实现
linux·网络·笔记·tcp/ip
feng_you_ying_li9 小时前
linux之网络层
linux·运维·网络
未来之窗软件服务9 小时前
Linux 运维日志删除的多种方式—东方仙盟
linux·运维·服务器·仙盟创梦ide·东方仙盟
小雉系统9 小时前
BcoreOS实战:用一个升级包,让Rocky Linux跑进Debian服务器
linux·运维·服务器·debian
thethefighter9 小时前
快速预览Word、WPS秒开,支持空格弹窗
linux·word·wps·快速预览·空格弹窗·nhdeepview
用户8351414049499 小时前
把日志和寄存器值喂给AI做故障定位,我的实际问法
linux·ubuntu