PHY芯片 AR8033驱动代码分析

版权声明:本文为博主原创文章,若要转载请注明出处。 https://blog.csdn.net/Qidi_Huang/article/details/51398729

【思路描述】

    想要在Linux上使用AR8033需要做2部分工作,一是编写设备驱动并将设备驱动注册到内核,二是创建设备通信要使用的mdio总线并将设备注册到总线上。关于 AR8033 芯片的介绍可以看我的这篇博文《PHY芯片 AR8033学习笔记》


【代码分析】

a)  驱动注册流程:

    文件mdio_gpio.c 是 mdio_gpio 模块的代码所在。在模块加载函数 mdio_gpio_init() 中通过语句 ret = platform_driver_register(&mdio_gpio_driver) 将mdio 驱动注册为“平台设备驱动”,其中mdio_gpio_driver 是一个 platform_driver 结构体,初始化代码如下:

static struct platform_driver mdio_gpio_driver= {
       .probe= mdio_gpio_probe,    // 关联设备的probe函数
       .remove= __devexit_p(mdio_gpio_remove),    // 关联设备的remove函数
       .driver            = {
              .name      = "mdio-gpio",    // 驱动名
              .owner    = THIS_MODULE,
       },
};

    而在 platform_driver_register() 这个函数中,则进一步将驱动总线类型设定为 platform_bus_type,以及关联驱动的probe()函数、remove()函数和shutdown()函数。其代码细节如下:

int platform_driver_register(structplatform_driver *drv)
{
       drv->driver.bus= &platform_bus_type;
       if(drv->probe)
              drv->driver.probe = platform_drv_probe;    // 注意,这是drv->driver.probe
       if(drv->remove)
              drv->driver.remove= platform_drv_remove;    // 关联drv->driver.remove
       if(drv->shutdown)
              drv->driver.shutdown= platform_drv_shutdown;    // 关联drv->driver.shutdown
       return driver_register(&drv->driver);
}

    可以看到,对于所有驱动而言初始化到这一步时都会指向probe()、remove()、shutdown()这3个函数,这3个函数分别用来返回“平台设备驱动”的probe()、remove()、shutdown()函数。代码如下:

static int platform_drv_probe(struct device*_dev)
{
       struct platform_driver *drv = to_platform_driver(_dev->driver);
       struct platform_device *dev = to_platform_device(_dev);
       return drv->probe(dev);
}

    而在platform_driver_register()函数结尾的 return 语句中,再调用driver_register(&drv->driver) 进一步对驱动进行注册。通过语句driver_find(drv->name, drv->bus)查找总线上是否已经注册过该驱动,若没有则使用语句bus_add_driver(drv) 将驱动添加到总线中。至此,驱动注册流程结束。代码如下:

int driver_register(struct device_driver*drv)
{
       int ret;
       struct device_driver *other;
       BUG_ON(!drv->bus->p);    // 打开调试
   // 做一些检测
       if((drv->bus->probe && drv->probe) ||
           (drv->bus->remove && drv->remove) ||
           (drv->bus->shutdown && drv->shutdown))
              printk(KERN_WARNING "Driver '%s' needs updating - please use "
                     "bus_typemethods\n", drv->name);
 
       other = driver_find(drv->name, drv->bus);    // 在总线中查找是否已经注册该驱动
       if(other) {
              printk(KERN_ERR "Error: Driver '%s' is already registered, "
                     "aborting...\n",drv->name);
              return -EBUSY;
       }
       ret = bus_add_driver(drv);   // 将驱动添加到总线
       if(ret)
              return ret;
       ret= driver_add_groups(drv, drv->groups);
       if(ret)
              bus_remove_driver(drv);
       return ret;
}

 

b)  设备注册流程:

    驱动注册流程的末尾会调用驱动的 probe() 函数,即 mdio_gpio_probe() 函数。在该函数中,通过语句new_bus = mdio_gpio_bus_init(&pdev->dev, pdata,pdev->id) 初始化一个 mdio 总线设备,在使用语句 mdiobus_register(new_bus)对该设备总线进行注册。代码如下:

static int __devinit mdio_gpio_probe(structplatform_device *pdev)
{
       struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data;
       struct mii_bus *new_bus;
       intret;
       if(!pdata)
              return-ENODEV;
       new_bus = mdio_gpio_bus_init(&pdev->dev, pdata,pdev->id);
       if(!new_bus)
              return -ENODEV;
       ret = mdiobus_register(new_bus);
       if(ret)
              mdio_gpio_bus_deinit(&pdev->dev);
       return ret;
}

    进入函数 mdiobus_register() 查看代码,内容可以分为2部分。一是对 mdio 总线设备进行真正的注册,二是注册成功后,在总线上根据 phy_mask搜索 PHY 设备。代码如下:

int mdiobus_register(struct mii_bus *bus)
{
       int i, err;
       if(NULL == bus || NULL == bus->name ||
                     NULL== bus->read ||
                     NULL== bus->write)
              return-EINVAL;
       BUG_ON(bus->state!= MDIOBUS_ALLOCATED &&
              bus->state != MDIOBUS_UNREGISTERED);
       bus->dev.parent= bus->parent;
       bus->dev.class= &mdio_bus_class;
       bus->dev.groups= NULL;
       dev_set_name(&bus->dev,"%s", bus->id);
       err = device_register(&bus->dev);    // 注册 mdio总线设备
       if(err) {
              printk(KERN_ERR "mii_bus %s failed to register\n", bus->id);
              return -EINVAL;
       }
       mutex_init(&bus->mdio_lock);
       if(bus->reset)
              bus->reset(bus);
       for(i = 0; i < PHY_MAX_ADDR; i++) {
              if((bus->phy_mask & (1 << i)) == 0) {
                     structphy_device *phydev;
                     phydev = mdiobus_scan(bus, i);    // 在总线上搜索 phy 设备
                     if(IS_ERR(phydev)) {
                            err = PTR_ERR(phydev);
                            goto error;
                     }
              }
       }
       bus->state= MDIOBUS_REGISTERED;
…   // 以下省略
}

    进入函数 mdiobus_scan() 查看代码,可以看到在该函数中使用 phydev = get_phy_device(bus, addr) 语句从总线设备上获取到 phy 设备,然后通过语句phy_device_register(phydev) 对phy 设备进行注册。至此,设备注册流程结束。代码如下:

struct phy_device *mdiobus_scan(structmii_bus *bus, int addr)
{
       struct phy_device *phydev;
       struct mdio_board_entry *be;
       int err;
 
       phydev = get_phy_device(bus, addr);    // 从总线设备上获取 phy 设备
       if(IS_ERR(phydev) || phydev == NULL)
              returnphydev;
       mutex_lock(&__mdio_board_lock);
       list_for_each_entry(be,&__mdio_board_list, list)
              mdiobus_setup_phydev_from_boardinfo(bus,phydev,
                                              &be->board_info);
       mutex_unlock(&__mdio_board_lock);
       err = phy_device_register(phydev);    // 将 phy 设备注册到内核
       if(err) {
              phy_device_free(phydev);
              return NULL;
       }
       return phydev;
}


猜你喜欢

转载自blog.csdn.net/Qidi_Huang/article/details/51398729
PHY