1.NXP 官方开发板 Linux 内核编译

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

1.1 修改顶层 Makefile

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

  • 第 252 和 253 行分别设置了 ARCH 和 CROSS_COMPILE 这两个变量的值,这样在编译的时候就不用输入很长的命令了。

1.2 配置并编译 Linux 内核

  • 和 uboot 一样,在编译 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 内核:

    1
    2
    make clean //第一次编译 Linux 内核之前先清理一下
    make imx_v7_mfg_defconfig //配置 Linux 内核
  • 配置完成以后如图:

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

    1
    make -j16 //编译 Linux 内核
  • 等待编译完成,结果如图所示:

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

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

1.3 Linux 内核启动测试

  • 在上一小节我们已经得到了 NXP 官方 I.MX6ULL EVK 开发板对应的 zImage 和 imx6ull-14x14-evk.dtb 这两个文件。这两个文件能不能在正点原子的 I.MX6U-ALPHA EMMC 版开发板上启动呢?测试一下不就知道了,在测试之前确保 uboot 中的环境变量 bootargs 内容如下:

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

    1
    2
    cp arch/arm/boot/zImage /home/zuozhongkai/linux/tftpboot/ -f
    cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/zuozhongkai/linux/tftpboot/ -f
  • 拷贝完成以后就可以测试了,启动开发板,进入 uboot 命令行模式,然后输入如下命令将 zImage 和 imx6ull-14x14-evk.dtb 下载到开发板中并启动:

    1
    2
    3
    tftp 80800000 zImage
    tftp 83000000 imx6ull-14x14-evk.dtb
    bootz 80800000 - 83000000
  • 结果图所示:

  • 从图可以看出,此时 Linux 内核已经启动了,如果 EMMC 中的根文件系统存在,我们就可以进入到 Linux 系统里面使用命令进行操作如图所示:

1.4 根文件系统缺失错误

  • Linux 内核启动以后是需要根文件系统的,根文件系统存在哪里是由 uboot 的 bootargs 环境变量指定,bootargs 会传递给 Linux 内核作为命令行参数。比如上一小节设置 root=/dev/mmcblk1p2,也就是说根文件系统存储在 /dev/mmcblk1p2 中,也就是 EMMC 的分区 2 中。这是因为正点原子的 EMMC 版本开发板出厂的时候已经 EMMC 的分区 2 中烧写好了根文件系统,所以设置 root=/dev/mmcblk1p2。如果我们不设置根文件系统路径,或者说根文件系统路径设置错误的话会出现什么问题?这个问题是很常见的,我们在实际的工作中开发一个产品,这个产品的第一版硬件出来以后我们是没有对应的根文件系统可用的,必须要自己做根文件系统。在构建出对应的根文件系统之前 Linux 内核是没有根文件系统可用的,此时 Linux 内核启动以后会出现什么问题呢?带着这个问题,我们将 uboot 中的 bootargs 环境变量改为“console=ttymxc0,115200”,也就是不填写 root 的内容了,命令如下:

    1
    2
    setenv bootargs 'console=ttymxc0,115200' //设置 bootargs
    saveenv //保存
  • 修改完成以后重新从网络启动,启动以后会有如图 37.2.4.1 所示错误:

  • 在图中最后会有下面这一行:

    1
    Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
  • 也就是提示内核崩溃,因为 VFS(虚拟文件系统)不能挂载根文件系统,因为根文件系统目录不存在。即使根文件系统目录存在,如果根文件系统目录里面是空的依旧会提示内核崩溃。这个就是根文件系统缺失导致的内核崩溃,但是内核是启动了的,只是根文件系统不存在而已。

