目前大多数Android的嵌入式系统都会使用TypeC做为adb的调试口,这样很方便。但是在有的时候,迫不得已必须将adb功能切换到USB3.0口,但由于当前的内核USB框架只能支持一个USB口作为Peripheral
功能,所以RK3399 SDK默认配置Type-C0作为OTG mode 支持USB Peripheral功能,而Type-C1只支持Hostmode。
注意: 下面介绍的方法只在rk3399的Industry的7.1版本上实现成功并测试过,其他版本仅做参考,但也大同小异。
Kernel: v4.4.126
Device: rk3399
Platform Version: Android 7.1
首先,参考原理图如下:
RK提供的dts中默认的rk3399.dtsi,默认
usbdrd3_0: usb@fe800000 {
...
usbdrd_dwc3_0: dwc3@fe800000 {
compatible = "snps,dwc3";
dr_mode = "otg";
};
...
};
usbdrd3_1: usb@fe900000 {
...
usbdrd_dwc3_1: dwc3@fe900000 {
compatible = "snps,dwc3";
dr_mode = "host";
};
...
};
我们需要在自己的dts中修改usbdrd_dwc3_1的dr_mode模式为otg,将usbdrd_dwc3_0的模式修改为host
&usbdrd_dwc3_0 {
status = "okay";
dr_mode = "host";
};
&usbdrd_dwc3_1 {
status = "okay";
dr_mode = "otg";
};
kernel/include/linux/phy/phy.h
diff --git a/kernel/include/linux/phy/phy.h b/kernel/include/linux/phy/phy.h
index a3965c3..c0daa66 100644
--- a/kernel/include/linux/phy/phy.h
+++ b/kernel/include/linux/phy/phy.h
@@ -36,6 +36,7 @@ enum phy_mode {
* @power_on: powering on the phy
* @power_off: powering off the phy
* @set_mode: set the mode of the phy
+ * @set_vbusdet: usb disconnect of the phy
* @reset: resetting the phy
* @cp_test: prepare for the phy compliance test
* @owner: the module owner containing the ops
@@ -46,6 +47,7 @@ struct phy_ops {
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode);
+ int (*set_vbusdet)(struct phy *phy, bool level);
int (*reset)(struct phy *phy);
int (*cp_test)(struct phy *phy);
struct module *owner;
@@ -133,6 +135,7 @@ int phy_exit(struct phy *phy);
int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode);
+int phy_set_vbusdet(struct phy *phy, bool level);
int phy_reset(struct phy *phy);
int phy_cp_test(struct phy *phy);
static inline int phy_get_bus_width(struct phy *phy)
@@ -247,6 +250,13 @@ static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
return -ENOSYS;
}
+static inline int phy_set_vbusdet(struct phy *phy, bool level)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
static inline int phy_reset(struct phy *phy)
{
if (!phy)
kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 66fb407..1f11ae1 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -37,6 +37,8 @@
#include
#include
#include
+#include
+#include
#define BIT_WRITEABLE_SHIFT 16
#define SCHEDULE_DELAY (60 * HZ)
@@ -250,6 +252,7 @@ struct rockchip_usb2phy_port {
struct delayed_work chg_work;
struct delayed_work otg_sm_work;
struct delayed_work sm_work;
+ struct delayed_work peripheral_work;
struct regulator *vbus;
const struct rockchip_usb2phy_port_cfg *port_cfg;
struct notifier_block event_nb;
@@ -816,12 +819,37 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode)
return ret;
}
+static int rockchip_usb2phy_set_vbusdet(struct phy *phy, bool level)
+{
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+ int ret = 0;
+
+ if (rport->port_id != USB2PHY_PORT_OTG)
+ return ret;
+
+ if (rphy->phy_cfg->reg == 0xe460) {
+ if (level)
+ {
+ ret = regmap_write(rphy->grf, 0x4518, GENMASK(20, 20) | 0x10);
+ }
+ else
+ {
+ ret = regmap_write(rphy->grf, 0x4518, GENMASK(20, 20) | 0x00);
+ }
+ }
+
+ return ret;
+}
+
+
static const struct phy_ops rockchip_usb2phy_ops = {
.init = rockchip_usb2phy_init,
.exit = rockchip_usb2phy_exit,
.power_on = rockchip_usb2phy_power_on,
.power_off = rockchip_usb2phy_power_off,
.set_mode = rockchip_usb2phy_set_mode,
+ .set_vbusdet = rockchip_usb2phy_set_vbusdet,
.owner = THIS_MODULE,
};
@@ -1530,13 +1558,24 @@ static int rockchip_otg_event(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static void rockchip_usb2phy_peripheral_work(struct work_struct *work)
+{
+ struct rockchip_usb2phy_port *rport =
+ container_of(work, struct rockchip_usb2phy_port, peripheral_work.work);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+ extcon_set_state(rphy->edev, EXTCON_USB, true);
+ extcon_sync(rphy->edev, EXTCON_USB);
+ schedule_delayed_work(&rport->peripheral_work, 3 * HZ);
+
+}
+
static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
struct rockchip_usb2phy_port *rport,
struct device_node *child_np)
{
int ret;
int iddig;
-
+ int gpio_vbus_5v;
rport->port_id = USB2PHY_PORT_OTG;
rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
rport->state = OTG_STATE_UNDEFINED;
@@ -1584,6 +1623,32 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
rport->vbus = NULL;
}
+
+
+ rport->vbus_always_on =
+ of_property_read_bool(child_np, "rockchip,vbus-always-on");
+ if (rport->vbus_always_on)
+ {
+ ret = of_get_named_gpio_flags(child_np, "vbus-5v-gpios", 0, NULL);
+ if (ret < 0) {
+ printk("%s() Can not read property vbus-5v-gpio\n", __FUNCTION__);
+ } else {
+ gpio_vbus_5v = ret;
+ ret = devm_gpio_request(rphy->dev, gpio_vbus_5v, "vbus-gpio");
+ if(ret < 0)
+ printk("%s() devm_gpio_request vbus-gpio request ERROR\n", __FUNCTION__);
+ ret = gpio_direction_output(gpio_vbus_5v,1);
+ if(ret < 0)
+ printk("%s() gpio_direction_output vbus-gpio set ERROR\n", __FUNCTION__);
+ }
+
+ INIT_DELAYED_WORK(&rport->peripheral_work, rockchip_usb2phy_peripheral_work);
+ schedule_delayed_work(&rport->peripheral_work, 3 * HZ);
+
+ goto out;
+ }
+
+
rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
if (rport->mode == USB_DR_MODE_HOST ||
rport->mode == USB_DR_MODE_UNKNOWN) {
@@ -1600,9 +1665,6 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
goto out;
}
- if (rport->vbus_always_on)
- goto out;
-
wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
INIT_DELAYED_WORK(&rport->bypass_uart_work,
rockchip_usb_bypass_uart_work);
kernel/drivers/phy/phy-core.c
diff --git a/kernel/drivers/phy/phy-core.c b/kernel/drivers/phy/phy-core.c
index 0587933..8dd548b 100644
--- a/kernel/drivers/phy/phy-core.c
+++ b/kernel/drivers/phy/phy-core.c
@@ -387,6 +387,21 @@ int phy_cp_test(struct phy *phy)
}
EXPORT_SYMBOL_GPL(phy_cp_test);
+int phy_set_vbusdet(struct phy *phy, bool level)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_vbusdet)
+ return 0;
+
+ mutex_lock(&phy->mutex);
+ ret = phy->ops->set_vbusdet(phy, level);
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_vbusdet);
+
/**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy
kernel/drivers/usb/dwc3/dwc3-rockchip.c
diff --git a/kernel/drivers/usb/dwc3/dwc3-rockchip.c b/kernel/drivers/usb/dwc3/dwc3-rockchip.c
index 539b89a..7cf9675 100644
--- a/kernel/drivers/usb/dwc3/dwc3-rockchip.c
+++ b/kernel/drivers/usb/dwc3/dwc3-rockchip.c
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -31,6 +32,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -47,6 +49,7 @@
struct dwc3_rockchip {
int num_clocks;
bool connected;
+ bool disconnect;
bool skip_suspend;
bool suspended;
bool force_mode;
@@ -56,6 +59,7 @@ struct dwc3_rockchip {
struct device *dev;
struct clk **clks;
struct dwc3 *dwc;
+ struct dentry *root;
struct reset_control *otg_rst;
struct extcon_dev *edev;
struct usb_hcd *hcd;
@@ -96,6 +100,7 @@ static ssize_t dwc3_mode_store(struct device *device,
struct dwc3_rockchip *rockchip = dev_get_drvdata(device);
struct dwc3 *dwc = rockchip->dwc;
enum usb_dr_mode new_dr_mode;
+ //char buf[32];
if (!rockchip->original_dr_mode)
rockchip->original_dr_mode = dwc->dr_mode;
@@ -107,15 +112,21 @@ static ssize_t dwc3_mode_store(struct device *device,
if (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) {
new_dr_mode = USB_DR_MODE_OTG;
+ phy_set_vbusdet(dwc->usb2_generic_phy, 0);
} else if (!strncmp(buf, "1", 1) || !strncmp(buf, "host", 4)) {
new_dr_mode = USB_DR_MODE_HOST;
+ phy_set_vbusdet(dwc->usb2_generic_phy, 0);
} else if (!strncmp(buf, "2", 1) || !strncmp(buf, "peripheral", 10)) {
new_dr_mode = USB_DR_MODE_PERIPHERAL;
+ phy_set_vbusdet(dwc->usb2_generic_phy, 1);
} else {
dev_info(rockchip->dev, "illegal dr_mode\n");
+ phy_set_vbusdet(dwc->usb2_generic_phy, 0);
return count;
}
+ msleep(200);
+
if (dwc->dr_mode == new_dr_mode) {
dev_info(rockchip->dev, "Same with current dr_mode\n");
return count;
@@ -378,6 +389,17 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
mutex_lock(&rockchip->lock);
+ if (extcon_get_cable_state_(edev, EXTCON_USB)) {
+ if ((dwc->link_state == DWC3_LINK_STATE_U3) && !rockchip->disconnect) {
+ phy_set_vbusdet(dwc->usb2_generic_phy, 0);
+ msleep(3000);
+ phy_set_vbusdet(dwc->usb2_generic_phy, 1);
+ rockchip->disconnect = true;
+ } else if(dwc->link_state == DWC3_LINK_STATE_U0) {
+ rockchip->disconnect = false;
+ }
+ }
+
if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_PERIPHERAL :
extcon_get_cable_state_(edev, EXTCON_USB)) {
if (rockchip->connected)
@@ -624,6 +646,7 @@ out:
static int dwc3_rockchip_get_extcon_dev(struct dwc3_rockchip *rockchip)
{
+ //int ret;
struct device *dev = rockchip->dev;
struct extcon_dev *edev;
@@ -743,6 +766,7 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node, *child;
struct platform_device *child_pdev;
+ //struct usb_hcd *hcd = NULL;
unsigned int count;
int ret;
device/rockchip/rk3399/init.rk30board.usb.rc
setprop sys.usb.controller "fe900000.dwc3"
echo peripheral > /sys/devices/platform/usb@fe900000/dwc3_mode
在Anroid启动后,在adb中执行上面的指令,即可将adb切换到usb3.0口,验证的时候需要使用双公头的usb3.0线。
注意: 虽然adb被切换到了usb3.0,但是进入loader刷机模式,还是需要用TypeC来进行刷机。
转载请注明出处:http://www.wolfnx.com/2020/03/24/Switch-Adb-To-Usb3.0
作者 : wolfnx
邮箱 : [email protected]
邮箱2 : [email protected]