mii_bus
mii_bus 用于读写phy芯片。
其中的重点是mii_bus->read 和mii_bus->write。
/*
* The Bus class for PHYs. Devices which provide access to
* PHYs should register using this structure
*/
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);
/*
* A lock to ensure that only one thing can read/write
* the MDIO bus at a time
*/
struct mutex mdio_lock;
struct device *parent;
enum {
MDIOBUS_ALLOCATED = 1,
MDIOBUS_REGISTERED,
MDIOBUS_UNREGISTERED,
MDIOBUS_RELEASED,
} state;
struct device dev;
/* list of all PHYs on bus */
struct mdio_device *mdio_map[PHY_MAX_ADDR];
/* PHY addresses to be ignored when probing */
u32 phy_mask;
/* PHY addresses to ignore the TA/read failure */
u32 phy_ignore_ta_mask;
/*
* An array of interrupts, each PHY's interrupt at the index
* matching its address
*/
int irq[PHY_MAX_ADDR];
/* GPIO reset pulse width in microseconds */
int reset_delay_us;
/* RESET GPIO descriptor pointer */
struct gpio_desc *reset_gpiod;
/* bus capabilities, used for probing */
enum {
MDIOBUS_NO_CAP = 0,
MDIOBUS_C22,
MDIOBUS_C45,
MDIOBUS_C22_C45,
} probe_capabilities;
};
ifconfig ethx up
of_phy_connect //网口up时会调用此函数
->of_phy_find_device //(of_mdio.c) //通过设备树找到phy 设备
->bus_find_device_by_of_node
->to_mdio_device
->to_phy_device
->phy_connect_direct //将以太网设备连接到特定的phy设备
->phy_attach_direct //将网络设备与指定的phy设备绑定
of_phy_find_device 内容如下:
struct phy_device *of_phy_find_device(struct device_node *phy_np)
{
struct device *d;
struct mdio_device *mdiodev;
if (!phy_np)
return NULL;
d = bus_find_device_by_of_node(&mdio_bus_type, phy_np);
if (d) {
mdiodev = to_mdio_device(d);
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
return to_phy_device(d);
put_device(d);
}
return NULL;
}
to_mdio_device 与to_phy_device的定义如下
#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
#define to_phy_device(d) container_of(to_mdio_device(d), \
struct phy_device, mdio)
container_of(ptr,type,member)
container_of(to_mdio_device(d), struct phy_device, mdio)
该函数的意义时得到type 类型的结构体首地址(即得到该结构体)。
原理很简单: 已知结构体type的成员member的地址ptr,求解结构体type的起始地址。
type的起始地址 = ptr - size。 size 不用计较它如何得知,我们只要知道该函数会返回member成员所在结构体的首地址就好了。
phy_attach_direct 函数定义在 phy_device.c。
int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
u32 flags, phy_interface_t interface)
{
struct mii_bus *bus = phydev->mdio.bus;
struct device *d = &phydev->mdio.dev;
struct module *ndev_owner = NULL;
bool using_genphy = false;
int err;
/* For Ethernet device drivers that register their own MDIO bus, we
* will have bus->owner match ndev_mod, so we do not want to increment
* our own module->refcnt here, otherwise we would not be able to
* unload later on.
*/
if (dev)
ndev_owner = dev->dev.parent->driver->owner;
if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
phydev_err(phydev, "failed to get the bus module\n");
return -EIO;
}
get_device(d);
/* Assume that if there is no driver, that it doesn't
* exist, and we should use the genphy driver.
*/
if (!d->driver) { //获取mdio驱动
if (phydev->is_c45)
d->driver = &genphy_c45_driver.mdiodrv.driver; //c45的
else
d->driver = &genphy_driver.mdiodrv.driver; //通用的mdio驱动
using_genphy = true;
}
if (!try_module_get(d->driver->owner)) {
phydev_err(phydev, "failed to get the device driver module\n");
err = -EIO;
goto error_put_device;
}
if (using_genphy) {
err = d->driver->probe(d);
if (err >= 0)
err = device_bind_driver(d);
if (err)
goto error_module_put;
}
if (phydev->attached_dev) { //判断是否已经绑定,attached_dev 即net_device ;
dev_err(&dev->dev, "PHY already attached\n");
err = -EBUSY;
goto error;
}
phydev->phy_link_change = phy_link_change; //连接变换函数
if (dev) { //绑定网络设备与phy设备
phydev->attached_dev = dev;
dev->phydev = phydev;
}
/* Some Ethernet drivers try to connect to a PHY device before
* calling register_netdevice() -> netdev_register_kobject() and
* does the dev->dev.kobj initialization. Here we only check for
* success which indicates that the network device kobject is
* ready. Once we do that we still need to keep track of whether
* links were successfully set up or not for phy_detach() to
* remove them accordingly.
*/
phydev->sysfs_links = false;
phy_sysfs_create_links(phydev);
if (!phydev->attached_dev) {
err = sysfs_create_file(&phydev->mdio.dev.kobj,
&dev_attr_phy_standalone.attr);
if (err)
phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
}
phydev->dev_flags = flags;
phydev->interface = interface; //phy 设备接口
phydev->state = PHY_READY;
/* Initial carrier state is off as the phy is about to be
* (re)initialized.
*/
if (dev)
netif_carrier_off(phydev->attached_dev);
/* Do initial configuration here, now that
* we have certain key parameters
* (dev_flags and interface)
*/
err = phy_init_hw(phydev); //初始化硬件,估计是设置寄存器。
if (err)
goto error;
phy_resume(phydev); //恢复phy 设备(使能BMCR(0x00) power位)
phy_led_triggers_register(phydev); //phy led触发注册
return err;
error:
/* phy_detach() does all of the cleanup below */
phy_detach(phydev);
return err;
error_module_put:
module_put(d->driver->owner);
error_put_device:
put_device(d);
if (ndev_owner != bus->owner)
module_put(bus->owner);
return err;
}
phy_init_hw 函数,此函数会调用到phy 驱动函数。
int phy_init_hw(struct phy_device *phydev)
{
int ret = 0;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
if (!phydev->drv) //phy 驱动
return 0;
if (phydev->drv->soft_reset) //phy 驱动软复位
ret = phydev->drv->soft_reset(phydev);
if (ret < 0)
return ret;
ret = phy_scan_fixups(phydev);
if (ret < 0)
return ret;
if (phydev->drv->config_init)
ret = phydev->drv->config_init(phydev); //config_init
return ret;
}
phy设备驱动工作流程:
phy_devic.c 下会注册mdio总线、通用phy驱动genphy_driver
。
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.soft_reset = genphy_no_soft_reset,
.get_features = genphy_read_abilities,
.aneg_done = genphy_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
};
static int __init phy_init(void)
{
int rc;
rc = mdio_bus_init(); //注册mdio总线
if (rc)
return rc;
features_init(); //features:特性
// phy驱动注册========================
rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);
if (rc)
goto err_c45;
rc = phy_driver_register(&genphy_driver, THIS_MODULE);
if (rc) {
phy_driver_unregister(&genphy_c45_driver);
err_c45:
mdio_bus_exit();
}
return rc;
}
static void __exit phy_exit(void)
{
phy_driver_unregister(&genphy_c45_driver);
phy_driver_unregister(&genphy_driver);
mdio_bus_exit();
}
subsys_initcall(phy_init);
module_exit(phy_exit);
mdio 总线注册
struct bus_type mdio_bus_type = { //mdio 总线类型结构体
.name = "mdio_bus",
.match = mdio_bus_match, //用于匹配phy_device 与 phy_driver (匹配之类的程序属于纯软件的核心层,与硬件无关)
.uevent = mdio_uevent,
};
EXPORT_SYMBOL(mdio_bus_type);
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;
}
mdio_bus_match 定义在drivers/net/phy/mdio_bus.c。
值得注意的是 to_mdio_device、to_phy_device、to_phy_driver
这3个函数可以通过struct device *dev 和struct device_driver *drv
获得想要的结构体类型。利用前面的container_of
也可以做到。
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;
}
其它内核版本的mdio_bus_match
1 static int mdio_bus_match(struct device *dev,
struct device_driver *drv)
2 {
3 struct phy_device *phydev = to_phy_device(dev);
4 struct phy_driver *phydrv = to_phy_driver(drv);
5 6
if (of_driver_match_device(dev, drv))
7 return 1;
8 9
if (phydrv->match_phy_device)
10 return phydrv->match_phy_device(phydev);
11
12 return (phydrv->phy_id & phydrv->phy_id_mask) ==
13 (phydev->phy_id & phydrv->phy_id_mask);
14 }
phy_device 创建
get_phy_device
->get_phy_id->phy_device_create
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
struct phy_c45_device_ids c45_ids;
u32 phy_id = 0;
int r;
c45_ids.devices_in_package = 0;
memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
if (r)
return ERR_PTR(r);
/* If the phy_id is mostly Fs, there is no device there */
if ((phy_id & 0x1fffffff) == 0x1fffffff)
return ERR_PTR(-ENODEV);
return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
struct mdio_device *mdiodev;
int ret = 0;
/* We allocate the device, and initialize the default values */
dev = = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
mdiodev = &dev->mdio;
mdiodev->dev.parent = &bus->dev;
mdiodev->dev.bus = &mdio_bus_type;
mdiodev->dev.type = &mdio_bus_phy_type;
mdiodev->bus = bus;
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;
dev->speed = SPEED_UNKNOWN;
dev->duplex = DUPLEX_UNKNOWN;
dev->pause = 0;
dev->asym_pause = 0;
dev->link = 0;
dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE;
dev->is_c45 = is_c45;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
dev->irq = bus->irq[addr];
dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
dev->state = PHY_DOWN;
mutex_init(&dev->lock);
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
if (is_c45 && c45_ids) {
const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
int i;
for (i = 1; i < num_ids; i++) {
if (c45_ids->device_ids[i] == 0xffffffff)
continue;
ret = phy_request_driver_module(dev,
c45_ids->device_ids[i]);
if (ret)
break;
}
} else {
ret = phy_request_driver_module(dev, phy_id);
}
if (!ret) {
device_initialize(&mdiodev->dev);
} else {
kfree(dev);
dev = ERR_PTR(ret);
}
return dev;
}
mdiodev->bus_match :phy_bus_match
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.device_ids[i] == 0xffffffff)
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);
}
}