linux 系统移植(第四期)--Uboot移植(4)--在U-Boot 中添加自己的开发板(3) -网络驱动修改-- Ubuntu20.04

文章目录

前言

[一、I.MX6U-ALPHA 开发板网络简介](#一、I.MX6U-ALPHA 开发板网络简介)

二、硬件原理图原理

[1.ENET1 的网络原理图](#1.ENET1 的网络原理图)

[2.ENET2 的网络原理图](#2.ENET2 的网络原理图)

三、程序代码修改

[1、网络 PHY 地址修改](#1、网络 PHY 地址修改)

[2、删除 uboot 中 74LV595 的驱动代码](#2、删除 uboot 中 74LV595 的驱动代码)

[3、添加 I.MX6U-ALPHA 开发板网络复位引脚驱动](#3、添加 I.MX6U-ALPHA 开发板网络复位引脚驱动)

[4、修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link](#4、修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link)

四、编译下载

总结


前言

接着上一期修改网络驱动。


一、I.MX6U-ALPHA 开发板网络简介

I.MX6UL/ULL内部有个以太网 MAC外设,也就是 ENET,需要外接 一个 PHY芯片来实现网络通信功能,也就是内部 MAC+外部 PHY芯片的方案

有些设备用的DM9000芯片,DM9000 是 "MAC+PHY" 一体芯片,用于无内部 MAC 的处理器,通过类 SRAM 接口通信。

I.MX6U 的内部 MAC 带有专用 DMA,处理网络数据包的效率和速度远高于 DM9000 的 SRAM 读写方式。

I.MX6UL/ULL 有两个网络接口 ENET1 和 ENET2,正点原子的 I.MX6U-ALPHA 开发板提供了这两个网络接口,其中 ENET1 和 ENET2 都使用 LAN8720A 作为 PHY 芯片。NXP 官方的I.MX6ULL EVK 开发板使用 KSZ8081 这颗 PHY 芯片,但实际产品中不一定会使用这个芯片,所以现在我们来讲解一下更换了PHY 芯片,该如何调整网络驱动,使网络工作正常。

二、硬件原理图原理

1.ENET1 的网络原理图

I.MX6U-ALPHA 开发板 ENET1 的网络原理图如下图所示:

ENET1 的网络 PHY 芯片为 LAN8720A ,通过 RMII 接口与 I.MX6ULL 相连, I.MX6U-ALPHA 开发板的 ENET1 引脚与 NXP 官方的 I.MX6ULL EVK 开发板基本一样,唯独复位引脚不同。
从上图中可以看出 ENET1 复位引脚ENET1_RST 接到了 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。
LAN8720A 内部有寄存器, I.MX6ULL 会读取 LAN8720 内部寄存器来判断当前的物理链接状态、连接速度(10M 还是 100M) 和双工状态 ( 半双工还是全双工)。I.MX6ULL 通过 MDIO接口来读取PHY 芯片的内部寄存器,此端口有两个引脚,为 ENET_MDC 和 ENET_MDIO。
1、ENET_MDC 提供时钟。
2、ENET_MDIO 进行数据传输。
注意:一个MDIO接口可以管理32个PHY芯片。MIDO通过PHY ADDR来确定访问那个PHY芯片。ALPHA开发板ENET1的PHY ADDR是0x0,ENET2的PHY ADDR是0X1.
所以我们要修改 ENET1 网络驱动的话重点就三点:
①、 ENET1 复位引脚初始化。
②、 LAN8720A 的器件 ID 。
③、 LAN8720 驱动

2.ENET2 的网络原理图

再来看一下 ENET2 的原理图:

关于 ENET2 网络驱动的修改也注意一下三点:

①、ENET2 的复位引脚,从上图可以看出,ENET2 的复位引脚 ENET2_RST 接到了I.MX6ULL 的 SNVS_TAMPER8 上。

②、ENET2 所使用的 PHY 芯片器件地址,从上图可以看出,PHY 器件地址为 0X1。

③、LAN8720 驱动,ENET1 和 ENET2 都使用的 LAN8720,所以驱动肯定是一样的。

三、程序代码修改

1、网络 PHY 地址修改

首先修改 uboot 中的 ENET1 和 ENET2 的 PHY 地址和驱动,打开 mx6ull_alientek_emmc.h

这个文件,找到如下代码:

复制代码
#ifdef CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MII
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV          1

#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE                 ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR       0x2
#define CONFIG_FEC_XCV_TYPE          RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE                 ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR       0x1

#define CONFIG_FEC_XCV_TYPE          RMII
#endif

#define CONFIG_ETHPRIME              "FEC"

#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif

宏 CONFIG_FEC_ENET_DEV 用于选择使用哪个网口,默认为 1 ,也就是选择ENET2。
此代码中 ENET1 的 PHY 地址,默认是 0X2 ,第 339 行为 ENET2 的 PHY 地址,默
认为 0x1。

而I.MX6U-ALPHA开发板ENET1 的 PHY 地址为0X0,ENET2 的 PHY 地址为0X1。
#define CONFIG_PHY_MICREL ,这个宏用于使能 uboot 中 Micrel 公司的 PHY驱动,KSZ8081 这颗 PHY 芯片就是 Micrel 公司生产的。
我们使用的是 为 LAN8720A芯片,而这个芯片是 SMSC 公司生产的。
这里我们得 将 CONFIG_PHY_MICREL 改为 CONFIG_PHY_SMSC

所以我们要修改以下三个地方:
①、修改 ENET1 网络 PHY 的地址。
②、修改 ENET2 网络 PHY 的地址。
③、使能 SMSC 公司的 PHY 驱动。

复制代码
#ifdef CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MII
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV		1

#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE			ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR          0x0
#define CONFIG_FEC_XCV_TYPE             RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE			ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR		0x1
#define CONFIG_FEC_XCV_TYPE		RMII
#endif
#define CONFIG_ETHPRIME			"FEC"

#define CONFIG_PHYLIB
#define CONFIG_PHY_SMSC
#endif

2、删除 uboot 中 74LV595 的驱动代码

uboot 中网络 PHY 芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开

mx6ull_alientek_emmc.c,找到如下代码:

复制代码
#define IOX_SDI     IMX_GPIO_NR(5, 10)
#define IOX_STCP    IMX_GPIO_NR(5, 7)
#define IOX_SHCP    IMX_GPIO_NR(5, 11)
#define IOX_OE      IMX_GPIO_NR(5, 8)

这些的宏定义是 74LV595 的相关 GPIO,而I.MX6U-ALPHA开发板并没有使用74LV595,所以要删掉,并替换为如下代码:

复制代码
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET	IMX_GPIO_NR(5, 8)

ENET1 的复位引脚连接到 SNVS_TAMPER7 上,对应 GPIO5_IO07,ENET2 的复位引脚连

接到 SNVS_TAMPER8 上,对应 GPIO5_IO08。查找IMX6UL数据手册如下:

继续在 mx6ull_alientek_emmc.c 中找到如下代码:

复制代码
static iomux_v3_cfg_t const iox_pads[] = {
    /* IOX_SDI */
    MX6_PAD_BOOT_MODE0_GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_SHCP */
    MX6_PAD_BOOT_MODE1_GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_STCP */
    MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
    /* IOX_nOE */
    MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};

同理,示例代码 33.2.7.5 是 74LV595 的 IO 配置参数结构体,将其删除掉。继续在
mx6ull_alientek_emmc.c 中找到函数 iox74lv_init,如下所示:

复制代码
static void iox74lv_init(void)
{
    int i;

    gpio_direction_output(IOX_OE, 0);

    for (i = 7; i >= 0; i--) {
        gpio_direction_output(IOX_SHCP, 0);
        gpio_direction_output(IOX_SDI, seq[gp_output[i]][0]);
        udelay(500);
        gpio_direction_output(IOX_SHCP, 1);
        udelay(500);
    }

    /*
     * shift register will be output to pins
     */
    gpio_direction_output(IOX_STCP, 1);
}

void iox74lv_set(int index)
{
    int i;

    for (i = 7; i >= 0; i--) {
        gpio_direction_output(IOX_SHCP, 0);

        if (i == index)
            gpio_direction_output(IOX_SDI, seq[gp_output[i]][0]);
        else
            gpio_direction_output(IOX_SDI, seq[gp_output[i]][1]);

        udelay(500);
        gpio_direction_output(IOX_SHCP, 1);
        udelay(500);
    }

    /*
     * shift register will be output to pins
     */
    gpio_direction_output(IOX_STCP, 1);
}

iox74lv_init 函数是 74LV595 的初始化函数,iox74lv_set 函数用于控制 74LV595 的 IO 输出

电平,将这两个函数全部删除掉!

在 mx6ull_alientek_emmc.c 中找到 board_init 函数,此函数是板子初始化函数,会被

board_init_r 调用,board_init 函数内容如下:

复制代码
int board_init(void)
{
    ......

    imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
    iox74lv_init();

    ......

    return 0;
}

board_init 会调用 imx_iomux_v3_setup_multiple_pads 和 iox74lv_init 这两个函数来初始化

74lv595 的 GPIO,将这两行删除掉。至此,mx6ull_alientek_emmc.c 中关于 74LV595 芯片的驱

动代码都删除掉了,接下来就是添加 I.MX6U-ALPHA 开发板两个网络复位引脚了。

3、添加I.MX6U-ALPHA开发板网络复位引脚驱动

在 mx6ull_alientek_emmc.c 中找到结构体数组 fec1_pads 和 fec2_pads,这两个结构体是 ENET1 和 ENET2 这两个网口的 IO 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数,完成以后如下所示:

复制代码
static iomux_v3_cfg_t const fec1_pads[] = {
    MX6_PAD_GPIO1_IO06_ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
    MX6_PAD_GPIO1_IO07_ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_TX_DATA0_ENET1_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_TX_DATA1_ENET1_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_TX_EN_ENET1_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_TX_CLK_ENET1_REF_CLK_1 | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL),
    MX6_PAD_ENET1_RX_DATA0_ENET1_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_RX_DATA1_ENET1_RDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_RX_EN_ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET1_RX_ER_ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_SNVS_TAMPER7_GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),  /* 初始化ETH1 RESET引脚 */
};

static iomux_v3_cfg_t const fec2_pads[] = {
    MX6_PAD_GPIO1_IO06_ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
    MX6_PAD_GPIO1_IO07_ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_TX_DATA0_ENET2_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_TX_DATA1_ENET2_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_TX_EN_ENET2_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_TX_CLK_ENET2_REF_CLK2 | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL),
    MX6_PAD_ENET2_RX_DATA0_ENET2_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_RX_DATA1_ENET2_RDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_RX_EN_ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_ENET2_RX_ER_ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
    MX6_PAD_SNVS_TAMPER8_GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),  /* 初始化ETH2 RESET引脚 */
};

有注释的那两行代码就是ENET1 和 ENET2 的复位 IO 配置参数。

继续在文件 mx6ull_alientek_emmc.c 中找到函数 setup_iomux_fec,此函数默认代码如下:

函数 setup_iomux_fec 就是根据 fec1_pads 和 fec2_pads 这两个网络 IO 配置数组来初始化

I.MX6ULL 的网络 IO。我们需要在其中添加网络复位 IO 的初始化代码,并且复位一下 PHY 芯

片,修改后的 setup_iomux_fec 函数如下:

复制代码
static void setup_iomux_fec(int fec_id)
{
	if (fec_id == 0)
	{

		imx_iomux_v3_setup_multiple_pads(fec1_pads,
						 ARRAY_SIZE(fec1_pads));

		gpio_direction_output(ENET1_RESET, 1);
		gpio_set_value(ENET1_RESET, 0);
		mdelay(100);
		gpio_set_value(ENET1_RESET, 1);
	}

	else
	{
		imx_iomux_v3_setup_multiple_pads(fec2_pads,
						 ARRAY_SIZE(fec2_pads));
		gpio_direction_output(ENET2_RESET, 1);
		gpio_set_value(ENET2_RESET, 0);
		mdelay(100);
		gpio_set_value(ENET2_RESET, 1);
	}
}

gpio_direction_output(ENET1_RESET, 1);
        gpio_set_value(ENET1_RESET, 0);
        mdelay(100);
        gpio_set_value(ENET1_RESET, 1); 

这个就是对应 ENET1的复位 IO 初始化,将这两个 IO 设置为输出并且硬件复位一下 LAN8720A

同理ENET2也是一样的。

uboot 中的 LAN8720A 驱动有点问题,打开文件 drivers/net/phy/phy.c,找到函数 genphy_update_link,这是个通用 PHY 驱动函数,此函数用于更新 PHY 的连接状态和速度。使用 LAN8720A 的时候需要在此函数中添加一些代码,修改后的函数 genphy_update_link 如下所示:

复制代码
221 int genphy_update_link(struct phy_device *phydev)
222 {
223     unsigned int mii_reg;
224
225 #ifdef CONFIG_PHY_SMSC
226     static int lan8720_flag = 0;
227     int bmcr_reg = 0;
228     if (lan8720_flag == 0) {
229         bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
230         phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
231         while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
232             udelay(100);
233         }
234         phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
235         lan8720_flag = 1;
236     }
237 #endif
238
239     /*
240      * Wait if the link is up, and autonegotiation is in progress
241      * (ie - we're capable and it's not done)
242      */
243     mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
291
292     return 0;
293 }

225 行~237 行就是新添加的代码,为条件编译代码段,只有使用 SMSC 公司的 PHY 这段代码才会执行。

第 229 行读取 LAN8720A 芯片的 BMCR 寄存器(寄存器地址为 0),该寄存器是 LAN8720A 的核心配置寄存器,此行的作用是读取并暂存该寄存器的默认配置值。

第 230 行向 BMCR 寄存器写入复位指令 BMCR_RESET(数值为 0X8000),由于 BMCR 寄存器的第 15 位为软件复位控制位,因此该行操作实质是对 LAN8720A 执行软件复位,复位完成后该位会自动清零。

第 231 至 233 行循环等待 LAN8720A 软件复位完成,具体判断逻辑为:持续读取 BMCR 寄存器的第 15 位,若该位仍为 1,则表示复位尚未完成,需等待(每次等待 100 微秒);若为 0 则说明复位完成,退出循环。

第 234 行将第 229 行读取并保存的 BMCR 寄存器原始配置值重新写入,确保复位后 LAN8720A 恢复到复位前的配置状态。
至此网络的复位引脚驱动修改完成。

四、编译下载

重新编译 uboot ,然后将 u-boot.bin 烧写到 SD 卡中并 启动,uboot 启动信息如下图 所示:

从图中可以看到"Net: FEC1"这一行,提示当前使用的 FEC1 这个网口,也就是 ENET2。在 uboot 中使用网络之前要先设置几个环境变量,命令如下:

复制代码
setenv ipaddr 192.168.1.55          //开发板 IP 地址
setenv ethaddr b8:ae:1d:01:00:00    //开发板网卡 MAC 地址
setenv gatewayip 192.168.1.1        //开发板默认网关
setenv netmask 255.255.255.0        //开发板子网掩码
setenv serverip 192.168.1.250       //服务器地址,也就是 Ubuntu 地址
saveenv                             //保存环境变量

设置好环境变量以后就可以在 uboot 中使用网络了,用网线将 I.MX6U-ALPHA 上的 ENET2

与电脑或者路由器连接起来,保证开发板和电脑在同一个网段内。


总结

修改开发板的uboot网络驱动.

相关推荐
heda32 小时前
zip在linux上解压出错Unicode编码-解决
linux·运维·python
翼龙云_cloud2 小时前
阿里云渠道商:阿里云弹性伸缩混合管理指南
服务器·阿里云·云计算
muddjsv2 小时前
支撑 TCP/IP 协议运行的核心硬件:从物理层到网络层的全梳理
服务器·网络·tcp/ip
2301_765715142 小时前
Linux虚拟机NAT模式网络故障解析与修复指南
linux·运维·服务器
焦糖布丁的午夜2 小时前
数据库大王mysql---linux
linux·数据库·mysql
OpsEye2 小时前
监控 100 问(四):如何实现 IT 监控自动化
运维·网络·it运维·it·监控·监控系统
博图光电2 小时前
博图双目结构光相机——叉车自动化视觉定位解决方案
运维·数码相机·自动化
北京阿法龙科技有限公司2 小时前
告别繁琐巡检:AR智能眼镜打造工业&电力运维闭环体系|阿法龙XR云平台
运维·ar·xr
冰冰菜的扣jio2 小时前
RocketMQ入门——快速搭建
linux·rocketmq