裕泰微YT8521SH PHY芯片在uboot下的代码适配(一)
本文主要是介绍uboot下的适配代码来源和具体修改。
https://github.com/starfive-tech/u-boot/blob/JH7110_VisionFive2_devel/drivers/net/phy/motorcomm.c#L307
星火科技的PHY用的裕泰微,所以基础的uboot demo可以用他们家的补丁。但是具体配置都不是很对,还是看我的代码为准。
kernel下的PHY驱动
https://github.com/ROCm/ROCK-Kernel-Driver/blob/cc2d8659760f7729b3bed35dc7870eecc5499fd4/drivers/net/phy/motorcomm.c
https://github.com/rockchip-linux/kernel/blob/9ed2be4b9c001ca8006cb4c72928c09927c44f89/drivers/net/phy/motorcomm.c#L2
https://github.com/cnxsoft/YT8521S/tree/main
这个写的很好,应该是正式的kernel 驱动补丁。
https://patchwork.kernel.org/project/netdevbpf/patch/20220620023621.1852-1-Frank.Sae@motor-comm.com/
在 …/u-boot/drivers/net/phy 目录下添加motorcomm.c
修改…/ u-boot/drivers/net/phy 目录下的 Kconfig 文件
添加
config PHY_YT
bool “YT Ethernet PHYs support”
修改…/ u-boot /drivers/net/phy 目录下的 Makefile
添加
obj-$(CONFIG_PHY_YT) += motorcomm.o
修改…/ u-boot /drivers/net/phy 目录下的 phy.c
添加
#ifdef CONFIG_PHY_YT
phy_yutai_init();
#endif
./include/configs/S1020.h中添加CONFIG_PHY_YT=y
,打开CONFIG_RGMII
在./board/中
这里需要同步修改,在uboot下使用哪一个网口进行传输就define哪一个BASE_ADDR
phy_addr的修改需要对照硬件原理图进行。
如图的话就是3。
motorcomm.c如下
// SPDX-License-Identifier: GPL-2.0+
/*
* RealTek PHY drivers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
* author Andy Fleming
*
*/
#include <config.h>
#include <common.h>
#include <phy.h>
#include <bitfield.h>
#define REG_PHY_SPEC_STATUS 0x11
#define REG_DEBUG_ADDR_OFFSET 0x1e
#define REG_DEBUG_DATA 0x1f
#define EXTREG_SLEEP_CONTROL 0x27
#define YTPHY_EXTREG_CHIP_CONFIG 0xa001
#define YTPHY_EXTREG_RGMII_CONFIG1 0xa003
#define YTPHY_PAD_DRIVES_STRENGTH_CFG 0xa010
#define YTPHY_DUPLEX 0x2000
#define YTPHY_DUPLEX_BIT 13
#define YTPHY_SPEED_MODE 0xc000
#define YTPHY_SPEED_MODE_BIT 14
#define YTPHY_RGMII_SW_DR_MASK GENMASK(5, 4)
#define YTPHY_RGMII_RXC_DR_MASK GENMASK(15, 13)
#define YT8521_EXT_CLK_GATE 0xc
#define YT8521_EN_SLEEP_SW_BIT 15
#define SPEED_UNKNOWN -1
#define MOTORCOMM_PHY_ID_MASK 0x00000fff
static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
{
int ret;
ret = phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return phy_read(phydev, MDIO_DEVAD_NONE, REG_DEBUG_DATA);
}
static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
{
int ret;
ret = phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return phy_write(phydev, MDIO_DEVAD_NONE, REG_DEBUG_DATA, val);
}
static int ytphy_parse_status(struct phy_device *phydev)
{
int val;
int speed, speed_mode, duplex;
val = phy_read(phydev, MDIO_DEVAD_NONE, REG_PHY_SPEC_STATUS);
if (val < 0)
return val;
duplex = (val & YTPHY_DUPLEX) >> YTPHY_DUPLEX_BIT;
speed_mode = (val & YTPHY_SPEED_MODE) >> YTPHY_SPEED_MODE_BIT;
printf("speed: %d,duplex: %d\n", speed, duplex );
switch (speed_mode) {
case 2:
speed = SPEED_1000;
break;
case 1:
speed = SPEED_100;
break;
default:
speed = SPEED_10;
break;
}
phydev->speed = speed;
phydev->duplex = duplex;
return 0;
}
static int ytphy_startup(struct phy_device *phydev)
{
int retval;
retval = genphy_update_link(phydev);
if (retval)
return retval;
ytphy_parse_status(phydev);
return 0;
}
static void ytphy_of_config(struct phy_device *phydev)
{
u32 val;
u32 cfg;
int i;
val = ytphy_read_ext(phydev, YTPHY_EXTREG_CHIP_CONFIG);
printf("read reg 0xa001 = 0x%x\n",val);
val &= (~0x3f); //mode_sel:UTP_TO_RGMII LDO 3.3V
val |= (1 << 8); //rxc_dly_en
ytphy_write_ext(phydev, 0xa001, val);
val = ytphy_read_ext(phydev, YTPHY_PAD_DRIVES_STRENGTH_CFG);
val |= (0x30); //dr_rx_rgmii:strongest
ytphy_write_ext(phydev, YTPHY_PAD_DRIVES_STRENGTH_CFG, val);
val = ytphy_read_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1);
printf("read reg 0xa003 = 0x%x\n",val);
ytphy_write_ext(phydev, YTPHY_EXTREG_RGMII_CONFIG1, val);
ytphy_write_ext(phydev, 0xa000, 0);
}
static int yt8521_config(struct phy_device *phydev)
{
int ret, val;
ret = 0;
/*set delay config*/
ytphy_of_config(phydev);
/* disable auto sleep */
val = ytphy_read_ext(phydev, EXTREG_SLEEP_CONTROL);
if (val < 0)
return val;
val &= ~(1 << YT8521_EN_SLEEP_SW_BIT);
ret = ytphy_write_ext(phydev, EXTREG_SLEEP_CONTROL, val);
if (ret < 0)
return ret;
val = ytphy_read_ext(phydev, YT8521_EXT_CLK_GATE);
genphy_config_aneg(phydev);
return 0;
}
static struct phy_driver YT8521_driver = {
.name = "YuTai YT8521",
.uid = 0x0000011a,
.mask = 0x00000fff,
.features = PHY_GBIT_FEATURES,
.config = &yt8521_config,
.startup = &ytphy_startup,
.shutdown = &genphy_shutdown,
};
int phy_yutai_init(void)
{
phy_register(&YT8521_driver);
return 0;
}
在./driver/net/phy.c
中的phy_reset
修改
参考的是Zynq-Linux移植学习笔记之49-国产ZYNQ适配国产裕太PHY网络调试
为什么要这么写呢?参考下面的寄存器说明:
正常就可以ping通了
我的硬件原理图上是2ns延迟,和以下配置
对应DATASHEET中的A001寄存器
以上其他详细的寄存器配置说明可以看YT8521SH 的 Datasheet。