RK3568プラットフォームにおいて、PHY芯片每には struct phy_device 型のデバイスが生成され、対応する struct phy_driver 型のドライバが割り当てられる。これらは mdio_bus_type バスに接続されており、MACは struct net_device として登録される。
- バス - struct mii_bus(MIIはメディア独立インターフェースを意味する)
- デバイス - struct phy_device
- _driver - struct phy_driver
コードパス:rk356x-linux/kernel/drivers/net/phy、RK内部EPHYドライバコードは drivers/net/phy/rockchip.c
GMACドライバコードの場所:drivers/net/ethernet/stmicro/stmmac/*
rk356x-linux/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
- phy_device構造体
struct phy_device {
struct mdio_device mdio;
struct phy_driver *drv;
u32 phy_id;
struct phy_c45_device_ids c45_ids;
unsigned is_c45:1;
unsigned is_internal:1;
unsigned is_pseudo_fixed_link:1;
unsigned is_gigabit_capable:1;
unsigned has_fixups:1;
unsigned suspended:1;
unsigned suspended_by_mdio_bus:1;
unsigned sysfs_links:1;
unsigned loopback_enabled:1;
unsigned downshifted_rate:1;
unsigned autoneg:1;
unsigned link:1;
unsigned autoneg_complete:1;
unsigned interrupts:1;
enum phy_state state;
u32 dev_flags;
phy_interface_t interface;
int speed;
int duplex;
int port;
int pause;
int asym_pause;
u8 master_slave_get;
u8 master_slave_set;
u8 master_slave_state;
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
u32 eee_broken_modes;
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
unsigned int phy_num_led_triggers;
struct phy_led_trigger *last_triggered;
struct phy_led_trigger *led_link_trigger;
#endif
int irq;
void *priv;
struct phy_package_shared *shared;
struct sk_buff *skb;
void *ehdr;
struct nlattr *nest;
struct delayed_work state_queue;
struct mutex lock;
bool sfp_bus_attached;
struct sfp_bus *sfp_bus;
struct phylink *phylink;
struct net_device *attached_dev;
struct mii_timestamper *mii_ts;
u8 mdix;
u8 mdix_ctrl;
void (*phy_link_change)(struct phy_device *phydev, bool up);
void (*adjust_link)(struct net_device *dev);
#if IS_ENABLED(CONFIG_MACSEC)
const struct macsec_ops *macsec_ops;
#endif
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
};
- phy_driver構造体
struct phy_driver {
struct mdio_driver_common mdiodrv;
u32 phy_id;
char *name;
u32 phy_id_mask;
const unsigned long * const features;
u32 flags;
const void *driver_data;
int (*soft_reset)(struct phy_device *phydev);
int (*config_init)(struct phy_device *phydev);
int (*probe)(struct phy_device *phydev);
int (*get_features)(struct phy_device *phydev);
int (*suspend)(struct phy_device *phydev);
int (*resume)(struct phy_device *phydev);
int (*config_aneg)(struct phy_device *phydev);
int (*aneg_done)(struct phy_device *phydev);
int (*read_status)(struct phy_device *phydev);
int (*ack_interrupt)(struct phy_device *phydev);
int (*config_intr)(struct phy_device *phydev);
int (*did_interrupt)(struct phy_device *phydev);
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
void (*remove)(struct phy_device *phydev);
int (*match_phy_device)(struct phy_device *phydev);
int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
void (*link_change_notify)(struct phy_device *dev);
int (*read_mmd)(struct phy_device *dev, int devnum, u16);
int (*write_mmd)(struct phy_device *dev, int devnum, u16, u16);
int (*read_page)(struct phy_device *dev);
int (*write_page)(struct phy_device *dev, int page);
int (*module_info)(struct phy_device *dev, struct ethtool_modinfo *);
int (*module_eeprom)(struct phy_device *dev, struct ethtool_eeprom *, u8 *);
int (*cable_test_start)(struct phy_device *dev);
int (*cable_test_tdr_start)(struct phy_device *dev, const struct phy_tdr_config *);
int (*cable_test_get_status)(struct phy_device *dev, bool *);
int (*get_sset_count)(struct phy_device *dev);
void (*get_strings)(struct phy_device *dev, u8 *data);
void (*get_stats)(struct phy_device *dev, struct ethtool_stats *, u64 *);
int (*get_tunable)(struct phy_device *dev, struct ethtool_tunable *, void *);
int (*set_tunable)(struct phy_device *dev, struct ethtool_tunable *, const void *);
int (*set_loopback)(struct phy_device *dev, bool enable);
int (*get_sqi)(struct phy_device *dev);
int (*get_sqi_max)(struct phy_device *dev);
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
- mii_bus関連構造体
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);
int (*write)(struct mii_bus *bus, int addr, int, u16 val);
int (*reset)(struct mii_bus *bus);
struct mdio_bus_stats stats[PHY_MAX_ADDR];
struct mutex mdio_lock;
struct device *parent;
enum {
MDIOBUS_ALLOCATED = 1,
MDIOBUS_REGISTERED,
MDIOBUS_UNREGISTERED,
MDIOBUS_RELEASED,
} state;
struct device dev;
struct mdio_device *mdio_map[PHY_MAX_ADDR];
u32 phy_mask;
u32 phy_ignore_ta_mask;
int irq[PHY_MAX_ADDR];
int reset_delay_us;
int reset_post_delay_us;
struct gpio_desc *reset_gpiod;
enum {
MDIOBUS_NO_CAP = 0,
MDIOBUS_C22,
MDIOBUS_C45,
MDIOBUS_C22_C45,
} probe_capabilities;
struct mutex shared_lock;
struct phy_package_shared *shared[PHY_MAX_ADDR];
};
- phy驱动フレームワーク
#define phy_module_driver(__phy_drivers, __count) \
static int __init phy_module_init(void) \
{ \
return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
} \
module_init(phy_module_init);
(1). phy_driverの初期化 - phy_init
static int __init phy_init(void)
{
int rc;
rc = mdio_bus_init();
if (rc)
return rc;
ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops);
features_init();
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_register(&genphy_c45_driver);
err_c45:
mdio_bus_exit();
}
return rc;
}
//mdioバス驅動を登録
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;
}
int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
int retval;
if (WARN_ON(new_driver->features && new_driver->get_features)) {
pr_err("%s: features and get_features must not both be set\n",
new_driver->name);
return -EINVAL;
}
new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
new_driver->mdiodrv.driver.name = new_driver->name;
new_driver->mdiodrv.driver.bus = &mdio_bus_type;
new_driver->mdiodrv.driver.probe = phy_probe;
new_driver->mdiodrv.driver.remove = phy_remove;
new_driver->mdiodrv.driver.owner = owner;
new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
retval = driver_register(&new_driver->mdiodrv.driver);
if (retval) {
pr_err("%s: Error %d in registering driver\n",
new_driver->name, retval);
return retval;
}
pr_debug("%s: Registered new driver\n", new_driver->name);
return 0;
}
rk356x-linux/kernel/drivers/net/phy/phy_device.c
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;
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->port = PORT_TP;
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);
device_initialize(&mdiodev->dev);
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) {
put_device(&mdiodev->dev);
dev = ERR_PTR(ret);
}
return dev;
}
EXPORT_SYMBOL(phy_device_create);
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);
}
}
phydrv->match_phy_device関数の登録例:
.match_phy_device = rtlgen_match_phy_device,
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.get_features = genphy_read_abilities,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
};
(2)mdio初期化
static struct attribute *mdio_bus_device_statistics_attrs[] = {
&dev_attr_mdio_bus_device_transfers.attr.attr,
&dev_attr_mdio_bus_device_errors.attr.attr,
&dev_attr_mdio_bus_device_writes.attr.attr,
&dev_attr_mdio_bus_device_reads.attr.attr,
NULL,
};
static const struct attribute_group mdio_bus_device_statistics_group = {
.name = "statistics",
.attrs = mdio_bus_device_statistics_attrs,
};
static const struct attribute_group *mdio_bus_dev_groups[] = {
&mdio_bus_device_statistics_group,
NULL,
};
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;
}
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.dev_groups = mdio_bus_dev_groups,
.match = mdio_bus_match,
.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マッチング関係:mdio_device.c / mdio_bus.c
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
{
struct mdio_device *mdiodev;
mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
if (!mdiodev)
return ERR_PTR(-ENOMEM);
mdiodev->dev.release = mdio_device_release;
mdiodev->dev.parent = &bus->dev;
mdiodev->dev.bus = &mdio_bus_type;
mdiodev->device_free = mdio_device_free;
mdiodev->device_remove = mdio_device_remove;
mdiodev->bus = bus;
mdiodev->addr = addr;
dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
device_initialize(&mdiodev->dev);
return mdiodev;
}
EXPORT_SYMBOL(mdio_device_create);
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.dev_groups = mdio_bus_dev_groups,
.match = mdio_bus_match,
.uevent = mdio_uevent,
};
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;
}
呼び出し関係図
rk_gmac_probe
└→ stmmac_dvr_probe
└→ stmmac_mdio_register(stmmac_mdio_read)
└→ of_mdiobus_register
└→ mdiobus_register
└→ __mdiobus_register
└→ mdiobus_scan
└→ get_phy_device
└→ phy_device_create
stmmacマッチング規則(of_match_table関数)
static const struct of_device_id rk_gmac_dwmac_match[] = {
#ifdef CONFIG_CPU_PX30
{ .compatible = "rockchip,px30-gmac", .data = &px30_ops },
#endif
#ifdef CONFIG_CPU_RK1808
{ .compatible = "rockchip,rk1808-gmac", .data = &rk1808_ops },
#endif
#ifdef CONFIG_CPU_RK312X
{ .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops },
#endif
#ifdef CONFIG_CPU_RK322X
{ .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops },
#endif
#ifdef CONFIG_CPU_RK3288
{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
#endif
#ifdef CONFIG_CPU_RK3308
{ .compatible = "rockchip,rk3308-mac", .data = &rk3308_ops },
#endif
#ifdef CONFIG_CPU_RK3328
{ .compatible = "rockchip,rk3328-gmac", .data = &rk3328_ops },
#endif
#ifdef CONFIG_CPU_RK3366
{ .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops },
#endif
#ifdef CONFIG_CPU_RK3368
{ .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
#endif
#ifdef CONFIG_CPU_RK3399
{ .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops },
#endif
#ifdef CONFIG_CPU_RK3528
{ .compatible = "rockchip,rk3528-gmac", .data = &rk3528_ops },
#endif
#ifdef CONFIG_CPU_RK3562
{ .compatible = "rockchip,rk3562-gmac", .data = &rk3562_ops },
#endif
#ifdef CONFIG_CPU_RK3568
{ .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops },
#endif
#ifdef CONFIG_CPU_RK3588
{ .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
#endif
#ifdef CONFIG_CPU_RV1106
{ .compatible = "rockchip,rv1106-gmac", .data = &rv1106_ops },
#endif
#ifdef CONFIG_CPU_RV1108
{ .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops },
#endif
#ifdef CONFIG_CPU_RV1126
{ .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops },
#endif
{ }
};
MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
static struct platform_driver rk_gmac_dwmac_driver = {
.probe = rk_gmac_probe,
.remove = rk_gmac_remove,
.driver = {
.name = "rk_gmac-dwmac",
.pm = &rk_gmac_pm_ops,
.of_match_table = rk_gmac_dwmac_match,
},
};
デバイストリー解析:rk356x-linux/kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
struct plat_stmmacenet_data *stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
mdioデバイストリーマッチング規則
int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
{
struct device_node *child;
bool scanphys = false;
int addr, rc;
if (!np)
return mdiobus_register(mdio);
if (!of_device_is_available(np))
return -ENODEV;
mdio->phy_mask = ~0;
mdio->dev.of_node = np;
mdio->dev.fwnode = of_fwnode_handle(np);
mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY;
of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us);
mdio->reset_post_delay_us = 0;
of_property_read_u32(np, "reset-post-delay-us", &mdio->reset_post_delay_us);
rc = mdiobus_register(mdio);
if (rc)
return rc;
for_each_available_child_of_node(np, child) {
addr = of_mdio_parse_addr(&mdio->dev, child);
if (addr < 0) {
scanphys = true;
continue;
}
if (of_mdiobus_child_is_phy(child))
rc = of_mdiobus_register_phy(mdio, child, addr);
else
rc = of_mdiobus_register_device(mdio, child, addr);
if (rc == -ENODEV)
dev_err(&mdio->dev,
"MDIO device at address %d is missing.\n",
addr);
else if (rc)
goto unregister;
}
if (!scanphys)
return 0;
for_each_available_child_of_node(np, child) {
if (of_find_property(child, "reg", NULL))
continue;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
if (mdiobus_is_registered_device(mdio, addr))
continue;
dev_info(&mdio->dev, "scan phy %pOFn at address %i\n",
child, addr);
if (of_mdiobus_child_is_phy(child)) {
rc = of_mdiobus_register_phy(mdio, child, addr);
if (!rc)
break;
if (rc != -ENODEV)
goto unregister;
}
}
}
return 0;
unregister:
of_node_put(child);
mdiobus_unregister(mdio);
return rc;
}
EXPORT_SYMBOL(of_mdiobus_register);