CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/10/31/Exynos4412——网卡移植和NFS启动/#more
为Exynos 4412移植好网卡驱动,并采用NFS挂载根文件系统启动。
玩Exynos4412的第一个目标是想弄下LCD驱动。
在开始流畅的写驱动前,前期的准备工作又多又繁琐。
上次移植好了内核,内核和根文件系统都是在RAM进行进行加载的,后面写驱动的时候,肯定不方便。
就以往的经验来看,uboot、kernel都应该在SD卡或者eMMC上,这样断电才不会丢失。根文件系统在调试驱动的时候应该是nfs挂载,调试完成了再烧写到SD卡或者eMMC上。
因此,写驱动前至少还要移植好SD卡驱动、网卡驱动。eMMC可暂时不管。
本次就先移植好网卡驱动,并尝试NFS挂载根文件系统。
1.硬件结构
JZ2440中的网卡芯片是DM9000,采用的是内存接口。
Tiny4412的网卡芯片是DM9621,采用的是USB接口。
而且该芯片并没有直接接在SOC的USB上,而是接在USB4604这个HUB芯片上。
USB4604通过HSIC接口(XuhostSTROBE1和XuhostDATA1)接在SOC上。
框架如下:
因此,需要重点关注USB4604和DM9621。查看这两块的原理图:
USB4604:
DM9621:
发现USB4604的一些功能并没有使用,如外接SPI/I2C设备等。除了数据端口,还能控制的就是复位引脚。
DM9621的驱动在本内核的drivers/net/usb/dm9601.c已经实现,但为了支持识别bootargs中的mac地址的功能,需要稍微修改dm9601.c,添加解析mac地址的功能,并且还需要将解析到的有效的mac地址设置到dm9621中,否则网络不能用。
USB4604的驱动在本内核的drivers/usb/misc/usb4604.c已经实现,但由于没有外接SPI/I2C,需要删除一些,可以参考drivers/usb/misc/usb3503.c或者博客里的代码。我直接用的博客里面的代码。
此外,还需在设备树使能将要用到的外设:hsi、ehci、otg等。
2.移植网卡驱动
目前需要修改的文件有:
(1)设备树文件:arch/arm/boot/dts/exynos4412-tiny4412.dts;
(2)DM9621驱动:drivers/net/usb/dm9601.c;
(3)USB4604驱动:drivers/usb/misc/usb4604.c;
(4)USB4604驱动头文件:include/linux/platform_data/usb4604.h(新建);
(5)设备树头文件:include/dt-bindings/usb4640/usb4604.h(新建);
(6)DM9621驱动加入内核;
(7)USB4604驱动加入内核;
2.1 修改设备树文件
主要添加了usb-hub的复位引脚信息和使能了一些外设。
{% codeblock lang:c%}
— /work/tools/linux-4.13.9/arch/arm/boot/dts/exynos4412-tiny4412.dts 2017-10-21 08:55:07.000000000 -0700
+++ /work/linux-4.13.9/arch/arm/boot/dts/exynos4412-tiny4412.dts 2017-10-30 02:34:10.363297869 -0700
@@ -14,6 +14,7 @@
/dts-v1/;
#include “exynos4412.dtsi”
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/usb4604/usb4604.h>
/ {
model = “FriendlyARM TINY4412 board based on Exynos4412”;
@@ -21,6 +22,7 @@
chosen {
stdout-path = &serial_0;
-
bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 init=/linuxrc earlyprintk";
};
memory@40000000 {
@@ -28,6 +30,12 @@
reg = <0x40000000 0x40000000>;
}; -
usb-hub {
-
compatible = "smsc,usb4604";
-
reset-gpios = <&gpm2 4 GPIO_ACTIVE_LOW>;
-
initial-mode = <USB4604_MODE_HUB>;
-
};
-
leds {
compatible = “gpio-leds”;
@@ -79,7 +87,7 @@
bus-width = <4>;
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
pinctrl-names = “default”;
- status = “okay”;
- status = “disabled”;
};
&serial_0 {
@@ -97,3 +105,32 @@
&serial_3 {
status = “okay”;
};
+
+
+&exynos_usbphy {
- status = “okay”;
+};
+&ehci {
- status = “okay”;
- port@0 {
-
status = "okay";
- };
- port@1 {
-
status = "okay";
- };
- port@2 {
-
status = "okay";
- };
+};
+&ohci {
- status = “okay”;
- port@0 {
-
status = "okay";
- };
+};
+&hsotg {
- status = “okay”;
+};
{% endcodeblock %}
2.2 修改DM9621驱动
主要是添加解析mac地址的功能,并且还需要将解析到的有效的mac地址设置到dm9621中。
{% codeblock lang:c%}
— /work/tools/linux-4.13.9/drivers/net/usb/dm9601.c 2017-10-21 08:55:07.000000000 -0700
+++ /work/linux-4.13.9/drivers/net/usb/dm9601.c 2017-10-30 02:06:54.319597116 -0700
@@ -58,6 +58,39 @@
#define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */
#define DM_TIMEOUT 1000
+/* Setup ethernet address */
+static u8 param_addr[ETH_ALEN];
+
+static int __init dm9601_set_mac(char *str) {
- u8 addr[ETH_ALEN];
- uint val;
- int idx = 0;
- char *p = str, *end;
- while (*p && idx < ETH_ALEN) {
-
val = simple_strtoul(p, &end, 16);
-
if (end <= p) {
-
break;
-
} else {
-
addr[idx++] = val;
-
p = end;
-
if (*p == ':'|| *p == '-') {
-
p++;
-
} else {
-
break;
-
}
-
}
- }
- if (idx == ETH_ALEN) {
-
printk("Setup ethernet address to %pM\n", addr);
-
memcpy(param_addr, addr, ETH_ALEN);
- }
- return 1;
+}
+__setup(“ethmac=”, dm9601_set_mac);
static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
{
int err;
@@ -190,8 +223,6 @@
return dm_read_shared_word(dev, 0, offset, value);
}
static int dm9601_get_eeprom_len(struct net_device *dev)
{
return DM_EEPROM_LEN;
@@ -281,9 +312,9 @@
.set_msglevel = usbnet_set_msglevel,
.get_eeprom_len = dm9601_get_eeprom_len,
.get_eeprom = dm9601_get_eeprom,
+// .get_settings = usbnet_get_settings,
+// .set_settings = usbnet_set_settings,
.nway_reset = usbnet_nway_reset,
- .get_link_ksettings = usbnet_get_link_ksettings,
- .set_link_ksettings = usbnet_set_link_ksettings,
};
static void dm9601_set_multicast(struct net_device *net)
@@ -343,7 +374,6 @@
.ndo_start_xmit = usbnet_start_xmit,
.ndo_tx_timeout = usbnet_tx_timeout,
.ndo_change_mtu = usbnet_change_mtu,
- .ndo_get_stats64 = usbnet_get_stats64,
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = dm9601_ioctl,
.ndo_set_rx_mode = dm9601_set_multicast,
@@ -390,7 +420,11 @@
/*- Overwrite the auto-generated address only with good ones.
*/
- Overwrite the auto-generated address only with good ones.
- if (is_valid_ether_addr(mac))
- if (is_valid_ether_addr(param_addr)) {
-
/* write MAC to dm9621 */
-
memcpy(dev->net->dev_addr, param_addr, ETH_ALEN);
-
__dm9601_set_mac_address(dev);
- } else if (is_valid_ether_addr(mac))
memcpy(dev->net->dev_addr, mac, ETH_ALEN);
else {
printk(KERN_WARNING
{% endcodeblock %}
2.3 修改USB4604驱动##
这个几乎就全改了,应该原来的那个稍微改改也能用。
{% codeblock lang:c%}
/*
- Driver for SMSC USB4604 USB 2.0 hub controller driver
*/
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb4604.h>
struct usb4604 {
enum usb4604_mode mode;
struct device *dev;
int gpio_reset;
};
static int usb4604_reset(struct usb4604 *hub, int state)
{
if (gpio_is_valid(hub->gpio_reset))
gpio_set_value_cansleep(hub->gpio_reset, state);
/* Wait 1ms for hub logic to stabilize */
if (state)
usleep_range(1, 10);
return 0;
}
static int usb4604_connect(struct usb4604 *hub)
{
struct device *dev = hub->dev;
usb4604_reset(hub, 1);
hub->mode = USB4604_MODE_HUB;
dev_info(dev, "switched to HUB mode\n");
return 0;
}
static int usb4604_switch_mode(struct usb4604 *hub, enum usb4604_mode mode)
{
struct device *dev = hub->dev;
int err = 0;
switch (mode) {
case USB4604_MODE_HUB:
err = usb4604_connect(hub);
break;
case USB4604_MODE_STANDBY:
usb4604_reset(hub, 0);
dev_info(dev, "switched to STANDBY mode\n");
break;
default:
dev_err(dev, "unknown mode is requested\n");
err = -EINVAL;
break;
}
return err;
}
static int usb4604_probe(struct usb4604 *hub)
{
struct device *dev = hub->dev;
struct usb4604_platform_data *pdata = dev_get_platdata(dev);
struct device_node *np = dev->of_node;
int err;
u32 mode = USB4604_MODE_HUB;
if (pdata) {
hub->gpio_reset = pdata->gpio_reset;
hub->mode = pdata->initial_mode;
} else if (np) {
hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
if (hub->gpio_reset == -EPROBE_DEFER)
return -EPROBE_DEFER;
of_property_read_u32(np, "initial-mode", &mode);
hub->mode = mode;
}
if (gpio_is_valid(hub->gpio_reset)) {
err = devm_gpio_request_one(dev, hub->gpio_reset,
GPIOF_OUT_INIT_LOW, "usb4604 reset");
if (err) {
dev_err(dev,
"unable to request GPIO %d as reset pin (%d)\n",
hub->gpio_reset, err);
return err;
}
}
usb4604_switch_mode(hub, hub->mode);
dev_info(dev, "%s: probed in %s mode\n", __func__,
(hub->mode == USB4604_MODE_HUB) ? "hub" : "standby");
return 0;
}
static int usb4604_platform_probe(struct platform_device *pdev)
{
struct usb4604 *hub;
hub = devm_kzalloc(&pdev->dev, sizeof(struct usb4604), GFP_KERNEL);
if (!hub)
return -ENOMEM;
hub->dev = &pdev->dev;
return usb4604_probe(hub);
}
#ifdef CONFIG_OF
static const struct of_device_id usb4604_of_match[] = {
{ .compatible = “smsc,usb4604”, },
{},
};
MODULE_DEVICE_TABLE(of, usb4604_of_match);
#endif
static struct platform_driver usb4604_platform_driver = {
.driver = {
.name = USB4604_NAME,
.of_match_table = of_match_ptr(usb4604_of_match),
},
.probe = usb4604_platform_probe,
};
static int __init usb4604_init(void)
{
int err;
err = platform_driver_register(&usb4604_platform_driver);
if (err != 0)
pr_err(“usb4604: Failed to register platform driver: %d\n”,
err);
return 0;
}
module_init(usb4604_init);
static void __exit usb4604_exit(void)
{
platform_driver_unregister(&usb4604_platform_driver);
}
module_exit(usb4604_exit);
MODULE_DESCRIPTION(“USB4604 USB HUB driver”);
MODULE_LICENSE(“GPL”);
{% endcodeblock %}
2.4 USB4604驱动头文件##
枚举usb模式。
{% codeblock lang:c%}
#ifndef USB4604_H
#define USB4604_H
#define USB4604_NAME “usb4604”
enum usb4604_mode {
USB4604_MODE_UNKNOWN = 1,
USB4604_MODE_HUB,
USB4604_MODE_STANDBY,
};
struct usb4604_platform_data {
enum usb4604_mode initial_mode;
int gpio_reset;
};
#endif
{% endcodeblock %}
2.5 设备树头文件##
提供usb-hub的工作模式宏。
{% codeblock lang:c%}
#ifndef _DT_BINDINGS_USB4604
#define _DT_BINDINGS_USB4604
#define USB4604_MODE_UNKNOWN 1
#define USB4604_MODE_HUB 2
#define USB4604_MODE_STANDBY 3
#endif
{% endcodeblock %}
2.6 DM9621驱动加入内核##
进入配置界面:
make menuconfig
将DM9621驱动加入内核
2.7 USB4604驱动加入内核##
将USB4604驱动加入内核
2.8 重新编译、烧写、测试##
- 编译:
make uImage LOADADDR=0x40008000
make dtbs
- 烧写:
在开发板uboot界面输入:
dnw 0x40600000;dnw 0x41000000;dnw 0x42000000
在Ubuntu依次输入:
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/uImage
sudo ./dnw /work/ramdisk/ramdisk.img
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/dts/exynos4412-tiny4412.dtb
最后再开发板启动:
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/uImage
sudo ./dnw /work/ramdisk/ramdisk.img
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/dts/exynos4412-tiny4412.dtb
- 测试:
设置网卡ip
ifconfig eth0 192.168.1.225
ping主机:
ping -c4 192.168.1.226
3.NFS启动
这里的根文件系统直接使用的是韦老大第三期项目视频的根文件系统,之前在JZ2440上也能运行,就直接拿来用了。
此外修改了根文件系统、的etc/init.d/rcS
,加入了ifconfig eth0 192.168.1.225
设置静态IP。
设置uboot参数:
setenv bootargs 'root=/dev/nfs rw nfsroot=192.168.1.226:/work/fs_mini_mdev_new ethmac=00:40:5c:26:0a:5b ip=192.168.1.225:192.168.1.226:192.168.1.1:255.255.255.0::eth0:off console=ttySAC0,115200 init=/linuxrc'; saveenv
开发板下载及启动:
dnw 0x40000000;dnw 0x42000000;bootm 0x40000000 - 0x42000000
Ubuntu下载:
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/uImage
sudo ./dnw /work/linux-4.13.9/arch/arm/boot/dts/exynos4412-tiny4412.dtb
NFS启动效果:
4.心得
感谢摩斯电码的博客,很多的参考他的,节约了很多时间。
下一步移植完SD卡的驱动,将uboot和内核都放在SD卡上,根文件系统挂在nfs上,就可以搞LCD了。