Пример #1
0
static void do_hsi_cawake_tasklet(unsigned long hsi_p)
{
    struct hsi_port *port = (struct hsi_port *)hsi_p;
    struct hsi_dev *hsi_ctrl = port->hsi_controller;

    spin_lock(&hsi_ctrl->lock);
    hsi_clocks_enable(hsi_ctrl->dev, __func__);
    port->in_cawake_tasklet = true;

    port->cawake_status = hsi_get_cawake(port);
    hsi_do_cawake_process(port);
    port->in_cawake_tasklet = false;
    hsi_clocks_disable(hsi_ctrl->dev, __func__);
    spin_unlock(&hsi_ctrl->lock);
}
/* Check if a HSI port is busy :
 * - ACWAKE is high
 * - data transfer (Write) is ongoing for a given HSI channel
 * - CAWAKE is high
 * - CAWAKE is not used (receiver in 3-wires mode)
 * - Currently in HSI interrupt tasklet
 * - Currently in HSI CAWAKE tasklet (for SSI)
 */
bool hsi_is_hsi_port_busy(struct hsi_port *pport)
{
	struct hsi_dev *hsi_ctrl = pport->hsi_controller;
	bool cur_cawake = hsi_get_cawake(pport);
	int ch;

	if (pport->in_int_tasklet) {
		dev_dbg(hsi_ctrl->dev, "Interrupt tasklet running\n");
		return true;
	}

	if (pport->in_cawake_tasklet) {
		dev_dbg(hsi_ctrl->dev, "SSI Cawake tasklet running\n");
		return true;
	}

	if (pport->wake_rx_3_wires_mode) {
		dev_dbg(hsi_ctrl->dev, "Receiver Port %d in 3 wires mode,"
			"acwake_status %d\n", pport->port_number,
			pport->acwake_status);
		return true;
	}

	if (cur_cawake || pport->acwake_status) {
		dev_dbg(hsi_ctrl->dev, "Port %d: WAKE status: acwake_status %d,"
			"cur_cawake %d", pport->port_number,
			pport->acwake_status, cur_cawake);
		return true;
	}

	for (ch = 0; ch < pport->max_ch; ch++)
		if (hsi_is_channel_busy(&pport->hsi_channel[ch])) {
			dev_dbg(hsi_ctrl->dev, "Port %d; channel %d "
				"busy\n", pport->port_number, ch);
			return true;
		}

	return false;
}
Пример #3
0
/**
 * hsi_ioctl - HSI I/O control
 * @dev - hsi device channel reference to apply the I/O control
 *						(or port associated to it)
 * @command - HSI I/O control command
 * @arg - parameter associated to the control command. NULL, if no parameter.
 *
 * Return 0 on success, a negative value on failure.
 *
 */
