예제 #1
0
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;
}
예제 #2
0
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;
}