static bool check_link_status(struct mem_link_device *mld)
{
	if (mipi_lli_get_link_status() != LLI_MOUNTED)
		return false;

	if (gpio_get_value(mld->gpio_cp_status) == 0)
		return false;

	return true;
}
Beispiel #2
0
/**
@brief	interrupt handler for a wakeup interrupt

1) Reads the interrupt value\n
2) Performs interrupt handling\n

@param irq	the IRQ number
@param data	the pointer to a data
*/
static irqreturn_t ap_wakeup_interrupt(int irq, void *data)
{
    struct mem_link_device *mld = (struct mem_link_device *)data;
    int ap_wakeup = gpio_get_value(mld->gpio_ap_wakeup);
    int cp_wakeup = gpio_get_value(mld->gpio_cp_wakeup);
    int cpu = raw_smp_processor_id();

    change_irq_type(irq, ap_wakeup);

    if (work_pending(&mld->cp_sleep_dwork.work))
        cancel_delayed_work(&mld->cp_sleep_dwork);

    if (ap_wakeup) {
        mld->last_cp2ap_intr = cpu_clock(cpu);
        if (!cp_wakeup)
            gpio_set_value(mld->gpio_cp_wakeup, 1);

        if (!wake_lock_active(&mld->ap_wlock))
            wake_lock(&mld->ap_wlock);

        if (mipi_lli_get_link_status() == LLI_UNMOUNTED)
            mipi_lli_set_link_status(LLI_WAITFORMOUNT);

        if (!mipi_lli_suspended())
            gpio_set_value(mld->gpio_ap_status, 1);

    } else {
        if (wake_lock_active(&mld->ap_wlock))
            wake_unlock(&mld->ap_wlock);

        if (mipi_lli_get_link_status() & LLI_WAITFORMOUNT)
            mipi_lli_set_link_status(LLI_UNMOUNTED);
        queue_delayed_work(system_nrt_wq, &mld->cp_sleep_dwork,
                           msecs_to_jiffies(sleep_timeout));
    }

    print_pm_status(mld);

    return IRQ_HANDLED;
}
static bool check_link_status(struct mem_link_device *mld)
{
	struct link_device *ld = &mld->link_dev;
	struct modem_ctl *mc = ld->mc;
	struct modem_link_pm *pm = &ld->pm;

	if (gpio_get_value(mld->gpio_cp_status) == 0)
		return false;

	if (mipi_lli_get_link_status() != LLI_MOUNTED)
		return false;

	if (cp_online(mc))
		return pm->link_active ? pm->link_active(pm) : true;

	return true;
}
static bool lli_link_unmounted(struct link_device *ld)
{
	return (mipi_lli_get_link_status() == LLI_UNMOUNTED);
}
static void run_pm_fsm(struct modem_link_pm *pm, enum pm_event event)
{
	struct link_device *ld = container_of(pm, struct link_device, pm);
	struct modem_ctl *mc = ld->mc;
	struct pm_fsm *fsm = &pm->fsm;
	enum pm_state c_state;
	enum pm_state n_state;
	unsigned long flags;

	spin_lock_irqsave(&pm->lock, flags);

	c_state = fsm->state;
	n_state = fsm->state;

	if (!pm->active) {
		release_ap2cp_wakeup(pm);
		if (event == PM_EVENT_LINK_ERROR)
			n_state = PM_STATE_CP_FAIL;
		goto exit;
	}

	if (fsm_locked(c_state))
		goto exit;

	if (event == PM_EVENT_CP_HOLD_REQUEST) {
		if (!cp_online(mc))
			goto exit;

		pm->hold_requested = true;

		if (!hold_possible(c_state))
			goto exit;
	}

#ifdef DEBUG_MODEM_IF
	print_pm_event(pm, event);
#endif
	switch (c_state) {
	case PM_STATE_UNMOUNTED:
		if (event == PM_EVENT_LINK_SUSPENDED) {
			n_state = PM_STATE_SUSPENDED;
		} else if (event == PM_EVENT_CP2AP_WAKEUP_HIGH
			   || event == PM_EVENT_CP_HOLD_REQUEST) {
			if (link_suspended(pm)) {
				n_state = PM_STATE_SUSPENDED;
				prepare_mount(pm);
			} else {
				n_state = next_state_from_resume(pm);
				if (n_state == PM_STATE_RESETTING) {
					prepare_mount(pm);
					unmounted_to_resetting(pm);
				} else if (n_state == PM_STATE_HOLDING) {
					prepare_mount(pm);
					unmounted_to_holding(pm);
				}
			}
		}
		break;

	case PM_STATE_SUSPENDED:
		if (event == PM_EVENT_LINK_RESUMED) {
			n_state = next_state_from_resume(pm);
			if (n_state == PM_STATE_RESETTING) {
				prepare_mount(pm);
				unmounted_to_resetting(pm);
			} else if (n_state == PM_STATE_HOLDING) {
				prepare_mount(pm);
				unmounted_to_holding(pm);
			}
		} else if (event == PM_EVENT_CP2AP_WAKEUP_HIGH
			   || event == PM_EVENT_CP_HOLD_REQUEST) {
			n_state = PM_STATE_SUSPENDED;
			prepare_mount(pm);
		}
		break;

	case PM_STATE_HOLDING:
		if (event == PM_EVENT_CP2AP_WAKEUP_HIGH) {
			n_state = PM_STATE_RESETTING;
			holding_to_resetting(pm);
		} else if (event == PM_EVENT_WDOG_TIMEOUT) {
			/*
			It is not guaranteed for FSM to succeed in getting GPIO
			interrupt events or for stop_pm_wdog() to succeed in
			deleting the WDOG timer always.
			So, gpio_cp2ap_wakeup and gpio_cp2ap_status must always
			be checked before state transition.
			*/
			if (gpio_get_value(pm->gpio_cp2ap_wakeup)) {
				n_state = PM_STATE_RESETTING;
				holding_to_resetting(pm);
			} else {
				n_state = PM_STATE_WDOG_TIMEOUT;
			}
		}
		break;

	case PM_STATE_RESETTING:
		if (event == PM_EVENT_LINK_RESET) {
			n_state = PM_STATE_MOUNTING;
			stop_pm_wdog(pm, c_state, event);
			assert_ap2cp_status(pm);

			mif_info("%s: state: ap2cp_status_pin_done\n", __func__);
			start_pm_wdog(pm, n_state, PM_STATE_ACTIVE,
				      PM_EVENT_LINK_MOUNTED, LINKPM_WATCHDOG_TIMEOUT);
		} else if (event == PM_EVENT_WDOG_TIMEOUT) {
			n_state = PM_STATE_AP_FAIL;
		} else if (event == PM_EVENT_CP2AP_WAKEUP_LOW) {
			n_state = PM_STATE_CP_FAIL;
		}
		break;

	case PM_STATE_MOUNTING:
		if (event == PM_EVENT_LINK_MOUNTED
			|| event == PM_EVENT_CP2AP_STATUS_HIGH) {
			n_state = PM_STATE_ACTIVE;
			stop_pm_wdog(pm, c_state, event);
		} else if (event == PM_EVENT_WDOG_TIMEOUT) {
			n_state = PM_STATE_WDOG_TIMEOUT;
		} else if (event == PM_EVENT_CP2AP_WAKEUP_LOW) {
#if 0
			n_state = PM_STATE_CP_FAIL;
#else
			n_state = PM_STATE_AP_FAIL;
#endif
		}
		break;

	case PM_STATE_ACTIVE:
		if (event == PM_EVENT_CP2AP_WAKEUP_LOW) {
			n_state = PM_STATE_AP_FREE;
			schedule_cp_free(pm);
#if 0
			if (mipi_lli_get_link_status() == LLI_MOUNTED) {
				n_state = PM_STATE_AP_FREE;
				schedule_cp_free(pm);
			}
#endif
		} else if (event == PM_EVENT_CP2AP_STATUS_LOW) {
#ifdef REPORT_CRASHDMP
			n_state = PM_STATE_CP_FAIL;
#else
			n_state = PM_STATE_AP_FREE;
			schedule_cp_free(pm);
#endif
		}
		break;

	case PM_STATE_AP_FREE:
		if (event == PM_EVENT_CP2AP_WAKEUP_HIGH) {
			n_state = PM_STATE_ACTIVE;
			cancel_cp_free(pm);
			assert_ap2cp_wakeup(pm);
		} else if (event == PM_EVENT_CP_HOLD_REQUEST) {
			n_state = PM_STATE_AP_FREE;
			cancel_cp_free(pm);
			assert_ap2cp_wakeup(pm);
			schedule_cp_free(pm);
		} else if (event == PM_EVENT_CP_HOLD_TIMEOUT) {
			/*
			It is not guaranteed for cancel_cp_free() to succeed
			in canceling the cp_free_dwork always.
			So, cp2ap_wakeup must always be checked before state
			transition.
			*/
			if (!gpio_get_value(pm->gpio_cp2ap_wakeup)) {
				n_state = PM_STATE_CP_FREE;
				pm->hold_requested = false;
				release_ap2cp_wakeup(pm);
			} else {
				n_state = PM_STATE_ACTIVE;
				cancel_cp_free(pm);
				assert_ap2cp_wakeup(pm);
			}
		} else if (event == PM_EVENT_CP2AP_STATUS_LOW) {
			n_state = PM_STATE_CP_FAIL;
		}
		break;

	case PM_STATE_CP_FREE:
		if (event == PM_EVENT_CP2AP_STATUS_LOW) {
			n_state = PM_STATE_UNMOUNTING;
			start_pm_wdog(pm, n_state, PM_STATE_UNMOUNTED,
				      PM_EVENT_LINK_UNMOUNTED, LINKPM_WATCHDOG_TIMEOUT);
		} else if (event == PM_EVENT_CP2AP_WAKEUP_HIGH) {
			n_state = PM_STATE_ACTIVE;
			assert_ap2cp_wakeup(pm);
		} else if (event == PM_EVENT_CP_HOLD_REQUEST) {
			n_state = PM_STATE_ACTIVATING;
			assert_ap2cp_wakeup(pm);
			start_pm_wdog(pm, n_state, PM_STATE_ACTIVE,
				      PM_EVENT_CP2AP_WAKEUP_HIGH, LINKPM_WATCHDOG_TIMEOUT);
		}
		break;

	case PM_STATE_ACTIVATING:
		if (event == PM_EVENT_CP2AP_WAKEUP_HIGH) {
			n_state = PM_STATE_ACTIVE;
			stop_pm_wdog(pm, c_state, event);
			assert_ap2cp_wakeup(pm);
		} else if (event == PM_EVENT_CP2AP_STATUS_LOW) {
			n_state = PM_STATE_UNMOUNTING;
			stop_pm_wdog(pm, c_state, event);
			release_ap2cp_wakeup(pm);
		} else if (event == PM_EVENT_WDOG_TIMEOUT) {
			/*
			It is not guaranteed for FSM to succeed in getting GPIO
			interrupt events or for stop_pm_wdog() to succeed in
			deleting the WDOG timer always.
			So, gpio_cp2ap_wakeup and gpio_cp2ap_status must always
			be checked before state transition.
			*/
			if (gpio_get_value(pm->gpio_cp2ap_wakeup))
				n_state = PM_STATE_ACTIVE;
			else if (!gpio_get_value(pm->gpio_cp2ap_status))
				n_state = PM_STATE_UNMOUNTING;
			else
				n_state = PM_STATE_WDOG_TIMEOUT;
		}
		break;

	case PM_STATE_UNMOUNTING:
		if (event == PM_EVENT_LINK_UNMOUNTED) {
			if (pm->hold_requested) {
				if (cp_online(mc))
					n_state = PM_STATE_HOLDING;
				else
					n_state = PM_STATE_UNMOUNTED;
				pm->hold_requested = false;
			} else {
				n_state = PM_STATE_UNMOUNTED;
			}
			stop_pm_wdog(pm, c_state, event);
			release_ap2cp_status(pm);
			if (n_state == PM_STATE_HOLDING) {
				prepare_mount(pm);
				unmounted_to_holding(pm);
			}
		} else if (event == PM_EVENT_WDOG_TIMEOUT) {
			n_state = PM_STATE_WDOG_TIMEOUT;
		}
		break;

	case PM_STATE_CP_BOOTING:
		if (event == PM_EVENT_CP2AP_WAKEUP_HIGH) {
			n_state = PM_STATE_ACTIVE;
			assert_ap2cp_wakeup(pm);
		} else if (event == PM_EVENT_LINK_ERROR) {
			n_state = PM_STATE_CP_FAIL;
		}
		break;

	default:
		break;
	}

	set_pm_fsm(pm, c_state, n_state, event);

#ifdef DEBUG_MODEM_IF
	print_pm_fsm(pm);
#endif

	decide_pm_wake(pm, c_state, n_state);

exit:
	spin_unlock_irqrestore(&pm->lock, flags);

	check_pm_fail(pm, c_state, n_state);
}