1.硬件原理图

 

看下面的原理图

VCC_OTG_EN 引脚,这个脚主要是用来控制给外部OTG设备提供电源控制的。如果设备作为DEVICE设备,这时候VBUS的电是由外部提供的,比如通过USB线和电脑连接,这个时候,VBUS的电压是由电脑提供的。

如果设备作为HOST设备,比如连接U盘,我们需要给U盘提供VBUS电压,就需要控制VCC_OTG_EN引脚给VBUS提供电压。

看我解决Linux下的OTG切换问题_物联网

USB_OTG_ID 脚

这个脚用来判断有OTG设备插入

看我解决Linux下的OTG切换问题_敏捷开发_02

VBUS电压检测,这个电路看不懂有什么作用,硬件设计预期是希望如果有外部的USB设备给设备供电,就切换模式,但是实际上,不管设备作为HOST还是DEVICE,这个电压会一直存在。所以总觉得这个设计有点问题。

看我解决Linux下的OTG切换问题_软件开发_03

ID脚电压,没有插入OTG设备的时候,电压是1.79v,插入OTG设备的时候,电压是 0V。

看我解决Linux下的OTG切换问题_单片机_042.软件控制

dts 部分

&usb0 {
 pinctrl-names = "iddig_irq_init", "drvvbus_init", "drvvbus_low", "drvvbus_high";
 pinctrl-0 = <&usb0_iddig>;
 pinctrl-1 = <&usb0_drvvbus>;
 pinctrl-2 = <&usb0_drvvbus_low>;
 pinctrl-3 = <&usb0_drvvbus_high>;
 usb-power-supply = <&mt6392_vusb_reg>;

 drvvbus_gpio = <&pio 0 2>;
 iddig_gpio = <&pio 41 2>;

 status = "okay";
};

774 /* USB GPIO start */
775         usb0_drvvbus: drvvbus_init {
776                 pins_cmd_dat {
777                         pins = <MT8167_PIN_0_EINT0__FUNC_GPIO0>;
778                         output-low;
779                 };
780         };
781
782         usb0_drvvbus_high: drvvbus_high {
783                 pins_cmd_dat {
784                         pins = <MT8167_PIN_0_EINT0__FUNC_GPIO0>;
785                         slew-rate = <1>;
786                         output-high;
787                 };
788         };
789
790         usb0_drvvbus_low: drvvbus_low {
791                 pins_cmd_dat {
792                         pins = <MT8167_PIN_0_EINT0__FUNC_GPIO0>;
793                         slew-rate = <1>;
794                         output-low;
795                 };
796         };
797         usb0_iddig: iddig_irq_init {
798                 pins_cmd_dat {
799                         pins = <MT8167_PIN_41_KPROW1__FUNC_IDDIG>;
800                         slew-rate = <0>;
801                         bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
802                 };
803         };
804 /* USB GPIO end */

引脚初始化部分

