内核移植 首先是要将官方的板载内核进行下载,在其之上进行改动来支持自己的板子。
例如可以下载nxp关于imx6ull的linux内核,github的地址GitHub - Freescale/linux-fslc at 5.4-2.1.x-imx
交叉编译器使用的为arm-linux-gnueabihf-
随便弄一个内核下来,编译顶层的Makefile
1 2 ARCH ?= arm CROSS_COMPILE ?= arm-linux-gnueabihf-
这两个后面不要加空格直接回车,不然找不到交叉编译器。
添加自己的板子 之后复制一份自己的配置文件
1 2 3 cd arch/arm/configs ls cp imx_v7_mfg_defonfig imx_alientek_emmc_defconfig
之后进入arch/arm/boot/dts,复制一份imx6ull-14x14-evk.dts
1 cp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts
修改makefile的dtb-$(CONFIG_SOC_IMX6ULL)的内容,添加设备树
之后返回顶层编译工程
1 2 3 make distclean make imx_alientek_emmc_defconfig make -j8
这里的-j8是因为我的虚拟机就只有8个处理器
修改板子主频 在串口软件例如我的是Mobaxterm,查看cpu信息。
BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高, BogoMIPS 值就越大。BogoMIPS 只是粗略的计算 CPU 性能,并不十分准确。
1 2 cd /sys/bus/cpu/devices/cpu0/cpufreq cat cpuinfo_cur_freq
此目录中记录了 CPU 频率等信息,这些文件的含义如下: 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(调频)可以调节的最低频率。
1 cat scaling_governor = ondemand
ondmand是定期检查负载,调整功率,一般用这个。
查看 stats 目录下的 time_in_state 文件可以看到 CPU 在各频率下的工作时间,命令如下:
1 cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
如果想一直高频就在内核文件的imx_alientek_emmc_defconfig文件
1 2 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y //屏蔽掉 CONFIG_CPU_FREQ_GOV_ONDEMAND=y //添加
EMMC驱动修改 修改imx6ull-alientek-emmc.dts的usdhc2部分,将其修改为
1 2 3 4 5 6 7 8 9 10 734 &usdhc2 {735 pinctrl-names = "default" , "state_100mhz" , "state_200mhz" ;736 pinctrl-0 = <&pinctrl_usdhc2_8bit>;737 pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;738 pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;739 bus-width = <8 >;740 non-removable;741 no-1 -8 -v;742 status = "okay" ;743 };
网络驱动修改 修改LAN8720的复位以及网络时钟引脚驱动 ENET1 复位引脚 ENET1_RST 连接在 I.M6ULL 的 SNVS_TAMPER7 这个引脚上。 ENET2的复位引脚 ENET2_RST 连接在 I.MX6ULL 的 SNVS_TAMPER8 上。打开设备树文件 imx6ullalientek-emmc.dts,找到如下代码:
1 2 3 4 5 6 7 8 584 pinctrl_spi4: spi4grp {585 fsl,pins = <586 MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1 587 MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1 588 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1 589 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000 590 >;591 };
删除原有的588和589行。
1 2 3 4 5 6 7 125 spi4 {126 compatible = "spi-gpio" ;127 pinctrl-names = "default" ;128 pinctrl-0 = <&pinctrl_spi4>;129 pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;...... 133 cs-gpios = <&gpio5 7 0 >;
GPIO5_IO07 和 GPIO5_IO08 分别作为 ENET1 和 ENET2 的复位引脚,而不是 SPI4 的什么功能引脚,因此将示例代码 37.4.3.2 中的第 129 行和第 133 行处的代码删除掉!! 否则会干扰到网络复位引脚!
在 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 24 1 &iomuxc_snvs {2 pinctrl-names = "default_snvs" ;3 pinctrl-0 = <&pinctrl_hog_2>;4 imx6ul-evk {5 ...... 43 44 45 pinctrl_enet1_reset: enet1resetgrp {46 fsl,pins = <47 48 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0 49 >;50 };51 52 53 pinctrl_enet2_reset: enet2resetgrp {54 fsl,pins = <55 56 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0 57 >;58 };59 };60 };
最后还需要修改一下 ENET1 和 ENET2 的网络时钟引脚配置, 继续在 imx6ull-alientekemmc.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 309 pinctrl_enet1: enet1grp {310 fsl,pins = <311 MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 312 MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0 313 MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0 314 MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0 315 MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 316 MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0 317 MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0 318 MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009 319 >;320 };321 322 pinctrl_enet2: enet2grp {323 fsl,pins = <324 MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 325 MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 326 MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 327 MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 328 MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 329 MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 330 MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 331 MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 332 MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 333 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009 334 >;335 };
第 318 和 333 行, 分别为 ENET1 和 ENET2 的网络时钟引脚配置信息,将这两个引脚的电气属性值改为 0x4001b009,原来默认值为 0x4001b031。
修改 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 1 &fec1 {2 pinctrl-names = "default" ;3 pinctrl-0 = <&pinctrl_enet1&pinctrl_enet1_reset>;4 phy-mode = "rmii" ;...... 9 status = "okay" ;10 };11 12 &fec2 {13 pinctrl-names = "default" ;14 pinctrl-0 = <&pinctrl_enet2&pinctrl_enet2_reset>;15 phy-mode = "rmii" ;...... 36 };
第 34 行,修改后的 fec1 节点“pinctrl-0”属性值。 第 1415 行,修改后的 fec2 节点“pinctrl-0”属性值 .
修改 LAN8720A 的 PHY 地址 ENET1 的 LAN8720A 地址为 0x0, 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 171 &fec1 {172 pinctrl-names = "default" ;...... 175 phy-handle = <ðphy0>;176 status = "okay" ;177 };178 179 &fec2 {180 pinctrl-names = "default" ;...... 183 phy-handle = <ðphy1>;184 status = "okay" ;185 186 mdio {187 #address-cells = <1> ; 188 #size-cells = <0> ; 189 190 ethphy0: ethernet-phy@0 {191 compatible = "ethernet-phy-ieee802.3-c22" ;192 reg = <2 >;193 };194 195 ethphy1: ethernet-phy@1 {196 compatible = "ethernet-phy-ieee802.3-c22" ;197 reg = <1 >;198 };199 };200 };
第 171177 行, ENET1 对应的设备树节点。 第 179200 行, ENET2 对应的设备树节点。但是第 186~198 行的 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 171 &fec1 {172 pinctrl-names = "default" ;173 pinctrl-0 = <&pinctrl_enet1174 &pinctrl_enet1_reset>;175 phy-mode = "rmii" ;176 phy-handle = <ðphy0>;177 phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;178 phy-reset-duration = <200 >;179 status = "okay" ;180 };181 182 &fec2 {183 pinctrl-names = "default" ;184 pinctrl-0 = <&pinctrl_enet2185 &pinctrl_enet2_reset>;186 phy-mode = "rmii" ;187 phy-handle = <ðphy1>;188 phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;189 phy-reset-duration = <200 >;190 status = "okay" ;191 192 mdio {193 #address-cells = <1> ; 194 #size-cells = <0> ; 195 196 ethphy0: ethernet-phy@0 {197 compatible = "ethernet-phy-ieee802.3-c22" ;198 smsc,disable-energy-detect;199 reg = <0 >;200 };201 202 ethphy1: ethernet-phy@1 {203 compatible = "ethernet-phy-ieee802.3-c22" ;204 smsc,disable-energy-detect;205 reg = <1 >;206 };207 };208 };
修改 fec_main.c 文件 要 在 I.MX6ULL 上 使 用 LAN8720A , 需 要 修 改 一 下 Linux 内 核 源 码 , 打 开drivers/net/ethernet/freescale/fec_main.c,找到函数 fec_probe,在 fec_probe 中加入如下代码:
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 3438 static int 3439 fec_probe(struct platform_device *pdev)3440 {3441 struct fec_enet_private *fep ;3442 struct fec_platform_data *pdata ;3443 struct net_device *ndev ;3444 int i, irq, ret = 0 ;3445 struct resource *r ;3446 const struct of_device_id *of_id ;3447 static int dev_id;3448 struct device_node *np = pdev->dev.of_node, *phy_node;3449 int num_tx_qs;3450 int num_rx_qs;3451 3452 3455 void __iomem *IMX6U_ENET1_TX_CLK;3456 void __iomem *IMX6U_ENET2_TX_CLK;3457 3458 IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC , 4 );3459 writel(0X14 , IMX6U_ENET1_TX_CLK);3460 3461 IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC , 4 );3462 writel(0X14 , IMX6U_ENET2_TX_CLK);3463 ...... 3656 return ret;3657 }
配置 Linux 内核,使能 LAN8720 驱动 输入命令“make menuconfig”,打开图形化配置界面,选择使能 LAN8720A 的驱动,路径如下:
1 2 3 4 -> Device Drivers -> Network device support -> PHY Device support and infrastructure -> Drivers for SMSC PHYs
修改 smsc.c 文件 LAN8720A 的驱动文件是 drivers/net/phy/smsc.c, 修改SMSC PHY的复位函数 ,修改以后的 smsc_phy_reset函数内容如下所示:
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 ) { np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000" ); if (np == NULL ) { return -EINVAL; } } if (phydev->addr == 1 ) { 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); 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 ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) { rc |= MII_LAN83C185_MODE_ALL; phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc); } phy_write(phydev, MII_BMCR, BMCR_RESET); 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 这两个函数,需要添加的头文件如下所示:
1 2 #include <linux/of_gpio.h> #include <linux/io.h>
网络驱动测试 修改好设备树和 Linux 内核以后重新编译一下,得到新的 zImage 镜像文件和 imx6ullalientek-emmc.dtb 设备树文件,使用网线将 I.MX6U-ALPHA 开发板的两个网口与路由器或者电脑连接起来,最后使用新的文件启动 Linux 内核。启动以后使用“ifconfig”命令查看一下当前活动的网卡有哪些 。
输入命令“ifconfig -a”来查看一下开发板中存在的所有网卡,
使用如下命令依次打开 eth0 和 eth1 这两个网卡:
1 2 ifconfig eth0 up ifconfig eth1 up
使用命令给两个网卡配置IP地址
1 2 ifconfig eth0 192.168.1.251 ifconfig eth1 192.168.1.252
一定要和自己的电脑处于同一个网段内 ,可以ping一下自己的主机地址。