コード例 #1
0
/**
@brief		forbid CP from going to sleep

Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the
mem_link_device instance.

@param mld	the pointer to a mem_link_device instance

@remark		CAUTION!!! permit_cp_sleep() MUST be invoked after
		forbid_cp_sleep() success to decrease the "ref_cnt" counter.
*/
static void forbid_cp_sleep(struct mem_link_device *mld)
{
	struct link_device *ld = &mld->link_dev;
	int ap_status = gpio_get_value(mld->gpio_ap_status);
	int cp_wakeup = gpio_get_value(mld->gpio_cp_wakeup);
	unsigned long flags;

	spin_lock_irqsave(&mld->pm_lock, flags);

	atomic_inc(&mld->ref_cnt);

	gpio_set_value(mld->gpio_ap_status, 1);
	gpio_set_value(mld->gpio_cp_wakeup, 1);

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

	spin_unlock_irqrestore(&mld->pm_lock, flags);

	if (!ap_status || !cp_wakeup)
		print_pm_status(mld);

	if (check_link_status(mld) < 0) {
		print_pm_status(mld);
		mif_err("%s: ERR! check_link_status fail\n", ld->name);
		mem_forced_cp_crash(mld);
	}
}
コード例 #2
0
static void stop_pm(struct mem_link_device *mld)
{
	print_pm_status(mld);

	mif_disable_irq(&mld->irq_ap_wakeup);
	mif_disable_irq(&mld->irq_cp_status);
}
コード例 #3
0
static irqreturn_t cp_status_handler(int irq, void *data)
{
	struct mem_link_device *mld = (struct mem_link_device *)data;
	struct link_device *ld = &mld->link_dev;
	struct modem_ctl *mc = ld->mc;
	int cp_status = gpio_get_value(mld->gpio_cp_status);
	unsigned long flags;

	spin_lock_irqsave(&mld->pm_lock, flags);

	change_irq_type(irq, cp_status);

	if (!cp_online(mc))
		goto exit;

	if (cp_status) {
		if (!wake_lock_active(&mld->cp_wlock))
			wake_lock(&mld->cp_wlock);
	} else {
		gpio_set_value(mld->gpio_ap_status, 0);

		if (wake_lock_active(&mld->cp_wlock))
			wake_unlock(&mld->cp_wlock);
	}

exit:
	print_pm_status(mld);
	spin_unlock_irqrestore(&mld->pm_lock, flags);
	return IRQ_HANDLED;
}
コード例 #4
0
static void release_cp_wakeup(struct work_struct *ws)
{
	struct mem_link_device *mld;
	int i;
	unsigned long flags;

	mld = container_of(ws, struct mem_link_device, cp_sleep_dwork.work);

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

	spin_lock_irqsave(&mld->pm_lock, flags);
	i = atomic_read(&mld->ref_cnt);
	spin_unlock_irqrestore(&mld->pm_lock, flags);
	if (i > 0)
		goto reschedule;

	if (gpio_get_value(mld->gpio_ap_wakeup) == 0) {
		gpio_set_value(mld->gpio_cp_wakeup, 0);
		gpio_set_value(mld->gpio_ap_status, 0);
	}

#if 1
	print_pm_status(mld);
#endif

	return;

reschedule:
	queue_delayed_work(system_nrt_wq, &mld->cp_sleep_dwork,
			   msecs_to_jiffies(sleep_timeout));
}
コード例 #5
0
/**
@brief		finalize handling the PHONE_START command from CP

@param mld	the pointer to a mem_link_device instance
*/
static void finalize_cp_start(struct mem_link_device *mld)
{
	int ap_wakeup = gpio_get_value(mld->gpio_ap_wakeup);
	int cp_status = gpio_get_value(mld->gpio_cp_status);

	change_irq_type(mld->irq_ap_wakeup.num, ap_wakeup);
	change_irq_type(mld->irq_cp_status.num, cp_status);

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

	if (cp_status) {
		if (!wake_lock_active(&mld->ap_wlock))
			wake_lock(&mld->cp_wlock);
	} else {
		if (wake_lock_active(&mld->ap_wlock))
			wake_unlock(&mld->cp_wlock);
	}

	print_pm_status(mld);
}
コード例 #6
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_handler(int irq, void *data)
{
	struct mem_link_device *mld = (struct mem_link_device *)data;
	struct link_device *ld = &mld->link_dev;
	int ap_wakeup = gpio_get_value(mld->gpio_ap_wakeup);
	int ap_status = gpio_get_value(mld->gpio_ap_status);

	s5p_change_irq_type(irq, ap_wakeup);

	if (!cp_online(ld->mc))
		goto exit;

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

	print_pm_status(mld);

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

		if (!c2c_suspended() && !ap_status)
			gpio_set_value(mld->gpio_ap_status, 1);
	} else {
		if (wake_lock_active(&mld->ap_wlock))
			wake_unlock(&mld->ap_wlock);

		queue_delayed_work(system_nrt_wq, &mld->cp_sleep_dwork,
				msecs_to_jiffies(CP_WAKEUP_HOLD_TIME));
	}