void mt_usb_init_drvvbus(void)
{
#if (!(defined(SWITCH_CHARGER) || defined(FPGA_PLATFORM))) && !(defined(OTG_BOOST_BY_SWITCH_CHARGER))
#ifdef CONFIG_OF
#if defined(CONFIG_MTK_LEGACY)
 mt_set_gpio_mode(drvvbus_pin, drvvbus_pin_mode);/*should set GPIO2 as gpio mode.*/
 mt_set_gpio_dir(drvvbus_pin, GPIO_DIR_OUT);
 mt_get_gpio_pull_enable(drvvbus_pin);
 mt_set_gpio_pull_select(drvvbus_pin, GPIO_PULL_UP);
#else
 int ret = 0;

 DBG(0, "****%s:%d Init Drive VBUS!!!!!\n", __func__, __LINE__);
 if (drvvbus_pin < 0)
  return;

 if (IS_ERR(pinctrl))
  return;

 pinctrl_drvvbus = pinctrl_lookup_state(pinctrl, "drvvbus_init");
 if (IS_ERR(pinctrl_drvvbus)) {
  ret = PTR_ERR(pinctrl_drvvbus);
  dev_err(mtk_musb->controller, "Cannot find usb pinctrl drvvbus\n");
 }

 pinctrl_drvvbus_low = pinctrl_lookup_state(pinctrl, "drvvbus_low");
 if (IS_ERR(pinctrl_drvvbus_low)) {
  ret = PTR_ERR(pinctrl_drvvbus_low);
  dev_err(mtk_musb->controller, "Cannot find usb pinctrl drvvbus_low\n");
 }

 pinctrl_drvvbus_high = pinctrl_lookup_state(pinctrl, "drvvbus_high");
 if (IS_ERR(pinctrl_drvvbus_high)) {
  ret = PTR_ERR(pinctrl_drvvbus_high);
  dev_err(mtk_musb->controller, "Cannot find usb pinctrl drvvbus_high\n");
 }

 if (!IS_ERR(pinctrl_drvvbus)) {
  pinctrl_select_state(pinctrl, pinctrl_drvvbus);
  DBG(0, "****%s:%d end Init Drive VBUS KS!!!!!\n", __func__, __LINE__);
 }
#endif
#else
 mt_set_gpio_mode(GPIO_OTG_DRVVBUS_PIN, GPIO_OTG_DRVVBUS_PIN_M_GPIO);/*should set GPIO2 as gpio mode.*/
 mt_set_gpio_dir(GPIO_OTG_DRVVBUS_PIN, GPIO_DIR_OUT);
 mt_get_gpio_pull_enable(GPIO_OTG_DRVVBUS_PIN);
 mt_set_gpio_pull_select(GPIO_OTG_DRVVBUS_PIN, GPIO_PULL_UP);
#endif
#endif
}

ID 引脚初始化部分

static void otg_int_init(void)
{
#if CONFIG_OF
#if defined(CONFIG_MTK_LEGACY)
 mt_set_gpio_mode(iddig_pin, GPIO_MODE_00);
 mt_set_gpio_dir(iddig_pin, GPIO_DIR_IN);
 mt_set_gpio_pull_enable(iddig_pin, GPIO_PULL_ENABLE);
 mt_set_gpio_pull_select(iddig_pin, GPIO_PULL_UP);

 mt_eint_set_sens(IDDIG_EINT_PIN, MT_LEVEL_SENSITIVE);
 mt_eint_set_hw_debounce(IDDIG_EINT_PIN, 64);
 mt_eint_registration(IDDIG_EINT_PIN, EINTF_TRIGGER_LOW, mt_usb_ext_iddig_int, FALSE);
#else
 int ret = 0;

 DBG(0, "****%s:%d before Init IDDIG KS!!!!!\n", __func__, __LINE__);

 if (iddig_pin < 0) {
  DBG(0, "iddig_pin is invalid:%d\n", iddig_pin);
  return;
 }
 pinctrl_iddig = pinctrl_lookup_state(pinctrl, "iddig_irq_init");
 if (IS_ERR(pinctrl_iddig)) {
  ret = PTR_ERR(pinctrl_iddig);
  DBG(0, "Cannot find usb pinctrl iddig_irq_init\n");
 }

 if (IS_ERR(pinctrl) || IS_ERR(pinctrl_iddig)) {
  DBG(0, "pinctrl or pinctrl_iddig error\n");
  return;
 }

 pinctrl_select_state(pinctrl, pinctrl_iddig);
 DBG(0, "usb iddig_pin %d\n", iddig_pin);

#if 0
 gpio_set_debounce(iddig_pin, 64000);
 DBG(0, "will call __gpio_to_irq\n");
#endif
 usb_iddig_number = __gpio_to_irq(iddig_pin);
 DBG(0, "usb usb_iddig_number %d\n", usb_iddig_number);

 ret = request_irq(usb_iddig_number, mt_usb_ext_iddig_int, IRQF_TRIGGER_LOW, "USB_IDDIG", NULL);
 if (ret > 0)
  DBG(0, "USB IDDIG IRQ LINE not available!!\n");
 else
  DBG(0, "USB IDDIG IRQ LINE available!!\n");

 irq_set_irq_wake(usb_iddig_number, 1);
#endif
#else
 u32 phy_id_pull = 0;

 phy_id_pull = __raw_readl(U2PHYDTM1);
 phy_id_pull |= ID_PULL_UP;
 __raw_writel(phy_id_pull, U2PHYDTM1);

 musb_writel(mtk_musb->mregs, USB_L1INTM, IDDIG_INT_STATUS | musb_readl(mtk_musb->mregs, USB_L1INTM));
#endif
}

