summaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-bcm2835aux.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-bcm2835aux.c')
-rw-r--r--drivers/spi/spi-bcm2835aux.c43
1 files changed, 31 insertions, 12 deletions
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c
index f92b4a69fffa..05d2d6eb7bfa 100644
--- a/drivers/spi/spi-bcm2835aux.c
+++ b/drivers/spi/spi-bcm2835aux.c
@@ -104,6 +104,7 @@ struct bcm2835aux_spi {
u8 *rx_buf;
int tx_len;
int rx_len;
+ int pending;
};
static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg)
@@ -120,15 +121,27 @@ static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg,
static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs)
{
u32 data;
- int i;
int count = min(bs->rx_len, 3);
data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO);
if (bs->rx_buf) {
- for (i = 0; i < count; i++)
- *bs->rx_buf++ = (data >> (8 * (2 - i))) & 0xff;
+ switch (count) {
+ case 4:
+ *bs->rx_buf++ = (data >> 24) & 0xff;
+ /* fallthrough */
+ case 3:
+ *bs->rx_buf++ = (data >> 16) & 0xff;
+ /* fallthrough */
+ case 2:
+ *bs->rx_buf++ = (data >> 8) & 0xff;
+ /* fallthrough */
+ case 1:
+ *bs->rx_buf++ = (data >> 0) & 0xff;
+ /* fallthrough - no default */
+ }
}
bs->rx_len -= count;
+ bs->pending -= count;
}
static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs)
@@ -151,6 +164,7 @@ static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs)
/* and decrement length */
bs->tx_len -= count;
+ bs->pending += count;
/* write to the correct TX-register */
if (bs->tx_len)
@@ -183,6 +197,7 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)
/* check if we have data to write */
while (bs->tx_len &&
+ (bs->pending < 12) &&
(!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
BCM2835_AUX_SPI_STAT_TX_FULL))) {
bcm2835aux_wr_fifo(bs);
@@ -234,6 +249,7 @@ static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master,
/* fill in tx fifo with data before enabling interrupts */
while ((bs->tx_len) &&
+ (bs->pending < 12) &&
(!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
BCM2835_AUX_SPI_STAT_TX_FULL))) {
bcm2835aux_wr_fifo(bs);
@@ -245,8 +261,7 @@ static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master,
static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master,
struct spi_device *spi,
- struct spi_transfer *tfr,
- unsigned long xfer_time_us)
+ struct spi_transfer *tfr)
{
struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
unsigned long timeout;
@@ -305,7 +320,8 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
{
struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
unsigned long spi_hz, clk_hz, speed;
- unsigned long spi_used_hz, xfer_time_us;
+ unsigned long spi_used_hz;
+ unsigned long long xfer_time_us;
/* calculate the registers to handle
*
@@ -348,16 +364,19 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
bs->rx_buf = tfr->rx_buf;
bs->tx_len = tfr->len;
bs->rx_len = tfr->len;
+ bs->pending = 0;
- /* calculate the estimated time in us the transfer runs */
- xfer_time_us = tfr->len
- * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */
- * 1000000 / spi_used_hz;
+ /* calculate the estimated time in us the transfer runs
+ * note that there are are 2 idle clocks after each
+ * chunk getting transferred - in our case the chunk size
+ * is 3 bytes, so we approximate this by 9 bits/byte
+ */
+ xfer_time_us = tfr->len * 9 * 1000000;
+ do_div(xfer_time_us, spi_used_hz);
/* run in polling mode for short transfers */
if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US)
- return bcm2835aux_spi_transfer_one_poll(master, spi, tfr,
- xfer_time_us);
+ return bcm2835aux_spi_transfer_one_poll(master, spi, tfr);
/* run in interrupt mode for all others */
return bcm2835aux_spi_transfer_one_irq(master, spi, tfr);