2.在 Linux 中添加自己的开发板

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

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

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

    1
    2
    cd arch/arm/configs
    cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig
  • 打开 imx_alientek_emmc_defconfig 文件,找到“CONFIG_ARCH_MULTI_V6=y”这一行,将其屏蔽掉,如图所示:

  • 因为 I.MX6ULL 是 ARMV7 架构的,因此要屏蔽掉 V6 相关选项,否则后面做驱动实验的时候可能会遇到驱动模块无法加载的情况。以后 imx_alientek_emmc_defconfig 就是正点原子的 EMMC 版开发板默认配置文件了。完成以后如图:

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

    1
    make imx_alientek_emmc_defconfig 

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

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

    1
    2
    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 ,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    dtb-$(CONFIG_SOC_IMX6ULL) += \ 
    imx6ull-14x14-ddr3-arm2.dtb \
    imx6ull-14x14-ddr3-arm2-adc.dtb \
    imx6ull-14x14-ddr3-arm2-cs42888.dtb \
    imx6ull-14x14-ddr3-arm2-ecspi.dtb \
    imx6ull-14x14-ddr3-arm2-emmc.dtb \
    imx6ull-14x14-ddr3-arm2-epdc.dtb \
    imx6ull-14x14-ddr3-arm2-flexcan2.dtb \
    imx6ull-14x14-ddr3-arm2-gpmi-weim.dtb \
    imx6ull-14x14-ddr3-arm2-lcdif.dtb \
    imx6ull-14x14-ddr3-arm2-ldo.dtb \
    imx6ull-14x14-ddr3-arm2-qspi.dtb \
    imx6ull-14x14-ddr3-arm2-qspi-all.dtb \
    imx6ull-14x14-ddr3-arm2-tsc.dtb \
    imx6ull-14x14-ddr3-arm2-uart2.dtb \
    imx6ull-14x14-ddr3-arm2-usb.dtb \
    imx6ull-14x14-ddr3-arm2-wm8958.dtb \
    imx6ull-14x14-evk.dtb \
    imx6ull-14x14-evk-btwifi.dtb \
    imx6ull-14x14-evk-emmc.dtb \
    imx6ull-14x14-evk-gpmi-weim.dtb \
    imx6ull-14x14-evk-usb-certi.dtb \
    imx6ull-alientek-emmc.dtb \
    imx6ull-9x9-evk.dtb \
    imx6ull-9x9-evk-btwifi.dtb \
    imx6ull-9x9-evk-ldo.dtb
  • 这样编译 Linux 的时候就可以从 imx6ull-alientek-emmc.dts 编译出 imx6ull-alientek-emmc.dtb 文件了

2.3 编译测试

  • Linux 内核里面已经添加了正点原子 I.MX6UL-ALIPHA EMMC 版 开 发 板 了 , 接 下 接 编 译 测 试 一 下 , 我 们 可 以 创 建 一 个 编 译 脚 本 ,imx6ull_alientek_emmc.sh,脚本内容如下:

    1
    2
    3
    4
    5
    #!/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 内核,命令如下:

    1
    2
    chmod 777 imx6ull_alientek_emmc.sh //给予可执行权限 
    ./imx6ull_alientek_emmc.sh //执行 shell 脚本编译内核
  • 编译完成以后就会在目录 arch/arm/boot 下生成 zImage 镜像文件。在 arch/arm/boot/dts 目录下生成 imx6ull-alientek-emmc.dtb 文件。将这两个文件拷贝到 tftp 目录下,然后重启开发板,在uboot 命令模式中使用 tftp 命令下载这两个文件并启动,命令如下:

    1
    2
    3
    tftp 80800000 zImage 
    tftp 83000000 imx6ull-alientek-emmc.dtb
    bootz 80800000 – 83000000
  • Linux 内核启动成功,说明我们已经在 NXP 提供的 Linux 内核源码中添加了正点原子I.MX6UL-ALPHA 开发板。

3.CPU 主频和网络驱动修改

