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网络驱动.

相关推荐
一叶知秋yyds1 小时前
Ubuntu 虚拟机安装 OpenClaw 完整流程
linux·运维·ubuntu·openclaw
专吃海绵宝宝菠萝屋的派大星2 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
斯普信云原生组2 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器
大数据新鸟2 小时前
操作系统之虚拟内存
java·服务器·网络
safestar20123 小时前
ES批量写入性能调优:BulkProcessor 参数详解与实战案例
java·大数据·运维·jenkins
来一颗砂糖橘3 小时前
负载均衡的多维深度解析
运维·负载均衡
楠奕3 小时前
CentOS7安装GoldenDB单机搭建及常见报错解决方案
linux·运维·服务器
GCTTTTTT4 小时前
远程服务器走本地代理
运维·服务器
剑锋所指,所向披靡!4 小时前
Linux常用指令(2)
linux·运维·服务器
做咩啊~4 小时前
6.增加一个flat网段
服务器·openstack