内核移植

首先是要将官方的板载内核进行下载,在其之上进行改动来支持自己的板子。

例如可以下载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信息。

1
cat /proc/cpuinfo

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 /*enet1 reset zuozhongkai*/
45 pinctrl_enet1_reset: enet1resetgrp {
46 fsl,pins = <
47 /* used for enet1 reset */
48 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
49 >;
50 };
51
52 /*enet2 reset zuozhongkai*/
53 pinctrl_enet2_reset: enet2resetgrp {
54 fsl,pins = <
55 /* used for enet2 reset */
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”属性值。
第 14
15 行,修改后的 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 = <&ethphy0>;
176 status = "okay";
177 };
178
179 &fec2 {
180 pinctrl-names = "default";
......
183 phy-handle = <&ethphy1>;
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 对应的设备树节点。
第 179
200 行, 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_enet1
174 &pinctrl_enet1_reset>;
175 phy-mode = "rmii";
176 phy-handle = <&ethphy0>;
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_enet2
185 &pinctrl_enet2_reset>;
186 phy-mode = "rmii";
187 phy-handle = <&ethphy1>;
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 /* 设置 MX6UL_PAD_ENET1_TX_CLK 和 MX6UL_PAD_ENET2_TX_CLK
3453 * 这两个 IO 的复用寄存器的 SION 位为 1。
3454 */
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) /* 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;
}

最后我们还需要在 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一下自己的主机地址。