【正点原子I.MX6U-MINI移植篇】kernel移植过程详解(二)

一、下载Linux内核

这里使用NXP官方提供的Linux源码,将其移植到正点原子I.MX6U-MINI开发板上。NXP官方原版Liux源码路径为:1、例程源码->4、NXP官方原版Uboot和Linux->linux-imx-rel_imx4.1.15_2.1.0_ga.tar.bz2

在ubunut中的/home/zhiguoxin/linux/IMX6ULL/目录下创建一个nxp_kernel文件夹用于存放NXP的内核。使用FileZilla将其发送到Ubuntu中并解压,得到名为linux-imx-rel_imx4.1.15_2.1.0_ga的目录。

cd /home/zhiguoxin/linux/IMX6ULL/
mkdir nxp_kernel
cd nxp_kernel
tar -vxjf linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

二、NXP官方开发板Linux内核编译

NXP提供的Linux源码肯定是可以在自己的I.MX6ULL EVK开发板上运行下去的,所以我们肯定是以I.MX6 ULL EVK开发板为参考,然后将Linux内核移植到I.MX6U-ALPHA开发板上的。

2.1 修改顶层Makefile

修改顶层Makefile,直接在顶层Makefile文件里面定义ARCH和CROSS COMPILE这两个的变量值为armarm-linux-gnueabihf-,结果如图所示:

2.2 配置并编译Linux内核

在编译 Linux 内核之前要先配置 Linux 内核。每个板子都有其对应的默认配置文件,这 些默认配置文件保存 在 arch/arm/configs 目录中。

imx_v7_defconfig 和imx_v7_mfg_defconfig 都可作为 I.MX6ULL EVK 开发板所使用的默认配置文件。但是这里建议使用 imx_v7_mfg_defconfig 这个默认配置文件,首先此配置文件默认支持 I.MX6UL 这款芯片,而且重要的一点就是此文件编译出来的 zImage 可以通过 NXP 官方提供的 MfgTool 工具烧写!!imx_v7_mfg_defconfig 中的“mfg”的意思就是 MfgTool。

进入到 Ubuntu 中的 Linux 源码根目录下,执行如下命令配置 Linux 内核:

make clean  //第一次编译 Linux 内核之前先清理一下
make imx_v7_mfg_defconfig //配置 Linux 内核

配置完成以后就可以编译了,使用如下命令编译 Linux 内核:

make -j16  //编译 Linux 内核

等待编译完成,结果如图所示:

Linux 内核编译完成以后会在arch/arm/boot 目录下生成zImage镜像文件,如果使用设备树的话还会在 arch/arm/boot/dts 目录下开发板对应的.dtb(设备树)文件,比如imx6ull-14x14-evk.dtb就是 NXP 官方的I.MX6ULL EVK开发板对应的设备树文件。至此我们得到两个文件:

  • ①、Linux内核镜像文件:zImage
  • ②、NXP官方 I.MX6ULL EVK开发板对应的设备树文件:imx6ull-14x14-evk.dtb

2.3 Linux内核启动测试

在上面已经得到了 NXP 官方I.MX6ULL EVK 开发板对应的 zImage和imx6ull-14x14-evk.dtb 这两个文件。这两个文件能不能在正点原子的 I.MX6U-ALPHA EMMC 版开发板上启动呢?测试一下不就知道了。

在测试之前确保 uboot 中的环境变量bootargs内容如下:

console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw

使用命令

setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv

将上一小节编译出来的zImage和imx6ull-14x14-evk.dtb复制到Ubuntu 中的tftp目录下,因为我们要在 uboot 中使用 tftp 命令将其下载到开发板中,拷贝命令如下:

cp arch/arm/boot/zImage /home/zhiguoxin/linux/tftp -f
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/zhiguoxin/linux/tftp -f

拷贝完成以后就可以测试了,启动开发板,进入uboot 命令行模式,然后输入如下命令将zImage 和 imx6ull-14x14-evk.dtb 下载到开发板中并启动:

tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000

可以看出,此时Linux内核已经启动了。

三、在Linux中添加自己的开发板

通过编译NXP官方I.MX6ULL EVK 开发板对应的Linux内核,发现其可以在正点原子的EMMC版本开发板启动,所以我们就参考I.MX6ULL EVK开发板的设置,在Linux内核中添加正点原子的I.MX6U-ALPHA开发板。

3.1 添加开发板默认配置文件 件

将 arch/arm/configs目录下的imx_v7_mfg_defconfig重新复制一份 , 命名 为imx_alientek_emmc_defconfig,命令如下:

cd arch/arm/configs
cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig

以后imx_alientek_emmc_defconfig就是正点原子的EMMC版开发板默认配置文件了。完成以后如图所示:

