nuttx/arch/arm/src/s32k3xx/s32k3xx_lpspi.c
2022-12-15 22:21:32 +08:00

2332 lines
66 KiB
C

/****************************************************************************
* arch/arm/src/s32k3xx/s32k3xx_lpspi.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Copyright 2022 NXP */
/****************************************************************************
* The external functions, s32k3xx_lpspi0/1/2select and
* s32k3xx_lpspi0/1/2status must be provided by board-specific logic. They
* are implementations of the select and status methods of the SPI interface
* defined by struct s32k3xx_lpspi_ops_s (see include/nuttx/spi/spi.h).
* All other methods (including s32k3xx_lpspibus_initialize()) are provided
* by common S32K3XX logic. To use this common SPI logic on your board:
*
* 1. Provide logic in s32k3xx_boardinitialize() to configure SPI chip
* select pins.
* 2. Provide s32k3xx_lpspi0/1/2select() and s32k3xx_lpspi0/1/2status()
* functions in your board-specific logic. These functions will perform
* chip selection and status operations using GPIOs in the way your
* board is configured.
* 3. Add a calls to s32k3xx_lpspibus_initialize() in your low level
* application initialization logic
* 4. The handle returned by s32k3xx_lpspibus_initialize() may then be
* used to bind the SPI driver to higher level logic (e.g., calling
* mmcsd_lpspislotinitialize(), for example, will bind the SPI driver
* to the SPI MMC/SD driver).
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <endian.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/mutex.h>
#include <nuttx/spi/spi.h>
#include <nuttx/power/pm.h>
#include "arm_internal.h"
#include "chip.h"
#include "s32k3xx_pin.h"
#include "hardware/s32k3xx_pinmux.h"
#include "hardware/s32k3xx_lpspi.h"
#include "s32k3xx_clockconfig.h"
#include "s32k3xx_lpspi.h"
#ifdef CONFIG_S32K3XX_LPSPI_DMA
#include "hardware/s32k3xx_dmamux.h"
#include "s32k3xx_edma.h"
#endif
#include <arch/board/board.h>
#if defined(CONFIG_S32K3XX_LPSPI0) || defined(CONFIG_S32K3XX_LPSPI1) || \
defined(CONFIG_S32K3XX_LPSPI2) || defined(CONFIG_S32K3XX_LPSPI3) || \
defined(CONFIG_S32K3XX_LPSPI4) || defined(CONFIG_S32K3XX_LPSPI5)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* SPI interrupts */
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
# error "Interrupt driven SPI not yet supported"
#endif
/* Can't have both interrupt driven SPI and SPI DMA */
#if defined(CONFIG_S32K3XX_LPSPI_INTERRUPTS) && defined(CONFIG_S32K3XX_LPSPI_DMA)
# error "Cannot enable both interrupt mode and DMA mode for SPI"
#endif
#define SPI_SR_CLEAR (LPSPI_SR_WCF | LPSPI_SR_FCF | LPSPI_SR_TCF | \
LPSPI_SR_TEF | LPSPI_SR_REF | LPSPI_SR_DMF)
/****************************************************************************
* Private Types
****************************************************************************/
struct s32k3xx_lpspidev_s
{
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
uint32_t spibase; /* SPIn base address */
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
uint8_t spiirq; /* SPI IRQ number */
#endif
mutex_t lock; /* Held while chip is selected for mutual exclusion */
uint32_t frequency; /* Requested clock frequency */
uint32_t actual; /* Actual clock frequency */
int8_t nbits; /* Width of word in bits */
uint8_t mode; /* Mode 0,1,2,3 */
uint32_t pincfg; /* Input output port configuration */
#ifdef CONFIG_S32K3XX_LPSPI_DMA
volatile uint32_t rxresult; /* Result of the RX DMA */
volatile uint32_t txresult; /* Result of the TX DMA */
const uint16_t rxch; /* The RX DMA channel number */
const uint16_t txch; /* The TX DMA channel number */
DMACH_HANDLE rxdma; /* DMA channel handle for RX transfers */
DMACH_HANDLE txdma; /* DMA channel handle for TX transfers */
sem_t rxsem; /* Wait for RX DMA to complete */
sem_t txsem; /* Wait for TX DMA to complete */
#endif
};
enum s32k3xx_delay_e
{
LPSPI_PCS_TO_SCK = 1, /* PCS-to-SCK delay. */
LPSPI_LAST_SCK_TO_PCS, /* Last SCK edge to PCS delay. */
LPSPI_BETWEEN_TRANSFER /* Delay between transfers. */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Helpers */
static inline uint32_t
s32k3xx_lpspi_getreg32(struct s32k3xx_lpspidev_s *priv, uint8_t offset);
static inline void s32k3xx_lpspi_putreg32(struct s32k3xx_lpspidev_s *priv,
uint8_t offset, uint32_t value);
static inline uint32_t
s32k3xx_lpspi_readword(struct s32k3xx_lpspidev_s *priv);
static inline void s32k3xx_lpspi_writeword(struct s32k3xx_lpspidev_s *priv,
uint32_t byte);
static inline uint16_t
s32k3xx_lpspi_9to16bitmode(struct s32k3xx_lpspidev_s *priv);
static uint32_t s32k3xx_lpspi_pckfreq(uintptr_t base);
static inline uint32_t
s32k3xx_lpspi_set_delays(struct s32k3xx_lpspidev_s *priv,
uint32_t delay_ns, enum s32k3xx_delay_e type);
static inline uint32_t
s32k3xx_lpspi_set_delay_scaler(struct s32k3xx_lpspidev_s *priv,
uint32_t scaler, enum s32k3xx_delay_e type);
/* DMA support */
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static int spi_dmarxwait(struct s32k3xx_lpspidev_s *priv);
static int spi_dmatxwait(struct s32k3xx_lpspidev_s *priv);
static inline void spi_dmarxwakeup(struct s32k3xx_lpspidev_s *priv);
static inline void spi_dmatxwakeup(struct s32k3xx_lpspidev_s *priv);
static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg,
bool done, int result);
static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg,
bool done, int result);
static inline void spi_dmarxstart(struct s32k3xx_lpspidev_s *priv);
static inline void spi_dmatxstart(struct s32k3xx_lpspidev_s *priv);
#endif
/* SPI methods */
static int s32k3xx_lpspi_lock(struct spi_dev_s *dev, bool lock);
static uint32_t s32k3xx_lpspi_setfrequency(struct spi_dev_s *dev,
uint32_t frequency);
static void s32k3xx_lpspi_setmode(struct spi_dev_s *dev,
enum spi_mode_e mode);
static void s32k3xx_lpspi_setbits(struct spi_dev_s *dev, int nbits);
#ifdef CONFIG_SPI_HWFEATURES
static int s32k3xx_lpspi_hwfeatures(struct spi_dev_s *dev,
s32k3xx_lpspi_hwfeatures_t features);
#endif
static uint32_t s32k3xx_lpspi_send(struct spi_dev_s *dev, uint32_t wd);
static void s32k3xx_lpspi_exchange(struct spi_dev_s *dev,
const void *txbuffer, void *rxbuffer,
size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
static void s32k3xx_lpspi_sndblock(struct spi_dev_s *dev,
const void *txbuffer, size_t nwords);
static void s32k3xx_lpspi_recvblock(struct spi_dev_s *dev, void *rxbuffer,
size_t nwords);
#endif
/* Initialization */
static void s32k3xx_lpspi_bus_initialize(struct s32k3xx_lpspidev_s *priv);
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI0
static const struct spi_ops_s g_spi0ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi0select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi0status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi0cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi0register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi0dev =
{
.spidev =
{
.ops = &g_spi0ops,
},
.spibase = S32K3XX_LPSPI0_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI0,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI0_DMA
.rxch = DMA_REQ_LPSPI0_RX,
.txch = DMA_REQ_LPSPI0_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI0_PINCFG,
};
#endif
#ifdef CONFIG_S32K3XX_LPSPI1
static const struct spi_ops_s g_spi1ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi1select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi1status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi1cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi1register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi1dev =
{
.spidev =
{
.ops = &g_spi1ops,
},
.spibase = S32K3XX_LPSPI1_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI1,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI1_DMA
.rxch = DMA_REQ_LPSPI1_RX,
.txch = DMA_REQ_LPSPI1_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI1_PINCFG,
};
#endif
#ifdef CONFIG_S32K3XX_LPSPI2
static const struct spi_ops_s g_spi2ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi2select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi2status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi2cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi2register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi2dev =
{
.spidev =
{
.ops = &g_spi2ops,
},
.spibase = S32K3XX_LPSPI2_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI2,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI2_DMA
.rxch = DMA_REQ_LPSPI2_RX,
.txch = DMA_REQ_LPSPI2_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI2_PINCFG,
};
#endif
#ifdef CONFIG_S32K3XX_LPSPI3
static const struct spi_ops_s g_spi3ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi3select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi3status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi3cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi3register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi3dev =
{
.spidev =
{
.ops = &g_spi3ops,
},
.spibase = S32K3XX_LPSPI3_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI3,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI3_DMA
.rxch = DMA_REQ_LPSPI3_RX,
.txch = DMA_REQ_LPSPI3_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI3_PINCFG,
};
#endif
#ifdef CONFIG_S32K3XX_LPSPI4
static const struct spi_ops_s g_spi4ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi4select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi4status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi4cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi4register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi4dev =
{
.spidev =
{
.ops = &g_spi4ops,
},
.spibase = S32K3XX_LPSPI4_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI4,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI4_DMA
.rxch = DMA_REQ_LPSPI4_RX,
.txch = DMA_REQ_LPSPI4_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI4_PINCFG,
};
#endif
#ifdef CONFIG_S32K3XX_LPSPI5
static const struct spi_ops_s g_spi5ops =
{
.lock = s32k3xx_lpspi_lock,
.select = s32k3xx_lpspi5select,
.setfrequency = s32k3xx_lpspi_setfrequency,
.setmode = s32k3xx_lpspi_setmode,
.setbits = s32k3xx_lpspi_setbits,
#ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = s32k3xx_lpspi_hwfeatures,
#endif
.status = s32k3xx_lpspi5status,
#ifdef CONFIG_SPI_CMDDATA
.cmddata = s32k3xx_lpspi5cmddata,
#endif
.send = s32k3xx_lpspi_send,
#ifdef CONFIG_SPI_EXCHANGE
.exchange = s32k3xx_lpspi_exchange,
#else
.sndblock = s32k3xx_lpspi_sndblock,
.recvblock = s32k3xx_lpspi_recvblock,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = s32k3xx_lpspi5register, /* Provided externally */
#else
.registercallback = 0, /* Not implemented */
#endif
};
static struct s32k3xx_lpspidev_s g_lpspi5dev =
{
.spidev =
{
.ops = &g_spi5ops,
},
.spibase = S32K3XX_LPSPI5_BASE,
#ifdef CONFIG_S32K3XX_LPSPI_INTERRUPTS
.spiirq = S32K3XX_IRQ_LPSPI5,
#endif
.lock = NXMUTEX_INITIALIZER,
#ifdef CONFIG_S32K3XX_LPSPI5_DMA
.rxch = DMA_REQ_LPSPI5_RX,
.txch = DMA_REQ_LPSPI5_TX,
.rxsem = SEM_INITIALIZER(0),
.txsem = SEM_INITIALIZER(0),
#endif
.pincfg = CONFIG_S32K3XX_LPSPI5_PINCFG,
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: s32k3xx_lpspi_getreg8
*
* Description:
* Get the contents of the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
*
* Returned Value:
* The contents of the 8-bit register
*
****************************************************************************/
static inline uint8_t s32k3xx_lpspi_getreg8(struct s32k3xx_lpspidev_s *priv,
uint8_t offset)
{
return getreg8(priv->spibase + offset);
}
/****************************************************************************
* Name: s32k3xx_lpspi_putreg8
*
* Description:
* Write a 8-bit value to the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
* value - the 8-bit value to be written
*
****************************************************************************/
static inline void s32k3xx_lpspi_putreg8(struct s32k3xx_lpspidev_s *priv,
uint8_t offset, uint8_t value)
{
putreg8(value, priv->spibase + offset);
}
/****************************************************************************
* Name: s32k3xx_lpspi_getreg
*
* Description:
* Get the contents of the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
*
* Returned Value:
* The contents of the 32-bit register
*
****************************************************************************/
static inline uint32_t
s32k3xx_lpspi_getreg32(struct s32k3xx_lpspidev_s *priv, uint8_t offset)
{
return getreg32(priv->spibase + offset);
}
/****************************************************************************
* Name: s32k3xx_lpspi_putreg
*
* Description:
* Write a 16-bit value to the SPI register at offset
*
* Input Parameters:
* priv - private SPI device structure
* offset - offset to the register of interest
* value - the 32-bit value to be written
*
* Returned Value:
* The contents of the 32-bit register
*
****************************************************************************/
static inline void s32k3xx_lpspi_putreg32(struct s32k3xx_lpspidev_s *priv,
uint8_t offset, uint32_t value)
{
putreg32(value, priv->spibase + offset);
}
/****************************************************************************
* Name: s32k3xx_lpspi_readword
*
* Description:
* Read one word from SPI
*
* Input Parameters:
* priv - Device-specific state data
*
* Returned Value:
* word as read
*
****************************************************************************/
static inline uint32_t
s32k3xx_lpspi_readword(struct s32k3xx_lpspidev_s *priv)
{
/* Wait until the receive buffer is not empty */
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET) &
LPSPI_SR_RDF) == 0)
{
}
/* Then return the received byte */
return (uint32_t) s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_RDR_OFFSET);
}
/****************************************************************************
* Name: s32k3xx_lpspi_writeword
*
* Description:
* Write one word to SPI
*
* Input Parameters:
* priv - Device-specific state data
* word - word to send
*
* Returned Value:
* None
*
****************************************************************************/
static inline void s32k3xx_lpspi_writeword(struct s32k3xx_lpspidev_s *priv,
uint32_t word)
{
/* Wait until the transmit buffer is empty */
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET) &
LPSPI_SR_TDF) == 0)
{
}
/* Then send the word */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_TDR_OFFSET, word);
}
/****************************************************************************
* Name: s32k3xx_lpspi_write_dword
*
* Description:
* Write two words to SPI
*
* Input Parameters:
* priv - Device-specific state data
* word0, word1 - words to send
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
static inline void s32k3xx_lpspi_write_dword(struct s32k3xx_lpspidev_s *priv,
uint32_t word0, uint32_t word1)
{
/* Wait until the transmit buffer is empty */
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET)
& LPSPI_SR_TDF) == 0)
{
}
/* Then send the words, use the FIFO */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_TDR_OFFSET, word0);
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_TDR_OFFSET, word1);
}
#endif
/****************************************************************************
* Name: s32k3xx_lpspi_9to16bitmode
*
* Description:
* Check if the SPI is operating in more then 8 bit mode
* On the S32K the frame size can grow to 4096 bit/frame
*
* Input Parameters:
* priv - Device-specific state data
*
* Returned Value:
* value: frame size
*
****************************************************************************/
static inline uint16_t
s32k3xx_lpspi_9to16bitmode(struct s32k3xx_lpspidev_s *priv)
{
uint16_t ret;
ret = ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_TCR_OFFSET) &
LPSPI_TCR_FRAMESZ_MASK) + 1);
return ret;
}
/****************************************************************************
* Name: s32k3xx_lpspi_modifyreg32
*
* Description:
* Clear and set bits in register
*
* Input Parameters:
* priv - Device-specific state data
* offset - Register offset
* clrbits - The bits to clear
* setbits - The bits to set
*
* Returned Value:
* None
*
****************************************************************************/
static void s32k3xx_lpspi_modifyreg32(struct s32k3xx_lpspidev_s *priv,
uint8_t offset, uint32_t clrbits,
uint32_t setbits)
{
modifyreg32(priv->spibase + offset, clrbits, setbits);
}
/****************************************************************************
* Name: s32k3xx_lpspi_pckfreq
*
* Description:
* Get the peripheral clock frequency for the LPSPI peripheral
*
* Input Parameters:
* base - The base address of the LPSPI peripheral registers
*
* Returned Value:
* The frequency of the LPSPI functional input frequency
* (or zero on a failure)
*
****************************************************************************/
static uint32_t s32k3xx_lpspi_pckfreq(uintptr_t base)
{
uint32_t clkfreq;
#ifdef CONFIG_S32K3XX_LPSPI0
if (base == S32K3XX_LPSPI0_BASE)
{
clkfreq = s32k3xx_get_freq(AIPS_PLAT_CLK);
}
else
#endif
{
clkfreq = s32k3xx_get_freq(AIPS_SLOW_CLK);
}
DEBUGASSERT(clkfreq >= 0);
return clkfreq;
}
/****************************************************************************
* Name: s32k3xx_lpspi_set_delays
*
* Description:
* SET LPSPI Delay times
*
* Input Parameters:
* priv - Device-specific state data
* scaler - scaler value
* type - delay time type
*
* Returned Value:
* None
*
****************************************************************************/
static inline uint32_t
s32k3xx_lpspi_set_delay_scaler(struct s32k3xx_lpspidev_s *priv,
uint32_t scaler, enum s32k3xx_delay_e type)
{
switch (type)
{
case LPSPI_PCS_TO_SCK:
return LPSPI_CCR_PCSSCK(scaler);
break;
case LPSPI_LAST_SCK_TO_PCS:
return LPSPI_CCR_SCKPCS(scaler);
break;
case LPSPI_BETWEEN_TRANSFER:
return LPSPI_CCR_DBT(scaler);
break;
}
return 0;
}
/****************************************************************************
* Name: s32k3xx_lpspi_set_delays
*
* Description:
* SET LPSPI Delay times
*
* Input Parameters:
* priv - Device-specific state data
* delay_ns - delay time in nano seconds
* type - delay time type
*
* Returned Value:
* None
*
****************************************************************************/
static inline uint32_t
s32k3xx_lpspi_set_delays(struct s32k3xx_lpspidev_s *priv,
uint32_t delay_ns, enum s32k3xx_delay_e type)
{
uint32_t inclock;
uint64_t real_delay;
uint32_t scaler;
uint32_t best_scaler;
uint32_t diff;
uint32_t min_diff;
uint64_t initial_delay_ns;
uint32_t clock_div_prescaler;
uint32_t additional_scaler;
/* Get the frequency of the LPSPI functional input clock */
inclock = s32k3xx_lpspi_pckfreq(priv->spibase);
DEBUGASSERT(inclock != 0);
/* Get the pre-scaled input clock */
clock_div_prescaler = inclock /
(1 << ((s32k3xx_lpspi_getreg32(priv,
S32K3XX_LPSPI_TCR_OFFSET) &
LPSPI_TCR_PRESCALE_MASK) >> LPSPI_TCR_PRESCALE_SHIFT));
min_diff = 0xffffffff;
/* Initialize scaler to max value to generate the max delay */
best_scaler = 0xff;
if (type == LPSPI_BETWEEN_TRANSFER)
{
/* First calculate the initial, default delay, note min delay is 2
* clock cycles. Due to large size of * calculated values (uint64_t),
* we need to break up the calculation into several steps to ensure
* accurate calculated results
*/
initial_delay_ns = 1000000000U;
initial_delay_ns *= 2;
initial_delay_ns /= clock_div_prescaler;
additional_scaler = 1U;
}
else
{
/* First calculate the initial, default delay, min delay is 1 clock
* cycle. Due to large size of calculated values (uint64_t), we need to
* break up the calculation into several steps to ensure accurate
* calculated * results.
*/
initial_delay_ns = 1000000000U;
initial_delay_ns /= clock_div_prescaler;
additional_scaler = 0;
}
/* If the initial, default delay is already greater than the desired delay,
* then * set the delay to their initial value (0) and return the delay. In
* other words, * there is no way to decrease the delay value further.
*/
if (initial_delay_ns >= delay_ns)
{
return s32k3xx_lpspi_set_delay_scaler(priv, 0, type);
}
else
{
/* If min_diff = 0, the exit for loop */
for (scaler = 0; (scaler < 256) && min_diff; scaler++)
{
/* Calculate the real delay value as we cycle through the scaler
* values. Due to large size of calculated values (uint64_t),
* we need to break up the calculation into several steps to ensure
* accurate calculated results
*/
real_delay = 1000000000U;
real_delay *= (scaler + 1 + additional_scaler);
real_delay /= clock_div_prescaler;
/* calculate the delay difference based on the conditional
* statement that states that the calculated delay must not be less
* then the desired delay
*/
if (real_delay >= delay_ns)
{
diff = real_delay - delay_ns;
if (min_diff > diff)
{
/* A better match found */
min_diff = diff;
best_scaler = scaler;
}
}
}
return s32k3xx_lpspi_set_delay_scaler(priv, best_scaler, type);
}
}
/****************************************************************************
* Name: s32k3xx_lpspi_lock
*
* Description:
* On SPI buses where there are multiple devices, it will be necessary to
* lock SPI to have exclusive access to the buses for a sequence of
* transfers. The bus should be locked before the chip is selected. After
* locking the SPI bus, the caller should then also call the setfrequency,
* setbits, and setmode methods to make sure that the SPI is properly
* configured for the device. If the SPI bus is being shared, then it
* may have been left in an incompatible state.
*
* Input Parameters:
* dev - Device-specific state data
* lock - true: Lock spi bus, false: unlock SPI bus
*
* Returned Value:
* None
*
****************************************************************************/
static int s32k3xx_lpspi_lock(struct spi_dev_s *dev, bool lock)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
int ret;
if (lock)
{
ret = nxmutex_lock(&priv->lock);
}
else
{
ret = nxmutex_unlock(&priv->lock);
}
return ret;
}
/****************************************************************************
* Name: s32k3xx_lpspi_setfrequency
*
* Description:
* Set the SPI frequency.
*
* Input Parameters:
* dev - Device-specific state data
* frequency - The SPI frequency requested
*
* Returned Value:
* Returns the actual frequency selected
*
****************************************************************************/
static uint32_t s32k3xx_lpspi_setfrequency(struct spi_dev_s *dev,
uint32_t frequency)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t men;
uint32_t regval;
uint32_t inclock;
uint32_t prescaler;
uint32_t best_prescaler;
uint32_t scaler;
uint32_t best_scaler;
uint32_t real_frequency;
uint32_t best_frequency;
uint32_t diff;
uint32_t min_diff;
/* Has the LPSPI bus frequency changed? */
if (frequency != priv->frequency)
{
/* Disable LPSPI if it is enabled */
men = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN;
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
LPSPI_CR_MEN, 0);
}
/* Get the frequency of the LPSPI functional input clock */
inclock = s32k3xx_lpspi_pckfreq(priv->spibase);
DEBUGASSERT(inclock != 0);
min_diff = 0xffffffff;
best_prescaler = 7;
best_scaler = 255;
best_frequency = 0;
for (prescaler = 0; (prescaler < 8) && min_diff; prescaler++)
{
for (scaler = 0; (scaler < 256) && min_diff; scaler++)
{
real_frequency = inclock / ((1 << prescaler) * (scaler + 2));
/* Calculate the frequency difference based on conditional
* statement that states that the calculated frequency must not
* exceed desired frequency.
*/
if (frequency >= real_frequency)
{
diff = frequency - real_frequency;
if (min_diff > diff)
{
/* A better match found */
min_diff = diff;
best_prescaler = prescaler;
best_scaler = scaler;
best_frequency = real_frequency;
}
}
}
}
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_TCR_OFFSET,
LPSPI_TCR_PRESCALE_MASK,
LPSPI_TCR_PRESCALE(best_prescaler));
priv->frequency = frequency;
priv->actual = best_frequency;
regval = 0x0;
regval |= s32k3xx_lpspi_set_delays(priv, 1000000000 / best_frequency,
LPSPI_PCS_TO_SCK);
regval |= s32k3xx_lpspi_set_delays(priv, 1000000000 / best_frequency,
LPSPI_LAST_SCK_TO_PCS);
regval |= s32k3xx_lpspi_set_delays(priv, 1000000000 / best_frequency,
LPSPI_BETWEEN_TRANSFER);
/* Write the best values in the CCR register */
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CCR_OFFSET,
LPSPI_CCR_SCKDIV_MASK,
LPSPI_CCR_SCKDIV(best_scaler));
/* Re-enable LPSPI if it was enabled previously */
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET, 0,
LPSPI_CR_MEN);
}
}
return priv->actual;
}
/****************************************************************************
* Name: s32k3xx_lpspi_setmode
*
* Description:
* Set the SPI mode. see enum spi_mode_e mode for mode definitions
*
* Input Parameters:
* dev - Device-specific state data
* mode - The SPI mode requested
*
* Returned Value:
* Returns the actual frequency selected
*
****************************************************************************/
static void s32k3xx_lpspi_setmode(struct spi_dev_s *dev,
enum spi_mode_e mode)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t setbits;
uint32_t clrbits;
uint32_t men;
spiinfo("mode=%d\n", mode);
/* Has the mode changed? */
if (mode != priv->mode)
{
/* Disable LPSPI if it is enabled */
men = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN;
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
LPSPI_CR_MEN, 0);
}
switch (mode)
{
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
setbits = 0;
clrbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA;
break;
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
setbits = LPSPI_TCR_CPHA;
clrbits = LPSPI_TCR_CPOL;
break;
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
setbits = LPSPI_TCR_CPOL;
clrbits = LPSPI_TCR_CPHA;
break;
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
setbits = LPSPI_TCR_CPOL | LPSPI_TCR_CPHA;
clrbits = 0;
break;
default:
return;
}
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_TCR_OFFSET,
clrbits, setbits);
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_RSR_OFFSET) &
LPSPI_RSR_RXEMPTY) != LPSPI_RSR_RXEMPTY)
{
/* Flush SPI read FIFO */
s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_RSR_OFFSET);
}
/* Save the mode so that subsequent re-configurations will be faster */
priv->mode = mode;
/* Re-enable LPSPI if it was enabled previously */
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
0, LPSPI_CR_MEN);
}
}
}
/****************************************************************************
* Name: s32k3xx_lpspi_setbits
*
* Description:
* Set the number of bits per word.
*
* Input Parameters:
* dev - Device-specific state data
* nbits - The number of bits requested
*
* Returned Value:
* None
*
****************************************************************************/
static void s32k3xx_lpspi_setbits(struct spi_dev_s *dev, int nbits)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t regval;
uint32_t men;
int savbits = nbits;
spiinfo("nbits=%d\n", nbits);
/* Has the number of bits changed? */
if (nbits != priv->nbits)
{
if (nbits < 2 || nbits > 4096)
{
return;
}
/* Disable LPSPI if it is enabled */
men = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN;
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
LPSPI_CR_MEN, 0);
}
regval = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_TCR_OFFSET);
regval &= ~LPSPI_TCR_FRAMESZ_MASK;
regval |= LPSPI_TCR_FRAMESZ(nbits - 1);
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_TCR_OFFSET, regval);
/* Save the selection so that subsequent re-configurations will
* be faster.
*/
priv->nbits = savbits; /* nbits has been clobbered... save the signed
* value. */
/* Re-enable LPSPI if it was enabled previously */
if (men)
{
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
0, LPSPI_CR_MEN);
}
}
}
/****************************************************************************
* Name: s32k3xx_lpspi_hwfeatures
*
* Description:
* Set hardware-specific feature flags.
*
* Input Parameters:
* dev - Device-specific state data
* features - H/W feature flags
*
* Returned Value:
* Zero (OK) if the selected H/W features are enabled; A negated errno
* value if any H/W feature is not supportable.
*
****************************************************************************/
#ifdef CONFIG_SPI_HWFEATURES
static int s32k3xx_lpspi_hwfeatures(struct spi_dev_s *dev,
s32k3xx_lpspi_hwfeatures_t features)
{
#ifdef CONFIG_SPI_BITORDER
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t setbits;
uint32_t clrbits;
int savbits = nbits;
spiinfo("features=%08x\n", features);
/* Transfer data LSB first? */
if ((features & HWFEAT_LSBFIRST) != 0)
{
setbits = LPSPI_TCR_LSBF;
clrbits = 0;
}
else
{
setbits = 0;
clrbits = LPSPI_TCR_LSBF;
}
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_TCR_OFFSET,
clrbits, setbits);
/* Other H/W features are not supported */
return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS;
#else
return -ENOSYS;
#endif
}
#endif
/****************************************************************************
* Name: s32k3xx_lpspi_send
*
* Description:
* Exchange one word on SPI
*
* Input Parameters:
* dev - Device-specific state data
* wd - The word to send. the size of the data is determined by the
* number of bits selected for the SPI interface.
*
* Returned Value:
* response
*
****************************************************************************/
static uint32_t s32k3xx_lpspi_send(struct spi_dev_s *dev, uint32_t wd)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t regval;
uint32_t ret;
DEBUGASSERT(priv && priv->spibase);
s32k3xx_lpspi_writeword(priv, wd);
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET) &
LPSPI_SR_RDF) != LPSPI_SR_RDF);
ret = s32k3xx_lpspi_readword(priv);
/* Check and clear any error flags (Reading from the SR clears the error
* flags).
*/
regval = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET);
spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32
" Status: %02" PRIx32 "\n", wd, ret, regval);
UNUSED(regval);
return ret;
}
/****************************************************************************
* Name: s32k3xx_lpspi_send_dword
*
* Description:
* Exchange two words on SPI
*
* Input Parameters:
* dev - Device-specific state data
* wd0, wd1 - The word to send. the size of the data is determined by the
* number of bits selected for the SPI interface.
*
* Returned Value:
* response
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
static uint32_t s32k3xx_lpspi_send_dword(struct spi_dev_s *dev, uint32_t wd0,
uint32_t wd1, uint32_t *rw1)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint32_t regval;
uint32_t ret;
DEBUGASSERT(priv && priv->spibase);
/* check if the receive buffer is empty, if not clear it */
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET)
& LPSPI_SR_RDF))
{
s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_RDR_OFFSET);
}
s32k3xx_lpspi_write_dword(priv, wd0, wd1);
while ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET)
& LPSPI_SR_RDF) != LPSPI_SR_RDF);
ret = s32k3xx_lpspi_readword(priv);
*rw1 = s32k3xx_lpspi_readword(priv);
/* Check and clear any error flags (Reading from the SR clears the error
* flags).
*/
regval = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_SR_OFFSET);
spiinfo("Sent: %04" PRIx32 " %04" PRIx32 " Return: %04"
PRIx32 " %04" PRIx32 " Status: %02" PRIx32 "\n",
wd0, wd1, ret, *rw1, regval);
UNUSED(regval);
return ret;
}
#endif
/****************************************************************************
* Name: s32k3xx_lpspi_exchange (no DMA). aka s32k3xx_lpspi_exchange_nodma
*
* Description:
* Exchange a block of data on SPI without using DMA
*
* Input Parameters:
* dev - Device-specific state data
* txbuffer - A pointer to the buffer of 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
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits >8, the data is packed into
* uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
#if !defined(CONFIG_S32K3XX_LPSPI_DMA)
static void s32k3xx_lpspi_exchange(struct spi_dev_s *dev,
const void *txbuffer, void *rxbuffer,
size_t nwords)
#else
static void s32k3xx_lpspi_exchange_nodma(struct spi_dev_s *dev,
const void *txbuffer,
void *rxbuffer, size_t nwords)
#endif
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
uint16_t framesize;
DEBUGASSERT(priv && priv->spibase);
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
/* bit mode? */
framesize = s32k3xx_lpspi_9to16bitmode(priv);
if (framesize > 16 && framesize % 32 != 0)
{
/* 17-bit or higher, byte transfer due to padding
* take care of big endian mode of hardware !!
*/
const uint8_t *src = txbuffer;
uint8_t *dest = rxbuffer;
uint32_t word = 0x0;
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
uint32_t word1 = 0x0;
uint32_t rword1;
bool dwords = false;
#endif
while (nwords-- > 0)
{
/* Get the next word to write. Is there a source buffer? */
if (src)
{
/* read the required number of bytes */
switch (framesize)
{
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
case 40:
word = (src[0] << 24) + (src[1] << 16)
+ (src[2] << 8) + src[3];
word1 = src[4];
src += 5;
dwords = true;
break;
#endif
default:
break;
}
}
else
{
word = 0xffffffff;
}
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
/* Exchange 2 words */
if (dwords)
{
word = s32k3xx_lpspi_send_dword(dev, word, word1, &rword1);
}
else
#endif
{
word = s32k3xx_lpspi_send(dev, word);
}
/* Is there a buffer to receive the return value? */
if (dest)
{
switch (framesize)
{
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
case 40:
dest[0] = (word >> 24) & 0xff;
dest[1] = (word >> 16) & 0xff;
dest[2] = (word >> 8) & 0xff;
dest[3] = word & 0xff;
dest[4] = rword1 & 0xff;
dest += 5;
break;
#endif
default:
break;
}
}
}
}
else if (framesize > 16)
{
/* 32-bit or 64 bit, word size memory transfers */
const uint32_t *src = txbuffer;
uint32_t *dest = rxbuffer;
uint32_t word = 0x0;
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
uint32_t word1 = 0x0;
uint32_t rword1;
bool dwords = false;
#endif
while (nwords-- > 0)
{
/* Get the next word to write. Is there a source buffer? */
if (src)
{
/* read the required number of bytes */
switch (framesize)
{
case 32:
word = __builtin_bswap32(*src);
src += 4;
break;
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
case 64:
word = __builtin_bswap32(src[0]);
word1 = __builtin_bswap32(src[1]);
src += 8;
dwords = true;
#endif
default:
break;
}
}
else
{
word = 0xffffffff;
}
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
/* Exchange 2 words */
if (dwords)
{
word = s32k3xx_lpspi_send_dword(dev, word, word1, &rword1);
}
else
#endif
{
word = s32k3xx_lpspi_send(dev, word);
}
/* Is there a buffer to receive the return value? */
if (dest)
{
switch (framesize)
{
case 32:
*dest = __builtin_bswap32(word);
dest += 4;
break;
#ifdef CONFIG_S32K3XX_LPSPI_DWORD
case 64:
dest[0] = __builtin_bswap32(word);
dest[1] = __builtin_bswap32(rword1);
dest += 8;
break;
#endif
default:
break;
}
}
}
}
else if (framesize > 8)
{
/* 16-bit mode */
const uint16_t *src = txbuffer;
uint16_t *dest = rxbuffer;
uint16_t word;
while (nwords-- > 0)
{
/* Get the next word to write. Is there a source buffer? */
if (src)
{
word = swap16(*src++);
/* read the required number of bytes */
}
else
{
word = 0xffff;
}
/* Exchange one word */
word = (uint16_t) s32k3xx_lpspi_send(dev, (uint32_t) word);
/* Is there a buffer to receive the return value? */
if (dest)
{
*dest++ = swap16(word);
}
}
}
else
{
/* 8-bit mode */
const uint8_t *src = txbuffer;
uint8_t *dest = rxbuffer;
uint8_t word;
while (nwords-- > 0)
{
/* Get the next word to write. Is there a source buffer? */
if (src)
{
word = *src++;
}
else
{
word = 0xff;
}
/* Exchange one word */
word = (uint8_t) s32k3xx_lpspi_send(dev, (uint32_t) word);
/* Is there a buffer to receive the return value? */
if (dest)
{
*dest++ = word;
}
}
}
}
/****************************************************************************
* Name: spi_exchange (with DMA capability)
*
* Description:
* Exchange a block of data on SPI using DMA
*
* Input Parameters:
* dev - Device-specific state data
* txbuffer - A pointer to the buffer of 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
* selected for the SPI interface. If nbits <= 8, the data is
* packed into uint8_t's; if nbits > 8, the data is packed into
* uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static void s32k3xx_lpspi_exchange(struct spi_dev_s *dev,
const void *txbuffer, void *rxbuffer,
size_t nwords)
{
int ret;
size_t adjust;
ssize_t nbytes;
static uint8_t rxdummy[4] aligned_data(4);
static const uint16_t txdummy = 0xffff;
uint32_t regval;
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)dev;
DEBUGASSERT(priv != NULL);
DEBUGASSERT(priv && priv->spibase);
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
/* Convert the number of word to a number of bytes */
nbytes = (priv->nbits > 8) ? nwords << 2 : nwords;
/* Invalid DMA channels fall back to non-DMA method. */
if (priv->rxdma == NULL || priv->txdma == NULL
#ifdef CONFIG_S32K3XX_LPSPI_DMATHRESHOLD
/* If this is a small SPI transfer, then let
* s32k3xx_lpspi_exchange_nodma() do the work.
*/
|| nbytes <= CONFIG_S32K3XX_LPSPI_DMATHRESHOLD
#endif
)
{
s32k3xx_lpspi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
return;
}
/* ERR050456 workaround: Reset FIFOs using CR[RST] bit */
regval = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CFGR1_OFFSET);
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET,
LPSPI_CR_RTF | LPSPI_CR_RRF,
LPSPI_CR_RTF | LPSPI_CR_RRF);
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_CFGR1_OFFSET, regval);
/* Clear all status bits */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_SR_OFFSET, SPI_SR_CLEAR);
/* disable DMA */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_DER_OFFSET, 0);
if (txbuffer)
{
up_clean_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + nbytes);
}
if (rxbuffer)
{
up_invalidate_dcache((uintptr_t)rxbuffer,
(uintptr_t)rxbuffer + nbytes);
}
/* Set up the DMA */
adjust = (priv->nbits > 8) ? 2 : 1;
struct s32k3xx_edma_xfrconfig_s config;
config.saddr = priv->spibase + S32K3XX_LPSPI_RDR_OFFSET;
config.daddr = (uint32_t) (rxbuffer ? rxbuffer : rxdummy);
config.soff = 0;
config.doff = rxbuffer ? adjust : 0;
config.iter = 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;
#ifdef CONFIG_KINETIS_EDMA_ELINK
config.linkch = NULL;
#endif
s32k3xx_dmach_xfrsetup(priv->rxdma, &config);
config.saddr = (uint32_t) (txbuffer ? txbuffer : &txdummy);
config.daddr = priv->spibase + S32K3XX_LPSPI_TDR_OFFSET;
config.soff = txbuffer ? adjust : 0;
config.doff = 0;
config.iter = 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;
#ifdef CONFIG_KINETIS_EDMA_ELINK
config.linkch = NULL;
#endif
s32k3xx_dmach_xfrsetup(priv->txdma, &config);
/* Start the DMAs */
spi_dmarxstart(priv);
spi_dmatxstart(priv);
/* Invoke SPI DMA */
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_DER_OFFSET,
0, LPSPI_DER_TDDE | LPSPI_DER_RDDE);
/* Then wait for each to complete */
ret = spi_dmatxwait(priv);
if (ret < 0)
{
ret = spi_dmarxwait(priv);
}
/* Reset any status */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_SR_OFFSET,
s32k3xx_lpspi_getreg32(priv,
S32K3XX_LPSPI_SR_OFFSET));
/* Disable DMA */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_DER_OFFSET, 0);
}
#endif /* CONFIG_S32K3XX_SPI_DMA */
/****************************************************************************
* Name: s32k3xx_lpspi_sndblock
*
* Description:
* Send a block of data on SPI
*
* Input Parameters:
* dev - Device-specific state data
* txbuffer - A pointer to the buffer of data to be sent
* nwords - the length of data to send from the buffer in number of
* words. The wordsize is determined by the number of
* bits-per-word selected for the SPI interface. If nbits <= 8,
* the data is packed into uint8_t's; if nbits >8, the data is
* packed into uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
#ifndef CONFIG_SPI_EXCHANGE
static void s32k3xx_lpspi_sndblock(struct spi_dev_s *dev,
const void *txbuffer, size_t nwords)
{
spiinfo("txbuffer=%p nwords=%d\n", txbuffer, nwords);
return s32k3xx_lpspi_exchange(dev, txbuffer, NULL, nwords);
}
#endif
/****************************************************************************
* Name: s32k3xx_lpspi_recvblock
*
* Description:
* Receive a block of data from SPI
*
* Input Parameters:
* dev - Device-specific state data
* rxbuffer - A pointer to the buffer in which to receive data
* nwords - the length of data that can be received in the buffer in
* number of words. The wordsize is determined by the number
* of bits-per-word selected for the SPI interface. If
* nbits <= 8, the data is packed into uint8_t's; if nbits >8,
* the data is packed into uint16_t's
*
* Returned Value:
* None
*
****************************************************************************/
#ifndef CONFIG_SPI_EXCHANGE
static void s32k3xx_lpspi_recvblock(struct spi_dev_s *dev, void *rxbuffer,
size_t nwords)
{
spiinfo("rxbuffer=%p nwords=%d\n", rxbuffer, nwords);
return s32k3xx_lpspi_exchange(dev, NULL, rxbuffer, nwords);
}
#endif
/****************************************************************************
* Name: s32k3xx_lpspi_bus_initialize
*
* Description:
* Initialize the selected SPI bus in its default state (Master, 8-bit,
* mode 0, etc.)
*
* Input Parameters:
* priv - private SPI device structure
*
* Returned Value:
* None
*
****************************************************************************/
static void s32k3xx_lpspi_bus_initialize(struct s32k3xx_lpspidev_s *priv)
{
uint32_t reg = 0;
/* NOTE:
* Clocking to the LPSPI peripheral must be provided by board-specific
* logic as part of the clock configuration logic.
*/
/* Reset to known status */
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET, 0, LPSPI_CR_RST);
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET, 0,
LPSPI_CR_RTF | LPSPI_CR_RRF);
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_CR_OFFSET, 0x00);
/* Set LPSPI to master */
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CFGR1_OFFSET, 0,
LPSPI_CFGR1_MASTER);
/* Set specific PCS to active high or low */
/* TODO: Not needed for now */
/* Set Configuration Register 1 related setting. */
reg = s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CFGR1_OFFSET);
reg &= ~(LPSPI_CFGR1_OUTCFG |
LPSPI_CFGR1_PINCFG_MASK | LPSPI_CFGR1_NOSTALL);
reg |= LPSPI_CFGR1_OUTCFG_RETAIN;
reg |= ((priv->pincfg << LPSPI_CFGR1_PINCFG_SHIFT)
& LPSPI_CFGR1_PINCFG_MASK);
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_CFGR1_OFFSET, reg);
/* Set frequency and delay times */
s32k3xx_lpspi_setfrequency((struct spi_dev_s *)priv, 400000);
/* Set default watermarks */
s32k3xx_lpspi_putreg32(priv, S32K3XX_LPSPI_FCR_OFFSET,
LPSPI_FCR_TXWATER(0) | LPSPI_FCR_RXWATER(0));
/* Set Transmit Command Register */
s32k3xx_lpspi_setbits((struct spi_dev_s *)priv, 8);
s32k3xx_lpspi_setmode((struct spi_dev_s *)priv, SPIDEV_MODE0);
/* Enable LPSPI */
s32k3xx_lpspi_modifyreg32(priv, S32K3XX_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN);
}
/****************************************************************************
* Name: spi_dmarxwait
*
* Description:
* Wait for DMA to complete.
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static int spi_dmarxwait(struct s32k3xx_lpspidev_s *priv)
{
int ret;
/* Take the semaphore (perhaps waiting). If the result is zero, then the
* DMA must not really have completed.
*/
do
{
ret = nxsem_wait_uninterruptible(&priv->rxsem);
/* The only expected error is ECANCELED which would occur if the
* calling thread were canceled.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (priv->rxresult == 0 && ret == OK);
return ret;
}
#endif
/****************************************************************************
* Name: spi_dmatxwait
*
* Description:
* Wait for DMA to complete.
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static int spi_dmatxwait(struct s32k3xx_lpspidev_s *priv)
{
int ret;
/* Take the semaphore (perhaps waiting). If the result is zero, then the
* DMA must not really have completed.
*/
do
{
ret = nxsem_wait_uninterruptible(&priv->txsem);
/* The only expected error is ECANCELED which would occur if the
* calling thread were canceled.
*/
DEBUGASSERT(ret == OK || ret == -ECANCELED);
}
while (priv->txresult == 0 && ret == OK);
return ret;
}
#endif
/****************************************************************************
* Name: spi_dmarxwakeup
*
* Description:
* Signal that DMA is complete
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static inline void spi_dmarxwakeup(struct s32k3xx_lpspidev_s *priv)
{
nxsem_post(&priv->rxsem);
}
#endif
/****************************************************************************
* Name: spi_dmatxwakeup
*
* Description:
* Signal that DMA is complete
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static inline void spi_dmatxwakeup(struct s32k3xx_lpspidev_s *priv)
{
nxsem_post(&priv->txsem);
}
#endif
/****************************************************************************
* Name: spi_dmarxcallback
*
* Description:
* Called when the RX DMA completes
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)arg;
priv->rxresult = result | 0x80000000; /* assure non-zero */
spi_dmarxwakeup(priv);
}
#endif
/****************************************************************************
* Name: spi_dmatxcallback
*
* Description:
* Called when the RX DMA completes
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result)
{
struct s32k3xx_lpspidev_s *priv = (struct s32k3xx_lpspidev_s *)arg;
/* Wake-up the SPI driver */
priv->txresult = result | 0x80000000; /* assure non-zero */
spi_dmatxwakeup(priv);
}
#endif
/****************************************************************************
* Name: spi_dmarxstart
*
* Description:
* Start RX DMA
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static inline void spi_dmarxstart(struct s32k3xx_lpspidev_s *priv)
{
priv->rxresult = 0;
s32k3xx_dmach_start(priv->rxdma, spi_dmarxcallback, priv);
}
#endif
/****************************************************************************
* Name: spi_dmatxstart
*
* Description:
* Start TX DMA
*
****************************************************************************/
#ifdef CONFIG_S32K3XX_LPSPI_DMA
static inline void spi_dmatxstart(struct s32k3xx_lpspidev_s *priv)
{
priv->txresult = 0;
s32k3xx_dmach_start(priv->txdma, spi_dmatxcallback, priv);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: s32k3xx_lpspibus_initialize
*
* Description:
* Initialize the selected SPI bus
*
* Input Parameters:
* Port number (for hardware that has multiple SPI interfaces)
*
* Returned Value:
* Valid SPI device structure reference on success; a NULL on failure
*
****************************************************************************/
struct spi_dev_s *s32k3xx_lpspibus_initialize(int bus)
{
struct s32k3xx_lpspidev_s *priv = NULL;
irqstate_t flags = enter_critical_section();
#ifdef CONFIG_S32K3XX_LPSPI0
if (bus == 0)
{
/* Select SPI0 */
priv = &g_lpspi0dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI0 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI0_SCK);
s32k3xx_pinconfig(PIN_LPSPI0_MISO);
s32k3xx_pinconfig(PIN_LPSPI0_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
#ifdef CONFIG_S32K3XX_LPSPI1
if (bus == 1)
{
/* Select SPI1 */
priv = &g_lpspi1dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI1 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI1_SCK);
s32k3xx_pinconfig(PIN_LPSPI1_MISO);
s32k3xx_pinconfig(PIN_LPSPI1_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
#ifdef CONFIG_S32K3XX_LPSPI2
if (bus == 2)
{
/* Select SPI2 */
priv = &g_lpspi2dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI2 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI2_SCK);
s32k3xx_pinconfig(PIN_LPSPI2_MISO);
s32k3xx_pinconfig(PIN_LPSPI2_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
#ifdef CONFIG_S32K3XX_LPSPI3
if (bus == 3)
{
/* Select SPI3 */
priv = &g_lpspi3dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI2 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI3_SCK);
s32k3xx_pinconfig(PIN_LPSPI3_MISO);
s32k3xx_pinconfig(PIN_LPSPI3_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
#ifdef CONFIG_S32K3XX_LPSPI4
if (bus == 4)
{
/* Select SPI3 */
priv = &g_lpspi4dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI2 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI4_SCK);
s32k3xx_pinconfig(PIN_LPSPI4_MISO);
s32k3xx_pinconfig(PIN_LPSPI4_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
#ifdef CONFIG_S32K3XX_LPSPI5
if (bus == 5)
{
/* Select SPI3 */
priv = &g_lpspi5dev;
/* Only configure if the bus is not already configured */
if ((s32k3xx_lpspi_getreg32(priv, S32K3XX_LPSPI_CR_OFFSET) &
LPSPI_CR_MEN) == 0)
{
/* Configure SPI2 pins: SCK, MISO, and MOSI */
s32k3xx_pinconfig(PIN_LPSPI5_SCK);
s32k3xx_pinconfig(PIN_LPSPI5_MISO);
s32k3xx_pinconfig(PIN_LPSPI5_MOSI);
/* Set up default configuration: Master, 8-bit, etc. */
s32k3xx_lpspi_bus_initialize(priv);
}
}
else
#endif
{
spierr("ERROR: Unsupported SPI bus: %d\n", bus);
}
#ifdef CONFIG_S32K3XX_LPSPI_DMA
if (priv->rxch && priv->txch)
{
if (priv->txdma == NULL && priv->rxdma == NULL)
{
priv->txdma = s32k3xx_dmach_alloc(priv->txch | DMAMUX_CHCFG_ENBL,
0);
priv->rxdma = s32k3xx_dmach_alloc(priv->rxch | DMAMUX_CHCFG_ENBL,
0);
DEBUGASSERT(priv->rxdma && priv->txdma);
}
}
else
{
priv->rxdma = NULL;
priv->txdma = NULL;
}
#endif
leave_critical_section(flags);
return (struct spi_dev_s *)priv;
}
#endif /* CONFIG_S32K3XX_LPSPI0 || CONFIG_S32K3XX_LPSPI1 || CONFIG_S32K3XX_LPSPI2 */