Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 69 additions & 65 deletions drivers/spi/spi_nrfx_spim.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <drivers/spi.h>
#include <nrfx_spim.h>
#include <hal/nrf_clock.h>
#include <string.h>

#define LOG_DOMAIN "spi_nrfx_spim"
Expand All @@ -18,20 +19,23 @@ LOG_MODULE_REGISTER(spi_nrfx_spim);
struct spi_nrfx_data {
struct spi_context ctx;
const struct device *dev;
size_t chunk_len;
bool busy;
size_t chunk_len;
bool busy;
bool initialized;
#if (CONFIG_SPI_NRFX_RAM_BUFFER_SIZE > 0)
uint8_t buffer[CONFIG_SPI_NRFX_RAM_BUFFER_SIZE];
uint8_t buffer[CONFIG_SPI_NRFX_RAM_BUFFER_SIZE];
#endif
};

struct spi_nrfx_config {
nrfx_spim_t spim;
size_t max_chunk_len;
uint32_t max_freq;
nrfx_spim_config_t config;
nrfx_spim_config_t def_config;
};

static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context);

static inline struct spi_nrfx_data *get_dev_data(const struct device *dev)
{
return dev->data;
Expand Down Expand Up @@ -104,18 +108,20 @@ static inline nrf_spim_bit_order_t get_nrf_spim_bit_order(uint16_t operation)
static int configure(const struct device *dev,
const struct spi_config *spi_cfg)
{
struct spi_context *ctx = &get_dev_data(dev)->ctx;
const nrfx_spim_t *spim = &get_dev_config(dev)->spim;
nrf_spim_frequency_t freq;
struct spi_nrfx_data *dev_data = get_dev_data(dev);
const struct spi_nrfx_config *dev_config = get_dev_config(dev);
struct spi_context *ctx = &dev_data->ctx;
uint32_t max_freq = dev_config->max_freq;
nrfx_spim_config_t config;
nrfx_err_t result;

if (spi_context_configured(ctx, spi_cfg)) {
if (dev_data->initialized && spi_context_configured(ctx, spi_cfg)) {
/* Already configured. No need to do it again. */
return 0;
}

if (SPI_OP_MODE_GET(spi_cfg->operation) != SPI_OP_MODE_MASTER) {
LOG_ERR("Slave mode is not supported on %s",
dev->name);
LOG_ERR("Slave mode is not supported on %s", dev->name);
return -EINVAL;
}

Expand All @@ -130,8 +136,7 @@ static int configure(const struct device *dev,
}

if (SPI_WORD_SIZE_GET(spi_cfg->operation) != 8) {
LOG_ERR("Word sizes other than 8 bits"
" are not supported");
LOG_ERR("Word sizes other than 8 bits are not supported");
return -EINVAL;
}

Expand All @@ -140,17 +145,41 @@ static int configure(const struct device *dev,
return -EINVAL;
}

ctx->config = spi_cfg;
spi_context_cs_configure(ctx);
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
/* On nRF5340, the 32 Mbps speed is supported by the application core
* when it is running at 128 MHz (see the Timing specifications section
* in the nRF5340 PS).
*/
if (max_freq > 16000000 &&
nrf_clock_hfclk_div_get(NRF_CLOCK) != NRF_CLOCK_HFCLK_DIV_1) {
max_freq = 16000000;
}
#endif

config = dev_config->def_config;

/* Limit the frequency to that supported by the SPIM instance. */
config.frequency = get_nrf_spim_frequency(MIN(spi_cfg->frequency,
max_freq));
config.mode = get_nrf_spim_mode(spi_cfg->operation);
config.bit_order = get_nrf_spim_bit_order(spi_cfg->operation);

if (dev_data->initialized) {
nrfx_spim_uninit(&dev_config->spim);
dev_data->initialized = false;
}

result = nrfx_spim_init(&dev_config->spim, &config,
event_handler, dev_data);
if (result != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize nrfx driver: %08x", result);
return -EIO;
}

/* Limit the frequency to that supported by the SPIM instance */
freq = get_nrf_spim_frequency(MIN(spi_cfg->frequency,
get_dev_config(dev)->max_freq));
dev_data->initialized = true;

nrf_spim_configure(spim->p_reg,
get_nrf_spim_mode(spi_cfg->operation),
get_nrf_spim_bit_order(spi_cfg->operation));
nrf_spim_frequency_set(spim->p_reg, freq);
ctx->config = spi_cfg;
spi_context_cs_configure(ctx);

return 0;
}
Expand Down Expand Up @@ -214,6 +243,18 @@ static void transfer_next_chunk(const struct device *dev)
dev_data->busy = false;
}

static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context)
{
struct spi_nrfx_data *dev_data = p_context;

if (p_event->type == NRFX_SPIM_EVENT_DONE) {
spi_context_update_tx(&dev_data->ctx, 1, dev_data->chunk_len);
spi_context_update_rx(&dev_data->ctx, 1, dev_data->chunk_len);

transfer_next_chunk(dev_data->dev);
}
}

