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;
}