diff options
author | Jonas Gorski <jogo@openwrt.org> | 2013-02-04 10:19:55 +0000 |
---|---|---|
committer | Jonas Gorski <jogo@openwrt.org> | 2013-02-04 10:19:55 +0000 |
commit | 8506964e6d6f89ae67951d0eafe4717b63b1f610 (patch) | |
tree | a60f167fa77a88974c7304c47fd3c0a29a43505e /target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch | |
parent | 3b4fced67dad0b733d77f78205cfecfc0d654695 (diff) | |
download | upstream-8506964e6d6f89ae67951d0eafe4717b63b1f610.tar.gz upstream-8506964e6d6f89ae67951d0eafe4717b63b1f610.tar.bz2 upstream-8506964e6d6f89ae67951d0eafe4717b63b1f610.zip |
bcm63xx: add support for linux 3.8
Based on 3.8-rc6
Signed-off-by: Jonas Gorski <jogo@openwrt.org>
SVN-Revision: 35482
Diffstat (limited to 'target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch')
-rw-r--r-- | target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch b/target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch new file mode 100644 index 0000000000..85b8524de1 --- /dev/null +++ b/target/linux/brcm63xx/patches-3.8/107-spi-bcm63xx-fix-multi-transfer-messages.patch @@ -0,0 +1,240 @@ +From 725e81d507b1098cd275d4e3333c77c4b750fa79 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jogo@openwrt.org> +Date: Sun, 9 Dec 2012 01:53:05 +0100 +Subject: [PATCH V2 2/2] spi/bcm63xx: work around inability to keep CS up + +This SPI controller does not support keeping CS asserted after sending +a transfer. +Since messages expected on this SPI controller are rather short, we can +work around it for normal use cases by sending all transfers at once in +a big full duplex stream. + +This means that we cannot change the speed between transfers if they +require CS to be kept asserted, but these would have been rejected +before anyway because of the inability of keeping CS asserted. + +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +--- +V1 -> V2: + * split out rejection logic into separate patch + * fixed return type of bcm63xx_txrx_bufs() + * slightly reworked bcm63xx_txrx_bufs, obsoleting one local variable + + drivers/spi/spi-bcm63xx.c | 134 +++++++++++++++++++++++++++++++++++---------- + 1 file changed, 106 insertions(+), 28 deletions(-) + +--- a/drivers/spi/spi-bcm63xx.c ++++ b/drivers/spi/spi-bcm63xx.c +@@ -37,6 +37,8 @@ + + #define PFX KBUILD_MODNAME + ++#define BCM63XX_SPI_MAX_PREPEND 15 ++ + struct bcm63xx_spi { + struct completion done; + +@@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_ + return 0; + } + +-static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) ++static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, ++ unsigned int num_transfers) + { + struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); + u16 msg_ctl; + u16 cmd; + u8 rx_tail; +- unsigned int timeout = 0; ++ unsigned int i, timeout = 0, prepend_len = 0, len = 0; ++ struct spi_transfer *t = first; ++ bool do_rx = false; ++ bool do_tx = false; + + /* Disable the CMD_DONE interrupt */ + bcm_spi_writeb(bs, 0, SPI_INT_MASK); +@@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_ + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", + t->tx_buf, t->rx_buf, t->len); + +- if (t->tx_buf) +- memcpy_toio(bs->tx_io, t->tx_buf, t->len); ++ if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) ++ prepend_len = t->len; ++ ++ /* prepare the buffer */ ++ for (i = 0; i < num_transfers; i++) { ++ if (t->tx_buf) { ++ do_tx = true; ++ memcpy_toio(bs->tx_io + 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; ++ /* prepend is half-duplex write only */ ++ if (t == first) ++ prepend_len = 0; ++ } ++ ++ len += t->len; ++ ++ t = list_entry(t->transfer_list.next, struct spi_transfer, ++ transfer_list); ++ } ++ ++ 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) { +@@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_ + + /* 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); + +@@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_ + /* read out all data */ + rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); + ++ if (do_rx && rx_tail != len) ++ return -EIO; ++ ++ if (!rx_tail) ++ return 0; ++ ++ len = 0; ++ t = first; + /* Read out all the data */ +- if (rx_tail) +- memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail); ++ for (i = 0; i < num_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 0; + } +@@ -252,46 +300,76 @@ 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 n_transfers = 0, total_len = 0; ++ bool can_use_prepend = false; + ++ /* ++ * This SPI controller does not support keeping CS active after a ++ * transfer. ++ * Work around this by merging as many transfers we can into one big ++ * full-duplex transfers. ++ */ + list_for_each_entry(t, &m->transfers, transfer_list) { + status = bcm63xx_spi_check_transfer(spi, t); + if (status < 0) + goto exit; + ++ if (!first) ++ first = t; ++ ++ 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; ++ + /* we can only transfer one fifo worth of data */ +- if (t->len > bs->fifo_size) { ++ if ((can_use_prepend && ++ total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || ++ (!can_use_prepend && total_len > bs->fifo_size)) { + dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", +- t->len, bs->fifo_size); ++ total_len, bs->fifo_size); + status = -EINVAL; + goto exit; + } + +- /* CS will be deasserted directly after transfer */ +- if (t->delay_usecs) { +- dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); ++ /* all combined transfers have to have the same speed */ ++ if (t->speed_hz != first->speed_hz) { ++ dev_err(&spi->dev, "unable to change speed between transfers\n"); + status = -EINVAL; + goto exit; + } + +- if (!t->cs_change && +- !list_is_last(&t->transfer_list, &m->transfers)) { +- dev_err(&spi->dev, "unable to keep CS asserted between transfers\n"); ++ /* CS will be deasserted directly after transfer */ ++ if (t->delay_usecs) { ++ dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); + status = -EINVAL; + goto exit; + } + +- /* configure adapter for a new transfer */ +- bcm63xx_spi_setup_transfer(spi, t); +- +- /* send the data */ +- status = bcm63xx_txrx_bufs(spi, t); +- if (status) +- 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); ++ ++ /* send the data */ ++ status = bcm63xx_txrx_bufs(spi, first, n_transfers); ++ if (status) ++ goto exit; ++ ++ m->actual_length += total_len; ++ ++ first = NULL; ++ n_transfers = 0; ++ total_len = 0; ++ can_use_prepend = false; ++ } + } + exit: + m->status = status; |