drivers: spi_nrfx_spim: workaround for nRF52832 errata anomaly 58

See:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fstruct_nrf52%2Fstruct%2Fnrf52832_errata.html
https://infocenter.nordicsemi.com/pdf/nRF52832_Rev_3_Errata_v1.0.pdf

Code derived from the example PAN 58 workaround code.

Adds a new nRF SPIM Devicetree binding property called
anomaly-58-workaround that allows the workaround to be enabled
if required per SPIM instance.

Signed-off-by: Nick Ward <nick.ward@setec.com.au>
This commit is contained in:
Nick Ward 2021-10-26 09:43:56 +11:00 committed by Christopher Friedt
parent 0cfba7e9b3
commit fa94be591d
3 changed files with 134 additions and 14 deletions

View file

@ -19,9 +19,13 @@ config SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
clocked out when RXD.MAXCNT == 1 and TXD.MAXCNT <= 1).
Without this override, the SPI Master is only available
without EasyDMA. Note that the 'SPIM' and 'SPIS' drivers
use EasyDMA, while the 'SPI' driver does not. Use this
option ONLY if you are certain that transactions with
RXD.MAXCNT == 1 and TXD.MAXCNT <= 1 will NOT be executed.
use EasyDMA, while the 'SPI' driver does not.
When used in conjunction with nRF SPIM Devicetree property
'anomaly-58-workaround' a workaround can be enabled per SPIM
instance. If you are certain that transactions with
RXD.MAXCNT == 1 and TXD.MAXCNT <= 1 will NOT be executed
then nRF52832 PPI and GPIOTE resources can be saved by not
enabling 'anomaly-58-workaround' via the Devicetree.
# Workaround for not being able to have commas in macro arguments
DT_COMPAT_NORDIC_NRF_SPI := nordic,nrf-spi

View file

