static int synquacer_spi_transfer_one()

in spi-synquacer.c [353:482]


static int synquacer_spi_transfer_one(struct spi_master *master,
				      struct spi_device *spi,
				      struct spi_transfer *xfer)
{
	struct synquacer_spi *sspi = spi_master_get_devdata(master);
	int ret;
	int status = 0;
	u32 words;
	u8 bpw;
	u32 val;

	val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
	val &= ~SYNQUACER_HSSPI_DMSTOP_STOP;
	writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);

	val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
	val |= SYNQUACER_HSSPI_FIFOCFG_RX_FLUSH;
	val |= SYNQUACER_HSSPI_FIFOCFG_TX_FLUSH;
	writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);

	/*
	 * See if we can transfer 4-bytes as 1 word
	 * to maximize the FIFO buffer efficiency.
	 */
	bpw = xfer->bits_per_word;
	if (bpw == 8 && !(xfer->len % 4) && !(spi->mode & SPI_LSB_FIRST))
		xfer->bits_per_word = 32;

	ret = synquacer_spi_config(master, spi, xfer);

	/* restore */
	xfer->bits_per_word = bpw;

	if (ret)
		return ret;

	reinit_completion(&sspi->transfer_done);

	sspi->tx_buf = xfer->tx_buf;
	sspi->rx_buf = xfer->rx_buf;

	switch (sspi->bpw) {
	case 8:
		words = xfer->len;
		break;
	case 16:
		words = xfer->len / 2;
		break;
	case 24:
		/* fallthrough, should use 32-bits access */
	case 32:
		words = xfer->len / 4;
		break;
	default:
		dev_err(sspi->dev, "unsupported bpw: %d\n", sspi->bpw);
		return -EINVAL;
	}

	if (xfer->tx_buf)
		sspi->tx_words = words;
	else
		sspi->tx_words = 0;

	if (xfer->rx_buf)
		sspi->rx_words = words;
	else
		sspi->rx_words = 0;

	if (xfer->tx_buf) {
		status = write_fifo(sspi);
		if (status < 0) {
			dev_err(sspi->dev, "failed write_fifo. status: 0x%x\n",
				status);
			return status;
		}
	}

	if (xfer->rx_buf) {
		val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
		val &= ~(SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_MASK <<
			 SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT);
		val |= ((sspi->rx_words > SYNQUACER_HSSPI_FIFO_DEPTH ?
			SYNQUACER_HSSPI_FIFO_RX_THRESHOLD : sspi->rx_words) <<
			SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT);
		writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
	}

	writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_TXC);
	writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_RXC);

	/* Trigger */
	val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
	val |= SYNQUACER_HSSPI_DMSTART_START;
	writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);

	if (xfer->tx_buf) {
		val = SYNQUACER_HSSPI_TXE_FIFO_EMPTY;
		writel(val, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
		status = wait_for_completion_timeout(&sspi->transfer_done,
			msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC));
		writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
	}

	if (xfer->rx_buf) {
		u32 buf[SYNQUACER_HSSPI_FIFO_DEPTH];

		val = SYNQUACER_HSSPI_RXE_FIFO_MORE_THAN_THRESHOLD |
		      SYNQUACER_HSSPI_RXE_SLAVE_RELEASED;
		writel(val, sspi->regs + SYNQUACER_HSSPI_REG_RXE);
		status = wait_for_completion_timeout(&sspi->transfer_done,
			msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC));
		writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE);

		/* stop RX and clean RXFIFO */
		val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
		val |= SYNQUACER_HSSPI_DMSTOP_STOP;
		writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
		sspi->rx_buf = buf;
		sspi->rx_words = SYNQUACER_HSSPI_FIFO_DEPTH;
		read_fifo(sspi);
	}

	if (status < 0) {
		dev_err(sspi->dev, "failed to transfer. status: 0x%x\n",
			status);
		return status;
	}

	return 0;
}