2332 lines
66 KiB
C
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 */
|