基于335X的Linux网口驱动分析

Posted lh03061238

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于335X的Linux网口驱动分析相关的知识,希望对你有一定的参考价值。

基于335X的linux网口驱动分析

 

一、 系统构成

 

1、  硬件平台 AM335X

2、  LINUX内核版本 4.4.12

 

二、 网口驱动构架(mdio部分)

mdio网口驱动部分 使用 总线、设备、驱动模型,总结如下:

 

             

   
   

struct bus_type platform_bus_type = {

   

    .name                      =     "platform",

   

    .dev_groups          = platform_dev_groups,

   

    .match                     = platform_match,

   

    .uevent                    = platform_uevent,

   

    .pm               = &platform_dev_pm_ops,

   

};

   
   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

             

   
   

davinci_mdio: [email protected] {

   

    compatible     = "ti,davinci_mdio";

   

}

   
   

         

   
   

 

   

davinci_mdio_driver

   
   

 

 

 

 

 

 

 

 

 

若设备和驱动匹配,则执行

davinci_mdio_probe

  --> mdiobus_register

   --> device_register

    --> mdiobus_scan

        --> get_phy_device

            --> get_phy_id // 读寄存器

                -->  phy_device_create // 创建phy设备

                --> INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!!!!初始化状态机函数

        --> phy_device_register

 

经过以上步骤,由davinci_mdio_driver又创建出一个mdio_bus_type类型的bus.并扫描并创建了连接在bus上的phydev.

 

             

   
   

struct bus_type mdio_bus_type = {

   

    .name                      =     "mdio_bus",

   

    .match                     = mdio_bus_match,

   

    .pm               = MDIO_BUS_PM_OPS,

   

    .dev_groups          = mdio_dev_groups,

   

};

   
   

         

   
   

davinci_mdio_probe

   

  --> mdiobus_register

   

       --> device_register

   

        --> mdiobus_scan

   

            --> get_phy_device

   

            --> get_phy_id // 读寄存器

   

                -->  phy_device_create // 创建phy设备

   

                -->     INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!!!!初始化状态机函数

   

            --> phy_device_register

   

 

   
   

         

   
   

 

   

 

   

代码路径:drivers/net/phy/phy_device.c

   

phy_init

   

-->     mdio_bus_init 注册mdio总线

   

    -->     class_register(&mdio_bus_class);

   

    -->     bus_register(&mdio_bus_type);

   

-->     phy_driver_register(&genphy_driver);

   

 // 赋值总线、probe等

   

    --> driver_register

   
   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

若设备和驱动匹配,这时,也进行了phydev和phy_driver的匹配和绑定。则执行phy_probe

 

以上总线下的设备和驱动的匹配过程如下,以platform_bus为例,其它总线也是这个流程

 

platform_device跟platform_driver的匹配

 

drivers/base/platform.c

 

a. 注册 platform_driver 的过程:

platform_driver_register

    __platform_driver_register

        drv->driver.probe = platform_drv_probe;

        driver_register

            bus_add_driver

                klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);    // 把 platform_driver 放入 platform_bus_type 的driver链表中

                driver_attach

                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  // 对于plarform_bus_type下的每一个设备, 调用__driver_attach

                        __driver_attach

                            ret = driver_match_device(drv, dev);  // 判断dev和drv是否匹配成功

                                        return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用 platform_bus_type.match

                            driver_probe_device(drv, dev);

                                        really_probe

                                            drv->probe  // platform_drv_probe

                                                platform_drv_probe

                                                    struct platform_driver *drv = to_platform_driver(_dev->driver);

                                                    drv->probe

                           

b. 注册 platform_device 的过程:

platform_device_register

    platform_device_add

        device_add

            bus_add_device

                klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中

            bus_probe_device(dev);

                device_initial_probe

                    __device_attach

                        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // // 对于plarform_bus_type下的每一个driver, 调用 __device_attach_driver

                                    __device_attach_driver

                                        ret = driver_match_device(drv, dev);

                                                    return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用platform_bus_type.match

                                        driver_probe_device

                                           

匹配函数是platform_bus_type.match, 即platform_match,

匹配过程按优先顺序罗列如下:

