static enum dwc_otg_state do_a_host(struct dwc_otg2 *otg) { int rc = 0; u32 otg_events, user_events, otg_mask, user_mask; int id = RID_UNKNOWN; unsigned long flags; /* If Battery low and connected charger is not ACA-DOCK. * Then stop trying to start host mode. */ if ((otg->usb2_phy.vbus_state == VBUS_DISABLED) && (otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK)) { otg_uevent_trigger(&otg->usb2_phy); return DWC_STATE_B_IDLE; } if (otg->charging_cap.chrg_type != POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) { dwc_otg_enable_vbus(otg, 1); /* meant receive vbus valid event*/ if (do_wait_vbus_raise(otg) == DWC_STATE_A_HOST) otg_err(otg, "Drive VBUS maybe fail!\n"); } rc = start_host(otg); if (rc < 0) { stop_host(otg); otg_err(otg, "start_host failed!"); return DWC_STATE_INVALID; } stay_host: otg_events = 0; user_events = 0; user_mask = USER_A_BUS_DROP | USER_ID_B_CHANGE_EVENT; otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_A_DEV_SESS_END_DET_EVNT; rc = sleep_until_event(otg, otg_mask, user_mask, &otg_events, &user_events, 0); if (rc < 0) { stop_host(otg); return DWC_STATE_EXIT; } /* Higher priority first */ if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); /* ACA-Dock plug out */ if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_DISCONNECT); else dwc_otg_enable_vbus(otg, 0); stop_host(otg); return DWC_STATE_B_IDLE; } if (user_events & USER_A_BUS_DROP) { /* Due to big consume by DUT, even ACA-Dock connected, * the battery capability still maybe decrease. For this * case, still save host mode. Because DUT haven't drive VBus.*/ if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) goto stay_host; dwc_otg_enable_vbus(otg, 0); stop_host(otg); return DWC_STATE_B_IDLE; } if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); id = get_id(otg); /* Plug out ACA_DOCK/USB device */ if (id == RID_FLOAT) { if (otg->charging_cap.chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK) { /* ACA_DOCK plug out, receive * id change prior to vBus change */ dwc_otg_notify_charger_type(otg, POWER_SUPPLY_CHARGER_EVENT_DISCONNECT); stop_host(otg); } else { /* Normal USB device plug out */ spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE; spin_unlock_irqrestore(&otg->lock, flags); stop_host(otg); dwc_otg_enable_vbus(otg, 0); } } else if (id == RID_GND || id == RID_A) { otg_dbg(otg, "Stay DWC_STATE_A_HOST!!\n"); /* Prevent user fast plug in after plug out. * It will cause the first ID change event lost. * So need to check real ID currently. */ goto stay_host; } else { otg_err(otg, "Meet invalid charger cases!"); spin_lock_irqsave(&otg->lock, flags); otg->charging_cap.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE; spin_unlock_irqrestore(&otg->lock, flags); stop_host(otg); } return DWC_STATE_WAIT_VBUS_FALL; } /* Higher priority first */ if (user_events & USER_ID_B_CHANGE_EVENT) { otg_dbg(otg, "USER_ID_B_CHANGE_EVENT\n"); stop_host(otg); otg->user_events |= USER_ID_B_CHANGE_EVENT; return DWC_STATE_B_IDLE; } /* Invalid state */ return DWC_STATE_INVALID; }
int otg_main_thread(void *data) { struct dwc_otg2 *otg = (struct dwc_otg2 *)data; /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ allow_signal(SIGINT); allow_signal(SIGTERM); allow_signal(SIGKILL); allow_signal(SIGUSR1); pm_runtime_get_sync(otg->dev); /* Allow the thread to be frozen */ set_freezable(); otg_dbg(otg, "Thread running\n"); while (otg->state != DWC_STATE_TERMINATED) { int next = DWC_STATE_B_IDLE; otg_dbg(otg, "\n\n\nMain thread entering state\n"); switch (otg->state) { case DWC_STATE_B_IDLE: otg_dbg(otg, "DWC_STATE_B_IDLE\n"); next = do_connector_id_status(otg); break; case DWC_STATE_CHARGER_DETECTION: otg_dbg(otg, "DWC_STATE_CHARGER_DETECTION\n"); next = do_charger_detection(otg); break; case DWC_STATE_WAIT_VBUS_RAISE: otg_dbg(otg, "DWC_STATE_WAIT_VBUS_RAISE\n"); next = do_wait_vbus_raise(otg); break; case DWC_STATE_WAIT_VBUS_FALL: otg_dbg(otg, "DWC_STATE_WAIT_VBUS_FALL\n"); next = do_wait_vbus_fall(otg); break; case DWC_STATE_CHARGING: otg_dbg(otg, "DWC_STATE_CHARGING\n"); next = do_charging(otg); break; case DWC_STATE_A_HOST: otg_dbg(otg, "DWC_STATE_A_HOST\n"); next = do_a_host(otg); break; case DWC_STATE_B_PERIPHERAL: otg_dbg(otg, "DWC_STATE_B_PERIPHERAL\n"); start_peripheral(otg); next = do_b_peripheral(otg); stop_peripheral(otg); break; case DWC_STATE_EXIT: otg_dbg(otg, "DWC_STATE_EXIT\n"); next = DWC_STATE_TERMINATED; break; case DWC_STATE_INVALID: otg_dbg(otg, "DWC_STATE_INVALID!!!\n"); default: otg_dbg(otg, "Unknown State %d, sleeping...\n", otg->state); sleep_main_thread(otg); break; } otg->prev = otg->state; otg->state = next; } pm_runtime_mark_last_busy(otg->dev); pm_runtime_put_autosuspend(otg->dev); otg->main_thread = NULL; otg_dbg(otg, "OTG main thread exiting....\n"); return 0; }