USB 触发中断代码

/*
 * handle all the irqs defined by the HDRC core. for now we expect:  other
 * irq sources (phy, dma, etc) will be handled first, musb->int_* values
 * will be assigned, and the irq will already have been acked.
 *
 * called in irq context with spinlock held, irqs blocked
 */
irqreturn_t musb_interrupt(struct musb *musb)
{
 irqreturn_t retval = IRQ_NONE;
 u8 devctl, power = 0;
#ifndef USE_SSUSB_QMU
 u32 reg = 0, ep_num = 0;
#endif

#ifdef POWER_SAVING_MODE
 if (!(os_readl(U3D_SSUSB_U2_CTRL_0P) & SSUSB_U2_PORT_PDN)) {
  devctl = (u8) os_readl(U3D_DEVICE_CONTROL);
  power = (u8) os_readl(U3D_POWER_MANAGEMENT);
 } else {
  devctl = 0;
  power = 0;
  musb->int_usb = 0;
 }
#else
 devctl = (u8) os_readl(U3D_DEVICE_CONTROL);
 power = (u8) os_readl(U3D_POWER_MANAGEMENT);
#endif

 /* dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", */
 os_printk(K_DEBUG, "IRQ %s usb%04x tx%04x rx%04x\n",
    (devctl & USB_DEVCTL_HOSTMODE) ? "host" : "peripheral",
    musb->int_usb, musb->int_tx, musb->int_rx);

 if (unlikely(!musb->softconnect)) {
  os_printk(K_WARNIN, "!softconnect, IRQ %s usb%04x tx%04x rx%04x\n",
    (devctl & USB_DEVCTL_HOSTMODE) ? "host" : "peripheral",
    musb->int_usb, musb->int_tx, musb->int_rx);
  return IRQ_HANDLED;
 }

 /* the core can interrupt us for multiple reasons; docs have
  * a generic interrupt flowchart to follow
  */
 if (musb->int_usb)
  retval |= musb_stage0_irq(musb, musb->int_usb, devctl, power);

 /* "stage 1" is handling endpoint irqs */

 /* handle endpoint 0 first */
 if (musb->int_tx & 1)
  retval |= musb_g_ep0_irq(musb);

#ifndef USE_SSUSB_QMU
 /* RX on endpoints 1-15 */
 reg = musb->int_rx >> 1;
 ep_num = 1;
 while (reg) {
  if (reg & 1) {
   /* musb_ep_select(musb->mregs, ep_num); */
   /* REVISIT just retval = ep->rx_irq(...) */
   retval = IRQ_HANDLED;
   musb_g_rx(musb, ep_num);
  }

  reg >>= 1;
  ep_num++;
 }

 /* TX on endpoints 1-15 */
 reg = musb->int_tx >> 1;
 ep_num = 1;
 while (reg) {
  if (reg & 1) {
   /* musb_ep_select(musb->mregs, ep_num); */
   /* REVISIT just retval |= ep->tx_irq(...) */
   retval = IRQ_HANDLED;
   musb_g_tx(musb, ep_num);
  }
  reg >>= 1;
  ep_num++;
 }
#endif

 return retval;
}
EXPORT_SYMBOL_GPL(musb_interrupt);

中断函数注册,主要是在插入把拔出usb的时候产生中断

 /* attach to the IRQ */
 if (request_irq(musb->nIrq, musb->isr, IRQF_TRIGGER_LOW, dev_name(dev), musb)) {
  DBG(0, "request_irq %d failed!\n", musb->nIrq);
  status = -ENODEV;
  goto fail3;
 }
