mdio总线扫描phy设备和phy驱动注册

1、mdio 类型的bus总线注册

phy_init
    mdio_bus_init

struct bus_type mdio_bus_type = {
	.name		= "mdio_bus",
	.match		= mdio_bus_match,
	.pm		= MDIO_BUS_PM_OPS,
};
EXPORT_SYMBOL(mdio_bus_type);   //miibus就是属于这种类型的总线

int __init mdio_bus_init(void)
{
	int ret;

	ret = class_register(&mdio_bus_class);
	if (!ret) {
		ret = bus_register(&mdio_bus_type);
		if (ret)
			class_unregister(&mdio_bus_class);
	}
	return ret;
}

2、mii_bus 总线注册和phy设备扫描

include/linux/phy.h
#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)

__mdiobus_register 
   mdiobus_scan
       get_phy_device     
          phy_device_create 
          phy_device_register
get_phy_device: 获取phy的id信息,里面调用bus的read函数,读取phy寄存器的id信息。
扫描到一个设备以后调用phy_device_create和phy_device_register创建和注册一个设备(有id信息)//顺便初始化mdiodev数据
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
                                     bool is_c45,
                                     struct phy_c45_device_ids *c45_ids)
{
        struct phy_device *dev;
        struct mdio_device *mdiodev;
		
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return ERR_PTR(-ENOMEM);

        mdiodev = &dev->mdio;
        mdiodev->dev.release = phy_device_release;
        mdiodev->dev.parent = &bus->dev;
        mdiodev->dev.bus = &mdio_bus_type;
        mdiodev->bus = bus;
        mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS;
        mdiodev->bus_match = phy_bus_match;
        mdiodev->addr = addr;
        mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
        mdiodev->device_free = phy_mdio_device_free;
        mdiodev->device_remove = phy_mdio_device_remove;
        ....
}

3、设备和driver的数据包含关系:

phy_dev {
    mdiodev {
	    bus
	}
} 
struct mdio_device {
        struct device dev;
        const struct dev_pm_ops *pm_ops;
        struct mii_bus *bus;
        int (*bus_match)(struct device *dev, struct device_driver *drv);
        void (*device_free)(struct mdio_device *mdiodev);
        void (*device_remove)(struct mdio_device *mdiodev);  
        /* Bus address of the MDIO device (0-31) */
        int addr;
        int flags;
};
struct phy_device {
        struct mdio_device mdio;
        struct phy_driver *drv;
        u32 phy_id;
        phy_interface_t interface;
        ....
}
struct mii_bus {
        struct module *owner;
        const char *name;
        char id[MII_BUS_ID_SIZE];
        void *priv;
        int (*read)(struct mii_bus *bus, int addr, int regnum);
        int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
        int (*reset)(struct mii_bus *bus);
		....
}
==>

phy_driver {
    struct mdio_driver_common mdiodrv;
    u32 phy_id;
	....
}
struct mdio_driver_common {
        struct device_driver driver;
        int flags;
};  

struct device_driver {  
        const char              *name;
        struct bus_type         *bus;   //这个
        
        struct module           *owner;
}

4、流程详细介绍

总线注册和设备创建流程:
1、调用 mdiobus_alloc 分配一个mii_bus数据结构。
2、填充mii_bus数据,包括read/write函数。
3、mdiobus_register注册mii_bus总线,探测设备并注册一个phy设备,
这个phy设备记录了phy的id信息。

设备驱动注册和加载:

module_phy_driver
#define module_phy_driver(__phy_drivers)                                \
        phy_module_driver(__phy_drivers, ARRAY_SIZE(__phy_drivers))
#define phy_module_driver(__phy_drivers, __count)                       \
static int __init phy_module_init(void)                                 \
{                                                                       \
        return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
} 

phy_drivers_register
    phy_driver_register
	
//顺便初始化mdiodrv数据和bus数据
int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{       
        int retval;
        new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
        new_driver->mdiodrv.driver.name = new_driver->name;
        new_driver->mdiodrv.driver.bus = &mdio_bus_type;  //这个就是上面的mdio_bus_type
        new_driver->mdiodrv.driver.probe = phy_probe;
        new_driver->mdiodrv.driver.remove = phy_remove;
        new_driver->mdiodrv.driver.owner = owner;
        retval = driver_register(&new_driver->mdiodrv.driver);
        if (retval) {
                return retval;
        }
        return 0;
}        
new_driver -> device_driver(mdiodrv)->bus_type

注册phy驱动,将触发驱动加载过程。