int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
{
	struct hsi_channel *ch;
	struct hsi_dev *hsi_ctrl;
	struct hsi_port *pport;
	void __iomem *base;
	unsigned int port, channel;
	u32 acwake;
	int err = 0;
	int fifo = 0;
	u8 ret;
	struct hsi_platform_data *pdata;

	if (unlikely((!dev) ||
		     (!dev->ch) ||
		     (!dev->ch->hsi_port) ||
		     (!dev->ch->hsi_port->hsi_controller)) ||
	    (!(dev->ch->flags & HSI_CH_OPEN))) {
		pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n");
		return -EINVAL;
	}

	ch = dev->ch;
	pport = ch->hsi_port;
	hsi_ctrl = ch->hsi_port->hsi_controller;
	port = ch->hsi_port->port_number;
	channel = ch->channel_number;
	base = hsi_ctrl->base;

	dev_dbg(hsi_ctrl->dev, "IOCTL: ch %d, command %d\n", channel, command);

	spin_lock_bh(&hsi_ctrl->lock);
	hsi_clocks_enable_channel(hsi_ctrl->dev, channel, __func__);

	switch (command) {
	case HSI_IOCTL_ACWAKE_UP:
		/* Wake up request to Modem (typically OMAP initiated) */
		/* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */
		if (ch->flags & HSI_CH_ACWAKE) {
			dev_dbg(hsi_ctrl->dev, "Duplicate ACWAKE UP\n");
			err = -EPERM;
			goto out;
		}

		ch->flags |= HSI_CH_ACWAKE;
		pport->acwake_status |= BIT(channel);

		/* We only claim once the wake line per channel */
		acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		if (!(acwake & HSI_WAKE(channel))) {
			hsi_outl(HSI_SET_WAKE(channel), base,
				 HSI_SYS_SET_WAKE_REG(port));
		}

		goto out;
		break;
	case HSI_IOCTL_ACWAKE_DOWN:
		/* Low power request initiation (OMAP initiated, typically */
		/* following inactivity timeout) */
		/* ACPU HSI block shall still be capable of receiving */
		if (!(ch->flags & HSI_CH_ACWAKE)) {
			dev_dbg(hsi_ctrl->dev, "Duplicate ACWAKE DOWN\n");
			err = -EPERM;
			goto out;
		}

		acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		if (unlikely(pport->acwake_status !=
				(acwake & HSI_WAKE_MASK))) {
			dev_warn(hsi_ctrl->dev,
				"ACWAKE shadow register mismatch"
				" acwake_status: 0x%x, HSI_SYS_WAKE_REG: 0x%x",
				pport->acwake_status, acwake);
			pport->acwake_status = acwake & HSI_WAKE_MASK;
		}
		/* SSI_TODO: add safety check for SSI also */

		ch->flags &= ~HSI_CH_ACWAKE;
		pport->acwake_status &= ~BIT(channel);

		/* Release the wake line per channel */
		if ((acwake & HSI_WAKE(channel))) {
			hsi_outl(HSI_CLEAR_WAKE(channel), base,
				 HSI_SYS_CLEAR_WAKE_REG(port));
		}

		goto out;
		break;
	case HSI_IOCTL_SEND_BREAK:
		hsi_outl(1, base, HSI_HST_BREAK_REG(port));
		/*HSI_TODO : need to deactivate clock after BREAK frames sent*/
		/*Use interrupt ? (if TX BREAK INT exists)*/
		break;
	case HSI_IOCTL_GET_ACWAKE:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		*(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		break;
	case HSI_IOCTL_FLUSH_RX:
		ret = hsi_hsr_fifo_flush_channel(hsi_ctrl, port, channel);
		if (arg)
			*(size_t *)arg = ret;

		/* Ack the RX Int */
		hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base,
			     HSI_SYS_MPU_STATUS_CH_REG(port, pport->n_irq,
						       channel));
		break;
	case HSI_IOCTL_FLUSH_TX:
		ret = hsi_hst_fifo_flush_channel(hsi_ctrl, port, channel);
		if (arg)
			*(size_t *)arg = ret;

		/* Ack the TX Int */
		hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base,
			     HSI_SYS_MPU_STATUS_CH_REG(port, pport->n_irq,
						       channel));
		break;
	case HSI_IOCTL_GET_CAWAKE:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_get_cawake(dev->ch->hsi_port);
		if (err < 0) {
			err = -ENODEV;
			goto out;
		}
		*(u32 *)arg = err;
		break;
	case HSI_IOCTL_SET_RX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
		break;
	case HSI_IOCTL_GET_RX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
		break;
	case HSI_IOCTL_SET_TX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
		break;
	case HSI_IOCTL_GET_TX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
		break;
	case HSI_IOCTL_SW_RESET:
		dev_info(hsi_ctrl->dev, "SW Reset\n");
		err = hsi_softreset(hsi_ctrl);

		/* Reset HSI config to default */
		hsi_softreset_driver(hsi_ctrl);
		break;
	case HSI_IOCTL_GET_FIFO_OCCUPANCY:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
		if (unlikely(fifo < 0)) {
			dev_err(hsi_ctrl->dev, "No valid FIFO id found for "
					       "channel %d.\n", channel);
			err = -EFAULT;
			goto out;
		}
		*(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo);
		break;
	case HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE:
		dev_info(hsi_ctrl->dev,
			 "Entering RX wakeup in 3 wires mode (no CAWAKE)\n");
		pport->wake_rx_3_wires_mode = 1;

		/* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
		 * HSI will NOT generate the Swakeup for 2nd frame if it entered
		 * IDLE after 1st received frame */
		if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
			if (hsi_driver_device_is_hsi(to_platform_device
							(hsi_ctrl->dev)))
				hsi_set_pm_force_hsi_on(hsi_ctrl);

		/* When WAKE is not available, ACREADY must be set to 1 at
		 * reset else remote will never have a chance to transmit. */
		hsi_outl_or(HSI_SET_WAKE_3_WIRES | HSI_SET_WAKE_READY_LVL_1,
			    base, HSI_SYS_SET_WAKE_REG(port));
		hsi_driver_disable_interrupt(pport, HSI_CAWAKEDETECTED);
		break;
	case HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE:
		dev_info(hsi_ctrl->dev, "Entering RX wakeup in 4 wires mode\n");
		pport->wake_rx_3_wires_mode = 0;

		/* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
		 * HSI will NOT generate the Swakeup for 2nd frame if it entered
		 * IDLE after 1st received frame */
		if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
			if (hsi_driver_device_is_hsi(to_platform_device
							(hsi_ctrl->dev)))
				hsi_set_pm_default(hsi_ctrl);

		hsi_driver_enable_interrupt(pport, HSI_CAWAKEDETECTED);
		hsi_outl_and(HSI_SET_WAKE_3_WIRES_MASK,	base,
			     HSI_SYS_SET_WAKE_REG(port));
		break;
	case HSI_IOCTL_SET_HI_SPEED:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		hsi_ctrl->hsi_fclk_req = *(unsigned int *)arg ?
					HSI_FCLK_HI_SPEED : HSI_FCLK_LOW_SPEED;

		if (hsi_ctrl->hsi_fclk_req == hsi_ctrl->hsi_fclk_current) {
			dev_dbg(hsi_ctrl->dev, "HSI FClk already @%ldHz\n",
				 hsi_ctrl->hsi_fclk_current);
			goto out;
		}

		if (hsi_is_controller_transfer_ongoing(hsi_ctrl)) {
			err = -EBUSY;
			goto out;
		}
		hsi_ctrl->clock_change_ongoing = true;
		spin_unlock_bh(&hsi_ctrl->lock);

		pdata = dev_get_platdata(hsi_ctrl->dev);

		/* Set the HSI FCLK to requested value. */
		err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
					  hsi_ctrl->hsi_fclk_req);
		if (err < 0) {
			dev_err(hsi_ctrl->dev, "%s: Cannot set HSI FClk to"
				" %ldHz, err %d\n", __func__,
				hsi_ctrl->hsi_fclk_req, err);
		} else {
			dev_info(hsi_ctrl->dev, "HSI FClk changed from %ldHz to"
				 " %ldHz\n", hsi_ctrl->hsi_fclk_current,
				 hsi_ctrl->hsi_fclk_req);
			hsi_ctrl->hsi_fclk_current = hsi_ctrl->hsi_fclk_req;
		}

		spin_lock_bh(&hsi_ctrl->lock);
		hsi_ctrl->clock_change_ongoing = false;

		break;
	case HSI_IOCTL_GET_SPEED:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}

		*(unsigned long *)arg = hsi_ctrl->hsi_fclk_current;
		break;

	default:
		err = -ENOIOCTLCMD;
		break;
	}
