zephyr/drivers/spi/spim_nrf52.c

449 lines
11 KiB
C

/*
* Copyright (c) 2017 Intel Corp.
*
* SPDX-License-Identifier:0xApache-2.0
*/
/*
* #define CONFIG_ASSERT
* #define __ASSERT_ON 1
*/
#include <errno.h>
#include <spi.h>
#include <soc.h>
#include <nrf.h>
#include <misc/util.h>
#include <gpio.h>
#define SYS_LOG_DOMAIN "spim"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL
#include <logging/sys_log.h>
/* @todo
*
* Add support for 52840 spim2.
*/
struct spim_nrf52_config {
volatile NRF_SPIM_Type *base;
void (*irq_config_func)(struct device *dev);
struct spi_config default_cfg;
struct {
uint8_t sck;
uint8_t mosi;
uint8_t miso;
#define SS_UNUSED 255
/* Pin number of up to 4 slave devices */
uint8_t ss[4];
} psel;
uint8_t orc;
};
struct spim_nrf52_data {
struct k_sem sem;
struct device *gpio_port;
uint8_t slave;
uint8_t stopped:1;
uint8_t txd:1;
uint8_t rxd:1;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
uint32_t tx_cnt;
uint32_t rx_cnt;
#endif
};
#define NRF52_SPIM_INT_END SPIM_INTENSET_END_Msk
#define NRF52_SPIM_INT_ENDRX SPIM_INTENSET_ENDRX_Msk
#define NRF52_SPIM_INT_ENDTX SPIM_INTENSET_ENDTX_Msk
#define NRF52_SPIM_ENABLE SPIM_ENABLE_ENABLE_Enabled
#define NRF52_SPIM_DISABLE SPIM_ENABLE_ENABLE_Disabled
static void spim_nrf52_print_cfg_registers(struct device *dev)
{
const struct spim_nrf52_config *config = dev->config->config_info;
volatile NRF_SPIM_Type *spim = config->base;
SYS_LOG_DBG("\n"
"SHORTS=0x%x INT=0x%x FREQUENCY=0x%x CONFIG=0x%x\n"
"ENABLE=0x%x SCKPIN=%d MISOPIN=%d MOSIPIN=%d\n"
"RXD.(PTR=0x%x MAXCNT=0x%x AMOUNT=0x%x)\n"
"TXD.(PTR=0x%x MAXCNT=0x%x AMOUNT=0x%x)\n",
spim->SHORTS, spim->INTENSET, spim->FREQUENCY, spim->CONFIG,
spim->ENABLE, spim->PSEL.SCK, spim->PSEL.MISO, spim->PSEL.MOSI,
spim->RXD.PTR, spim->RXD.MAXCNT, spim->RXD.AMOUNT,
spim->TXD.PTR, spim->TXD.MAXCNT, spim->TXD.AMOUNT);
/* maybe unused */
(void)spim;
}
static int spim_nrf52_configure(struct device *dev,
struct spi_config *spi_config)
{
const struct spim_nrf52_config *config = dev->config->config_info;
struct spim_nrf52_data *data = dev->driver_data;
volatile NRF_SPIM_Type *spim = config->base;
uint32_t flags;
SYS_LOG_DBG("config=0x%x max_sys_freq=%d", spi_config->config,
spi_config->max_sys_freq);
/* make sure SPIM block is off */
spim->ENABLE = NRF52_SPIM_DISABLE;
spim->INTENCLR = 0xffffffffUL;
spim->SHORTS = 0;
spim->ORC = config->orc;
spim->TXD.LIST = 0;
spim->RXD.LIST = 0;
spim->TXD.MAXCNT = 0;
spim->RXD.MAXCNT = 0;
spim->EVENTS_END = 0;
spim->EVENTS_ENDTX = 0;
spim->EVENTS_ENDRX = 0;
spim->EVENTS_STOPPED = 0;
spim->EVENTS_STARTED = 0;
data->stopped = 1;
data->txd = 0;
data->rxd = 0;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
data->tx_cnt = 0;
data->rx_cnt = 0;
#endif
switch (spi_config->max_sys_freq) {
case 125000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_K125;
break;
case 250000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_K250;
break;
case 500000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_K500;
break;
case 1000000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_M1;
break;
case 2000000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_M2;
break;
case 4000000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_M4;
break;
case 8000000:
spim->FREQUENCY = SPIM_FREQUENCY_FREQUENCY_M8;
default:
SYS_LOG_ERR("unsupported frequency sck=%d\n",
spi_config->max_sys_freq);
return -EINVAL;
}
flags = spi_config->config;
/* nrf5 supports only 8 bit word size */
if (SPI_WORD_SIZE_GET(flags) != 8) {
SYS_LOG_ERR("unsupported word size\n");
return -EINVAL;
}
if (flags & SPI_MODE_LOOP) {
SYS_LOG_ERR("loopback unsupported\n");
return -EINVAL;
}
spim->CONFIG = (flags & SPI_TRANSFER_LSB) ? SPIM_CONFIG_ORDER_LsbFirst
: SPIM_CONFIG_ORDER_MsbFirst;
spim->CONFIG |= (flags & SPI_MODE_CPOL) ? SPIM_CONFIG_CPOL_ActiveLow
: SPIM_CONFIG_CPOL_ActiveHigh;
spim->CONFIG |= (flags & SPI_MODE_CPHA) ? SPIM_CONFIG_CPHA_Trailing
: SPIM_CONFIG_CPHA_Leading;
spim->INTENSET = NRF52_SPIM_INT_END;
spim_nrf52_print_cfg_registers(dev);
return 0;
}
static int spim_nrf52_slave_select(struct device *dev, uint32_t slave)
{
struct spim_nrf52_data *data = dev->driver_data;
const struct spim_nrf52_config *config = dev->config->config_info;
__ASSERT((slave > 0) && (slave <= 4), "slave=%d", slave);
slave--;
if (config->psel.ss[slave] == SS_UNUSED) {
SYS_LOG_ERR("Slave %d is not configured\n", slave);
return -EINVAL;
}
data->slave = slave;
return 0;
}
static inline void spim_nrf52_csn(struct device *gpio_port, uint32_t pin,
bool select)
{
int status;
status = gpio_pin_write(gpio_port, pin, select ? 0x0 : 0x1);
__ASSERT_NO_MSG(status == 0);
}
static int spim_nrf52_transceive(struct device *dev, const void *tx_buf,
uint32_t tx_buf_len, void *rx_buf,
uint32_t rx_buf_len)
{
const struct spim_nrf52_config *config = dev->config->config_info;
struct spim_nrf52_data *data = dev->driver_data;
volatile NRF_SPIM_Type *spim = config->base;
SYS_LOG_DBG("transceive tx_buf=0x%p rx_buf=0x%p tx_len=0x%x "
"rx_len=0x%x\n", tx_buf, rx_buf, tx_buf_len, rx_buf_len);
if (spim->ENABLE) {
return -EALREADY;
}
spim->ENABLE = NRF52_SPIM_ENABLE;
__ASSERT_NO_MSG(data->stopped);
data->stopped = 0;
if (tx_buf_len) {
__ASSERT_NO_MSG(tx_buf);
spim->TXD.MAXCNT = tx_buf_len;
spim->TXD.PTR = (uint32_t)tx_buf;
data->txd = 0;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
data->tx_cnt = 0;
#endif
} else {
spim->TXD.MAXCNT = 0;
}
if (rx_buf_len) {
__ASSERT_NO_MSG(rx_buf);
spim->RXD.MAXCNT = rx_buf_len;
spim->RXD.PTR = (uint32_t)rx_buf;
data->rxd = 0;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
data->rx_cnt = 0;
#endif
} else {
spim->RXD.MAXCNT = 0;
}
if (data->slave != SS_UNUSED) {
spim_nrf52_csn(data->gpio_port, config->psel.ss[data->slave],
true);
}
spim->INTENSET = NRF52_SPIM_INT_END;
SYS_LOG_DBG("spi_xfer %s/%s CS%d\n", rx_buf_len ? "R" : "-",
tx_buf_len ? "W" : "-", data->slave);
/* start SPI transfer transaction */
spim->TASKS_START = 1;
/* Wait for the transfer to complete */
k_sem_take(&data->sem, K_FOREVER);
if (data->slave != SS_UNUSED) {
spim_nrf52_csn(data->gpio_port, config->psel.ss[data->slave],
false);
}
/* Disable SPIM block for power saving */
spim->INTENCLR = 0xffffffffUL;
spim->ENABLE = NRF52_SPIM_DISABLE;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
SYS_LOG_DBG("xfer complete rx_cnt=0x%x tx_cnt=0x%x rxd=%d txd=%d "
"stopped=%d\n", data->rx_cnt, data->tx_cnt, data->rxd,
data->txd, data->stopped);
#endif
return 0;
}
static void spim_nrf52_isr(void *arg)
{
struct device *dev = arg;
const struct spim_nrf52_config *config = dev->config->config_info;
struct spim_nrf52_data *data = dev->driver_data;
volatile NRF_SPIM_Type *spim = config->base;
if (spim->EVENTS_END) {
data->rxd = 1;
data->txd = 1;
/* assume spi transaction has stopped */
data->stopped = 1;
/* Cortex M4 specific EVENTS register clearing requires 4 cycles
* delayto avoid re-triggering of interrupt. Call to
* k_sem_give() will ensure this limit.
*/
spim->EVENTS_END = 0;
#if (SYS_LOG_LEVEL > SYS_LOG_LEVEL_INFO)
data->rx_cnt = spim->RXD.AMOUNT;
data->tx_cnt = spim->TXD.AMOUNT;
SYS_LOG_DBG("endrxtx rx_cnt=%d tx_cnt=%d", data->rx_cnt,
data->tx_cnt);
#endif
k_sem_give(&data->sem);
}
}
static int spim_nrf52_init(struct device *dev)
{
const struct spim_nrf52_config *config = dev->config->config_info;
struct spim_nrf52_data *data = dev->driver_data;
volatile NRF_SPIM_Type *spim = config->base;
int status;
int i;
SYS_LOG_DBG("%s", dev->config->name);
data->gpio_port = device_get_binding(CONFIG_GPIO_NRF5_P0_DEV_NAME);
k_sem_init(&data->sem, 0, UINT_MAX);
for (i = 0; i < sizeof(config->psel.ss); i++) {
if (config->psel.ss[i] != SS_UNUSED) {
status = gpio_pin_configure(data->gpio_port,
config->psel.ss[i],
GPIO_DIR_OUT);
__ASSERT_NO_MSG(status == 0);
spim_nrf52_csn(data->gpio_port, config->psel.ss[i],
false);
SYS_LOG_DBG("CS%d=%d\n", i, config->psel.ss[i]);
}
}
data->slave = SS_UNUSED;
status = gpio_pin_configure(data->gpio_port, config->psel.sck,
GPIO_DIR_OUT);
__ASSERT_NO_MSG(status == 0);
status = gpio_pin_configure(data->gpio_port, config->psel.mosi,
GPIO_DIR_OUT);
__ASSERT_NO_MSG(status == 0);
status = gpio_pin_configure(data->gpio_port, config->psel.miso,
GPIO_DIR_IN);
__ASSERT_NO_MSG(status == 0);
spim->PSEL.SCK = config->psel.sck;
spim->PSEL.MOSI = config->psel.mosi;
spim->PSEL.MISO = config->psel.miso;
status = spim_nrf52_configure(dev, (void *)&config->default_cfg);
if (status) {
return status;
}
config->irq_config_func(dev);
return 0;
}
static const struct spi_driver_api spim_nrf52_driver_api = {
.configure = spim_nrf52_configure,
.slave_select = spim_nrf52_slave_select,
.transceive = spim_nrf52_transceive,
};
/* i2c & spi (SPIM, SPIS, SPI) instance with the same id (e.g. I2C_0 and SPI_0)
* can NOT be used at the same time on nRF5x chip family.
*/
#if defined(CONFIG_SPIM0_NRF52) && !defined(CONFIG_I2C_0)
static void spim_nrf52_config_func_0(struct device *dev);
static const struct spim_nrf52_config spim_nrf52_config_0 = {
.base = NRF_SPIM0,
.irq_config_func = spim_nrf52_config_func_0,
.default_cfg = {
.config = CONFIG_SPI_0_DEFAULT_CFG,
.max_sys_freq = CONFIG_SPI_0_DEFAULT_BAUD_RATE,
},
.psel = {
.sck = CONFIG_SPIM0_NRF52_GPIO_SCK_PIN,
.mosi = CONFIG_SPIM0_NRF52_GPIO_MOSI_PIN,
.miso = CONFIG_SPIM0_NRF52_GPIO_MISO_PIN,
.ss = { CONFIG_SPIM0_NRF52_GPIO_SS_PIN_0,
CONFIG_SPIM0_NRF52_GPIO_SS_PIN_1,
CONFIG_SPIM0_NRF52_GPIO_SS_PIN_2,
CONFIG_SPIM0_NRF52_GPIO_SS_PIN_3 },
},
.orc = CONFIG_SPIM0_NRF52_ORC,
};
static struct spim_nrf52_data spim_nrf52_data_0;
DEVICE_AND_API_INIT(spim_nrf52_0, CONFIG_SPI_0_NAME, spim_nrf52_init,
&spim_nrf52_data_0, &spim_nrf52_config_0,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&spim_nrf52_driver_api);
static void spim_nrf52_config_func_0(struct device *dev)
{
IRQ_CONNECT(NRF5_IRQ_SPI0_TWI0_IRQn, CONFIG_SPI_0_IRQ_PRI,
spim_nrf52_isr, DEVICE_GET(spim_nrf52_0), 0);
irq_enable(NRF5_IRQ_SPI0_TWI0_IRQn);
}
#endif /* CONFIG_SPIM0_NRF52 && !CONFIG_I2C_0 */
#if defined(CONFIG_SPIM1_NRF52) && !defined(CONFIG_I2C_1)
static void spim_nrf52_config_func_1(struct device *dev);
static const struct spim_nrf52_config spim_nrf52_config_1 = {
.base = NRF_SPIM1,
.irq_config_func = spim_nrf52_config_func_1,
.default_cfg = {
.config = CONFIG_SPI_1_DEFAULT_CFG,
.max_sys_freq = CONFIG_SPI_1_DEFAULT_BAUD_RATE,
},
.psel = {
.sck = CONFIG_SPIM1_NRF52_GPIO_SCK_PIN,
.mosi = CONFIG_SPIM1_NRF52_GPIO_MOSI_PIN,
.miso = CONFIG_SPIM1_NRF52_GPIO_MISO_PIN,
.ss = { CONFIG_SPIM1_NRF52_GPIO_SS_PIN_0,
CONFIG_SPIM1_NRF52_GPIO_SS_PIN_1,
CONFIG_SPIM1_NRF52_GPIO_SS_PIN_2,
CONFIG_SPIM1_NRF52_GPIO_SS_PIN_3 },
},
.orc = CONFIG_SPIM0_NRF52_ORC,
};
static struct spim_nrf52_data spim_nrf52_data_1;
DEVICE_AND_API_INIT(spim_nrf52_1, CONFIG_SPI_1_NAME, spim_nrf52_init,
&spim_nrf52_data_1, &spim_nrf52_config_1,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&spim_nrf52_driver_api);
static void spim_nrf52_config_func_1(struct device *dev)
{
IRQ_CONNECT(NRF5_IRQ_SPI1_TWI1_IRQn, CONFIG_SPI_1_IRQ_PRI,
spim_nrf52_isr, DEVICE_GET(spim_nrf52_1), 0);
irq_enable(NRF5_IRQ_SPI1_TWI1_IRQn);
}
#endif /* CONFIG_SPIM1_NRF52 && !CONFIG_I2C_1 */