EMMC SYNC机制_linux

uboot 成功加载之后通常需要检查:(1)、SD 卡和 EMMC 驱动是否正常(常规情况下都是正常的),(2)、LCD 屏幕驱动是否正常(问题不大);(3)、网络驱动是否正常(核心部分)

考虑到后续需要通过网络加载 Linux 内核镜像等,所以网络驱动是必不可缺的部分!

uboot 启动的时候提示 “Board Net Initialization Failed” 和 “No ethernet found.” 这两行,说明网络驱动存在大问题。

EMMC SYNC机制_EMMC SYNC机制_02

作者概述:实际工作中, 一般情况下 SD 卡和 EMMC 等内存驱动都不会存在太大问题,核心部分的问题还是出现在网络驱动部分(比如:S3C2440)!!!

2.2 修改NXP官方uboot

先在 configs 目录下创建默认配置文件,复制 mx6ull_14x14_evk_emmc_defconfig,然后重命名为 mx6ull_alientek_emmc_defconfig,命令如下:

cd configs
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_alientek_emmc_defconfig

然后将文件 mx6ull_alientek_emmc_defconfig 中的内容改成下面的:

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
CONFIG_CMD_GPIO=y

可以看出,mx6ull_alientek_emmc_defconfig 基本和 mx6ull_14x14_evk_emmc_defconfig 中的内容一样,只是第 1 行和第 4 行做了修改。修改的 2 行就是编译 uboot 配置时可以根据自己复制的副本进行配置编译了!

2.2.1 添加开发板对应的头文件

在目录 include/configs 下添加 I.MX6ULL-ALPHA 开 发 板 对 应 的 头 文 件 , 复 制 include/configs/mx6ullevk.h,并重命名为 mx6ull_alientek_emmc.h,命令如下:

cp include/configs/mx6ullevk.h mx6ull_alientek_emmc.h

修改防止重定义的 #ifndef 和 #define 的预处理代码:

EMMC SYNC机制_运维_03

mx6ull_alientek_emmc.h 里面有很多宏定义,这些宏定义基本用于配置 uboot,也有一些 I.MX6ULL 的配置项目。如果我们自己要想使能或者禁止 uboot 的某些功能,那就在 mx6ull_alientek_emmc.h 里面做修改即可。

2.2.2 添加开发板对应的板级文件夹

uboot 中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等等。NXP 的 I.MX 系列芯片的所有板级文件夹都存放在 board/freescale 目录下,在这个目录下有个名为 mx6ullevk 的文件夹,这个文件夹就是 NXP 官方 I.MX6ULL EVK 开发板的板级文件夹。复制 mx6ullevk,将其重命名为 mx6ull_alientek_emmc,命令如下:

cd board/freescale/
cp mx6ullevk/ -r mx6ull_alientek_emmc

进入 mx6ull_alientek_emmc 目录中 , 将 其 中 的 mx6ullevk.c 文 件 重 命 名 为 mx6ull_alientek_emmc.c,命令如下:

cd mx6ull_alientek_emmc
mv mx6ullevk.c mx6ull_alientek_emmc.c

EMMC SYNC机制_开发板_04

我们还需要对 mx6ull_alientek_emmc 目录下的文件做一些修改:

1、修改 mx6ull_alientek_emmc 目录下的 Makefile 文件

将 mx6ull_alientek_emmc 下的 Makefile 文件内容改为如下所示:

EMMC SYNC机制_运维_05

第 6 行的 obj-y,改为 mx6ull_alientek_emmc.o,这样才会编译 mx6ull_alientek_emmc.c 这个文件。

2、修改 mx6ull_alientek_emmc 目录下的 imximage.cfg 文件

将 imximage.cfg 中的下面一句:PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000

改为:PLUGIN    board/freescale/mx6ull-alientek_emmc/plugin.bin 0x00907000

EMMC SYNC机制_开发板_06

