diff options
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.patch | 279 |
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); |