Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ee652faf authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "spi: spi-geni-qcom: Add support for SE DMA mode"

parents 44a69824 569605a6
Loading
Loading
Loading
Loading
+135 −49
Original line number Diff line number Diff line
@@ -311,7 +311,7 @@ static int select_xfer_mode(struct spi_master *spi,
				struct spi_message *spi_msg)
{
	struct spi_geni_master *mas = spi_master_get_devdata(spi);
	int mode = FIFO_MODE;
	int mode = SE_DMA;
	int fifo_disable = (geni_read_reg(mas->base, GENI_IF_FIFO_DISABLE_RO) &
							FIFO_IF_DISABLE);
	bool dma_chan_valid =
@@ -325,10 +325,10 @@ static int select_xfer_mode(struct spi_master *spi,
	 */
	if (fifo_disable && !dma_chan_valid)
		mode = -EINVAL;
	else if (!fifo_disable)
		mode = SE_DMA;
	else if (dma_chan_valid)
		mode = GSI_DMA;
	else
		mode = FIFO_MODE;
	return mode;
}

@@ -719,25 +719,20 @@ static int spi_geni_prepare_message(struct spi_master *spi,

	mas->cur_xfer_mode = select_xfer_mode(spi, spi_msg);

	if (mas->cur_xfer_mode == FIFO_MODE) {
		geni_se_select_mode(mas->base, FIFO_MODE);
		reinit_completion(&mas->xfer_done);
		ret = setup_fifo_params(spi_msg->spi, spi);
	if (mas->cur_xfer_mode < 0) {
		dev_err(mas->dev, "%s: Couldn't select mode %d", __func__,
							mas->cur_xfer_mode);
		ret = -EINVAL;
	} else if (mas->cur_xfer_mode == GSI_DMA) {
		mas->num_tx_eot = 0;
		mas->num_rx_eot = 0;
		mas->num_xfers = 0;
		reinit_completion(&mas->tx_cb);
		reinit_completion(&mas->rx_cb);
		memset(mas->gsi, 0,
				(sizeof(struct spi_geni_gsi) * NUM_SPI_XFER));
		geni_se_select_mode(mas->base, GSI_DMA);
		ret = spi_geni_map_buf(mas, spi_msg);
	} else {
		dev_err(mas->dev, "%s: Couldn't select mode %d", __func__,
							mas->cur_xfer_mode);
		ret = -EINVAL;
		geni_se_select_mode(mas->base, mas->cur_xfer_mode);
		ret = setup_fifo_params(spi_msg->spi, spi);
	}

	return ret;
}

@@ -967,24 +962,64 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
		geni_write_reg(trans_len, mas->base, SE_SPI_RX_TRANS_LEN);
		mas->rx_rem_bytes = xfer->len;
	}

	if (trans_len > (mas->tx_fifo_depth * mas->tx_fifo_width)) {
		if (mas->cur_xfer_mode != SE_DMA) {
			mas->cur_xfer_mode = SE_DMA;
			geni_se_select_mode(mas->base, mas->cur_xfer_mode);
		}
	} else {
		if (mas->cur_xfer_mode != FIFO_MODE) {
			mas->cur_xfer_mode = FIFO_MODE;
			geni_se_select_mode(mas->base, mas->cur_xfer_mode);
		}
	}

	geni_write_reg(spi_tx_cfg, mas->base, SE_SPI_TRANS_CFG);
	geni_setup_m_cmd(mas->base, m_cmd, m_param);
	GENI_SE_DBG(mas->ipc, false, mas->dev,
		"%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x\n",
		__func__, trans_len, xfer->len, spi_tx_cfg, m_cmd);
	if (m_cmd & SPI_TX_ONLY)
		geni_write_reg(mas->tx_wm, mas->base, SE_GENI_TX_WATERMARK_REG);
	"%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x cs%d mode%d\n",
		__func__, trans_len, xfer->len, spi_tx_cfg, m_cmd,
			xfer->cs_change, mas->cur_xfer_mode);
	if ((m_cmd & SPI_RX_ONLY) && (mas->cur_xfer_mode == SE_DMA)) {
		int ret = 0;

		ret =  geni_se_rx_dma_prep(mas->wrapper_dev, mas->base,
				xfer->rx_buf, xfer->len, &xfer->rx_dma);
		if (ret)
			GENI_SE_ERR(mas->ipc, true, mas->dev,
				"Failed to setup Rx dma %d\n", ret);
	}
	if (m_cmd & SPI_TX_ONLY) {
		if (mas->cur_xfer_mode == FIFO_MODE) {
			geni_write_reg(mas->tx_wm, mas->base,
					SE_GENI_TX_WATERMARK_REG);
		} else if (mas->cur_xfer_mode == SE_DMA) {
			int ret = 0;

			ret =  geni_se_tx_dma_prep(mas->wrapper_dev, mas->base,
					(void *)xfer->tx_buf, xfer->len,
							&xfer->tx_dma);
			if (ret)
				GENI_SE_ERR(mas->ipc, true, mas->dev,
					"Failed to setup tx dma %d\n", ret);
		}
	}


	/* Ensure all writes are done before the WM interrupt */
	mb();
}

static void handle_fifo_timeout(struct spi_geni_master *mas)
static void handle_fifo_timeout(struct spi_geni_master *mas,
					struct spi_transfer *xfer)
{
	unsigned long timeout;

	geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
	reinit_completion(&mas->xfer_done);
	geni_cancel_m_cmd(mas->base);
	if (mas->cur_xfer_mode == FIFO_MODE)
		geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
	/* Ensure cmd cancel is written */
	mb();
@@ -1000,6 +1035,15 @@ static void handle_fifo_timeout(struct spi_geni_master *mas)
			dev_err(mas->dev,
				"Failed to cancel/abort m_cmd\n");
	}
	if (mas->cur_xfer_mode == SE_DMA) {
		if (xfer->tx_buf)
			geni_se_tx_dma_unprep(mas->wrapper_dev,
					xfer->tx_dma, xfer->len);
		if (xfer->rx_buf)
			geni_se_rx_dma_unprep(mas->wrapper_dev,
					xfer->rx_dma, xfer->len);
	}

}

static int spi_geni_transfer_one(struct spi_master *spi,
@@ -1015,7 +1059,8 @@ static int spi_geni_transfer_one(struct spi_master *spi,
		return -EINVAL;
	}

	if (mas->cur_xfer_mode == FIFO_MODE) {
	if (mas->cur_xfer_mode != GSI_DMA) {
		reinit_completion(&mas->xfer_done);
		setup_fifo_xfer(xfer, mas, slv->mode, spi);
		timeout = wait_for_completion_timeout(&mas->xfer_done,
					msecs_to_jiffies(SPI_XFER_TIMEOUT_MS));
@@ -1029,7 +1074,22 @@ static int spi_geni_transfer_one(struct spi_master *spi,
			ret = -ETIMEDOUT;
			goto err_fifo_geni_transfer_one;
		}

		if (mas->cur_xfer_mode == SE_DMA) {
			if (xfer->tx_buf)
				geni_se_tx_dma_unprep(mas->wrapper_dev,
					xfer->tx_dma, xfer->len);
			if (xfer->rx_buf)
				geni_se_rx_dma_unprep(mas->wrapper_dev,
					xfer->rx_dma, xfer->len);
		}
	} else {
		mas->num_tx_eot = 0;
		mas->num_rx_eot = 0;
		mas->num_xfers = 0;
		reinit_completion(&mas->tx_cb);
		reinit_completion(&mas->rx_cb);

		setup_gsi_xfer(xfer, mas, slv, spi);
		if ((mas->num_xfers >= NUM_SPI_XFER) ||
			(list_is_last(&xfer->transfer_list,
@@ -1073,7 +1133,7 @@ static int spi_geni_transfer_one(struct spi_master *spi,
	dmaengine_terminate_all(mas->tx);
	return ret;
err_fifo_geni_transfer_one:
	handle_fifo_timeout(mas);
	handle_fifo_timeout(mas, xfer);
	return ret;
}

@@ -1189,7 +1249,9 @@ static irqreturn_t geni_spi_irq(int irq, void *dev)
		goto exit_geni_spi_irq;
	}
	m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
	if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
	if (mas->cur_xfer_mode == FIFO_MODE) {
		if ((m_irq & M_RX_FIFO_WATERMARK_EN) ||
						(m_irq & M_RX_FIFO_LAST_EN))
			geni_spi_handle_rx(mas);

		if ((m_irq & M_TX_FIFO_WATERMARK_EN))
@@ -1199,23 +1261,47 @@ static irqreturn_t geni_spi_irq(int irq, void *dev)
			(m_irq & M_CMD_ABORT_EN)) {
			complete(&mas->xfer_done);
			/*
		 * If this happens, then a CMD_DONE came before all the buffer
		 * bytes were sent out. This is unusual, log this condition and
		 * disable the WM interrupt to prevent the system from stalling
		 * due an interrupt storm.
		 * If this happens when all Rx bytes haven't been received, log
		 * the condition.
			 * If this happens, then a CMD_DONE came before all the
			 * buffer bytes were sent out. This is unusual, log this
			 * condition and disable the WM interrupt to prevent the
			 * system from stalling due an interrupt storm.
			 * If this happens when all Rx bytes haven't been
			 * received, log the condition.
			 */
			if (mas->tx_rem_bytes) {
			geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
				geni_write_reg(0, mas->base,
						SE_GENI_TX_WATERMARK_REG);
				GENI_SE_DBG(mas->ipc, false, mas->dev,
					"%s:Premature Done.tx_rem%d bpw%d\n",
				__func__, mas->tx_rem_bytes, mas->cur_word_len);
					__func__, mas->tx_rem_bytes,
						mas->cur_word_len);
			}
			if (mas->rx_rem_bytes)
				GENI_SE_DBG(mas->ipc, false, mas->dev,
					"%s:Premature Done.rx_rem%d bpw%d\n",
				__func__, mas->rx_rem_bytes, mas->cur_word_len);
						__func__, mas->rx_rem_bytes,
							mas->cur_word_len);
		}
	} else if (mas->cur_xfer_mode == SE_DMA) {
		u32 dma_tx_status = geni_read_reg(mas->base,
							SE_DMA_TX_IRQ_STAT);
		u32 dma_rx_status = geni_read_reg(mas->base,
							SE_DMA_RX_IRQ_STAT);

		if (dma_tx_status)
			geni_write_reg(dma_tx_status, mas->base,
						SE_DMA_TX_IRQ_CLR);
		if (dma_rx_status)
			geni_write_reg(dma_rx_status, mas->base,
						SE_DMA_RX_IRQ_CLR);
		if (dma_tx_status & TX_DMA_DONE)
			mas->tx_rem_bytes = 0;
		if (dma_rx_status & RX_DMA_DONE)
			mas->rx_rem_bytes = 0;
		if (!mas->tx_rem_bytes && !mas->rx_rem_bytes)
			complete(&mas->xfer_done);
		if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN))
			complete(&mas->xfer_done);
	}
exit_geni_spi_irq:
	geni_write_reg(m_irq, mas->base, SE_GENI_M_IRQ_CLEAR);