mirror of https://github.com/hak5/openwrt-owl.git
bcm63xx: fix spi transfer handling
* Accept transfers without bits_per_word set. * Work around the inability of the hardware of keeping CS asserted. Signed-off-by: Jonas Gorski <jogo@openwrt.org> SVN-Revision: 34320owl
parent
2a01fa43a5
commit
1baeede939
|
@ -0,0 +1,29 @@
|
|||
From fbef4dff80be6254e36ab5b9c655d248a3991ded Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Sat, 24 Nov 2012 12:08:22 +0100
|
||||
Subject: [PATCH 3.7] spi/bcm63xx: fix transfer bits_per_words check
|
||||
|
||||
Transfers often do not have bits_per_words set, so use the spi device's
|
||||
bits_per_words in this case.
|
||||
|
||||
This fixes the driver rejecting valid transfers e.g. generated by
|
||||
spi_write() or spi_read().
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
drivers/spi/spi-bcm63xx.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-bcm63xx.c
|
||||
+++ b/drivers/spi/spi-bcm63xx.c
|
||||
@@ -103,7 +103,8 @@ static int bcm63xx_spi_check_transfer(st
|
||||
{
|
||||
u8 bits_per_word;
|
||||
|
||||
- bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
|
||||
+ bits_per_word = (t && t->bits_per_word) ?
|
||||
+ t->bits_per_word : spi->bits_per_word;
|
||||
if (bits_per_word != 8) {
|
||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, bits_per_word);
|
|
@ -0,0 +1,279 @@
|
|||
From 0f2ae1e1282ff64f74a5e36f7da874f94911225e Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Wed, 14 Nov 2012 22:22:33 +0100
|
||||
Subject: [PATCH] spi/bcm63xx: fix multi transfer messages
|
||||
|
||||
The BCM63XX SPI controller does not support keeping CS asserted after
|
||||
sending its buffer. This breaks common usages like spi_write_then_read,
|
||||
where it is expected to be kept active during the whole transfers.
|
||||
|
||||
Work around this by combining the transfers into one if the buffer
|
||||
allows. For spi_write_then_read, use the prepend byte feature to write
|
||||
to "prepend" the write if it is less than 15 bytes, allowing the whole
|
||||
fifo size for the read.
|
||||
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
Tested on a SPI conntected switch which required keeping CS active between
|
||||
the register read command and reading the register contents.
|
||||
|
||||
Based on Mark's spi/next.
|
||||
|
||||
Not sure if this is stable material, as it's quite invasive.
|
||||
|
||||
drivers/spi/spi-bcm63xx.c | 172 ++++++++++++++++++++++++++++++---------------
|
||||
1 file changed, 117 insertions(+), 55 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-bcm63xx.c
|
||||
+++ b/drivers/spi/spi-bcm63xx.c
|
||||
@@ -38,6 +38,8 @@
|
||||
#define PFX KBUILD_MODNAME
|
||||
#define DRV_VER "0.1.2"
|
||||
|
||||
+#define BCM63XX_SPI_MAX_PREPEND 15
|
||||
+
|
||||
struct bcm63xx_spi {
|
||||
struct completion done;
|
||||
|
||||
@@ -50,16 +52,10 @@ struct bcm63xx_spi {
|
||||
unsigned int msg_type_shift;
|
||||
unsigned int msg_ctl_width;
|
||||
|
||||
- /* Data buffers */
|
||||
- const unsigned char *tx_ptr;
|
||||
- unsigned char *rx_ptr;
|
||||
-
|
||||
/* data iomem */
|
||||
u8 __iomem *tx_io;
|
||||
const u8 __iomem *rx_io;
|
||||
|
||||
- int remaining_bytes;
|
||||
-
|
||||
struct clk *clk;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
@@ -184,50 +180,60 @@ static int bcm63xx_spi_setup(struct spi_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/* Fill the TX FIFO with as many bytes as possible */
|
||||
-static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
|
||||
-{
|
||||
- u8 size;
|
||||
-
|
||||
- /* Fill the Tx FIFO with as many bytes as possible */
|
||||
- size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
|
||||
- bs->fifo_size;
|
||||
- memcpy_toio(bs->tx_io, bs->tx_ptr, size);
|
||||
- bs->remaining_bytes -= size;
|
||||
-}
|
||||
-
|
||||
static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
|
||||
- struct spi_transfer *t)
|
||||
+ struct spi_transfer *first,
|
||||
+ unsigned int n_transfers)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
|
||||
u16 msg_ctl;
|
||||
u16 cmd;
|
||||
+ unsigned int i, timeout, total_len = 0, prepend_len = 0, len = 0;
|
||||
+ struct spi_transfer *t = first;
|
||||
+ u8 rx_tail;
|
||||
+ bool do_rx = false;
|
||||
+ bool do_tx = false;
|
||||
|
||||
/* Disable the CMD_DONE interrupt */
|
||||
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
|
||||
|
||||
- dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
|
||||
- t->tx_buf, t->rx_buf, t->len);
|
||||
+ if (n_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
|
||||
+ prepend_len = t->len;
|
||||
+
|
||||
+ /* prepare the buffer */
|
||||
+ for (i = 0; i < n_transfers; i++) {
|
||||
+ if (t->tx_buf) {
|
||||
+ do_tx = true;
|
||||
+ memcpy_toio(bs->tx_io + total_len, t->tx_buf, t->len);
|
||||
+
|
||||
+ /* don't prepend more than one tx */
|
||||
+ if (t != first)
|
||||
+ prepend_len = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (t->rx_buf) {
|
||||
+ do_rx = true;
|
||||
+ if (t == first)
|
||||
+ prepend_len = 0;
|
||||
+ }
|
||||
|
||||
- /* Transmitter is inhibited */
|
||||
- bs->tx_ptr = t->tx_buf;
|
||||
- bs->rx_ptr = t->rx_buf;
|
||||
-
|
||||
- if (t->tx_buf) {
|
||||
- bs->remaining_bytes = t->len;
|
||||
- bcm63xx_spi_fill_tx_fifo(bs);
|
||||
+ total_len += t->len;
|
||||
+
|
||||
+ t = list_entry(t->transfer_list.next, struct spi_transfer,
|
||||
+ transfer_list);
|
||||
}
|
||||
|
||||
+ len = total_len - prepend_len;
|
||||
+
|
||||
init_completion(&bs->done);
|
||||
|
||||
/* Fill in the Message control register */
|
||||
- msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
|
||||
+ msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
|
||||
|
||||
- if (t->rx_buf && t->tx_buf)
|
||||
+ if (do_rx && do_tx && prepend_len == 0)
|
||||
msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
|
||||
- else if (t->rx_buf)
|
||||
+ else if (do_rx)
|
||||
msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
|
||||
- else if (t->tx_buf)
|
||||
+ else if (do_tx)
|
||||
msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
|
||||
|
||||
switch (bs->msg_ctl_width) {
|
||||
@@ -245,14 +251,41 @@ static unsigned int bcm63xx_txrx_bufs(st
|
||||
|
||||
/* Issue the transfer */
|
||||
cmd = SPI_CMD_START_IMMEDIATE;
|
||||
- cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
|
||||
+ cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
|
||||
cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
|
||||
bcm_spi_writew(bs, cmd, SPI_CMD);
|
||||
|
||||
/* Enable the CMD_DONE interrupt */
|
||||
bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
|
||||
|
||||
- return t->len - bs->remaining_bytes;
|
||||
+ timeout = wait_for_completion_timeout(&bs->done, HZ);
|
||||
+ if (!timeout)
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ /* read out all data */
|
||||
+ rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
|
||||
+
|
||||
+ if (do_rx && rx_tail != len)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!rx_tail)
|
||||
+ return total_len;
|
||||
+
|
||||
+ len = 0;
|
||||
+ t = first;
|
||||
+ /* Read out all the data */
|
||||
+ for (i = 0; i < n_transfers; i++) {
|
||||
+ if (t->rx_buf)
|
||||
+ memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
|
||||
+
|
||||
+ if (t != first || prepend_len == 0)
|
||||
+ len += t->len;
|
||||
+
|
||||
+ t = list_entry(t->transfer_list.next, struct spi_transfer,
|
||||
+ transfer_list);
|
||||
+ }
|
||||
+
|
||||
+ return total_len;
|
||||
}
|
||||
|
||||
static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
|
||||
@@ -277,42 +310,71 @@ static int bcm63xx_spi_transfer_one(stru
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
- struct spi_transfer *t;
|
||||
+ struct spi_transfer *t, *first = NULL;
|
||||
struct spi_device *spi = m->spi;
|
||||
int status = 0;
|
||||
- unsigned int timeout = 0;
|
||||
+ unsigned int n_transfers = 0, total_len = 0;
|
||||
+ bool can_use_prepend = false;
|
||||
|
||||
+ /*
|
||||
+ * This SPI controller does not support keeping CS active after a
|
||||
+ * transfer, so we need to combine the transfers into one until we may
|
||||
+ * deassert CS.
|
||||
+ */
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
- unsigned int len = t->len;
|
||||
- u8 rx_tail;
|
||||
-
|
||||
status = bcm63xx_spi_check_transfer(spi, t);
|
||||
if (status < 0)
|
||||
goto exit;
|
||||
|
||||
- /* configure adapter for a new transfer */
|
||||
- bcm63xx_spi_setup_transfer(spi, t);
|
||||
+ if (!first)
|
||||
+ first = t;
|
||||
|
||||
- while (len) {
|
||||
- /* send the data */
|
||||
- len -= bcm63xx_txrx_bufs(spi, t);
|
||||
-
|
||||
- timeout = wait_for_completion_timeout(&bs->done, HZ);
|
||||
- if (!timeout) {
|
||||
- status = -ETIMEDOUT;
|
||||
- goto exit;
|
||||
- }
|
||||
+ n_transfers++;
|
||||
+ total_len += t->len;
|
||||
+
|
||||
+ if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
|
||||
+ first->len <= BCM63XX_SPI_MAX_PREPEND)
|
||||
+ can_use_prepend = true;
|
||||
+ else if (can_use_prepend && t->tx_buf)
|
||||
+ can_use_prepend = false;
|
||||
+
|
||||
+ if ((can_use_prepend &&
|
||||
+ total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
|
||||
+ (!can_use_prepend && total_len > bs->fifo_size)) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
|
||||
- /* read out all data */
|
||||
- rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
|
||||
+ /* all transfers have to be made at the same speed */
|
||||
+ if (t->speed_hz != first->speed_hz) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
|
||||
- /* Read out all the data */
|
||||
- if (rx_tail)
|
||||
- memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
|
||||
+ /* CS will be deasserted directly after the transfer */
|
||||
+ if (t->delay_usecs) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
}
|
||||
|
||||
- m->actual_length += t->len;
|
||||
+ if (t->cs_change ||
|
||||
+ list_is_last(&t->transfer_list, &m->transfers)) {
|
||||
+ /* configure adapter for a new transfer */
|
||||
+ bcm63xx_spi_setup_transfer(spi, first);
|
||||
+
|
||||
+ status = bcm63xx_txrx_bufs(spi, first, n_transfers);
|
||||
+ if (status < 0)
|
||||
+ goto exit;
|
||||
+
|
||||
+ m->actual_length += status;
|
||||
+ first = NULL;
|
||||
+ status = 0;
|
||||
+ n_transfers = 0;
|
||||
+ total_len = 0;
|
||||
+ can_use_prepend = false;
|
||||
+ }
|
||||
}
|
||||
+
|
||||
exit:
|
||||
m->status = status;
|
||||
spi_finalize_current_message(master);
|
|
@ -23,7 +23,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>
|
|||
.width = 2,
|
||||
--- a/drivers/mtd/redboot.c
|
||||
+++ b/drivers/mtd/redboot.c
|
||||
@@ -75,6 +75,7 @@ static int parse_redboot_partitions(stru
|
||||
@@ -72,6 +72,7 @@ static int parse_redboot_partitions(stru
|
||||
int nulllen = 0;
|
||||
int numslots;
|
||||
unsigned long offset;
|
||||
|
@ -31,7 +31,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>
|
|||
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
|
||||
static char nullstring[] = "unallocated";
|
||||
#endif
|
||||
@@ -181,6 +182,16 @@ static int parse_redboot_partitions(stru
|
||||
@@ -178,6 +179,16 @@ static int parse_redboot_partitions(stru
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>
|
|||
for (i = 0; i < numslots; i++) {
|
||||
struct fis_list *new_fl, **prev;
|
||||
|
||||
@@ -201,10 +212,10 @@ static int parse_redboot_partitions(stru
|
||||
@@ -198,10 +209,10 @@ static int parse_redboot_partitions(stru
|
||||
goto out;
|
||||
}
|
||||
new_fl->img = &buf[i];
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
From fbef4dff80be6254e36ab5b9c655d248a3991ded Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Sat, 24 Nov 2012 12:08:22 +0100
|
||||
Subject: [PATCH 3.7] spi/bcm63xx: fix transfer bits_per_words check
|
||||
|
||||
Transfers often do not have bits_per_words set, so use the spi device's
|
||||
bits_per_words in this case.
|
||||
|
||||
This fixes the driver rejecting valid transfers e.g. generated by
|
||||
spi_write() or spi_read().
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
drivers/spi/spi-bcm63xx.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/spi/spi-bcm63xx.c
|
||||
+++ b/drivers/spi/spi-bcm63xx.c
|
||||
@@ -103,7 +103,8 @@ static int bcm63xx_spi_check_transfer(st
|
||||
{
|
||||
u8 bits_per_word;
|
||||
|
||||
- bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
|
||||
+ bits_per_word = (t && t->bits_per_word) ?
|
||||
+ t->bits_per_word : spi->bits_per_word;
|
||||
if (bits_per_word != 8) {
|
||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, bits_per_word);
|
|
@ -0,0 +1,279 @@
|
|||
From 0f2ae1e1282ff64f74a5e36f7da874f94911225e Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Wed, 14 Nov 2012 22:22:33 +0100
|
||||
Subject: [PATCH] spi/bcm63xx: fix multi transfer messages
|
||||
|
||||
The BCM63XX SPI controller does not support keeping CS asserted after
|
||||
sending its buffer. This breaks common usages like spi_write_then_read,
|
||||
where it is expected to be kept active during the whole transfers.
|
||||
|
||||
Work around this by combining the transfers into one if the buffer
|
||||
allows. For spi_write_then_read, use the prepend byte feature to write
|
||||
to "prepend" the write if it is less than 15 bytes, allowing the whole
|
||||
fifo size for the read.
|
||||
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
Tested on a SPI conntected switch which required keeping CS active between
|
||||
the register read command and reading the register contents.
|
||||
|
||||
Based on Mark's spi/next.
|
||||
|
||||
Not sure if this is stable material, as it's quite invasive.
|
||||
|
||||
drivers/spi/spi-bcm63xx.c | 172 ++++++++++++++++++++++++++++++---------------
|
||||
1 file changed, 117 insertions(+), 55 deletions(-)
|
||||
|
||||
--- a/drivers/spi/spi-bcm63xx.c
|
||||
+++ b/drivers/spi/spi-bcm63xx.c
|
||||
@@ -38,6 +38,8 @@
|
||||
#define PFX KBUILD_MODNAME
|
||||
#define DRV_VER "0.1.2"
|
||||
|
||||
+#define BCM63XX_SPI_MAX_PREPEND 15
|
||||
+
|
||||
struct bcm63xx_spi {
|
||||
struct completion done;
|
||||
|
||||
@@ -50,16 +52,10 @@ struct bcm63xx_spi {
|
||||
unsigned int msg_type_shift;
|
||||
unsigned int msg_ctl_width;
|
||||
|
||||
- /* Data buffers */
|
||||
- const unsigned char *tx_ptr;
|
||||
- unsigned char *rx_ptr;
|
||||
-
|
||||
/* data iomem */
|
||||
u8 __iomem *tx_io;
|
||||
const u8 __iomem *rx_io;
|
||||
|
||||
- int remaining_bytes;
|
||||
-
|
||||
struct clk *clk;
|
||||
struct platform_device *pdev;
|
||||
};
|
||||
@@ -184,50 +180,60 @@ static int bcm63xx_spi_setup(struct spi_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/* Fill the TX FIFO with as many bytes as possible */
|
||||
-static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
|
||||
-{
|
||||
- u8 size;
|
||||
-
|
||||
- /* Fill the Tx FIFO with as many bytes as possible */
|
||||
- size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
|
||||
- bs->fifo_size;
|
||||
- memcpy_toio(bs->tx_io, bs->tx_ptr, size);
|
||||
- bs->remaining_bytes -= size;
|
||||
-}
|
||||
-
|
||||
static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
|
||||
- struct spi_transfer *t)
|
||||
+ struct spi_transfer *first,
|
||||
+ unsigned int n_transfers)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
|
||||
u16 msg_ctl;
|
||||
u16 cmd;
|
||||
+ unsigned int i, timeout, total_len = 0, prepend_len = 0, len = 0;
|
||||
+ struct spi_transfer *t = first;
|
||||
+ u8 rx_tail;
|
||||
+ bool do_rx = false;
|
||||
+ bool do_tx = false;
|
||||
|
||||
/* Disable the CMD_DONE interrupt */
|
||||
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
|
||||
|
||||
- dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
|
||||
- t->tx_buf, t->rx_buf, t->len);
|
||||
+ if (n_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
|
||||
+ prepend_len = t->len;
|
||||
+
|
||||
+ /* prepare the buffer */
|
||||
+ for (i = 0; i < n_transfers; i++) {
|
||||
+ if (t->tx_buf) {
|
||||
+ do_tx = true;
|
||||
+ memcpy_toio(bs->tx_io + total_len, t->tx_buf, t->len);
|
||||
+
|
||||
+ /* don't prepend more than one tx */
|
||||
+ if (t != first)
|
||||
+ prepend_len = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (t->rx_buf) {
|
||||
+ do_rx = true;
|
||||
+ if (t == first)
|
||||
+ prepend_len = 0;
|
||||
+ }
|
||||
|
||||
- /* Transmitter is inhibited */
|
||||
- bs->tx_ptr = t->tx_buf;
|
||||
- bs->rx_ptr = t->rx_buf;
|
||||
-
|
||||
- if (t->tx_buf) {
|
||||
- bs->remaining_bytes = t->len;
|
||||
- bcm63xx_spi_fill_tx_fifo(bs);
|
||||
+ total_len += t->len;
|
||||
+
|
||||
+ t = list_entry(t->transfer_list.next, struct spi_transfer,
|
||||
+ transfer_list);
|
||||
}
|
||||
|
||||
+ len = total_len - prepend_len;
|
||||
+
|
||||
init_completion(&bs->done);
|
||||
|
||||
/* Fill in the Message control register */
|
||||
- msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
|
||||
+ msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
|
||||
|
||||
- if (t->rx_buf && t->tx_buf)
|
||||
+ if (do_rx && do_tx && prepend_len == 0)
|
||||
msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
|
||||
- else if (t->rx_buf)
|
||||
+ else if (do_rx)
|
||||
msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
|
||||
- else if (t->tx_buf)
|
||||
+ else if (do_tx)
|
||||
msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
|
||||
|
||||
switch (bs->msg_ctl_width) {
|
||||
@@ -241,14 +247,41 @@ static unsigned int bcm63xx_txrx_bufs(st
|
||||
|
||||
/* Issue the transfer */
|
||||
cmd = SPI_CMD_START_IMMEDIATE;
|
||||
- cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
|
||||
+ cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
|
||||
cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
|
||||
bcm_spi_writew(bs, cmd, SPI_CMD);
|
||||
|
||||
/* Enable the CMD_DONE interrupt */
|
||||
bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
|
||||
|
||||
- return t->len - bs->remaining_bytes;
|
||||
+ timeout = wait_for_completion_timeout(&bs->done, HZ);
|
||||
+ if (!timeout)
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ /* read out all data */
|
||||
+ rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
|
||||
+
|
||||
+ if (do_rx && rx_tail != len)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!rx_tail)
|
||||
+ return total_len;
|
||||
+
|
||||
+ len = 0;
|
||||
+ t = first;
|
||||
+ /* Read out all the data */
|
||||
+ for (i = 0; i < n_transfers; i++) {
|
||||
+ if (t->rx_buf)
|
||||
+ memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
|
||||
+
|
||||
+ if (t != first || prepend_len == 0)
|
||||
+ len += t->len;
|
||||
+
|
||||
+ t = list_entry(t->transfer_list.next, struct spi_transfer,
|
||||
+ transfer_list);
|
||||
+ }
|
||||
+
|
||||
+ return total_len;
|
||||
}
|
||||
|
||||
static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
|
||||
@@ -273,42 +306,71 @@ static int bcm63xx_spi_transfer_one(stru
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
- struct spi_transfer *t;
|
||||
+ struct spi_transfer *t, *first = NULL;
|
||||
struct spi_device *spi = m->spi;
|
||||
int status = 0;
|
||||
- unsigned int timeout = 0;
|
||||
+ unsigned int n_transfers = 0, total_len = 0;
|
||||
+ bool can_use_prepend = false;
|
||||
|
||||
+ /*
|
||||
+ * This SPI controller does not support keeping CS active after a
|
||||
+ * transfer, so we need to combine the transfers into one until we may
|
||||
+ * deassert CS.
|
||||
+ */
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
- unsigned int len = t->len;
|
||||
- u8 rx_tail;
|
||||
-
|
||||
status = bcm63xx_spi_check_transfer(spi, t);
|
||||
if (status < 0)
|
||||
goto exit;
|
||||
|
||||
- /* configure adapter for a new transfer */
|
||||
- bcm63xx_spi_setup_transfer(spi, t);
|
||||
+ if (!first)
|
||||
+ first = t;
|
||||
|
||||
- while (len) {
|
||||
- /* send the data */
|
||||
- len -= bcm63xx_txrx_bufs(spi, t);
|
||||
-
|
||||
- timeout = wait_for_completion_timeout(&bs->done, HZ);
|
||||
- if (!timeout) {
|
||||
- status = -ETIMEDOUT;
|
||||
- goto exit;
|
||||
- }
|
||||
+ n_transfers++;
|
||||
+ total_len += t->len;
|
||||
+
|
||||
+ if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
|
||||
+ first->len <= BCM63XX_SPI_MAX_PREPEND)
|
||||
+ can_use_prepend = true;
|
||||
+ else if (can_use_prepend && t->tx_buf)
|
||||
+ can_use_prepend = false;
|
||||
+
|
||||
+ if ((can_use_prepend &&
|
||||
+ total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
|
||||
+ (!can_use_prepend && total_len > bs->fifo_size)) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
|
||||
- /* read out all data */
|
||||
- rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
|
||||
+ /* all transfers have to be made at the same speed */
|
||||
+ if (t->speed_hz != first->speed_hz) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
|
||||
- /* Read out all the data */
|
||||
- if (rx_tail)
|
||||
- memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
|
||||
+ /* CS will be deasserted directly after the transfer */
|
||||
+ if (t->delay_usecs) {
|
||||
+ status = -EINVAL;
|
||||
+ goto exit;
|
||||
}
|
||||
|
||||
- m->actual_length += t->len;
|
||||
+ if (t->cs_change ||
|
||||
+ list_is_last(&t->transfer_list, &m->transfers)) {
|
||||
+ /* configure adapter for a new transfer */
|
||||
+ bcm63xx_spi_setup_transfer(spi, first);
|
||||
+
|
||||
+ status = bcm63xx_txrx_bufs(spi, first, n_transfers);
|
||||
+ if (status < 0)
|
||||
+ goto exit;
|
||||
+
|
||||
+ m->actual_length += status;
|
||||
+ first = NULL;
|
||||
+ status = 0;
|
||||
+ n_transfers = 0;
|
||||
+ total_len = 0;
|
||||
+ can_use_prepend = false;
|
||||
+ }
|
||||
}
|
||||
+
|
||||
exit:
|
||||
m->status = status;
|
||||
spi_finalize_current_message(master);
|
Loading…
Reference in New Issue