平台总线驱动和设备树匹配

平台总线介绍

总线代表着同类设备需要共同遵循的工作时序,不同的总线对于物理电平的要求是不一样的,对于每个比特的电平维持宽度也是不一样的,总线上传递的命令也会有自己的格式约束。

在Linux系统中总线可分为两种,

1、一种是实际存在的总线(例如I2C、SPI、USB等总线)。

2、另一种是虚拟存在的总线(platform总线)。

1.linux 设备驱动程序的演变

 1.1 硬件资源和驱动写在同一个文件里

简单不易扩展,需重新编译,不同的引脚要写不同的驱动。

1.2 总线设备驱动

硬件资源用 platform_device 指定;驱动在 platform_driver 实现。
稍复杂, 易扩展, 有较多冗余代码 .c 文件, 每增加平台换个引脚都要重新编译。
c 文件指定资源
1.3 设备树
硬件资源用设备树指定;驱动在 platform_driver 实现。
内核根据 dts 文件, 分配 / 设置 / 注册 platform_device,每增加平台换个引脚修改 dts 文件, 提供不同 dtb 文件即可。
稍复杂, 易扩展, 无冗余代码, 无需重新编译, 需要提供不同的设备树文件。

 2.案例:平台总线驱动和设备树匹配

参考:http://wiki.t-firefly.com/zh_CN/AIO-3399J/driver_gpio.html

首先在 DTS 文件中增加驱动的资源描述;
rk3399-nanopi4-rev07.dts /rev00.dts( 小板 ) 文件中根节点下添加一个节点:
myled_ctrl {
        status = "okay";
        compatible = "rk3399_led2_green";
        gpio_led = <&gpio1 23 GPIO_ACTIVE_HIGH>; /* GPIO1_C7 */
};

 保存然后编译内核 :

make ARCH=arm64 nanopi4-images -j4

 将内核镜像烧录到开发板

 运行开发板,验证是否成功。

root@SOM-RK3399v2:~# cd /sys/firmware/devicetree/base/
root@SOM-RK3399v2:/sys/firmware/devicetree/base# ls
root@SOM-RK3399v2:/sys/firmware/devicetree/base# cd myled_ctrl/
root@SOM-RK3399v2:/sys/firmware/devicetree/base/myled_ctrl# ls
compatible gpio_led name status
root@SOM-RK3399v2:/sys/firmware/devicetree/base/myled_ctrl#
cat compatible
rk3399_led2_greenroot@SOM-RK3399v2:/sys/firmware/devicetree/base/myled_ctrl
#
然后编写平台设备驱动程序运行。

 root@SOM-RK3399v2:/drv_code# ls

 led_drv.ko
root@SOM-RK3399v2:/drv_code# insmod led_drv.ko
[ 1250.159618] led_drv: loading out-of-tree module taints kernel.
[ 1250.160868] <xyd> call led_driver_init()
[ 1250.161925] <xyd> call led_driver_probe()
root@SOM-RK3399v2:/drv_code#
调试方法:
以下目录包含有注册进内核的所有 platform_device。
一个设备对于一个目录, 进入某个目录后, 如果它有 ”driver” 子目录, 就表示 platform_device 跟某个 platform_driver 驱动配对了。
[rootrk3399:/sys/devices/platform]# ls myled_ctrl/
driver driver_override modalias of_node power subsystem uevent
[rootrk3399:/sys/devices/platform]#

3.在 probe 函数中对 DTS 所添加的资源进行解析

使用设备树写驱动程序,设备树节点要与 platform_driver 能匹配。
驱动要求设备树提供什么, 我们就按这个要求去编写设备树, 匹配过程中所要求的东西是固
定:
1) 设备树要有 compatible 属性, 它的值是一个字符串;
2) platform_driver 中要有 of_match_table, 其中一项 compatible 成员设置为一个字符串;
3) 上述两个字符串要一致;

验证配置结果:

root@SOM-RK3399v2:/drv_code# ls
led_drv.ko
root@SOM-RK3399v2:/drv_code# insmod led_drv.ko
[ 1039.449461] led_drv: loading out-of-tree module taints kernel.
[ 1039.453253] <xyd> call led_driver_init()
[ 1039.454847] <xyd> call led_driver_probe() # 平台驱动匹配上 DTS 节点
root@SOM-RK3399v2:/# cd /sys/bus/platform
root@SOM-RK3399v2:/sys/bus/platform# ls
devices drivers drivers_autoprobe drivers_probe uevent
# 平台设备和驱动保存这个目录下
root@SOM-RK3399v2: /sys/bus/platform/drivers/myled# ls –al # 查看平台驱动
total 0
drwxr-xr-x 2 root root 0 Jun 7 07:43 .
drwxr-xr-x 120 root root 0 Jun 7 07:26 ..
--w------- 1 root root 4096 Jun 7 07:51 bind
lrwxrwxrwx 1 root root 0 Jun 7 07:51 module -> ../../../../module/led_drv
lrwxrwxrwx 1 root root 0 Jun 7 07:51 myled_ctrl -> ../../../../devices/platform/myled_ctrl
#myled_ctrl DTS 的节点 现在被转换为 platform_device
--w------- 1 root root 4096 Jun 7 07:43 uevent
--w------- 1 root root 4096 Jun 7 07:51 unbind
root@SOM-RK3399v2:/sys/bus/platform/devices/myled_ctrl# ls –al # 查看平台设备就是 DTS 节点转换的
total 0
drwxr-xr-x 3 root root 0 Jun 7 07:26 .
drwxr-xr-x 155 root root 0 Jun 7 07:26 ..
lrwxrwxrwx 1 root root 0 Jun 7 07:46 driver -> ../../../bus/platform/drivers/myled
# myled 是匹配
-rw-r--r-- 1 root root 4096 Jun 7 07:46 driver_override
-r--r--r-- 1 root root 4096 Jun 7 07:46 modalias
lrwxrwxrwx 1 root root 0 Jun 7 07:46 of_node -> ../../../firmware/devicetree/base/myled_ctrl
#myled_ctrl 平台设备是来自于 DTS 节点的
drwxr-xr-x 2 root root 0 Jun 7 07:46 power
lrwxrwxrwx 1 root root 0 Jun 7 07:26 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jun 7 07:26 uevent

