summaryrefslogtreecommitdiff
path: root/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch')
-rw-r--r--target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch279
1 files changed, 0 insertions, 279 deletions
diff --git a/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch b/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch
deleted file mode 100644
index 941de48..0000000
--- a/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch
+++ /dev/null
@@ -1,279 +0,0 @@
-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);