05、MDIO子系统之结构体抽象
针对
mii management
,抽象为struct mii_bus
;针对
phy
设备,抽象为struct phy_device
;针对
phy
设备的驱动,抽象为struct phy_driver
;
mdio_bus
为总线类型。
phy_device
通过其drv
指针,实现与phy_driver
的关联与绑定;mii_bus
借助其成员phy_map
,将所有连接至该mii management
的phy device
关联起来。
kernel/drivers/net/phy/mdio_bus.c
一、phy_device
1.1、结构体
mac控制器扫描到一个device,就会创建一个phy_device(mac控制器来创建)
struct phy_device {
struct mdio_device mdio; //设备
/* Information about the PHY type */
/* And management functions */
struct phy_driver *drv; // PHY设备驱动
u32 phy_id; //phy_id phy的总线地址
struct phy_c45_device_ids c45_ids;
unsigned is_c45:1; //当前的协议类型(C22、C45(车载以太网))
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; //自协商
/* The most recently read link state */
unsigned link:1;
unsigned autoneg_complete:1;
/* Interrupts are enabled */
unsigned interrupts:1;
enum phy_state state;
u32 dev_flags;
phy_interface_t interface;
/*
* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
*/
int speed; //速度 1000M、100、10
int duplex; //双工
int port;
int pause;
int asym_pause;
u8 master_slave_get;
u8 master_slave_set;
u8 master_slave_state;
/* Union of PHY and Attached devices' supported link modes */
/* See ethtool.h for more info */
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
/* used with phy_speed_down */
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
/* Energy efficient ethernet modes which should be prohibited */
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
/*
* Interrupt number for this PHY
* -1 means no interrupt
*/
int irq; //中断号
/* private data pointer */
/* For use by PHYs to maintain extra state */
void *priv; //私有数据
/* shared data pointer */
/* For use by PHYs inside the same package that need a shared state. */
struct phy_package_shared *shared;
/* Reporting cable test results */
struct sk_buff *skb;
void *ehdr;
struct nlattr *nest;
/* Interrupt and Polling infrastructure */
struct delayed_work state_queue;
struct mutex lock;
/* This may be modified under the rtnl 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)
/* MACsec management functions */
const struct macsec_ops *macsec_ops;
#endif
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
1.2、注册
在mac控制器驱动中probe函数会在mdio bus上面注册phy_device设备
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
一个 PHY 设备对应一个 phy_device 实例,然后需要向 Linux 内核注册这个实例。
注册接口:phy_device_register(就可以在这里添加堆栈的打印dump_stack)
(到底是谁完成了phy_device 的注册)
of_mdiobus_register//注册mdio bus,并注册phy设备 of_mdio.c
mdiobus_register//注册mdio bus
of_mdiobus_register_phy//循环查找phy设备,注册phy设备
get_phy_device//获取PHY设备,如果读不到PHY ID,设置为0x12345678
phy_device_create//创建phy设备,设置phy地址、总线类型、PHY ID、自协商,更新状态机
phy_device_register//注册phy设备
2
3
4
5
6
其中PHY ID的获取:
drivers/net/phy/phy_device.c
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;
c45_ids.mmds_present = 0;
memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
if (is_c45) {
r = get_phy_c45_ids(bus, addr, &c45_ids);
} else {
r = get_phy_c22_id(bus, addr, &phy_id);
}
if (r)
return ERR_PTR(r);
//扫到PHY以后才会调用到这里
return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
EXPORT_SYMBOL(get_phy_device);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
第11行到15行 通过get phy id 来读取PHY芯片的2、3寄存器
phy_device_create 来 填充 phy_device
1.2.1、get_phy_c22_id (获取ID)
static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
{
int phy_reg;
unsigned int u_phy_reg;
/* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
if (phy_reg < 0) {
/* returning -ENODEV doesn't stop bus scanning */
return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
}
u_phy_reg = (unsigned int)phy_reg;
*phy_id = (u_phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
if (phy_reg < 0) {
/* returning -ENODEV doesn't stop bus scanning */
return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO;
}
u_phy_reg = (unsigned int)phy_reg;
*phy_id |= (u_phy_reg & 0xffff);
/* If the phy_id is mostly Fs, there is no device there */
if ((*phy_id & 0x1fffffff) == 0x1fffffff)
return -ENODEV;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1.2.2、phy_device_create(设备phy_device 的结构体填充)
扫到PHY以后就会调用phy_device_create
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;
/* 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);
INIT_WORK(&dev->phy_queue, phy_change_work);
/* Request the appropriate module unconditionally; don't
* bother trying to do so only if it isn't already loaded,
* because that gets complicated. A hotplug event would have
* done an unconditional modprobe anyway.
* We don't do normal hotplug because it won't work for MDIO
* -- because it relies on the device staying around for long
* enough for the driver to get loaded. With MDIO, the NIC
* driver will get bored and give up as soon as it finds that
* there's no driver _already_ loaded.
*/
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
device_initialize(&mdiodev->dev);
return dev;
}
EXPORT_SYMBOL(phy_device_create);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
最后就是将phy_device 通过phy_device_register 完成注册,放到总线上面:
1.2.3、phy_device_register(最终完成设备的注册)
/**
* phy_device_register - Register the phy device on the MDIO bus
* @phydev: phy_device structure to be added to the MDIO bus
*/
int phy_device_register(struct phy_device *phydev)
{
int err;
err = mdiobus_register_device(&phydev->mdio);
if (err)
return err;
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
/* Run all of the fixups for this PHY */
err = phy_scan_fixups(phydev);
if (err) {
phydev_err(phydev, "failed to initialize\n");
goto out;
}
err = device_add(&phydev->mdio.dev);
if (err) {
phydev_err(phydev, "failed to add\n");
goto out;
}
return 0;
out:
/* Assert the reset signal */
phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio);
return err;
}
EXPORT_SYMBOL(phy_device_register);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
device_add的最终结果:
drivers/net/phy/mdio_bus.c
int mdiobus_register_device(struct mdio_device *mdiodev)
{
int err;
if (mdiodev->bus->mdio_map[mdiodev->addr])
return -EBUSY;
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
err = mdiobus_register_gpiod(mdiodev);
if (err)
return err;
err = mdiobus_register_reset(mdiodev);
if (err)
return err;
/* Assert the reset signal */
mdio_device_reset(mdiodev, 1);
}
mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
return 0;
}
EXPORT_SYMBOL(mdiobus_register_device);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
二、phy_driver
PHY的驱动使用phy_driver 结构体进行标识
2.1、结构体
kernel/include/linux$ vim phy.h
struct phy_driver {
struct mdio_driver_common mdiodrv;
u32 phy_id; //phy 芯片 ID
char *name; //PHY 名字
unsigned int phy_id_mask; //phy 的 id 的标志
u32 features; //phy 的特性
u32 flags; //标志
const void *driver_data;
int (*soft_reset)(struct phy_device *phydev);
int (*config_init)(struct phy_device *phydev); //PHY的初始化
int (*probe)(struct phy_device *phydev); //
/* PHY Power Management */
int (*suspend)(struct phy_device *phydev); //休眠
int (*resume)(struct phy_device *phydev); //唤醒
int (*config_aneg)(struct phy_device *phydev); //phy协商函数
/* Determines the negotiated speed and duplex */
int (*read_status)(struct phy_device *phydev); //读phy状态函数
/* Clears any pending interrupts */
int (*ack_interrupt)(struct phy_device *phydev);
/* Enables or disables interrupts */
int (*config_intr)(struct phy_device *phydev);
......
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2.2、注册
phy_driver_register 的参数就是 PHY 驱动的结构体,该结构体中包含了 PHY 驱动的名字、ID、配置初始化函数等信息。
drivers/net/phy/phy_device.c
/**
* phy_driver_register - register a phy_driver with the PHY layer
* @new_driver: new phy_driver to register
* @owner: module owning this PHY
*/
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;
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) {
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;
}
EXPORT_SYMBOL(phy_driver_register);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2.3、注册案例:通用驱动
目录:drivers/net$ vim phy/phy_device.c
static int phy_probe(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv);
int err = 0;
phydev->drv = phydrv;
/* Disable the interrupt if the PHY doesn't support it
* but the interrupt is still a valid one
*/
if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
phydev->irq = PHY_POLL;
if (phydrv->flags & PHY_IS_INTERNAL)
phydev->is_internal = true;
mutex_lock(&phydev->lock);
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
if (phydev->drv->probe) {
err = phydev->drv->probe(phydev);
if (err)
goto out;
}
/* Start out supporting everything. Eventually,
* a controller will attach, and may modify one
* or both of these values
*/
if (phydrv->features) {
linkmode_copy(phydev->supported, phydrv->features);
} else if (phydrv->get_features) {
err = phydrv->get_features(phydev);
} else if (phydev->is_c45) {
err = genphy_c45_pma_read_abilities(phydev);
} else {
err = genphy_read_abilities(phydev);
}
if (err)
goto out;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported))
phydev->autoneg = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->supported))
phydev->is_gigabit_capable = 1;
of_set_phy_supported(phydev);
phy_advertise_supported(phydev);
/* Get the EEE modes we want to prohibit. We will ask
* the PHY stop advertising these mode later on
*/
of_set_phy_eee_broken(phydev);
/* The Pause Frame bits indicate that the PHY can support passing
* pause frames. During autonegotiation, the PHYs will determine if
* they should allow pause frames to pass. The MAC driver should then
* use that result to determine whether to enable flow control via
* pause frames.
*
* Normally, PHY drivers should not set the Pause bits, and instead
* allow phylib to do that. However, there may be some situations
* (e.g. hardware erratum) where the driver wants to set only one
* of these bits.
*/
if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
!test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
phydev->supported);
}
/* Set the state to READY by default */
phydev->state = PHY_READY;
out:
/* Assert the reset signal */
if (err)
phy_device_reset(phydev, 1);
mutex_unlock(&phydev->lock);
return err;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
三、phy_driver 和 phy_deiver
net/phy/mdio_bus.c
四、probe
static int phy_probe(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv);
int err = 0;
phydev->drv = phydrv;
/* Disable the interrupt if the PHY doesn't support it
* but the interrupt is still a valid one
*/
if (!(phydrv->flags & PHY_HAS_INTERRUPT) &&
phy_interrupt_is_valid(phydev))
phydev->irq = PHY_POLL;
if (phydrv->flags & PHY_IS_INTERNAL)
phydev->is_internal = true;
mutex_lock(&phydev->lock);
/* Start out supporting everything. Eventually,
* a controller will attach, and may modify one
* or both of these values
*/
phydev->supported = phydrv->features;
of_set_phy_supported(phydev);
phydev->advertising = phydev->supported;
/* Get the EEE modes we want to prohibit. We will ask
* the PHY stop advertising these mode later on
*/
of_set_phy_eee_broken(phydev);
/* The Pause Frame bits indicate that the PHY can support passing
* pause frames. During autonegotiation, the PHYs will determine if
* they should allow pause frames to pass. The MAC driver should then
* use that result to determine whether to enable flow control via
* pause frames.
*
* Normally, PHY drivers should not set the Pause bits, and instead
* allow phylib to do that. However, there may be some situations
* (e.g. hardware erratum) where the driver wants to set only one
* of these bits.
*/
if (phydrv->features & (SUPPORTED_Pause | SUPPORTED_Asym_Pause)) {
phydev->supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
phydev->supported |= phydrv->features &
(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
} else {
phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
}
/* Set the state to READY by default */
phydev->state = PHY_READY;
if (phydev->drv->probe) {
/* Deassert the reset signal */
phy_device_reset(phydev, 0);
err = phydev->drv->probe(phydev);
if (err) {
/* Assert the reset signal */
phy_device_reset(phydev, 1);
}
}
mutex_unlock(&phydev->lock);
return err;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72