IMX6ULL开发板,系统移植——第一步Uboot移植

        此篇为笔者在学习Linux开发时所遇到的一些操作凝练,旨在想学习Linux开发的小伙伴能不被Uboot移植难住,从而快速进入学习Linux驱动开发。

        另外说明一下,本教程使用的是EMMC版

1、编译nxp官方uboot

        在Uboot开发包目录下,编写shell脚本,命名为mx6ull_emmc.sh

#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

        其中 mx6ull_14x14_evk_emmc_defconfig是uboot包下configs里的配置文件;

        然后给该脚本mx6ull_emmc.sh增加777权限后,执行该编译:

sudo chmod 777 mx6ull_emmc.sh
./mx6ull_emmc.sh

        出现下面提示后表示内核编译通过

IMX6ULL开发板,系统移植——第一步Uboot移植_第1张图片2、烧写nxp官方uboot测试

        将 imxdownload 软件拷贝到 uboot 源码根目录下,然后使用 imxdownload 软件将 u-boot.bin烧写到 SD 卡中

chmod 777 imxdownload     //给予 imxdownload 可执行权限
./imxdownload u-boot.bin /dev/sdd     //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 里面

        烧写完成后,使用SecureCRT打开开发板与电脑连接的端口号,并复位开发板,就会在SecureCRT中打印如下信息:

IMX6ULL开发板,系统移植——第一步Uboot移植_第2张图片

         NXP官方提供的Uboot移植完毕~~

        不过NXP提供的Uboot有很多配置与正点原子的IMX6ULL开发板不匹配,因此要做一下修改

4、移植为自己的开发平台

        在 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

5、添加开发板对应的头文件

        在 目 录 include/configs 下 添 加 I.MX6ULL-ALPHA 开 发 板 对 应 的 头 文 件 , 复 制include/configs/mx6ullevk.h(原厂的),并重命名为 mx6ull_alientek_emmc.h(自己的)

cp include/configs/mx6ullevk.h mx6ull_alientek_emmc.h
将
#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H
修改为
#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H
#define __MX6ULL_ALIENTEK_EMMC_CONFIG_H

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

        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

        还需要对 mx6ull_alientek_emmc 目录下的Makefile imximage.cfgKconfigMAINTAINERS文件做一些修改:

(1)修改 mx6ull_alientek_emmc 目录下的 Makefile 文件

1 # (C) Copyright 2015 Freescale Semiconductor, Inc.
2 #
3 # SPDX-License-Identifier: GPL-2.0+
4 #
5
6 obj-y := mx6ull_alientek_emmc.o
7
8 extra-$(CONFIG_USE_PLUGIN) := plugin.bin
9 $(obj)/plugin.bin: $(obj)/plugin.o
10 $(OBJCOPY) -O binary --gap-fill 0xff $< $@

        第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

(3)修改 mx6ull_alientek_emmc 目录下的 Kconfig 文件

        修改后的内容

if TARGET_MX6ULL_ALIENTEK_EMMC

 config SYS_BOARD
 default "mx6ull_alientek_emmc"

 config SYS_VENDOR
 default "freescale"

 config SYS_SOC
 default "mx6"

 config SYS_CONFIG_NAME
 default "mx6ull_alientek_emmc"

 endif

(4)修改 mx6ull_alientek_emmc 目录下的 MAINTAINERS 文件

        修改后的内容:

MX6ULL_ALIENTEK_EMMC BOARD
M: Peng Fan 
S: Maintained
F: board/freescale/mx6ull_alientek_emmc/
F: include/configs/mx6ull_alientek_emmc.h

7、修改 U-Boot 图形界面配置文件

        修改文件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"

8、使用新添加的板子配置编译 uboot

        新建一个名为 mx6ull_alientek_emmc.sh 的 shell 脚本

#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_alientek_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

        等待编译完成,编译完成以后输入如下命令,查看一下 添加的mx6ull_alientek_emmc.h 这个头文件有没有被引用。

grep -nR "mx6ull_alientek_emmc.h"

IMX6ULL开发板,系统移植——第一步Uboot移植_第3张图片

         编译完成以后就使用 imxdownload 将新编译出来的 u-boot.bin 烧写到 SD 卡中测试:

IMX6ULL开发板,系统移植——第一步Uboot移植_第4张图片

        默认uboot中的LCD 驱动和网络驱动在正点原子的I.MX6U-ALPHA开发板上是有问题的,需要修改。

9、LCD适配

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

        一般修改 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。修改结构体display_info_t const displays[]里的参数为当前型号LCD的参数。并修改mx6ull_alientek_emmc.h中的panel参数。

10、网络驱动修改

(1)查看原理图

要修改 ENET1 网络驱动的话重点就三点:

①、ENET1 复位引脚初始化。

②、LAN8720A 的器件 ID。

③、LAN8720 驱动

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

①、ENET2 的复位引脚,ENET2 的复位引脚 ENET2_RST 接到了I.MX6ULL 的 SNVS_TAMPER8 上。

②、ENET2 所使用的 PHY 芯片器件地址,PHY 器件地址为 0X1。

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

(2)网络 PHY 地址修改

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