设备树节点指定资源,platform_driver 获得资源:

如果设备树节点里使用 reg 属性, 那么内核生成的 platform_device 时会用 reg 属性来设置 IORESOURCE_MEM 类型的资源;
如果设备树节点里使用 interrupts 属性, 那么内核生成的 platform_device 时会用 interrupts 属性来设置 IORESOURCE_IRQ 类型的资源, 对于 interrupts 属性, 内核会检查它的有效性, 不建议在设备树里使用该属性表示其他资源。

4.内核对设备树的处理

1) dts ubuntu 主机上被编译为 dtb 文件;
2) u-boot dtb 文件传给内核;
3) 内核解析 dtb 文件, 把每一个节点转换为 device_node 结构体;
4) 对于某些 device_node 结构体, 会被转换为 platform_device 结构体;
dtb 中每一个节点都被转换为 device_node 结构体。

哪些设备树节点会被转换为 platform_device:
1) 根节点下含有 compatible 属性的子节点;
2) 含有特定 compatible 属性的节点的子节点;
如果一个节点的compatible属性,它的值是这三者之一:
"simple-bus","simple-mfd","isa","arm,amba-bus"
那么它的子节点(需含 compatible 属性)也可转换为 platform_device.
3) 总线 I2C,SPI 节点下的子节点,不转换为 platform_device.
某个总线下子节点,应该交给对应的总线驱动程序来处理,它们不应该转换为platform_device.

匹配实现原理
platform_device.h
对于 dts 生成的 platform_device dev 成员有 device 对象有 of_node 属性
struct platform_device {
        const char *name;
        int id;
        bool id_auto;
        struct device dev; // 父类的成员 名字叫 dev
        u32 num_resources;
        struct resource *resource;
        const struct platform_device_id *id_entry;
        char *driver_override; /* Driver name to force a match */
        /* MFD cell pointer */
        struct mfd_cell *mfd_cell;
        /* arch specific additions */
        struct pdev_archdata archdata;
};
device.h
struct device {
        struct device *parent;
        ....
        /* arch specific additions */
        struct dev_archdata archdata;
        struct device_node *of_node; /* associated device tree node */
        struct fwnode_handle *fwnode; /* firmware device node */
        ......
        dev_t devt; /* dev_t, creates the sysfs "dev" */
        u32 id; /* device instance */
};    
of_node 含有的属性, compatible 最优先比较
匹配过程
搜索 platform_match 函数
static int platform_match(struct device *dev, struct device_driver *drv)
{
        struct platform_device *pdev = to_platform_device(dev);
        struct platform_driver *pdrv = to_platform_driver(drv);
        /* When driver_override is set, only bind to the matching driver */
        if (pdev->driver_override)
                return !strcmp(pdev->driver_override, drv->name);
        /* Attempt an OF style match first */
        if ( of_driver_match_device (dev, drv)) // 使用设备树来匹配
                        return 1;
         /* Then try ACPI style match */
        if (acpi_driver_match_device(dev, drv))
                return 1;
        /* Then try to match against the id table */
        if (pdrv->id_table) // 使用 id_table 匹配
                return platform_match_id(pdrv->id_table, pdev) != NULL;
        /* fall-back to driver name match */
        return (strcmp(pdev->name, drv->name) == 0); // 使用名字匹配
}
案例 : 多个 gpio 引脚设置
修改设备树文件
rk3399-nanopi4-rev07.dts 文件中根节点下添加一个节点 :
gpio-leds {
        compatible = "xyd-leds";
        status = "okay";
        led2-green {//led2-green
                gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; //GPIO_ACTIVE_LOW
                label = "green_led2";
        };
        led2-orange {//led2-orange
                gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>;
                label = "green_led2";
        };
};
烧录后
 
root@SOM-RK3399v2:/sys/firmware/devicetree/base# ls gpio-leds/
compatible led2-green name pinctrl-0 status
led@1 led2-orange phandle pinctrl-names
root@SOM-RK3399v2:/sys/firmware/devicetree/base#       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁静的海2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值