1. 前言
内核版本:linux 4.9.225,以freescale为例。
2. 概述
PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII/GMII/RMII/SGMII/XGMII等多种媒体独立接口(介质无关接口)与数据链路层的MAC芯片相连,并通过MDIO接口实现对PHY状态的监控、配置和管理。
PHY与MAC整体的大致连接框架如下(图片来源于网络):
PHY的整个硬件系统组成比较复杂,PHY与MAC相连(也可以通过一个中间设备相连),MAC与CPU相连(有集成在内部的,也有外接的方式),PHY与MAC通过MII和MDIO/MDC相连,MII是走网络数据的,MDIO/MDC是用来与PHY的寄存器通讯的,对PHY进行配置。
PHY的驱动与I2C/SPI的驱动一样,分为控制器驱动和设备器驱动。本节先讲控制器驱动。
3. PHY的控制器驱动总述
PHY的控制器驱动和SPI/I2C非常类似,控制器的核心功能是实现具体的读写功能。区别在于PHY的控制器读写功能的实现大致可以分为两种方式():
- 直接调用CPU的MDIO控制器(直接调用cpu对应的寄存器)的方式;
- 通过GPIO/外围soc模拟MDIO时序的方式;
PHY的控制器一般被描述为mdio_bus平台设备(注意:这是一个设备,等同于SPI/I2C中的master设备;和总线、驱动、设备中的bus不是一个概念)。
既然是平台设备,那么设备树中必定要有可以被解析为平台设备的节点,也要有对应的平台设备驱动。与SPI驱动类似,PHY设备模型也是在控制器驱动的probe函数中注册的。
本文两者都会涉及,主要讲后者。
4. 通过GPIO/外围soc模拟MDIO时序的方式
4.1 控制器平台设备在设备树中的大致描述方式(不完全准确,主要描述匹配的规则)
# linux-4.9.225\Documentation\devicetree\bindings\soc\fsl\cpm_qe\network.txt
* MDIO
Currently defined compatibles: fsl,pq1-fec-mdio (reg is same as first resource of FEC device) fsl,cpm2-mdio-bitbang (reg is port C registers)
Properties for fsl,cpm2-mdio-bitbang:
fsl,mdio-pin : pin of port C controlling mdio data
fsl,mdc-pin : pin of port C controlling mdio clock
Example: mdio@10d40 {
compatible = "fsl,mpc8272ads-mdio-bitbang",
"fsl,mpc8272-mdio-bitbang",
"fsl,cpm2-mdio-bitbang";
reg = <10d40 14>;
#address-cells = <1>;
#size-cells = <0>;
fsl,mdio-pin = <12>;
fsl,mdc-pin = <13>;
# linux-4.9.225\Documentation\devicetree\bindings\phy
xxx_phy: xxx-phy@xxx { //描述控制器下挂PHY设备的节点
reg = <0x0>; //PHY的地址
};
};
4.2 控制器平台驱动代码走读
4.2.1 控制器平台驱动的注册
static const struct of_device_id fs_enet_mdio_bb_match[] = {
{
.compatible = "fsl,cpm2-mdio-bitbang", //匹配平台设备的名称
},
{},
};
MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match);
static struct platform_driver fs_enet_bb_mdio_driver = {
.driver = {
.name = "fsl-bb-mdio",
.of_match_table = fs_enet_mdio_bb_match,
},
.probe = fs_enet_mdio_probe,
.remove = fs_enet_mdio_remove,
};
module_platform_driver(fs_enet_bb_mdio_driver); //注册控制器平台设备驱动
4.2.2 控制器平台驱动的probe函数走读
/**********************************************************************************************
通过GPIO/外围soc模拟MDIO时序方式的MDIO驱动(probe函数中完成PHY设备的创建和注册)
***********************************************************************************************/
# linux-4.9.225\drivers\net\ethernet\freescale\fs_enet\mii-bitbang.c
fs_enet_mdio_probe(struct platform_device *ofdev)
|--- bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL)
|
|--- bitbang->ctrl.ops = &bb_ops ----------------------------------------------->| static struct mdiobb_ops bb_ops = {
| | .owner = THIS_MODULE,
| | .set_mdc = mdc,
| | .set_mdio_dir = mdio_dir,
| | .set_mdio_data = mdio, |-->实现为GPIO的读写
| | .get_mdio_data = mdio_read,
| | };
| \<---------------------------------------------------------|
|--- new_bus = alloc_mdio_bitbang(&bitbang->ctrl) |
| |--- bus = mdiobus_alloc() -----------| | struct mdiobb_ctrl *ctrl = bus->priv| |
| |--- bus->read = mdiobb_read -----------| | ctrl->ops->set_mdc | |
| |---