FPGA Marvell 88exxxx phy 动起来

FPGA Marvell 88exxxx phy 动起来

环境 
xilinx FPGA : Zynq or Kintex-7/Artix-7


方案 
驱动Marvell的Phy 正统套路是找marvell要芯片的手册 
但是不买上个千百片,人家Marvell可能不会理你 
零售代理商家可以提供么? 应该是有的


背景知识 
- - - 什么是MII、GMII、RGMII、SGMII、1000BaseX 
1. MII 的英文全称是 media-independent interface (MII) ,即介质独立接口 
初衷是为了当时的高速(百兆)以太网制定mac标准接口,由IEEE 802.3u工作组标准化 
采用“介质独立”接口的硬件,无论是双绞线还是光纤,都可以一视同仁,省去了更换的成本和麻烦 
2. 伴随MII接口的,是Management Data Input/Output (MDIO)串行控制总线,完成MAC和PHY的控制交互

为了适配不同的接口需求,扩展的MII接口形式如下 
reduced media-independent interface (RMII) 
gigabit media-independent interface (GMII) 
reduced gigabit media-independent interface (RGMII) 
serial gigabit media-independent interface (SGMII)


策略1:官方自测

1. 建立工程

手头利用vivado建立一个zynq的IPI工程,添加zynq PS,依靠自动生成功能补完最小系统,开始编译…… 
之后是export hdf—->generate SDK bsp的标准套路

FPGA Marvell 88exxxx phy 动起来_第1张图片

值得记录的是Zynq的PS部分采用MIO和EMIO来复用有限的IO资源,这年头什么都是越来越:软件定义XXX,软件无线电,HLS 
接口采用了MIO结构,也可以软件配置了

FPGA Marvell 88exxxx phy 动起来_第2张图片

2. 外设自测

在生成的SDK中为外设建立一个自测: 
file–new–Application Project–standalone–peripheral test 
建好了的自测代码中,我们只关心核心的EMAC INIT部分

XEmacPs_SetMdioDivisor(EmacPsInstancePtr, MDC_DIV_224);
        if ((Platform & PLATFORM_MASK) == PLATFORM_SILICON) {
            EmacPsUtilEnterLoopback(EmacPsInstancePtr, EMACPS_LOOPBACK_SPEED_1G);
            XEmacPs_SetOperatingSpeed(EmacPsInstancePtr,EMACPS_LOOPBACK_SPEED_1G);
        } else {
            EmacPsUtilEnterLoopback(EmacPsInstancePtr, EMACPS_LOOPBACK_SPEED);
            XEmacPs_SetOperatingSpeed(EmacPsInstancePtr,EMACPS_LOOPBACK_SPEED);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

代码块做了三件事:为MDIO的mdc设置分频比、将PHY置为回环模式、设置工作速率 
不同版本的SDK生成的驱动也会有区别,在vivado新版sdk中,已经可以区分是marvell的phy还是TI的phy了

XEmacPs_PhyRead(EmacPsInstancePtr, PhyAddr, PHY_DETECT_REG1, &PhyIdentity);

    if (PhyIdentity == PHY_ID_MARVELL) {
        Status = EmacPsUtilMarvellPhyLoopback(EmacPsInstancePtr, Speed, PhyAddr);
    }

    if (PhyIdentity == PHY_ID_TI) {
        Status = EmacPsUtilTiPhyLoopback(EmacPsInstancePtr, Speed, PhyAddr);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

顺利的话,裸系统的代码可以辅助理解phy的驱动流程:写寄存器 
一般写pcore和写phy均会在驱动中涉及,具体定义参见相关手册 
值得记录的是,reg22为页寄存器:IEEE只给phy定义了32个寄存器空间,所以用其中一个寄存器来作为页寄存器,扩展维度 
流程参见自测,此处无必要详述

3. 外设再测

顺手可以在向导中生成一下标准平台的SDK工程: 
我选了个ZynqMP SOC的Lwip 
FPGA Marvell 88exxxx phy 动起来_第3张图片

从Lwip的代码中找找eth的驱动


unsigned configure_IEEE_phy_speed(XAxiEthernet *xaxiemacp, unsigned speed)
{
    u16 control;
    u32 phy_addr = detect_phy(xaxiemacp);
    u16 phy_val;

    if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
                XAE_PHY_TYPE_RGMII_2_0) {
        /* Setting Tx and Rx Delays for RGMII mode */
        XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x2);

        XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &phy_val);
        phy_val |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
        XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, phy_val);

        XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x0);
    }

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr,
                IEEE_CONTROL_REG_OFFSET,
                &control);
    control &= ~IEEE_CTRL_LINKSPEED_1000M;
    control &= ~IEEE_CTRL_LINKSPEED_100M;
    control &= ~IEEE_CTRL_LINKSPEED_10M;

    if (speed == 1000) {
        control |= IEEE_CTRL_LINKSPEED_1000M;
    }

    else if (speed == 100) {
        //...

    }
        //...
    }
    //此处有复位
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr,
                IEEE_CONTROL_REG_OFFSET,
                control | IEEE_CTRL_RESET_MASK);

    if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) ==
            XAE_PHY_TYPE_SGMII) {
        //SGMII模式下的ISOLATE和自协商
    }

    {//等待配置结束
        volatile int wait;
        for (wait=0; wait < 100000; wait++);
        for (wait=0; wait < 100000; wait++);
    }
    return 0;
}
  • 1
  • 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