out:
	/* All IOCTL end by disabling the clocks, except ACWAKE high. */
	hsi_clocks_disable_channel(hsi_ctrl->dev, channel, __func__);

	spin_unlock_bh(&hsi_ctrl->lock);

	return err;
}
Пример #4
0
/**
 * hsi_ioctl - HSI I/O control
 * @dev - hsi device channel reference to apply the I/O control
 *						(or port associated to it)
 * @command - HSI I/O control command
 * @arg - parameter associated to the control command. NULL, if no parameter.
 *
 * Return 0 on success, a negative value on failure.
 *
 */
int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
{
	struct hsi_channel *ch;
	struct hsi_dev *hsi_ctrl;
	struct hsi_port *pport;
	void __iomem *base;
	unsigned int port, channel;
	u32 acwake;
	int err = 0;
	int fifo = 0;

	if (unlikely((!dev) ||
		     (!dev->ch) ||
		     (!dev->ch->hsi_port) ||
		     (!dev->ch->hsi_port->hsi_controller)) ||
	    (!(dev->ch->flags & HSI_CH_OPEN))) {
		pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n");
		return -EINVAL;
	}

	ch = dev->ch;
	pport = ch->hsi_port;
	hsi_ctrl = ch->hsi_port->hsi_controller;
	port = ch->hsi_port->port_number;
	channel = ch->channel_number;
	base = hsi_ctrl->base;

	dev_dbg(dev->device.parent, "IOCTL: ch %d, command %d\n",
		channel, command);

	spin_lock_bh(&hsi_ctrl->lock);
	hsi_clocks_enable_channel(dev->device.parent, channel, __func__);

	switch (command) {
	case HSI_IOCTL_ACWAKE_UP:
		if (ch->flags & HSI_CH_ACWAKE) {
			dev_dbg(dev->device.parent, "Duplicate ACWAKE UP\n");
			err = -EPERM;
			goto out;
		}

		/* Wake up request to Modem (typically OMAP initiated) */
		/* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */

		ch->flags |= HSI_CH_ACWAKE;
		pport->acwake_status |= BIT(channel);

		/* We only claim once the wake line per channel */
		acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		if (!(acwake & HSI_WAKE(channel))) {
			hsi_outl(HSI_SET_WAKE(channel), base,
				 HSI_SYS_SET_WAKE_REG(port));
		}

		goto out;
		break;
	case HSI_IOCTL_ACWAKE_DOWN:
		/* Low power request initiation (OMAP initiated, typically */
		/* following inactivity timeout) */
		/* ACPU HSI block shall still be capable of receiving */
		if (!(ch->flags & HSI_CH_ACWAKE)) {
			dev_dbg(dev->device.parent, "Duplicate ACWAKE DOWN\n");
			err = -EPERM;
			goto out;
		}

		acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		if (unlikely(pport->acwake_status !=
				(acwake & HSI_WAKE_MASK))) {
			dev_warn(dev->device.parent,
				"ACWAKE shadow register mismatch"
				" acwake_status: 0x%x, HSI_SYS_WAKE_REG: 0x%x",
				pport->acwake_status, acwake);
			pport->acwake_status = acwake & HSI_WAKE_MASK;
		}
		/* SSI_TODO: add safety check for SSI also */

		ch->flags &= ~HSI_CH_ACWAKE;
		pport->acwake_status &= ~BIT(channel);

		/* Release the wake line per channel */
		if ((acwake & HSI_WAKE(channel))) {
			hsi_outl(HSI_CLEAR_WAKE(channel), base,
				 HSI_SYS_CLEAR_WAKE_REG(port));
		}

		goto out;
		break;
	case HSI_IOCTL_SEND_BREAK:
		hsi_outl(1, base, HSI_HST_BREAK_REG(port));
		/*HSI_TODO : need to deactivate clock after BREAK frames sent*/
		/*Use interrupt ? (if TX BREAK INT exists)*/
		break;
	case HSI_IOCTL_GET_ACWAKE:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		*(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port));
		break;
	case HSI_IOCTL_FLUSH_RX:
		hsi_outl(0, base, HSI_HSR_RXSTATE_REG(port));
		break;
	case HSI_IOCTL_FLUSH_TX:
		hsi_outl(0, base, HSI_HST_TXSTATE_REG(port));
		break;
	case HSI_IOCTL_GET_CAWAKE:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_get_cawake(dev->ch->hsi_port);
		if (err < 0) {
			err = -ENODEV;
			goto out;
		}
		*(u32 *)arg = err;
		break;
	case HSI_IOCTL_SET_RX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
		break;
	case HSI_IOCTL_GET_RX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
		break;
	case HSI_IOCTL_SET_TX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
		break;
	case HSI_IOCTL_GET_TX:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
		break;
	case HSI_IOCTL_SW_RESET:
		dev_info(dev->device.parent, "SW Reset\n");
		err = hsi_softreset(hsi_ctrl);

		/* Reset HSI config to default */
		hsi_softreset_driver(hsi_ctrl);
		break;
	case HSI_IOCTL_GET_FIFO_OCCUPANCY:
		if (!arg) {
			err = -EINVAL;
			goto out;
		}
		fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
		if (unlikely(fifo < 0)) {
			dev_err(hsi_ctrl->dev, "No valid FIFO id found for "
					       "channel %d.\n", channel);
			err = -EFAULT;
			goto out;
		}
		*(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo);
		break;
	default:
		err = -ENOIOCTLCMD;
		break;
	}
out:
	/* All IOCTL end by disabling the clocks, except ACWAKE high. */
	hsi_clocks_disable_channel(dev->device.parent, channel, __func__);

	spin_unlock_bh(&hsi_ctrl->lock);

	return err;
}
/**
 * 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;
}
static int hsi_debug_port_show(struct seq_file *m, void *p)
{
	struct hsi_port *hsi_port = m->private;
	struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
	void __iomem *base = hsi_ctrl->base;
	unsigned int port = hsi_port->port_number;
	int ch, fifo;
	long buff_offset;
	struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);

	hsi_clocks_enable(hsi_ctrl->dev, __func__);

	if (hsi_port->cawake_gpio >= 0)
		seq_printf(m, "CAWAKE\t\t: %d\n", hsi_get_cawake(hsi_port));

	seq_printf(m, "WAKE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_SYS_WAKE_REG(port)));
	seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
		   hsi_inl(base,
			   HSI_SYS_MPU_ENABLE_REG(port, hsi_port->n_irq)));
	seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
		   hsi_inl(base,
			   HSI_SYS_MPU_STATUS_REG(port, hsi_port->n_irq)));
	if (hsi_driver_device_is_hsi(pdev)) {
		seq_printf(m, "MPU_U_ENABLE_IRQ%d\t: 0x%08x\n",
			   hsi_port->n_irq,
			   hsi_inl(base, HSI_SYS_MPU_U_ENABLE_REG(port,
							hsi_port->n_irq)));
		seq_printf(m, "MPU_U_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
			   hsi_inl(base,
				   HSI_SYS_MPU_U_STATUS_REG(port,
							    hsi_port->n_irq)));
	}
	/* HST */
	seq_printf(m, "\nHST\n===\n");
	seq_printf(m, "MODE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_MODE_REG(port)));
	seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_FRAMESIZE_REG(port)));
	seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_DIVISOR_REG(port)));
	seq_printf(m, "CHANNELS\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_CHANNELS_REG(port)));
	seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_ARBMODE_REG(port)));
	seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_TXSTATE_REG(port)));
	if (hsi_driver_device_is_hsi(pdev)) {
		seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
			   hsi_inl(base, HSI_HST_BUFSTATE_REG(1)));
		seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
			   hsi_inl(base, HSI_HST_BUFSTATE_REG(2)));
	} else {
		seq_printf(m, "BUFSTATE\t: 0x%08x\n",
			   hsi_inl(base, HSI_HST_BUFSTATE_REG(port)));
	}
	seq_printf(m, "BREAK\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HST_BREAK_REG(port)));
	for (ch = 0; ch < 8; ch++) {
		buff_offset = hsi_hst_buffer_reg(hsi_ctrl, port, ch);
		if (buff_offset >= 0)
			seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
				   hsi_inl(base, buff_offset));
	}
	if (hsi_driver_device_is_hsi(pdev)) {
		for (fifo = 0; fifo < HSI_HST_FIFO_COUNT; fifo++) {
			seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
				   hsi_inl(base,
					   HSI_HST_MAPPING_FIFO_REG(fifo)));
		}
	}
	/* HSR */
	seq_printf(m, "\nHSR\n===\n");
	seq_printf(m, "MODE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_MODE_REG(port)));
	seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port)));
	seq_printf(m, "CHANNELS\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_CHANNELS_REG(port)));
	seq_printf(m, "COUNTERS\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_COUNTERS_REG(port)));
	seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_RXSTATE_REG(port)));
	if (hsi_driver_device_is_hsi(pdev)) {
		seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
			   hsi_inl(base, HSI_HSR_BUFSTATE_REG(1)));
		seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
			   hsi_inl(base, HSI_HSR_BUFSTATE_REG(2)));
	} else {
		seq_printf(m, "BUFSTATE\t: 0x%08x\n",
			   hsi_inl(base, HSI_HSR_BUFSTATE_REG(port)));
	}
	seq_printf(m, "BREAK\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_BREAK_REG(port)));
	seq_printf(m, "ERROR\t\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_ERROR_REG(port)));
	seq_printf(m, "ERRORACK\t: 0x%08x\n",
		   hsi_inl(base, HSI_HSR_ERRORACK_REG(port)));
	for (ch = 0; ch < 8; ch++) {
		buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, port, ch);
		if (buff_offset >= 0)
			seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
				   hsi_inl(base, buff_offset));
	}
	if (hsi_driver_device_is_hsi(pdev)) {
		for (fifo = 0; fifo < HSI_HSR_FIFO_COUNT; fifo++) {
			seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
				   hsi_inl(base,
					   HSI_HSR_MAPPING_FIFO_REG(fifo)));
		}
		seq_printf(m, "DLL\t: 0x%08x\n",
			   hsi_inl(base, HSI_HSR_DLL_REG));
		seq_printf(m, "DIVISOR\t: 0x%08x\n",
			   hsi_inl(base, HSI_HSR_DIVISOR_REG(port)));
	}

	hsi_clocks_disable(hsi_ctrl->dev, __func__);

	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;
}