3.问题以及分析

我们项目遇到一个问题

插入OTG设备后,再拔掉,仍是OTG模式,没检测到OTG设备拔出

插入OTG设备和拔开OTG设备的软件日志

====插入OTG====
[1623:kworker/0:2][MUSB]musb_id_pin_work 376: work start, is_host=0
[1623:kworker/0:2][MUSB]musb_is_host 231: will mask PMIC charger detection
[1623:kworker/0:2][MUSB]musb_is_host 254: iddig_state = 0
[1623:kworker/0:2][MUSB]musb_is_host 283: usb_is_host = 1
[1623:kworker/0:2][MUSB]musb_id_pin_work 384: musb is as host, already lock
[1623:kworker/0:2][MUSB]musb_id_pin_work 424: force PHY to host mode, 0x6d=3e, 0x6c=2c
[1623:kworker/0:2][MUSB]musb_start 1290: start, is_host=1 is_active=0
[1623:kworker/0:2][MUSB]musb_id_pin_work 464: work end, is_host=1
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=1, power=1, is_host=1
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=2, power=1, is_host=1
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=3, power=1, is_host=1
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=2, power=1, is_host=1
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=3, power=1, is_host=1
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=1, power=1, is_host=1
[0:swapper/0][MUSB]musb_stage0_irq 1102: CONNECT (a_host) devctl 5d
[1623:kworker/0:2]scsi host1: usb-storage 1-1:1.0
[1829:cat][MUSB]musb_host_rx 2048: RX end 5 STALL
[2423:usb-storage][MUSB]musb_host_rx 2048: RX end 5 STALL
[0:swapper/0][MUSB]musb_host_rx 2048: RX end 5 STALL
[0:swapper/0][MUSB]musb_host_rx 2048: RX end 5 STALL
[0:swapper/0][MUSB]musb_host_rx 2048: RX end 5 STALL
[0:swapper/0][MUSB]musb_host_rx 2048: RX end 5 STALL
[658:Binder:536_5][MUSB]musb_host_rx 2048: RX end 5 STALL
[0:swapper/0][MUSB]musb_host_rx 2048: RX end 5 STALL

====拔下OTG====

[0:swapper/0][MUSB]musb_stage0_irq 1107: DISCONNECT (a_host) as Host, devctl 19
[0:swapper/0][MUSB]musb_root_disconnect 197: host disconnect (a_host)
[1623:kworker/0:2][MUSB]musb_id_pin_work 376: work start, is_host=1
[1623:kworker/0:2][MUSB]musb_is_host 231: will mask PMIC charger detection
[1623:kworker/0:2][MUSB]musb_is_host 254: iddig_state = 1
[1623:kworker/0:2][MUSB]musb_is_host 274: will unmask PMIC charger detection
[1623:kworker/0:2][MUSB]musb_is_host 283: usb_is_host = 0
[1623:kworker/0:2][MUSB]switch_int_to_host 334: enable iddig irq LOW @lin 334
[1623:kworker/0:2][MUSB]switch_int_to_host 340: switch_int_to_host is done
[1623:kworker/0:2][MUSB]musb_id_pin_work 464: work end, is_host=0
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=1, power=0, is_host=0
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=2, power=0, is_host=0
[1623:kworker/0:2]musb-hdrc musb-hdrc: prop=3, power=0, is_host=0
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=2, power=0, is_host=0
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=3, power=0, is_host=0
[646:usb@1.1-service]musb-hdrc musb-hdrc: prop=1, power=0, is_host=0

获取otg状态的函数