以后就可以使用如下命令来配置正点原子EMMC版开发板对应的Linux内核了:

make imx_alientek_emmc_defconfig

3.2 添加开发板对应的设备树文件

添加适合正点原子 EMMC 版开发板的设备树文件,进入目录 arch/arm/boot/dts 中,复制一份 imx6ull-14x14-evk.dts,然后将其重命名为 imx6ull-alientek-emmc.dts,命令如下:

cd arch/arm/boot/dts
cp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts

.dts是设备树源码文件,编译Linux的时候会将其编译为.dtb文件。imx6ull-alientek-emmc.dts创建好以后我们还需要修改文件arch/arm/boot/dts/Makefile, 找 到 “dtb-$(CONFIG_SOC_IMX6ULL)”配置项,在此配置项中加入“imx6ull-alientek-emmc.dtb” ,如下所示:

第422行为“imx6ull-alientek-emmc.dtb”,这样编译Linux 的时候就可以从 imx6ull-alientek-emmc.dts编译出 imx6ull-alientek-emmc.dtb文件了。

3.3 编译测试

创建一个编译脚本,imx6ull_alientek_emmc.sh,脚本内容如下:

touch imx6ull_alientek_emmc.sh
#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

  • 第2行,清理工程。
  • 第3行,使用默认配置文件imx_alientek_emmc_defconfig来配置Linux内核。
  • 第4行,打开Linux的图形配置界面,如果不需要每次都打开图形配置界面可以删除此行。
  • 第5行,编译Linux。

执行shell脚本 imx6ull_alientek_emmc.sh编译Linux内核,命令如下:

chmod 777 imx6ull_alientek_emmc.sh //给予可执行权限
./imx6ull_alientek_emmc.sh //执行 shell 脚本编译内核

编译完成以后就会在目录arch/arm/boot下生成zImage镜像文件。在arch/arm/boot/dts目录下生成 imx6ull-alientek-emmc.dtb文件。将这两个文件拷贝到tftp目录下,

cp arch/arm/boot/zImage /home/zhiguoxin/linux/tftp -f
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/zhiguoxin/linux/tftp -f

然后重启开发板,在uboot命令模式中使用tftp命令下载这两个文件并启动,命令如下:

tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 8080000083000000

只要出现如图 所示内容就表示 Linux 内核启动成功:

 Linux 内核启动

Linux内核启动成功,说明我们已经在NXP提供的 Linux内核源码中添加了正点原子I.MX6UL-MINI开发板。

四、CPU主频和网络驱动修改

4.1 CPU主频修改

正点原子I.MX6U-MINI开发板所使用的 I.MX6ULL 芯片主频都是792MHz 的,也就是NXP官方宣传的800MHz版本。

1 、设置 I.MX6U-MINI开发板工作在792MHz

确保EMMC中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令),输入如下命令查看cpu信息:

cat /proc/cpuinfo

结果如图所示:

在图中有BogoMIPS这一条,此时 BogoMIIS 为 3.00,BogoMIPS是Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS 值就越大。

BogoMIPS只是粗略的计算CPU性能,并不十分准确。但是我们可以通过 BogoMIPS 值来大致的判断当前处理器的性能。在图中并没有看到当前 CPU 的工作频率,那我们就转变另一种方法查看当前CPU的工作频率。

进入到目录/sys/bus/cpu/devices/cpu0/cpufreq中,此目录下会有很多文件,使用如下命令查看当前CPU频率:

cd /sys/bus/cpu/devices/cpu0/cpufreq
cat cpuinfo_cur_freq

4.2 使能8线EMMC驱动

正点原子EMMC版本核心板上的EMMC 采用的8位数据线,原理图如图所示:

Linux内核驱动里面 EMMC默是4线模式的,4线模式肯定没有8线模式的速度快,所以本节我们将EMMC的驱动修改为8线模式。

修改方法很简单,直接修改设备树即可,打开文件 imx6ull-alientek-emmc.dts,找到如下所示内容:

cd arch/arm/boot/dts
vi imx6ull-alientek-emmc.dts
&usdhc2 {
    
    
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>;
non-removable;
status = "okay";
};

修改完成以后保存一下 imx6ull-alientek-emmc.dts,然后使用命令make dtbs重新编译一下设备树,编译完成以后使用新的设备树重启Linux 系统即可。

4.3 修改网络驱动

因为在后面学习Linux驱动开发的时候要用到网络调试驱动,所以必须要把网络驱动调试好。在讲解uboot移植的时候就已经说过了,正点原子开发板的网络和NXP官方的网络硬件上不同,网络PHY芯片由KSZ8081换为了 LAN8720A,两个网络 PHY芯片的复位IO也不同。所以Linux内核自带的网络驱动是驱动不起来I.MX6U-MINI开发板上的网络的,需要做修改。

