Bootstrap

Linux移植教程——基于 I.MX6ULL 开发板

请确保以完成 bootloader 的移植:U-boot 配置、编译、移植

一、源码下载

        1. Linux 官网下载:The Linux Kernel Archives

        2.半导体厂商官网下载: 面向i.MX 6ULL和6ULZ应用处理器的评估套件

我用的是 NXP 提供的 linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

 二、配置文件

        下载到虚拟机后,解压进入目录

        修改Makefile

        ARCH        ?= arm
        CROSS_COMPILE   ?= arm-linux-gnueabihf-

保存后退出

拷贝一份配置。

cd arch/arm/configs/

cp imx_v7_mfg_defconfig imx_lyh_emmc_defconfig

打开配置文件,屏蔽 “ CONFIG_ARCH_MULTI_V6=y
        因为 I.MX6ULL ARMV7 架构的,因此要屏蔽掉 V6 相关选项,否则后面做驱动实验的 时候可能会遇到驱动模块无法加载的情况。

三、设备树

cd arch/arm/boot/dts

cp imx6ull-14x14-evk.dts imx6ull-lyh-emmc.dts

修改当前目录的 Makefile,添加自己的 dtb

四、编译

注意:我使用的交叉编译是 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf ,在测试其他交叉编译版本时,发现可能编译失败,建议使用与笔者相同的版本

写一个简单的脚本,方便编译:

vi imx_lyh.sh

#!/bin/sh
make distclean
make imx_lyh_emmc_defconfig
make menuconfig
make all -j6

chmod 777 imx_lyh.sh
./imx_lyh.sh
        编译完成以后就会在目录 arch/arm/boot 下生成 zImage 镜像文件。在 arch/arm/boot/dts 目录下生成 imx6ull-lyh-emmc.dtb 文件。将这两个文件拷贝到 tftp 目录下,然后重启开发板,在 uboot 命令模式中使用 tftp 命令下载这两个文件并启动
tftp 80800000 zImage                                 //zImage 下载到开发板 DRAM 的 0x80800000
tftp 83000000 imx6ull-lyh-emmc.dtb         
bootz 80800000 - 83000000

 出现以下界面:

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

确保 EMMC 中的根文件系统可用。
nfs挂载启动
setenv bootargs "console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.137.10:/home/lyh/linux/nfs/rootfs ip=192.168.137.9:192.168.137.10:192.168.137.1:255.255.255.0::eth0:off"

emmc启动

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

5.1 CPU主频

cat /proc/cpuinfo
        BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高, BogoMIPS 值就越大。 BogoMIPS 只是粗略的计算 CPU 性能,并不十分准确。
进入到目录 /sys/bus/cpu/devices/cpu0/cpufreq

cpuinfo_cur_freq :当前 cpu 工作频率,从 CPU 寄存器读取到的工作频率。
cpuinfo_max_freq :处理器所能运行的最高工作频率 ( 单位 : KHz )。
cpuinfo_min_freq :处理器所能运行的最低工作频率 ( 单位 : KHz )。
cpuinfo_transition_latency :处理器切换频率所需要的时间 ( 单位 :ns)
scaling_available_frequencies :处理器支持的主频率列表 ( 单位 : KHz )。
scaling_available_governors :当前内核中支持的所有 governor( 调频 ) 类型。
scaling_cur_freq :保存着 cpufreq 模块缓存的当前 CPU 频率,不会对 CPU 硬件寄存器进
行检查。
scaling_driver :该文件保存当前 CPU 所使用的调频驱动。
scaling_governor governor( 调频 ) 策略, Linux 内核一共有 5 中调频策略,
①、 Performance ,最高性能,直接用最高频率,不考虑耗电。
②、 Interactive ,一开始直接用最高频率,然后根据 CPU 负载慢慢降低。
③、 Powersave ,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
④、 Userspace ,可以在用户空间手动调节频率。
⑤、 Ondemand ,定时检查负载,然后根据负载来调节频率。负载低的时候降低 CPU 频率,
这样省电,负载高的时候提高 CPU 频率,增加性能。
scaling_max_freq governor( 调频 ) 可以调节的最高频率。
cpuinfo_min_freq governor( 调频 ) 可以调节的最低频率。
stats 目录下给出了 CPU 各种运行频率的统计情况,比如 CPU 在各频率下的运行时间以及
变频次数。

