summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/spi/spi-fsl-dspi.c101
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;
}