[PATCH rtems 2/3] bsp/imx: Use GPIOs for SPI CS.

Christian Mauderer christian.mauderer at embedded-brains.de
Fri Jul 17 05:54:44 UTC 2020


The chip select lines of the iMX SPI module doesn't work well for a
generic API like the one RTEMS uses. The existing solution only worked
in some special cases and had odd bugs when trying transfers of
different sizes (like deselecting between each byte for lengths that are
not dividable by 4).

With this patch the same approach like on FreeBSD or Linux is used:
Treat the CS lines as GPIOs.
---
 bsps/arm/imx/spi/imx-ecspi.c | 92 +++++++++++++++++++++++++++++++++---
 1 file changed, 85 insertions(+), 7 deletions(-)

diff --git a/bsps/arm/imx/spi/imx-ecspi.c b/bsps/arm/imx/spi/imx-ecspi.c
index a18047f2e0..26ba812f62 100644
--- a/bsps/arm/imx/spi/imx-ecspi.c
+++ b/bsps/arm/imx/spi/imx-ecspi.c
@@ -14,6 +14,7 @@
 
 #include <bsp.h>
 #include <bsp/fdt.h>
+#include <bsp/imx-gpio.h>
 #include <libfdt.h>
 #include <arm/freescale/imx/imx_ccmvar.h>
 #include <arm/freescale/imx/imx_ecspireg.h>
@@ -23,6 +24,8 @@
 #include <sys/endian.h>
 
 #define IMX_ECSPI_FIFO_SIZE 64
+#define IMX_ECSPI_MAX_CHIPSELECTS 4
+#define IMX_ECSPI_CS_NONE IMX_ECSPI_MAX_CHIPSELECTS
 
 typedef struct imx_ecspi_bus imx_ecspi_bus;
 
@@ -44,6 +47,10 @@ struct imx_ecspi_bus {
   void (*pop)(imx_ecspi_bus *, volatile imx_ecspi *);
   rtems_id task_id;
   rtems_vector_number irq;
+  struct {
+    struct imx_gpio_pin pin;
+    bool valid;
+  } cspins[IMX_ECSPI_MAX_CHIPSELECTS];
 };
 
 static bool imx_ecspi_is_rx_fifo_not_empty(volatile imx_ecspi *regs)
@@ -129,6 +136,27 @@ static void imx_ecspi_push(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
   }
 }
 
+/* Call with IMX_ECSPI_CS_NONE for @a cs to set all to idle */
+static void
+imx_ecspi_set_chipsel(imx_ecspi_bus *bus, uint32_t cs)
+{
+  size_t i;
+
+  /* Currently this is fixed active low */
+  static const uint32_t idle = 1;
+  static const uint32_t select = 0;
+
+  for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
+    if (bus->cspins[i].valid) {
+      if (i != cs) {
+        imx_gpio_set_output(&bus->cspins[i].pin, idle);
+      } else {
+        imx_gpio_set_output(&bus->cspins[cs].pin, select);
+      }
+    }
+  }
+}
+
 static uint32_t imx_ecspi_conreg_divider(imx_ecspi_bus *bus, uint32_t speed_hz)
 {
   uint32_t post;
@@ -286,6 +314,11 @@ static void imx_ecspi_next_msg(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
         msg->cs
       );
     }
+    if ((msg->mode & SPI_NO_CS) != 0) {
+      imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
+    } else {
+      imx_ecspi_set_chipsel(bus, msg->cs);
+    }
 
     bus->todo = msg->len;
     bus->rx_buf = msg->rx_buf;
@@ -323,6 +356,37 @@ static void imx_ecspi_interrupt(void *arg)
   }
 }
 
+static int imx_ecspi_check_messages(
+  imx_ecspi_bus *bus,
+  const spi_ioc_transfer *msg,
+  uint32_t size)
+{
+  while(size > 0) {
+    if (msg->delay_usecs != 0) {
+      return -EINVAL;
+    }
+    if (msg->bits_per_word > 32) {
+      return -EINVAL;
+    }
+    if ((msg->mode &
+        ~(SPI_CPHA | SPI_CPOL | SPI_LOOP | SPI_NO_CS)) != 0) {
+      return -EINVAL;
+    }
+    if ((msg->mode & SPI_NO_CS) == 0 &&
+        (msg->cs > IMX_ECSPI_MAX_CHIPSELECTS || !bus->cspins[msg->cs].valid)) {
+      return -EINVAL;
+    }
+    if (msg->cs_change != 0) {
+      return -EINVAL;
+    }
+
+    ++msg;
+    --size;
+  }
+
+  return 0;
+}
+
 static int imx_ecspi_transfer(
   spi_bus *base,
   const spi_ioc_transfer *msgs,
@@ -330,16 +394,22 @@ static int imx_ecspi_transfer(
 )
 {
   imx_ecspi_bus *bus;
+  int rv;
 
   bus = (imx_ecspi_bus *) base;
 
-  bus->msg_todo = n;
-  bus->msg = &msgs[0];
-  bus->task_id = rtems_task_self();
+  rv = imx_ecspi_check_messages(bus, msgs, n);
 
-  imx_ecspi_next_msg(bus, bus->regs);
-  rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
-  return 0;
+  if (rv == 0) {
+    bus->msg_todo = n;
+    bus->msg = &msgs[0];
+    bus->task_id = rtems_task_self();
+
+    imx_ecspi_next_msg(bus, bus->regs);
+    rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+    imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
+  }
+  return rv;
 }
 
 static void imx_ecspi_destroy(spi_bus *base)
@@ -356,6 +426,14 @@ static int imx_ecspi_init(imx_ecspi_bus *bus, const void *fdt, int node)
   rtems_status_code sc;
   int len;
   const uint32_t *val;
+  size_t i;
+
+  for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
+    rtems_status_code sc_gpio = imx_gpio_init_from_fdt_property(
+        &bus->cspins[i].pin, node, "cs-gpios", IMX_GPIO_MODE_OUTPUT, i);
+    bus->cspins[i].valid = (sc_gpio == RTEMS_SUCCESSFUL);
+  }
+  imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
 
   imx_ecspi_config(
     bus,
@@ -436,7 +514,7 @@ int spi_bus_register_imx(const char *bus_path, const char *alias_or_path)
   }
 
   bus->base.max_speed_hz = imx_ccm_ecspi_hz();
-  bus->base.delay_usecs = 1;
+  bus->base.delay_usecs = 0;
   bus->regs = imx_get_reg_of_node(fdt, node);
   bus->irq = imx_get_irq_of_node(fdt, node, 0);
 
-- 
2.26.2



More information about the devel mailing list