基于 dts 的平台设备驱动函数简单介绍

前言

以前基于 3.18 追的的 s3c24xx 系列的流程,因为对这个芯片熟。。。

本文解析的设备树:

    ///////////////////////////////////////////////////////////////////////////
    //      // 串口模块
    //      // s3c24xx.dtsi
    //      uart0: serial@50000000 {
    //          compatible = "samsung,s3c2410-uart";
    //          reg = <0x50000000 0x4000>;              // ULCON0 0x50000000
    //          interrupts = <1 28 0 4>, <1 28 1 4>;
    //          status = "disabled";
    //      };    
    //      
    //      // s3c2416.dtsi    
    //      serial@50000000 {                           // ULCON0 0x50000000
    //          compatible = "samsung,s3c2440-uart";
    //          clock-names = "uart", "clk_uart_baud2",
    //                  "clk_uart_baud3";
    //          clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,
    //                  <&clocks SCLK_UART>;
    //      }; 

dts 注册平台设备

// Setup.c (z:\work\e266l_cmcc\kernel-3.18\arch\arm64\kernel)
    arch_initcall_sync(arm64_device_init);
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
            // 遍历根节点的每个一次子节点
            for_each_child_of_node(root, child)
                rc = of_platform_bus_create(child, matches, lookup, parent, true);
                    // 【核心】如果不是ARM Primecell Peripherals,那么我们就需要向 platform bus 上增加一个 platform device 了。
                    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
                            ///////////////////////////////////////////////////////////////////////
                            // of_device_alloc 除了分配 struct platform_device 的内存,还分配了该 platform device 需要
                            // 的 resource 的内存。当然,这就需要解析该 device node 的 interrupt 资源以及 memory address
                            // 资源
                            /////////////////////////////////////////////
                            ///////////////////////////////////////////////////////////////////////////
                            //      // 串口模块
                            //      // s3c24xx.dtsi
                            //      uart0: serial@50000000 {
                            //          compatible = "samsung,s3c2410-uart";
                            //          reg = <0x50000000 0x4000>;              // ULCON0 0x50000000
                            //          interrupts = <1 28 0 4>, <1 28 1 4>;
                            //          status = "disabled";
                            //      };    
                            //      
                            //      // s3c2416.dtsi    
                            //      serial@50000000 {                           // ULCON0 0x50000000
                            //          compatible = "samsung,s3c2440-uart";
                            //          clock-names = "uart", "clk_uart_baud2",
                            //                  "clk_uart_baud3";
                            //          clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,
                            //                  <&clocks SCLK_UART>;
                            //      }; 
                            dev = of_device_alloc(np, bus_id, parent);
                                        ///////////////////////////////////////////////////////////
                                        // 分配 platform_device 设备
                                        dev = platform_device_alloc("", -1);
                                        ///////////////////////////////////////////////////////////
                                        // 计算 io/irq 资源数量
                                        //      of_address_to_resource(): 解析 reg 字段,返回的资源保存在 temp_res 中 
                                        while (of_address_to_resource(np, num_reg, &temp_res) == 0)

                                        // 解析中断 = 0?    
                                        num_irq = of_irq_count(np);

                                        # 填充平台设备的资源表:中断、内存
                                        if (num_irq || num_reg) 
                                            // 进行 HW 硬件中断号与 IRQ 中断号映射
                                            of_irq_to_resource_table(np, res, num_irq)
                                                。。。 
                                                domain->ops->xlate(domain, irq_data->np, irq_data->args,irq_data->args_count, &hwirq, &type)
                                                    // Irq-s3c24xx.c (kernel-3.18\drivers\irqchip)
                                                    ###############################################################################################
                                                    # xlate 函数就是将指定的设备(node参数)上若干个(intsize参数)中断属性(intspec参数)翻译成HW
                                                    # interrupt ID(out_hwirq参数)和trigger类型(out_type)
                                                    // 例: 
                                                    //  uart0{
                                                    //      ...
                                                    //      interrupts = <1 28 0 4>, <1 28 1 4>;
                                                    //      解析方式:
                                                    //              1: 次级中断控制器 SUBSRCPND 
                                                    //              28: 位于主控制器 SRCPND[28]
                                                    //              0/1: 位于 SUBSRCPND[0-1]
                                                    //  }             
                                                    // 我们已经通过 compatible 属性找到了适合的 interrupt controller,那么如何解析 reg 属性呢?我们知道,对于
                                                    // s3c2416 的 interrupt controller 而言,其 #interrupt-cells 的属性值是 4,定义为。每个域的解释如下:
                                                    //     (1)ctrl_num 表示使用哪一种类型的 interrupt controller,其值的解释如下:
                                                    //         - 0 ... main controller
                                                    //         - 1 ... sub controller
                                                    //         - 2 ... second main controller
                                                    //     (2)parent_irq。对于 sub controller,parent_irq 标识了其在 main controller 的 bit position。
                                                    //     (3)ctrl_irq 标识了在 controller 中的 bit 位置。
                                                    //     (4)type 标识了该中断的 trigger type,例如:上升沿触发还是电平触发。
                                                    s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,const u32 *intspec, unsigned int intsize,irq_hw_number_t *out_hwirq, unsigned int *out_type)
                                                        // 
                                                        // Array holding pointers to the global controller structs
                                                        // [0] ... main_intc
                                                        // [1] ... sub_intc
                                                        // [2] ... main_intc2 on s3c2416
                                                        // 
                                                        // static struct s3c_irq_intc *s3c_intc[3];
                                                        intc = s3c_intc[intspec[0]];

                                                        // 将 DTS 中的配置转换为硬件中断号
                                                        *out_hwirq = intspec[0] * 32 + intspec[2];
                                                        
                                                        // 转换中断电平:
                                                        //     IRQ_TYPE_NONE        = 0x00000000,
                                                        //     IRQ_TYPE_EDGE_RISING = 0x00000001,
                                                        //     IRQ_TYPE_EDGE_FALLING    = 0x00000002,
                                                        //     IRQ_TYPE_EDGE_BOTH   = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
                                                        //     IRQ_TYPE_LEVEL_HIGH  = 0x00000004,
                                                        //     IRQ_TYPE_LEVEL_LOW   = 0x00000008,
                                                        //     IRQ_TYPE_LEVEL_MASK  = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
                                                        *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;


                                                。。。
                                                // 注册到域中,中断域可参考 DTS 在 s3c2416 中有   
                                                // 创建HW interrupt ID和IRQ number的映射关系
                                                virq = irq_create_mapping(domain, hwirq);  
                                                    # 调用irq domain的map callback函数
                                                    ret = domain->ops->map(domain, virq, hwirq);   
                                                        // Irq-s3c24xx.c (kernel-3.18\drivers\irqchip)
                                                        s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,irq_hw_number_t hw)      
                                                            irq_set_chip_and_handler(virq, &s3c_irq_level_chip,handle_edge_irq);
                                                                ////////////////////////////////////////////////////////////////////////////
                                                                irq_set_chip_and_handler_name(irq, chip, handle, NULL);
                                                                        irq_set_chip(irq, chip);
                                                                            desc->irq_data.chip = chip;
                                                                        __irq_set_handler(irq, handle, 0, name);
                                                                            desc->handle_irq = handle;




                            # DMA 操作 
                            of_dma_configure(&dev->dev);          
                                使用连续的 DMA 分配需求, 设置平台的 DMA 操作函数 */
                                if (of_dma_is_coherent(dev->of_node)) {
                                    // Dma-mapping.h (kernel-3.18\arch\arm\include\asm)
                                    set_arch_dma_coherent_ops(dev);
                                            // struct dma_map_ops arm_coherent_dma_ops = {
                                            //     .alloc           = arm_coherent_dma_alloc,
                                            //     .free            = arm_coherent_dma_free,
                                            //     .mmap            = arm_dma_mmap,
                                            //     .get_sgtable     = arm_dma_get_sgtable,
                                            //     .map_page        = arm_coherent_dma_map_page,
                                            //     .map_sg          = arm_dma_map_sg,
                                            //     .set_dma_mask        = arm_dma_set_mask,
                                            // };
                                            set_dma_ops(dev, &arm_coherent_dma_ops);              

平台资源获取

// 获得 IRQ 中断号,此时已经经过了 DTS ==》 HW ID => IRQ 的绑定了
platform_get_irq(struct platform_device *dev, unsigned int num)
        // 查找映射
        ret = of_irq_get(dev->dev.of_node, num);
                    rc = of_irq_parse_one(dev, index, &oirq);
                    if (rc)
                        return rc;
                    // 如果没有则手动映射下
                    domain = irq_find_host(oirq.np);
                    return irq_create_of_mapping(&oirq);
        
        // 获得资源,设置中断电平
        r = platform_get_resource(dev, IORESOURCE_IRQ, num);
        if (r && r->flags & IORESOURCE_BITS)
            irqd_set_trigger_type(irq_get_irq_data(r->start),r->flags & IORESOURCE_BITS);

platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)  
        for (i = 0; i < dev->num_resources; i++) {
            struct resource *r = &dev->resource[i];

            if (type == resource_type(r) && num-- == 0)
                return r;
        }