exit:
	return IRQ_HANDLED;
}
コード例 #7
0
static inline int check_link_status(struct mem_link_device *mld)
{
	unsigned int magic = get_magic(mld);
	int cnt;

	if (gpio_get_value(mld->gpio_cp_status) != 0 && magic == MEM_IPC_MAGIC)
		return 0;

	cnt = 0;
	while (gpio_get_value(mld->gpio_cp_status) == 0) {
		if (gpio_get_value(mld->gpio_ap_status) == 0) {
			print_pm_status(mld);
			gpio_set_value(mld->gpio_ap_status, 1);
		}

		cnt++;
		if (cnt >= 100) {
			mif_err("ERR! cp_status != 1 (cnt %d)\n", cnt);
			return -EACCES;
		}

		if (in_interrupt())
			udelay(100);
		else
			usleep_range(100, 200);
	}

	cnt = 0;
	while (1) {
		magic = get_magic(mld);
		if (magic == MEM_IPC_MAGIC)
			break;

		cnt++;
		if (cnt >= 100) {
			mif_err("ERR! magic 0x%X != IPC_MAGIC (cnt %d)\n",
				magic, cnt);
			return -EACCES;
		}

		if (in_interrupt())
			udelay(100);
		else
			usleep_range(100, 200);
	}

	return 0;
}
コード例 #8
0
/**
@brief	interrupt handler for a MIPI-LLI IPC interrupt

1) Get a free mst buffer\n
2) Reads the RXQ status and saves the status to the mst buffer\n
3) Saves the interrupt value to the mst buffer\n
4) Invokes mem_irq_handler that is common to all memory-type interfaces\n

@param data	the pointer to a mem_link_device instance
@param intr	the interrupt value
*/
static void lli_irq_handler(void *data, u32 intr)
{
	struct mem_link_device *mld = (struct mem_link_device *)data;
	struct mst_buff *msb;

	/* Prohibit CP from going to sleep */
	if (gpio_get_value(mld->gpio_cp_status) == 0
	    || gpio_get_value(mld->gpio_ap_status) == 0)
		print_pm_status(mld);

	msb = mem_take_snapshot(mld, RX);
	if (!msb)
		return;
	msb->snapshot.int2ap = (u16)intr;

	mem_irq_handler(mld, msb);
}
コード例 #9
0
static void start_pm(struct mem_link_device *mld)
{
    if (pm_enable) {
        int ap_wakeup = gpio_get_value(mld->gpio_ap_wakeup);
        int cp_status = gpio_get_value(mld->gpio_cp_status);

        print_pm_status(mld);

        change_irq_type(mld->irq_ap_wakeup.num, ap_wakeup);
        mif_enable_irq(&mld->irq_ap_wakeup);

        change_irq_type(mld->irq_cp_status.num, cp_status);
        mif_enable_irq(&mld->irq_cp_status);
    } else {
        wake_lock(&mld->ap_wlock);
    }
}
コード例 #10
0
static irqreturn_t cp_status_handler(int irq, void *data)
{
	struct mem_link_device *mld = (struct mem_link_device *)data;
	struct link_device *ld = &mld->link_dev;
	int cp_status = gpio_get_value(mld->gpio_cp_status);
	unsigned long flags;

	spin_lock_irqsave(&mld->pm_lock, flags);

	s5p_change_irq_type(irq, cp_status);

	if (!cp_online(ld->mc))
		goto exit;

	print_pm_status(mld);

	if (cp_status) {
		if (!wake_lock_active(&mld->cp_wlock))
			wake_lock(&mld->cp_wlock);
	} else {
		if (atomic_read(&mld->ref_cnt) > 0) {
			/*
			** This status means that IPC TX is in progress from AP
			** to CP. So, CP_WAKEUP must be set to 1. Otherwise, it
			** is a critically erroneous status.
			*/
			if (gpio_get_value(mld->gpio_cp_wakeup) == 0) {
				mif_err("%s: ERR! cp_wakeup == 0\n", ld->name);
				goto exit;
			}
			/* CP_STATUS will be reset to 1 soon due to CP_WAKEUP.*/
		} else {
			gpio_set_value(mld->gpio_ap_status, 0);

			if (wake_lock_active(&mld->cp_wlock))
				wake_unlock(&mld->cp_wlock);
		}
	}

exit:
	spin_unlock_irqrestore(&mld->pm_lock, flags);
	return IRQ_HANDLED;
}
コード例 #11
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;
}
コード例 #12
0
/**
@brief		forbid CP from going to sleep

Wakes up a CP if it can sleep and increases the "ref_cnt" counter in the
mem_link_device instance.

@param mld	the pointer to a mem_link_device instance

@remark		CAUTION!!! permit_cp_sleep() MUST be invoked after
		forbid_cp_sleep() success to decrease the "ref_cnt" counter.
*/
static void forbid_cp_sleep(struct mem_link_device *mld)
{
	int ref_cnt;
	unsigned long flags;
	int cp_wakeup;

	spin_lock_irqsave(&mld->pm_lock, flags);

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

	ref_cnt = atomic_inc_return(&mld->ref_cnt);
	mif_debug("ref_cnt %d\n", ref_cnt);

	cp_wakeup = gpio_get_value(mld->gpio_cp_wakeup);
	gpio_set_value(mld->gpio_cp_wakeup, 1);

	if (cp_wakeup == 0)
		print_pm_status(mld);

	spin_unlock_irqrestore(&mld->pm_lock, flags);
}
コード例 #13
0
static void release_cp_wakeup(struct work_struct *ws)
{
	struct mem_link_device *mld;
	struct link_device *ld;
	int i;
	unsigned long flags;

	mld = container_of(ws, struct mem_link_device, cp_sleep_dwork.work);

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

	spin_lock_irqsave(&mld->pm_lock, flags);
	i = atomic_read(&mld->ref_cnt);
	spin_unlock_irqrestore(&mld->pm_lock, flags);
	if (i > 0)
		goto reschedule;

	/*
	 * If there is any IPC message remained in a TXQ, AP must prevent CP
	 * from going to sleep.
	 */
	ld = &mld->link_dev;
	for (i = 0; i < MAX_SIPC5_DEVICES; i++) {
		if (ld->skb_txq[i]->qlen > 0)
			goto reschedule;
	}

	if (gpio_get_value(mld->gpio_ap_wakeup))
		goto reschedule;

	gpio_set_value(mld->gpio_cp_wakeup, 0);
	print_pm_status(mld);
	return;

reschedule:
	queue_delayed_work(system_nrt_wq, &mld->cp_sleep_dwork,
			   msecs_to_jiffies(CP_WAKEUP_HOLD_TIME));
}
コード例 #14
0
static int init_pm(struct mem_link_device *mld)
{
	int err;
	unsigned int gpio;
	unsigned int irq_ap_wakeup;
	unsigned int irq_cp_status;
	unsigned long flags;

	gpio_set_value(mld->gpio_ap_status, 0);

	/*
	Retrieve GPIO#, IRQ#, and IRQ flags for PM
	*/
	gpio = mld->gpio_ap_wakeup;
	irq_ap_wakeup = gpio_to_irq(gpio);
	mif_err("CP2AP_WAKEUP GPIO:%d IRQ:%d\n", gpio, irq_ap_wakeup);

	gpio = mld->gpio_cp_wakeup;
	mif_err("AP2CP_WAKEUP GPIO:%d\n", gpio);

	gpio = mld->gpio_cp_status;
	irq_cp_status = gpio_to_irq(gpio);
	mif_err("CP2AP_STATUS GPIO:%d IRQ:%d\n", gpio, irq_cp_status);

	gpio = mld->gpio_ap_status;
	mif_err("AP2CP_STATUS GPIO:%d\n", gpio);

	/*
	Initialize locks, completions, bottom halves, etc.
	*/
	wake_lock_init(&mld->ap_wlock, WAKE_LOCK_SUSPEND, "lli_ap_wlock");

	wake_lock_init(&mld->cp_wlock, WAKE_LOCK_SUSPEND, "lli_cp_wlock");

	INIT_DELAYED_WORK(&mld->cp_sleep_dwork, release_cp_wakeup);

	spin_lock_init(&mld->pm_lock);
	spin_lock_init(&mld->sig_lock);
	atomic_set(&mld->ref_cnt, 0);

	/*
	Enable IRQs for PM
	*/
	print_pm_status(mld);

	flags = (IRQF_TRIGGER_HIGH | IRQF_ONESHOT);

	mif_init_irq(&mld->irq_ap_wakeup, irq_ap_wakeup,
		     "lli_cp2ap_wakeup", flags);
	err = mif_request_irq(&mld->irq_ap_wakeup, ap_wakeup_interrupt, mld);
	if (err)
		return err;
	mif_disable_irq(&mld->irq_ap_wakeup);

	mif_init_irq(&mld->irq_cp_status, irq_cp_status,
		     "lli_cp2ap_status", flags);
	err = mif_request_irq(&mld->irq_cp_status, cp_status_handler, mld);
	if (err)
		return err;
	mif_disable_irq(&mld->irq_cp_status);

	return 0;
}