目录
1.修改设备树EMMC驱动
关闭EMMC 1.8V供电选项
由上图可以看出,EMMC工作电压是3.3V,因此我们需要在usdhc2设备树节点中添加“no-1-8-v”选项,也就是关闭1.8V这个功能选项,防止内核在运行的时候使用1.8V去驱动EMMC,导致EMMC驱动出现问题,修改后的usdhc2节点内容如下:
修改后的代码如下所示:
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "imx6ull-alientek.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;
no-1-8-v;
status = "okay";
};
2.修改设备树网络驱动
Linux驱动开发时经常使用网络调试驱动,因此需要将Linux系统网络驱动调试好,正点原子ALIENTEK-ALPHA开发板网络和NXP官方网络硬件上有所不同,NXP网络PHY芯片使用的 KSZ8081而ALIENTEK-ALPHA开发板网络PHY芯片使用的时LAN8720,两个网络芯片的复位IO口也不相同,因此NXP官方移植的Linux内核自带的网络驱动驱动ALIENTEK-ALPHA开发板上的网络存在问题,需要修改。
2.1NXP官方i.MX6ULL EVK开发板与ALIENTEK-ALPHA开发板网络接口对比
NXP官方i.MX6ULL EVK开发板ENET1网络PHY芯片引脚控制图
NXP官方i.MX6ULL EVK开发板ENET2网络PHY芯片引脚控制图
NXP官方i.MX6ULL EVK开发板ENET1和ENET2网络PHY芯片与MX6ULL芯片连接图
ALIENTEK-ALPHA开发板ENET1网络PHY芯片引脚控制图
ALIENTEK-ALPHA开发板ENET2网络PHY芯片引脚控制图
ALIENTEK-ALPHA开发板ENET1和ENET2网络PHY芯片与MX6ULL芯片连接图
NXP官方i.MX6ULL EVK开发板和ALIENTEK-ALPHA开发板ENET1和ENET2网络PHY芯片引脚连接对照表
由上表可知NXP官方i.MX6ULL EVK开发板和ALIENTEK-ALPHA开发板ENET1和ENET2网络PHY芯片的复位引脚不同,其余引脚相同,NXP官方EVK开发板网络PHY芯片复位引脚是通过74LV595扩展而来,而ALIENTEK-ALPHA开发板网络PHY芯片复位引脚连接到I.M6ULL的SNVS_TAMPER7和SNVS_TAMPER8引脚。
2.2 修改LAN8720的复位及网络时钟引脚驱动
修改NXP官方Linux内核网络驱动的设备树代码,打开设备树文件imx6ull-alientek.dts文件屏蔽74LV595控制引脚且增加ENET1和ENET2网路PHY芯片复位引脚控制,更改如下图所示:
更改后的代码如下:
spi4 { /****************** shield 74lv595 pin 20240303 **************/
compatible = "spi-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
/*pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;*/
status = "okay";
/*gpio-sck = <&gpio5 11 0>;
gpio-mosi = <&gpio5 10 0>;
cs-gpios = <&gpio5 7 0>;*/
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
gpio_spi: gpio_spi@0 {
compatible = "fairchild,74hc595";
gpio-controller;
#gpio-cells = <2>;
reg = <0>;
registers-number = <1>;
registers-default = /bits/ 8 <0x57>;
spi-max-frequency = <100000>;
};
};
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
pinctrl_hog_2: hoggrp-2 {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x80000000
>;
};
pinctrl_dvfs: dvfsgrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x79
>;
};
pinctrl_lcdif_reset: lcdifresetgrp {
fsl,pins = <
/* used for lcd reset */
MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79
>;
};
pinctrl_spi4: spi4grp {
fsl,pins = <
/**********shield 74lv595 pin **************************
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
*/
>;
};
/******************* add enet1 reset pin set 20240303 *********************/
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = <
/********* enet1 reset pin set *****************/
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
>;
};
/******************* add enet2 reset pin set 20240303 *********************/
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = <
/********* enet2 reset pin set *****************/
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
>;
};
pinctrl_sai2_hp_det_b: sai2_hp_det_grp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059
>;
};
};
};
2.3 修改ENET1和ENET2节点的pinctrl-0属性
打开设备树imx6ull-alientek.dts文件中找到fec1和fec2两个节点,修改其中的“pinctrl-0”属性值,增加ENET1和ENET2网络PHY芯片复位引脚初始化,修改后的代码如下所示:
2.4 设置LAN8720芯片复位引脚电平及地址
设置ENET1和ENET2网络PHY芯片复位引脚电平及电平持续时间,复位有效信号为低电平且电平持续时间为200ms,修改后的代码如下图所示:
由2.1章节可知,ALIENTEK-ALPHA开发板ENET1网络PHY芯片 LAN8720A的地址为0x0,ENET1网络PHY芯片 LAN8720A的地址为0x1,因此需要设置设备树文件PHY芯片的地址,打开设备树imx6ull-alientek.dts文件,找到fec2网络设备节点,fec2节点中mdio节点描述了ENET1和ENET2网络的PHY地址信息,“ethernet-phy@”后面的数字就是PHY的地址同时reg的值也表示PHY的地址,修改后的代码如下图所示:
修改后的fec1和fec2网络节点代码如下所示:
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_enet1_reset>; /*enet1 reset pin set*/
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>; /*enet1 reset pin output low level*/
phy-reset-duration = <200>; /*low level time 200ms*/
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_enet2_reset>; /*enet2 reset pin set*/
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; /*enet2 reset pin output low level*/
phy-reset-duration = <200>; /*low level time 200ms*/
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
/* set ENET1 PHY LAN8720 address 0x0*/
ethphy0: ethernet-phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0>;
};
/* set ENET2 PHY LAN8720 address 0x1*/
ethphy1: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
};
};
};
保存修改后的文件,然后使用“make dtbs”命令重新编译下设备树文件,如下图所示:
2.5 修改fec_main.c文件
在I.MX6ULL上使用LAN8720A,需要修改下Linux内核源码,打开drivers/net/
ethernet/freescale/fec_main.c文件,找到函数fec_probe,在fec_probe函数中加入如下图所示代码:
修改后的函数源码如下所示:
static int
fec_probe(struct platform_device *pdev)
{
struct fec_enet_private *fep;
struct fec_platform_data *pdata;
struct net_device *ndev;
int i, irq, ret = 0;
struct resource *r;
const struct of_device_id *of_id;
static int dev_id;
struct device_node *np = pdev->dev.of_node, *phy_node;
int num_tx_qs;
int num_rx_qs;
/*set MX6UL_PAD_ENET1_TX_CLK and MX6UL_PAD_ENET2_TX_CLK
IOMUXC_SW_MUX_CTL_PAD_ENET1_TX_CLK register SION
IOMUXC_SW_MUX_CTL_PAD_ENET1_TX_CLK register SION */
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);
fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
/* Init network device */
ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
num_tx_qs, num_rx_qs);
if (!ndev)
return -ENOMEM;
SET_NETDEV_DEV(ndev, &pdev->dev);
/* setup board info structure */
fep = netdev_priv(ndev);
of_id = of_match_device(fec_dt_ids, &pdev->dev);
if (of_id)
pdev->id_entry = of_id->data;
fep->quirks = pdev->id_entry->driver_data;
fep->netdev = ndev;
fep->num_rx_queues = num_rx_qs;
fep->num_tx_queues = num_tx_qs;
#if !defined(CONFIG_M5272)
/* default enable pause frame auto negotiation */
if (fep->quirks & FEC_QUIRK_HAS_GBIT)
fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
#endif
/* Select default pin state */
pinctrl_pm_select_default_state(&pdev->dev);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
fep->hwp = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(fep->hwp)) {
ret = PTR_ERR(fep->hwp);
goto failed_ioremap;
}
fep->pdev = pdev;
fep->dev_id = dev_id++;
platform_set_drvdata(pdev, ndev);
fec_enet_of_parse_stop_mode(pdev);
if (of_get_property(np, "fsl,magic-packet", NULL))
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
phy_node = of_parse_phandle(np, "phy-handle", 0);
if (!phy_node && of_phy_is_fixed_link(np)) {
ret = of_phy_register_fixed_link(np);
if (ret < 0) {
dev_err(&pdev->dev,
"broken fixed-link specification\n");
goto failed_phy;
}
phy_node = of_node_get(np);
}
fep->phy_node = phy_node;
ret = of_get_phy_mode(pdev->dev.of_node);
if (ret < 0) {
pdata = dev_get_platdata(&pdev->dev);
if (pdata)
fep->phy_interface = pdata->phy;
else
fep->phy_interface = PHY_INTERFACE_MODE_MII;
} else {
fep->phy_interface = ret;
}
fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(fep->clk_ipg)) {
ret = PTR_ERR(fep->clk_ipg);
goto failed_clk;
}
fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(fep->clk_ahb)) {
ret = PTR_ERR(fep->clk_ahb);
goto failed_clk;
}
fep->itr_clk_rate = clk_get_rate(fep->clk_ahb);
/* enet_out is optional, depends on board */
fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
if (IS_ERR(fep->clk_enet_out))
fep->clk_enet_out = NULL;
fep->ptp_clk_on = false;
mutex_init(&fep->ptp_clk_mutex);
/* clk_ref is optional, depends on board */
fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref");
if (IS_ERR(fep->clk_ref))
fep->clk_ref = NULL;
fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX;
fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp");
if (IS_ERR(fep->clk_ptp)) {
fep->clk_ptp = NULL;
fep->bufdesc_ex = false;
}
pm_runtime_enable(&pdev->dev);
ret = fec_enet_clk_enable(ndev, true);
if (ret)
goto failed_clk;
fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
if (!IS_ERR(fep->reg_phy)) {
ret = regulator_enable(fep->reg_phy);
if (ret) {
dev_err(&pdev->dev,
"Failed to enable phy regulator: %d\n", ret);
goto failed_regulator;
}
} else {
fep->reg_phy = NULL;
}
fec_reset_phy(pdev);
if (fep->bufdesc_ex)
fec_ptp_init(pdev);
ret = fec_enet_init(ndev);
if (ret)
goto failed_init;
for (i = 0; i < FEC_IRQ_NUM; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
if (i)
break;
ret = irq;
goto failed_irq;
}
ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,
0, pdev->name, ndev);
if (ret)
goto failed_irq;
fep->irq[i] = irq;
}
ret = of_property_read_u32(np, "fsl,wakeup_irq", &irq);
if (!ret && irq < FEC_IRQ_NUM)
fep->wake_irq = fep->irq[irq];
else
fep->wake_irq = fep->irq[0];
init_completion(&fep->mdio_done);
ret = fec_enet_mii_init(pdev);
if (ret)
goto failed_mii_init;
/* Carrier starts down, phylib will bring it up */
netif_carrier_off(ndev);
fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&pdev->dev);
ret = register_netdev(ndev);
if (ret)
goto failed_register;
device_init_wakeup(&ndev->dev, fep->wol_flag &
FEC_WOL_HAS_MAGIC_PACKET);
if (fep->bufdesc_ex && fep->ptp_clock)
netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
fep->rx_copybreak = COPYBREAK_DEFAULT;
INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
return 0;
failed_register:
fec_enet_mii_remove(fep);
failed_mii_init:
failed_irq:
failed_init:
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
failed_regulator:
fec_enet_clk_enable(ndev, false);
failed_clk:
failed_phy:
of_node_put(phy_node);
failed_ioremap:
free_netdev(ndev);
return ret;
}
2.6 配置Linux内核,使能LAN8720驱动
输入命令“make menuconfig”,打开图形化配置界面,选择使能 LAN8720A 的驱动,路径如下:
-> Device Drivers
-> Network device support
-> PHY Device support and infrastructure
-> Drivers for SMSC PHYs
配置过程如下图所示:
上图中选择将“Drivers for SMSC PHYs”编译到Linux内核中,因此“<>”里面变成了“*”。LAN8720是SMSC公司产品,因此勾选这个就会编译LAN8720驱动,配置完成后点击保存配置按键后推出配置界面,重新编译Linux内核。
3.网络驱动测试
将修改后的设备树和Linux内核重新编译,得到新的Linux镜像zImage文件和设备树imx6ull-alientek-emmc.dtb文件,将上面的两个文件拷贝到Ubuntu服务器tftpboot目录下,使用网线将ALIENTEK-ALPHA开发板ENET2网络接口使用网线连接到路由器,启动开发板uboot启动完成后自动从Ubuntu服务器tftpboot目录下下载Linux镜像文件和设备树文件,然后启动Linux内核,操作步骤如下图所示:
使用ifconfig -a命令查看ALIENTEK-ALPHA开发板中存在的所有网卡信息,结果如下图所示:
由上图可知,ALIENTEK-ALPHA开发板存在两个网络接口eth0和eth1,其中
eth0对应于开发板上ENET2网络,eth1对应于开发板ENET1网络,使用“ping”命令来ping下Ubuntu主机(IP地址为192.168.0.108)和百度,ping命令结果如下图所示:
将ALIENTEK-ALPHA开发板ENET2网线插入ENET1网络接口,使用ifconfig命令查看开发板网卡,如下图所示:
使用“ping”命令测试ALIENTEK-ALPHA开发板ENET1网络,测试结果如下图所示:
经过测试ALIENTEK-ALPHA开发板ENET1和ENET2网络工作正常,Linux内核网络驱动修改完成。