看我解决Linux下的OTG切换问题
共 28772字,需浏览 58分钟
·
2021-05-15 23:56
1.硬件原理图
看下面的原理图
VCC_OTG_EN 引脚,这个脚主要是用来控制给外部OTG设备提供电源控制的。如果设备作为DEVICE设备,这时候VBUS的电是由外部提供的,比如通过USB线和电脑连接,这个时候,VBUS的电压是由电脑提供的。
如果设备作为HOST设备,比如连接U盘,我们需要给U盘提供VBUS电压,就需要控制VCC_OTG_EN引脚给VBUS提供电压。
USB_OTG_ID 脚
这个脚用来判断有OTG设备插入
VBUS电压检测,这个电路看不懂有什么作用,硬件设计预期是希望如果有外部的USB设备给设备供电,就切换模式,但是实际上,不管设备作为HOST还是DEVICE,这个电压会一直存在。所以总觉得这个设计有点问题。
ID脚电压,没有插入OTG设备的时候,电压是1.79v
,插入OTG设备的时候,电压是 0
V。
2.软件控制
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状态.
就是上图的那个引脚,但是可惜的是,我们引脚上接的这个脚不是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就会工作走开机流程。
type-c 和传统的USB是不同的,传统的USB线为了实现OTG功能会增加一个OTG引脚,但是TPYEC上是没有这个OTG线的,看下TPYEC的引脚图。
上面有CC1、CC2引脚,是用这个来实现OTG功能的。
但是我们引脚设计上,就不会这样搞了,我们是TYPEC接口,但是实现OTG功能还是用的原来的方式,我们没有使用CC1、CC2、这几个引脚。
如下图,是使用TYPEC实现OTG的硬件原理图
普通的TYPE C充电线CC管脚通过56K电阻上拉到VBUS,而OTG线一般这是通过5.1K欧下拉接地。