3、修改 mx6ull_alientek_emmc 目录下的 Kconfig 文件

修改 Kconfig 文件,修改后的内容如下:

if TARGET_MX6ULL_ALIENTEK_EMMC 

config SYS_BOARD
	default "mx6ull_alientek_emmc"

config SYS_VENDOR
	default "freescale"

config SYS_CONFIG_NAME
	default "mx6ull_alientek_emmc"

endif

4、修改 mx6ull_alientek_emmc 目录下的 MAINTAINERS 文件

修改 MAINTAINERS 文件,修改后的内容如下:

MX6ULLEVK BOARD
M:	Peng Fan <peng.fan@nxp.com>
S:	Maintained
F:	board/freescale/mx6ull_alientek_emmc/
F:	include/configs/mx6ull_alientek_emmc.h
F:	configs/mx6ull_alientek_emmc_deconfig
2.2.3 修改 U-Boot 图形界面配置文件

uboot 是支持图形界面配置。修改文件 arch/arm/cpu/armv7/mx6/Kconfig (如果用的 I.MX6UL 的话,应该修改 arch/arm/Kconfig 这个文件),在 207 行加入如下内容:

config TARGET_MX6ULL_ALIENTEK_EMMC
	bool "Support mx6ull_alientek_emmc"
	select MX6ULL
	select DM
	select DM_THERMAL

在最后一行的 endif 的前一行添加如下内容:

source "board/freescale/mx6ull_alientek_emmc/Kconfig"

EMMC SYNC机制_linux_07

上述操作都是为了能够创建出自己制作的开发板的编译和配置文件,这样以后就可以在自己创建的副本中改动,不需要在半导体厂商提供的 ”蓝本“ 代码上改动!

2.3 LCD驱动修改

一般 uboot 中修改驱动基本都是在 xxx.h 和 xxx.c 这两个文件中进行的,xxx 为板子名称,比如 mx6ull_alientek_emmc.h 和 mx6ull_alientek_emmc.c 这两个文件。

EMMC SYNC机制_运维_08

一般修改 LCD 驱动重点注意以下几点: **①、**LCD 所使用的 GPIO,查看 uboot 中 LCD 的 IO 配置是否正确。 **②、**LCD 背光引脚 GPIO 的配置。 **③、**LCD 配置参数是否正确。

正点原子的 I.MX6U-ALPHA 开发板 LCD 原理图和 NXP 官方 I.MX6ULL 开发板一致,也就是 LCD 的 IO 和背光 IO 都一样的,所以 IO 部分就不用修改了。需要修改的是 LCD 参数,打开文件 mx6ull_alientek_emmc.c,找到如下所示内容:

struct display_info_t const displays[] = {{
	.bus = MX6UL_LCDIF1_BASE_ADDR,
	.addr = 0,
	.pixfmt = 24,
	.detect = NULL,
	.enable	= do_enable_parallel_lcd,
	.mode	= {
		.name			= "TFT43AB",
		.xres           = 480,
		.yres           = 272,
		.pixclock       = 108695,
		.left_margin    = 8,
		.right_margin   = 4,
		.upper_margin   = 2,
		.lower_margin   = 4,
		.hsync_len      = 41,
		.vsync_len      = 10,
		.sync           = 0,
		.vmode          = FB_VMODE_NONINTERLACED
        } 
    } 
};

代码中定义了一个变量 displays,类型为 display_info_t,这个结构体是 LCD 信息结构体,其中包括了 LCD 的分辨率,像素格式,LCD 的各个参数等。 我们需要根据自己 LCD 屏幕的参数去修改该 displays 变量内的数据,作者是 7 寸屏幕,修改后如下:

struct display_info_t const displays[] = {{
	.bus = MX6UL_LCDIF1_BASE_ADDR,
	.addr = 0,
	.pixfmt = 24,
	.detect = NULL,
	.enable	= do_enable_parallel_lcd,
	.mode	= {
		.name			= "TFT7016",
		.xres           = 1024,
		.yres           = 600,
		.pixclock       = 19531,
		.left_margin    = 140,
		.right_margin   = 160,
		.upper_margin   = 20,
		.lower_margin   = 12,
		.hsync_len      = 20,
		.vsync_len      = 3,
		.sync           = 0,
		.vmode          = FB_VMODE_NONINTERLACED
        } 
    } 
};

打开 mx6ull_alientek_emmc.h,找到所有如下语句:

panel = TFT43AB 改为 panel = TFT7016

EMMC SYNC机制_驱动开发_09

2.4 网络驱动修改

网络驱动的修改是非常繁琐且重要的,因为后续 Linux 内核的加载,虚拟根文件系统 rootfs 都离不开网络驱动的支持。

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

I.MX6UL/ULL 内部有个以太网 MAC 外设,也就是 ENET,需要外接一个 PHY 芯片来实现网络通信功能,也就是内部 MAC+外部 PHY 芯片的方案。大家可能听过 DM9000 这个网络芯片,在一些没有内部 MAC 的 CPU 中,比如三星的 2440,4412 等,就会采用 DM9000 来实现联网功能。DM9000 提供了一个类似 SRAM 的访问接口,主控 CPU 通过这个接口即可与 DM9000 进行通信,DM9000 就是一个 MAC+PHY 芯片。

I.MX6UL/ULL 有两个网络接口 ENET1 和 ENET2,正点原子的 I.MX6U-ALPHA 开发板提供了这两个网络接口,其中 ENET1 和 ENET2 都使用 LAN8720A 作为 PHY 芯片(正点的外部 PHY 芯片与 NXP 官方的 IMX6ULL 不一样)。

网络驱动的匹配需要根据硬件的 PCB 原理图来进行分析,需要把握住根据 PHY 芯片的连接引脚与芯片驱动进行修改!

正点原子 IMX6ULL 的 ENET1:

EMMC SYNC机制_EMMC SYNC机制_10

ENET1 的网络 PHY 芯片为 LAN8720A,通过 RMII 接口与 I.MX6ULL 相连,正点原子 I.MX6U-ALPHA 开发板的 ENET1 引脚与 NXP 官方的 I.MX6ULL EVK 开发板基本一样,唯独复位引脚不同。从上图可以看出,正点原子 I.MX6U-ALPHA 开发板的 ENET1 复位引脚 ENET1_RST 接到了 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。I.MX6U-ALPHA 开发板 ENET1 上连接的 LAN8720A器件地址为 0X0,所示我们要修改 ENET1 网络驱动的话重点就三点:

①、ENET1 复位引脚初始化。 ②、LAN8720A 的器件 ID。 ③、LAN8720 驱动

正点原子 IMX6ULL 的 ENET2:

EMMC SYNC机制_运维_11

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

①、ENET2 的复位引脚,从上图可以看出,ENET2 的复位引脚 ENET2_RST 接到了 I.MX6ULL 的 SNVS_TAMPER8 上。 ②、ENET2 所使用的 PHY 芯片器件地址,从上图可以看出,PHY 器件地址为 0X1。 ③、LAN8720 驱动,ENET1 和 ENET2 都使用的 LAN8720,所以驱动肯定是一样的。

2.4.2 网络 PHY 地址修改

首先修改 uboot 中的 ENET1 和 ENET2 的 PHY 地址和驱动,打开 mx6ull_alientek_emmc.h 这个文件,找到如下代码并进行如下修改:

EMMC SYNC机制_驱动开发_12

如果要使用 LAN8720A,那么就得将 CONFIG_PHY_MICREL 改为 CONFIG_PHY_SMSC,也就是使能 uboot 中的 SMSC 公司中的 PHY 驱动,因为 LAN8720A 就是 SMSC 公司生产的。

通过宏定义:CONFIG_FEC_ENET_DEV 的 0 或 1 决定是选择启用 ENET1 和  ENET2;