platform_bus 总线注册

//Main.c (z:\work\e266l_cmcc\kernel-3.18\init)
    start_kernel(void)
        rest_init(void)
            kernel_thread(kernel_init, NULL, CLONE_FS);
                kernel_init(void *unused)
                    kernel_init_freeable(void)
                        do_basic_setup(void)    
                            driver_init()
                                /////////////////////////
                                // 创建 sys 下的目录节点
                                devices_init();
                                    devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
                                    dev_kobj = kobject_create_and_add("dev", NULL);
                                    sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
                                    sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
                                buses_init();
                                    bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
                                    system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
                                ///////////////////////////////
                                // 初始化平台总线
                                platform_bus_init();
                                    /////////////////////////////////////////
                                    // 仅仅是注册了一个总线的设备,因为此时还
                                    // 没有总线,所以很快返回了
                                    //  struct device platform_bus = {
                                    //      .init_name	= "platform",
                                    //  };
                                    error = device_register(&platform_bus);
                                                    device_initialize(dev);
                                                    return device_add(dev);
                                                    
                                    //////////////////////////////////////////
                                    // 注册总线,主要是创建了一系统 /sys 下目录
                                    // struct bus_type platform_bus_type = {
                                    //      .name		= "platform",
                                    //      .dev_groups	= platform_dev_groups,
                                    //      .match		= platform_match,
                                    //      .uevent		= platform_uevent,
                                    //      .pm		= &platform_dev_pm_ops,
                                    //  };
                                    error =  bus_register(&platform_bus_type);
                                                priv->subsys.kobj.kset = bus_kset;
                                                priv->subsys.kobj.ktype = &bus_ktype;
                                                priv->drivers_autoprobe = 1;
                                                retval = kset_register(&priv->subsys);
                                                retval = bus_create_file(bus, &bus_attr_uevent);
                                                priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);
                                                priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
                                                retval = add_probe_files(bus);
                                                            retval = bus_create_file(bus, &bus_attr_drivers_probe);
                                                            retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);


