Commit 4a8ee2ab authored by Mark Brown's avatar Mark Brown

Merge series "TCFQ to XSPI migration for NXP DSPI driver" from Vladimir Oltean <olteanv@gmail.com>

Vladimir Oltean <vladimir.oltean@nxp.com>:

From: Vladimir Oltean <vladimir.oltean@nxp.com>

This series aims to remove the most inefficient transfer method from the
NXP DSPI driver.

TCFQ (Transfer Complete Flag) mode works by transferring one word,
waiting for its TX confirmation interrupt (or polling on the equivalent
status bit), sending the next word, etc, until the buffer is complete.

The issue with this mode is that it's fundamentally incompatible with
any sort of batching such as writing to a FIFO. But actually, due to
previous patchset ("Compatible string consolidation for NXP DSPI driver"):

https://patchwork.kernel.org/cover/11414593/

all existing users of TCFQ mode today already support a more advanced
feature set, in the form of XSPI (extended SPI). XSPI brings 2 extra
features:

- Word sizes up to 32 bits. This is sub-utilized today, and acceleration
  of smaller-than-32 bpw values is provided.
- "Command cycling", basically the ability to write multiple words in a
  row and receiving an interrupt only after the completion of the last
  one. This is what enables us to make use of the full FIFO depth of
  this controller.

Series was tested on the NXP LS1021A-TSN and LS1043A-RDB boards, both
functionally as well as from a performance standpoint.

The command used to benchmark the increased throughput was:

spidev_test --device /dev/spidev1.0 --bpw 8 --size 256 --cpha --iter 10000000 --speed 20000000

where spidev1.0 is a dummy spidev node, using a chip select that no
peripheral responds to.

On LS1021A, which has a 4-entry-deep FIFO and a less powerful CPU, the
performance increase brought by this patchset is from 2700 kbps to 5800
kbps.

On LS1043A, which has a 16-entry-deep FIFO and a more powerful CPU, the
performance increases from 4100 kbps to 13700 kbps.

On average, SPI software timestamping is not adversely affected by the
extra batching, due to the extra patches.

There is one extra patch which clarifies why the TCFQ users were not
converted to the "other" mode in this driver that makes use of the FIFO,
which would be EOQ mode.

My request to the many people on CC (known users and/or contributors) is
to give this series a test to ensure there are no regressions, and for
the Coldfire maintainers to clarify whether the EOQ limitation is
acceptable for them in the long run.

Vladimir Oltean (12):
  spi: spi-fsl-dspi: Simplify bytes_per_word gymnastics
  spi: spi-fsl-dspi: Remove unused chip->void_write_data
  spi: spi-fsl-dspi: Don't mask off undefined bits
  spi: spi-fsl-dspi: Add comments around dspi_pop_tx and dspi_push_rx
    functions
  spi: spi-fsl-dspi: Rename fifo_{read,write} and {tx,cmd}_fifo_write
  spi: spi-fsl-dspi: Implement .max_message_size method for EOQ mode
  spi: Do spi_take_timestamp_pre for as many times as necessary
  spi: spi-fsl-dspi: Convert TCFQ users to XSPI FIFO mode
  spi: spi-fsl-dspi: Accelerate transfers using larger word size if
    possible
  spi: spi-fsl-dspi: Optimize dspi_setup_accel for lowest interrupt
    count
  spi: spi-fsl-dspi: Use EOQ for last word in buffer even for XSPI mode
  spi: spi-fsl-dspi: Take software timestamp in dspi_fifo_write

 drivers/spi/spi-fsl-dspi.c | 421 ++++++++++++++++++++++++-------------
 drivers/spi/spi.c          |  19 +-
 include/linux/spi/spi.h    |   3 +-
 3 files changed, 288 insertions(+), 155 deletions(-)

