spi: imx: Use the longuest possible burst size when in dynamic_burst

Dynamic burst mode allows to group together multiple words and send them
in one continuous burst. When the number of bytes to be sent is not a
strict multiple of the FIFO entry size (32 bits), the controller expects
the non aligned bits to be sent first.

This commit adds support for this particular constraint, avoiding the
need to send the non-aligned bytes one by one at the end of the
transfer, speeding-up transfer speed in that case.

With this method, a transfer is divided into multiple bursts, limited in
size by the maximum amount of data that the controller can transfer in
one continuous burst (which is 512 bytes).

The non-512 byte part of the transfer is sent first. The remaining bytes
to be transferred in the current burst is stored in the 'remainder'
field.

With this method, the read_u32 field is no longer necessary, and is
removed.

This was tested on imx6 solo and imx6 quad.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Maxime Chevallier 2018-07-17 16:31:54 +02:00 committed by Mark Brown
parent 0486dd4d61
commit 2ca300ac6e
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 86 additions and 38 deletions

View File

@ -94,7 +94,7 @@ struct spi_imx_data {
void *rx_buf;
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
unsigned int dynamic_burst, read_u32;
unsigned int dynamic_burst;
/* Slave mode */
bool slave_mode;
@ -139,6 +139,8 @@ static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \
*(type *)spi_imx->rx_buf = val; \
spi_imx->rx_buf += sizeof(type); \
} \
\
spi_imx->remainder -= sizeof(type); \
}
#define MXC_SPI_BUF_TX(type) \
@ -292,22 +294,36 @@ static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx)
*(u32 *)spi_imx->rx_buf = val;
spi_imx->rx_buf += sizeof(u32);
}
spi_imx->remainder -= sizeof(u32);
}
static void spi_imx_buf_rx_swap(struct spi_imx_data *spi_imx)
{
unsigned int bytes_per_word;
int unaligned;
u32 val;
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
if (spi_imx->read_u32) {
unaligned = spi_imx->remainder % 4;
if (!unaligned) {
spi_imx_buf_rx_swap_u32(spi_imx);
return;
}
if (bytes_per_word == 1)
spi_imx_buf_rx_u8(spi_imx);
else if (bytes_per_word == 2)
if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) {
spi_imx_buf_rx_u16(spi_imx);
return;
}
val = readl(spi_imx->base + MXC_CSPIRXDATA);
while (unaligned--) {
if (spi_imx->rx_buf) {
*(u8 *)spi_imx->rx_buf = (val >> (8 * unaligned)) & 0xff;
spi_imx->rx_buf++;
}
spi_imx->remainder--;
}
}
static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx)
@ -336,40 +352,30 @@ static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx)
static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
{
u32 ctrl, val;
unsigned int bytes_per_word;
int unaligned;
u32 val = 0;
if (spi_imx->count == spi_imx->remainder) {
ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
if (spi_imx->count > MX51_ECSPI_CTRL_MAX_BURST) {
spi_imx->remainder = spi_imx->count %
MX51_ECSPI_CTRL_MAX_BURST;
val = MX51_ECSPI_CTRL_MAX_BURST * 8 - 1;
} else if (spi_imx->count >= sizeof(u32)) {
spi_imx->remainder = spi_imx->count % sizeof(u32);
val = (spi_imx->count - spi_imx->remainder) * 8 - 1;
} else {
spi_imx->remainder = 0;
val = spi_imx->bits_per_word - 1;
spi_imx->read_u32 = 0;
}
unaligned = spi_imx->count % 4;
ctrl |= (val << MX51_ECSPI_CTRL_BL_OFFSET);
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
}
if (spi_imx->count >= sizeof(u32)) {
if (!unaligned) {
spi_imx_buf_tx_swap_u32(spi_imx);
return;
}
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
if (bytes_per_word == 1)
spi_imx_buf_tx_u8(spi_imx);
else if (bytes_per_word == 2)
if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) {
spi_imx_buf_tx_u16(spi_imx);
return;
}
while (unaligned--) {
if (spi_imx->tx_buf) {
val |= *(u8 *)spi_imx->tx_buf << (8 * unaligned);
spi_imx->tx_buf++;
}
spi_imx->count--;
}
writel(val, spi_imx->base + MXC_CSPITXDATA);
}
static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx)
@ -388,6 +394,8 @@ static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx)
spi_imx->rx_buf += n_bytes;
spi_imx->slave_burst -= n_bytes;
}
spi_imx->remainder -= sizeof(u32);
}
static void mx53_ecspi_tx_slave(struct spi_imx_data *spi_imx)
@ -997,12 +1005,52 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active);
}
static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits)
{
u32 ctrl;
ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
ctrl |= ((n_bits - 1) << MX51_ECSPI_CTRL_BL_OFFSET);
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
}
static void spi_imx_push(struct spi_imx_data *spi_imx)
{
unsigned int burst_len, fifo_words;
if (spi_imx->dynamic_burst)
fifo_words = 4;
else
fifo_words = spi_imx_bytes_per_word(spi_imx->bits_per_word);
/*
* Reload the FIFO when the remaining bytes to be transferred in the
* current burst is 0. This only applies when bits_per_word is a
* multiple of 8.
*/
if (!spi_imx->remainder) {
if (spi_imx->dynamic_burst) {
/* We need to deal unaligned data first */
burst_len = spi_imx->count % MX51_ECSPI_CTRL_MAX_BURST;
if (!burst_len)
burst_len = MX51_ECSPI_CTRL_MAX_BURST;
spi_imx_set_burst_len(spi_imx, burst_len * 8);
spi_imx->remainder = burst_len;
} else {
spi_imx->remainder = fifo_words;
}
}
while (spi_imx->txfifo < spi_imx->devtype_data->fifo_size) {
if (!spi_imx->count)
break;
if (spi_imx->txfifo && (spi_imx->count == spi_imx->remainder))
if (spi_imx->dynamic_burst &&
spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder,
fifo_words))
break;
spi_imx->tx(spi_imx);
spi_imx->txfifo++;
@ -1108,12 +1156,9 @@ static int spi_imx_setupxfer(struct spi_device *spi,
spi_imx->bits_per_word == 16 ||
spi_imx->bits_per_word == 32)) {
spi_imx->read_u32 = 1;
spi_imx->rx = spi_imx_buf_rx_swap;
spi_imx->tx = spi_imx_buf_tx_swap;
spi_imx->dynamic_burst = 1;
spi_imx->remainder = t->len;
} else {
if (spi_imx->bits_per_word <= 8) {
@ -1126,6 +1171,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
spi_imx->rx = spi_imx_buf_rx_u32;
spi_imx->tx = spi_imx_buf_tx_u32;
}
spi_imx->dynamic_burst = 0;
}
if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t))
@ -1309,6 +1355,7 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
spi_imx->rx_buf = transfer->rx_buf;
spi_imx->count = transfer->len;
spi_imx->txfifo = 0;
spi_imx->remainder = 0;
reinit_completion(&spi_imx->xfer_done);
@ -1346,6 +1393,7 @@ static int spi_imx_pio_transfer_slave(struct spi_device *spi,
spi_imx->rx_buf = transfer->rx_buf;
spi_imx->count = transfer->len;
spi_imx->txfifo = 0;
spi_imx->remainder = 0;
reinit_completion(&spi_imx->xfer_done);
spi_imx->slave_aborted = false;