Ejemplo n.º 1
0
/**
 * hsi_read_cancel - Cancel pending read request.
 * @dev - hsi device channel where to cancel the pending read.
 *
 * read_done() callback will not be called after success of this function.
 *
 * Return: -ENXIO : No DMA channel found for specified HSI channel
 *	   -ECANCELED : read cancel success, data not available at expected
 *			address.
 *	   0 : transfer is already over, data already available at expected
 *	       address.
 *
 * Note: whatever returned value, read callback will not be called after cancel.
 */
int hsi_read_cancel(struct hsi_device *dev)
{
	struct hsi_dev *hsi_ctrl;
	int err;
	if (unlikely(!dev || !dev->ch)) {
		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
		return -ENODEV;
	}

	hsi_ctrl = dev->ch->hsi_port->hsi_controller;

	dev_dbg(hsi_ctrl->dev, "%s ch %d\n", __func__, dev->n_ch);

	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
		dev_err(hsi_ctrl->dev, "HSI device NOT open\n");
		return -ENODEV;
	}

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

	err = __hsi_read_cancel(dev->ch);

	hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number,
				   __func__);
	spin_unlock_bh(&hsi_ctrl->lock);

	return err;
}
Ejemplo n.º 2
0
/**
 * hsi_unpoll - HSI poll feature, disables data interrupt on frame reception
 * @dev - hsi device channel reference to apply the I/O control
 *						(or port associated to it)
 *
 * Return 0 on success, a negative value on failure.
 *
 */
int hsi_unpoll(struct hsi_device *dev)
{
	struct hsi_channel *ch;
	struct hsi_dev *hsi_ctrl;

	if (unlikely(!dev || !dev->ch))
		return -EINVAL;
	dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);

	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
		dev_err(dev->device.parent, "HSI device NOT open\n");
		return -EINVAL;
	}

	ch = dev->ch;
	hsi_ctrl = ch->hsi_port->hsi_controller;

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

	ch->flags &= ~HSI_CH_RX_POLL;

	hsi_driver_disable_read_interrupt(ch);

	hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number,
				   __func__);
	spin_unlock_bh(&hsi_ctrl->lock);

	return 0;
}
Ejemplo n.º 3
0
/**
 * hsi_set_port_event_cb - register port_event callback.
 * @dev - reference to hsi device channel where the callback is associated to.
 * @port_event_cb - callback to signal events from the channel port.
 */
void hsi_set_port_event_cb(struct hsi_device *dev,
			   void (*port_event_cb) (struct hsi_device *dev,
						  unsigned int event,
						  void *arg))
{
	struct hsi_port *port = dev->ch->hsi_port;
	struct hsi_dev *hsi_ctrl = port->hsi_controller;

	dev_dbg(hsi_ctrl->dev, "%s ch %d\n", __func__, dev->n_ch);

	write_lock_bh(&dev->ch->rw_lock);
	dev->ch->port_event = port_event_cb;
	write_unlock_bh(&dev->ch->rw_lock);

	/* Since we now have a callback registered for events, we can now */
	/* enable the CAWAKE, ERROR and BREAK interrupts */
	spin_lock_bh(&hsi_ctrl->lock);
	hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number,
					__func__);

	hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
					| HSI_BREAKDETECTED);
	hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number,
				   __func__);
	spin_unlock_bh(&hsi_ctrl->lock);
}
Ejemplo n.º 4
0
/**
 * hsi_close - close given hsi device channel
 * @dev - reference to hsi device channel.
 */
void hsi_close(struct hsi_device *dev)
{
	struct hsi_dev *hsi_ctrl;

	if (!dev || !dev->ch) {
		pr_err(LOG_NAME "Trying to close wrong HSI device %p\n", dev);
		return;
	}
	dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);

	hsi_ctrl = dev->ch->hsi_port->hsi_controller;

	spin_lock_bh(&hsi_ctrl->lock);

	hsi_clocks_enable_channel(hsi_ctrl->dev, dev->ch->channel_number,
					__func__);

	if (dev->ch->flags & HSI_CH_OPEN) {
		dev->ch->flags &= ~HSI_CH_OPEN;
		__hsi_write_cancel(dev->ch);
		__hsi_read_cancel(dev->ch);
	}

	hsi_clocks_disable_channel(hsi_ctrl->dev, dev->ch->channel_number,
				   __func__);
	spin_unlock_bh(&hsi_ctrl->lock);
}
Ejemplo n.º 5
0
/**
 * hsi_open - open a hsi device channel.
 * @dev - Reference to the hsi device channel to be openned.
 *
 * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
 */