--
2.17.1
parents 6ac12131 e9bac900
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#define SPI_RSER 0x30 #define SPI_RSER 0x30
#define SPI_RSER_TCFQE BIT(31) #define SPI_RSER_TCFQE BIT(31)
#define SPI_RSER_EOQFE BIT(28) #define SPI_RSER_EOQFE BIT(28)
#define SPI_RSER_CMDTCFE BIT(23)
#define SPI_PUSHR 0x34 #define SPI_PUSHR 0x34
#define SPI_PUSHR_CMD_CONT BIT(15) #define SPI_PUSHR_CMD_CONT BIT(15)
...@@ -110,19 +111,17 @@ ...@@ -110,19 +111,17 @@
struct chip_data { struct chip_data {
u32 ctar_val; u32 ctar_val;
u16 void_write_data;
}; };
enum dspi_trans_mode { enum dspi_trans_mode {
DSPI_EOQ_MODE = 0, DSPI_EOQ_MODE = 0,
DSPI_TCFQ_MODE, DSPI_XSPI_MODE,
DSPI_DMA_MODE, DSPI_DMA_MODE,
}; };
struct fsl_dspi_devtype_data { struct fsl_dspi_devtype_data {
enum dspi_trans_mode trans_mode; enum dspi_trans_mode trans_mode;
u8 max_clock_factor; u8 max_clock_factor;
bool xspi_mode;
int fifo_size; int fifo_size;
int dma_bufsize; int dma_bufsize;
}; };
...@@ -148,37 +147,32 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { ...@@ -148,37 +147,32 @@ static const struct fsl_dspi_devtype_data devtype_data[] = {
}, },
[LS1021A] = { [LS1021A] = {
/* Has A-011218 DMA erratum */ /* Has A-011218 DMA erratum */
.trans_mode = DSPI_TCFQ_MODE, .trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 4, .fifo_size = 4,
}, },
[LS1012A] = { [LS1012A] = {
/* Has A-011218 DMA erratum */ /* Has A-011218 DMA erratum */
.trans_mode = DSPI_TCFQ_MODE, .trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 16, .fifo_size = 16,
}, },
[LS1043A] = { [LS1043A] = {
/* Has A-011218 DMA erratum */ /* Has A-011218 DMA erratum */
.trans_mode = DSPI_TCFQ_MODE, .trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 16, .fifo_size = 16,
}, },
[LS1046A] = { [LS1046A] = {
/* Has A-011218 DMA erratum */ /* Has A-011218 DMA erratum */
.trans_mode = DSPI_TCFQ_MODE, .trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 16, .fifo_size = 16,
}, },
[LS2080A] = { [LS2080A] = {
.trans_mode = DSPI_DMA_MODE, .trans_mode = DSPI_DMA_MODE,
.dma_bufsize = 8, .dma_bufsize = 8,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 4, .fifo_size = 4,
}, },
[LS2085A] = { [LS2085A] = {
...@@ -191,7 +185,6 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { ...@@ -191,7 +185,6 @@ static const struct fsl_dspi_devtype_data devtype_data[] = {
.trans_mode = DSPI_DMA_MODE, .trans_mode = DSPI_DMA_MODE,
.dma_bufsize = 8, .dma_bufsize = 8,
.max_clock_factor = 8, .max_clock_factor = 8,
.xspi_mode = true,
.fifo_size = 4, .fifo_size = 4,
}, },
[MCF5441X] = { [MCF5441X] = {
...@@ -234,36 +227,93 @@ struct fsl_dspi { ...@@ -234,36 +227,93 @@ struct fsl_dspi {
size_t len; size_t len;
const void *tx; const void *tx;
void *rx; void *rx;
void *rx_end;
u16 void_write_data;
u16 tx_cmd; u16 tx_cmd;
u8 bits_per_word;
u8 bytes_per_word;
const struct fsl_dspi_devtype_data *devtype_data; const struct fsl_dspi_devtype_data *devtype_data;
wait_queue_head_t waitq; wait_queue_head_t waitq;
u32 waitflags; u32 waitflags;
struct fsl_dspi_dma *dma; struct fsl_dspi_dma *dma;
int oper_word_size;
int oper_bits_per_word;
int words_in_flight;
void (*host_to_dev)(struct fsl_dspi *dspi, u32 *txdata);
void (*dev_to_host)(struct fsl_dspi *dspi, u32 rxdata);
}; };
static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
{
memcpy(txdata, dspi->tx, dspi->oper_word_size);
dspi->tx += dspi->oper_word_size;
}
static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
{
memcpy(dspi->rx, &rxdata, dspi->oper_word_size);
dspi->rx += dspi->oper_word_size;
}
static void dspi_8on32_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
{
*txdata = cpu_to_be32(*(u32 *)dspi->tx);
dspi->tx += sizeof(u32);
}
static void dspi_8on32_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
{
*(u32 *)dspi->rx = be32_to_cpu(rxdata);
dspi->rx += sizeof(u32);
}
static void dspi_8on16_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
{
*txdata = cpu_to_be16(*(u16 *)dspi->tx);
dspi->tx += sizeof(u16);
}
static void dspi_8on16_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
{
*(u16 *)dspi->rx = be16_to_cpu(rxdata);
dspi->rx += sizeof(u16);
}
static void dspi_16on32_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
{
u16 hi = *(u16 *)dspi->tx;
u16 lo = *(u16 *)(dspi->tx + 2);
*txdata = (u32)hi << 16 | lo;
dspi->tx += sizeof(u32);
}
static void dspi_16on32_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
{
u16 hi = rxdata & 0xffff;
u16 lo = rxdata >> 16;
*(u16 *)dspi->rx = lo;
*(u16 *)(dspi->rx + 2) = hi;
dspi->rx += sizeof(u32);
}
/*
* Pop one word from the TX buffer for pushing into the
* PUSHR register (TX FIFO)
*/
static u32 dspi_pop_tx(struct fsl_dspi *dspi) static u32 dspi_pop_tx(struct fsl_dspi *dspi)
{ {
u32 txdata = 0; u32 txdata = 0;
if (dspi->tx) { if (dspi->tx)
if (dspi->bytes_per_word == 1) dspi->host_to_dev(dspi, &txdata);
txdata = *(u8 *)dspi->tx; dspi->len -= dspi->oper_word_size;
else if (dspi->bytes_per_word == 2)
txdata = *(u16 *)dspi->tx;
else /* dspi->bytes_per_word == 4 */
txdata = *(u32 *)dspi->tx;
dspi->tx += dspi->bytes_per_word;
}
dspi->len -= dspi->bytes_per_word;
return txdata; return txdata;
} }
/* Prepare one TX FIFO entry (txdata plus cmd) */
static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi)
{ {
u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi); u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi);
...@@ -276,21 +326,12 @@ static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) ...@@ -276,21 +326,12 @@ static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi)
return cmd << 16 | data; return cmd << 16 | data;
} }
/* Push one word to the RX buffer from the POPR register (RX FIFO) */
static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata)
{ {
if (!dspi->rx) if (!dspi->rx)
return; return;
dspi->dev_to_host(dspi, rxdata);
/* Mask off undefined bits */
rxdata &= (1 << dspi->bits_per_word) - 1;
if (dspi->bytes_per_word == 1)
*(u8 *)dspi->rx = rxdata;
else if (dspi->bytes_per_word == 2)
*(u16 *)dspi->rx = rxdata;
else /* dspi->bytes_per_word == 4 */
*(u32 *)dspi->rx = rxdata;
dspi->rx += dspi->bytes_per_word;
} }
static void dspi_tx_dma_callback(void *arg) static void dspi_tx_dma_callback(void *arg)
...@@ -407,8 +448,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) ...@@ -407,8 +448,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi)
dspi->devtype_data->fifo_size; dspi->devtype_data->fifo_size;
while (curr_remaining_bytes) { while (curr_remaining_bytes) {
/* Check if current transfer fits the DMA buffer */ /* Check if current transfer fits the DMA buffer */
dma->curr_xfer_len = curr_remaining_bytes dma->curr_xfer_len = curr_remaining_bytes /
/ dspi->bytes_per_word; dspi->oper_word_size;
if (dma->curr_xfer_len > bytes_per_buffer) if (dma->curr_xfer_len > bytes_per_buffer)
dma->curr_xfer_len = bytes_per_buffer; dma->curr_xfer_len = bytes_per_buffer;
...@@ -418,8 +459,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) ...@@ -418,8 +459,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi)
goto exit; goto exit;
} else { } else {
const int len = const int len = dma->curr_xfer_len *
dma->curr_xfer_len * dspi->bytes_per_word; dspi->oper_word_size;
curr_remaining_bytes -= len; curr_remaining_bytes -= len;
message->actual_length += len; message->actual_length += len;
if (curr_remaining_bytes < 0) if (curr_remaining_bytes < 0)
...@@ -610,124 +651,224 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, ...@@ -610,124 +651,224 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns,
} }
} }
static void fifo_write(struct fsl_dspi *dspi) static void dspi_pushr_write(struct fsl_dspi *dspi)
{ {
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi));
} }
static void cmd_fifo_write(struct fsl_dspi *dspi) static void dspi_pushr_cmd_write(struct fsl_dspi *dspi, u16 cmd)
{ {
u16 cmd = dspi->tx_cmd; /*
* The only time when the PCS doesn't need continuation after this word
if (dspi->len > 0) * is when it's last. We need to look ahead, because we actually call
* dspi_pop_tx (the function that decrements dspi->len) _after_
* dspi_pushr_cmd_write with XSPI mode. As for how much in advance? One
* word is enough. If there's more to transmit than that,
* dspi_xspi_write will know to split the FIFO writes in 2, and
* generate a new PUSHR command with the final word that will have PCS
* deasserted (not continued) here.
*/
if (dspi->len > dspi->oper_word_size)
cmd |= SPI_PUSHR_CMD_CONT; cmd |= SPI_PUSHR_CMD_CONT;
regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd); regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd);
} }
static void tx_fifo_write(struct fsl_dspi *dspi, u16 txdata) static void dspi_pushr_txdata_write(struct fsl_dspi *dspi, u16 txdata)
{ {
regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata); regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata);
} }
static void dspi_tcfq_write(struct fsl_dspi *dspi) static void dspi_xspi_write(struct fsl_dspi *dspi, int cnt, bool eoq)
{ {
/* Clear transfer count */ u16 tx_cmd = dspi->tx_cmd;
dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT;
if (dspi->devtype_data->xspi_mode && dspi->bits_per_word > 16) { if (eoq)
/* Write the CMD FIFO entry first, and then the two tx_cmd |= SPI_PUSHR_CMD_EOQ;
* corresponding TX FIFO entries.
/* Update CTARE */
regmap_write(dspi->regmap, SPI_CTARE(0),
SPI_FRAME_EBITS(dspi->oper_bits_per_word) |
SPI_CTARE_DTCP(cnt));
/*
* Write the CMD FIFO entry first, and then the two
* corresponding TX FIFO entries (or one...).
*/ */
dspi_pushr_cmd_write(dspi, tx_cmd);
/* Fill TX FIFO with as many transfers as possible */
while (cnt--) {
u32 data = dspi_pop_tx(dspi); u32 data = dspi_pop_tx(dspi);
cmd_fifo_write(dspi); dspi_pushr_txdata_write(dspi, data & 0xFFFF);
tx_fifo_write(dspi, data & 0xFFFF); if (dspi->oper_bits_per_word > 16)
tx_fifo_write(dspi, data >> 16); dspi_pushr_txdata_write(dspi, data >> 16);
} else {
/* Write one entry to both TX FIFO and CMD FIFO
* simultaneously.
*/
fifo_write(dspi);
} }
} }
static u32 fifo_read(struct fsl_dspi *dspi) static void dspi_xspi_fifo_write(struct fsl_dspi *dspi)
{ {
u32 rxdata = 0; int num_fifo_entries = dspi->devtype_data->fifo_size;
int bytes_in_flight;
bool eoq = false;
regmap_read(dspi->regmap, SPI_POPR, &rxdata); /* In XSPI mode each 32-bit word occupies 2 TX FIFO entries */
return rxdata; if (dspi->oper_word_size == 4)
} num_fifo_entries /= 2;
static void dspi_tcfq_read(struct fsl_dspi *dspi) /*
{ * Integer division intentionally trims off odd (or non-multiple of 4)
dspi_push_rx(dspi, fifo_read(dspi)); * numbers of bytes at the end of the buffer, which will be sent next
* time using a smaller oper_word_size.
*/
dspi->words_in_flight = dspi->len / dspi->oper_word_size;
if (dspi->words_in_flight > num_fifo_entries)
dspi->words_in_flight = num_fifo_entries;
bytes_in_flight = dspi->words_in_flight * dspi->oper_word_size;
/*
* If the PCS needs to de-assert (i.e. we're at the end of the buffer
* and cs_change does not want the PCS to stay on), then we need a new
* PUSHR command, since this one (for the body of the buffer)
* necessarily has the CONT bit set.
* So send one word less during this go, to force a split and a command
* with a single word next time, when CONT will be unset.
*/
if (!(dspi->tx_cmd & SPI_PUSHR_CMD_CONT) &&
bytes_in_flight == dspi->len)
eoq = true;
dspi_xspi_write(dspi, dspi->words_in_flight, eoq);
} }
static void dspi_eoq_write(struct fsl_dspi *dspi) static void dspi_eoq_fifo_write(struct fsl_dspi *dspi)
{ {
int fifo_size = dspi->devtype_data->fifo_size; int num_fifo_entries = dspi->devtype_data->fifo_size;
u16 xfer_cmd = dspi->tx_cmd; u16 xfer_cmd = dspi->tx_cmd;
dspi->words_in_flight = num_fifo_entries;
/* Fill TX FIFO with as many transfers as possible */ /* Fill TX FIFO with as many transfers as possible */
while (dspi->len && fifo_size--) { while (dspi->len && num_fifo_entries--) {
dspi->tx_cmd = xfer_cmd; dspi->tx_cmd = xfer_cmd;
/* Request EOQF for last transfer in FIFO */ /* Request EOQF for last transfer in FIFO */
if (dspi->len == dspi->bytes_per_word || fifo_size == 0) if (dspi->len == dspi->oper_word_size || num_fifo_entries == 0)
dspi->tx_cmd |= SPI_PUSHR_CMD_EOQ; dspi->tx_cmd |= SPI_PUSHR_CMD_EOQ;
/* Clear transfer count for first transfer in FIFO */
if (fifo_size == (dspi->devtype_data->fifo_size - 1))
dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT;
/* Write combined TX FIFO and CMD FIFO entry */ /* Write combined TX FIFO and CMD FIFO entry */
fifo_write(dspi); dspi_pushr_write(dspi);
} }
} }
static void dspi_eoq_read(struct fsl_dspi *dspi) static u32 dspi_popr_read(struct fsl_dspi *dspi)
{ {
int fifo_size = dspi->devtype_data->fifo_size; u32 rxdata = 0;
regmap_read(dspi->regmap, SPI_POPR, &rxdata);
return rxdata;
}
static void dspi_fifo_read(struct fsl_dspi *dspi)
{
/* Read one FIFO entry and push to rx buffer */ /* Read one FIFO entry and push to rx buffer */
while ((dspi->rx < dspi->rx_end) && fifo_size--) while (dspi->words_in_flight--)
dspi_push_rx(dspi, fifo_read(dspi)); dspi_push_rx(dspi, dspi_popr_read(dspi));
} }
static int dspi_rxtx(struct fsl_dspi *dspi) static void dspi_setup_accel(struct fsl_dspi *dspi)
{ {
struct spi_message *msg = dspi->cur_msg; struct spi_transfer *xfer = dspi->cur_transfer;
enum dspi_trans_mode trans_mode; bool odd = !!(dspi->len & 1);
u16 spi_tcnt;
u32 spi_tcr;
spi_take_timestamp_post(dspi->ctlr, dspi->cur_transfer, /* No accel for frames not multiple of 8 bits at the moment */
dspi->progress, !dspi->irq); if (xfer->bits_per_word % 8)
goto no_accel;
if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) {
dspi->oper_bits_per_word = 16;
} else if (odd && dspi->len <= dspi->devtype_data->fifo_size) {
dspi->oper_bits_per_word = 8;
} else {
/* Start off with maximum supported by hardware */
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
dspi->oper_bits_per_word = 32;
else
dspi->oper_bits_per_word = 16;
/* Get transfer counter (in number of SPI transfers). It was /*
* reset to 0 when transfer(s) were started. * And go down only if the buffer can't be sent with
* words this big
*/ */
regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); do {
spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); if (dspi->len >= DIV_ROUND_UP(dspi->oper_bits_per_word, 8))
break;
dspi->oper_bits_per_word /= 2;
} while (dspi->oper_bits_per_word > 8);
}
if (xfer->bits_per_word == 8 && dspi->oper_bits_per_word == 32) {
dspi->dev_to_host = dspi_8on32_dev_to_host;
dspi->host_to_dev = dspi_8on32_host_to_dev;
} else if (xfer->bits_per_word == 8 && dspi->oper_bits_per_word == 16) {
dspi->dev_to_host = dspi_8on16_dev_to_host;
dspi->host_to_dev = dspi_8on16_host_to_dev;
} else if (xfer->bits_per_word == 16 && dspi->oper_bits_per_word == 32) {
dspi->dev_to_host = dspi_16on32_dev_to_host;
dspi->host_to_dev = dspi_16on32_host_to_dev;
} else {
no_accel:
dspi->dev_to_host = dspi_native_dev_to_host;
dspi->host_to_dev = dspi_native_host_to_dev;
dspi->oper_bits_per_word = xfer->bits_per_word;
}
dspi->oper_word_size = DIV_ROUND_UP(dspi->oper_bits_per_word, 8);
/*
* Update CTAR here (code is common for both EOQ and XSPI modes).
* We will update CTARE in the portion specific to XSPI, when we
* also know the preload value (DTCP).
*/
regmap_write(dspi->regmap, SPI_CTAR(0),
dspi->cur_chip->ctar_val |
SPI_FRAME_BITS(dspi->oper_bits_per_word));
}
static void dspi_fifo_write(struct fsl_dspi *dspi)
{
struct spi_transfer *xfer = dspi->cur_transfer;
struct spi_message *msg = dspi->cur_msg;
int bytes_sent;
dspi_setup_accel(dspi);
spi_take_timestamp_pre(dspi->ctlr, xfer, dspi->progress, !dspi->irq);
if (dspi->devtype_data->trans_mode == DSPI_EOQ_MODE)
dspi_eoq_fifo_write(dspi);
else
dspi_xspi_fifo_write(dspi);
/* Update total number of bytes that were transferred */ /* Update total number of bytes that were transferred */
msg->actual_length += spi_tcnt * dspi->bytes_per_word; bytes_sent = dspi->words_in_flight * dspi->oper_word_size;
dspi->progress += spi_tcnt; msg->actual_length += bytes_sent;
dspi->progress += bytes_sent / DIV_ROUND_UP(xfer->bits_per_word, 8);
trans_mode = dspi->devtype_data->trans_mode; spi_take_timestamp_post(dspi->ctlr, dspi->cur_transfer,
if (trans_mode == DSPI_EOQ_MODE) dspi->progress, !dspi->irq);
dspi_eoq_read(dspi); }
else if (trans_mode == DSPI_TCFQ_MODE)
dspi_tcfq_read(dspi); static int dspi_rxtx(struct fsl_dspi *dspi)
{
dspi_fifo_read(dspi);
if (!dspi->len) if (!dspi->len)
/* Success! */ /* Success! */
return 0; return 0;
spi_take_timestamp_pre(dspi->ctlr, dspi->cur_transfer, dspi_fifo_write(dspi);
dspi->progress, !dspi->irq);
if (trans_mode == DSPI_EOQ_MODE)
dspi_eoq_write(dspi);
else if (trans_mode == DSPI_TCFQ_MODE)
dspi_tcfq_write(dspi);
return -EINPROGRESS; return -EINPROGRESS;
} }
...@@ -741,7 +882,7 @@ static int dspi_poll(struct fsl_dspi *dspi) ...@@ -741,7 +882,7 @@ static int dspi_poll(struct fsl_dspi *dspi)
regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_read(dspi->regmap, SPI_SR, &spi_sr);
regmap_write(dspi->regmap, SPI_SR, spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr);
if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) if (spi_sr & (SPI_SR_EOQF | SPI_SR_CMDTCF))
break; break;
} while (--tries); } while (--tries);
...@@ -759,7 +900,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) ...@@ -759,7 +900,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_read(dspi->regmap, SPI_SR, &spi_sr);
regmap_write(dspi->regmap, SPI_SR, spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr);
if (!(spi_sr & SPI_SR_EOQF)) if (!(spi_sr & (SPI_SR_EOQF | SPI_SR_CMDTCF)))
return IRQ_NONE; return IRQ_NONE;
if (dspi_rxtx(dspi) == 0) { if (dspi_rxtx(dspi) == 0) {
...@@ -805,32 +946,21 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, ...@@ -805,32 +946,21 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
dspi->tx_cmd |= SPI_PUSHR_CMD_CONT; dspi->tx_cmd |= SPI_PUSHR_CMD_CONT;
} }
dspi->void_write_data = dspi->cur_chip->void_write_data;
dspi->tx = transfer->tx_buf; dspi->tx = transfer->tx_buf;
dspi->rx = transfer->rx_buf; dspi->rx = transfer->rx_buf;
dspi->rx_end = dspi->rx + transfer->len;
dspi->len = transfer->len; dspi->len = transfer->len;
dspi->progress = 0; dspi->progress = 0;
/* Validated transfer specific frame size (defaults applied) */
dspi->bits_per_word = transfer->bits_per_word;
if (transfer->bits_per_word <= 8)
dspi->bytes_per_word = 1;
else if (transfer->bits_per_word <= 16)
dspi->bytes_per_word = 2;
else
dspi->bytes_per_word = 4;
regmap_update_bits(dspi->regmap, SPI_MCR, regmap_update_bits(dspi->regmap, SPI_MCR,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF);
/*
* Static CTAR setup for modes that don't dynamically adjust it
* via dspi_setup_accel (aka for DMA)
*/
regmap_write(dspi->regmap, SPI_CTAR(0), regmap_write(dspi->regmap, SPI_CTAR(0),
dspi->cur_chip->ctar_val | dspi->cur_chip->ctar_val |
SPI_FRAME_BITS(transfer->bits_per_word)); SPI_FRAME_BITS(transfer->bits_per_word));
if (dspi->devtype_data->xspi_mode)
regmap_write(dspi->regmap, SPI_CTARE(0),
SPI_FRAME_EBITS(transfer->bits_per_word) |
SPI_CTARE_DTCP(1));
spi_take_timestamp_pre(dspi->ctlr, dspi->cur_transfer, spi_take_timestamp_pre(dspi->ctlr, dspi->cur_transfer,
dspi->progress, !dspi->irq); dspi->progress, !dspi->irq);
...@@ -839,11 +969,11 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, ...@@ -839,11 +969,11 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
switch (trans_mode) { switch (trans_mode) {
case DSPI_EOQ_MODE: case DSPI_EOQ_MODE:
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
dspi_eoq_write(dspi); dspi_fifo_write(dspi);
break; break;
case DSPI_TCFQ_MODE: case DSPI_XSPI_MODE:
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_CMDTCFE);
dspi_tcfq_write(dspi); dspi_fifo_write(dspi);
break; break;
case DSPI_DMA_MODE: case DSPI_DMA_MODE:
regmap_write(dspi->regmap, SPI_RSER, regmap_write(dspi->regmap, SPI_RSER,
...@@ -912,8 +1042,6 @@ static int dspi_setup(struct spi_device *spi) ...@@ -912,8 +1042,6 @@ static int dspi_setup(struct spi_device *spi)
sck_cs_delay = pdata->sck_cs_delay; sck_cs_delay = pdata->sck_cs_delay;
} }
chip->void_write_data = 0;
clkrate = clk_get_rate(dspi->clk); clkrate = clk_get_rate(dspi->clk);
hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate); hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
...@@ -1071,16 +1199,13 @@ static void dspi_init(struct fsl_dspi *dspi) ...@@ -1071,16 +1199,13 @@ static void dspi_init(struct fsl_dspi *dspi)
{ {
unsigned int mcr = SPI_MCR_PCSIS; unsigned int mcr = SPI_MCR_PCSIS;
if (dspi->devtype_data->xspi_mode) if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
mcr |= SPI_MCR_XSPI; mcr |= SPI_MCR_XSPI;
if (!spi_controller_is_slave(dspi->ctlr)) if (!spi_controller_is_slave(dspi->ctlr))
mcr |= SPI_MCR_MASTER; mcr |= SPI_MCR_MASTER;
regmap_write(dspi->regmap, SPI_MCR, mcr); regmap_write(dspi->regmap, SPI_MCR, mcr);
regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR);
if (dspi->devtype_data->xspi_mode)
regmap_write(dspi->regmap, SPI_CTARE(0),
SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1));
} }
static int dspi_slave_abort(struct spi_master *master) static int dspi_slave_abort(struct spi_master *master)
...@@ -1102,6 +1227,22 @@ static int dspi_slave_abort(struct spi_master *master) ...@@ -1102,6 +1227,22 @@ static int dspi_slave_abort(struct spi_master *master)
return 0; return 0;
} }
/*
* EOQ mode will inevitably deassert its PCS signal on last word in a queue
* (hardware limitation), so we need to inform the spi_device that larger
* buffers than the FIFO size are going to have the chip select randomly
* toggling, so it has a chance to adapt its message sizes.
*/
static size_t dspi_max_message_size(struct spi_device *spi)
{
struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller);
if (dspi->devtype_data->trans_mode == DSPI_EOQ_MODE)
return dspi->devtype_data->fifo_size;
return SIZE_MAX;
}
static int dspi_probe(struct platform_device *pdev) static int dspi_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
...@@ -1123,6 +1264,7 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1123,6 +1264,7 @@ static int dspi_probe(struct platform_device *pdev)
ctlr->setup = dspi_setup; ctlr->setup = dspi_setup;
ctlr->transfer_one_message = dspi_transfer_one_message; ctlr->transfer_one_message = dspi_transfer_one_message;
ctlr->max_message_size = dspi_max_message_size;
ctlr->dev.of_node = pdev->dev.of_node; ctlr->dev.of_node = pdev->dev.of_node;
ctlr->cleanup = dspi_cleanup; ctlr->cleanup = dspi_cleanup;
...@@ -1159,7 +1301,7 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1159,7 +1301,7 @@ static int dspi_probe(struct platform_device *pdev)
} }
} }
if (dspi->devtype_data->xspi_mode) if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else else
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
...@@ -1171,7 +1313,7 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1171,7 +1313,7 @@ static int dspi_probe(struct platform_device *pdev)
goto out_ctlr_put; goto out_ctlr_put;
} }
if (dspi->devtype_data->xspi_mode) if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
regmap_config = &dspi_xspi_regmap_config[0]; regmap_config = &dspi_xspi_regmap_config[0];
else else
regmap_config = &dspi_regmap_config; regmap_config = &dspi_regmap_config;
...@@ -1183,7 +1325,7 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1183,7 +1325,7 @@ static int dspi_probe(struct platform_device *pdev)
goto out_ctlr_put; goto out_ctlr_put;
} }
if (dspi->devtype_data->xspi_mode) { if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) {
dspi->regmap_pushr = devm_regmap_init_mmio( dspi->regmap_pushr = devm_regmap_init_mmio(
&pdev->dev, base + SPI_PUSHR, &pdev->dev, base + SPI_PUSHR,
&dspi_xspi_regmap_config[1]); &dspi_xspi_regmap_config[1]);
...@@ -1208,9 +1350,6 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1208,9 +1350,6 @@ static int dspi_probe(struct platform_device *pdev)
dspi_init(dspi); dspi_init(dspi);
if (dspi->devtype_data->trans_mode == DSPI_TCFQ_MODE)
goto poll_mode;
dspi->irq = platform_get_irq(pdev, 0); dspi->irq = platform_get_irq(pdev, 0);
if (dspi->irq <= 0) { if (dspi->irq <= 0) {
dev_info(&pdev->dev, dev_info(&pdev->dev,
......
...@@ -1515,17 +1515,15 @@ void spi_take_timestamp_pre(struct spi_controller *ctlr, ...@@ -1515,17 +1515,15 @@ void spi_take_timestamp_pre(struct spi_controller *ctlr,
if (!xfer->ptp_sts) if (!xfer->ptp_sts)
return; return;
if (xfer->timestamped_pre) if (xfer->timestamped)
return; return;
if (progress < xfer->ptp_sts_word_pre) if (progress > xfer->ptp_sts_word_pre)
return; return;
/* Capture the resolution of the timestamp */ /* Capture the resolution of the timestamp */
xfer->ptp_sts_word_pre = progress; xfer->ptp_sts_word_pre = progress;
xfer->timestamped_pre = true;
if (irqs_off) { if (irqs_off) {
local_irq_save(ctlr->irq_flags); local_irq_save(ctlr->irq_flags);
preempt_disable(); preempt_disable();
...@@ -1554,7 +1552,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr, ...@@ -1554,7 +1552,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
if (!xfer->ptp_sts) if (!xfer->ptp_sts)
return; return;
if (xfer->timestamped_post) if (xfer->timestamped)
return; return;
if (progress < xfer->ptp_sts_word_post) if (progress < xfer->ptp_sts_word_post)
...@@ -1570,7 +1568,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr, ...@@ -1570,7 +1568,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
/* Capture the resolution of the timestamp */ /* Capture the resolution of the timestamp */
xfer->ptp_sts_word_post = progress; xfer->ptp_sts_word_post = progress;
xfer->timestamped_post = true; xfer->timestamped = true;
} }
EXPORT_SYMBOL_GPL(spi_take_timestamp_post); EXPORT_SYMBOL_GPL(spi_take_timestamp_post);
...@@ -1675,12 +1673,9 @@ void spi_finalize_current_message(struct spi_controller *ctlr) ...@@ -1675,12 +1673,9 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
} }
} }
if (unlikely(ctlr->ptp_sts_supported)) { if (unlikely(ctlr->ptp_sts_supported))
list_for_each_entry(xfer, &mesg->transfers, transfer_list) { list_for_each_entry(xfer, &mesg->transfers, transfer_list)
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_pre); WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped);
WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_post);
}
}
spi_unmap_msg(ctlr, mesg); spi_unmap_msg(ctlr, mesg);
......
...@@ -933,8 +933,7 @@ struct spi_transfer { ...@@ -933,8 +933,7 @@ struct spi_transfer {
struct ptp_system_timestamp *ptp_sts; struct ptp_system_timestamp *ptp_sts;
bool timestamped_pre; bool timestamped;
bool timestamped_post;
struct list_head transfer_list; struct list_head transfer_list;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment