UBOOT PHY 驱动分析及调试方法

UBOOT版本:2017.11

 

一、PHY 简介

Media Independent Interface ( MII ),介质独立接口,起初是定义 100M 以太网(Fast Ethernet)的 MAC 层与 PHY 芯片之间的传输标准。

MAC 与 PHY 之间的 MII 连接可以是可插拔的连接器,或者是同一块 PCB 上 MAC 与 PHY 之间的走线。

MDIO 与MDCLK是 MII 接口的一部分,二者可称为 SMI (Serial Management Interface) 串行管理接口,用于在 MAC 和 PHY 之间传递配置信息。

在 MDIO 规范中定义 PHY 地址为 5 bit,即同一组MDIO最多可配置 2^5 = 32 个 PHY。

 

MII 接口图如下所示:

能够和MII相提并论的还有RMII(精简MII)、SMII(串行MII)、GMII(千兆MII)、RGMII(精简GMII)等接口,它们与普通MII相比较,仅是传输速率与数据传输方式不同等,这里不做过多的介绍。

 

二、PHY 设备创建及驱动匹配

以 rockchip 为例,当它的 mac 驱动 gmac_rockchip 成功的被匹配时,会调用到 gmac_rockchip_probe ,mac 相关我们不做分析,直接进入主题 designware_eth_probe 函数中。

首先会获取到对应的 regulator ,并会做一些电源配置,

1 device_get_supply_regulator(dev, "phy-supply",
2                 &phy_supply);
3 regulator_set_enable(phy_supply, true);

UBOOT 中引入了 DM 驱动模型,相关的设备与驱动事先已经绑定过,如这里通过 phandle 进而找出其对应的,驱动在 rk8xx.c 中定义:

1 U_BOOT_DRIVER(rk8xx_switch) = {
2     .name = "rk8xx_switch",
3     .id = UCLASS_REGULATOR,
4     .ops = &rk8xx_switch_ops,
5     .probe = rk8xx_switch_probe,
6 };

接下来要初始化 mdio 总线,并将其注册到系统的 mii_devs 中,在获取某个控制器时,即可通过 miiphy_get_dev_by_name 来获取:

1 dw_mdio_init(dev->name, dev);
2 priv->bus = miiphy_get_dev_by_name(dev->name);

电源以及 MDIO 总线已初始化完毕,万事俱备,接下来就正式的对 phy 进行操作了,一起来看 dw_phy_init 函数:

 1 static int dw_phy_init(struct dw_eth_dev *priv, void *dev)
 2 {
 3     struct phy_device *phydev;
 4     int mask = 0xffffffff, ret;
 5 
 6 #ifdef CONFIG_PHY_ADDR
 7     mask = 1 << CONFIG_PHY_ADDR;
 8 #endif
 9 
10     phydev = phy_find_by_mask(priv->bus, mask, priv->interface);
11     if (!phydev)
12         return -ENODEV;
13 
14     phy_connect_dev(phydev, dev);
15 
16     phydev->supported &= PHY_GBIT_FEATURES;
17     if (priv->max_speed) {
18         ret = phy_set_supported(phydev, priv->max_speed);
19         if (ret)
20             return ret;
21     }
22     phydev->advertising = phydev->supported;
23 
24     priv->phydev = phydev;
25     phy_config(phydev);
26 
27     return 0;
28 }

我们主要来看 phy_find_by_mask ,这里是核心,其它细节配置不去分析以免喧宾夺主。

在 phy_find_by_mask 中先是调用 search_for_existing_phy(bus, phy_mask, interface) ,它通过查询 phymap 看看我们系统中是不是已经有了能用的 phy dev 了,如果有,那么证明它已经初始化过并已经绑定过合适的驱动,用它就没有问题,

显然我们是没有的,那么就需要自己创造了,接下来来到 create_phy_by_mask :

 1 static struct phy_device *create_phy_by_mask(struct mii_dev *bus,
 2         unsigned phy_mask, int devad, phy_interface_t interface)
 3 {
 4     u32 phy_id = 0xffffffff;
 5     bool is_c45;
 6 
 7     while (phy_mask) {
 8         int addr = ffs(phy_mask) - 1;
 9         int r = get_phy_id(bus, addr, devad, &phy_id);
10         /* If the PHY ID is mostly f's, we didn't find anything */
11         if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff) {
12             is_c45 = (devad == MDIO_DEVAD_NONE) ? false : true;
13             return phy_device_create(bus, addr, phy_id, is_c45,
14                          interface);
15         }
16         phy_mask &= ~(1 << addr);
17     }
18     return NULL;
19 }

