Signed-off-by: Jiri Vlasak <[email protected]>
---
 arch/arm/src/kinetis/kinetis_spi.c | 53 +++++++++++++++++++++---------
 1 file changed, 38 insertions(+), 15 deletions(-)

diff --git a/arch/arm/src/kinetis/kinetis_spi.c 
b/arch/arm/src/kinetis/kinetis_spi.c
index 430b3e7bc9..fde628c5b1 100644
--- a/arch/arm/src/kinetis/kinetis_spi.c
+++ b/arch/arm/src/kinetis/kinetis_spi.c
@@ -1293,9 +1293,21 @@ static void spi_exchange_nodma(struct spi_dev_s *dev,
  * Description:
  *   Exchange a block of data on SPI using DMA
  *
+ *   DMA maps developer's user-space memory txbuffer directly to SPI's PUSHR
+ *   register. 8- and 16-bit writes and reads to the PUSHR are bad (8-bit 0xff
+ *   write to the PUSHR results in 0xfcffffff in the TX FIFO, effectively
+ *   re-using data (low 16 bits of PUSHR) as command information (high 16 bits
+ *   of PUSHR), see K60 Reference Manual K60P144M150SF3RM.pdf, 53.3.7.)
+ *   Therefore, txbuffer, when not NULL, MUST be 32-bit pointer that includes
+ *   both, data and command information.
+ *
+ *   When txbuffer != NULL && rxbuffer != NULL, then rxbuffer needs to be
+ *   32-bit pointer, too. If txbuffer == NULL, decide based on the number of
+ *   bits transferred over SPI.
+ *
  * Input Parameters:
  *   dev      - Device-specific state data
- *   txbuffer - A pointer to the buffer of data to be sent
+ *   txbuffer - A 32-bit pointer to the buffer of cmd info and data to be sent
  *   rxbuffer - A pointer to a buffer in which to receive data
  *   nwords   - the length of data to be exchanged in units of words.
  *              The wordsize is determined by the number of bits-per-word
@@ -1316,7 +1328,7 @@ static void spi_exchange(struct spi_dev_s *dev, const 
void *txbuffer,
   size_t                   adjust;
   ssize_t                  nbytes;
   static uint8_t           rxdummy[4] aligned_data(4);
-  static const uint16_t    txdummy = 0xffff;
+  static const uint32_t    txdummy = 0x0000ffff;
   struct kinetis_spidev_s *priv    = (struct kinetis_spidev_s *)dev;
 
   DEBUGASSERT(priv != NULL);
@@ -1374,11 +1386,15 @@ static void spi_exchange(struct spi_dev_s *dev, const 
void *txbuffer,
   config.daddr  = (uint32_t) (rxbuffer ? rxbuffer : rxdummy);
   config.soff   = 0;
   config.doff   = rxbuffer ? adjust : 0;
-  config.iter   = nbytes;
+  config.iter   = txbuffer ? nwords : nbytes;
   config.flags  = EDMA_CONFIG_LINKTYPE_LINKNONE;
-  config.ssize  = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
-  config.dsize  = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
-  config.nbytes = adjust;
+  config.ssize  = txbuffer
+                  ? EDMA_32BIT
+                  : (adjust == 1 ? EDMA_8BIT : EDMA_16BIT);
+  config.dsize  = txbuffer
+                  ? EDMA_32BIT
+                  : (adjust == 1 ? EDMA_8BIT : EDMA_16BIT);
+  config.nbytes = txbuffer ? 4 : adjust;
 #ifdef CONFIG_KINETIS_EDMA_ELINK
   config.linkch = NULL;
 #endif
@@ -1390,13 +1406,17 @@ static void spi_exchange(struct spi_dev_s *dev, const 
void *txbuffer,
 
   config.saddr  = (uint32_t) (txbuffer ? txbuffer : &txdummy);
   config.daddr  = priv->spibase + KINETIS_SPI_PUSHR_OFFSET;
-  config.soff   = txbuffer ? adjust : 0;
+  config.soff   = txbuffer ? 4 : 0;
   config.doff   = 0;
-  config.iter   = nbytes;
+  config.iter   = txbuffer ? nwords : nbytes;
   config.flags  = EDMA_CONFIG_LINKTYPE_LINKNONE;
-  config.ssize  = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
-  config.dsize  = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
-  config.nbytes = adjust;
+  config.ssize  = txbuffer
+                  ? EDMA_32BIT
+                  : (adjust == 1 ? EDMA_8BIT : EDMA_16BIT);
+  config.dsize  = txbuffer
+                  ? EDMA_32BIT
+                  : (adjust == 1 ? EDMA_8BIT : EDMA_16BIT);
+  config.nbytes = txbuffer ? 4 : adjust;
 #ifdef CONFIG_KINETIS_EDMA_ELINK
   config.linkch = NULL;
 #endif
@@ -1410,14 +1430,17 @@ static void spi_exchange(struct spi_dev_s *dev, const 
void *txbuffer,
                 SPI_RSER_RFDF_RE | SPI_RSER_TFFF_RE |
                 SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_DIRS);
 
+  /* Reset TCR to zero. It can also be reset using CTCNT field in the upper 16
+   * bits (command info) of the PUSHR register. Do not write the TCR when the
+   * module is in the Running state.
+   */
+
+  spi_putreg(priv, KINETIS_SPI_TCR_OFFSET, 0);
+
   /* Start the DMAs */
 
   spi_dmarxstart(priv);
   spi_run(priv, true);
-
-  spi_putreg(priv, KINETIS_SPI_TCR_OFFSET, 0);
-  spi_write_control(priv, SPI_PUSHR_CTAS_CTAR0);
-
   spi_dmatxstart(priv);
 
   /* Then wait for each to complete */
-- 
2.47.3

Reply via email to