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;
}
}