/* Called when leaving a state. Do state clean up jobs here */ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { switch (old_state) { case OTG_STATE_B_IDLE: otg_del_timer(fsm, B_SE0_SRP); fsm->b_se0_srp = 0; fsm->adp_sns = 0; fsm->adp_prb = 0; break; case OTG_STATE_B_SRP_INIT: fsm->data_pulse = 0; fsm->b_srp_done = 0; break; case OTG_STATE_B_PERIPHERAL: if (fsm->otg->gadget) fsm->otg->gadget->host_request_flag = 0; break; case OTG_STATE_B_WAIT_ACON: otg_del_timer(fsm, B_ASE0_BRST); fsm->b_ase0_brst_tmout = 0; break; case OTG_STATE_B_HOST: break; case OTG_STATE_A_IDLE: fsm->adp_prb = 0; break; case OTG_STATE_A_WAIT_VRISE: otg_del_timer(fsm, A_WAIT_VRISE); fsm->a_wait_vrise_tmout = 0; break; case OTG_STATE_A_WAIT_BCON: otg_del_timer(fsm, A_WAIT_BCON); fsm->a_wait_bcon_tmout = 0; break; case OTG_STATE_A_HOST: otg_del_timer(fsm, A_WAIT_ENUM); break; case OTG_STATE_A_SUSPEND: otg_del_timer(fsm, A_AIDL_BDIS); fsm->a_aidl_bdis_tmout = 0; fsm->a_suspend_req_inf = 0; break; case OTG_STATE_A_PERIPHERAL: otg_del_timer(fsm, A_BIDL_ADIS); fsm->a_bidl_adis_tmout = 0; if (fsm->otg->gadget) fsm->otg->gadget->host_request_flag = 0; break; case OTG_STATE_A_WAIT_VFALL: otg_del_timer(fsm, A_WAIT_VFALL); fsm->a_wait_vfall_tmout = 0; otg_del_timer(fsm, A_WAIT_VRISE); break; case OTG_STATE_A_VBUS_ERR: break; default: break; } }
static void ci_handle_vbus_glitch(struct ci_hdrc *ci) { bool valid_vbus_change = false; if (hw_read_otgsc(ci, OTGSC_BSV)) { if (!ci_is_vbus_glitch(ci)) { if (ci_otg_is_fsm_mode(ci)) { ci->fsm.b_sess_vld = 1; ci->fsm.b_ssend_srp = 0; otg_del_timer(&ci->fsm, B_SSEND_SRP); otg_del_timer(&ci->fsm, B_SRP_FAIL); } valid_vbus_change = true; } } else { if (ci->vbus_active && !ci_otg_is_fsm_mode(ci)) valid_vbus_change = true; } if (valid_vbus_change) { ci->b_sess_valid_event = true; ci_otg_queue_work(ci); } }
/* Called when leaving a state. Do state clean up jobs here */ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { switch (old_state) { case OTG_STATE_B_IDLE: otg_del_timer(fsm, b_se0_srp_tmr); fsm->b_se0_srp = 0; break; case OTG_STATE_B_SRP_INIT: fsm->b_srp_done = 0; break; case OTG_STATE_B_PERIPHERAL: break; case OTG_STATE_B_WAIT_ACON: otg_del_timer(fsm, b_ase0_brst_tmr); fsm->b_ase0_brst_tmout = 0; break; case OTG_STATE_B_HOST: break; case OTG_STATE_A_IDLE: break; case OTG_STATE_A_WAIT_VRISE: otg_del_timer(fsm, a_wait_vrise_tmr); fsm->a_wait_vrise_tmout = 0; break; case OTG_STATE_A_WAIT_BCON: otg_del_timer(fsm, a_wait_bcon_tmr); fsm->a_wait_bcon_tmout = 0; break; case OTG_STATE_A_HOST: otg_del_timer(fsm, a_wait_enum_tmr); break; case OTG_STATE_A_SUSPEND: otg_del_timer(fsm, a_aidl_bdis_tmr); fsm->a_aidl_bdis_tmout = 0; fsm->a_suspend_req = 0; break; case OTG_STATE_A_PERIPHERAL: break; case OTG_STATE_A_WAIT_VFALL: otg_del_timer(fsm, a_wait_vrise_tmr); break; case OTG_STATE_A_VBUS_ERR: break; default: break; } }
static int otg_handle_role_switch(struct otg_fsm *fsm, struct usb_device *udev) { int err; enum usb_otg_state state = fsm->otg->state; if (state == OTG_STATE_A_HOST) { /* Set b_hnp_enable */ if (!fsm->a_set_b_hnp_en) { err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, 0, USB_DEVICE_B_HNP_ENABLE, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (err < 0) { /* Continue polling */ otg_add_timer(fsm, HNP_POLLING); return 0; } else { fsm->a_set_b_hnp_en = 1; } } fsm->a_bus_req = 0; if (fsm->tst_maint) { fsm->tst_maint = 0; fsm->otg_vbus_off = 0; otg_del_timer(fsm, A_TST_MAINT); } return HOST_REQUEST_FLAG; } else if (state == OTG_STATE_B_HOST) { fsm->b_bus_req = 0; return HOST_REQUEST_FLAG; } return -EINVAL; }
/* * Called by host to poll peripheral if it wants to be host * Return value: * - host request flag(1) if the device wants to be host, * - host request flag(0) if the device keeps peripheral role, * - otherwise, error code. */ int otg_hnp_polling(struct otg_fsm *fsm) { struct usb_device *udev; int retval; enum usb_otg_state state = fsm->otg->state; struct usb_otg_descriptor *desc = NULL; if ((state != OTG_STATE_A_HOST || !fsm->b_hnp_enable) && state != OTG_STATE_B_HOST) return -EINVAL; udev = usb_hub_find_child(fsm->otg->host->root_hub, 1); if (!udev) { dev_err(fsm->otg->host->controller, "no usb dev connected, can't start HNP polling\n"); return -ENODEV; } if (udev->state != USB_STATE_CONFIGURED) { dev_dbg(&udev->dev, "the B dev is not resumed!\n"); otg_add_timer(fsm, HNP_POLLING); return -EPERM; } /* * Legacy otg test device does not support HNP polling, * start HNP directly for legacy otg test device. */ if (fsm->tst_maint && (__usb_get_extra_descriptor(udev->rawdescriptors[0], le16_to_cpu(udev->config[0].desc.wTotalLength), USB_DT_OTG, (void **) &desc) == 0)) { /* shorter bLength of OTG 1.3 or earlier */ if (desc->bLength < 5) { fsm->a_bus_req = 0; fsm->tst_maint = 0; otg_del_timer(fsm, A_TST_MAINT); return HOST_REQUEST_FLAG; } } fsm->host_req_flag = 0; /* Get host request flag from connected USB device */ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE, 0, OTG_STS_SELECTOR, &fsm->host_req_flag, 1, USB_CTRL_GET_TIMEOUT); if (retval == 1) { if (fsm->host_req_flag == HOST_REQUEST_FLAG) { retval = otg_handle_role_switch(fsm, udev); } else if (fsm->host_req_flag == 0) { /* Continue polling */ otg_add_timer(fsm, HNP_POLLING); retval = 0; } else { dev_err(&udev->dev, "host request flag is invalid\n"); retval = -EINVAL; } } else { dev_warn(&udev->dev, "Get one byte OTG status failed\n"); retval = -EIO; } return retval; }