static void start_link_pm(struct modem_link_pm *pm, enum pm_event event)
{
	int cp2ap_wakeup;
	int cp2ap_status;
	enum pm_state state;
	unsigned long flags;

	spin_lock_irqsave(&pm->lock, flags);

	if (pm->active) {
		mif_err("%s: PM is already ACTIVE\n", pm->link_name);
		goto exit;
	}

	lock_pm_wake(pm);

	if (event == PM_EVENT_LOCK_ON) {
		state = PM_STATE_LOCKED_ON;
		assert_ap2cp_wakeup(pm);
		assert_ap2cp_status(pm);
	} else if (event == PM_EVENT_CP_BOOTING) {
		state = PM_STATE_CP_BOOTING;
		assert_ap2cp_wakeup(pm);
		assert_ap2cp_status(pm);
	} else {
		state = PM_STATE_UNMOUNTED;
		release_ap2cp_wakeup(pm);
		release_ap2cp_status(pm);
	}

	/*
	Enable every CP-to-AP IRQ and set it as a wake-up source
	*/
	cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup);
	change_irq_level(pm->cp2ap_wakeup_irq.num, cp2ap_wakeup);
	mif_enable_irq(&pm->cp2ap_wakeup_irq);

	cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status);
	change_irq_level(pm->cp2ap_status_irq.num, cp2ap_status);
	mif_enable_irq(&pm->cp2ap_status_irq);

	set_pm_fsm(pm, PM_STATE_UNMOUNTED, state, event);

	pm->hold_requested = false;

	pm->active = true;

#ifdef DEBUG_MODEM_IF
	print_pm_fsm(pm);
#endif

exit:
	spin_unlock_irqrestore(&pm->lock, flags);
}
示例#2
0
static irqreturn_t cp2ap_status_handler(int irq, void *data)
{
	struct modem_link_pm *pm = (struct modem_link_pm *)data;
	int cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status);

	if (cp2ap_status)
		run_pm_fsm(pm, PM_EVENT_CP2AP_STATUS_HIGH);
	else
		run_pm_fsm(pm, PM_EVENT_CP2AP_STATUS_LOW);

	change_irq_level(irq, cp2ap_status);

	return IRQ_HANDLED;
}
/**
@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 cp2ap_wakeup_handler(int irq, void *data)
{
	struct modem_link_pm *pm = (struct modem_link_pm *)data;
	int cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup);

	mif_debug("%s: cp2ap_wakeup[%d]\n", __func__, cp2ap_wakeup);
	if (cp2ap_wakeup) {
		run_pm_fsm(pm, PM_EVENT_CP2AP_WAKEUP_HIGH);
	}
	else {
		run_pm_fsm(pm, PM_EVENT_CP2AP_WAKEUP_LOW);
	}

	change_irq_level(irq, cp2ap_wakeup);

	return IRQ_HANDLED;
}
示例#4
0
int init_link_device_pm(struct link_device *ld,
			struct modem_link_pm *pm,
			struct link_pm_svc *pm_svc,
			void (*fail_fn)(struct modem_link_pm *),
			void (*cp_fail_fn)(struct modem_link_pm *))
{
	struct modem_data *mdata = ld->mdm_data;
	int err;
	int cp2ap_wakeup;
	int cp2ap_status;
	unsigned int num;
	unsigned long flags;
	char name[MAX_NAME_LEN];

	/*
	Set up variables for PM
	*/
	pm->link_name = ld->name;
	pm->fail_handler = fail_fn;
	pm->cp_fail_handler = cp_fail_fn;

	/*
	Retrieve GPIO pins and IRQ numbers for PM
	*/
	pm->gpio_cp2ap_wakeup = mdata->gpio_ap_wakeup;
	pm->gpio_ap2cp_wakeup = mdata->gpio_cp_wakeup;
	pm->gpio_cp2ap_status = mdata->gpio_cp_status;
	pm->gpio_ap2cp_status = mdata->gpio_ap_status;

	num = gpio_to_irq(pm->gpio_cp2ap_wakeup);
	flags = IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_ONESHOT;
	snprintf(name, MAX_NAME_LEN, "%s_cp2ap_wakeup", pm->link_name);
	mif_init_irq(&pm->cp2ap_wakeup_irq, num, name, flags);

	num = gpio_to_irq(pm->gpio_cp2ap_status);
	flags = IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_ONESHOT;
	snprintf(name, MAX_NAME_LEN, "%s_cp2ap_status", pm->link_name);
	mif_init_irq(&pm->cp2ap_status_irq, num, name, flags);

	mif_err("CP2AP_WAKEUP GPIO:%d IRQ:%d\n",
		pm->gpio_cp2ap_wakeup, pm->cp2ap_wakeup_irq.num);

	mif_err("AP2CP_WAKEUP GPIO:%d\n", pm->gpio_ap2cp_wakeup);

	mif_err("CP2AP_STATUS GPIO:%d IRQ:%d\n",
		pm->gpio_cp2ap_status, pm->cp2ap_status_irq.num);

	mif_err("AP2CP_STATUS GPIO:%d\n", pm->gpio_ap2cp_status);

	/*
	Register cp2ap_wakeup IRQ handler
	*/
	cp2ap_wakeup = gpio_get_value(pm->gpio_cp2ap_wakeup);
	change_irq_level(pm->cp2ap_wakeup_irq.num, cp2ap_wakeup);

	err = mif_request_irq(&pm->cp2ap_wakeup_irq, cp2ap_wakeup_handler, pm);
	if (err) {
		mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n",
			pm->link_name, pm->cp2ap_wakeup_irq.name,
			pm->cp2ap_wakeup_irq.num, err);
		return err;
	}
	mif_disable_irq(&pm->cp2ap_wakeup_irq);

	mif_err("%s: %s_irq#%d handler registered\n", pm->link_name,
		pm->cp2ap_wakeup_irq.name, pm->cp2ap_wakeup_irq.num);

	/*
	Register cp2ap_status IRQ handler
	*/
	cp2ap_status = gpio_get_value(pm->gpio_cp2ap_status);
	change_irq_level(pm->cp2ap_status_irq.num, cp2ap_status);

	err = mif_request_irq(&pm->cp2ap_status_irq, cp2ap_status_handler, pm);
	if (err) {
		mif_err("%s: ERR! request_irq(%s#%d) fail (%d)\n",
			pm->link_name, pm->cp2ap_status_irq.name,
			pm->cp2ap_status_irq.num, err);
		free_irq(pm->cp2ap_wakeup_irq.num, pm);
		return err;
	}
	mif_disable_irq(&pm->cp2ap_status_irq);

	mif_err("%s: %s_irq#%d handler registered\n", pm->link_name,
		pm->cp2ap_status_irq.name, pm->cp2ap_status_irq.num);

	/*
	Initialize common variables for PM
	*/
	spin_lock_init(&pm->lock);

	snprintf(pm->wlock_name, MAX_NAME_LEN, "%s_pm_wlock", pm->link_name);
	wake_lock_init(&pm->wlock, WAKE_LOCK_SUSPEND, pm->wlock_name);

	snprintf(pm->wq_name, MAX_NAME_LEN, "%s_pm_wq", pm->link_name);
	flags = WQ_NON_REENTRANT | WQ_UNBOUND | WQ_HIGHPRI;
	pm->wq = alloc_workqueue(pm->wq_name, flags, 1);
	if (!pm->wq) {
		mif_err("%s: ERR! fail to create %s\n",
			pm->link_name, pm->wq_name);
		return -EFAULT;
	}

	INIT_DELAYED_WORK(&pm->cp_free_dwork, cp_free_work_func);

	init_pm_fsm(pm);

	/*
	Register PM functions set by the common link PM framework and used by
	each link device driver
	*/
	pm->start = start_link_pm;
	pm->stop = stop_link_pm;
	pm->request_hold = request_hold;
	pm->release_hold = release_hold;
	pm->link_active = link_active;

	return 0;
}