int hsi_open(struct hsi_device *dev)
{
	struct hsi_channel *ch;
	struct hsi_port *port;
	struct hsi_dev *hsi_ctrl;
	int err;

	if (!dev || !dev->ch) {
		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
		return -EINVAL;
	}
	dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);

	ch = dev->ch;
	if (!ch->read_done || !ch->write_done) {
		dev_err(dev->device.parent,
			"Trying to open with no (read/write) callbacks "
			"registered\n");
		return -EINVAL;
	}
	if (ch->flags & HSI_CH_OPEN) {
		dev_err(dev->device.parent, "Port %d Channel %d already open\n",
			dev->n_p, dev->n_ch);
		return -EBUSY;
	}

	port = ch->hsi_port;
	hsi_ctrl = port->hsi_controller;

	spin_lock_bh(&hsi_ctrl->lock);

	err = hsi_clocks_enable_channel(hsi_ctrl->dev, ch->channel_number,
					__func__);
	if (err < 0) {
		spin_unlock_bh(&hsi_ctrl->lock);
		return err;
	}

	/* Restart with flags cleaned up */
	ch->flags = HSI_CH_OPEN;

	if (port->wake_rx_3_wires_mode)
		hsi_driver_enable_interrupt(port, HSI_ERROROCCURED
						| HSI_BREAKDETECTED);
	else
		hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED
						| HSI_ERROROCCURED
						| HSI_BREAKDETECTED);

	/* NOTE: error and break are port events and do not need to be
	 * enabled for HSI extended enable register */

	hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__);
	spin_unlock_bh(&hsi_ctrl->lock);

	return 0;
}
Ejemplo n.º 6
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;
}
Ejemplo n.º 7
0
/**
 * hsi_read - read data from the hsi device channel
 * @dev - hsi device channel reference to read data from.
 * @addr - pointer to a 32-bit word data to store the data.
 * @size - number of 32-bit word to be stored.
 *
 * Return 0 on sucess, a negative value on failure.
 * A success value only indicates that the request has been accepted.
 * Data is only available in the buffer when the read_done callback is called.
 *
 */
int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size)
{
	struct hsi_channel *ch;
	struct hsi_dev *hsi_ctrl;
	int err;

	if (unlikely(!dev)) {
		pr_err(LOG_NAME "Null dev pointer in hsi_read\n");
		return -EINVAL;
	}

	if (unlikely(!dev->ch || !addr || (size <= 0))) {
		dev_err(dev->device.parent, "Wrong parameters "
			"hsi_device %p data %p count %d", dev, addr, size);
		return -EINVAL;
	}

	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
		dev_err(dev->device.parent, "HSI device NOT open\n");
		return -EINVAL;
	}

	ch = dev->ch;
	hsi_ctrl = ch->hsi_port->hsi_controller;

	spin_lock_bh(&hsi_ctrl->lock);

	if (hsi_ctrl->clock_change_ongoing) {
		dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n");
		spin_unlock_bh(&hsi_ctrl->lock);
		return -EAGAIN;
	}

	if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled)
		dev_dbg(hsi_ctrl->dev,
			"hsi_read with HSI clocks OFF, clock_enabled = %d\n",
			hsi_ctrl->clock_enabled);

	hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
				__func__);

	if (ch->read_data.addr != NULL) {
		dev_err(hsi_ctrl->dev, "# Invalid request - Read "
				"operation pending port %d channel %d\n",
					ch->hsi_port->port_number,
					ch->channel_number);
		err = -EINVAL;
		goto done;
	}

	ch->read_data.addr = addr;
	ch->read_data.size = size;
	ch->read_data.lch = -1;

	if (size == 1)
		err = hsi_driver_enable_read_interrupt(ch, addr);
	else
		err = hsi_driver_read_dma(ch, addr, size);

	if (unlikely(err < 0)) {
		ch->read_data.addr = NULL;
		ch->read_data.size = 0;
		dev_err(hsi_ctrl->dev, "Failed to program read\n");
	}

done:
	hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__);
	spin_unlock_bh(&hsi_ctrl->lock);

	return err;
}
Ejemplo n.º 8
0
/**
 * hsi_write - write data into the hsi device channel
 * @dev - reference to the hsi device channel to write into.
 * @addr - pointer to a 32-bit word data to be written.
 * @size - number of 32-bit word to be written.
 *
 * Return 0 on success, a negative value on failure.
 * A success value only indicates that the request has been accepted.
 * Transfer is only completed when the write_done callback is called.
 *
 */
