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:
parent
0cfba7e9b3
commit
fa94be591d
|
@ -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
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue