diff options
author | NeilBrown <neil@brown.name> | 2018-06-07 08:04:21 +1000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-06-17 09:05:11 +0200 |
commit | bf732c6bff5b5767a1c2ec6495dccd76d71c05eb (patch) | |
tree | ddfcdcd757e615ab52b872731c18501a7dd6146d /drivers/staging/mt7621-spi | |
parent | a83834c1c9ba446f694c0bc29bce25687bf06582 (diff) | |
download | blackbird-op-linux-bf732c6bff5b5767a1c2ec6495dccd76d71c05eb.tar.gz blackbird-op-linux-bf732c6bff5b5767a1c2ec6495dccd76d71c05eb.zip |
staging: mt7621-spi: revised half-duplex message handling
The mt7621 SPI engine has a 32 byte buffer and the driver
currently only allows 32-byte read requests and 36 bytes writes
(there is a 4byte op/addr buffer).
This is an unnecessary limitation. As the SPI clock is controlled
by the host it is quite acceptable to send a larger message in
multiple smaller transactions. As long as Chip Select is kept asserted
the whole time, the SPI engine can be run multiple times for
a single SPI message.
This patch factors out the transaction logic and calls for each
transfer in the message. A write transfer might leave bytes in the
buffer to be combined with a following read transfer, as this is
a common pattern.
With this in place, we can remove the current max_transfer_size limit.
In testing, this increases the read throughput for a NOR flash chip
from 1.4MB/s to 2.3MB/s, a 50% improvement.
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/mt7621-spi')
-rw-r--r-- | drivers/staging/mt7621-spi/spi-mt7621.c | 165 |
1 files changed, 98 insertions, 67 deletions
diff --git a/drivers/staging/mt7621-spi/spi-mt7621.c b/drivers/staging/mt7621-spi/spi-mt7621.c index d43576d2a3f9..11474e6ad01b 100644 --- a/drivers/staging/mt7621-spi/spi-mt7621.c +++ b/drivers/staging/mt7621-spi/spi-mt7621.c @@ -65,6 +65,7 @@ struct mt7621_spi { unsigned int sys_freq; unsigned int speed; struct clk *clk; + int pending_write; struct mt7621_spi_ops *ops; }; @@ -96,6 +97,7 @@ static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex) master &= ~(1 << 10); mt7621_spi_write(rs, MT7621_SPI_MASTER, master); + rs->pending_write = 0; } static void mt7621_spi_set_cs(struct spi_device *spi, int enable) @@ -173,90 +175,124 @@ static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs) return -ETIMEDOUT; } -static int mt7621_spi_transfer_half_duplex(struct spi_master *master, - struct spi_message *m) +static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs, + int rx_len, u8 *buf) { - struct mt7621_spi *rs = spi_master_get_devdata(master); - struct spi_device *spi = m->spi; - unsigned int speed = spi->max_speed_hz; - struct spi_transfer *t = NULL; - int status = 0; - int i, len = 0; - int rx_len = 0; - u32 data[9] = { 0 }; - u32 val; + /* Combine with any pending write, and perform one or + * more half-duplex transactions reading 'len' bytes. + * Data to be written is already in MT7621_SPI_DATA* + */ + int tx_len = rs->pending_write; - mt7621_spi_wait_till_ready(rs); + rs->pending_write = 0; - list_for_each_entry(t, &m->transfers, transfer_list) { - const u8 *buf = t->tx_buf; + while (rx_len || tx_len) { + int i; + u32 val = (min(tx_len, 4) * 8) << 24; + int rx = min(rx_len, 32); - if (t->rx_buf) - rx_len += t->len; + if (tx_len > 4) + val |= (tx_len - 4) * 8; + val |= (rx * 8) << 12; + mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val); - if (!buf) - continue; + tx_len = 0; - if (t->speed_hz < speed) - speed = t->speed_hz; + val = mt7621_spi_read(rs, MT7621_SPI_TRANS); + val |= SPI_CTL_START; + mt7621_spi_write(rs, MT7621_SPI_TRANS, val); - if (WARN_ON(len + t->len > 36)) { - status = -EIO; - goto msg_done; - } + mt7621_spi_wait_till_ready(rs); - for (i = 0; i < t->len; i++, len++) - data[len / 4] |= buf[i] << (8 * (len & 3)); + for (i = 0; i < rx; i++) { + if ((i % 4) == 0) + val = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i); + *buf++ = val & 0xff; + val >>= 8; + } + rx_len -= i; } +} - if (WARN_ON(rx_len > 32)) { - status = -EIO; - goto msg_done; - } +static inline void mt7621_spi_flush(struct mt7621_spi *rs) +{ + mt7621_spi_read_half_duplex(rs, 0, NULL); +} - if (mt7621_spi_prepare(spi, speed)) { - status = -EIO; - goto msg_done; +static void mt7621_spi_write_half_duplex(struct mt7621_spi *rs, + int tx_len, const u8 *buf) +{ + int val = 0; + int len = rs->pending_write; + + if (len & 3) { + val = mt7621_spi_read(rs, MT7621_SPI_OPCODE + (len & ~3)); + if (len < 4) { + val <<= (4 - len) * 8; + val = swab32(val); + } } - data[0] = swab32(data[0]); - if (len < 4) - data[0] >>= (4 - len) * 8; - for (i = 0; i < len; i += 4) - mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]); - - val = (min_t(int, len, 4) * 8) << 24; - if (len > 4) - val |= (len - 4) * 8; - val |= (rx_len * 8) << 12; - mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val); + while (tx_len > 0) { + if (len >= 36) { + rs->pending_write = len; + mt7621_spi_flush(rs); + len = 0; + } - mt7621_spi_set_cs(spi, 1); + val |= *buf++ << (8 * (len & 3)); + len++; + if ((len & 3) == 0) { + if (len == 4) + /* The byte-order of the opcode is weird! */ + val = swab32(val); + mt7621_spi_write(rs, MT7621_SPI_OPCODE + len - 4, val); + val = 0; + } + tx_len -= 1; + } + if (len & 3) { + if (len < 4) { + val = swab32(val); + val >>= (4 - len) * 8; + } + mt7621_spi_write(rs, MT7621_SPI_OPCODE + (len & ~3), val); + } + rs->pending_write = len; +} - val = mt7621_spi_read(rs, MT7621_SPI_TRANS); - val |= SPI_CTL_START; - mt7621_spi_write(rs, MT7621_SPI_TRANS, val); +static int mt7621_spi_transfer_half_duplex(struct spi_master *master, + struct spi_message *m) +{ + struct mt7621_spi *rs = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + unsigned int speed = spi->max_speed_hz; + struct spi_transfer *t = NULL; + int status = 0; mt7621_spi_wait_till_ready(rs); - mt7621_spi_set_cs(spi, 0); - - for (i = 0; i < rx_len; i += 4) - data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i); + list_for_each_entry(t, &m->transfers, transfer_list) + if (t->speed_hz < speed) + speed = t->speed_hz; - m->actual_length = len + rx_len; + if (mt7621_spi_prepare(spi, speed)) { + status = -EIO; + goto msg_done; + } - len = 0; + mt7621_spi_set_cs(spi, 1); + m->actual_length = 0; list_for_each_entry(t, &m->transfers, transfer_list) { - u8 *buf = t->rx_buf; - - if (!buf) - continue; - - for (i = 0; i < t->len; i++, len++) - buf[i] = data[len / 4] >> (8 * (len & 3)); + if (t->rx_buf) + mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf); + else if (t->tx_buf) + mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf); + m->actual_length += t->len; } + mt7621_spi_flush(rs); + mt7621_spi_set_cs(spi, 0); msg_done: m->status = status; spi_finalize_current_message(master); @@ -383,11 +419,6 @@ static const struct of_device_id mt7621_spi_match[] = { }; MODULE_DEVICE_TABLE(of, mt7621_spi_match); -static size_t max_transfer_size(struct spi_device *spi) -{ - return 32; -} - static int mt7621_spi_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -433,7 +464,6 @@ static int mt7621_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; master->num_chipselect = 2; - master->max_transfer_size = max_transfer_size; dev_set_drvdata(&pdev->dev, master); @@ -443,6 +473,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) rs->master = master; rs->sys_freq = clk_get_rate(rs->clk); rs->ops = ops; + rs->pending_write = 0; dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq); device_reset(&pdev->dev); |