diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 8c121b3374dd..5af4cf74392c 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -71,6 +71,8 @@ #define BCM2835_SPI_CS_CS_10 0x00000002 #define BCM2835_SPI_CS_CS_01 0x00000001 +#define BCM2835_SPI_FIFO_SIZE 64 +#define BCM2835_SPI_FIFO_SIZE_3_4 48 #define BCM2835_SPI_POLLING_LIMIT_US 30 #define BCM2835_SPI_POLLING_JIFFIES 2 #define BCM2835_SPI_DMA_MIN_LENGTH 96 @@ -216,6 +218,45 @@ static inline void bcm2835_wait_tx_fifo_empty(struct bcm2835_spi *bs) cpu_relax(); } +/** + * bcm2835_rd_fifo_blind() - blindly read up to @count bytes from RX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes available for reading in RX FIFO + */ +static inline void bcm2835_rd_fifo_blind(struct bcm2835_spi *bs, int count) +{ + u8 val; + + count = min(count, bs->rx_len); + bs->rx_len -= count; + + while (count) { + val = bcm2835_rd(bs, BCM2835_SPI_FIFO); + if (bs->rx_buf) + *bs->rx_buf++ = val; + count--; + } +} + +/** + * bcm2835_wr_fifo_blind() - blindly write up to @count bytes to TX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes available for writing in TX FIFO + */ +static inline void bcm2835_wr_fifo_blind(struct bcm2835_spi *bs, int count) +{ + u8 val; + + count = min(count, bs->tx_len); + bs->tx_len -= count; + + while (count) { + val = bs->tx_buf ? *bs->tx_buf++ : 0; + bcm2835_wr(bs, BCM2835_SPI_FIFO, val); + count--; + } +} + static void bcm2835_spi_reset_hw(struct spi_master *master) { struct bcm2835_spi *bs = spi_master_get_devdata(master); @@ -239,6 +280,19 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* + * An interrupt is signaled either if DONE is set (TX FIFO empty) + * or if RXR is set (RX FIFO >= ¾ full). + */ + if (cs & BCM2835_SPI_CS_RXF) + bcm2835_rd_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); + else if (cs & BCM2835_SPI_CS_RXR) + bcm2835_rd_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE_3_4); + + if (bs->tx_len && cs & BCM2835_SPI_CS_DONE) + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); /* Read as many bytes as possible from FIFO */ bcm2835_rd_fifo(bs); @@ -258,7 +312,7 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) static int bcm2835_spi_transfer_one_irq(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, - u32 cs) + u32 cs, bool fifo_empty) { struct bcm2835_spi *bs = spi_master_get_devdata(master); @@ -269,6 +323,8 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); /* fill TX FIFO as much as possible */ + if (fifo_empty) + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); bcm2835_wr_fifo(bs); /* enable interrupts */ @@ -682,7 +738,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, * if we are interrupted here, then the data is * getting transferred by the HW while we are interrupted */ - bcm2835_wr_fifo(bs); + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); /* set the timeout */ timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES; @@ -705,7 +761,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, bs->tx_len, bs->rx_len); /* fall back to interrupt mode */ return bcm2835_spi_transfer_one_irq(master, spi, - tfr, cs); + tfr, cs, false); } } @@ -779,7 +835,7 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs); /* run in interrupt-mode */ - return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs, true); } static int bcm2835_spi_prepare_message(struct spi_master *master,