3.1 CPU 主频修改

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

    • 确保EMMC 中的根文件系统可用!然后重新启动开发板,进入终端(可以输入命令)

    • 利用如下命令行以后输入如下命令查看cpu 信息:

      1
      cat /proc/cpuinfo 

    • 在图中有 BogoMIPS 这一条,此时 BogoMIPS 为3.00,BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS 值就越大。BogoMIPS 只是粗略的计算 CPU 性能,并不十分准确。但是我们可以通过 BogoMIPS 值来大致的判断当前处理器的性能。在图中并没有看到当前 CPU 的工作频率,那我们就转变另一种方法查看当前 CPU 的工作频率。进入到目录/sys/bus/cpu/devices/cpu0/cpufreq中,此目录下会有很多文件,如图所示:

    • 此目录中记录了 CPU 频率等信息:

    • scaling_governor:governor(调频)策略,Linux 内核一共有 5 中调频策略,

      1. Performance,最高性能,直接用最高频率,不考虑耗电。
      2. Interactive,一开始直接用最高频率,然后根据 CPU 负载慢慢降低。
      3. Powersave,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
      4. Userspace,可以在用户空间手动调节频率。
      5. Ondemand,定时检查负载,然后根据负载来调节频率。负载低的时候降低 CPU 频率,这样省电,负载高的时候提高 CPU 频率,增加性能。
    • 使用如下命令查看当前 CPU 频率:

      1
      cat cpuinfo_cur_freq 
    • 查得当前 CPU 频率为 198MHz,工作频率很低!其他的值如下:

      1
      2
      3
      4
      5
      6
      7
      8
      cpuinfo_cur_freq = 198000 
      cpuinfo_max_freq = 792000
      cpuinfo_min_freq = 198000
      scaling_cur_freq = 198000
      scaling_max_freq = 792000
      cat scaling_min_freq = 198000
      scaling_available_frequencies = 198000 396000 528000 792000
      cat scaling_governor = ondemand
    • 可以看出,当前 CPU 支持 198MHz、396MHz、528Mhz 和 792MHz 四种频率切换,其中调频策略为 ondemand,也就是定期检查负载,然后根据负载情况调节 CPU 频率。因为当前我们开发板并没有做什么工作,因此 CPU 频率降低为 198MHz 以省电。如果开发板做一些高负载的工作,比如播放视频等操作那么 CPU 频率就会提升上去。查看 stats 目录下的 time_in_state 文件可以看到 CPU 在各频率下的工作时间,命令如下:

      1
      cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state 

    • 从图中可以看出,CPU 在 198MHz、396MHz、528MHz 和 792MHz 都工作过,其中 198MHz 的工作时间最长!假如我们想让 CPU 一直工作在 792MHz 那该怎么办?很简单,配置Linux 内核,将调策略选择为performance。或者修改imx_alientek_emmc_defconfig 文件,此文件中有下面几行:

    1
    2
    3
    4
    CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y  // 配置 ondemand 为默认调频策略
    CONFIG_CPU_FREQ_GOV_POWERSAVE=y // 使能 powersave 策略
    CONFIG_CPU_FREQ_GOV_USERSPACE=y // 使能 userspace 策略
    CONFIG_CPU_FREQ_GOV_INTERACTIVE=y // 使能 interactive 策略
    • 将示例代码中的第 1 行屏蔽掉,然后在第 4 行后面添加:

      1
      CONFIG_CPU_FREQ_GOV_ONDEMAND=y 
    • 修改完成以后重新编译 Linux 内核,编译之前先清理一下工程!因为我们重新修改过默认配置文件了,编译完成以后使用新的 zImage 镜像文件重新启动 Linux 。再次查看 /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq 文件的值,可以看出,当前 CPU 频率为 792MHz 了。查看 scaling_governor 文件看一下当前的调频策略,可以看出当前的 CPU 调频策略为 preformance,也就是高性能模式,一直以最高主频运行。

  2. 超频至 700MHz

    • 修改一下设备树文件 arch/arm/boot/dts/imx6ull.dtsi 即可,打开imx6ull.dtsi,找到下面代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      cpu0: cpu@0 { 
      compatible = "arm,cortex-a7";
      device_type = "cpu";
      reg = <0>;
      clock-latency = <61036>; /* two CLK32 periods
      operating-points = <
      /* kHz uV */
      996000 1275000
      792000 1225000
      696000 1225000 // 新加入的
      528000 1175000
      396000 1025000
      198000 950000
      >;
      fsl,soc-operating-points = <
      /* KHz uV */
      996000 1175000
      792000 1175000
      696000 1175000 // 新加入的
      528000 1175000
      396000 1175000
      198000 1175000
      >;
    • 第 10 行,加入了“696000 1225000”,这个就是696MHz 的支持。

    • 第 19 行,加入了“696000 1175000”,也是对 696MHz 的支持。 修改好以后保存,并且编译设备树,在 Linux 内核源码根目录下输入如下命令编译设备树:

    1
    make dtbs 
    • 命令“make dtbs”只编译设备树文件,也就是将.dts 编译为.dtb,编译完成以后使用新的设备 树 文 件 imx6ull-alientek_emmc.dtb 启 动 Linux 。 重 启 以 后 查 看 文 件/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies 的内容,可以看出,此时支持了 696MHz。如果设置调频策略为 performance,那么处理器就会一直工作在696MHz。可以对比一下工作在528MHz 和696MHz 下的BogoMIPS 的值,528MHz 和 696MHz 下的 BogoMIPS 值分别为8.00 和 10.54,相当于性能提升了(10.54/8)-1=31.75%。

3.2 修改 EMMC 驱动

  1. 使能8 线 EMMC 驱动

    • 正点原子EMMC 版本核心板上的EMMC 采用的 8 位数据线

    • Linux 内核驱动里面 EMMC 默认是4 线模式的,4 线模式肯定没有 8 线模式的速度快,所以本节我们将 EMMC 的驱动修改为 8 线模式。修改方法很简单,直接修改设备树即可,打开文件 imx6ull-alientek-emmc.dts,找到如下所示内容:

    1
    2
    3
    4
    5
    6
    &usdhc2 { 
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_usdhc2>;
    non-removable;
    status = "okay";
    };
    • 改为如下代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      &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";
      };
  2. 关闭 EMMC 1.8V 供电选项

    • 此时EMMC 工作电压是3.3V 的,因此我们要在示例代码中的 usdhc2 设备树节点中添加“no-1-8-v”选项,也就是关闭 1.8V 这个功能选项。防止内核在运行的时候用 1.8V 去驱动 EMMC,导致 EMMC 驱动出现问题,修改后的 usdhc2 节点内容如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      &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;
      status = "okay";
      };
    • 修改完成以后保存一下 imx6ull-alientek-emmc.dts,然后使用命令“make dtbs”重新编译一下设备树,编译完成以后使用新的设备树重启 Linux 系统即可。