需要修改的代码部分如下:

**1、**修改 ENET1 网络 PHY 的地址。

**2、**修改 ENET2 网络 PHY 的地址。

**3、**使能 SMSC 公司的 PHY 驱动。

2.4.3 删除 uboot 中 74LV595 的驱动代码

uboot 中网络 PHY 芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开 mx6ull_alientek_emmc.c,找到如下代码进行如下修改:

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

/* #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) */
#define ENET1_RESET IMX_GPIO_NR(5,7)
#define ENET2_RESET IMX_GPIO_NR(5,8)

EMMC SYNC机制_EMMC SYNC机制_13

继续在 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),
};

继续在 mx6ull_alientek_emmc.c 中找到函数 iox74lv_init,iox74lv_init 函数是 74LV595 的初始化函数,iox74lv_set 函数用于控制 74LV595 的 IO 输出电平,将这两个函数全部删除掉!

在 mx6ull_alientek_emmc.c 中找到 board_init 函数,此函数是板子初始化函数,会被 board_init_r 调用,board_init 会调用 imx_iomux_v3_setup_multiple_pads 和 iox74lv_init 这两个函数来初始化 74lv595 的 GPIO,将这两行删除掉。至此,mx6ull_alientek_emmc.c 中关于 74LV595 芯片的驱动代码都删除掉了,接下来就是添加 I.MX6U-ALPHA 开发板两个网络复位引脚了。

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

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

/*
 * pin conflicts for fec1 and fec2, GPIO1_IO06 and GPIO1_IO07 can only
 * be used for ENET1 or ENET2, cannot be used for both.
 */
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_CLK1 | 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_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
	MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
	MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
};

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_CLK__ENET2_REF_CLK2 | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL),
	MX6_PAD_ENET2_TX_EN__ENET2_TX_EN | MUX_PAD_CTRL(ENET_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),
};

继续在文件 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(20);
		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(20);
		gpio_set_value(ENET2_RESET,1);
	}		
}

代码中分别对 ENET1 和 ENET2 的复位 IO 初始化,将这两个 IO 设置为输出并且硬件复位一下 LAN8720A,这个硬件复位很重要!否则可能导致 uboot 无法识别 LAN8720A。

2.4.5 修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link

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

/**
 * genphy_update_link - update link status in @phydev
 * @phydev: target phy_device struct
 *
 * Description: Update the value in phydev->link to reflect the
 *   current link value.  In order to do this, we need to read
 *   the status register twice, keeping the second value.
 */
int genphy_update_link(struct phy_device *phydev)
{
	unsigned int mii_reg;

	 static int lan8720_flag = 0;
	 int bmcr_reg = 0;
	 if (lan8720_flag == 0) {
	 bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); 
	 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); 
	 while(phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0X8000) {
	 udelay(100); 
	 }
	 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg); 
	 lan8720_flag = 1;
	 }
	/*
	 * Wait if the link is up, and autonegotiation is in progress
	 * (ie - we're capable and it's not done)
	 */
	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

	/*
	 * If we already saw the link up, and it hasn't gone down, then
	 * we don't need to wait for autoneg again
	 */
	if (phydev->link && mii_reg & BMSR_LSTATUS)
		return 0;

	if ((phydev->autoneg == AUTONEG_ENABLE) &&
	    !(mii_reg & BMSR_ANEGCOMPLETE)) {
		int i = 0;

		printf("%s Waiting for PHY auto negotiation to complete",
			phydev->dev->name);
		while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
			/*
			 * Timeout reached ?
			 */
			if (i > PHY_ANEG_TIMEOUT) {
				printf(" TIMEOUT !\n");
				phydev->link = 0;
				return 0;
			}

			if (ctrlc()) {
				puts("user interrupt!\n");
				phydev->link = 0;
				return -EINTR;
			}

			if ((i++ % 500) == 0)
				printf(".");