4.3.1 修改LAN8720的复位以及网络时钟引脚驱动

ENET1复位引脚ENET1_RST连接在 I.M6ULL的SNVS_TAMPER7这个引脚上。ENET2的复位引脚 ENET2_RST 连接在I.MX6ULL 的SNVS_TAMPER8上。打开设备树文件imx6ull-alientek-emmc.dts,找到如下代码:

将 588 和 589 这两行删除掉!

删除掉以后继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码,将示例代码中的第 129 行和第 133 行处的代码删除掉!!否则会干扰到网络复位引脚!

 在 imx6ull-alientek-emmc.dts 里面找到名为“iomuxc_snvs”的节点(就是直接搜索),然后在此节点下添加网络复位引脚信息,添加完成以后的“iomuxc_snvs”的节点内容如下:

    /*enet1 reset zhiguoxin*/
    pinctrl_enet1_reset: enet1resetgrp {
    
    
            fsl,pins = <
            /* used for enet1 reset */
            MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
        >;
    };

    /*enet2 reset zhiguoxin*/
    pinctrl_enet2_reset: enet2resetgrp {
    
    
            fsl,pins = <
            /* used for enet2 reset */
            MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
        >;
    };

第 1 行,imx6ull-alientek-emmc.dts 文件中 iomuxc_snvs 节点。
第 559~600 行,ENET1 网络复位引脚配置信息。
第 603~608 行,ENET2 网络复位引脚配置信息。

最后还需要修改一下 ENET1 和 ENET2 的网络时钟引脚配置,继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码


第 316和 331行,分别为 ENET1 和 ENET2 的网络时钟引脚配置信息,将这两个引脚的电气属性值改为0x4001b009,原来默认值为 0x4001b031。

修改完成以后记得保存一下 imx6ull-alientek-emmc.dts,网络复位以及时钟引脚驱动就修改好了。

4.3.2 修改fec1和fec2节点的pinctrl-0属性

在 imx6ull-alientek-emmc.dts 文件中找到名为“fec1”和“fec2”的这两个节点,修改其中的“pinctrl-0”属性值,修改以后如下所示:

4.3.3 修改LAN8720A的PHY地址

我们说过ENET1的LAN8720A地址为0x0,ENET2的LAN8720A地址为0x1。在 imx6ull-alientek-emmc.dts中找到如下代码:

  • 第 171 和 172 行,添加了 ENET1 网络复位引脚所使用的IO为GPIO5_IO07,低电平有效。复位低电平信号持续时间为 200ms。
  • 第 180 和 181 行,ENET2 网络复位引脚所使用的 IO 为 GPIO5_IO08,同样低电平有效,持续时间同样为 200ms。
  • 第 191 和 197 行,“smsc,disable-energy-detect”表明 PHY 芯片是 SMSC 公司的,这样 Linux内核就会找到 SMSC 公司的 PHY 芯片驱动来驱动 LAN8720A。
  • 第 190 行,注意“ethernet-phy@”后面的数字是 PHY 的地址,ENET1 的 PHY 地址为 0,所以“@”后面是 0(默认为 2)。
  • 第 193 行,reg 的值也表示 PHY 地址,ENET1 的 PHY 地址为 0,所以 reg=0。
  • 第 196 行,ENET2 的 PHY 地址为 1,因此“@”后面为 1。
  • 第 199 行,因为 ENET2 的 PHY 地址为 1,所以 reg=1。
    至此,LAN8720A 的 PHY 地址就改好了,保存一下 imx6ull-alientek-emmc.dts 文件。然后使用“make dtbs”命令重新编译一下设备树。

4.3.4 修改fec_main.c文件

要在I.MX6ULL上使用LAN8720A, 需要修改一下Linux内核源码 ,打开
drivers/net/ethernet/freescale/fec_main.c,找到函数fec_probe,在 fec_probe中加入如下代码:

cd drivers/net/ethernet/freescale
vi fec_main.c
/* 设置 MX6UL_PAD_ENET1_TX_CLK 和 MX6UL_PAD_ENET2_TX_CLK
* 这两个 IO 的复用寄存器的 SION 位为 1。
*/
void __iomem *IMX6U_ENET1_TX_CLK;
void __iomem *IMX6U_ENET2_TX_CLK;

IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4);
writel(0X14, IMX6U_ENET1_TX_CLK);

IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC, 4);
writel(0X14, IMX6U_ENET2_TX_CLK);


