static int tegra_i2c_xfer_msg()

in busses/i2c-tegra.c [1222:1367]


static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
			      struct i2c_msg *msg,
			      enum msg_end_type end_state)
{
	unsigned long time_left, xfer_time = 100;
	size_t xfer_size;
	u32 int_mask;
	int err;

	err = tegra_i2c_flush_fifos(i2c_dev);
	if (err)
		return err;

	i2c_dev->msg_buf = msg->buf;
	i2c_dev->msg_buf_remaining = msg->len;
	i2c_dev->msg_err = I2C_ERR_NONE;
	i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
	reinit_completion(&i2c_dev->msg_complete);

	if (i2c_dev->msg_read)
		xfer_size = msg->len;
	else
		xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;

	xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);

	i2c_dev->dma_mode = xfer_size > I2C_PIO_MODE_PREFERRED_LEN &&
			    i2c_dev->dma_buf && !i2c_dev->atomic_mode;

	tegra_i2c_config_fifo_trig(i2c_dev, xfer_size);

	/*
	 * Transfer time in mSec = Total bits / transfer rate
	 * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
	 */
	xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
				       i2c_dev->timings.bus_freq_hz);

	int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
	tegra_i2c_unmask_irq(i2c_dev, int_mask);

	if (i2c_dev->dma_mode) {
		if (i2c_dev->msg_read) {
			dma_sync_single_for_device(i2c_dev->dev,
						   i2c_dev->dma_phys,
						   xfer_size, DMA_FROM_DEVICE);

			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
			if (err)
				return err;
		} else {
			dma_sync_single_for_cpu(i2c_dev->dev,
						i2c_dev->dma_phys,
						xfer_size, DMA_TO_DEVICE);
		}
	}

	tegra_i2c_push_packet_header(i2c_dev, msg, end_state);

	if (!i2c_dev->msg_read) {
		if (i2c_dev->dma_mode) {
			memcpy(i2c_dev->dma_buf + I2C_PACKET_HEADER_SIZE,
			       msg->buf, msg->len);

			dma_sync_single_for_device(i2c_dev->dev,
						   i2c_dev->dma_phys,
						   xfer_size, DMA_TO_DEVICE);

			err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
			if (err)
				return err;
		} else {
			tegra_i2c_fill_tx_fifo(i2c_dev);
		}
	}

	if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
		int_mask |= I2C_INT_PACKET_XFER_COMPLETE;

	if (!i2c_dev->dma_mode) {
		if (msg->flags & I2C_M_RD)
			int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
		else if (i2c_dev->msg_buf_remaining)
			int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
	}

	tegra_i2c_unmask_irq(i2c_dev, int_mask);
	dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n",
		i2c_readl(i2c_dev, I2C_INT_MASK));

	if (i2c_dev->dma_mode) {
		time_left = tegra_i2c_wait_completion(i2c_dev,
						      &i2c_dev->dma_complete,
						      xfer_time);

		/*
		 * Synchronize DMA first, since dmaengine_terminate_sync()
		 * performs synchronization after the transfer's termination
		 * and we want to get a completion if transfer succeeded.
		 */
		dmaengine_synchronize(i2c_dev->msg_read ?
				      i2c_dev->rx_dma_chan :
				      i2c_dev->tx_dma_chan);

		dmaengine_terminate_sync(i2c_dev->msg_read ?
					 i2c_dev->rx_dma_chan :
					 i2c_dev->tx_dma_chan);

		if (!time_left && !completion_done(&i2c_dev->dma_complete)) {
			dev_err(i2c_dev->dev, "DMA transfer timed out\n");
			tegra_i2c_init(i2c_dev);
			return -ETIMEDOUT;
		}

		if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) {
			dma_sync_single_for_cpu(i2c_dev->dev,
						i2c_dev->dma_phys,
						xfer_size, DMA_FROM_DEVICE);

			memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, msg->len);
		}
	}

	time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete,
					      xfer_time);

	tegra_i2c_mask_irq(i2c_dev, int_mask);

	if (time_left == 0) {
		dev_err(i2c_dev->dev, "I2C transfer timed out\n");
		tegra_i2c_init(i2c_dev);
		return -ETIMEDOUT;
	}

	dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
		time_left, completion_done(&i2c_dev->msg_complete),
		i2c_dev->msg_err);

	i2c_dev->dma_mode = false;

	err = tegra_i2c_error_recover(i2c_dev, msg);
	if (err)
		return err;

	return 0;
}