# platform device 注册流程

// Platform.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
platform_device_register(struct platform_device *pdev)
    ret = platform_device_add(pdev);
                pdev->dev.bus = &platform_bus_type;
                // 从资源树中分配资源
                for (i = 0; i < pdev->num_resources; i++)
                    struct resource *p, *r = &pdev->resource[i];
                    if (resource_type(r) == IORESOURCE_MEM)
                        p = &iomem_resource;
                    else if (resource_type(r) == IORESOURCE_IO)
                        p = &ioport_resource;
                     insert_resource(p, r)

                ret = device_add(&pdev->dev);
                            kobject_uevent(&dev->kobj, KOBJ_ADD);
                            bus_probe_device(dev);
                                    if (bus->p->drivers_autoprobe) {
                                        ret = device_attach(dev);
                                                        ///////////////////////////////
                                                        // 如果设备已经绑定了驱动
                                                        if (dev->driver)
                                                            klist_node_attached(&dev->p->knode_driver)
                                                            ret = device_bind_driver(dev);
                                                        //////////////////////////////
                                                        // 如果当前设备没有绑定驱动
                                                        ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
                                                                            //////////////////////////////////////////////////////////
                                                                            // Dd.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
                                                                            __device_attach(struct device_driver *drv, void *data)
                                                                                    /////////////////////////////////////////////////////
                                                                                    // 检查能不能匹配上:
                                                                                    //      1. 检查 dts 匹配 
                                                                                    //      2. 根据 id_table 匹配 
                                                                                    //      3. 根据名称匹配
                                                                                    if (!driver_match_device(drv, dev))return 0;
                                                                                                return drv->bus->match ? drv->bus->match(dev, drv) : 1;
                                                                                                //////////////////////////////////////////////////////
                                                                                                // 调用总线匹配函数
                                                                                                platform_match(struct device *dev, struct device_driver *drv)
                                                                                                        ////////////////////////////////////////////
                                                                                                        // 匹配 dts 中是否有相应项
                                                                                                        if (of_driver_match_device(dev, drv))return 1;
                                                                                                                    return of_match_device(drv->of_match_table, dev) != NULL;
                                                                                                                                    return of_match_node(matches, dev->of_node);  
                                                                                                                                                    match = __of_match_node(matches, node);
                                                                                                                                                                    for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++)   
                                                                                                                                                                            score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name);
                                                                                                                                                                                                if (compat && compat[0])
                                                                                                                                                                                                    prop = __of_find_property(device, "compatible", NULL);
                                                                                                        //////////////////////////////////////////////
                                                                                                        // 根据 id_table 匹配
                                                                                                        if (pdrv->id_table)
                                                                                                            return platform_match_id(pdrv->id_table, pdev) != NULL;
                                                                                                        
                                                                                                        ////////////////////////////////////////////////
                                                                                                        // 根据名称进行匹配
                                                                                                        return (strcmp(pdev->name, drv->name) == 0);
                                                                                    
                                                                                    /////////////////////////////////////////////////////////
                                                                                    // 如果有,首先调用总线的 probe(), 否则调用匹配的驱动的 probe()
                                                                                    return driver_probe_device(drv, dev);
                                                                                            ret = really_probe(dev, drv);
                                                                                                        ret = pinctrl_bind_pins(dev);
                                                                                                        //////////////////////////////////////
                                                                                                        // 调用总线的 probe()
                                                                                                        if (dev->bus->probe)
                                                                                                            ret = dev->bus->probe(dev);
                                                                                                        //////////////////////////////////////
                                                                                                        // 调用驱动的 probe()
                                                                                                        else if (drv->probe)
                                                                                                            ret = drv->probe(dev);
                                                                            
                                       
                                    }
                                    list_for_each_entry(sif, &bus->p->interfaces, node)
                                        if (sif->add_dev)
                                            sif->add_dev(dev, sif);

