一、前言
以下为 S32V234 的 Ethernet 驱动的大致框架和 Mac 驱动详解。
二、硬件框架
Ethernet 驱动分为两部分:Mac 驱动和 Phy 驱动。
Mac 模块一般集成在芯片内部,Phy 模块一般外挂在芯片外,两者通过 RMII/GRMII 进行通信,S32V234 可以通过寄存器 SRC_GPR3[ENET_MODE] 来选择 RMII 还是 GRMII 。
RMII/GRMII 接口分为两部分:数据接口和控制接口。
其中控制接口有另外的 MDIO 控制器来控制, MDIO 控制器是 Mac 控制器里的一部分,通过 MDIO 总线来控制 Phy 芯片。数据接口由 Mac 控制器控制,通过 RMII/GRMII的其他接和口Phy 进行数据传输。
三、device & driver
根据 datasheet 和 设备树,找到了 Mac 的 device 节点是 fec: ethernet@40032000(以下 fec 可与 Mac 等同),对应的驱动文件是 drivers/net/ethernet/freescale/fec_main.c
四、MAC 驱动(fec_main.c)
注册为平台总线设备:
static struct platform_driver fec_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .pm = &fec_pm_ops, .of_match_table = fec_dt_ids, }, .id_table = fec_devtype, .probe = fec_probe, .remove = fec_drv_remove, }; module_platform_driver(fec_driver); |
通过 platform 总线匹配 device 和 driver 成功之后,进入 fec_probe 函数。
1. fec_probe 函数
在 probe 里面主要完成以下几件事:
- 获取设备信息,申请各项资源
- 从 fec的设备树节点中获取phy子节点和 phy 模式(phy-mode = "rgmii")
- 设置时钟、电源管理
- 重启并初始化 fec,申请队列和DMA,设置MAC地址,设置 mii_bus->read/mii_bus->write 函数(fec_enet_init)
- 申请中断(发送完成中断,mii 中断等处理函数)
- 初始化核心数据结构mii_bus,注册 MDIO bus,注册 phy 设备(fec_enet_mii_init)
- 向内核注册net_device
2. fec_enet_init 函数
定义在 drivers/net/ethernet/freescale/fec_main.c 中,被 fec_probe 调用。
它主要完成以下动作:
- tx、rx 的 dma 队列申请
- 获取mac地址,设置mac地址,写入mac寄存器
- 设置 tx、rx 的队列地址
- 初始化网卡操作函数和 ethtool_ops
- 重启 mac
网卡操作函数:
static const struct net_device_ops fec_netdev_ops = { .ndo_open = fec_enet_open, .ndo_stop = fec_enet_close, .ndo_start_xmit = fec_enet_start_xmit,//发包函数 .ndo_set_rx_mode = set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = fec_timeout, .ndo_set_mac_address = fec_set_mac_address, .ndo_do_ioctl = fec_enet_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = fec_poll_controller, #endif .ndo_set_features = fec_set_features, }; |
ethtool_ops:
static const struct ethtool_ops fec_enet_ethtool_ops = { .get_drvinfo = fec_enet_get_drvinfo, .get_regs_len = fec_enet_get_regs_len, .get_regs = fec_enet_get_regs, .nway_reset = phy_ethtool_nway_reset, .get_link = ethtool_op_get_link, .get_coalesce = fec_enet_get_coalesce, .set_coalesce = fec_enet_set_coalesce, #ifndef CONFIG_M5272 .get_pauseparam = fec_enet_get_pauseparam, .set_pauseparam = fec_enet_set_pauseparam, .get_strings = fec_enet_get_strings, .get_ethtool_stats = fec_enet_get_ethtool_stats, .get_sset_count = fec_enet_get_sset_count, #endif .get_ts_info = fec_enet_get_ts_info, .get_tunable = fec_enet_get_tunable, .set_tunable = fec_enet_set_tunable, .get_wol = fec_enet_get_wol, .set_wol = fec_enet_set_wol, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, }; |
3. fec_enet_mii_init 函数
定义在 drivers/net/ethernet/freescale/fec_main.c 中,被 fec_probe 调用。
它主要完成以下动作:
- 初始化核心数据结构mii_bus(调用 mdiobus_register,初始化 mii_speed、mii_bus->read、mii_bus->write 接口等)
- 获取设备树“mdio”节点,注册 MDIO bus(of_mdiobus_register,这个函数里注册 MDIO bus和phy 设备)
主要调用流程:
fec_probe -> fec_enet_mii_init -> of_mdiobus_register -> mdiobus_register
4. mdiobus_register(__mdiobus_register)
位于 drivers/net/phy/mdio_bus.c,被 of_mdiobus_register 调用,没有条件,进入 of_mdiobus_register 即执行。
主要完成:
- device_register(&bus->dev):注册 mdio bus
- mdiobus_scan(bus, i):探测时被忽略的 phy 地址将不执行此函数(if ((bus->phy_mask & (1 << i)) != 0))
5. of_mdiobus_register_phy
位于 drivers/of/of_mdio.c,被 of_mdiobus_register 调用,为遍历设备树匹配到的每个 phy 节点执行以下函数
主要完成:
获取 phy id,创建 phy device,功能与 mdiobus_scan 相似。
of_get_phy_id -> phy_device_create (如果 of_get_phy_id 失败则调用 get_phy_device) -> phy_device_register
在 mac 驱动根据 phy id 匹配到 phy 节点并注册 phy 设备后(phy_device_register),成功把 phy device 注册到 mdiobus 上(device_add),那么 phy_probe(phy 驱动部分) 就会被调用。
总结:以上就是 S32V234 bsp23.1 版本的 Mac 驱动详解。