//网络默认 ID 配置参数
325 #ifdef CONFIG_CMD_NET
326 #define CONFIG_CMD_PING
327 #define CONFIG_CMD_DHCP
328 #define CONFIG_CMD_MII
329 #define CONFIG_FEC_MXC
330 #define CONFIG_MII
331 #define CONFIG_FEC_ENET_DEV 1
332
333 #if (CONFIG_FEC_ENET_DEV == 0)
334 #define IMX_FEC_BASE ENET_BASE_ADDR
335 #define CONFIG_FEC_MXC_PHYADDR 0x2
336 #define CONFIG_FEC_XCV_TYPE RMII
337 #elif (CONFIG_FEC_ENET_DEV == 1)
338 #define IMX_FEC_BASE ENET2_BASE_ADDR
339 #define CONFIG_FEC_MXC_PHYADDR 0x1
340 #define CONFIG_FEC_XCV_TYPE RMII
341 #endif
342 #define CONFIG_ETHPRIME "FEC"
343
344 #define CONFIG_PHYLIB
345 #define CONFIG_PHY_MICREL
346 #endif

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

        正点原子的 I.MX6U-ALPHA 开发板 ENET1 的 PHY 地址为0X0,ENET2 的 PHY 地址为 0X1,所以需要将第 335 行的宏 CONFIG_FEC_MXC_PHYADDR改为 0x0。

有三处要修改:

①、修改 ENET1 网络 PHY 的地址。

②、修改 ENET2 网络 PHY 的地址。

③、使能 SMSC 公司的 PHY 驱动,即CONFIG_PHY_MICREL 改为 CONFIG_PHY_SMSC。

(3)删除 uboot 中 74LV595 的驱动代码

        NXP 官方I.MX6ULL EVK 开发板使用 74LV595 来扩展 IO,两个网络的复位引脚就是由 74LV595 来控制的。正点原子的 I.MX6U-ALPHA 开发板并没有使用 74LV595,因此我们将 mx6ull_alientek_emmc.c 中的相关代码删除掉

//74LV595 引脚
#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)
参考上面提供的原理图可知io应修改为
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)

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

 //74LV595 的 IO 配置参数结构体,将其删除掉
 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_set 删掉

找到函数 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[qn_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[qn_output[i]][0]);
else
gpio_direction_output(IOX_SDI, seq[qn_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);
};

        找到 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(); ......

        board_init 会调用 imx_iomux_v3_setup_multiple_pads 和 iox74lv_init 这两个函数来初始化74lv595 的 GPIO,将这两行删除掉。接下来就是添加 I.MX6U-ALPHA 开发板两个网络复位引脚了。

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

在 mx6ull_alientek_emmc.c 中找到如下所示代码:

//默认网络 IO 结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 };
652
653 static iomux_v3_cfg_t const fec2_pads[] = {
654 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
655 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
664 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
665 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 };

        结构体数组 fec1_pads 和 fec2_pads 是 ENET1 和 ENET2 这两个网口的 IO 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数

//添加网络复位 IO 后的结构体数组
640 static iomux_v3_cfg_t const fec1_pads[] = {
641 MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
642 MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
649 MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
650 MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
651 MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),// ENET1的复位 IO 配置参数
652 };
653
654 static iomux_v3_cfg_t const fec2_pads[] = {
655 MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
656 MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
......
665 MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
666 MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
667 MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),// ENET2的复位 IO 配置参数
668 };

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

//setup_iomux_fec 函数默认代码
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 imx_iomux_v3_setup_multiple_pads(fec1_pads,
672 ARRAY_SIZE(fec1_pads));
673 else
674 imx_iomux_v3_setup_multiple_pads(fec2_pads,
675 ARRAY_SIZE(fec2_pads));
676 }

        我们需要在其中添加网络复位 IO 的初始化代码,并且复位一下 PHY 芯片,修改后的 setup_iomux_fec 函数如下:

//修改后的 setup_iomux_fec 函数
668 static void setup_iomux_fec(int fec_id)
669 {
670 if (fec_id == 0)
671 {
672
673 imx_iomux_v3_setup_multiple_pads(fec1_pads,
674 ARRAY_SIZE(fec1_pads));
675
676 gpio_direction_output(ENET1_RESET, 1);
677 gpio_set_value(ENET1_RESET, 0);
678 mdelay(20);
679 gpio_set_value(ENET1_RESET, 1);
680 }
681 else
682 {
683 imx_iomux_v3_setup_multiple_pads(fec2_pads,
684 ARRAY_SIZE(fec2_pads));
685 gpio_direction_output(ENET2_RESET, 1);
686 gpio_set_value(ENET2_RESET, 0);
687 mdelay(20);
688 gpio_set_value(ENET2_RESET, 1);
689 }
690 }

(5)修改 drivers/net/phy/phy.c 文件中的函数 genphy_update_link

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

//修改后的 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 }

至此网络的复位引脚驱动修改完成,重新编译 uboot,然后将 u-boot.bin 烧写到 SD 卡中并启动。

后续就可以移植Linux内核以及根文件系统了 !!

你可能感兴趣的:(驱动开发)