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