a. 比较 platform_dev.driver_override 和 platform_driver.drv->name

b. 比较 platform_dev.dev.of_node的compatible属性 和 platform_driver.drv->of_match_table

c. 比较 platform_dev.name 和 platform_driver.id_table

d. 比较 platform_dev.name 和 platform_driver.drv->name

有一个成功, 即匹配成功

 

 

 

CPSW也是一个内部设备,也遵循总线、设备、驱动模型。CPSW设备从设备树中获得,DRV则是系统注册,匹配后进行probe,其主要执行过程如下:

cpsw_probe

--> CPSW平台数据、资源、DMA初始化、赋值netdev_ops、ethtool_ops

--> 注册网络设备:register_netdev

--> cpsw_ndo_open (此处根据实际经验得到,目前还没有从源代码级别解答出调用过程)

--> cpsw_slave_open

    --> phy_connect (传递cpsw_adjust_link)

    --> bus_find_device_by_name (从mdio总线上查找device)

    --> to_phy_device (从device结构体中找到phy_device)

    --> phy_connect_direct (传递handler,初始化 ***)

        --> phy_attach_direct ()

            --> d->driver = &genphy_driver[GENPHY_DRV_1G].driver;(通用驱动赋值,3.17支持1G和10G)

            --> d->driver->probe (这里就调用到Generic PHY的检测函数phy_probe)

            --> device_bind_driver (绑定驱动到设备)

            --> phydev->attached_dev = dev; (有函数使用到attached_dev指针)

            --> phydev->state = PHY_READY; (将PHY状态标记为PHY_READY)

            --> phy_init_hw (硬件级的初始化,最后会调用genphy_config_init)

        --> phy_prepare_link (赋值cpsw_adjust_link为adjust_link)

        --> phy_start_machine (启动PHY状态机 ***)

        --> phy_start_interrupts (经测了,但好像没调用到这里)

--> phy_start(PHY_READY变成PHY_UP)

--> 其它的CPSW的初始化

 

 

 

基于335X的linux网口驱动分析

 

一、 系统构成

 

1、  硬件平台 AM335X

2、  LINUX内核版本 4.4.12

 

二、 网口驱动构架(mdio部分)

mdio网口驱动部分 使用 总线、设备、驱动模型,总结如下:

 

             

   
   

struct bus_type platform_bus_type = {

   

    .name                      =     "platform",

   

    .dev_groups          = platform_dev_groups,

   

    .match                     = platform_match,

   

    .uevent                    = platform_uevent,

   

    .pm               = &platform_dev_pm_ops,

   

};

   
   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

             

   
   

davinci_mdio: [email protected] {

   

    compatible     = "ti,davinci_mdio";

   

}

   
   

         

   
   

 

   

davinci_mdio_driver

   
   

 

 

 

 

 

 

 

 

 

若设备和驱动匹配,则执行

davinci_mdio_probe

  --> mdiobus_register

   --> device_register

    --> mdiobus_scan

        --> get_phy_device

            --> get_phy_id // 读寄存器

                -->  phy_device_create // 创建phy设备

                --> INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!!!!初始化状态机函数

        --> phy_device_register

 

经过以上步骤,由davinci_mdio_driver又创建出一个mdio_bus_type类型的bus.并扫描并创建了连接在bus上的phydev.

 

             

   
   

struct bus_type mdio_bus_type = {

   

    .name                      =     "mdio_bus",

   

    .match                     = mdio_bus_match,

   

    .pm               = MDIO_BUS_PM_OPS,

   

    .dev_groups          = mdio_dev_groups,

   

};

   
   

         

   
   

davinci_mdio_probe

   

  --> mdiobus_register

   

       --> device_register

   

        --> mdiobus_scan

   

            --> get_phy_device

   

            --> get_phy_id // 读寄存器

   

                -->  phy_device_create // 创建phy设备

   

                -->     INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); // !!!!!!初始化状态机函数

   

            --> phy_device_register

   

 

   
   

         

   
   

 

   

 

   

代码路径:drivers/net/phy/phy_device.c

   

phy_init

   

-->     mdio_bus_init 注册mdio总线

   

    -->     class_register(&mdio_bus_class);

   

    -->     bus_register(&mdio_bus_type);

   