int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
{
	struct hsi_channel *ch;
	struct hsi_dev *hsi_ctrl;
	int err;

	if (unlikely(!dev)) {
		pr_err(LOG_NAME "Null dev pointer in hsi_write\n");
		return -EINVAL;
	}

	if (unlikely(!dev->ch || !addr || (size <= 0))) {
		dev_err(dev->device.parent,
			"Wrong parameters hsi_device %p data %p count %d",
			dev, addr, size);
		return -EINVAL;
	}

	ch = dev->ch;
	hsi_ctrl = ch->hsi_port->hsi_controller;

	dev_dbg(hsi_ctrl->dev, "%s ch %d, @%x, size %d u32\n", __func__,
		dev->n_ch, (u32) addr, size);

	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
		dev_err(hsi_ctrl->dev, "HSI device NOT open\n");
		return -EINVAL;
	}

	if (ch->write_data.addr != NULL) {
		dev_err(hsi_ctrl->dev, "# Invalid request - Write "
				"operation pending port %d channel %d\n",
					ch->hsi_port->port_number,
					ch->channel_number);
		return -EINVAL;
	}

	spin_lock_bh(&hsi_ctrl->lock);

	if (hsi_ctrl->clock_change_ongoing) {
		dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n");
		spin_unlock_bh(&hsi_ctrl->lock);
		return -EAGAIN;
	}

	if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled)
		dev_dbg(hsi_ctrl->dev,
			"hsi_write with HSI clocks OFF, clock_enabled = %d\n",
			hsi_ctrl->clock_enabled);

	hsi_clocks_enable_channel(dev->device.parent,
				ch->channel_number, __func__);

	ch->write_data.addr = addr;
	ch->write_data.size = size;
	ch->write_data.lch = -1;

	if (size == 1)
		err = hsi_driver_enable_write_interrupt(ch, addr);
	else
		err = hsi_driver_write_dma(ch, addr, size);

	if (unlikely(err < 0)) {
		ch->write_data.addr = NULL;
		ch->write_data.size = 0;
		dev_err(hsi_ctrl->dev, "Failed to program write\n");
	}

	spin_unlock_bh(&hsi_ctrl->lock);

	/* Leave clocks enabled until transfer is complete (write callback */
	/* is called */
	return err;
}
Ejemplo n.º 9
0
/**
 * hsi_open - open a hsi device channel.
 * @dev - Reference to the hsi device channel to be openned.
 *
 * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
 */
int hsi_open(struct hsi_device *dev)
{
	struct hsi_channel *ch;
	struct hsi_port *port;
	struct hsi_dev *hsi_ctrl;
	int err;

	if (!dev || !dev->ch) {
		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
		return -EINVAL;
	}
	dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);

	ch = dev->ch;
	if (!ch->read_done || !ch->write_done) {
		dev_err(dev->device.parent,
			"Trying to open with no (read/write) callbacks "
			"registered\n");
		return -EINVAL;
	}
	if (ch->flags & HSI_CH_OPEN) {
		dev_err(dev->device.parent, "Port %d Channel %d already open\n",
			dev->n_p, dev->n_ch);
		return -EBUSY;
	}

	port = ch->hsi_port;
	hsi_ctrl = port->hsi_controller;
	if (!hsi_ctrl) {
		dev_err(dev->device.parent,
			"%s: Port %d Channel %d has no hsi controller?\n",
			__func__, dev->n_p, dev->n_ch);
		return -EINVAL;
	}

	if (hsi_ctrl->hsi_fclk_current == 0) {
		struct hsi_platform_data *pdata;

		pdata = dev_get_platdata(hsi_ctrl->dev);

		/* Retry to set the HSI FCLK to default. */
		err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
					  pdata->default_hsi_fclk);
		if (err) {
			dev_err(dev->device.parent,
				"%s: Error %d setting HSI FClk to %ld. "
				"Will retry on next open\n",
				__func__, err, pdata->default_hsi_fclk);
			return err;
		} else {
			dev_info(dev->device.parent, "HSI clock is now %ld\n",
				 pdata->default_hsi_fclk);
			hsi_ctrl->hsi_fclk_current = pdata->default_hsi_fclk;
		}
	}
	spin_lock_bh(&hsi_ctrl->lock);
	hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
				__func__);

	/* Restart with flags cleaned up */
	ch->flags = HSI_CH_OPEN;

	hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
					| HSI_BREAKDETECTED);

	/* NOTE: error and break are port events and do not need to be
	 * enabled for HSI extended enable register */

	hsi_clocks_disable_channel(hsi_ctrl->dev, ch->channel_number, __func__);
	spin_unlock_bh(&hsi_ctrl->lock);

	return 0;
}
Ejemplo n.º 10
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;
}