作者信息
作者: 彭東林
QQ:405728433
平臺簡介
開發板:tiny4412ADK + S700 + 4GB Flash
要移植的內核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自帶的 U-Boot 2010.12 (為支持uImage啟動,做了少許改動)
busybox版本:busybox 1.25
網卡晶元:DM9621NP
交叉編譯工具鏈: arm-none-linux-gnueabi-gcc
(gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29))
概述
tiny4412的網卡部分跟我之前的tq2440差別很大,在tq2440中使用網卡晶元是DM9000,使用的是記憶體介面,移植起來很容易,但是到了tiny4412就不一樣了,tiny4412使用的網卡晶元是DM9621,它是usb介面的,而且並沒有直接連接到exynos4412上,中間通過一個hub晶元usb4640,然後usb4640通過HSIC介面(XhsicSTROBE0和XhsicDATA0)連接到exynos4412上,下麵是一些參考資料:
- HSI介紹
http://www.cnblogs.com/pengdonglin137/p/5151331.html
http://www.cnblogs.com/pengdonglin137/p/5151006.html
- exynos4412手冊的第31章USB 2.0 Host Controller
- 原理圖
上面這張圖就是tiny4412板子上的USB連接圖,我們重點關註DM9621和USB4640。
移植
通過閱讀usb4640的晶元手冊,同時結合tiny4412的原理圖發現,usb4640的部分功能在tiny4412上是沒有用的,沒有外接的SPI Flash和I2C設備,大部分引腳都懸空了,除了數據傳輸的埠外,我們可以控制的就只剩下USB4640的複位引腳了,並且板子起來後,我用萬用表測量了usb4640的複位引腳的電壓,發現是0.7v左右,說明usb4640一直處於複位狀態,最終針對usb4640我們要做的就是只是板子起來後,將控制usb4640複位的引腳電平拉高即可,當然需要在設備樹中添加usb4640用到的GPIO資源。
通過對比tiny4412自帶的linux-3.0.86版本的內核,對於DM9621的驅動在Linux 4.4中全部在driver/net/usb/dm9601.c中實現了,並且對於dm9601這樣的usb設備不需要出現在設備樹中。所以針對dm9601我們要做的就是:在內核配置時將DM9601的驅動選中即可,同時發現,為了支持識別bootargs中的mac地址的功能,需要稍微修改dm9601.c,添加解析mac地址的功能,並且還需要將解析到的有效的mac地址設置到dm9621中,否則網路沒法用(發現這個問題也廢了一些時間)。
此外,最關鍵的是一定要在設備樹中將用到的片內外設使能:如hsi、ehci、ohci、otg等等。
下麵是patch:
1: diff --git a/Makefile b/Makefile
2: index 70dea02..5d96411 100644
3: --- a/Makefile
4: +++ b/Makefile
5: @@ -248,8 +248,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
6: # "make" in the configured kernel build directory always uses that.
7: # Default value for CROSS_COMPILE is not to prefix executables
8: # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
9: -ARCH ?= $(SUBARCH)
10: -CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
11: +ARCH ?= arm
12: +CROSS_COMPILE ?= /root/tiny4412_android5/SysPort/cross_compile/arm-2014.05/bin/arm-none-linux-gnueabi-
13:
14: # Architecture as present in compile.h
15: UTS_MACHINE := $(ARCH)
16: diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts
17: index 4840bbd..69a0d5d 100644
18: --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts
19: +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts
20: @@ -14,6 +14,7 @@
21: /dts-v1/;
22: #include "exynos4412.dtsi"
23: #include <;dt-bindings/gpio/gpio.h>
24: +#include <;dt-bindings/usb4640/usb4640.h>
25:
26: / {
27: model = "FriendlyARM TINY4412 board based on Exynos4412";
28: @@ -21,6 +22,7 @@
29:
30: chosen {
31: stdout-path = &;serial_0;
32: + bootargs = "root=/dev/ram0 rw rootfstype=ext4 console=ttySAC0,115200 ethmac=1C:6F:65:34:51:7E init=/linuxrc";
33: };
34:
35: memory {
36: @@ -68,6 +70,12 @@
37: clock-frequency = <;24000000>;
38: };
39: };
40: +
41: + usb-hub {
42: + compatible = "smsc,usb4640";
43: + reset-gpios = <&gpm2 4 GPIO_ACTIVE_LOW>;
44: + initial-mode = <;USB4640_MODE_HUB>;
45: + };
46: };
47:
48: &;rtc {
49: @@ -78,7 +86,7 @@
50: bus-width = <;4>;
51: pinctrl-0 = <;&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
52: pinctrl-names = "default";
53: - status = "okay";
54: + status = "disabled";
55: };
56:
57: &;serial_0 {
58: @@ -96,3 +104,32 @@
59: &;serial_3 {
60: status = "okay";
61: };
62: +
63: +&;exynos_usbphy {
64: + status = "okay";
65: +};
66: +
67: +&;ehci {
68: + status = "okay";
69: + port@0 {
70: + status = "okay";
71: + };
72: + port@1 {
73: + status = "okay";
74: + };
75: + port@2 {
76: + status = "okay";
77: + };
78: +};
79: +
80: +&;ohci {
81: + status = "okay";
82: + port@0 {
83: + status = "okay";
84: + };
85: +};
86: +
87: +&;hsotg {
88: + status = "okay";
89: +};
90: +
91: diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
92: index 0b4bdd3..4361385 100644
93: --- a/drivers/net/usb/dm9601.c
94: +++ b/drivers/net/usb/dm9601.c
95: @@ -58,6 +58,39 @@
96: #define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */
97: #define DM_TIMEOUT 1000
98:
99: +/* Setup ethernet address */
100: +static u8 param_addr[ETH_ALEN];
101: +
102: +static int __init dm9601_set_mac(char *str) {
103: + u8 addr[ETH_ALEN];
104: + uint val;
105: + int idx = 0;
106: + char *p = str, *end;
107: +
108: + while (*p &;& idx < ETH_ALEN) {
109: + val = simple_strtoul(p, &;end, 16);
110: + if (end <;= p) {
111: + break;
112: + } else {
113: + addr[idx++] = val;
114: + p = end;
115: + if (*p == ':'|| *p == '-') {
116: + p++;
117: + } else {
118: + break;
119: + }
120: + }
121: + }
122: +
123: + if (idx == ETH_ALEN) {
124: + printk("Setup ethernet address to %pM\n", addr);
125: + memcpy(param_addr, addr, ETH_ALEN);
126: + }
127: +
128: + return 1;
129: +}
130: +__setup("ethmac=", dm9601_set_mac);
131: +
132: static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
133: {
134: int err;
135: @@ -190,8 +223,6 @@ static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
136: return dm_read_shared_word(dev, 0, offset, value);
137: }
138:
139: -
140: -
141: static int dm9601_get_eeprom_len(struct net_device *dev)
142: {
143: return DM_EEPROM_LEN;
144: @@ -389,7 +420,11 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
145: /*
146: * Overwrite the auto-generated address only with good ones.
147: */
148: - if (is_valid_ether_addr(mac))
149: + if (is_valid_ether_addr(param_addr)) {
150: + /* write MAC to dm9621 */
151: + memcpy(dev->;net->dev_addr, param_addr, ETH_ALEN);
152: + __dm9601_set_mac_address(dev);
153: + } else if (is_valid_ether_addr(mac))
154: memcpy(dev->;net->dev_addr, mac, ETH_ALEN);
155: else {
156: printk(KERN_WARNING
157: diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
158: index f7a7fc2..c1fcc2f 100644
159: --- a/drivers/usb/misc/Kconfig
160: +++ b/drivers/usb/misc/Kconfig
161: @@ -249,6 +249,11 @@ config USB_HSIC_USB3503
162: help
163: This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
164:
165: +config USB_HSIC_USB4640
166: + tristate "USB4640 HSIC to USB20 Driver"
167: + help
168: + This option enables support for SMSC USB4640 HSIC to USB 2.0 Driver.
169: +
170: config USB_LINK_LAYER_TEST
171: tristate "USB Link Layer Test driver"
172: help
173: diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
174: index 45fd4ac..05b6344 100644
175: --- a/drivers/usb/misc/Makefile
176: +++ b/drivers/usb/misc/Makefile
177: @@ -25,6 +25,7 @@ obj-$(CONFIG_USB_USS720) += uss720.o
178: obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
179: obj-$(CONFIG_USB_YUREX) += yurex.o
180: obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
181: +obj-$(CONFIG_USB_HSIC_USB4640) += usb4640.o
182: obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
183:
184: obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
185: diff --git a/drivers/usb/misc/usb4640.c b/drivers/usb/misc/usb4640.c
186: new file mode 100644
187: index 0000000..9c9c01b
188: --- /dev/null
189: +++ b/drivers/usb/misc/usb4640.c
190: @@ -0,0 +1,154 @@
191: +/*
192: + * Driver for SMSC USB4640 USB 2.0 hub controller driver
193: + *
194: + */
195: +
196: +#include <;linux/gpio.h>
197: +#include <;linux/delay.h>
198: +#include <;linux/slab.h>
199: +#include <;linux/module.h>
200: +#include <;linux/of_gpio.h>
201: +#include <;linux/platform_device.h>
202: +#include <;linux/platform_data/usb4640.h>
203: +
204: +struct usb4640 {
205: + enum usb4640_mode mode;
206: + struct device *dev;
207: + int gpio_reset;
208: +};
209: +
210: +static int usb4640_reset(struct usb4640 *hub, int state)
211: +{
212: + if (gpio_is_valid(hub->;gpio_reset))
213: + gpio_set_value_cansleep(hub->;gpio_reset, state);
214: +
215: + /* Wait 1ms for hub logic to stabilize */
216: + if (state)
217: + usleep_range(1, 10);
218: +
219: + return 0;
220: +}
221: +
222: +static int usb4640_connect(struct usb4640 *hub)
223: +{
224: + struct device *dev = hub->;dev;
225: +
226: + usb4640_reset(hub, 1);
227: + hub->mode = USB4640_MODE_HUB;
228: + dev_info(dev, "switched to HUB mode\n");
229: +
230: + return 0;
231: +}
232: +
233: +static int usb4640_switch_mode(struct usb4640 *hub, enum usb4640_mode mode)
234: +{
235: + struct device *dev = hub->;dev;
236: + int err = 0;
237: +
238: + switch (mode) {
239: + case USB4640_MODE_HUB:
240: + err = usb4640_connect(hub);
241: + break;
242: +
243: + case USB4640_MODE_STANDBY:
244: + usb4640_reset(hub, 0);
245: + dev_info(dev, "switched to STANDBY mode\n");
246: + break;
247: +
248: + default:
249: + dev_err(dev, "unknown mode is requested\n");
250: + err = -EINVAL;
251: + break;
252: + }
253: +
254: + return err;
255: +}
256: +
257: +
258: +static int usb4640_probe(struct usb4640 *hub)
259: +{
260: + struct device *dev = hub->;dev;
261: + struct usb4640_platform_data *pdata = dev_get_platdata(dev);
262: + struct device_node *np = dev->of_node;
263: + int err;
264: + u32 mode = USB4640_MODE_HUB;
265: +
266: + if (pdata) {
267: + hub->;gpio_reset = pdata->gpio_reset;
268: + hub->;mode = pdata->initial_mode;
269: + } else if (np) {
270: + hub->;gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
271: + if (hub->;gpio_reset == -EPROBE_DEFER)
272: + return -EPROBE_DEFER;
273: + of_property_read_u32(np, "initial-mode", &mode);
274: + hub->;mode = mode;
275: + }
276: +
277: + if (gpio_is_valid(hub->;gpio_reset)) {
278: + err = devm_gpio_request_one(dev, hub->;gpio_reset,
279: + GPIOF_OUT_INIT_LOW, "usb4640 reset");
280: + if (err) {
281: + dev_err(dev,
282: + "unable to request GPIO %d as reset pin (%d)\n",
283: + hub->;gpio_reset, err);
284: + return err;
285: + }
286: + }
287: +
288: + usb4640_switch_mode(hub, hub->;mode);
289: +
290: + dev_info(dev, "%s: probed in %s mode\n", __func__,
291: + (hub->;mode == USB4640_MODE_HUB) ? "hub" : "standby");
292: +
293: + return 0;
294: +}
295: +
296: +static int usb4640_platform_probe(struct platform_device *pdev)
297: +{
298: + struct usb4640 *hub;
299: +
300: + hub = devm_kzalloc(&;pdev->dev, sizeof(struct usb4640), GFP_KERNEL);
301: + if (!hub)
302: + return -ENOMEM;
303: + hub->dev = &pdev->dev;
304: +
305: + return usb4640_probe(hub);
306: +}
307: +
308: +#ifdef CONFIG_OF
309: +static const struct of_device_id usb4640_of_match[] = {
310: + { .compatible = "smsc,usb4640", },
311: + {},
312: +};
313: +MODULE_DEVICE_TABLE(of, usb4640_of_match);
314: +#endif
315: +
316: +static struct platform_driver usb4640_platform_driver = {
317: + .driver = {
318: + .name = USB4640_NAME,
319: + .of_match_table = of_match_ptr(usb4640_of_match),
320: + },
321: + .probe = usb4640_platform_probe,
322: +};
323: +
324: +static int __init usb4640_init(void)
325: +{
326: + int err;
327: +
328: + err = platform_driver_register(&;usb4640_platform_driver);
329: + if (err != 0)
330: + pr_err("usb4640: Failed to register platform driver: %d\n",
331: + err);
332: +
333: + return 0;
334: +}
335: +module_init(usb4640_init);
336: +
337: +static void __exit usb4640_exit(void)
338: +{
339: + platform_driver_unregister(&;usb4640_platform_driver);
340: +}
341: +module_exit(usb4640_exit);
342: +
343: +MODULE_DESCRIPTION("USB4640 USB HUB driver");
344: +MODULE_LICENSE("GPL");
345: diff --git a/include/dt-bindings/usb4640/usb4640.h b/include/dt-bindings/usb4640/usb4640.h
346: new file mode 100644
347: index 0000000..ef3e1e1
348: --- /dev/null
349: +++ b/include/dt-bindings/usb4640/usb4640.h
350: @@ -0,0 +1,7 @@
351: +#ifndef _DT_BINDINGS_USB4640
352: +#define _DT_BINDINGS_USB4640
353: +
354: +#define USB4640_MODE_UNKNOWN 1
355: +#define USB4640_MODE_HUB 2
356: +#define USB4640_MODE_STANDBY 3
357: +#endif
358: diff --git a/include/linux/platform_data/usb4640.h b/include/linux/platform_data/usb4640.h
359: new file mode 100644
360: index 0000000..5a416ab
361: --- /dev/null
362: +++ b/include/linux/platform_data/usb4640.h
363: @@ -0,0 +1,17 @@
364: +#ifndef __USB4640_H__
365: +#define __USB4640_H__
366: +
367: +#define USB4640_NAME "usb4640"
368: +
369: +enum usb4640_mode {
370: + USB4640_MODE_UNKNOWN = 1,
371: + USB4640_MODE_HUB,
372: + USB4640_MODE_STANDBY,
373: +};
374: +
375: +struct usb4640_platform_data {
376: + enum usb4640_mode initial_mode;
377: + int gpio_reset;
378: +};
379: +
380: +#endif
測試
用ramdisk啟動內核,可以看到如下log
[ 2.690100] usb 2-2.2: new full-speed USB device number 4 using exynos-ehci
[ 2.795595] usb 2-2.2: not running at top speed; connect to a high speed hub
[ 2.869980] usb 2-2.2: New USB device found, idVendor=0a46, idProduct=9621
[ 2.870147] usb 2-2.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 2.883960] dm9601 2-2.2:1.0 eth0: register 'dm9601' at usb-12580000.ehci-2.2, Davicom DM96xx USB 10/100 Ethernet, 1c:6f:65:34:51:7e
執行如下命令:
ifconfig eth0 192.168.1.8
我的開發機使用橋接的方式連接到物理網卡上,ip是192.168.1.10,在板子上執行如下命令:
ping 192.168.1.10
看到如下log:
1: [root@tiny4412 ]# ping 192.168.1.10
2: PING 192.168.1.10 (192.168.1.10): 56 data bytes
3: 64 bytes from 192.168.1.10: seq=0 ttl=64 time=1.354 ms
4: 64 bytes from 192.168.1.10: seq=1 ttl=64 time=1.210 ms
5: 64 bytes from 192.168.1.10: seq=2 ttl=64 time=1.550 ms
6: 64 bytes from 192.168.1.10: seq=3 ttl=64 time=2.336 ms
7: 64 bytes from 192.168.1.10: seq=4 ttl=64 time=1.612 ms
8: ^C
9: --- 192.168.1.10 ping statistics ---
10: 5 packets transmitted, 5 packets received, 0% packet loss
11: round-trip min/avg/max = 1.210/1.612/2.336 ms
可以將配置IP的命令寫到/etc/profile中,就不用每次都配置ip了。
下麵我們看看系統的GPIO使用情況,再次之前需要掛載debugfs文件系統到/sys/kernel/debug下,修改配置文件/etc/fstab (需要在開發機上修改,然後重新製作ramdisk鏡像),添加一行:
debugfs /sys/kernel/debug debugfs defaults 0 0
然後重新製作生成ramdisk.img,啟動系統,執行如下命令:
1: [root@tiny4412 ]# mount
2: /dev/root on / type ext4 (rw,relatime,data=ordered)
3: devtmpfs on /dev type devtmpfs (rw,relatime,size=480388k,nr_inodes=120097,mode=755)
4: proc on /proc type proc (rw,relatime)
5: tmpfs on /tmp type tmpfs (rw,relatime)
6: sysfs on /sys type sysfs (rw,relatime)
7: debugfs on /sys/kernel/debug type debugfs (rw,relatime)
8: devpts on /dev/pts type devpts (rw,relatime,mode=600)
9:
10: [root@tiny4412 ]# cat /sys/kernel/debug/gpio
11: ......
12:
13: GPIOs 128-135, platform/11000000.pinctrl, gpm0:
14:
15: GPIOs 136-142, platform/11000000.pinctrl, gpm1:
16:
17: GPIOs 143-147, platform/11000000.pinctrl, gpm2:
18: gpio-147 ( |usb4640 reset ) out hi
19:
20: GPIOs 148-155, platform/11000000.pinctrl, gpm3:
21:
22: GPIOs 156-163, platform/11000000.pinctrl, gpm4:
23: gpio-156 ( |? ) out hi
24: gpio-157 ( |? ) out hi
25: gpio-158 ( |? ) out hi
26: gpio-159 ( |? ) out hi
27:
28: GPIOs 164-169, platform/11000000.pinctrl, gpy0:
29:
30: ......
可以看到控制usb4640的gpio資源狀態以及控制led燈的gpio狀態。
下麵我們執行命令,將開發機上的一個共用目錄用nfs掛載到tiny4412上:
1: [root@tiny4412 ]# ls /mnt/
2: [root@tiny4412 ]# mount -t nfs -o nolock 192.168.1.10:/nfs_rootfs/rootfs /mnt
3: