环境
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:官方自测
手头利用vivado建立一个zynq的IPI工程,添加zynq PS,依靠自动生成功能补完最小系统,开始编译……
之后是export hdf—->generate SDK bsp的标准套路
值得记录的是Zynq的PS部分采用MIO和EMIO来复用有限的IO资源,这年头什么都是越来越软:软件定义XXX,软件无线电,HLS
接口采用了MIO结构,也可以软件配置了
在生成的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);
}
代码块做了三件事:为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);
}
顺利的话,裸系统的代码可以辅助理解phy的驱动流程:写寄存器
一般写pcore和写phy均会在驱动中涉及,具体定义参见相关手册
值得记录的是,reg22为页寄存器:IEEE只给phy定义了32个寄存器空间,所以用其中一个寄存器来作为页寄存器,扩展维度
流程参见自测,此处无必要详述
顺手可以在向导中生成一下标准平台的SDK工程:
我选了个ZynqMP SOC的Lwip
从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;
}
是不是很相似呢??其实完全一样么~~~
所以全都是一个套路呀
策略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;
}
通用的驱动会抽象一点,抽时间把驱动流程整理一下
引用wiki词条 Media-independent interface
https://en.wikipedia.org/wiki/Media-independent_interface#Gigabit_Media_Independent_Interface
/* 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