platform drvier 注册流程

platform_driver_register(drv)                           
    __platform_driver_register(drv, THIS_MODULE)                       
                drv->driver.bus = &platform_bus_type;
                if (drv->probe)
                    drv->driver.probe = platform_drv_probe;
                if (drv->remove)
                    drv->driver.remove = platform_drv_remove;
                if (drv->shutdown)
                    drv->driver.shutdown = platform_drv_shutdown;                    
                        
                return driver_register(&drv->driver);
                            ret = bus_add_driver(drv);
                                        if (drv->bus->p->drivers_autoprobe) {
                                            error = driver_attach(drv);
                                                            return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
                                                                                //////////////////////////////////////////////////////////////////
                                                                                // Dd.c (z:\work\e266l_cmcc\kernel-3.18\drivers\base)
                                                                                __driver_attach(struct device *dev, void *data)
                                                                                        /////////////////////////////////////////////////////
                                                                                        // 检查能不能匹配上:
                                                                                        //      1. 检查 dts 匹配 
                                                                                        //      2. 根据 id_table 匹配 
                                                                                        //      3. 根据名称匹配
                                                                                        if (!driver_match_device(drv, dev))return 0;
                                                                                        
                                                                                        /////////////////////////////////////////////////////////
                                                                                        // 如果有,首先调用总线的 probe(), 否则调用匹配的驱动的 probe()
                                                                                        if (!dev->driver)
                                                                                            driver_probe_device(drv, dev);
                                                                                                    ret = really_probe(dev, drv);
                                                                                
                                        }
                            kobject_uevent(&drv->p->kobj, KOBJ_ADD);

猜你喜欢

转载自blog.csdn.net/wangjun7121/article/details/88183757
DTS