/**
 * hsi_do_cawake_process - CAWAKE line management
 * @pport - HSI port to process
 *
 * This function handles the CAWAKE L/H transitions and call the event callback
 * accordingly.
 *
 * Returns 0 if CAWAKE event process, -EAGAIN if CAWAKE event processing is
 * delayed due to a pending DMA interrupt.
 * If -EAGAIN is returned, pport->hsi_tasklet has to be re-scheduled once
 * DMA tasklet has be executed. This should be done automatically by driver.
 *
*/
int hsi_do_cawake_process(struct hsi_port *pport)
{
	struct hsi_dev *hsi_ctrl = pport->hsi_controller;
	bool cawake_status = hsi_get_cawake(pport);

	if (pport->wake_rx_3_wires_mode) {
		dev_warn(hsi_ctrl->dev, "CAWAKE edge in RX 3 wires, exiting\n");
		return 0;
	}

	/* Deal with init condition */
	if (unlikely(pport->cawake_status < 0))
		pport->cawake_status = !cawake_status;
	dev_dbg(hsi_ctrl->dev, "%s: Interrupts are not enabled but CAWAKE came."
		"hsi: port[%d] irq[%d] irq_en=0x%08x dma_irq_en=0x%08x\n",
		__func__, pport->port_number, pport->n_irq,
		hsi_inl(pport->hsi_controller->base,
			HSI_SYS_MPU_ENABLE_REG(pport->port_number,
					pport->n_irq)),
		hsi_inl(pport->hsi_controller->base,
			HSI_SYS_GDD_MPU_IRQ_ENABLE_REG));

	/* Check CAWAKE line status */
	if (cawake_status) {
		dev_dbg(hsi_ctrl->dev, "CAWAKE rising edge detected\n");

		/* Check for possible mismatch (race condition) */
		if (unlikely(pport->cawake_status)) {
			dev_warn(hsi_ctrl->dev,
				"Missed previous CAWAKE falling edge...\n");
			spin_unlock(&hsi_ctrl->lock);
			hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN,
						NULL);
			spin_lock(&hsi_ctrl->lock);

			/* In case another CAWAKE interrupt occured and caused
			 * a race condition, clear CAWAKE backup interrupt to
			 * avoid handling twice the race condition */
			hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
						 true);
		}
		pport->cawake_status = 1;

		spin_unlock(&hsi_ctrl->lock);
		hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL);
		spin_lock(&hsi_ctrl->lock);

		/*
		* HSI - OMAP4430-2.2BUG00055: i702
		* HSI: DSP Swakeup generated is the same than MPU Swakeup.
		* System cannot enter in off mode due to the DSP.
		*/
		if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i702_PM_HSI_SWAKEUP))
			omap_pm_clear_dsp_wake_up();

	} else {
		dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n");

		/* Check for pending DMA interrupt */
		if (hsi_is_dma_read_int_pending(hsi_ctrl)) {
			dev_dbg(hsi_ctrl->dev, "Pending DMA Read interrupt "
					       "before CAWAKE->L, exiting "
					       "Interrupt tasklet.\n");
			return -EAGAIN;
		}
		if (unlikely(!pport->cawake_status)) {
			dev_warn(hsi_ctrl->dev,
				"Missed previous CAWAKE rising edge...\n");
			spin_unlock(&hsi_ctrl->lock);
			hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP,
						NULL);
			spin_lock(&hsi_ctrl->lock);

			/* In case another CAWAKE interrupt occured and caused
			 * a race condition, clear CAWAKE backup interrupt to
			 * avoid handling twice the race condition */
			hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
						 true);
		}
		pport->cawake_status = 0;

		spin_unlock(&hsi_ctrl->lock);
		hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL);
		spin_lock(&hsi_ctrl->lock);
	}

	/* If another CAWAKE event occured while previous is still processed */
	/* do not clear the status bit */
	cawake_status = hsi_get_cawake(pport);
	if (cawake_status != pport->cawake_status) {
		dev_warn(hsi_ctrl->dev, "CAWAKE line changed to %d while CAWAKE"
					"event is still being processed\n",
					cawake_status);
		return -EAGAIN;
	}

	return 0;
}
/**
 * hsi_do_cawake_process - CAWAKE line management
 * @pport - HSI port to process
 *
 * This function handles the CAWAKE L/H transitions and call the event callback
 * accordingly.
 *
 * Returns 0 if CAWAKE event process, -EAGAIN if CAWAKE event processing is
 * delayed due to a pending DMA interrupt.
 * If -EAGAIN is returned, pport->hsi_tasklet has to be re-scheduled once
 * DMA tasklet has be executed. This should be done automatically by driver.
 *
*/
int hsi_do_cawake_process(struct hsi_port *pport)
{
	struct hsi_dev *hsi_ctrl = pport->hsi_controller;
	bool cawake_status = hsi_get_cawake(pport);

	if (pport->wake_rx_3_wires_mode) {
		dev_warn(hsi_ctrl->dev, "CAWAKE edge in RX 3 wires, exiting\n");
		return 0;
	}

	/* Deal with init condition */
	if (unlikely(pport->cawake_status < 0))
		pport->cawake_status = !cawake_status;

	/* Check CAWAKE line status */
	if (cawake_status) {
		dev_dbg(hsi_ctrl->dev, "CAWAKE rising edge detected\n");

		/* Check for possible mismatch (race condition) */
		if (unlikely(pport->cawake_status)) {
			dev_warn(hsi_ctrl->dev,
				"Missed previous CAWAKE falling edge...\n");
			spin_unlock(&hsi_ctrl->lock);
			hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN,
						NULL);
			spin_lock(&hsi_ctrl->lock);

			/* In case another CAWAKE interrupt occured and caused
			 * a race condition, clear CAWAKE backup interrupt to
			 * avoid handling twice the race condition */
			hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
						 true);
		}
		pport->cawake_status = 1;

		/* Allow data reception */
		hsi_hsr_resume(hsi_ctrl);

		spin_unlock(&hsi_ctrl->lock);
		hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL);
		spin_lock(&hsi_ctrl->lock);

		/*
		* HSI - OMAP4430-2.2BUG00055: i702
		* HSI: DSP Swakeup generated is the same than MPU Swakeup.
		* System cannot enter in off mode due to the DSP.
		*/
		if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i702_PM_HSI_SWAKEUP))
			omap_pm_clear_dsp_wake_up();

	} else {
		dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n");

		/* Check for pending DMA interrupt */
		if (hsi_is_dma_read_int_pending(hsi_ctrl)) {
			dev_dbg(hsi_ctrl->dev, "Pending DMA Read interrupt "
					       "before CAWAKE->L, exiting "
					       "Interrupt tasklet.\n");
			return -EAGAIN;
		}
		if (unlikely(!pport->cawake_status)) {
			dev_warn(hsi_ctrl->dev,
				"Missed previous CAWAKE rising edge...\n");
			spin_unlock(&hsi_ctrl->lock);
			hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP,
						NULL);
			spin_lock(&hsi_ctrl->lock);

			/* In case another CAWAKE interrupt occured and caused
			 * a race condition, clear CAWAKE backup interrupt to
			 * avoid handling twice the race condition */
			hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
						 true);
		}
		pport->cawake_status = 0;

		/* Forbid data reception */
		hsi_hsr_suspend(hsi_ctrl);

		spin_unlock(&hsi_ctrl->lock);
		hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL);
		spin_lock(&hsi_ctrl->lock);
	}

	return 0;
}