[PATCH rtems] bsps/imxrt: Improve SPI driver
Christian Mauderer
christian.mauderer at embedded-brains.de
Wed Sep 1 13:54:33 UTC 2021
It wasn't possible to keep the CS line low between multiple message
descriptors in one transfer. This patch reworks the driver so that it is
possible.
Update #4180
---
bsps/arm/imxrt/spi/imxrt-lpspi.c | 197 +++++++++++++++++++------------
1 file changed, 124 insertions(+), 73 deletions(-)
diff --git a/bsps/arm/imxrt/spi/imxrt-lpspi.c b/bsps/arm/imxrt/spi/imxrt-lpspi.c
index 06d8f715d9..80b47f9663 100644
--- a/bsps/arm/imxrt/spi/imxrt-lpspi.c
+++ b/bsps/arm/imxrt/spi/imxrt-lpspi.c
@@ -43,16 +43,19 @@ struct imxrt_lpspi_bus {
uint32_t src_clock_hz;
clock_ip_name_t clock_ip;
- uint32_t msg_todo;
- const spi_ioc_transfer *msg;
rtems_binary_semaphore sem;
- uint32_t tcr;
+ bool cs_change_on_last_msg;
+ uint32_t rx_msg_todo;
+ const spi_ioc_transfer *rx_msg;
size_t remaining_rx_size;
uint8_t *rx_buf;
+ uint32_t tx_msg_todo;
+ const spi_ioc_transfer *tx_msg;
size_t remaining_tx_size;
const uint8_t *tx_buf;
+
uint32_t fifo_size;
};
@@ -146,11 +149,7 @@ static void imxrt_lpspi_config(
}
tcr |= LPSPI_TCR_PCS(msg->cs);
-
- if (!msg->cs_change) {
- tcr |= LPSPI_TCR_CONT_MASK;
- }
-
+ tcr |= LPSPI_TCR_CONT_MASK;
tcr |= LPSPI_TCR_FRAMESZ(word_size-1);
if (ccr_orig != ccr) {
@@ -159,9 +158,13 @@ static void imxrt_lpspi_config(
regs->CR |= LPSPI_CR_MEN_MASK;
}
- /* No CONTC on first write. Otherwise upper 8 bits are not written. */
- regs->TCR = tcr;
- regs->TCR = tcr | LPSPI_TCR_CONTC_MASK | LPSPI_TCR_CONT_MASK;
+ if (bus->cs_change_on_last_msg) {
+ /* No CONTC on first write. Otherwise upper 8 bits are not written. */
+ regs->TCR = tcr;
+ }
+ regs->TCR = tcr | LPSPI_TCR_CONTC_MASK;
+
+ bus->cs_change_on_last_msg = msg->cs_change;
}
static inline bool imxrt_lpspi_rx_fifo_not_empty(
@@ -184,48 +187,72 @@ static inline bool imxrt_lpspi_tx_fifo_not_full(
bus->fifo_size - 2;
}
+static void imxrt_lpspi_next_tx_msg(
+ struct imxrt_lpspi_bus *bus,
+ volatile LPSPI_Type *regs
+)
+{
+ if (bus->tx_msg_todo > 0) {
+ const spi_ioc_transfer *msg;
+
+ msg = bus->tx_msg;
+
+ imxrt_lpspi_config(bus, regs, msg);
+ bus->remaining_tx_size = msg->len;
+ bus->tx_buf = msg->tx_buf;
+ }
+}
+
static void imxrt_lpspi_fill_tx_fifo(
struct imxrt_lpspi_bus *bus,
volatile LPSPI_Type *regs
)
{
while(imxrt_lpspi_tx_fifo_not_full(bus, regs)
- && bus->remaining_tx_size > 0) {
- if (bus->remaining_tx_size == 1) {
- regs->TCR &= ~(LPSPI_TCR_CONT_MASK);
- }
+ && (bus->tx_msg_todo > 0 || bus->remaining_tx_size > 0)) {
+ if (bus->remaining_tx_size > 0) {
+ if (bus->remaining_tx_size == 1 && bus->tx_msg->cs_change) {
+ /*
+ * Necessary for getting the last data out of the Rx FIFO. See "i.MX
+ * RT1050 Processor Reference Manual Rev. 4" Chapter 47.3.2.2 "Receive
+ * FIFO and Data Match":
+ *
+ * "During a continuous transfer, if the transmit FIFO is empty, then
+ * the receive data is only written to the receive FIFO after the
+ * transmit FIFO is written or after the Transmit Command Register (TCR)
+ * is written to end the frame."
+ */
+ regs->TCR &= ~(LPSPI_TCR_CONT_MASK);
+ }
- if (bus->tx_buf != NULL) {
- regs->TDR = bus->tx_buf[0];
- ++bus->tx_buf;
- } else {
- regs->TDR = 0;
+ if (bus->tx_buf != NULL) {
+ regs->TDR = bus->tx_buf[0];
+ ++bus->tx_buf;
+ } else {
+ regs->TDR = 0;
+ }
+ --bus->remaining_tx_size;
+ }
+ if (bus->remaining_tx_size == 0) {
+ --bus->tx_msg_todo;
+ ++bus->tx_msg;
+ imxrt_lpspi_next_tx_msg(bus, regs);
}
- --bus->remaining_tx_size;
}
}
-static void imxrt_lpspi_next_msg(
+static void imxrt_lpspi_next_rx_msg(
struct imxrt_lpspi_bus *bus,
volatile LPSPI_Type *regs
)
{
- if (bus->msg_todo > 0) {
+ if (bus->rx_msg_todo > 0) {
const spi_ioc_transfer *msg;
- msg = bus->msg;
+ msg = bus->rx_msg;
- imxrt_lpspi_config(bus, regs, msg);
- bus->remaining_tx_size = msg->len;
bus->remaining_rx_size = msg->len;
bus->rx_buf = msg->rx_buf;
- bus->tx_buf = msg->tx_buf;
-
- imxrt_lpspi_fill_tx_fifo(bus, regs);
- regs->IER = LPSPI_IER_TDIE_MASK;
- } else {
- regs->IER = 0;
- rtems_binary_semaphore_post(&bus->sem);
}
}
@@ -234,15 +261,22 @@ static void imxrt_lpspi_pull_data_from_rx_fifo(
volatile LPSPI_Type *regs
)
{
- while (imxrt_lpspi_rx_fifo_not_empty(regs) && bus->remaining_rx_size > 0) {
- uint32_t data;
-
- data = regs->RDR;
- if (bus->rx_buf != NULL) {
- *bus->rx_buf = data;
- ++bus->rx_buf;
+ uint32_t data;
+ while (imxrt_lpspi_rx_fifo_not_empty(regs)
+ && (bus->rx_msg_todo > 0 || bus->remaining_rx_size > 0)) {
+ if (bus->remaining_rx_size > 0) {
+ data = regs->RDR;
+ if (bus->rx_buf != NULL) {
+ *bus->rx_buf = data;
+ ++bus->rx_buf;
+ }
+ --bus->remaining_rx_size;
+ }
+ if (bus->remaining_rx_size == 0) {
+ --bus->rx_msg_todo;
+ ++bus->rx_msg;
+ imxrt_lpspi_next_rx_msg(bus, regs);
}
- --bus->remaining_rx_size;
}
}
@@ -257,39 +291,22 @@ static void imxrt_lpspi_interrupt(void *arg)
imxrt_lpspi_pull_data_from_rx_fifo(bus, regs);
imxrt_lpspi_fill_tx_fifo(bus, regs);
- if (bus->remaining_tx_size == 0) {
- if (bus->remaining_rx_size > 0) {
- regs->IER = LPSPI_IER_RDIE_MASK;
- } else {
- --bus->msg_todo;
- ++bus->msg;
- imxrt_lpspi_next_msg(bus, regs);
- }
+ if (bus->tx_msg_todo > 0 || bus->remaining_tx_size > 0) {
+ regs->IER = LPSPI_IER_TDIE_MASK;
+ } else if (bus->rx_msg_todo > 0 || bus->remaining_rx_size > 0) {
+ regs->IER = LPSPI_IER_RDIE_MASK;
+ } else {
+ regs->IER = 0;
+ rtems_binary_semaphore_post(&bus->sem);
}
}
static inline int imxrt_lpspi_settings_ok(
struct imxrt_lpspi_bus *bus,
- const spi_ioc_transfer *msg
+ const spi_ioc_transfer *msg,
+ const spi_ioc_transfer *prev_msg
)
{
- if (msg->cs_change == 0) {
- /*
- * This one most likely would need a bigger workaround if it is necessary.
- * See "i.MX RT1050 Processor Reference Manual Rev. 4" Chapter 47.3.2.2
- * "Receive FIFO and Data Match":
- *
- * "During a continuous transfer, if the transmit FIFO is empty, then the
- * receive data is only written to the receive FIFO after the transmit FIFO
- * is written or after the Transmit Command Register (TCR) is written to end
- * the frame."
- *
- * It might is possible to extend the driver so that it can work with an
- * empty read buffer.
- */
- return -EINVAL;
- }
-
/* most of this is currently just not implemented */
if (msg->cs > 3 ||
msg->speed_hz > bus->base.max_speed_hz ||
@@ -299,6 +316,18 @@ static inline int imxrt_lpspi_settings_ok(
return -EINVAL;
}
+ if (prev_msg != NULL && !prev_msg->cs_change) {
+ /*
+ * A lot of settings have to be the same in this case because the upper 8
+ * bit of TCR can't be changed if it is a continuous transfer.
+ */
+ if (prev_msg->cs != msg->cs ||
+ prev_msg->speed_hz != msg->speed_hz ||
+ prev_msg->mode != msg->mode) {
+ return -EINVAL;
+ }
+ }
+
return 0;
}
@@ -308,17 +337,29 @@ static int imxrt_lpspi_check_messages(
uint32_t size
)
{
+ const spi_ioc_transfer *prev_msg = NULL;
+
while(size > 0) {
int rv;
- rv = imxrt_lpspi_settings_ok(bus, msg);
+ rv = imxrt_lpspi_settings_ok(bus, msg, prev_msg);
if (rv != 0) {
return rv;
}
+ prev_msg = msg;
++msg;
--size;
}
+ /*
+ * Check whether cs_change is set on last message. Can't work without it
+ * because the last received data is only put into the FIFO if it is the end
+ * of a transfer or if another TX byte is put into the FIFO.
+ */
+ if (!prev_msg->cs_change) {
+ return -EINVAL;
+ }
+
return 0;
}
@@ -336,10 +377,20 @@ static int imxrt_lpspi_transfer(
rv = imxrt_lpspi_check_messages(bus, msgs, n);
if (rv == 0) {
- bus->msg_todo = n;
- bus->msg = &msgs[0];
-
- imxrt_lpspi_next_msg(bus, bus->regs);
+ bus->tx_msg_todo = n;
+ bus->tx_msg = &msgs[0];
+ bus->rx_msg_todo = n;
+ bus->rx_msg = &msgs[0];
+ bus->cs_change_on_last_msg = true;
+
+ imxrt_lpspi_next_rx_msg(bus, bus->regs);
+ imxrt_lpspi_next_tx_msg(bus, bus->regs);
+ /*
+ * Enable the transmit FIFO empty interrupt which will cause an interrupt
+ * instantly because there is no data in the transmit FIFO. The interrupt
+ * will then fill the FIFO. So nothing else to do here.
+ */
+ bus->regs->IER = LPSPI_IER_TDIE_MASK;
rtems_binary_semaphore_wait(&bus->sem);
}
@@ -416,7 +467,7 @@ static int imxrt_lpspi_setup(spi_bus *base)
bus = (struct imxrt_lpspi_bus *) base;
- rv = imxrt_lpspi_settings_ok(bus, &msg);
+ rv = imxrt_lpspi_settings_ok(bus, &msg, NULL);
/*
* Nothing to do besides checking.
--
2.31.1
More information about the devel
mailing list