通过更改 scaling_governor 去更改频率

make menuconfig
CPU Power Management
-> CPU Frequency scaling
-> Default CPUFreq governor

5.2 修改 EMMC 驱动

正点原子 EMMC 版本核心板上的 EMMC 采用的 8 位数据线
        Linux 内核驱动里面 EMMC 默认是 4 线模式的, 4 线模式肯定没有 8 线模式的速度快,所 以本节我们将 EMMC 的驱动修改为 8 线模式。

打开设备树文件 imx6ull-alientek-emmc.dts ,找到 &usdhc2

改为:

& 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 ;
  no-1-8-v ;               // 防止内核在 运行的时候用 1.8V 去驱动 EMMC ,导致 EMMC 驱动出现问题
 status = "okay" ;
};

5.3 修改网络驱动

        ENET1 复位引脚 ENET1_RST 连接在 I.M6ULL SNVS_TAMPER7 这个引脚上。 ENET2 的复位引脚 ENET2_RST 连接在 I.MX6ULL SNVS_TAMPER8 上。

还是设备树文件 imx6ull-alientek-emmc.dts 中,找到:MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 ,删掉这两行

再找到 &gpio5 8 ,删掉图示两行

找到 &iomuxc_snvs ,在节点下添加
        /*enet1 reset*/
        pinctrl_enet1_reset : enet1resetgrp {
                fsl , pins = <
                       /* used for enet1 reset */
                        MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
                >;
        };
        /*enet2 reset */
        pinctrl_enet2_reset : enet2resetgrp {
                fsl , pins = <
                        /* used for enet2 reset */
                        MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
                >;
        };
找到 pinctrl_enet1 pinctrl_enet2,将最后一行分别修改为
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009
MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009
找到 & fec1 和 & fec2 ,修改pinctrl - 0
pinctrl - 0 = <& pinctrl_enet1
                    & pinctrl_enet1_reset >;
pinctrl - 0 = <& pinctrl_enet2
                    & pinctrl_enet2_reset >;

添加引脚信息

phy - reset - gpios = <& gpio5 7 GPIO_ACTIVE_LOW >;
phy - reset - duration = < 200 >;

phy - reset - gpios = <& gpio5 8 GPIO_ACTIVE_LOW >;
phy - reset - duration = < 200 >;

继续修改下方内容

         ethphy0: ethernet-phy@0 {
             compatible = "ethernet-phy-ieee802.3-c22";
             smsc,disable-energy-detect;
             reg = <0>;
         };

         ethphy1: ethernet-phy@1 {
             compatible = "ethernet-phy-ieee802.3-c22";
            smsc,disable-energy-detect;
             reg = <1>;
         };

smsc,disable-energy-detect ”表明 PHY 芯片是 SMSC 公司的,这样 Linux内核就会找到 SMSC 公司的 PHY 芯片驱动来驱动 LAN8720A

到此,设备树文件修改完成,保存退出

接下来修改 drivers/net/ethernet/freescale/fec_main.c 文件,找到 fec_probe 函数。

 

  /* 设置 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 );
打开 drivers/net/phy/smsc.c ,找到 smsc_phy_reset ,改为
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@02188000");
        if(np == NULL) {
            return -EINVAL;
        }
    }
    
    if(phydev->addr == 1) /* FEC2 */ {
        np = of_find_node_by_path("/soc/aips-bus@02000000/ethernet@020b4000");
        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;
}
        在测试 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 进行一次软复位。
#include <linux/of_gpio.h>
#include <linux/io.h>                //加上头文件

运行编译脚本,在 menuconfig 里配置驱动

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

记得保存配置,以免每次编译都要配置。

;