-->     phy_driver_register(&genphy_driver);

   

 // 赋值总线、probe等

   

    --> driver_register

   
   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

若设备和驱动匹配,这时,也进行了phydev和phy_driver的匹配和绑定。则执行phy_probe

 

以上总线下的设备和驱动的匹配过程如下,以platform_bus为例,其它总线也是这个流程

 

platform_device跟platform_driver的匹配

 

drivers/base/platform.c

 

a. 注册 platform_driver 的过程:

platform_driver_register

    __platform_driver_register

        drv->driver.probe = platform_drv_probe;

        driver_register

            bus_add_driver

                klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);    // 把 platform_driver 放入 platform_bus_type 的driver链表中

                driver_attach

                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  // 对于plarform_bus_type下的每一个设备, 调用__driver_attach

                        __driver_attach

                            ret = driver_match_device(drv, dev);  // 判断dev和drv是否匹配成功

                                        return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用 platform_bus_type.match

                            driver_probe_device(drv, dev);

                                        really_probe

                                            drv->probe  // platform_drv_probe

                                                platform_drv_probe

                                                    struct platform_driver *drv = to_platform_driver(_dev->driver);

                                                    drv->probe

                           

b. 注册 platform_device 的过程:

platform_device_register

    platform_device_add

        device_add

            bus_add_device

                klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中

            bus_probe_device(dev);

                device_initial_probe

                    __device_attach

                        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // // 对于plarform_bus_type下的每一个driver, 调用 __device_attach_driver

                                    __device_attach_driver

                                        ret = driver_match_device(drv, dev);

                                                    return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用platform_bus_type.match

                                        driver_probe_device

                                           

匹配函数是platform_bus_type.match, 即platform_match,

匹配过程按优先顺序罗列如下:

a. 比较 platform_dev.driver_override 和 platform_driver.drv->name

b. 比较 platform_dev.dev.of_node的compatible属性 和 platform_driver.drv->of_match_table

c. 比较 platform_dev.name 和 platform_driver.id_table

d. 比较 platform_dev.name 和 platform_driver.drv->name

有一个成功, 即匹配成功

 

 

 

CPSW也是一个内部设备,也遵循总线、设备、驱动模型。CPSW设备从设备树中获得,DRV则是系统注册,匹配后进行probe,其主要执行过程如下:

cpsw_probe

--> CPSW平台数据、资源、DMA初始化、赋值netdev_ops、ethtool_ops

--> 注册网络设备:register_netdev

--> cpsw_ndo_open (此处根据实际经验得到,目前还没有从源代码级别解答出调用过程)

--> cpsw_slave_open

    --> phy_connect (传递cpsw_adjust_link)

    --> bus_find_device_by_name (从mdio总线上查找device)

    --> to_phy_device (从device结构体中找到phy_device)

    --> phy_connect_direct (传递handler,初始化 ***)

        --> phy_attach_direct ()

            --> d->driver = &genphy_driver[GENPHY_DRV_1G].driver;(通用驱动赋值,3.17支持1G和10G)

            --> d->driver->probe (这里就调用到Generic PHY的检测函数phy_probe)

            --> device_bind_driver (绑定驱动到设备)

            --> phydev->attached_dev = dev; (有函数使用到attached_dev指针)

            --> phydev->state = PHY_READY; (将PHY状态标记为PHY_READY)

            --> phy_init_hw (硬件级的初始化,最后会调用genphy_config_init)

        --> phy_prepare_link (赋值cpsw_adjust_link为adjust_link)

        --> phy_start_machine (启动PHY状态机 ***)

        --> phy_start_interrupts (经测了,但好像没调用到这里)

--> phy_start(PHY_READY变成PHY_UP)

--> 其它的CPSW的初始化

 

以上是关于基于335X的Linux网口驱动分析的主要内容,如果未能解决你的问题,请参考以下文章

基于335X平台Linux交换芯片驱动开发

linux3.2 spi框架分析

am335x linux 网口配置

AM335X有关MMC的启动参数问题分析

「技术干货」工业触摸屏之驱动开发及异常分析(连载)

am335x Lan8710a 双网口配置