第 3455~3462 就是新加入的代码,如果要在 I.MX6ULL 上使用 LAN8720A 就需要设置ENET1 和 ENET2 的 TX_CLK 引脚复位寄存器的 SION 位为1。

4.4.5 配置Linux内核,使能LAN8720驱动

输入命令“make menuconfig”,打开图形化配置界面,选择使能 LAN8720A 的驱动,路径如下:

-> Device Drivers
	-> Network device support
		-> PHY Device support and infrastructure
			-> Drivers for SMSC PHYs

 使能 LAN8720A 驱动

选择将Drivers for SMSC PHYs编译到Linux内核中,因此<>里面变为了*。LAN8720A是SMSC公司出品的,因此勾选这个以后就会编译LAN8720驱动,配置好以后退出配置界面,然后重新编译一下Linux内核。

4.4.6 修改 smsc.c文件

在修改 smsc.c 文件之前先说点题外话,那就是我是怎么确定要修改 smsc.c 这个文件的。在写本书之前我并没有修改过 smsc.c 这个文件,都是使能 LAN8720A 驱动以后就直接使用。

但是我在测试 NFS 挂载文件系统的时候发现文件系统挂载成功率很低!老是提示NFS服务器找不到,三四次就有一次挂载失败!很折磨人。NFS 挂载就是通过网络来挂载文件系统,这样做的好处就是方便我们后续调试 Linux驱动。既然老是挂载失败那么可以肯定的是网络驱动有问题,网络驱动分两部分:内部MAC+外部 PHY,内部MAC驱动是由NXP提供的,一般不会出问题,否则的话用户早就给NXP反馈了。而且我用NXP官方的开发板测试网络是一直正常的,但是NXP官方的开发板所使用的PHY芯片为 KSZ8081。所以只有可能是外部PHY,也就是LAN8720A 的驱动可能出问题了。

鉴于LAN8720A 有“前车之鉴”,那就是在 uboot 中需要对LAN8720A 进行一次软复位,要设置 LAN8720A 的 BMCR(寄存器地址为 0)寄存器 bit15 为 1。所以我猜测,在 Linux 中也需要对 LAN8720A 进行一次软复位。

首先需要找到 LAN8720A 的驱动文件,LAN8720A 的驱动文件是 drivers/net/phy/smsc.c,在此文件中有个叫做 smsc_phy_reset 的函数,看名字都知道这是 SMSC PHY 的复位函数,因此,LAN8720A 肯定也会使用到这个复位函数,修改此函数的内容,修改以后的 smsc_phy_reset函数内容如下所示:

cd drivers/net/phy
vi smsc.c
static int smsc_phy_reset(struct phy_device *phydev)
{
    
    
    int err, phy_reset;
    int msec = 1;
    struct device_node *np;
    int timeout = 50000;

    if(phydev->addr == 0) /* FEC1 */
    {
    
    
        np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@188000");

        if(np == NULL)
        {
    
    
            return -EINVAL;
        }
    }

    if(phydev->addr == 1) /* FEC2 */
    {
    
    
        np = of_find_node_by_path("/soc/aips-bus@02000000/ethernet@0b4000");

        if(np == NULL)
        {
    
    
            return -EINVAL;
        }
    }

    err = of_property_read_u32(np, "phy-reset-duration", &msec);

    /* A sane reset duration should not be longer than 1s */
    if (!err && msec > 1000)
        msec = 1;

    phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);

    if (!gpio_is_valid(phy_reset))
        return;

    gpio_direction_output(phy_reset, 0);
    gpio_set_value(phy_reset, 0);
    msleep(msec);
    gpio_set_value(phy_reset, 1);

    int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);

    if (rc < 0)
        return rc;

    /* If the SMSC PHY is in power down mode, then set it
    * in all capable mode before using it.
    */
    if ((rc & MII_LAN83C185_MODE_MASK) ==
            MII_LAN83C185_MODE_POWERDOWN)
    {
    
    

        /* set "all capable" mode and reset the phy */
        rc |= MII_LAN83C185_MODE_ALL;
        phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
    }

    phy_write(phydev, MII_BMCR, BMCR_RESET);
    /* wait end of reset (max 500 ms) */

    do
    {
    
    
        udelay(10);

        if (timeout-- == 0)
            return -1;

        rc = phy_read(phydev, MII_BMCR);
    }
    while (rc & BMCR_RESET);

    return 0;
}

最后我们还需要在 drivers/net/phy/smsc.c 文件中添加两个头文件,因为修改后的smsc_phy_reset 函数用到了 gpio_direction_output 和gpio_set_value这两个函数,需要添加的头文件如下所示:

#include <linux/of_gpio.h>
#include <linux/io.h>

猜你喜欢

转载自blog.csdn.net/qq_39400113/article/details/128394901