summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-bcm2835.c107
1 files changed, 36 insertions, 71 deletions
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index 419a782..3f93718 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -67,7 +67,8 @@
#define BCM2835_SPI_CS_CS_01 0x00000001
#define BCM2835_SPI_TIMEOUT_MS 30000
-#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS)
+#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
+ | SPI_NO_CS | SPI_3WIRE)
#define DRV_NAME "spi-bcm2835"
@@ -91,25 +92,23 @@ static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val)
writel(val, bs->regs + reg);
}
-static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len)
+static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs)
{
u8 byte;
- while (len--) {
+ while (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD) {
byte = bcm2835_rd(bs, BCM2835_SPI_FIFO);
if (bs->rx_buf)
*bs->rx_buf++ = byte;
}
}
-static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len)
+static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs)
{
u8 byte;
- if (len > bs->len)
- len = bs->len;
-
- while (len--) {
+ while ((bs->len) &&
+ (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_TXD)) {
byte = bs->tx_buf ? *bs->tx_buf++ : 0;
bcm2835_wr(bs, BCM2835_SPI_FIFO, byte);
bs->len--;
@@ -122,64 +121,28 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
struct bcm2835_spi *bs = spi_master_get_devdata(master);
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
- /*
- * RXR - RX needs Reading. This means 12 (or more) bytes have been
- * transmitted and hence 12 (or more) bytes have been received.
- *
- * The FIFO is 16-bytes deep. We check for this interrupt to keep the
- * FIFO full; we have a 4-byte-time buffer for IRQ latency. We check
- * this before DONE (TX empty) just in case we delayed processing this
- * interrupt for some reason.
- *
- * We only check for this case if we have more bytes to TX; at the end
- * of the transfer, we ignore this pipelining optimization, and let
- * bcm2835_spi_finish_transfer() drain the RX FIFO.
- */
- if (bs->len && (cs & BCM2835_SPI_CS_RXR)) {
- /* Read 12 bytes of data */
- bcm2835_rd_fifo(bs, 12);
+ /* Read as many bytes as possible from FIFO */
+ bcm2835_rd_fifo(bs);
- /* Write up to 12 bytes */
- bcm2835_wr_fifo(bs, 12);
+ if (bs->len) { /* there is more data to transmit */
+ bcm2835_wr_fifo(bs);
+ } else { /* Transfer complete */
+ /* Disable SPI interrupts */
+ cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD);
+ bcm2835_wr(bs, BCM2835_SPI_CS, cs);
/*
- * We must have written something to the TX FIFO due to the
- * bs->len check above, so cannot be DONE. Hence, return
- * early. Note that DONE could also be set if we serviced an
- * RXR interrupt really late.
+ * Wake up bcm2835_spi_transfer_one(), which will call
+ * bcm2835_spi_finish_transfer(), to drain the RX FIFO.
*/
- return IRQ_HANDLED;
+ complete(&bs->done);
}
- /*
- * DONE - TX empty. This occurs when we first enable the transfer
- * since we do not pre-fill the TX FIFO. At any other time, given that
- * we refill the TX FIFO above based on RXR, and hence ignore DONE if
- * RXR is set, DONE really does mean end-of-transfer.
- */
- if (cs & BCM2835_SPI_CS_DONE) {
- if (bs->len) { /* First interrupt in a transfer */
- bcm2835_wr_fifo(bs, 16);
- } else { /* Transfer complete */
- /* Disable SPI interrupts */
- cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD);
- bcm2835_wr(bs, BCM2835_SPI_CS, cs);
-
- /*
- * Wake up bcm2835_spi_transfer_one(), which will call
- * bcm2835_spi_finish_transfer(), to drain the RX FIFO.
- */
- complete(&bs->done);
- }
-
- return IRQ_HANDLED;
- }
-
- return IRQ_NONE;
+ return IRQ_HANDLED;
}
static int bcm2835_spi_start_transfer(struct spi_device *spi,
- struct spi_transfer *tfr)
+ struct spi_transfer *tfr)
{
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
unsigned long spi_hz, clk_hz, cdiv;
@@ -191,13 +154,18 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi,
if (spi_hz >= clk_hz / 2) {
cdiv = 2; /* clk_hz/2 is the fastest we can go */
} else if (spi_hz) {
- /* CDIV must be a power of two */
- cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz));
+ /* CDIV must be a multiple of two */
+ cdiv = DIV_ROUND_UP(clk_hz, spi_hz);
+ cdiv += (cdiv % 2);
if (cdiv >= 65536)
cdiv = 0; /* 0 is the slowest we can go */
- } else
+ } else {
cdiv = 0; /* 0 is the slowest we can go */
+ }
+
+ if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf))
+ cs |= BCM2835_SPI_CS_REN;
if (spi->mode & SPI_CPOL)
cs |= BCM2835_SPI_CS_CPOL;
@@ -231,17 +199,12 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi,
}
static int bcm2835_spi_finish_transfer(struct spi_device *spi,
- struct spi_transfer *tfr, bool cs_change)
+ struct spi_transfer *tfr,
+ bool cs_change)
{
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
- /* Drain RX FIFO */
- while (cs & BCM2835_SPI_CS_RXD) {
- bcm2835_rd_fifo(bs, 1);
- cs = bcm2835_rd(bs, BCM2835_SPI_CS);
- }
-
if (tfr->delay_usecs)
udelay(tfr->delay_usecs);
@@ -253,7 +216,7 @@ static int bcm2835_spi_finish_transfer(struct spi_device *spi,
}
static int bcm2835_spi_transfer_one(struct spi_master *master,
- struct spi_message *mesg)
+ struct spi_message *mesg)
{
struct bcm2835_spi *bs = spi_master_get_devdata(master);
struct spi_transfer *tfr;
@@ -267,8 +230,10 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
if (err)
goto out;
- timeout = wait_for_completion_timeout(&bs->done,
- msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS));
+ timeout = wait_for_completion_timeout(
+ &bs->done,
+ msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS)
+ );
if (!timeout) {
err = -ETIMEDOUT;
goto out;
@@ -343,7 +308,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
clk_prepare_enable(bs->clk);
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
- dev_name(&pdev->dev), master);
+ dev_name(&pdev->dev), master);
if (err) {
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
goto out_clk_disable;