将TypeC的adb切换到USB3.0口

前言

目前大多数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

硬件

首先,参考原理图如下:

将TypeC的adb切换到USB3.0口_第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

  1. 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)
    
  2. 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);
    
  3. 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
    
  4. 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

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
邮箱 : [email protected]
邮箱2 : [email protected]

你可能感兴趣的:(rk3399,kernel,C)