nuttx/arch/arm/src/nrf52/nrf52_spi.c
Xiang Xiao 2e54df0f35 Don't include assert.h from public header file
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2021-06-03 08:36:03 -07:00

1494 lines
39 KiB
C

/****************************************************************************
* arch/arm/src/nrf52/nrf52_spi.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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <inttypes.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/semaphore.h>
#include <arch/board/board.h>
#include <nuttx/power/pm.h>
#include "arm_arch.h"
#include "barriers.h"
#include "nrf52_gpio.h"
#include "nrf52_spi.h"
#include "hardware/nrf52_spi.h"
#ifdef CONFIG_NRF52_SPI_MASTER_WORKAROUND_1BYTE_TRANSFER
# include "hardware/nrf52_gpiote.h"
# include "nrf52_ppi.h"
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* I2C0/SPI0 and I2C1/SPI1 share the same peripherals */
#if defined(CONFIG_NRF52_I2C0_MASTER) && defined(CONFIG_NRF52_SPI0_MASTER)
# error Unsupported configuration I2C0 + SPI0
#endif
#if defined(CONFIG_NRF52_I2C1_MASTER) && defined(CONFIG_NRF52_SPI1_MASTER)
# error Unsupported configuration I2C1 + SPI1
#endif
/* Reserve PPI channel and GPIOTE channel for 1 byte transfer workaround */
#ifdef CONFIG_NRF52_SPI_MASTER_WORKAROUND_1BYTE_TRANSFER
# define SPI_1B_WORKAROUND_PPI_CHAN (NRF52_PPI_NUM_CONFIGURABLE_CHANNELS - 1)
# define SPI_1B_WORKAROUND_GPIOTE_CHAN (7)
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct nrf52_spidev_s
{
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
uint32_t base; /* Base address of SPI register */
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
uint32_t irq; /* SPI IRQ number */
#endif
nrf52_pinset_t sck_pin; /* SCK pin configuration */
uint32_t frequency; /* Requested clock frequency */
uint8_t mode; /* Mode 0,1,2,3 */
sem_t exclsem; /* Held while chip is selected for mutual
* exclusion
*/
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
sem_t sem_isr; /* Interrupt wait semaphore */
#endif
bool initialized;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline void nrf52_spi_putreg(FAR struct nrf52_spidev_s *priv,
uint32_t offset,
uint32_t value);
static inline uint32_t nrf52_spi_getreg(FAR struct nrf52_spidev_s *priv,
uint32_t offset);
/* SPI methods */
static int nrf52_spi_lock(FAR struct spi_dev_s *dev, bool lock);
static uint32_t nrf52_spi_setfrequency(FAR struct spi_dev_s *dev,
uint32_t frequency);
static void nrf52_spi_setmode(FAR struct spi_dev_s *priv,
enum spi_mode_e mode);
static void nrf52_spi_setbits(FAR struct spi_dev_s *priv, int nbits);
#ifdef CONFIG_SPI_HWFEATURES
static int nrf52_spi_hwfeatures(FAR struct spi_dev_s *dev,
spi_hwfeatures_t features);
#endif
static uint32_t nrf52_spi_send(FAR struct spi_dev_s *dev, uint32_t wd);
static void nrf52_spi_exchange(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords);
#ifndef CONFIG_SPI_EXCHANGE
static void nrf52_spi_sndblock(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
size_t nwords);
static void nrf52_spi_recvblock(FAR struct spi_dev_s *dev,
FAR void *rxbuffer,
size_t nwords);
#endif
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
static int nrf52_spi_isr(int irq, FAR void *context, FAR void *arg);
#endif
/* Initialization */
static int nrf52_spi_init(FAR struct nrf52_spidev_s *priv);
static void nrf52_spi_pselinit(FAR struct nrf52_spidev_s *priv,
uint32_t offset, nrf52_pinset_t pinset);
static void nrf52_spi_gpioinit(FAR struct nrf52_spidev_s *priv);
#ifdef CONFIG_PM
static int nrf52_spi_deinit(FAR struct nrf52_spidev_s *priv);
static void nrf52_spi_gpiodeinit(FAR struct nrf52_spidev_s *priv);
static int nrf52_spi_pm_prepare(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate);
static void nrf52_spi_pm_notify(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_PM
struct pm_callback_s g_pm_callbacks =
{
.prepare = nrf52_spi_pm_prepare,
.notify = nrf52_spi_pm_notify
};
#endif
/* SPI0 */
#ifdef CONFIG_NRF52_SPI0_MASTER
static const struct spi_ops_s g_spi0ops =
{
.lock = nrf52_spi_lock,
.select = nrf52_spi0select,
.setfrequency = nrf52_spi_setfrequency,
.setmode = nrf52_spi_setmode,
.setbits = nrf52_spi_setbits,
# ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = nrf52_spi_hwfeatures,
# endif
.status = nrf52_spi0status,
# ifdef CONFIG_SPI_CMDDATA
.cmddata = nrf52_spi0cmddata,
# endif
.send = nrf52_spi_send,
# ifdef CONFIG_SPI_EXCHANGE
.exchange = nrf52_spi_exchange,
# else
.sndblock = nrf52_spi_sndblock,
.recvblock = nrf52_spi_recvblock,
# endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = nrf52_spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = nrf52_spi0register, /* Provided externally */
#else
.registercallback = NULL, /* Not implemented */
#endif
};
static struct nrf52_spidev_s g_spi0dev =
{
.spidev =
{
&g_spi0ops
},
.base = NRF52_SPIM0_BASE,
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
.irq = NRF52_IRQ_SPI_TWI_0,
#endif
.sck_pin = BOARD_SPI0_SCK_PIN,
.frequency = 0,
.mode = 0
};
#endif
/* SPI1 */
#ifdef CONFIG_NRF52_SPI1_MASTER
static const struct spi_ops_s g_spi1ops =
{
.lock = nrf52_spi_lock,
.select = nrf52_spi1select,
.setfrequency = nrf52_spi_setfrequency,
.setmode = nrf52_spi_setmode,
.setbits = nrf52_spi_setbits,
# ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = nrf52_spi_hwfeatures,
# endif
.status = nrf52_spi1status,
# ifdef CONFIG_SPI_CMDDATA
.cmddata = nrf52_spi1cmddata,
# endif
.send = nrf52_spi_send,
# ifdef CONFIG_SPI_EXCHANGE
.exchange = nrf52_spi_exchange,
# else
.sndlock = nrf52_spi_sndblock,
.recvblock = nrf52_spi_recvblock,
# endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = nrf52_spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = nrf52_spi1register, /* Provided externally */
#else
.registercallback = NULL, /* Not implemented */
#endif
};
static struct nrf52_spidev_s g_spi1dev =
{
.spidev =
{
&g_spi1ops
},
.base = NRF52_SPIM1_BASE,
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
.irq = NRF52_IRQ_SPI_TWI_1,
#endif
.sck_pin = BOARD_SPI1_SCK_PIN,
.frequency = 0,
.mode = 0
};
#endif
/* SPI2 */
#ifdef CONFIG_NRF52_SPI2_MASTER
static const struct spi_ops_s g_spi2ops =
{
.lock = nrf52_spi_lock,
.select = nrf52_spi2select,
.setfrequency = nrf52_spi_setfrequency,
.setmode = nrf52_spi_setmode,
.setbits = nrf52_spi_setbits,
# ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = nrf52_spi_hwfeatures,
# endif
.status = nrf52_spi2status,
# ifdef CONFIG_SPI_CMDDATA
.cmddata = nrf52_spi2cmddata,
# endif
.send = nrf52_spi_send,
# ifdef CONFIG_SPI_EXCHANGE
.exchange = nrf52_spi_exchange,
# else
.sndlock = nrf52_spi_sndblock,
.recvblock = nrf52_spi_recvblock,
# endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = nrf52_spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = nrf52_spi2register, /* Provided externally */
#else
.registercallback = NULL, /* Not implemented */
#endif
};
static struct nrf52_spidev_s g_spi2dev =
{
.spidev =
{
&g_spi2ops
},
.base = NRF52_SPIM2_BASE,
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
.irq = NRF52_IRQ_SPI2,
#endif
.sck_pin = BOARD_SPI2_SCK_PIN,
.frequency = 0,
.mode = 0
};
#endif
/* SPI3 */
#ifdef CONFIG_NRF52_SPI3_MASTER
static const struct spi_ops_s g_spi3ops =
{
.lock = nrf52_spi_lock,
.select = nrf52_spi3select,
.setfrequency = nrf52_spi_setfrequency,
.setmode = nrf52_spi_setmode,
.setbits = nrf52_spi_setbits,
# ifdef CONFIG_SPI_HWFEATURES
.hwfeatures = nrf52_spi_hwfeatures,
# endif
.status = nrf52_spi3status,
# ifdef CONFIG_SPI_CMDDATA
.cmddata = nrf52_spi3cmddata,
# endif
.send = nrf52_spi_send,
# ifdef CONFIG_SPI_EXCHANGE
.exchange = nrf52_spi_exchange,
# else
.sndlock = nrf52_spi_sndblock,
.recvblock = nrf52_spi_recvblock,
# endif
#ifdef CONFIG_SPI_TRIGGER
.trigger = nrf52_spi_trigger,
#endif
#ifdef CONFIG_SPI_CALLBACK
.registercallback = nrf52_spi3register, /* Provided externally */
#else
.registercallback = NULL, /* Not implemented */
#endif
};
static struct nrf52_spidev_s g_spi3dev =
{
.spidev =
{
&g_spi3ops
},
.base = NRF52_SPIM3_BASE,
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
.irq = NRF52_IRQ_SPI3,
#endif
.sck_pin = BOARD_SPI3_SCK_PIN,
.frequency = 0,
.mode = 0
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nrf52_spi_putreg
*
* Description:
* Put a 32-bit register value by offset
*
****************************************************************************/
static inline void nrf52_spi_putreg(FAR struct nrf52_spidev_s *priv,
uint32_t offset,
uint32_t value)
{
putreg32(value, priv->base + offset);
}
/****************************************************************************
* Name: nrf52_spi_getreg
*
* Description:
* Get a 32-bit register value by offset
*
****************************************************************************/
static inline uint32_t nrf52_spi_getreg(FAR struct nrf52_spidev_s *priv,
uint32_t offset)
{
return getreg32(priv->base + offset);
}
/****************************************************************************
* Name: nrf52_spi_isr
*
* Description:
* Common SPI interrupt service routine
*
****************************************************************************/
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
static int nrf52_spi_isr(int irq, FAR void *context, FAR void *arg)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)arg;
/* Get interrupt event */
if (nrf52_spi_getreg(priv, NRF52_SPIM_EVENTS_END_OFFSET) == 1)
{
/* Transfer is complete */
nxsem_post(&priv->sem_isr);
/* Clear event */
nrf52_spi_putreg(priv, NRF52_SPIM_EVENTS_END_OFFSET, 0);
}
return OK;
}
#endif
/****************************************************************************
* Name: nrf52_spi_init
*
* Description:
* Configure SPI
*
****************************************************************************/
static int nrf52_spi_init(FAR struct nrf52_spidev_s *priv)
{
/* Disable SPI */
nrf52_spi_putreg(priv, NRF52_SPIM_ENABLE_OFFSET, SPIM_ENABLE_DIS);
/* Configure SPI pins */
nrf52_spi_gpioinit(priv);
/* NOTE: Chip select pin must be configured by board-specific logic */
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
/* Enable interrupts for RX and TX done */
nrf52_spi_putreg(priv, NRF52_SPIM_INTENSET_OFFSET, SPIM_INT_END);
#endif
/* Enable SPI */
nrf52_spi_putreg(priv, NRF52_SPIM_ENABLE_OFFSET, SPIM_ENABLE_EN);
return OK;
}
#ifdef CONFIG_PM
/****************************************************************************
* Name: nrf52_spi_deinit
*
* Description:
* Configure SPI
*
****************************************************************************/
static int nrf52_spi_deinit(FAR struct nrf52_spidev_s *priv)
{
/* Disable SPI */
nrf52_spi_putreg(priv, NRF52_SPIM_ENABLE_OFFSET, SPIM_ENABLE_DIS);
#ifdef CONFIG_ARCH_CHIP_NRF52832
/* Apply workaround for errata 89 (replace dummy read by barrier to avoid
* compiler optimizing it away)
*/
nrf52_spi_putreg(priv, NRF52_SPIM_POWER_OFFSET, 0);
ARM_DSB();
ARM_ISB();
nrf52_spi_putreg(priv, NRF52_SPIM_POWER_OFFSET, 1);
#endif
/* Unconfigure SPI pins */
nrf52_spi_gpiodeinit(priv);
return OK;
}
#endif
/****************************************************************************
* Name: nrf52_spi_pselinit
*
* Description:
* Configure PSEL for SPI devices
*
****************************************************************************/
static void nrf52_spi_pselinit(FAR struct nrf52_spidev_s *priv,
uint32_t offset, nrf52_pinset_t pinset)
{
uint32_t regval;
int pin = GPIO_PIN_DECODE(pinset);
int port = GPIO_PORT_DECODE(pinset);
regval = (pin << SPIM_PSEL_PIN_SHIFT);
regval |= (port << SPIM_PSEL_PORT_SHIFT);
nrf52_spi_putreg(priv, offset, regval);
}
/****************************************************************************
* Name: nrf52_spi_gpioinit
*
* Description:
* Configure GPIO for SPI pins
*
****************************************************************************/
static void nrf52_spi_gpioinit(FAR struct nrf52_spidev_s *priv)
{
nrf52_gpio_config(priv->sck_pin);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELSCK_OFFSET, priv->sck_pin);
#ifdef CONFIG_NRF52_SPI0_MASTER
if (priv == &g_spi0dev)
{
#ifdef BOARD_SPI0_MISO_PIN
nrf52_gpio_config(BOARD_SPI0_MISO_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMISO_OFFSET,
BOARD_SPI0_MISO_PIN);
#endif
#ifdef BOARD_SPI0_MOSI_PIN
nrf52_gpio_config(BOARD_SPI0_MOSI_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMOSI_OFFSET,
BOARD_SPI0_MOSI_PIN);
nrf52_gpio_write(BOARD_SPI0_MOSI_PIN, false);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
if (priv == &g_spi1dev)
{
#ifdef BOARD_SPI1_MISO_PIN
nrf52_gpio_config(BOARD_SPI1_MISO_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMISO_OFFSET,
BOARD_SPI1_MISO_PIN);
#endif
#ifdef BOARD_SPI1_MOSI_PIN
nrf52_gpio_config(BOARD_SPI1_MOSI_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMOSI_OFFSET,
BOARD_SPI1_MOSI_PIN);
nrf52_gpio_write(BOARD_SPI1_MOSI_PIN, false);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
if (priv == &g_spi2dev)
{
#ifdef BOARD_SPI2_MISO_PIN
nrf52_gpio_config(BOARD_SPI2_MISO_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMISO_OFFSET,
BOARD_SPI2_MISO_PIN);
#endif
#ifdef BOARD_SPI2_MOSI_PIN
nrf52_gpio_config(BOARD_SPI2_MOSI_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMOSI_OFFSET,
BOARD_SPI2_MOSI_PIN);
nrf52_gpio_write(BOARD_SPI2_MOSI_PIN, false);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
if (priv == &g_spi3dev)
{
#ifdef BOARD_SPI3_MISO_PIN
nrf52_gpio_config(BOARD_SPI3_MISO_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMISO_OFFSET,
BOARD_SPI3_MISO_PIN);
#endif
#ifdef BOARD_SPI3_MOSI_PIN
nrf52_gpio_config(BOARD_SPI3_MOSI_PIN);
nrf52_spi_pselinit(priv, NRF52_SPIM_PSELMOSI_OFFSET,
BOARD_SPI3_MOSI_PIN);
nrf52_gpio_write(BOARD_SPI3_MOSI_PIN, false);
#endif
}
#endif
}
#ifdef CONFIG_PM
/****************************************************************************
* Name: nrf52_spi_gpioinit
*
* Description:
* Configure GPIO for SPI pins
*
****************************************************************************/
static void nrf52_spi_gpiodeinit(FAR struct nrf52_spidev_s *priv)
{
nrf52_gpio_unconfig(priv->sck_pin);
#ifdef CONFIG_NRF52_SPI0_MASTER
if (priv == &g_spi0dev)
{
#ifdef BOARD_SPI0_MISO_PIN
nrf52_gpio_unconfig(BOARD_SPI0_MISO_PIN);
#endif
#ifdef BOARD_SPI0_MOSI_PIN
nrf52_gpio_unconfig(BOARD_SPI0_MOSI_PIN);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
if (priv == &g_spi1dev)
{
#ifdef BOARD_SPI1_MISO_PIN
nrf52_gpio_unconfig(BOARD_SPI1_MISO_PIN);
#endif
#ifdef BOARD_SPI1_MOSI_PIN
nrf52_gpio_unconfig(BOARD_SPI1_MOSI_PIN);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
if (priv == &g_spi2dev)
{
#ifdef BOARD_SPI2_MISO_PIN
nrf52_gpio_unconfig(BOARD_SPI2_MISO_PIN);
#endif
#ifdef BOARD_SPI2_MOSI_PIN
nrf52_gpio_unconfig(BOARD_SPI2_MOSI_PIN);
#endif
}
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
if (priv == &g_spi3dev)
{
#ifdef BOARD_SPI3_MISO_PIN
nrf52_gpio_unconfig(BOARD_SPI3_MISO_PIN);
#endif
#ifdef BOARD_SPI3_MOSI_PIN
nrf52_gpio_unconfig(BOARD_SPI3_MOSI_PIN);
#endif
}
#endif
}
#endif
/****************************************************************************
* Name: nrf52_spi_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 nrf52_spi_lock(FAR struct spi_dev_s *dev, bool lock)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
int ret = OK;
if (lock)
{
ret = nxsem_wait_uninterruptible(&priv->exclsem);
}
else
{
ret = nxsem_post(&priv->exclsem);
}
return ret;
}
/****************************************************************************
* Name: nrf52_spi_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 nrf52_spi_setfrequency(FAR struct spi_dev_s *dev,
uint32_t frequency)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
uint32_t regval = 0;
if (priv->frequency == frequency)
{
/* We are already at this frequency */
return priv->frequency;
}
/* Frequencies are hardcoded */
switch (frequency)
{
case 125000:
{
regval = SPIM_FREQUENCY_125KBPS;
break;
}
case 250000:
{
regval = SPIM_FREQUENCY_250KBPS;
break;
}
case 500000:
{
regval = SPIM_FREQUENCY_500KBPS;
break;
}
case 1000000:
{
regval = SPIM_FREQUENCY_1MBPS;
break;
}
case 2000000:
{
regval = SPIM_FREQUENCY_2MBPS;
break;
}
case 4000000:
{
regval = SPIM_FREQUENCY_4MBPS;
break;
}
case 8000000:
{
regval = SPIM_FREQUENCY_8MBPS;
break;
}
default:
{
spierr("Frequency unsupported %" PRId32 "\n", frequency);
goto errout;
}
}
/* Write register */
nrf52_spi_putreg(priv, NRF52_SPIM_FREQUENCY_OFFSET, regval);
/* Save the frequency setting */
priv->frequency = frequency;
spiinfo("Frequency %" PRId32 "\n", frequency);
errout:
return priv->frequency;
}
/****************************************************************************
* Name: nrf52_spi_setmode
*
* Description:
* Set the SPI mode. see enum spi_mode_e for mode definitions
*
* Input Parameters:
* dev - Device-specific state data
* mode - The SPI mode requested
*
* Returned Value:
* Returns the actual frequency selected
*
****************************************************************************/
static void nrf52_spi_setmode(FAR struct spi_dev_s *dev,
enum spi_mode_e mode)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
uint32_t regval = 0;
spiinfo("mode=%d\n", mode);
/* Has the mode changed? */
if (mode != priv->mode)
{
regval = nrf52_spi_getreg(priv, NRF52_SPIM_CONFIG_OFFSET);
regval &= ~(SPIM_CONFIG_CPHA | SPIM_CONFIG_CPOL);
switch (mode)
{
case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */
{
break;
}
case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */
{
regval |= SPIM_CONFIG_CPHA;
break;
}
case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */
{
regval |= SPIM_CONFIG_CPOL;
break;
}
case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */
{
regval |= SPIM_CONFIG_CPHA;
regval |= SPIM_CONFIG_CPOL;
break;
}
default:
{
DEBUGASSERT(0);
return;
}
}
nrf52_spi_putreg(priv, NRF52_SPIM_CONFIG_OFFSET, regval);
/* According to manual we have to set SCK pin output
* value the same as CPOL value
*/
if (mode == SPIDEV_MODE2 || mode == SPIDEV_MODE3)
{
nrf52_gpio_write(priv->sck_pin, true);
}
else
{
nrf52_gpio_write(priv->sck_pin, false);
}
priv->mode = mode;
}
}
/****************************************************************************
* Name: nrf52_spi_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 nrf52_spi_setbits(FAR struct spi_dev_s *dev, int nbits)
{
if (nbits != 8)
{
spierr("ERROR: nbits not supported: %d\n", nbits);
}
return;
}
/****************************************************************************
* Name: nrf52_spi_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 nrf52_spi_hwfeatures(FAR struct spi_dev_s *dev,
spi_hwfeatures_t features)
{
#ifdef CONFIG_SPI_BITORDER
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
uint32_t setbits = 0;
uint32_t clrbits = 0;
uint32_t regval;
spiinfo("features=%08x\n", features);
/* Transfer data LSB first? */
if ((features & HWFEAT_LSBFIRST) != 0)
{
setbits = SPIM_CONFIG_ORDER;
clrbits = 0;
}
else
{
setbits = 0;
clrbits = SPIM_CONFIG_ORDER;
}
regval = nrf52_spi_getreg(priv, NRF52_SPIM_CONFIG_OFFSET);
regval &= ~clrbits;
regval |= setbits;
nrf52_spi_putreg(priv, NRF52_SPIM_CONFIG_OFFSET, regval);
#endif
/* Other H/W features are not supported */
return ((features & ~HWFEAT_LSBFIRST) == 0) ? OK : -ENOSYS;
}
#endif
/****************************************************************************
* Name: n4f52_spi_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 nrf52_spi_send(FAR struct spi_dev_s *dev, uint32_t wd)
{
uint32_t ret = 0;
/* Exchange one word on SPI */
nrf52_spi_exchange(dev, &wd, &ret, 1);
return ret;
}
#ifdef CONFIG_NRF52_SPI_MASTER_WORKAROUND_1BYTE_TRANSFER
/****************************************************************************
* Name: n4f52_spi_1b_workaround
*
* Description:
* Workaround to fix SPI Master 1 byte transfer for NRF52832.
*
* Input Parameters:
* dev - Device-specific state data
* enable - Enable/disable workaround
*
* Returned Value:
* None
*
****************************************************************************/
static void nrf52_spi_1b_workaround(FAR struct spi_dev_s *dev, bool enable)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
uint32_t pin = 0;
uint32_t port = 0;
pin = (priv->sck_pin & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
port = (priv->sck_pin & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
if (enable == true)
{
/* Create an event when SCK toggles */
putreg32((GPIOTE_CONFIG_MODE_EV |
pin << GPIOTE_CONFIG_PSEL_SHIFT |
port << GPIOTE_CONFIG_PORT_SHIFT |
GPIOTE_CONFIG_POL_TG),
NRF52_GPIOTE_CONFIG(SPI_1B_WORKAROUND_GPIOTE_CHAN));
/* Stop the SPIM instance when SCK toggles */
nrf52_ppi_set_event_ep(SPI_1B_WORKAROUND_PPI_CHAN,
NRF52_GPIOTE_EVENTS_IN(
SPI_1B_WORKAROUND_GPIOTE_CHAN));
nrf52_ppi_set_task_ep(SPI_1B_WORKAROUND_PPI_CHAN,
priv->base + NRF52_SPIM_TASK_STOP_OFFSET);
/* Enable PPI channel */
nrf52_ppi_channel_enable(SPI_1B_WORKAROUND_PPI_CHAN, true);
}
else
{
/* Disable event */
putreg32(0, NRF52_GPIOTE_CONFIG(SPI_1B_WORKAROUND_GPIOTE_CHAN));
nrf52_ppi_set_event_ep(SPI_1B_WORKAROUND_PPI_CHAN, 0);
nrf52_ppi_set_task_ep(SPI_1B_WORKAROUND_PPI_CHAN, 0);
/* Disable PPI channel */
nrf52_ppi_channel_enable(SPI_1B_WORKAROUND_PPI_CHAN, false);
}
}
#endif
/****************************************************************************
* Name: nrf52_spi_exchange
*
* 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.
*
* Returned Value:
* None
*
****************************************************************************/
static void nrf52_spi_exchange(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
FAR void *rxbuffer, size_t nwords)
{
FAR struct nrf52_spidev_s *priv = (FAR struct nrf52_spidev_s *)dev;
uint32_t regval = 0;
size_t nwords_left = nwords;
#ifdef CONFIG_NRF52_SPI_MASTER_WORKAROUND_1BYTE_TRANSFER
if (nwords <= 1)
{
nrf52_spi_1b_workaround(dev, true);
}
#endif
if (rxbuffer != NULL)
{
/* Write RXD data pointer */
regval = (uint32_t)rxbuffer;
nrf52_spi_putreg(priv, NRF52_SPIM_RXDPTR_OFFSET, regval);
}
else
{
nrf52_spi_putreg(priv, NRF52_SPIM_RXDMAXCNT_OFFSET, 0);
}
if (txbuffer != NULL)
{
/* Write TXD data pointer */
regval = (uint32_t)txbuffer;
nrf52_spi_putreg(priv, NRF52_SPIM_TXDPTR_OFFSET, regval);
}
else
{
nrf52_spi_putreg(priv, NRF52_SPIM_TXDMAXCNT_OFFSET, 0);
}
/* If more than 255 bytes, enable list mode to send data
* in batches
*/
if (nwords > 0xff)
{
if (rxbuffer != NULL)
{
nrf52_spi_putreg(priv, NRF52_SPIM_RXDLIST_OFFSET, 1);
}
if (txbuffer != NULL)
{
nrf52_spi_putreg(priv, NRF52_SPIM_TXDLIST_OFFSET, 1);
}
}
while (nwords_left > 0)
{
size_t transfer_size = (nwords_left > 255 ? 255 : nwords_left);
if (rxbuffer != NULL)
{
/* Write number of bytes in RXD buffer */
nrf52_spi_putreg(priv, NRF52_SPIM_RXDMAXCNT_OFFSET, transfer_size);
}
if (txbuffer != NULL)
{
/* Write number of bytes in TXD buffer */
nrf52_spi_putreg(priv, NRF52_SPIM_TXDMAXCNT_OFFSET, transfer_size);
}
/* SPI start */
nrf52_spi_putreg(priv, NRF52_SPIM_TASK_START_OFFSET, SPIM_TASKS_START);
#ifndef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
/* Wait for RX done and TX done */
while (nrf52_spi_getreg(priv, NRF52_SPIM_EVENTS_END_OFFSET) != 1);
/* Clear event */
nrf52_spi_putreg(priv, NRF52_SPIM_EVENTS_END_OFFSET, 0);
#else
/* Wait for transfer complete */
nxsem_wait_uninterruptible(&priv->sem_isr);
#endif
if (nrf52_spi_getreg(priv, NRF52_SPIM_TXDAMOUNT_OFFSET) !=
transfer_size)
{
spierr("Incomplete transfer wrote %" PRId32 " expected %zu\n",
regval, nwords);
}
/* SPI stop */
nrf52_spi_putreg(priv, NRF52_SPIM_TASK_STOP_OFFSET, SPIM_TASKS_STOP);
/* Wait for STOP event */
while (nrf52_spi_getreg(priv, NRF52_SPIM_EVENTS_STOPPED_OFFSET) != 1);
/* Clear event */
nrf52_spi_putreg(priv, NRF52_SPIM_EVENTS_STOPPED_OFFSET, 0);
nwords_left -= transfer_size;
}
/* Clear RX/TX DMA after transfer */
nrf52_spi_putreg(priv, NRF52_SPIM_RXDPTR_OFFSET, 0);
nrf52_spi_putreg(priv, NRF52_SPIM_RXDMAXCNT_OFFSET, 0);
nrf52_spi_putreg(priv, NRF52_SPIM_TXDPTR_OFFSET, 0);
nrf52_spi_putreg(priv, NRF52_SPIM_TXDMAXCNT_OFFSET, 0);
/* Clear list mode */
if (nwords > 0xff)
{
nrf52_spi_putreg(priv, NRF52_SPIM_RXDLIST_OFFSET, 0);
nrf52_spi_putreg(priv, NRF52_SPIM_TXDLIST_OFFSET, 0);
}
#ifdef CONFIG_NRF52_SPI_MASTER_WORKAROUND_1BYTE_TRANSFER
if (nwords <= 1)
{
nrf52_spi_1b_workaround(dev, false);
}
#endif
}
#ifndef CONFIG_SPI_EXCHANGE
/****************************************************************************
* Name: nrf52_spi_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.
*
* Returned Value:
* None
*
****************************************************************************/
static void nrf52_spi_sndblock(FAR struct spi_dev_s *dev,
FAR const void *txbuffer,
size_t nwords)
{
spiinfo("txbuffer=%p nwords=%zu\n", txbuffer, nwords);
return nrf52_spi_exchange(dev, txbuffer, NULL, nwords);
}
/****************************************************************************
* Name: nrf52_spi_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.
*
* Returned Value:
* None
*
****************************************************************************/
static void nrf52_spi_recvblock(FAR struct spi_dev_s *dev,
FAR void *rxbuffer,
size_t nwords)
{
spiinfo("txbuffer=%p nwords=%zu\n", rxbuffer, nwords);
return nrf52_spi_exchange(dev, NULL, rxbuffer, nwords);
}
#endif /* CONFIG_SPI_EXCHANGE */
/****************************************************************************
* Name: nrf52_spi_trigger
*
* Description:
* Trigger a previously configured DMA transfer.
*
* Input Parameters:
* dev - Device-specific state data
*
* Returned Value:
* OK - Trigger was fired
* -ENOSYS - Trigger not fired due to lack of DMA or low level support
* -EIO - Trigger not fired because not previously primed
*
****************************************************************************/
#ifdef CONFIG_SPI_TRIGGER
static int nrf52_spi_trigger(FAR struct spi_dev_s *dev)
{
return -ENOSYS;
}
#endif
#ifdef CONFIG_PM
/****************************************************************************
* Name: nrf52_spi_pm_prepare
****************************************************************************/
static int nrf52_spi_pm_prepare(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate)
{
if (pmstate == PM_STANDBY || pmstate == PM_SLEEP)
{
bool active = false;
#ifdef CONFIG_NRF52_SPI0_MASTER
active |= nrf52_spi_getreg(&g_spi0dev, SPIM_EVENTS_STARTED);
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
active |= nrf52_spi_getreg(&g_spi1dev, SPIM_EVENTS_STARTED);
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
active |= nrf52_spi_getreg(&g_spi2dev, SPIM_EVENTS_STARTED);
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
active |= nrf52_spi_getreg(&g_spi3dev, SPIM_EVENTS_STARTED);
#endif
if (active)
{
/* SPI is being used, cannot disable */
return -1;
}
else
{
/* SPI is inactive, can go to sleep */
return 0;
}
}
else
{
/* We can always go to any other state */
return 0;
}
}
/****************************************************************************
* Name: nrf52_spi_pm_notify
****************************************************************************/
static void nrf52_spi_pm_notify(FAR struct pm_callback_s *cb, int domain,
enum pm_state_e pmstate)
{
if (pmstate == PM_SLEEP || pmstate == PM_STANDBY)
{
/* Deinit SPI peripheral on each initialized device */
#ifdef CONFIG_NRF52_SPI0_MASTER
if (g_spi0dev.initialized)
{
nrf52_spi_deinit(&g_spi0dev);
}
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
if (g_spi1dev.initialized)
{
nrf52_spi_deinit(&g_spi1dev);
}
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
if (g_spi2dev.initialized)
{
nrf52_spi_deinit(&g_spi2dev);
}
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
if (g_spi3dev.initialized)
{
nrf52_spi_deinit(&g_spi3dev);
}
#endif
}
else
{
/* Reinit SPI peripheral on each initialized device */
#ifdef CONFIG_NRF52_SPI0_MASTER
if (g_spi0dev.initialized)
{
nrf52_spi_init(&g_spi0dev);
}
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
if (g_spi1dev.initialized)
{
nrf52_spi_init(&g_spi1dev);
}
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
if (g_spi2dev.initialized)
{
nrf52_spi_init(&g_spi2dev);
}
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
if (g_spi3dev.initialized)
{
nrf52_spi_init(&g_spi3dev);
}
#endif
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nrf52_spibus_initialize
*
* Description:
* Initialize the selected SPI port.
*
* Input Parameters:
* Port number (for hardware that has multiple SPI interfaces)
*
* Returned Value:
* Valid SPI device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct spi_dev_s *nrf52_spibus_initialize(int port)
{
FAR struct nrf52_spidev_s *priv = NULL;
/* Get SPI driver data */
switch (port)
{
#ifdef CONFIG_NRF52_SPI0_MASTER
case 0:
{
priv = &g_spi0dev;
break;
}
#endif
#ifdef CONFIG_NRF52_SPI1_MASTER
case 1:
{
priv = &g_spi1dev;
break;
}
#endif
#ifdef CONFIG_NRF52_SPI2_MASTER
case 2:
{
priv = &g_spi2dev;
break;
}
#endif
#ifdef CONFIG_NRF52_SPI3_MASTER
case 3:
{
priv = &g_spi3dev;
break;
}
#endif
default:
{
goto errout;
}
}
/* Initialize the SPI */
nrf52_spi_init(priv);
/* Mark device as initialized */
priv->initialized = true;
/* Initialize the SPI semaphore */
nxsem_init(&priv->exclsem, 0, 1);
#ifdef CONFIG_NRF52_SPI_MASTER_INTERRUPTS
/* This semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_init(&priv->sem_isr, 0, 0);
nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE);
/* Attach SPI interrupt */
irq_attach(priv->irq, nrf52_spi_isr, priv);
up_enable_irq(priv->irq);
#endif
errout:
return (FAR struct spi_dev_s *)priv;
}