Signed-off-by: Jiri Vlasak <[email protected]>
---
 arch/arm/src/kinetis/hardware/kinetis_dspi.h |   6 +
 arch/arm/src/kinetis/kinetis_spi.c           | 193 +++++++++++++++++++
 2 files changed, 199 insertions(+)

diff --git a/arch/arm/src/kinetis/hardware/kinetis_dspi.h 
b/arch/arm/src/kinetis/hardware/kinetis_dspi.h
index 787a58b003..b0f876c562 100644
--- a/arch/arm/src/kinetis/hardware/kinetis_dspi.h
+++ b/arch/arm/src/kinetis/hardware/kinetis_dspi.h
@@ -173,10 +173,13 @@
 #  define SPI_CTARM_BR_32768       (15 << SPI_CTARM_BR_SHIFT)
 #define SPI_CTARM_DT_SHIFT         (4)       /* Bits 4-7: Delay After Transfer 
Scaler */
 #define SPI_CTARM_DT_MASK          (15 << SPI_CTARM_DT_SHIFT)
+#  define SPI_CTARM_DT(n)          ((((n) & 0xf)) << SPI_CTARM_DT_SHIFT)
 #define SPI_CTARM_ASC_SHIFT        (8)       /* Bits 8-11: After SCK Delay 
Scaler */
 #define SPI_CTARM_ASC_MASK         (15 << SPI_CTARM_ASC_SHIFT)
+#  define SPI_CTARM_ASC(n)         ((((n) & 0xf)) << SPI_CTARM_ASC_SHIFT)
 #define SPI_CTARM_CSSCK_SHIFT      (12)      /* Bits 12-15: PCS to SCK Delay 
Scaler */
 #define SPI_CTARM_CSSCK_MASK       (15 << SPI_CTARM_CSSCK_SHIFT)
+#  define SPI_CTARM_CSSCK(n)       ((((n) & 0xf)) << SPI_CTARM_CSSCK_SHIFT)
 #  define SPI_CTARM_CSSCK_2        (0 << SPI_CTARM_CSSCK_SHIFT)
 #  define SPI_CTARM_CSSCK_4        (1 << SPI_CTARM_CSSCK_SHIFT)
 #  define SPI_CTARM_CSSCK_8        (2 << SPI_CTARM_CSSCK_SHIFT)
@@ -202,18 +205,21 @@
 #  define SPI_CTARM_PBR_7          (3 << SPI_CTARM_PBR_SHIFT)
 #define SPI_CTARM_PDT_SHIFT        (18)      /* Bits 18-19: Delay after 
Transfer Prescaler */
 #define SPI_CTARM_PDT_MASK         (3 << SPI_CTARM_PDT_SHIFT)
+#  define SPI_CTARM_PDT(n)         ((((n) & 0x3)) << SPI_CTARM_PDT_SHIFT)
 #  define SPI_CTARM_PDT_1          (0 << SPI_CTARM_PDT_SHIFT)
 #  define SPI_CTARM_PDT_3          (1 << SPI_CTARM_PDT_SHIFT)
 #  define SPI_CTARM_PDT_5          (2 << SPI_CTARM_PDT_SHIFT)
 #  define SPI_CTARM_PDT_7          (3 << SPI_CTARM_PDT_SHIFT)
 #define SPI_CTARM_PASC_SHIFT       (20)      /* Bits 20-21: After SCK Delay 
Prescaler */
 #define SPI_CTARM_PASC_MASK        (3 << SPI_CTARM_PASC_SHIFT)
+#  define SPI_CTARM_PASC(n)        ((((n) & 0x3)) << SPI_CTARM_PASC_SHIFT)
 #  define SPI_CTARM_PASC_1         (0 << SPI_CTARM_PASC_SHIFT)
 #  define SPI_CTARM_PASC_3         (1 << SPI_CTARM_PASC_SHIFT)
 #  define SPI_CTARM_PASC_5         (2 << SPI_CTARM_PASC_SHIFT)
 #  define SPI_CTARM_PASC_7         (3 << SPI_CTARM_PASC_SHIFT)
 #define SPI_CTARM_PCSSCK_SHIFT     (22)      /* Bits 22-23: PCS to SCK Delay 
Prescaler */
 #define SPI_CTARM_PCSSCK_MASK      (3 << SPI_CTARM_PCSSCK_SHIFT)
+#  define SPI_CTARM_PCSSCK(n)      ((((n) & 0x3)) << SPI_CTARM_PCSSCK_SHIFT)
 #  define SPI_CTARM_PCSSCK_1       (0 << SPI_CTARM_PCSSCK_SHIFT)
 #  define SPI_CTARM_PCSSCK_3       (1 << SPI_CTARM_PCSSCK_SHIFT)
 #  define SPI_CTARM_PCSSCK_5       (2 << SPI_CTARM_PCSSCK_SHIFT)