是不是很相似呢??其实完全一样么~~~ 
所以全都是一个套路呀


策略2:开源社区 
听说现在marvell的phy在petalinux的驱动上支持很完善

http://www.wiki.xilinx.com/PetaLinux

决定上开源社区去瞧一瞧 
去哪?当然是github 
啥啥都有,邓爷爷说的先富带动后富,开源精神vs拿来主义

https://github.com/Xilinx/linux-xlnx/blob/a96b8d2e1c9b37de51590b63bcce63a4036e760a/drivers/net/phy/marvell.c

linux采用devicetree来描述丰富的各类平台外设,具体啥是dts?水比较深 
引用一下相关学习地址:

http://www.wowotech.net/linux_kenrel/why-dt.html

回到正题,linux-xlnx分支下的phy配置结构,需要适配多种phy的特性,其配置做成了多层级适配的。即基本配置+特定phy的独立配置

这是核心基本配置的代码

/*
 * Set and/or override some configuration registers based on the
 * marvell,reg-init property stored in the of_node for the phydev.
 *
 * marvell,reg-init = ,...;
 *
 * There may be one or more sets of :
 *
 * reg-page: which register bank to use.
 * reg: the register.
 * mask: if non-zero, ANDed with existing register value.
 * value: ORed with the masked value and written to the regiser.
 *
 */
static int marvell_of_reg_init(struct phy_device *phydev)
{
    const __be32 *paddr;
    int len, i, saved_page, current_page, page_changed, ret;

    if (!phydev->mdio.dev.of_node)
        return 0;

    paddr = of_get_property(phydev->mdio.dev.of_node,
                "marvell,reg-init", &len);
    if (!paddr || len < (4 * sizeof(*paddr)))
        return 0;

    saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
    if (saved_page < 0)
        return saved_page;
    page_changed = 0;
    current_page = saved_page;

    ret = 0;
    len /= sizeof(*paddr);
    for (i = 0; i < len - 3; i += 4) {
        u16 reg_page = be32_to_cpup(paddr + i);
        u16 reg = be32_to_cpup(paddr + i + 1);
        u16 mask = be32_to_cpup(paddr + i + 2);
        u16 val_bits = be32_to_cpup(paddr + i + 3);
        int val;

        if (reg_page != current_page) {
            current_page = reg_page;
            page_changed = 1;
            ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
            if (ret < 0)
                goto err;
        }

        val = 0;
        if (mask) {
            val = phy_read(phydev, reg);
            if (val < 0) {
                ret = val;
                goto err;
            }
            val &= mask;
        }
        val |= val_bits;

        ret = phy_write(phydev, reg, val);
        if (ret < 0)
            goto err;

    }
err:
    if (page_changed) {
        i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
        if (ret == 0)
            ret = i;
    }
    return ret;
}
  • 1
  • 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

通用的驱动会抽象一点,抽时间把驱动流程整理一下


引用wiki词条 Media-independent interface 
https://en.wikipedia.org/wiki/Media-independent_interface#Gigabit_Media_Independent_Interface

PS 常用phy寄存器


/* Advertisement control register. */
#define ADVERTISE_10HALF    0x0020  /* Try for 10mbps half-duplex  */
#define ADVERTISE_1000XFULL 0x0020  /* Try for 1000BASE-X full-duplex */
#define ADVERTISE_10FULL    0x0040  /* Try for 10mbps full-duplex  */
#define ADVERTISE_1000XHALF 0x0040  /* Try for 1000BASE-X half-duplex */
#define ADVERTISE_100HALF   0x0080  /* Try for 100mbps half-duplex */
#define ADVERTISE_1000XPAUSE    0x0080  /* Try for 1000BASE-X pause    */
#define ADVERTISE_100FULL   0x0100  /* Try for 100mbps full-duplex */
#define ADVERTISE_1000XPSE_ASYM 0x0100  /* Try for 1000BASE-X asym pause */
#define ADVERTISE_100BASE4  0x0200  /* Try for 100mbps 4k packets  */


