设备树之创建platform设备

前言

在以前我们都是显示的添加平台设备信息,在引入设备树机制后,设备的相关信息都用设备树的方式来表示。内核或者驱动通过of系列函数来获取到这些信息。然后在内核中用链表的形式组织起来。

相关的结构体

属性:

struct property {
        char    *name;                  
        int     length;
        void    *value;	//重点
        struct property *next;          
        unsigned long _flags;           
        unsigned int unique_id;         
        struct bin_attribute attr;      
};

设备节点:

struct device_node {                    
        const char *name;
        const char *type;               
        phandle phandle;
        const char *full_name;
        struct fwnode_handle fwnode;    

        struct  property *properties;   
        struct  property *deadprops;    /* removed properties */
        struct  device_node *parent;    
        struct  device_node *child;     
        struct  device_node *sibling;   
        struct  kobject kobj;           
        unsigned long _flags;           
        void    *data;
#if defined(CONFIG_SPARC)
        const char *path_component_name;
        unsigned int unique_id;
        struct of_irq_controller *irq_trans;
#endif 
};

平台设备:

struct platform_device {
        const char      *name;
        int             id;
        bool            id_auto;
        struct device   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;
};

函数调用过程

开发板创建platform设备过程

设备树板级启动:

DT_MACHINE_START(IMX6UL, "Freescale i.MX6 UltraLite (Device Tree)")
        .map_io         = imx6ul_map_io,
        .init_irq       = imx6ul_init_irq,
        .init_machine   = imx6ul_init_machine,
        .init_late      = imx6ul_init_late,
        .dt_compat      = imx6ul_dt_compat,
MACHINE_END

板级设备的初始化,其中会调用和平台设备相关的这个函数:of_platform_default_populate()。

static void __init imx6ul_init_machine(void)
{
        struct device *parent;

        parent = imx_soc_device_init();
        if (parent == NULL)
                pr_warn("failed to initialize soc device\n");

        of_platform_default_populate(NULL, NULL, parent);
        imx6ul_enet_init();
        imx_anatop_init();
        imx6ul_pm_init();
}

在下面的包裹函数中会调用创建平台设备的开始函数。

int of_platform_default_populate(struct device_node *root,
                                 const struct of_dev_auxdata *lookup,
                                 struct device *parent)
{
        return of_platform_populate(root, of_default_bus_match_table, lookup,
                                    parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);

在下面的函数中会遍历root节点下的所有子节点,分别调用of_platform_bus_create()来创建平台设备。

drivers/of/platform.c:
int of_platform_populate(struct device_node *root,
                        const struct of_device_id *matches,      
                        const struct of_dev_auxdata *lookup,     
                        struct device *parent)                   
{
		...
        for_each_child_of_node(root, child) {    
                rc = of_platform_bus_create(child, matches, lookup, parent, true);
                if (rc) {
                        of_node_put(child);                      
                        break;          
                }
        }
		...
}

核心函数,这个函数的主要作用是为节点和它的子节点创建设备。

static int of_platform_bus_create(struct device_node *bus,
                                  const struct of_device_id *matches,
                                  const struct of_dev_auxdata *lookup,
                                  struct device *parent, bool strict)
{
		...
        /* Make sure it has a compatible property *
        /* 检查属性,上面的注释很明确的说明了需要有“compatible”属性。*/
        if (strict && (!of_get_property(bus, "compatible", NULL))) {			
                pr_debug("%s() - skipping %pOF, no compatible prop\n",
                         __func__, bus);
                return 0;
        }
        
		/* 2.填充相关信息 */
        auxdata = of_dev_lookup(lookup, bus);
        if (auxdata) {
                bus_id = auxdata->name;
                platform_data = auxdata->platform_data;
        }
        
		/* 3.创建平台设备 */
        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        
		/* 4.递归调用本函数为子节点创建平台设备 */
        for_each_child_of_node(bus, child) {
                rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
			...
        }
		...
}

函数调用过程简览:

of_platform_populate(){
	-->of_platform_bus_create()
		-->of_platform_device_create_pdata()
			-->of_device_alloc()
			-->of_msi_configure()
			-->of_device_add()
				-->device_add()
		-->of_platform_bus_create()
}

对比设备树和设备信息

首先查看设备树中关于lcd接口的信息:
在这里插入图片描述
再查看sysfs文件系统下面的platform的信息:
在这里插入图片描述
然后再查看lcdif的的设备树节点信息:
在这里插入图片描述
最后查看下clock-names,发现和我们的设备树信息是一致的。
在这里插入图片描述

总结

  • 在设备树中描述的平台设备信息,会在内核代码中自动展开,生成对应的平台设备信息,添加到设备链表中。
  • 自动转换成平台设备的条件:
    该节点必须含有compatible属性
    根节点的子节点(节点必须含有compatible属性),也即根节点下含有compatible属性的子节点。
    含有特殊compatible属性的节点的子节点(子节点必须含有compatible属性): 这些特殊的compatilbe属性为: “simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”
  • 不转化的条件:
    1.根节点下子节点,没有上边的那4种属性,那么该子节点的子节点都不转化。
    2.总线I2c,SPI节点下的子节点,由对应的总线驱动程序处理,不转化。
    3.根节点是例外的,生成platfrom_device时,即使有compatible属性也不会处理。
    注意:i2c, spi等总线节点会转换为platform_device,但是,spi、i2c下的子节点无论compatilbe是否为: “simple-bus”,“simple- mfd”,“isa”,"arm,amba-bus "都应该交给对应的总线驱动程序来处理而不会被转换为platform_device。

测试代码

/* 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");

参考

1.参考博文

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值