diff --git a/arch/arm/src/kinetis/kinetis_spi.c 
b/arch/arm/src/kinetis/kinetis_spi.c
index 4daccc58e7..9e60f1300b 100644
--- a/arch/arm/src/kinetis/kinetis_spi.c
+++ b/arch/arm/src/kinetis/kinetis_spi.c
@@ -167,6 +167,11 @@ static inline void spi_dmatxstart(struct kinetis_spidev_s 
*priv);
 static int         spi_lock(struct spi_dev_s *dev, bool lock);
 static uint32_t    spi_setfrequency(struct spi_dev_s *dev,
                                     uint32_t frequency);
+#ifdef CONFIG_SPI_DELAY_CONTROL
+static int         spi_setdelay(struct spi_dev_s *dev,
+                                uint32_t startdelay, uint32_t stopdelay,
+                                uint32_t csdelay, uint32_t ifdelay);
+#endif
 static void        spi_setmode(struct spi_dev_s *dev,
                                enum spi_mode_e mode);
 static void        spi_setbits(struct spi_dev_s *dev, int nbits);
@@ -196,6 +201,9 @@ static const struct spi_ops_s g_spi0ops =
   .lock              = spi_lock,
   .select            = kinetis_spi0select,
   .setfrequency      = spi_setfrequency,
+#ifdef CONFIG_SPI_DELAY_CONTROL
+  .setdelay          = spi_setdelay,
+#endif
   .setmode           = spi_setmode,
   .setbits           = spi_setbits,
 #  ifdef CONFIG_SPI_HWFEATURES
@@ -248,6 +256,9 @@ static const struct spi_ops_s g_spi1ops =
   .lock              = spi_lock,
   .select            = kinetis_spi1select,
   .setfrequency      = spi_setfrequency,
+#ifdef CONFIG_SPI_DELAY_CONTROL
+  .setdelay          = spi_setdelay,
+#endif
   .setmode           = spi_setmode,
   .setbits           = spi_setbits,
 #  ifdef CONFIG_SPI_HWFEATURES
@@ -300,6 +311,9 @@ static const struct spi_ops_s g_spi2ops =
   .lock              = spi_lock,
   .select            = kinetis_spi2select,
   .setfrequency      = spi_setfrequency,
+#ifdef CONFIG_SPI_DELAY_CONTROL
+  .setdelay          = spi_setdelay,
+#endif
   .setmode           = spi_setmode,
   .setbits           = spi_setbits,
 #  ifdef CONFIG_SPI_HWFEATURES
@@ -816,6 +830,185 @@ static uint32_t spi_setfrequency(struct spi_dev_s *dev,
   return priv->actual;
 }
 