这段代码比较有意思,如果你的 phy addr 没有指定,那么会通过 ffs 与 phy_mask &= ~(1 << addr) 来实现地址范围为 0~31 的遍历,通过读取 phy id,依次去尝试指定地址上是否有 phy 会响应。

假设存在一个地址为 0x0F 的设备,那么就会继而调用到 phy_device_create ,它会创建一个 phy dev,初始化配置、并为其搜索合适的 phy driver 并绑定,实现代码如下:

 1 static struct phy_device *phy_device_create(struct mii_dev *bus, int addr,
 2                         u32 phy_id, bool is_c45,
 3                         phy_interface_t interface)
 4 {
 5     struct phy_device *dev;
 6 
 7     /* We allocate the device, and initialize the
 8      * default values */
 9     dev = malloc(sizeof(*dev));
10     if (!dev) {
11         printf("Failed to allocate PHY device for %s:%d\n",
12             bus->name, addr);
13         return NULL;
14     }
15 
16     memset(dev, 0, sizeof(*dev));
17 
18     dev->duplex = -1;
19     dev->link = 0;
20     dev->interface = interface;
21 
22 #ifdef CONFIG_DM_ETH
23     dev->node = ofnode_null();
24 #endif
25 
26     dev->autoneg = AUTONEG_ENABLE;
27 
28     dev->addr = addr;
29     dev->phy_id = phy_id;
30     dev->is_c45 = is_c45;
31     dev->bus = bus;
32 
33     dev->drv = get_phy_driver(dev, interface);
34 
35     phy_probe(dev);
36 
37     bus->phymap[addr] = dev;
38 
39     return dev;
40 }

 get_phy_driver 就是执行 driver 的搜索动作,那么匹配的依据是什么?还记得上一步探索是否存在 phy 设备的条件是什么么,没有错,是 phy id,它是从实际设备中读取出来的值。

然后与系统中 phy driver 链表每个元素进行比对,如果一致那么就配对绑定。假如读出 id 为 0x1cc915 ,那么就会直接匹配 RealTek RTL8211E 。若是没有找到合适的 driver,系统会将用 Generic PHY 通用驱动与其配对。

接下来就是 phy_probe 会调用到 phy driver 中的 probe 函数,对 phy 设备进行硬件配置,

最后就是将已完备的 phy dev 加入到 phymap 数组,以 addr 为索引,证明它已经是一个经过认可的 phy dev 了,整体流程比较简单,不做过多的分析。

 

三、PHY 调试

UBOOT 带有 MII 命令集,可以通过相关配置打开,在控制台中通过命令来调试比较方便,

MII 命令集:

mii - MII utility commands

Usage:
mii device                            - list available devices
mii device <devname>                  - set current device
mii info   <addr>                     - display MII PHY info
mii read   <addr> <reg>               - read  MII PHY <addr> register <reg>
mii write  <addr> <reg> <data>        - write MII PHY <addr> register <reg>
mii modify <addr> <reg> <data> <mask> - modify MII PHY <addr> register <reg>
                                        updating bits identified in <mask>
mii dump   <addr> <reg>               - pretty-print <addr> <reg> (0-5 only)

如可用 mii dump 命令查看寄存器 0 相关信息:

=> mii dump
0.     (1040)                 -- PHY control register --
  (8000:0000) 0.15    =     0    reset
  (4000:0000) 0.14    =     0    loopback
  (2040:0040) 0. 6,13 =   b10    speed selection = 1000 Mbps
  (1000:1000) 0.12    =     1    A/N enable
  (0800:0000) 0.11    =     0    power-down
  (0400:0000) 0.10    =     0    isolate
  (0200:0000) 0. 9    =     0    restart A/N
  (0100:0000) 0. 8    =     0    duplex = half
  (0080:0000) 0. 7    =     0    collision test enable
  (003f:0000) 0. 5- 0 =     0    (reserved)

 

posted @ 2020-11-28 10:51  Zackary丶Liu  阅读(5281)  评论(0编辑  收藏  举报