前言
根据前面文章USB和Ethernet协议了解与原理图分析、DM_USB和DM_ETH模型简介所介绍,想必大家已经知道移植的流程应该是如何了,这里再简单的给出大致步骤,如下。
- 移植USB Host Controller驱动
- 使能USB3503 Hub
- 移植DM9621驱动
- 测试验证功能性
接下来就按照这几个步骤进行Uboot中DM9621网卡的移植之路,最终实现在Uboot中使用网络命令进行内核的加载等操作。
移植开始
移植USB Host Controller驱动
该部分驱动已经有三星官方提供到Uboot主线中,我们下载的源码包里面已经有对应的代码,drivers/usb/host/ehci-exynos.c
文件包含了其实现。我们要做的只是将其编译选项打开,编译进Uboot最终bin文件中即可,下面给出详细的步骤。
打开编译选项
- Kconfig中打开主机驱动宏,对应路径如下
Device Drivers --->
[*] USB support --->
- 打开Exynos4412对应的宏
CONFIG_USB_EHCI_EXYNOS
diff --git a/include/configs/itop4412.h b/include/configs/itop4412.h
index c071e02..5bd5fdc 100644
--- a/include/configs/itop4412.h
+++ b/include/configs/itop4412.h
@@ -35,6 +35,9 @@
/* #define MACH_TYPE_ITOP4412 0xffffffff */
#define CONFIG_MACH_TYPE MACH_TYPE_ITOP4412
+/* USB */
+#define CONFIG_USB_EHCI_EXYNOS
+
/* select serial console configuration */
#define CONFIG_SERIAL2
- 打开Uboot的USB命令宏,对应路径如下
Command line interface --->
Device access commands --->
[*] usb
设备树修改
加入ehci设备描述信息,如下:
45 ehci@12580000 {
46 compatible = "samsung,exynos-ehci";
47 reg = <0x12580000 0x100>;
48 #address-cells = <1>;
49 #size-cells = <1>;
50 status = "okay";
51 /* In order to reset USB ethernet, DM9621 RESET IO pin */
52 samsung,vbus-gpio = <&gpc0 1 0>;
53 phy {
54 compatible = "samsung,exynos-usb-phy";
55 reg = <0x125B0000 0x100>;
56 };
57 };
编译与调试
在上述操作编译完成后,就能使用USB主机控制器了,在终端键入USB start
便可以看到有相应的USB信息输出,接下来便可以进行USB 3503的配置与使能。
使能USB3503 Hub
配置GPIO
根据前面文章USB和Ethernet协议了解与原理图分析的分析,USB 3503 Hub芯片配置简单,只需要做些IO操作便可。通过地板原理图核心板原理图查找分析,需要操作GPM3_3 (USB3503_CONNECT)
和GPM2_4 (USB3503_RESET)
。这里使用Uboot的IO驱动模型,而不是直接写寄存器,通过对应的宏和标准GPIO接口调用,便可以完成对USB Hub芯片的初始化,下面给出代码。
diff --git a/board/samsung/itop4412/itop4412.c b/board/samsung/itop4412/itop4412.c
index 99a2fac..3068cd4 100644
--- a/board/samsung/itop4412/itop4412.c
+++ b/board/samsung/itop4412/itop4412.c
@@ -12,6 +12,14 @@
#include <asm/arch/periph.h>
#include <asm/arch/pinmux.h>
#include <usb.h>
+#include <usb/dwc2_udc.h>
+
+#define DEBUG
+#ifdef DEBUG
+#undef debug
+#define debug(fmt, args...) debug_cond(true, fmt, ##args)
+#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -20,15 +28,82 @@ u32 get_board_rev(void)
return 0;
}
+static void board_gpio_init(void)
+{
+#ifdef CONFIG_CMD_USB
+ /* USB3503A Connect */
+ gpio_request(EXYNOS4X12_GPIO_M33, "USB3503A Connect");
+
+ /* USB3503A Reset */
+ gpio_request(EXYNOS4X12_GPIO_M24, "USB3503A Reset");
+
+ /* Red LED2 Light On */
+ gpio_request(EXYNOS4X12_GPIO_L20, "Red LED2");
+ gpio_direction_output(EXYNOS4X12_GPIO_L20, 1);
+#endif
+}
+
int exynos_init(void)
{
+ debug("---> ready to call board_gpio_init()!\n");
+ board_gpio_init();
+
+ /* FIXME: maybe should be not called in here */
+ board_usb_init(0, USB_INIT_DEVICE);
+
return 0;
}
+#ifdef CONFIG_USB_GADGET
+static int s5pc210_phy_control(int on)
+{
+ /* FIXME: need to set power? */
+#if 0
+ struct udevice *dev;
+ int ret;
+
+ ret = regulator_get_by_platname("VDD_UOTG_3.0V", &dev);
+ if (ret) {
+ pr_err("Regulator get error: %d", ret);
+ return ret;
+ }
+
+ if (on)
+ return regulator_set_mode(dev, OPMODE_ON);
+ else
+ return regulator_set_mode(dev, OPMODE_LPM);
+#else
+ return 0;
+#endif
+}
+
+struct dwc2_plat_otg_data s5pc210_otg_data = {
+ .phy_control = s5pc210_phy_control,
+ .regs_phy = EXYNOS4X12_USBPHY_BASE,
+ .regs_otg = EXYNOS4X12_USBOTG_BASE,
+ .usb_phy_ctrl = EXYNOS4X12_USBPHY_CONTROL,
+ .usb_flags = PHY0_SLEEP,
+};
+#endif
+
+#if defined(CONFIG_USB_GADGET) || defined(CONFIG_CMD_USB)
+
int board_usb_init(int index, enum usb_init_type init)
{
- return 0;
+#ifdef CONFIG_CMD_USB
+ debug("---> ready to init usb3503\n");
+
+ /* USB3503A Disconnect, Reset, Connect */
+ gpio_direction_output(EXYNOS4X12_GPIO_M33, 0);
+ gpio_direction_output(EXYNOS4X12_GPIO_M24, 0);
+ gpio_direction_output(EXYNOS4X12_GPIO_M24, 1);
+ gpio_direction_output(EXYNOS4X12_GPIO_M33, 1);
+
+#endif
+ debug("USB_udc_probe\n");
+ return dwc2_udc_probe(&s5pc210_otg_data);
}
+#endif
#ifdef CONFIG_BOARD_EARLY_INIT_F
int exynos_early_init_f(void)
编译与调试
在执行完这步骤的时候,执行usb start; usb reset
后,便会有下面的对应输出,说明USB 3503已经在正常工作了,并且DM9621网卡已经使能了,接下来就是初始化并配置设备和适配到DM_ETH模型的操作了。
CPU: Exynos4412 @ 1 GHz
Model: itop-4412 based on Exynos4412
Board: itop-4412 based on Exynos4412
DRAM: 1 GiB
WARNING: Caches not enabled
---> ready to call board_gpio_init()!
---> ready to init usb3503
USB_udc_probe
MMC: SAMSUNG SDHCI: 0, EXYNOS DWMMC: 1
Net: No ethernet found.
Hit any key to stop autoboot: 0
u-boot # usb start
starting USB...
USB0: USB EHCI 1.00
scanning bus 0 for devices... 1 USB Device(s) found
u-boot # usb reset
resetting USB...
USB0: USB EHCI 1.00
scanning bus 0 for devices... 3 USB Device(s) found
u-boot # usb tree
USB device tree:
1 Hub (480 Mb/s, 0mA)
| u-boot EHCI Host Controller
|
+-2 Hub (480 Mb/s, 2mA)
|
+-3 See Interface (480 Mb/s, 180mA)
?
u-boot #
移植DM9621驱动
说明
在上面的步骤执行完,可以发现DM9621设备已经使能,能在USB总线上看到该设备了,接下来需要初始化和配置,并根据DM_ETH模型来提供相应的操作集便可。
移植开始
根据上篇文章DM_USB和DM_ETH模型简介所介绍,我们需要提供以下标准接口:
- 设备驱动
- 设备驱动操作集
- 设备
- 设备相关接口(因物理设备而异)
设备驱动
+U_BOOT_DRIVER(dm9601_eth) = {
+ .name = "dm9601_eth",
+ .id = UCLASS_ETH,
+ .probe = dm9601_eth_probe,
+ .ops = &dm9601_eth_ops,
+ .priv_auto_alloc_size = sizeof(struct dm9601_private),
+ .platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
设备驱动操作集
+static const struct eth_ops dm9601_eth_ops = {
+ .start = dm9601_eth_start,
+ .send = dm9601_eth_send,
+ .recv = dm9601_eth_recv,
+ .free_pkt = dm9601_free_pkt,
+ .stop = dm9601_eth_stop,
+ .write_hwaddr = dm9601_write_hwaddr,
+};
设备
+static const struct usb_device_id dm9601_eth_id_table[] = {
+ { USB_DEVICE(0x07aa, 0x9601), }, /* Corega FEther USB-TXC */
+ { USB_DEVICE(0x0a46, 0x9601), }, /* Davicom USB-100 */
+ { USB_DEVICE(0x0a46, 0x6688), }, /* ZT6688 USB NIC */
+ { USB_DEVICE(0x0a46, 0x0268), }, /* ShanTou ST268 USB NIC */
+ { USB_DEVICE(0x0a46, 0x8515), }, /* ADMtek ADM8515 USB NIC */
+ { USB_DEVICE(0x0a47, 0x9601), }, /* Hirose USB-100 */
+ { USB_DEVICE(0x0fe6, 0x8101), }, /* DM9601 USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0fe6, 0x9700), }, /* DM9601 USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0a46, 0x9000), }, /* DM9000E */
+ { USB_DEVICE(0x0a46, 0x9620), }, /* DM9620 USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0a46, 0x9621), }, /* DM9621A USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0a46, 0x9622), }, /* DM9622 USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0a46, 0x0269), }, /* DM962OA USB to Fast Ethernet Adapter */
+ { USB_DEVICE(0x0a46, 0x1269), }, /* DM9621A USB to Fast Ethernet Adapter */
+ {}, // END
+};
+
+U_BOOT_USB_DEVICE(dm9601_eth, dm9601_eth_id_table);
关键接口
DM9621系列网卡为USB转以太网卡设备,在主线中已经有相关的驱动框架使用,我们需要使用usb_ether.c
文件中的关键接口,如下:
usb_ether_register
usb_ether_deregister
usb_ether_receive
usb_ether_advance_rxbuf
usb_ether_get_rx_bytes
ueth_data结构体
23 struct ueth_data {
24 /* eth info */
25 #ifdef CONFIG_DM_ETH
26 uint8_t *rxbuf;
27 int rxsize;
28 int rxlen; /* Total bytes available in rxbuf */
29 int rxptr; /* Current position in rxbuf */
30 #else
31 struct eth_device eth_dev; /* used with eth_register */
32 /* driver private */
33 void *dev_priv;
34 #endif
35 int phy_id; /* mii phy id */
36
37 /* usb info */
38 struct usb_device *pusb_dev; /* this usb_device */
39 unsigned char ifnum; /* interface number */
40 unsigned char ep_in; /* in endpoint */
41 unsigned char ep_out; /* out ....... */
42 unsigned char ep_int; /* interrupt . */
43 unsigned char subclass; /* as in overview */
44 unsigned char protocol; /* .............. */
45 unsigned char irqinterval; /* Intervall for IRQ Pipe */
46 };
主要接口
博主原本想简单的概括一下关键接口的作用即可,但是觉得还是头文件中的声明和注释更能说明其用途,这里直接给出头文件include/usb_ether.h
中的内容,如下所示。
49 /**
50 * usb_ether_register() - register a new USB ethernet device
51 *
52 * This selects the correct USB interface and figures out the endpoints to use.
53 *
54 * @dev: USB device
55 * @ss: Place to put USB ethernet data
56 * @rxsize: Maximum size to allocate for the receive buffer
57 * @return 0 if OK, -ve on error
58 */
59 int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize);
60
61 /**
62 * usb_ether_deregister() - deregister a USB ethernet device
63 *
64 * @ueth: USB Ethernet device
65 * @return 0
66 */
67 int usb_ether_deregister(struct ueth_data *ueth);
68
69 /**
70 * usb_ether_receive() - recieve a packet from the bulk in endpoint
71 *
72 * The packet is stored in the internal buffer ready for processing.
73 *
74 * @ueth: USB Ethernet device
75 * @rxsize: Maximum size to receive
76 * @return 0 if a packet was received, -EAGAIN if not, -ENOSPC if @rxsize is
77 * larger than the size passed ot usb_ether_register(), other -ve on error
78 */
79 int usb_ether_receive(struct ueth_data *ueth, int rxsize);
80
81 /**
82 * usb_ether_get_rx_bytes() - obtain bytes from the internal packet buffer
83 *
84 * This should be called repeatedly to obtain packet data until it returns 0.
85 * After each packet is processed, call usb_ether_advance_rxbuf() to move to
86 * the next one.
87 *
88 * @ueth: USB Ethernet device
89 * @ptrp: Returns a pointer to the start of the next packet if there is
90 * one available
91 * @return number of bytes available, or 0 if none
92 */
93 int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp);
94
95 /**
96 * usb_ether_advance_rxbuf() - Advance to the next packet in the internal buffer
97 *
98 * After processing the data returned by usb_ether_get_rx_bytes(), call this
99 * function to move to the next packet. You must specify the number of bytes
100 * you have processed in @num_bytes.
101 *
102 * @ueth: USB Ethernet device
103 * @num_bytes: Number of bytes to skip, or -1 to skip all bytes
104 */
105 void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes);
具体实现
首先需要实现struct eth_ops
里的接口和对应的probe
接口,如何实现可以参考driver/usb/eth/asix.c
;对设备内部的操作,可以移植linux内核驱动里面的driver/net/usb/dm9601.c
中的实现到Uboot框架即可,具体文件dm9601.c。
编译配置
在移植好C程序后,还需要将代码编译进最终的u-boot.bin文件中,需要对Kconfig和Makefile文件增加相应的配置选项。
说明
本次移植是基于DM_ETH和DM_USB模型移植的,需要先使能如下配置,更多配置参阅文件itop4412_defconfig:
CONFIG_DM_USB=y
CONFIG_DM_ETH=y
CONFIG_USB_HOST_ETHER=y
CONFIG_USB_ETHER_DM9601=y
CONFIG_NET=y
CONFIG_CMD_USB=y
CONFIG_CMD_NET=y
其中CONFIG_USB_ETHER_DM9601=y为新增配置
Kconfig文件
diff --git a/u-boot-2017.11-bsp/u-boot-2017.11/drivers/usb/eth/Kconfig b/arm-devlop/uboot-2017-11/drivers/usb/eth/Kconfig
index 496a6d1..01b0d86 100644
--- a/u-boot-2017.11-bsp/u-boot-2017.11/drivers/usb/eth/Kconfig
+++ b/arm-devlop/uboot-2017-11/drivers/usb/eth/Kconfig
@@ -60,4 +60,11 @@ config USB_ETHER_SMSC95XX
Say Y here if you would like to support SMSC LAN95xx based USB 2.0
Ethernet Devices.
+config USB_ETHER_DM9601
+ bool "DAVICOM DM9601 (USB 2.0) support"
+ depends on USB_HOST_ETHER && DM_ETH
+ ---help---
+ Say Y here if you would like to support DAVICOM DM9601 based USB 2.0
+ Ethernet Devices.
+
endif
Makefile文件
diff --git a/u-boot-2017.11-bsp/u-boot-2017.11/drivers/usb/eth/Makefile b/arm-devlop/uboot-2017-11/drivers/usb/eth/Makefile
index 4b935a3..68491d4 100644
--- a/u-boot-2017.11-bsp/u-boot-2017.11/drivers/usb/eth/Makefile
+++ b/arm-devlop/uboot-2017-11/drivers/usb/eth/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o
obj-$(CONFIG_USB_ETHER_LAN75XX) += lan7x.o lan75xx.o
obj-$(CONFIG_USB_ETHER_LAN78XX) += lan7x.o lan78xx.o
obj-$(CONFIG_USB_ETHER_RTL8152) += r8152.o r8152_fw.o
+obj-$(CONFIG_USB_ETHER_DM9601) += dm9601.o
总结
到这一步已经完成了绝大部分工作了,剩下的便是编译并烧写到TF卡中,再讲拨码开关调到TF卡启动,最终运行查看效果。一运行就发现有如下的错误:
情况一
在运行到接收以太帧的时候,出现了data abrot异常,如下:
data abort
pc : [<7fe9a2a0>] lr : [<7aede325>]
reloc pc : [<43e432a0>] lr : [<3ee87325>]
sp : 7ae54ce0 ip : 00000014 fp : 00000fff
r10: 00000fff r9 : 7ae54ed8 r8 : 0000002e
r7 : 00000fff r6 : 7aede303 r5 : 0000001c r4 : 7aede311
r3 : 00000000 r2 : 7aede311 r1 : 00000014 r0 : 7aede311
Flags: nzCv IRQs off FIQs off Mode SVC_32
Resetting CPU ...
情况二
在解决完上面问题后,又出现了启动linux内核的时候,出现校验失败的问题,如下:
Verifying Checksum ... Bad Data CRC
ERROR: can't get kernel image!
看到这里想必有点懵,不知道从哪里做起,没事,下篇文章Uboot 网卡移植遇到的问题与解决方法博主给你解答。