Загрузка данных


static int lpi2c_imx_find_max_burst_num(unsigned int fifosize, unsigned int len)
{
	unsigned int i;

	for (i = fifosize / 2; i > 0; i--) {
		if (!(len % i))
			break;
	}

	return i;
}

/*
 * For a highest DMA efficiency, tx/rx burst number should be calculated according
 * to the FIFO depth.
 */
static void lpi2c_imx_dma_burst_num_calculate(struct lpi2c_imx_struct *lpi2c_imx)
{
	struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
	unsigned int cmd_num;

	if (dma->dma_msg_flag & I2C_M_RD) {
		/*
		 * One RX cmd word can trigger DMA receive no more than 256 bytes.
		 * The number of RX cmd words should be calculated based on the data
		 * length.
		 */
		cmd_num = DIV_ROUND_UP(dma->dma_len, CHUNK_DATA);
		dma->tx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->txfifosize,
				 cmd_num);
		dma->rx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->rxfifosize,
				 dma->dma_len);
	} else {
		dma->tx_burst_num = lpi2c_imx_find_max_burst_num(lpi2c_imx->txfifosize,
				 dma->dma_len);
	}
}

static int lpi2c_dma_config(struct lpi2c_imx_struct *lpi2c_imx)
{
	struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
	struct dma_slave_config rx = {}, tx = {};
	int ret;

	lpi2c_imx_dma_burst_num_calculate(lpi2c_imx);

	if (dma->dma_msg_flag & I2C_M_RD) {
		tx.dst_addr = dma->phy_addr + LPI2C_MTDR;
		tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
		tx.dst_maxburst = dma->tx_burst_num;
		tx.direction = DMA_MEM_TO_DEV;
		ret = dmaengine_slave_config(dma->chan_tx, &tx);
		if (ret < 0)
			return ret;

		rx.src_addr = dma->phy_addr + LPI2C_MRDR;
		rx.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
		rx.src_maxburst = dma->rx_burst_num;
		rx.direction = DMA_DEV_TO_MEM;
		ret = dmaengine_slave_config(dma->chan_rx, &rx);
		if (ret < 0)
			return ret;
	} else {
		tx.dst_addr = dma->phy_addr + LPI2C_MTDR;
		tx.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
		tx.dst_maxburst = dma->tx_burst_num;
		tx.direction = DMA_MEM_TO_DEV;
		ret = dmaengine_slave_config(dma->chan_tx, &tx);
		if (ret < 0)
			return ret;
	}

	return 0;
}

static void lpi2c_dma_enable(struct lpi2c_imx_struct *lpi2c_imx)
{
	struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
	/*
	 * TX interrupt will be triggerred when the number of words in
	 * the transmit FIFO is equal or less than TX watermark.
	 * RX interrupt will be triggerred when the number of words in
	 * the receive FIFO is greater than RX watermark.
	 * In order to trigger the DMA interrupt, TX watermark should be
	 * set equal to the DMA TX burst number but RX watermark should
	 * be set less than the DMA RX burst number.
	 */
	if (dma->dma_msg_flag & I2C_M_RD) {
		/* Set I2C TX/RX watermark */
		writel(dma->tx_burst_num | (dma->rx_burst_num - 1) << 16,
				 lpi2c_imx->base + LPI2C_MFCR);
		/* Enable I2C DMA TX/RX function */
		writel(MDER_TDDE | MDER_RDDE, lpi2c_imx->base + LPI2C_MDER);
	} else {
		/* Set I2C TX watermark */
		writel(dma->tx_burst_num, lpi2c_imx->base + LPI2C_MFCR);
		/* Enable I2C DMA TX function */
		writel(MDER_TDDE, lpi2c_imx->base + LPI2C_MDER);
	}

	/* Enable NACK detected */
	lpi2c_imx_intctrl(lpi2c_imx, MIER_NDIE);
};

/*
 * When lpi2c in TX DMA mode we can use one DMA TX channel to write
 * data word into TXFIFO, but in RX DMA mode it is different.
 *
 * LPI2C MTDR register is a command data and transmit data register.
 * Bit 8-10 is command data field and Bit 0-7 is transmit data field.
 * When the LPI2C master needs to read data, the data number to read
 * should be set in transmit data field and RECV_DATA should be set
 * into the command data field to receive (DATA[7:0] + 1) bytes. The
 * recv data command word is made of RECV_DATA in command data field
 * and the data number to read in transmit data field. When the length
 * of data that needs to be read exceeds 256 bytes, recv data command
 * word needs to be written to TXFIFO multiple times.
 *
 * So when in RX DMA mode, the TX channel also needs to be configured
 * additionally to send RX command words and the RX command word need
 * be set in advance before transmitting.
 */