phy_drivers_register
    phy_driver_register
	    driver_register
		   bus_add_driver
              driver_attach
                  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) {
				      klist_iter_init_node(&bus->p->klist_devices, &i,
                             (start ? &start->p->knode_bus : NULL));
                      while ((dev = next_device(&i)) && !error)
					  /* 循环找到phy设备 */
                      error = __driver_attach(dev, data);
				  
				  }

static int __driver_attach(struct device *dev, void *data)
{
        struct device_driver *drv = data;
        int ret;

        ret = driver_match_device(drv, dev);//这里
        if (ret == 0) {
                /* no match */
                return 0;
        } else if (ret == -EPROBE_DEFER) {
                dev_dbg(dev, "Device match requests probe deferral\n");
                driver_deferred_probe_add(dev);
        } else if (ret < 0) {
                dev_dbg(dev, "Bus failed to match device: %d", ret);
                return ret;
        } /* ret > 0 means positive match */
 
        if (dev->parent)        /* Needed for USB */
                device_lock(dev->parent);
        device_lock(dev); 
        if (!dev->driver) 
                driver_probe_device(drv, dev);//这里
        device_unlock(dev);
        if (dev->parent)
                device_unlock(dev->parent);
 
        return 0;
}
static inline int driver_match_device(struct device_driver *drv,
                                      struct device *dev)
{
        return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct mdio_device *mdio = to_mdio_device(dev);

	if (of_driver_match_device(dev, drv))
		return 1;

	if (mdio->bus_match)
		return mdio->bus_match(dev, drv);  //这里

	return 0;
}

//phy_device_create 注册的bus_match

driver_match_device -> mdio_bus_match -> mdiodev->bus_match = phy_bus_match   
-> driver_probe_device -> really_probe (dev->driver = drv;)

static int phy_bus_match(struct device *dev, struct device_driver *drv)
{
        struct phy_device *phydev = to_phy_device(dev);
        struct phy_driver *phydrv = to_phy_driver(drv);
        const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
        int i;

        if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
                return 0;
                
        if (phydrv->match_phy_device)
                return phydrv->match_phy_device(phydev);
        
        if (phydev->is_c45) {
                for (i = 1; i < num_ids; i++) {
                        if (!(phydev->c45_ids.devices_in_package & (1 << i)))
                                continue;
        
                        if ((phydrv->phy_id & phydrv->phy_id_mask) ==
                            (phydev->c45_ids.device_ids[i] &
                             phydrv->phy_id_mask))
                                return 1;
                }
                return 0;
        } else {
                return (phydrv->phy_id & phydrv->phy_id_mask) ==
                        (phydev->phy_id & phydrv->phy_id_mask);//这里匹配到
						//phydev是总线注册时候扫描到并注册的设备(这个设备有phy的id信息
						//phydrv就是注册的drv
        }
}

mdio 可以独立的作为CPU资源,也可以是MAC自己出mdio控制线管理phy设备。
对于前者,我们只需要独立的注册phy驱动即可就像上诉所实现,然后在mac驱动里面调用phy的操作接口,管理phy设备。
对于后者,由于是mac自己出的总线,那么mac驱动需要自己注册mdio_bus总线,并提供phy的读写接口。不需要注册phy驱动。

如下实现:

int stmmac_mdio_register(struct net_device *ndev)
{
	int err = 0;
	struct mii_bus *new_bus;
	struct stmmac_priv *priv = netdev_priv(ndev);
	struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
	struct device_node *mdio_node = priv->plat->mdio_node;
	int addr, found;

	if (!mdio_bus_data)
		return 0;

	new_bus = mdiobus_alloc();
	if (new_bus == NULL)
		return -ENOMEM;

	if (mdio_bus_data->irqs)
		memcpy(new_bus->irq, mdio_bus_data->irqs, sizeof(new_bus->irq));

#ifdef CONFIG_OF
	if (priv->device->of_node)
		mdio_bus_data->reset_gpio = -1;
#endif

	new_bus->name = "stmmac";
	if (priv->plat->has_gmac4) {
		new_bus->read = &stmmac_mdio_read_gmac4;
		new_bus->write = &stmmac_mdio_write_gmac4;
	} else {
		new_bus->read = &stmmac_mdio_read;
		new_bus->write = &stmmac_mdio_write;
	}

	new_bus->reset = &stmmac_mdio_reset;
	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
		 new_bus->name, priv->plat->bus_id);
	new_bus->priv = ndev;
	new_bus->phy_mask = mdio_bus_data->phy_mask;
	new_bus->parent = priv->device;

	if (mdio_node)
		err = of_mdiobus_register(new_bus, mdio_node);
	else
		err = mdiobus_register(new_bus);
	if (err != 0) {
		pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
		goto bus_register_fail;
	}

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值