@ -5,6 +5,10 @@
*/
#include <drivers/spi.h>
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
#include <nrfx_gpiote.h>
#include <nrfx_ppi.h>
#endif
#include <nrfx_spim.h>
#include <hal/nrf_clock.h>
#include <string.h>
@ -25,6 +29,11 @@ struct spi_nrfx_data {
#if (CONFIG_SPI_NRFX_RAM_BUFFER_SIZE > 0)
uint8_t buffer[CONFIG_SPI_NRFX_RAM_BUFFER_SIZE];
#endif
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
bool anomaly_58_workaround_active;
uint8_t ppi_ch;
uint8_t gpiote_ch;
#endif
};
struct spi_nrfx_config {
@ -32,6 +41,9 @@ struct spi_nrfx_config {
size_t max_chunk_len;
uint32_t max_freq;
nrfx_spim_config_t def_config;
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
bool anomaly_58_workaround;
#endif
};
static void event_handler(const nrfx_spim_evt_t *p_event, void *p_context);
@ -183,6 +195,87 @@ static int configure(const struct device *dev,
return 0;
}
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
/*
* Brief Workaround for transmitting 1 byte with SPIM.
*
* Derived from the setup_workaround_for_ftpan_58() function from
* the nRF52832 Rev 1 Errata v1.6 document anomaly 58 workaround.
*
* Warning Must not be used when transmitting multiple bytes.
*
* Warning After this workaround is used, the user must reset the PPI
* channel and the GPIOTE channel before attempting to transmit multiple
* bytes.
*/
static void anomaly_58_workaround_setup(const struct device *dev)
{
struct spi_nrfx_data *dev_data = get_dev_data(dev);
const struct spi_nrfx_config *dev_config = get_dev_config(dev);
NRF_SPIM_Type *spim = dev_config->spim.p_reg;
uint32_t ppi_ch = dev_data->ppi_ch;
uint32_t gpiote_ch = dev_data->gpiote_ch;
uint32_t eep = (uint32_t)&NRF_GPIOTE->EVENTS_IN[gpiote_ch];
uint32_t tep = (uint32_t)&spim->TASKS_STOP;
dev_data->anomaly_58_workaround_active = true;
/* Create an event when SCK toggles */
nrf_gpiote_event_configure(NRF_GPIOTE, gpiote_ch, spim->PSEL.SCK,
GPIOTE_CONFIG_POLARITY_Toggle);
nrf_gpiote_event_enable(NRF_GPIOTE, gpiote_ch);
/* Stop the spim instance when SCK toggles */
nrf_ppi_channel_endpoint_setup(NRF_PPI, ppi_ch, eep, tep);
nrf_ppi_channel_enable(NRF_PPI, ppi_ch);
/* The spim instance cannot be stopped mid-byte, so it will finish
* transmitting the first byte and then stop. Effectively ensuring
* that only 1 byte is transmitted.
*/
}
static void anomaly_58_workaround_clear(struct spi_nrfx_data *dev_data)
{
uint32_t ppi_ch = dev_data->ppi_ch;
uint32_t gpiote_ch = dev_data->gpiote_ch;
if (dev_data->anomaly_58_workaround_active) {
nrf_ppi_channel_disable(NRF_PPI, ppi_ch);
nrf_gpiote_task_disable(NRF_GPIOTE, gpiote_ch);
dev_data->anomaly_58_workaround_active = false;
}
}
static int anomaly_58_workaround_init(const struct device *dev)
{
struct spi_nrfx_data *data = get_dev_data(dev);
const struct spi_nrfx_config *config = get_dev_config(dev);
nrfx_err_t err_code;
data->anomaly_58_workaround_active = false;
if (config->anomaly_58_workaround) {
err_code = nrfx_ppi_channel_alloc(&data->ppi_ch);
if (err_code != NRFX_SUCCESS) {
LOG_ERR("Failed to allocate PPI channel");
return -ENODEV;
}
err_code = nrfx_gpiote_channel_alloc(&data->gpiote_ch);
if (err_code != NRFX_SUCCESS) {
LOG_ERR("Failed to allocate GPIOTE channel");
return -ENODEV;
}
LOG_DBG("PAN 58 workaround enabled for %s: ppi %u, gpiote %u",
dev->name, data->ppi_ch, data->gpiote_ch);
}
return 0;
}
#endif
static void transfer_next_chunk(const struct device *dev)
{
struct spi_nrfx_data *dev_data = get_dev_data(dev);
@ -217,20 +310,26 @@ static void transfer_next_chunk(const struct device *dev)
xfer.p_rx_buffer = ctx->rx_buf;
xfer.rx_length = spi_context_rx_buf_on(ctx) ? chunk_len : 0;
/* This SPIM driver is only used by the NRF52832 if
SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58 is enabled */
if (IS_ENABLED(CONFIG_SOC_NRF52832) &&
(xfer.rx_length == 1 && xfer.tx_length <= 1)) {
LOG_WRN("Transaction aborted since it would trigger nRF52832 PAN 58");
error = -EIO;
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
if (xfer.rx_length == 1 && xfer.tx_length <= 1) {
if (dev_config->anomaly_58_workaround) {
anomaly_58_workaround_setup(dev);
} else {
LOG_WRN("Transaction aborted since it would trigger "
"nRF52832 PAN 58");
error = -EIO;
}
}
if (!error) {
#endif
if (error == 0) {
result = nrfx_spim_xfer(&dev_config->spim, &xfer, 0);
if (result == NRFX_SUCCESS) {
return;
}
error = -EIO;
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
anomaly_58_workaround_clear(dev_data);
#endif
}
}
@ -247,6 +346,9 @@ 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) {
#ifdef CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58
anomaly_58_workaround_clear(dev_data);
#endif
spi_context_update_tx(&dev_data->ctx, 1, dev_data->chunk_len);
spi_context_update_rx(&dev_data->ctx, 1, dev_data->chunk_len);
@ -385,7 +487,7 @@ static int spim_nrfx_pm_action(const struct device *dev,
))
#define SPI_NRFX_SPIM_DEVICE(idx) \
BUILD_ASSERT( \
BUILD_ASSERT( \
!SPIM_NRFX_MISO_PULL_UP(idx) || !SPIM_NRFX_MISO_PULL_DOWN(idx),\
"SPIM"#idx \
": cannot enable both pull-up and pull-down on MISO line"); \
@ -400,7 +502,9 @@ static int spim_nrfx_pm_action(const struct device *dev,
return err; \
} \
spi_context_unlock_unconditionally(&get_dev_data(dev)->ctx); \
return 0; \
COND_CODE_1(CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58, \
(return anomaly_58_workaround_init(dev);), \
(return 0;)) \
} \
static struct spi_nrfx_data spi_##idx##_data = { \
SPI_CONTEXT_INIT_LOCK(spi_##idx##_data, ctx), \
@ -421,7 +525,11 @@ static int spim_nrfx_pm_action(const struct device *dev,
.orc = CONFIG_SPI_##idx##_NRF_ORC, \
.miso_pull = SPIM_NRFX_MISO_PULL(idx), \
SPI_NRFX_SPIM_EXTENDED_CONFIG(idx) \
} \
}, \
COND_CODE_1(CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58, \
(.anomaly_58_workaround = \
SPIM_PROP(idx, anomaly_58_workaround),), \
()) \
}; \
DEVICE_DT_DEFINE(SPIM(idx), \
spi_##idx##_init, \

View file

@ -17,3 +17,11 @@ properties:
type: boolean
required: false
description: Enable pull-down on MISO line
anomaly-58-workaround:
type: boolean
required: false
description: |
Enables the workaround for the nRF52832 SoC SPIM PAN 58 anomaly.
Must be used in conjunction with
CONFIG_SOC_NRF52832_ALLOW_SPIM_DESPITE_PAN_58=y