前言
目前大多数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
硬件
首先,参考原理图如下:
Device Tree
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";
};
Drivers
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 <linux/usb/of.h> #include <linux/usb/otg.h> #include <linux/wakelock.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> #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 <linux/dma-mapping.h> #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/debugfs.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> @@ -31,6 +32,7 @@ #include <linux/freezer.h> #include <linux/iopoll.h> #include <linux/reset.h> +#include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/usb/ch9.h> @@ -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
device/rockchip/rk3399/init.rk30board.usb.rc
setprop sys.usb.controller "fe900000.dwc3"
应用或者framework中执行
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
邮箱 : wolfnx@outlook.com
邮箱2 : lostnx@gmail.com