static bool musb_is_host(void)
{
 u8 devctl = 0;
 int iddig_state = 1;
 bool usb_is_host = 0;

 DBG(0, "will mask PMIC charger detection\n");
#ifndef FPGA_PLATFORM
#ifdef CONFIG_MTK_DUAL_INPUT_CHARGER_SUPPORT
#ifdef CONFIG_MTK_LOAD_SWITCH_FPF3040
 pmic_chrdet_int_en(0);
#endif
#endif
#endif

 musb_platform_enable(mtk_musb);

 if (iddig_pin < 0) {
  DBG(0, "iddig_pin is invalid:%d, return non-host!\n", iddig_pin);
  usb_is_host = 0;
  return usb_is_host;
 }

#ifdef ID_PIN_USE_EX_EINT
#if defined(CONFIG_MTK_LEGACY)
 iddig_state = mt_get_gpio_in(iddig_pin);
#else
 iddig_state = __gpio_get_value(iddig_pin);
#endif
 DBG(0, "iddig_state = %d\n", iddig_state);
#else
 iddig_state = 0;
 devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
 DBG(0, "devctl = %x before end session\n", devctl);
 devctl &= ~MUSB_DEVCTL_SESSION; /* this will cause A-device change back to B-device after A-cable plug out*/
 musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, devctl);
 msleep(delay_time);

 devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
 DBG(0, "devctl = %x before set session\n", devctl);

 devctl |= MUSB_DEVCTL_SESSION;
 musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, devctl);
 msleep(delay_time1);
 devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
 DBG(0, "devclt = %x\n", devctl);
#endif

 if (devctl & MUSB_DEVCTL_BDEVICE || iddig_state) {
  DBG(0, "will unmask PMIC charger detection\n");
#ifndef FPGA_PLATFORM
  pmic_chrdet_int_en(1);
#endif
  usb_is_host = false;
 } else {
  usb_is_host = true;
 }

 DBG(0, "usb_is_host = %d\n", usb_is_host);
 return usb_is_host;
}

插拔otg 的时候,会进入这个函数里面进行判断

从日志和下面的代码可以得出一个结论,我们在插入OTG和拔下OTG的时候,软件已经正常运行了,但是拔出后,可能因为某个原因,USB处在一个错误的状态,这个状态导致再插入正常的USB就没有反应了。

static void musb_id_pin_work(struct work_struct *data)
{
 u8 devctl = 0;
 unsigned long flags;

 /* need prepare clock because  musb_generic_disable may call prepare clock in atomic context */
 mt_usb_clock_prepare(mtk_musb);

 spin_lock_irqsave(&mtk_musb->lock, flags);
 musb_generic_disable(mtk_musb);
 spin_unlock_irqrestore(&mtk_musb->lock, flags);

 down(&mtk_musb->musb_lock);
 DBG(0, "work start, is_host=%d\n", mtk_musb->is_host);
 if (mtk_musb->in_ipo_off) {
  DBG(0, "do nothing due to in_ipo_off\n");
  goto out;
 }

 wake_lock(&mtk_musb->usb_lock);
 mtk_musb->is_host = musb_is_host();
 DBG(0, "musb is as %s, already lock\n", mtk_musb->is_host?"host":"device");
 switch_set_state((struct switch_dev *)&otg_state, mtk_musb->is_host);

 if (mtk_musb->is_host) {
  /*setup fifo for host mode*/
  ep_config_from_table_for_host(mtk_musb);
  /* wake_lock(&mtk_musb->usb_lock); */
  musb_platform_set_vbus(mtk_musb, 1);

  /* for no VBUS sensing IP*/
#if 1
  /* wait VBUS ready */
  msleep(100);
  /* clear session*/
  devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
  musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, (devctl&(~MUSB_DEVCTL_SESSION)));
  /* USB MAC OFF*/
  /* VBUSVALID=0, AVALID=0, BVALID=0, SESSEND=1, IDDIG=X */
  USBPHY_SET8(0x6c, 0x10);
  USBPHY_CLR8(0x6c, 0x2e);
  USBPHY_SET8(0x6d, 0x3e);
  DBG(0, "force PHY to idle, 0x6d=%x, 0x6c=%x\n", USBPHY_READ8(0x6d), USBPHY_READ8(0x6c));
  /* wait */
  mdelay(5);

  /* remove babble: NOISE_STILL_SOF:1, BABBLE_CLR_EN:0 */
  devctl = musb_readb(mtk_musb->mregs, MUSB_ULPI_REG_DATA);
  devctl = devctl | 0x80;
  devctl = devctl & 0xbf;
  musb_writeb(mtk_musb->mregs, MUSB_ULPI_REG_DATA, devctl);
  mdelay(5);

  /* restart session */
  devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
  musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, (devctl | MUSB_DEVCTL_SESSION));
  /* USB MAC ONand Host Mode*/
  /* VBUSVALID=1, AVALID=1, BVALID=1, SESSEND=0, IDDIG=0 */
  USBPHY_CLR8(0x6c, 0x10);
  USBPHY_SET8(0x6c, 0x2c);
  USBPHY_SET8(0x6d, 0x3e);
  DBG(0, "force PHY to host mode, 0x6d=%x, 0x6c=%x\n", USBPHY_READ8(0x6d), USBPHY_READ8(0x6c));
#endif

  musb_start(mtk_musb);
  MUSB_HST_MODE(mtk_musb);
  switch_int_to_device(mtk_musb);

#ifdef CONFIG_PM
  mtk_musb->is_active = 0;
  DBG(0, "set active to 0 in Pm runtime issue\n");
#endif
 } else {
  DBG(0, "devctl is %x\n", musb_readb(mtk_musb->mregs, MUSB_DEVCTL));
  musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0);
  musb_platform_set_vbus(mtk_musb, 0);

 /* for no VBUS sensing IP */
#if 1
  /* USB MAC OFF*/
  /* VBUSVALID=0, AVALID=0, BVALID=0, SESSEND=1, IDDIG=X */
  USBPHY_SET8(0x6c, 0x10);
  USBPHY_CLR8(0x6c, 0x2e);
  USBPHY_SET8(0x6d, 0x3e);
  DBG(0, "force PHY to idle, 0x6d=%x, 0x6c=%x\n", USBPHY_READ8(0x6d), USBPHY_READ8(0x6c));
#endif

#if !defined(MTK_HDMI_SUPPORT)
  musb_stop(mtk_musb);
#else
  mt_usb_check_reconnect();/*ALPS01688604, IDDIG noise caused by MHL init*/
#endif

  if (wake_lock_active(&mtk_musb->usb_lock))
   wake_unlock(&mtk_musb->usb_lock);

  mtk_musb->xceiv->otg->state = OTG_STATE_B_IDLE;
  MUSB_DEV_MODE(mtk_musb);
  switch_int_to_host(mtk_musb);
 }
out:
 DBG(0, "work end, is_host=%d\n", mtk_musb->is_host);
 up(&mtk_musb->musb_lock);

 mt_usb_clock_unprepare(mtk_musb);
}

里面有一个switch_int_to_host 把设备切换成host模式,从上面的日志上看,这个函数已经执行了,但是设备没有成功切换成host模式。

所以让设备恢复正常应该不是这个函数。

void switch_int_to_host(struct musb *musb)
{
#ifdef ID_PIN_USE_EX_EINT
#if defined(CONFIG_MTK_LEGACY)
 mt_eint_set_polarity(IDDIG_EINT_PIN, MT_EINT_POL_NEG);
 mt_eint_unmask(IDDIG_EINT_PIN);
#else
 irq_set_irq_type(usb_iddig_number, IRQF_TRIGGER_LOW);
 enable_irq(usb_iddig_number);
 irq_set_irq_wake(usb_iddig_number, 1);
 DBG(0, "enable iddig irq LOW @lin %d\n", __LINE__);
#endif
#else
 musb_writel(musb->mregs, USB_L1INTP, IDDIG_INT_STATUS);
 musb_writel(musb->mregs, USB_L1INTM, IDDIG_INT_STATUS|musb_readl(musb->mregs, USB_L1INTM));
#endif
 DBG(0, "switch_int_to_host is done\n");
}

最后我解决的方法也很简单,就是在otg拔出的时候,再connect一下usb就好了。

这个connectusb应该不是简单的connect,是让usb进入device的状态。

index b260550fb7..ba66a7444c
--- a/kernel-4.4/drivers/misc/mediatek/usb20/mt8167/usb20_host.c
+++ b/kernel-4.4/drivers/misc/mediatek/usb20/mt8167/usb20_host.c
@@ -459,6 +459,9 @@ static void musb_id_pin_work(struct work_struct *data)
                mtk_musb->xceiv->otg->state = OTG_STATE_B_IDLE;
                MUSB_DEV_MODE(mtk_musb);
                switch_int_to_host(mtk_musb);
+               mt_usb_connect();
+               /*weiqifa modify*/
+               printk("===weiqifa=== mt_usb_connect() \n");
        }
 out:
        DBG(0, "work end, is_host=%d\n", mtk_musb->is_host);

这个修改是参考了MTK的补丁,他们的做法是用一个新的GPIO口用来检测USB状态.

看我解决Linux下的OTG切换问题_单片机_05

就是上图的那个引脚,但是可惜的是,我们引脚上接的这个脚不是GPIO功能脚,所以MTK的补丁用不上,他们的补丁也就是检测是否有USB插入,并通过OTGID脚状态,然后再适配usb的状态。

用两个GPIO一起适配的原因是为了适应系统的低功耗机制,也就是说,USB的状态只有在插入usb的时候才会设置成connect状态,如果拔出,就不是connect状态,这样可以降低功耗。刚好,我们是DC供电,不是电池供电,这个可以忽略。

关于usb状态的轮询,可以看这个函数

如果进入OTG模式后,USB会一直处在DISCONNECT状态中。

#define USB_STATE_MONITOR_DELAY 5000
static struct delayed_work usb_state_monitor_dw;
static void do_usb_state_monitor_work(struct work_struct *work)
{
 struct gadget_info *gi = dev_get_drvdata(android_device);
 struct usb_composite_dev *cdev = &gi->cdev;
 char *usb_state = "NO-DEV";
 unsigned long flags;

 spin_lock_irqsave(&cdev->lock, flags);
 if (cdev->config)
  usb_state = "CONFIGURED";
 else if (gi->connected)
  usb_state = "CONNECTED";
 else
  usb_state = "DISCONNECTED";
 spin_unlock_irqrestore(&cdev->lock, flags);

 pr_info("usb_state<%s>\n", usb_state);
 schedule_delayed_work(&usb_state_monitor_dw, msecs_to_jiffies(USB_STATE_MONITOR_DELAY));
}
4.总结

在接手这个问题之前,我是没有搞过usb相关的,当然了,我看过很多文章和书籍,之前在rockchip平台上,我也有接触过usb相关的知识,解决实际问题还是第一次,看MTK代码的时候,大部分都是通过函数名字和变量定义猜测意图,加上硬件的一些验证。

比如

下面这个引脚的功能,参考设计和我们的设计不同,我们通过验证发现,只有这个引脚有电压,PMU就会工作走开机流程。



看我解决Linux下的OTG切换问题_单片机_06

type-c 和传统的USB是不同的,传统的USB线为了实现OTG功能会增加一个OTG引脚,但是TPYEC上是没有这个OTG线的,看下TPYEC的引脚图。

看我解决Linux下的OTG切换问题_usb_07
看我解决Linux下的OTG切换问题_单片机_08

上面有CC1、CC2引脚,是用这个来实现OTG功能的。

但是我们引脚设计上,就不会这样搞了,我们是TYPEC接口,但是实现OTG功能还是用的原来的方式,我们没有使用CC1、CC2、这几个引脚。

如下图,是使用TYPEC实现OTG的硬件原理图

看我解决Linux下的OTG切换问题_敏捷开发_09

普通的TYPE C充电线CC管脚通过56K电阻上拉到VBUS,而OTG线一般这是通过5.1K欧下拉接地。


 

看我解决Linux下的OTG切换问题_单片机_10