1.原理图与说明
1.1 原理图
1.2 说明
tiny4412
使用的网卡芯片是DM9621
,是usb接口的,而且并没有直接连接到exynos4412
上,中间通过一个hub芯片usb4640
,然后usb4640
通过HSIC
接口(XhsicSTROBE0
和XhsicDATA0
)连接到tiny4412
上。
2.软件支持
DM9621
的驱动在Linux4.4.1
中在driver/net/usb/dm9601.c
中实现了,我们需要在内核配置中将DM9601
的驱动编译进内核,linux
内核支持如下:
Device Drivers --->
Network device support --->
USB Network Adapters --->
Davicom DM96xx based USB 10/100ethernet devices
最关键的是要在设备树中将用到的片内外设使能:如hsi
、ehci
、ohci
、otg
等等。执行完这些操作后,板子上的三个usb
口(USBH1/USBH2/USBH3
)都可以识别了,即可以插入U盘
等设备了。
添加头文件,如include/dt-bindings/usb4640/usb4640.h
:
#ifndef _DT_BINDINGS_USB4640
#define _DT_BINDINGS_USB4640
#define USB4640_MODE_UNKNOWN 1
#define USB4640_MODE_HUB 2
#define USB4640_MODE_STANDBY 3
#define USB4640_I2C_NAME "usb4640"
#define USB4640_NAME "usb4640"
#define USB4640_OFF_PORT1 (1 << 1)
#define USB4640_OFF_PORT2 (1 << 2)
#define USB6640_OFF_PORT3 (1 << 3)
#endif
配置otg
支持(可选项):
补充文件drivers/usb/misc/usb4640.c
:
/*
* Driver for SMSC USB4640 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 <dt-bindings/usb4640/usb4640.h>
struct usb4640 {
int mode;
struct device *dev;
int gpio_reset;
};
struct usb4640_platform_data {
int initial_mode;
int port_off_mask;
int gpio_intn;
int gpio_connect;
int gpio_reset;
};
static int usb4640_reset(struct usb4640 *hub, int state)
{
if (gpio_is_valid(hub->gpio_reset))
gpio_set_value_cansleep(hub->gpio_reset, state);
dev_info(hub->dev, "%s\n", __func__);
/* Wait 1ms for hub logic to stabilize */
//if (state)
// usleep_range(1, 10);
usleep_range(1000, 4000);
return 0;
}
static int usb4640_connect(struct usb4640 *hub)
{
struct device *dev = hub->dev;
usb4640_reset(hub, 1);
hub->mode = USB4640_MODE_HUB;
dev_info(dev, "switched to HUB mode\n");
return 0;
}
static int usb4640_switch_mode(struct usb4640 *hub, int mode)
{
struct device *dev = hub->dev;
int err = 0;
switch (mode) {
case USB4640_MODE_HUB:
err = usb4640_connect(hub);
break;
case USB4640_MODE_STANDBY:
usb4640_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 usb4640_probe(struct usb4640 *hub)
{
struct device *dev = hub->dev;
struct usb4640_platform_data *pdata = dev_get_platdata(dev);
struct device_node *np = dev->of_node;
int err;
int mode = USB4640_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, "usb4640 reset");
if (err) {
dev_err(dev,
"unable to request GPIO %d as reset pin (%d)\n",
hub->gpio_reset, err);
return err;
}
}
usb4640_switch_mode(hub, hub->mode);
dev_info(dev, "%s: probed in %s mode\n", __func__,
(hub->mode == USB4640_MODE_HUB) ? "hub" : "standby");
return 0;
}
static int usb4640_platform_probe(struct platform_device *pdev)
{
struct usb4640 *hub;
printk("==========================usb4640 probe==============\n");
hub = devm_kzalloc(&pdev->dev, sizeof(struct usb4640), GFP_KERNEL);
if (!hub)
return -ENOMEM;
hub->dev = &pdev->dev;
return usb4640_probe(hub);
}
#ifdef CONFIG_OF
static const struct of_device_id usb4640_of_match[] = {
{ .compatible = "smsc,usb4640", },
{},
};
MODULE_DEVICE_TABLE(of, usb4640_of_match);
#endif
static struct platform_driver usb4640_platform_driver = {
.driver = {
.name = USB4640_NAME,
.of_match_table = of_match_ptr(usb4640_of_match),
},
.probe = usb4640_platform_probe,
};
static int __init usb4640_init(void)
{
int err;
err = platform_driver_register(&usb4640_platform_driver);
if (err != 0)
pr_err("usb4640: Failed to register platform driver: %d\n",
err);
return 0;
}
module_init(usb4640_init);
static void __exit usb4640_exit(void)
{
platform_driver_unregister(&usb4640_platform_driver);
}
module_exit(usb4640_exit);
MODULE_DESCRIPTION("USB4640 USB HUB driver");
MODULE_LICENSE("GPL");
另外,还需要在Makefile(drivers/usb/misc/Makefile)
中添加:
obj-$(CONFIG_USB_HSIC_USB4640) += usb4640.o
而且,还需要在Kconfig(drivers/usb/misc/Kconfig)
中添加:
config USB_HSIC_USB4640
tristate "USB4640 HSIC to USB20 Driver"
help
This option enables support for SMSC USB4640 HSIC to USB 2.0 Driver.
这个时候需要在menuconfig
中选中USB4640
配置项。
另外,还需要将arch/arm/boot/dts/exynos4412-tiny4412.dts
增加内容:
&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";
};
而且,需要增加:
usb-hub {
compatible = "smsc,usb4640";
reset-gpios = <&gpm2 4 GPIO_ACTIVE_LOW>;
initial-mode = <USB4640_MODE_HUB>;
status = "okay";
};
note
本篇网络支持仅限可以使用,但是有bug,例如,重复dnw
加载uImage
,会导致网络加载失效。