原理分析:OTG的识别主要靠的是ID引脚,当OTG设备插入机器时,会自动将usb的ID pin引脚拉低,从而会触发中断,进而识别出有OTG设备插入。
补充1:
usb id pin一般为1.8v,默认可以连接电脑usb或adb,即机器默认做从设备device。
而usb id pin拉低到0v后,将触发usb切换为host模式,即可以连接otg设备。
过程分析:
在usb20_host.c文件里面
mt_usb_otg_init函数中
void mt_usb_otg_init(struct musb *musb)
{
/* BYPASS OTG function in special mode */
if (get_boot_mode() == META_BOOT
|| get_boot_mode() == KERNEL_POWER_OFF_CHARGING_BOOT
|| get_boot_mode() == LOW_POWER_OFF_CHARGING_BOOT
) {
DBG(0, "in special mode %d\n", get_boot_mode());
return;
}
/* test */
INIT_DELAYED_WORK(&host_plug_test_work, do_host_plug_test_work); // host热拔插测试work
ktime_start = ktime_get();
INIT_DELAYED_WORK(&musb->host_work, musb_host_work); // 初始化了一个延时工作work
/* CONNECTION MANAGEMENT*/
#ifdef CONFIG_USB_C_SWITCH
DBG(0, "host controlled by TYPEC\n");
typec_control = 1;
#ifdef CONFIG_TCPC_CLASS
mutex_init(&tcpc_otg_lock);
mutex_init(&tcpc_otg_pwr_lock);
otg_tcpc_workq = create_singlethread_workqueue("tcpc_otg_workq");
otg_tcpc_power_workq = create_singlethread_workqueue("tcpc_otg_power_workq");
INIT_WORK(&tcpc_otg_power_work, tcpc_otg_power_work_call);
INIT_WORK(&tcpc_otg_work, tcpc_otg_work_call);
INIT_DELAYED_WORK(®ister_otg_work, do_register_otg_work);
queue_delayed_work(mtk_musb->st_wq, ®ister_otg_work, 0);
vbus_control = 0;
#else
typec_host_driver.priv_data = NULL;
register_typec_switch_callback(&typec_host_driver);
vbus_control = 0;
#endif
#else
DBG(0, "host controlled by IDDIG\n");
iddig_int_init(); // 初始化了iddig引脚
vbus_control = 1;
#endif
/* EP table */
musb->fifo_cfg_host = fifo_cfg_host;
musb->fifo_cfg_host_size = ARRAY_SIZE(fifo_cfg_host);
/*
* otg_state.name = "otg_state";
* otg_state.index = 0;
* otg_state.state = 0;
*
* if (switch_dev_register(&otg_state))
* pr_notice("switch_dev_register fail\n");
* else
* pr_debug("switch_dev register success\n");
*/
先看 iddig_int_init 函数
static int iddig_int_init(void)
{
int ret = 0;
ret = platform_driver_register(&otg_iddig_driver); // 平台设备注册了一个otg的driver
if (ret)
DBG(0, "ret:%d\n", ret);
return 0;
}
继续看下 otg_iddig_driver
static const struct of_device_id otg_iddig_of_match[] = {
{.compatible = "mediatek,usb_iddig_bi_eint"}, // 匹配的名字
{},
};
static int otg_iddig_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
iddig_eint_num = irq_of_parse_and_map(node, 0); // 解析dts里面的中断引脚
DBG(0, "iddig_eint_num<%d>\n", iddig_eint_num);
if (iddig_eint_num < 0)
return -ENODEV;
ret = request_irq(iddig_eint_num, mt_usb_ext_iddig_int, IRQF_TRIGGER_LOW, "USB_IDDIG", NULL);// 申请idd中断 低电平触发
if (ret) {
DBG(0, "request EINT <%d> fail, ret<%d>\n", iddig_eint_num, ret);
return ret;
}
return 0;
}
static struct platform_driver otg_iddig_driver = {
.probe = otg_iddig_probe,
/* .remove = otg_iddig_remove, */
/* .shutdown = otg_iddig_shutdown, */
.driver = {
.name = "otg_iddig",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(otg_iddig_of_match),
},
};
dts文件里面的内容
otg_iddig: otg_iddig {
compatible = "mediatek,usb_iddig_bi_eint";
};
&otg_iddig {
interrupt-parent = <&pio>;
interrupts = <16 IRQ_TYPE_EDGE_FALLING 16 0>; // 上面的中断其实就是解析这里的16,gpio16
status = "okay";
};
我们看下最重要的中断函数 mt_usb_ext_iddig_int
static irqreturn_t mt_usb_ext_iddig_int(int irq, void *dev_id)
{
iddig_cnt++;
iddig_req_host = !iddig_req_host;
DBG(0, "id pin assert, %s\n", iddig_req_host ? "connect" : "disconnect");
queue_delayed_work(mtk_musb->st_wq, &mtk_musb->host_work, msecs_to_jiffies(sw_deboun_time)); // 调度了延时工作work
disable_irq_nosync(iddig_eint_num);
return IRQ_HANDLED;
}
其实OTG触发中断后就只做了一件事就是调度上面的延时work,下面我们分析一下这个延时work
INIT_DELAYED_WORK(&musb->host_work, musb_host_work); // host干活函数
#define ID_PIN_WORK_RECHECK_TIME 30 /* 30 ms */
#define ID_PIN_WORK_BLOCK_TIMEOUT 30000 /* 30000 ms */
static void musb_host_work(struct work_struct *data) // host延时work
{
u8 devctl = 0;
unsigned long flags;
static int inited, timeout; /* default to 0 */
static s64 diff_time;
int host_mode;
int usb_clk_state = NO_CHANGE;
/* kernel_init_done should be set in early-init stage through init.$platform.usb.rc */
if (!inited && !kernel_init_done && !mtk_musb->is_ready && !timeout) {
ktime_end = ktime_get();
diff_time = ktime_to_ms(ktime_sub(ktime_end, ktime_start));
DBG_LIMIT(3, "init_done:%d, is_ready:%d, inited:%d, TO:%d, diff:%lld",
kernel_init_done, mtk_musb->is_ready, inited, timeout,
diff_time);
if (diff_time > ID_PIN_WORK_BLOCK_TIMEOUT) {
DBG(0, "diff_time:%lld\n", diff_time);
timeout = 1;
}
queue_delayed_work(mtk_musb->st_wq, &mtk_musb->host_work, msecs_to_jiffies(ID_PIN_WORK_RECHECK_TIME));
return;
} else if (!inited) {
DBG(0, "PASS, init_done:%d, is_ready:%d, inited:%d, TO:%d\n",
kernel_init_done, mtk_musb->is_ready, inited, timeout);
}
inited = 1;
/* always prepare clock and check if need to unprepater later */
/* clk_prepare_cnt +1 here */
usb_prepare_clock(true); // 准备usb clk
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;
}
/* flip */
if (host_plug_test_triggered)
host_mode = !mtk_musb->is_host;
else
host_mode = musb_is_host(); // 获取usb模式
DBG(0, "musb is as %s\n", host_mode?"host":"device");
/*switch_set_state((struct switch_dev *)&otg_state, host_mode);*/
if (host_mode) { // 如果是host模式,如果插otg就是跑这里
/* switch to HOST state before turn on VBUS */
MUSB_HST_MODE(mtk_musb);
/* to make sure all event clear */
msleep(32);
#ifdef CONFIG_MTK_UAC_POWER_SAVING
if (!usb_on_sram) {
int ret;
ret = gpd_switch_to_sram(mtk_musb->controller);
DBG(0, "gpd_switch_to_sram, ret<%d>\n", ret);
if (ret == 0)
usb_on_sram = 1;
}
#endif
/* setup fifo for host mode */
ep_config_from_table_for_host(mtk_musb);
__pm_stay_awake(&mtk_musb->usb_lock);
/* this make PHY operation workable */
musb_platform_enable(mtk_musb);
/* 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)));
set_usb_phy_mode(PHY_IDLE_MODE); // 设置usb phy模式
/* wait */
mdelay(5);
/* restart session */
devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL);
musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, (devctl | MUSB_DEVCTL_SESSION));
set_usb_phy_mode(PHY_HOST_ACTIVE);
#endif
musb_start(mtk_musb); // 开始
if (!typec_control && !host_plug_test_triggered)
switch_int_to_device(mtk_musb);
/* for some signal issue */
mdelay(5);
mt_usb_set_vbus(mtk_musb, 1); // 设置vbus电源
if (check_vbus() < 0)
DBG(0, "check vbus fail\n");
if (host_plug_test_enable && !host_plug_test_triggered)
queue_delayed_work(mtk_musb->st_wq, &host_plug_test_work, 0);
usb_clk_state = OFF_TO_ON;
} else { // 如果是device模式
/* for device no disconnect interrupt */
spin_lock_irqsave(&mtk_musb->lock, flags);
if (mtk_musb->is_active) {
DBG(0, "for not receiving disconnect interrupt\n");
usb_hcd_resume_root_hub(musb_to_hcd(mtk_musb));
musb_root_disconnect(mtk_musb);
}
spin_unlock_irqrestore(&mtk_musb->lock, flags);
DBG(1, "devctl is %x\n", musb_readb(mtk_musb->mregs, MUSB_DEVCTL));
musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0);
if (mtk_musb->usb_lock.active)
__pm_relax(&mtk_musb->usb_lock);
mt_usb_set_vbus(mtk_musb, 0);
/* for no VBUS sensing IP */
#if 1
set_usb_phy_mode(PHY_IDLE_MODE);
#endif
musb_stop(mtk_musb);
if (!typec_control && !host_plug_test_triggered)
switch_int_to_host(mtk_musb);
#ifdef CONFIG_MTK_UAC_POWER_SAVING
if (usb_on_sram) {
gpd_switch_to_dram(mtk_musb->controller);
usb_on_sram = 0;
}
#endif
/* to make sure all event clear */
msleep(32);
mtk_musb->xceiv->otg->state = OTG_STATE_B_IDLE;
/* switch to DEV state after turn off VBUS */
MUSB_DEV_MODE(mtk_musb);
usb_clk_state = ON_TO_OFF;
}
out:
DBG(0, "work end, is_host=%d\n", mtk_musb->is_host);
up(&mtk_musb->musb_lock);
if (usb_clk_state == ON_TO_OFF) {
/* clock on -> of: clk_prepare_cnt -2 */
usb_prepare_clock(false);
usb_prepare_clock(false);
} else if (usb_clk_state == NO_CHANGE) {
/* clock no change : clk_prepare_cnt -1 */
usb_prepare_clock(false);
}
}
执行到此,host模式便已经切换ok了,这是otg设备也已经可以识别到了。
下面是插入U盘时的串口log:
[ 243.735713] <0>-(0)[0:swapper/0][MUSB]mt_usb_ext_iddig_int 730: id pin assert, connect USB插入打印
[ 244.146373] <0>.(0)[973:kworker/u8:12][MUSB]musb_host_work 584: PASS, init_done:1, is_ready:1, inited:0, TO:0
[ 244.151959] <0>.(0)[973:kworker/u8:12][MUSB]musb_host_work 598: work start, is_host=0
[ 244.152940] <0>.(0)[973:kworker/u8:12][MUSB]musb_host_work 612: musb is as host
[ 244.193172] <0>.(0)[973:kworker/u8:12][MUSB]mt_usb_enable 298: begin <0,0>,<2,1,1,1>
[ 244.205044] <0>.(0)[973:kworker/u8:12][MUSB]set_usb_phy_mode 437: force PHY to mode 1, 0x6c=3f2f
[ 244.206147] <0>.(0)[973:kworker/u8:12][MUSB]hs_slew_rate_cal 302: [USBPHY]slew calibration:FM_OUT =321,x=4287,value=4
[ 244.207481] <0>.(0)[973:kworker/u8:12][MUSB]usb_phy_recover 685: skip efuse setting temporary, RG_USB20_INTR_CAL=0x19
[ 244.208796] <0>.(0)[973:kworker/u8:12][MUSB]usb_phy_recover 694: usb recovery success
[ 244.209783] <0>.(0)[973:kworker/u8:12][MUSB]mt_usb_enable 327: end, <2,1,2,1>
[ 244.316384] <0>.(0)[973:kworker/u8:12][MUSB]set_usb_phy_mode 437: force PHY to mode 0, 0x6c=3f11
[ 244.322493] <0>.(0)[973:kworker/u8:12][MUSB]set_usb_phy_mode 437: force PHY to mode 2, 0x6c=3f2d
[ 244.323594] <0>.(0)[973:kworker/u8:12][MUSB]musb_start 1346: start, is_host=1 is_active=0
[ 244.324607] <0>.(0)[973:kworker/u8:12][MUSB]mt_usb_enable 298: begin <1,1>,<3,1,2,1>
[ 244.325575] <0>.(0)[973:kworker/u8:12][MUSB]musb_start 1391: set ignore babble MUSB_ULPI_REG_DATA=88
[ 244.326722] <0>.(0)[973:kworker/u8:12][MUSB]musb_start 1399: add softconn
[ 244.327616] <0>.(0)[28:kworker/0:1]musb-hdrc musb-hdrc: prop=1, power=1, is_host=1
[ 244.328559] <0>.(0)[28:kworker/0:1]musb-hdrc musb-hdrc: prop=2, power=1, is_host=1
[ 244.329499] <0>.(0)[28:kworker/0:1]musb-hdrc musb-hdrc: prop=3, power=1, is_host=1
[ 244.330624] <0>.(0)[973:kworker/u8:12][MUSB]switch_int_to_device 389: switch_int_to_device is done
[ 244.333642] <1>.(1)[1052:usb@1.1-service]musb-hdrc musb-hdrc: prop=2, power=1, is_host=1
[ 244.334883] <1>.(1)[1052:usb@1.1-service]musb-hdrc musb-hdrc: prop=3, power=1, is_host=1
[ 244.335993] <1>.(1)[1052:usb@1.1-service]musb-hdrc musb-hdrc: prop=1, power=1, is_host=1
[ 244.336747] <0>.(0)[973:kworker/u8:12][MUSB]mt_usb_set_vbus 175: is_on<1>, control<1>
[ 244.337984] <0>.(0)[973:kworker/u8:12][MUSB]vbus_init 117: +++
[ 244.338726] <0>.(0)[973:kworker/u8:12][MUSB]vbus_init 125: ---
[ 244.339452] <0>.(0)[973:kworker/u8:12][MUSB]_set_vbus 147: op<1>, status<0>
[ 244.340902] <0>-(0)[973:kworker/u8:12]alarmtimer_enqueue, 838546272379
[ 244.342056] <0>.(0)[973:kworker/u8:12]mt635x-auxadc mt635x-auxadc: name:VCDT, channel=2, adc_out=0x346, adc_result=368
[ 244.343412] <0>.(0)[973:kworker/u8:12][MUSB]check_vbus 537: vbus=3481
[ 244.450086] <0>.(0)[973:kworker/u8:12]mt635x-auxadc mt635x-auxadc: name:VCDT, channel=2, adc_out=0x4a0, adc_result=520
[ 244.451428] <0>.(0)[973:kworker/u8:12][MUSB]check_vbus 537: vbus=4920
[ 244.452225] <0>.(0)[973:kworker/u8:12][MUSB]musb_host_work 712: work end, is_host=1
[ 244.483017] <1>.(1)[81:irq/220-mt6358-]mt6358-pmic 1000d000.pwrap:mt6357-pmic: Reg[0x91a]=0x40,name=chrdet_edge,hwirq=54,type=4
[ 244.484485] <1>.(1)[81:irq/220-mt6358-][MUSB]usb20_check_vbus_on 135: vbus_on<1>
[ 244.485398] <1>.(1)[81:irq/220-mt6358-]charger type: UNKNOWN, Now is usb host mode. Skip detection
[ 244.613519] <0>-(0)[0:swapper/0][MUSB]musb_stage0_irq 1072: MUSB_INTR_CONNECT (b_idle)
[ 244.614515] <0>-(0)[0:swapper/0]QMU_WARN,<musb_disable_q_all 333>, disable_q_all
[ 244.615480] <0>-(0)[0:swapper/0][MUSB]musb_stage0_irq 1155: CONNECT (a_host) devctl 5d
[ 244.616551] <0>-(0)[28:kworker/0:1][MUSB]musb_hub_control 370: port status 00010101,devctl=0x5d
[ 244.723052] <0>-(0)[28:kworker/0:1][MUSB]musb_hub_control 370: port status 00000101,devctl=0x5d
[ 244.724216] <0>-(0)[28:kworker/0:1][MUSB]musb_port_reset 129: force musb_platform_reset
[ 244.792968] <0>-(0)[28:kworker/0:1][MUSB]musb_hub_control 370: port status 00120503,devctl=0x5d
[ 244.849662] <0>.(0)[28:kworker/0:1]usb 1-1: new high-speed USB device number 2 using musb-hdrc
[ 244.851582] <0>-(0)[28:kworker/0:1][MUSB]musb_port_reset 129: force musb_platform_reset
[ 244.885206] <2>.(2)[0:swapper/2][mcdi]mcdi cpu: 48, 35, 24, 10, cluster : 20, pause = 62, multi core = 33, latency = 0, residency = 2, last core = 97, avail cpu = 000f, cluster = 0001, enabled = 1, max_s_state = 5, system_idle_hint = 00000000
[ 244.919727] <0>-(0)[28:kworker/0:1][MUSB]musb_hub_control 370: port status 00120503,devctl=0x5d
[ 244.997176] <0>.(0)[28:kworker/0:1]usb-storage 1-1:1.0: USB Mass Storage device detected
[ 244.999402] <0>.(0)[28:kworker/0:1]scsi host0: usb-storage 1-1:1.0
[ 245.001076] <0>-(0)[28:kworker/0:1][MUSB]musb_hub_control 370: port status 00020503,devctl=0x5d