static int transceive(const struct device *dev,
const struct spi_config *spi_cfg,
const struct spi_buf_set *tx_bufs,
Expand Down Expand Up @@ -288,41 +329,6 @@ static const struct spi_driver_api spi_nrfx_driver_api = {
.release = spi_nrfx_release,
};


static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context)
{
struct spi_nrfx_data *dev_data = p_context;

if (p_event->type == NRFX_SPIM_EVENT_DONE) {
spi_context_update_tx(&dev_data->ctx, 1, dev_data->chunk_len);
spi_context_update_rx(&dev_data->ctx, 1, dev_data->chunk_len);

transfer_next_chunk(dev_data->dev);
}
}

static int init_spim(const struct device *dev)
{
struct spi_nrfx_data *data = get_dev_data(dev);
nrfx_err_t result;

data->dev = dev;

/* This sets only default values of frequency, mode and bit order.
* The proper ones are set in configure() when a transfer is started.
*/
result = nrfx_spim_init(&get_dev_config(dev)->spim,
&get_dev_config(dev)->config,
event_handler,
data);
if (result != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize device: %s", dev->name);
return -EBUSY;
}

return 0;
}

#ifdef CONFIG_PM_DEVICE
static int spim_nrfx_pm_control(const struct device *dev,
enum pm_device_action action)
Expand All @@ -333,13 +339,14 @@ static int spim_nrfx_pm_control(const struct device *dev,

switch (action) {
case PM_DEVICE_ACTION_RESUME:
ret = init_spim(dev);
/* Force reconfiguration before next transfer */
data->ctx.config = NULL;
/* No action needed at this point, nrfx_spim_init() will be
* called at configuration before the next transfer.
*/
Comment on lines +342 to +344
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM hooks are no longer doing what is expected, ie, put the device into operational state when resume is called. Why is this change required?

Copy link
Member Author

@anangl anangl Sep 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, when resume was called, the device was brought to the state it was right after its initialization - it still needed to be reconfigured before the next transfer:

ret = init_spim(dev);
/* Force reconfiguration before next transfer */
data->ctx.config = NULL;

(at the device initialization, the nrfx driver was initialized with a default configuration, which was then altered accordingly when a transfer was requested). Now the difference is that the initialization of the device does not include the initialization of the nrfx driver - this is done when the device is configured right before a transfer is performed (only then we know the frequency, SPI mode, etc. that should be used), so there is no point in calling nrfx_spim_init() here and then, right before the next transfer, calling nrfx_spim_uninit() in order to be able to call nrfx_spim_init() again.
Alternatively, I would need to store the currently used nrfx driver configuration to be able to use it here in the call to nrfx_spim_init(). Then it would be not needed to reconfigure the device before the next transfer, provided that transfer would be requested with the same configuration as the last transfer before the device was suspended. But I'm not sure if it is worth spending more RAM just to achieve this possibility.

break;

case PM_DEVICE_ACTION_SUSPEND:
nrfx_spim_uninit(&config->spim);
data->initialized = false;
break;

default:
Expand Down Expand Up @@ -388,28 +395,25 @@ static int spim_nrfx_pm_control(const struct device *dev,
IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SPIM##idx), \
DT_IRQ(SPIM(idx), priority), \
nrfx_isr, nrfx_spim_##idx##_irq_handler, 0); \
int err = init_spim(dev); \
spi_context_unlock_unconditionally(&get_dev_data(dev)->ctx); \
return err; \
return 0; \
} \
static struct spi_nrfx_data spi_##idx##_data = { \
SPI_CONTEXT_INIT_LOCK(spi_##idx##_data, ctx), \
SPI_CONTEXT_INIT_SYNC(spi_##idx##_data, ctx), \
.dev = DEVICE_DT_GET(SPIM(idx)), \
.busy = false, \
}; \
static const struct spi_nrfx_config spi_##idx##z_config = { \
.spim = NRFX_SPIM_INSTANCE(idx), \
.max_chunk_len = (1 << SPIM##idx##_EASYDMA_MAXCNT_SIZE) - 1, \
.max_freq = SPIM##idx##_MAX_DATARATE * 1000000, \
.config = { \
.def_config = { \
.sck_pin = SPIM_PROP(idx, sck_pin), \
.mosi_pin = SPIM_PROP(idx, mosi_pin), \
.miso_pin = SPIM_PROP(idx, miso_pin), \
.ss_pin = NRFX_SPIM_PIN_NOT_USED, \
.orc = CONFIG_SPI_##idx##_NRF_ORC, \
.frequency = NRF_SPIM_FREQ_4M, \
.mode = NRF_SPIM_MODE_0, \
.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST, \
.miso_pull = SPIM_NRFX_MISO_PULL(idx), \
SPI_NRFX_SPIM_EXTENDED_CONFIG(idx) \
} \
Expand Down