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>
This commit is contained in:
parent
a83834c1c9
commit
bf732c6bff
|
@ -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,6 +175,92 @@ static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs,
|
||||
int rx_len, u8 *buf)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
rs->pending_write = 0;
|
||||
|
||||
while (rx_len || tx_len) {
|
||||
int i;
|
||||
u32 val = (min(tx_len, 4) * 8) << 24;
|
||||
int rx = min(rx_len, 32);
|
||||
|
||||
if (tx_len > 4)
|
||||
val |= (tx_len - 4) * 8;
|
||||
val |= (rx * 8) << 12;
|
||||
mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
|
||||
|
||||
tx_len = 0;
|
||||
|
||||
val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
|
||||
val |= SPI_CTL_START;
|
||||
mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
|
||||
|
||||
mt7621_spi_wait_till_ready(rs);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mt7621_spi_flush(struct mt7621_spi *rs)
|
||||
{
|
||||
mt7621_spi_read_half_duplex(rs, 0, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
while (tx_len > 0) {
|
||||
if (len >= 36) {
|
||||
rs->pending_write = len;
|
||||
mt7621_spi_flush(rs);
|
||||
len = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
|
@ -181,82 +269,30 @@ static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
|
|||
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;
|
||||
|
||||
mt7621_spi_wait_till_ready(rs);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
const u8 *buf = t->tx_buf;
|
||||
|
||||
if (t->rx_buf)
|
||||
rx_len += t->len;
|
||||
|
||||
if (!buf)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list)
|
||||
if (t->speed_hz < speed)
|
||||
speed = t->speed_hz;
|
||||
|
||||
if (WARN_ON(len + t->len > 36)) {
|
||||
status = -EIO;
|
||||
goto msg_done;
|
||||
}
|
||||
|
||||
for (i = 0; i < t->len; i++, len++)
|
||||
data[len / 4] |= buf[i] << (8 * (len & 3));
|
||||
}
|
||||
|
||||
if (WARN_ON(rx_len > 32)) {
|
||||
status = -EIO;
|
||||
goto msg_done;
|
||||
}
|
||||
|
||||
if (mt7621_spi_prepare(spi, speed)) {
|
||||
status = -EIO;
|
||||
goto msg_done;
|
||||
}
|
||||
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);
|
||||
|
||||
mt7621_spi_set_cs(spi, 1);
|
||||
|
||||
val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
|
||||
val |= SPI_CTL_START;
|
||||
mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
|
||||
|
||||
mt7621_spi_wait_till_ready(rs);
|
||||
m->actual_length = 0;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
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);
|
||||
|
||||
for (i = 0; i < rx_len; i += 4)
|
||||
data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
|
||||
|
||||
m->actual_length = len + rx_len;
|
||||
|
||||
len = 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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue