diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 101 |
1 files changed, 68 insertions, 33 deletions
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 7d7f8035f25b..451a837e5613 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -47,6 +47,7 @@ #define SPI_MCR_CLR_RXF (1 << 10) #define SPI_TCR 0x08 +#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) #define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4)) #define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) @@ -104,6 +105,8 @@ #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 +#define SPI_TCR_TCNT_MAX 0x10000 + struct chip_data { u32 mcr_val; u32 ctar_val; @@ -155,6 +158,8 @@ struct fsl_dspi { wait_queue_head_t waitq; u32 waitflags; + + u32 spi_tcnt; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) @@ -274,7 +279,6 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) int tx_count = 0; int tx_word; u32 dspi_pushr = 0; - int first = 1; tx_word = is_double_byte_mode(dspi); @@ -300,11 +304,6 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) } else if (tx_word && (dspi->len == 1)) dspi_pushr |= SPI_PUSHR_EOQ; - if (first) { - first = 0; - dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ - } - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); tx_count++; @@ -372,6 +371,10 @@ static int dspi_transfer_one_message(struct spi_master *master, struct spi_transfer *transfer; int status = 0; enum dspi_trans_mode trans_mode; + u32 spi_tcr; + + regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); + dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); message->actual_length = 0; @@ -413,11 +416,11 @@ static int dspi_transfer_one_message(struct spi_master *master, switch (trans_mode) { case DSPI_EOQ_MODE: regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); - message->actual_length += dspi_eoq_write(dspi); + dspi_eoq_write(dspi); break; case DSPI_TCFQ_MODE: regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); - message->actual_length += dspi_tcfq_write(dspi); + dspi_tcfq_write(dspi); break; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", @@ -516,47 +519,79 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; struct spi_message *msg = dspi->cur_msg; enum dspi_trans_mode trans_mode; - u32 spi_sr; + u32 spi_sr, spi_tcr; + u32 spi_tcnt, tcnt_diff; + int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); - trans_mode = dspi->devtype_data->trans_mode; - switch (trans_mode) { - case DSPI_EOQ_MODE: - dspi_eoq_read(dspi); - break; - case DSPI_TCFQ_MODE: - dspi_tcfq_read(dspi); - break; - default: - dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", - trans_mode); - return IRQ_HANDLED; - } - if (!dspi->len) { - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { - regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); - dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; - } + if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { + tx_word = is_double_byte_mode(dspi); - dspi->waitflags = 1; - wake_up_interruptible(&dspi->waitq); - } else { + regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); + spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); + /* + * The width of SPI Transfer Counter in SPI_TCR is 16bits, + * so the max couner is 65535. When the counter reach 65535, + * it will wrap around, counter reset to zero. + * spi_tcnt my be less than dspi->spi_tcnt, it means the + * counter already wrapped around. + * SPI Transfer Counter is a counter of transmitted frames. + * The size of frame maybe two bytes. + */ + tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt) + % SPI_TCR_TCNT_MAX; + tcnt_diff *= (tx_word + 1); + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) + tcnt_diff--; + + msg->actual_length += tcnt_diff; + + dspi->spi_tcnt = spi_tcnt; + + trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { case DSPI_EOQ_MODE: - msg->actual_length += dspi_eoq_write(dspi); + dspi_eoq_read(dspi); break; case DSPI_TCFQ_MODE: - msg->actual_length += dspi_tcfq_write(dspi); + dspi_tcfq_read(dspi); break; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", trans_mode); + return IRQ_HANDLED; + } + + if (!dspi->len) { + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { + regmap_update_bits(dspi->regmap, + SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, + SPI_FRAME_BITS(16)); + dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; + } + + dspi->waitflags = 1; + wake_up_interruptible(&dspi->waitq); + } else { + switch (trans_mode) { + case DSPI_EOQ_MODE: + dspi_eoq_write(dspi); + break; + case DSPI_TCFQ_MODE: + dspi_tcfq_write(dspi); + break; + default: + dev_err(&dspi->pdev->dev, + "unsupported trans_mode %u\n", + trans_mode); + } } } + return IRQ_HANDLED; } |