static int lpi2c_imx_dma_xfer(struct lpi2c_imx_struct *lpi2c_imx,
			 struct i2c_msg *msg)
{
	struct lpi2c_imx_dma *dma = lpi2c_imx->dma;
	int ret;

	/* When DMA mode failed before transferring, CPU mode can be used. */
	dma->using_pio_mode = true;

	dma->dma_len = msg->len;
	dma->dma_msg_flag = msg->flags;
	dma->dma_buf = i2c_get_dma_safe_msg_buf(msg, I2C_DMA_THRESHOLD);
	if (!dma->dma_buf)
		return -ENOMEM;

	ret = lpi2c_dma_config(lpi2c_imx);
	if (ret) {
		dev_err(&lpi2c_imx->adapter.dev, "DMA Config Fail, error %d\n", ret);
		goto disable_dma;
	}

	lpi2c_dma_enable(lpi2c_imx);

	ret = lpi2c_dma_submit(lpi2c_imx);
	if (ret) {
		dev_err(&lpi2c_imx->adapter.dev, "DMA submit Fail, error %d\n", ret);
		goto disable_dma;
	}

	if (dma->dma_msg_flag & I2C_M_RD) {
		ret = lpi2c_imx_alloc_rx_cmd_buf(lpi2c_imx);
		if (ret) {
			lpi2c_cleanup_dma(dma);
			goto disable_dma;
		}

		ret = lpi2c_dma_rx_cmd_submit(lpi2c_imx);
		if (ret) {
			lpi2c_cleanup_dma(dma);
			goto disable_dma;
		}
	}

	ret = lpi2c_imx_dma_msg_complete(lpi2c_imx);
	if (ret) {
		if (dma->dma_msg_flag & I2C_M_RD)
			lpi2c_cleanup_rx_cmd_dma(dma);
		lpi2c_cleanup_dma(dma);
		goto disable_dma;
	}

	/* When meet NACK in transfer, cleanup all DMA transfer */
	if ((readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) && !ret) {
		if (dma->dma_msg_flag & I2C_M_RD)
			lpi2c_cleanup_rx_cmd_dma(dma);
		lpi2c_cleanup_dma(dma);
		ret = -EIO;
		goto disable_dma;
	}

	if (dma->dma_msg_flag & I2C_M_RD)
		dma_unmap_single(dma->chan_tx->device->dev, dma->dma_tx_addr,
					 dma->rx_cmd_buf_len, DMA_TO_DEVICE);
	lpi2c_dma_unmap(dma);

disable_dma:
	/* Disable I2C DMA function */
	writel(0, lpi2c_imx->base + LPI2C_MDER);

	if (dma->dma_msg_flag & I2C_M_RD)
		kfree(dma->rx_cmd_buf);

	if (ret)
		i2c_put_dma_safe_msg_buf(dma->dma_buf, msg, false);
	else
		i2c_put_dma_safe_msg_buf(dma->dma_buf, msg, true);
	return ret;
}

static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
			  struct i2c_msg *msgs, int num)
{
	struct lpi2c_imx_struct *lpi2c_imx = i2c_get_adapdata(adapter);
	unsigned int temp;
	int i, result;

	result = lpi2c_imx_master_enable(lpi2c_imx);
	if (result)
		return result;

	for (i = 0; i < num; i++) {
		result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
		if (result)
			goto disable;

		/* quick smbus */
		if (num == 1 && msgs[0].len == 0)
			goto stop;

		lpi2c_imx->rx_buf = NULL;
		lpi2c_imx->tx_buf = NULL;
		lpi2c_imx->delivered = 0;
		lpi2c_imx->msglen = msgs[i].len;
		init_completion(&lpi2c_imx->complete);

		if (is_use_dma(lpi2c_imx, &msgs[i])) {
			result = lpi2c_imx_dma_xfer(lpi2c_imx, &msgs[i]);
			if (result && lpi2c_imx->dma->using_pio_mode)
				result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
		} else {
			result = lpi2c_imx_pio_xfer(lpi2c_imx, &msgs[i]);
		}

		if (result)
			goto stop;

		if (!(msgs[i].flags & I2C_M_RD)) {
			result = lpi2c_imx_txfifo_empty(lpi2c_imx);
			if (result)
				goto stop;
		}
	}

stop:
	lpi2c_imx_stop(lpi2c_imx);

	temp = readl(lpi2c_imx->base + LPI2C_MSR);
	if ((temp & MSR_NDF) && !result)
		result = -EIO;

disable:
	lpi2c_imx_master_disable(lpi2c_imx);

	dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
		(result < 0) ? "error" : "success msg",
		(result < 0) ? result : num);

	return (result < 0) ? result : num;
}