+/****************************************************************************
+ * Name: spi_setdelay
+ *
+ * Description:
+ *   Set the SPI Delays in nanoseconds. Optional.
+ *
+ * Input Parameters:
+ *   dev        - Device-specific state data
+ *   startdelay - The delay between CS active and first CLK
+ *   stopdelay  - The delay between last CLK and CS inactive
+ *   csdelay    - The delay between CS inactive and CS active again
+ *   ifdelay    - The delay between frames
+ *
+ * Returned Value:
+ *   Returns 0 if ok
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPI_DELAY_CONTROL
+static int spi_setdelay(struct spi_dev_s *dev,
+                        uint32_t startdelay, uint32_t stopdelay,
+                        uint32_t csdelay, uint32_t ifdelay)
+{
+  struct kinetis_spidev_s *priv = (struct kinetis_spidev_s *)dev;
+  bool spi_was_halted;
+  uint32_t regval;
+
+  uint64_t req;
+  uint64_t act;
+  uint8_t p;
+  uint8_t s;
+  uint8_t final_p = 0;
+  uint8_t final_s = 0;
+
+  uint32_t min;
+  uint32_t diff;
+
+  /* For requested startdelay (PCS to SCK Delay), stopdelay (After SCK Delay),
+   * and csdelay (Delay After Transfer), we need to find out values p and s
+   * from which we are able to compute the prescaler and the scaler, such that
+   *
+   *   1 / BOARD_BUS_FREQ * prescaler * scaler = req / pow(10, 9)
+   *
+   * where `req` is the requested value, either startdelay, stopdelay, or
+   * csdelay in nanoseconds, and
+   *
+   *   prescaler = (2 * p + 1)
+   *
+   * where `p` is PCSSCK, PASC, or PDT -- a 2-bit value in CTAR register, and
+   *
+   *   scaler = (2 << s)
+   *
+   * where `s` is CSSCK, ASC, or DT -- a 4-bit value in CTAR register. The
+   * approach of finding the `min` over all possible values is used, similar
+   * to the spi_setfrequency.
+   *
+   * Beware that computed `act` value needs to be greater or equal than
+   * the requested `req` value -- we are setting *minimum possible* delays.
+   *
+   * To avoid thinking, the following are transformations of the equation:
+   *
+   *   1 / BOARD_BUS_FREQ * prescaler * scaler = req / pow(10, 9)
+   *   prescaler * scaler = req * BOARD_BUS_FREQ / pow(10, 9)
+   *   (2 * p + 1) * (2 << s) = req * BOARD_BUS_FREQ / pow(10, 9)
+   *
+   * ifdelay is ignored -- not sure what CTAR fields should depend on it?
+   */
+
+  /* Do not write to the CTAR while the module is Running. */
+
+  spi_was_halted = spi_run(priv, false);
+
+  /* PCS to SCK Delay: computing PCSSCK and CSSCK. */
+
+  min  = UINT32_MAX;
+
+  req  = BOARD_BUS_FREQ;
+  req *= startdelay;
+  req /= 1000000000;
+
+  for (p = 0; p < (1 << 2); p++)
+    {
+      for (s = 0; s < (1 << 4); s++)
+        {
+          act = (2 * p + 1) * (2 << s);
+          if (act >= req)
+            {
+              diff = act - req;
+              if (diff < min)
+                {
+                  min = diff;
+                  final_p = p;
+                  final_s = s;
+                }
+            }
+        }
+    }
+
+  regval = spi_getreg(priv, priv->ctarsel);
+  regval &= ~(SPI_CTARM_PCSSCK_MASK | SPI_CTARM_CSSCK_MASK);
+  regval |= (SPI_CTARM_PCSSCK(final_p) | SPI_CTARM_CSSCK(final_s));
+  spi_putreg(priv, priv->ctarsel, regval);
+
+  spiinfo("PCSSCK set to 0x%02x, CSSCK set to 0x%04x", final_p, final_s);
+
+  /* After SCK Delay: computing PASC and ASC. */
+
+  min  = UINT32_MAX;
+
+  req  = BOARD_BUS_FREQ;
+  req *= stopdelay;
+  req /= 1000000000;
+
+  for (p = 0; p < (1 << 2); p++)
+    {
+      for (s = 0; s < (1 << 4); s++)
+        {
+          act = (2 * p + 1) * (2 << s);
+          if (act >= req)
+            {
+              diff = act - req;
+              if (diff < min)
+                {
+                  min = diff;
+                  final_p = p;
+                  final_s = s;
+                }
+            }
+        }
+    }
+
+  regval = spi_getreg(priv, priv->ctarsel);
+  regval &= ~(SPI_CTARM_PASC_MASK | SPI_CTARM_ASC_MASK);
+  regval |= (SPI_CTARM_PASC(final_p) | SPI_CTARM_ASC(final_s));
+  spi_putreg(priv, priv->ctarsel, regval);
+
+  spiinfo("PASC set to 0x%02x, ASC set to 0x%04x", final_p, final_s);
+
+  /* Delay After Transfer: computing PDT and DT. */
+
+  min  = UINT32_MAX;
+
+  req  = BOARD_BUS_FREQ;
+  req *= startdelay;
+  req /= 1000000000;
+
+  for (p = 0; p < (1 << 2); p++)
+    {
+      for (s = 0; s < (1 << 4); s++)
+        {
+          act = (2 * p + 1) * (2 << s);
+          if (act >= req)
+            {
+              diff = act - req;
+              if (diff < min)
+                {
+                  min = diff;
+                  final_p = p;
+                  final_s = s;
+                }
+            }
+        }
+    }
+
+  regval = spi_getreg(priv, priv->ctarsel);
+  regval &= ~(SPI_CTARM_PDT_MASK | SPI_CTARM_DT_MASK);
+  regval |= (SPI_CTARM_PDT(final_p) | SPI_CTARM_DT(final_s));
+  spi_putreg(priv, priv->ctarsel, regval);
+
+  spiinfo("PDT set to 0x%02x, DT set to 0x%04x", final_p, final_s);
+
+  if (!spi_was_halted) {
+    spi_run(priv, true);
+  }
+
+  return 0;
+}
+#endif
+
 /****************************************************************************
  * Name: spi_setmode
  *
-- 
2.47.3

Reply via email to