3.3 V2.4及以后版本底板网络驱动修改

  • 讲解 uboot 移植的时候就已经说过了,正点原子开发板的网络和NXP 官方的网络硬件上不同,网络 PHY 芯片由KSZ8081 换为了 SR8201F,两个网络 PHY 芯片的复位IO 也不同。所以 Linux 内核自带的网络驱动是驱动不起来 I.MX6U-ALPHA 开发板上的网络的,需要做修改。
  1. 修改 SR8201F 的复位以及网络时钟引脚驱动

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

      1
      2
      3
      4
      5
      6
      7
      8
      pinctrl_spi4: spi4grp { 
      fsl,pins = <
      MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
      MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
      MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1
      MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000
      >;
      };
    • 示例代码中第 5 和 6 行就是初始化 SNVS_TAMPER7 和 SNVS_TAMPER8 这两个引脚的,不过看样子好像是作为了 SPI4 的 IO,这不是我们想要的,所以将 588 和 589 这两行删除掉!删除掉以后继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码:

      1
      2
      3
      4
      5
      6
      7
      spi4 { 
      compatible = "spi-gpio";
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_spi4>;
      pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
      ...
      cs-gpios = <&gpio5 7 0>;
    • 第 5 行,设置GPIO5_IO08 为SPI4 的一个功能引脚(我也不清楚具体作为什么功能用),而 GPIO5_IO08 就是 SNVS_TAMPER8 的GPIO 功能引脚。 第 7 行设置 GPIO5_IO07 作为 SPI4 的片选引脚,而 GPIO5_IO07 就是 SNVS_TAMPER7 的 GPIO 功能引脚。 现在我们需要 GPIO5_IO07 和 GPIO5_IO08 分别作为 ENET1 和 ENET2 的复位引脚,而不是SPI4 的什么功能引脚,因此将示例代码中的第 5 行和第 7 行处的代码删除掉!!否则会干扰到网络复位引脚! 在 imx6ull-alientek-emmc.dts 里面找到名为“iomuxc_snvs”的节点(直接搜索),然后在此节点下添加网络复位引脚信息,添加完成以后的“iomuxc_snvs”的节点内容如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      &iomuxc_snvs { 
      pinctrl-names = "default_snvs";
      pinctrl-0 = <&pinctrl_hog_2>;
      imx6ul-evk {
      ... /*省略掉其他*/

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

      /*enet2 reset zuozhongkai*/
      pinctrl_enet2_reset: enet2resetgrp {
      fsl,pins = <
      /* used for enet2 reset */
      MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
      >;
      };
      };
      };
    • 第 1 行,imx6ull-alientek-emmc.dts 文件中 iomuxc_snvs 节点;第 8-13 行,ENET1 网络复位引脚配置信息;第 16~21 行,ENET2 网络复位引脚配置信息;最后还需要修改一下 ENET1 和 ENET2 的网络时钟引脚配置,继续在 imx6ull-alientek-emmc.dts 中找到如下所示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    pinctrl_enet1: enet1grp { 
    fsl,pins = <
    MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
    MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
    MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
    MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
    MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
    MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
    MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
    MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
    >;
    };

    pinctrl_enet2: enet2grp {
    fsl,pins = <
    MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
    MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
    MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
    MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
    MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
    MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
    MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
    MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
    MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
    MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031
    >;
    };
    • 第 10 和 25 行,分别为 ENET1 和ENET2 的网络时钟引脚配置信息,将这两个引脚的电气属性值改为 0x4001b031(原来默认值就是 0x4001b031)。修改完成以后记得保存一下 imx6ull-alientek-emmc.dts,网络复位以及时钟引脚驱动就修改好了。
  2. 修改 fec1 和fec2 节点的 pinctrl-0 属性

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      &fec1 { 
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_enet1
      &pinctrl_enet1_reset>;
      phy-mode = "rmii";
      ...
      status = "okay";
      };

      &fec2 {
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_enet2
      &pinctrl_enet2_reset>;
      phy-mode = "rmii";
      ...
      };
    • 第 3~4 行,修改后的 fec1 节点“pinctrl-0”属性值。

    • 第 12~13 行,修改后的 fec2 节点“pinctrl-0”属性值。

  3. 修改 SR8201F 的PHY 地址

    • 在 uboot 移植章节中,我们说过 ENET1 的 LAN8720A 地址为 0x2,ENET2 的 LAN8720A地址为 0x1。在 imx6ull-alientek-emmc.dts 中找到如下代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      &fec1 { 
      pinctrl-names = "default";
      ..
      phy-handle = <&ethphy0>;
      status = "okay";
      };

      &fec2 {
      pinctrl-names = "default";
      ..
      phy-handle = <&ethphy1>;
      status = "okay";

      mdio {
      #address-cells = <1>;
      #size-cells = <0>;

      ethphy0: ethernet-phy@0 {
      compatible = "ethernet-phy-ieee802.3-c22";
      reg = <2>;
      };

      ethphy1: ethernet-phy@1 {
      compatible = "ethernet-phy-ieee802.3-c22";
      reg = <1>;
      };
      };
      };
    • 第 1~6 行,ENET1 对应的设备树节点。

    • 第 8~28 行,ENET2 对应的设备树节点。但是第 14-28 行的 mdio 节点描述了 ENET1 和 ENET2 的 PHY 地址信息。将示例代码改为如下内容:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      &fec1 { 
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_enet1
      &pinctrl_enet1_reset>;
      phy-mode = "rmii";
      phy-handle = <&ethphy0>;
      phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;
      phy-reset-duration = <200>;
      status = "okay";
      };

      &fec2 {
      pinctrl-names = "default";
      pinctrl-0 = <&pinctrl_enet2
      &pinctrl_enet2_reset>;
      phy-mode = "rmii";
      phy-handle = <&ethphy1>;
      phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
      phy-reset-duration = <200>;
      status = "okay";

      mdio {
      #address-cells = <1>;
      #size-cells = <0>;

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

      ethphy1: ethernet-phy@1 {
      compatible = "ethernet-phy-ieee802.3-c22";
      smsc,disable-energy-detect;
      reg = <1>;
      };
      };
      };
    • 第 7 和 8 行,添加了 ENET1 网络复位引脚所使用的 IO 为 GPIO5_IO07,低电平有效。复位低电平信号持续时间为 200ms。

    • 第 18 和 19 行,ENET2 网络复位引脚所使用的 IO 为 GPIO5_IO08,同样低电平有效,持续时间同样为 200ms。

    • 第 28 和 34 行,smsc,disable-energy-detect表明 PHY 芯片是 SMSC 公司的,这样 Linux内核就会找到 SMSC 公司的 PHY 芯片驱动来驱动 LAN8720A。

    • 第 26 行,注意ethernet-phy@后面的数字是 PHY 的地址,ENET1 的 PHY 地址为 2,所以“@”后面是 2。

    • 第 29 行,reg 的值也表示 PHY 地址,ENET1 的 PHY 地址为2,所以reg=2。

    • 第 32 行,ENET2 的 PHY 地址为1,因此“@”后面为 1。

    • 第 35 行,因为 ENET2 的 PHY 地址为 1,所以 reg=1。

    • 至此,SR8201F 的 PHY 地址就改好了,保存一下 imx6ull-alientek-emmc.dts 文件。然后使
      用make dtbs命令重新编译一下设备树。

  4. 修改 fec_main.c 文件

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      static void fec_reset_phy(struct platform_device *pdev) 
      {
      int err, phy_reset;
      int msec = 1;
      struct device_node *np = pdev->dev.of_node;

      if (!np)
      return;

      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;

      err = devm_gpio_request_one(&pdev->dev, phy_reset,
      GPIOF_OUT_INIT_LOW, "phy-reset");
      if (err) {
      dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n"

      return;
      }
      msleep(msec);
      gpio_set_value(phy_reset, 1);
      msleep(200); /* 复位结束后至少再延时 150ms才能继续操作SR8201F */
      }
    • 第 28 行就是新加入的代码,根据 SR8201F 收据手册上的要求,SR8201F 在复位结束以后需要等待至少 150ms 才能操作 SR8201F,因此这里添加了一个 200ms 的延时。

  5. 网络驱动测试

    • 修改好设备树和 Linux 内核以后重新编译一下,得到新的 zImage 镜像文件和 imx6ull-alientek-emmc.dtb 设备树文件,使用网线将 I.MX6U-ALPHA 开发板的两个网口与路由器或者电脑连接起来,最后使用新的文件启动Linux 内核。启动以后使用ifconfig命令查看一下当前活动的网卡有哪些,结果如图所示:

    • 从图可以看出,当前没有活动的网卡。输入命令ifconfig -a来查看一下开发板中存在的所有网卡,结果如下图所示:

    • can0 为CAN 接口的网卡,eth0 和eth1 才是网络接口的网卡,其中eth0 对应于 ENET2,eth1 对应于 ENET1。使用如下命令依次打开 eth0 和 eth1 这两个网卡:

      1
      2
      ifconfig eth0 up 
      ifconfig eth1 up
    • 网卡的打开过程如图:

    • 可以看到Generic PHY字样,说明当前的网络驱动使用的就是 Linux 内核自带的通用网络 PHY 驱动。 再次输入ifconfig命令来查看一下当前活动的网卡,结果如图所示:

    • 可以看出,此时 eth0 和 eth1 两个网卡都已经打开,并且工作正常,但是这两个网卡都还没有 IP 地址,所以不能进行 ping 等操作。使用如下命令给两个网卡配置IP 地址:

      1
      2
      ifconfig eth0 192.168.1.251 
      ifconfig eth1 192.168.1.252
    • 上述命令配置 eth0 和eth1 这两个网卡的 IP 地址分别为 192.168.1.251 和 192.168.1.252,注意 IP 地址选择的合理性,一定要和自己的电脑处于同一个网段内,并且没有被其他的设备占用!设置好以后,使用ping命令来 ping 一下自己的主机,如果能ping 通那说明网络驱动修改成功!比如我的Ubuntu 主机 IP 地址为 192.168.1.250,使用如下命令 ping 一下:

      1
      ping 192.168.1.250 
    • 结果如图所示:

    • 可以看出,ping 成功,说明网络驱动修改成功!我们在后面的构建根文件系统和 Linux 驱动开发中就可以使用网络调试代码啦。

    4.总结

    • 关于Linux 内核的移植就讲解到这里,简单总结一下移植步骤:

      1. 在Linux 内核中查找可以参考的板子,一般都是半导体厂商自己做的开发板。

      2. 编译出参考板子对应的 zImage 和 .dtb 文件。

      3. 使用参考板子的 zImage 文件和 .dtb 文件在我们所使用的板子上启动 Linux 内核,看能否启动。

      4. 如果能启动的话就万事大吉,如果不能启动那就悲剧了,需要调试 Linux 内核。不过一般都会参考半导体官方的开发板设计自己的硬件,所以大部分情况下都会启动起来。启动Linux 内核用到的外设不多,一般就 DRAM(Uboot 都初始化好的)和串口。作为终端使用的串口一般都会参考半导体厂商的Demo 板。

      5. 修改相应的驱动,像 NAND Flash、EMMC、SD 卡等驱动官方的 Linux 内核都是已经提供好了,基本不会出问题。重点是网络驱动,因为 Linux 驱动开发一般都要通过网络调试代码,所以一定要确保网络驱动工作正常。如果是处理器 内部 MAC+外部 PHY 这种网络方案的话,一般网络驱动都很好处理,因为在 Linux 内核中是有外部 PHY 通用驱动的。只要设置好复位引脚、PHY 地址信息基本上都可驱动起来。

      6. Linux 内核启动以后需要根文件系统,如果没有根文件系统的话肯定会崩溃,所以确定Linux内核移植成功以后就要开始根文件系统的构建。