在设备树中描述platform_device

在设备树中描述platform_device

背景

在高通平台做gpio按键配置的时候,根据之前的经验,想从设备树中对应的关键字找到实际的驱动解析实现,以此加深对设备树属性配置的理解。

但是我并没有找到,只是知道在drivers/input/keyboard/gpio_keys.c解析了那一段的配置。翻阅以前自己整理的文章,也没发现一个所以然。

看来,对设备树的学习还是不能停下。

Reference:

介绍

定义:在设备树的dts文件里,带有compatible属性的节点就是表示一个platform_device.

用法:在设备树里增加一个设备节点,在内核里的dts文件里描述设备节点;此后,编写驱动代码完成对其的解析即可。

接口函数

在设备驱动里获取设备树中的设备资源需要一套接口函数来实现:

#include <linux/property.h>

函数以device开头表示读取设备的属性, 以fwnode开头表示读取子节点的属性。

参数propname代表指定要获取值的属性名。

设备属性

判断属性是否存在

bool device_property_present(struct device *dev, const char *propname);

常见用法1:作为指定的属性

    nvmem->read_only = device_property_present(config->dev, "read-only") |
               config->read_only;
	//...;

	if (nvmem->read_only) {
        //...;
    }

    /* Retrieve the PHY configuration properties */
    if (device_property_present(pdata->phy_dev, XGBE_BLWC_PROPERTY)) {
        ret = device_property_read_u32_array(pdata->phy_dev,
                             XGBE_BLWC_PROPERTY,
                             phy_data->blwc,
                             XGBE_SPEEDS);
        if (ret) {
            dev_err(pdata->dev, "invalid %s property\n",
                XGBE_BLWC_PROPERTY);
            return ret;
        }   
    } else {
        memcpy(phy_data->blwc, xgbe_phy_blwc,
               sizeof(phy_data->blwc));
    }

获取整数值

int device_property_read_u8_array(struct device *dev, const char *propname,
                  u8 *val, size_t nval);
int device_property_read_u16_array(struct device *dev, const char *propname,
                   u16 *val, size_t nval);
int device_property_read_u32_array(struct device *dev, const char *propname,
                   u32 *val, size_t nval);
int device_property_read_u64_array(struct device *dev, const char *propname,
                   u64 *val, size_t nval);

nval 代表 返回的数组成员个数。

文本值

获取
int device_property_read_string(struct device *dev, const char *propname,
                const char **val);

注:获取到的值会放进val中;

常见用法:

    device_property_read_string(dev, "label", &pdata->name);


		my_key {
			label = "btn1";
			gpios = <&tlmm 102 0x1>;
	
			linux,input-type = <1>;
			linux,code = <59>;
	
			debounce-interval = <15>;
	
			linux,can-disable;
			gpio-key,wakeup;
		};
判断文本值是否匹配
int device_property_match_string(struct device *dev,
                 const char *propname, const char *string);

例如:

    if (!device_property_match_string(dev, "rotary-encoder,encoding",
                         "binary")) {
        dev_info(dev, "binary");
        encoder->encoding = ROTENC_BINARY;
    } else {
        dev_err(dev, "unknown encoding setting\n");
        return -EINVAL;
    } 

获取子节点

/* 获取个数 */
unsigned int device_get_child_node_count(struct device *dev); //获取设备的子节点个数

/* 获取下一个子节点 */
struct fwnode_handle *device_get_next_child_node(struct device *dev,
                         struct fwnode_handle *child);


子节点属性

fwnode是表示子节点的对象地址,因为和上面的用法类似,因此不再详细说明。

/* 用于获取设备子节点的属性值函数.   */
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
                  const char *propname, u8 *val,
                  size_t nval);
int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
                   const char *propname, u16 *val,
                   size_t nval);
int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
                   const char *propname, u32 *val,
                   size_t nval);
int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
                   const char *propname, u64 *val,
                   size_t nval);
int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
                      const char *propname, const char **val,
                      size_t nval);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
                const char *propname, const char **val);
int fwnode_property_match_string(struct fwnode_handle *fwnode,
                 const char *propname, const char *string);

例子

设备树

举一个例子,如在某个被编译的.dts文件里加入以下内容:

mynodes@77885566 {  /* 则创建出来的platform_device的名为mynodes@77885566  */
    compatible = "mynodes";  /* 设备节点的属性 */
    autorepeat = <1>;

    /* 设备子节点 */
    btn1 {
        label = "btn1";   /* 设备子节点的属性 */ 
        code  = <0x11>;
    };    
    btn2 {
        label = "btn2";
        code  = <0x22>;
    };
};

增加内容后,则重编设备树:

make dtbs ARCH=arm64 CROSS_COMPILE=...

再把编译出来的dtb替换板上所用的dtb文件,重启系统后,可以查看到内容:

# ls /sys/bus/platform/devices/mynodes@77885566/
driver_override  of_node/         subsystem/
modalias         power/           uevent

# ls /sys/bus/platform/devices/mynodes@77885566/of_node/
autorepeat  btn1/       btn2/       compatible  name123456

在dst设备树文件描述设备后就需要与platform_driver进行匹配和驱动了。

驱动代码

用于获取mynodes设备资源的驱动源码:

/* mydrv.c */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>

//产生一个for循环用于检查所有的子节点
#define device_for_each_child_node(dev, child)              \
     for (child = device_get_next_child_node(dev, NULL); child;  \
          child = device_get_next_child_node(dev, child))

int myprobe(struct platform_device *pdev)
{
    struct fwnode_handle *fwhandle;
    const char *str;
    u32 val;

    //获取设备子节点的个数
    printk("child node count : %d\n", device_get_child_node_count(&pdev->dev));
    //获取设备属性autorepeat的值
    printk("%d\n", device_property_read_bool(&pdev->dev, "autorepeat"));

    //遍历设备的每个子节点
    device_for_each_child_node(&pdev->dev, fwhandle) {
        //获取设备子节点的label属性值
        fwnode_property_read_string(fwhandle, "label", &str);
        printk("label = %s\n",  str);
        //获取设备子节点的code属性值
        fwnode_property_read_u32(fwhandle, "code", &val);
        printk("code = %x\n", val);
    };

    return 0;
}

int myremove(struct platform_device *pdev)
{
    printk("in myremove ...\n");
    return 0;
}

struct of_device_id ids[] = {
    {.compatible = "mynodes"},
    {},
};

struct platform_driver mydrv = {
    .probe = myprobe,
    .remove = myremove,

    .driver = {
        .owner = THIS_MODULE,
        .name = "mydrv" ,

        .of_match_table = ids,
    },
};

module_platform_driver(mydrv);
MODULE_LICENSE("GPL");

编译驱动模块加载后的输出结果:

[  111.222065] child node count : 2
[  111.222429] 1
[  111.223054] label = btn1
[  111.223690] code = 11
[  111.224000] label = btn2
[  111.224623] code = 22
posted @ 2020-12-17 14:28  schips  阅读(2110)  评论(0编辑  收藏  举报