#define ADVERTISE_100_AND_10    (ADVERTISE_10FULL | ADVERTISE_100FULL | \
                                 ADVERTISE_10HALF | ADVERTISE_100HALF)
#define ADVERTISE_100       (ADVERTISE_100FULL | ADVERTISE_100HALF)
#define ADVERTISE_10        (ADVERTISE_10FULL | ADVERTISE_10HALF)

#define ADVERTISE_1000      0x0300


#define IEEE_CONTROL_REG_OFFSET                 0
#define IEEE_STATUS_REG_OFFSET                  1
#define IEEE_AUTONEGO_ADVERTISE_REG             4
#define IEEE_PARTNER_ABILITIES_1_REG_OFFSET     5
#define IEEE_PARTNER_ABILITIES_2_REG_OFFSET     8
#define IEEE_PARTNER_ABILITIES_3_REG_OFFSET     10
#define IEEE_1000_ADVERTISE_REG_OFFSET          9
#define IEEE_MMD_ACCESS_CONTROL_REG             13
#define IEEE_MMD_ACCESS_ADDRESS_DATA_REG        14
#define IEEE_COPPER_SPECIFIC_CONTROL_REG        16
#define IEEE_SPECIFIC_STATUS_REG                17
#define IEEE_COPPER_SPECIFIC_STATUS_REG_2       19
#define IEEE_EXT_PHY_SPECIFIC_CONTROL_REG       20
#define IEEE_CONTROL_REG_MAC                    21
#define IEEE_PAGE_ADDRESS_REGISTER              22

#define IEEE_CTRL_1GBPS_LINKSPEED_MASK          0x2040
#define IEEE_CTRL_LINKSPEED_MASK                0x0040
#define IEEE_CTRL_LINKSPEED_1000M               0x0040
#define IEEE_CTRL_LINKSPEED_100M                0x2000
#define IEEE_CTRL_LINKSPEED_10M                 0x0000
#define IEEE_CTRL_RESET_MASK                    0x8000
#define IEEE_CTRL_AUTONEGOTIATE_ENABLE          0x1000
#define IEEE_STAT_AUTONEGOTIATE_CAPABLE         0x0008
#define IEEE_STAT_AUTONEGOTIATE_COMPLETE        0x0020
#define IEEE_STAT_AUTONEGOTIATE_RESTART         0x0200
#define IEEE_STAT_1GBPS_EXTENSIONS              0x0100
#define IEEE_AN1_ABILITY_MASK                   0x1FE0
#define IEEE_AN3_ABILITY_MASK_1GBPS             0x0C00
#define IEEE_AN1_ABILITY_MASK_100MBPS           0x0380
#define IEEE_AN1_ABILITY_MASK_10MBPS            0x0060
#define IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK      0x0030

#define IEEE_ASYMMETRIC_PAUSE_MASK              0x0800
#define IEEE_PAUSE_MASK                         0x0400
#define IEEE_AUTONEG_ERROR_MASK                 0x8000

#define IEEE_MMD_ACCESS_CTRL_DEVAD_MASK         0x1F
#define IEEE_MMD_ACCESS_CTRL_PIDEVAD_MASK       0x801F
#define IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK     0x401F

#define PHY_R0_ISOLATE                          0x0400
#define PHY_DETECT_REG                          1
#define PHY_IDENTIFIER_1_REG                    2
#define PHY_IDENTIFIER_2_REG                    3
#define PHY_DETECT_MASK                         0x1808
#define PHY_MARVELL_IDENTIFIER                  0x0141
#define PHY_TI_IDENTIFIER                       0x2000

/* Marvel PHY flags */
#define MARVEL_PHY_IDENTIFIER                   0x141
#define MARVEL_PHY_MODEL_NUM_MASK               0x3F0
#define MARVEL_PHY_88E1111_MODEL                0xC0
#define MARVEL_PHY_88E1116R_MODEL               0x240
#define PHY_88E1111_RGMII_RX_CLOCK_DELAYED_MASK 0x0080

你可能感兴趣的:(FPGA)