drivers: spi: stm32: add error checking to IRQ mode

Now that struct spi_context supports passing errors from
interrupt-driven I/O handlers to waiting threads, we can enable error
interrupts and propagate errors to spi_transceive() callers.

To make it easier for users to debug SPI-related issues, log any error
bits set in SR when failures occur.

A subsequent patch will add error checking to polled mode as well, but
other cleanups and fixes will go in first to make this easier.

Note that this breaks the spi_loopback test on some targets, but it's
not a regression, as it wasn't working properly anyway. Subsequent
patches the bugs that this error checking has exposed.

Signed-off-by: Marti Bolivar <marti.bolivar@linaro.org>
This commit is contained in:
Marti Bolivar 2017-07-17 14:20:13 -04:00 committed by Anas Nashif
commit 69bc5ebdf3

View file

@ -25,16 +25,64 @@
#define CONFIG_DATA(cfg) \
((struct spi_stm32_data * const)(cfg)->dev->driver_data)
#ifdef LL_SPI_SR_UDR
#define SPI_STM32_ERR_MSK (LL_SPI_SR_UDR | LL_SPI_SR_CRCERR | LL_SPI_SR_MODF | \
LL_SPI_SR_OVR | LL_SPI_SR_FRE)
#else
#define SPI_STM32_ERR_MSK (LL_SPI_SR_CRCERR | LL_SPI_SR_MODF | \
LL_SPI_SR_OVR | LL_SPI_SR_FRE)
#endif
#ifdef CONFIG_SPI_STM32_INTERRUPT
static void spi_stm32_transmit(SPI_TypeDef *spi, struct spi_stm32_data *data);
static void spi_stm32_receive(SPI_TypeDef *spi, struct spi_stm32_data *data);
static int spi_stm32_get_err(SPI_TypeDef *spi)
{
u32_t sr = LL_SPI_ReadReg(spi, SR);
return (int)(sr & SPI_STM32_ERR_MSK);
}
static void spi_stm32_complete(struct spi_stm32_data *data, SPI_TypeDef *spi,
int status)
{
LL_SPI_DisableIT_TXE(spi);
LL_SPI_DisableIT_RXNE(spi);
LL_SPI_DisableIT_ERR(spi);
spi_context_cs_control(&data->ctx, false);
#if defined(CONFIG_SOC_SERIES_STM32L4X) || defined(CONFIG_SOC_SERIES_STM32F3X)
/* Flush RX buffer */
while (LL_SPI_IsActiveFlag_RXNE(spi)) {
(void) LL_SPI_ReceiveData8(spi);
}
#endif
if (LL_SPI_GetMode(spi) == LL_SPI_MODE_MASTER) {
while (LL_SPI_IsActiveFlag_BSY(spi)) {
/* NOP */
}
LL_SPI_Disable(spi);
}
spi_context_complete(&data->ctx, status);
}
static void spi_stm32_isr(void *arg)
{
struct device * const dev = (struct device *) arg;
const struct spi_stm32_config *cfg = dev->config->config_info;
struct spi_stm32_data *data = dev->driver_data;
SPI_TypeDef *spi = cfg->spi;
int err;
err = spi_stm32_get_err(spi);
if (err) {
spi_stm32_complete(data, spi, err);
return;
}
if (LL_SPI_IsActiveFlag_TXE(spi) &&
(spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx))) {
@ -51,26 +99,7 @@ static void spi_stm32_isr(void *arg)
if (!spi_context_tx_on(&data->ctx) &&
!spi_context_rx_on(&data->ctx)) {
LL_SPI_DisableIT_TXE(spi);
LL_SPI_DisableIT_RXNE(spi);
spi_context_cs_control(&data->ctx, false);
#if defined(CONFIG_SOC_SERIES_STM32L4X) || defined(CONFIG_SOC_SERIES_STM32F3X)
/* Flush RX buffer */
while (LL_SPI_IsActiveFlag_RXNE(spi)) {
(void) LL_SPI_ReceiveData8(spi);
}
#endif
if (LL_SPI_GetMode(spi) == LL_SPI_MODE_MASTER) {
while (LL_SPI_IsActiveFlag_BSY(spi)) {
/* NOP */
}
LL_SPI_Disable(spi);
}
spi_context_complete(&data->ctx, 0);
spi_stm32_complete(data, spi, spi_stm32_get_err(spi));
}
}
#endif
@ -263,14 +292,17 @@ static int transceive(struct spi_config *config,
spi_context_cs_control(&data->ctx, true);
#ifdef CONFIG_SPI_STM32_INTERRUPT
LL_SPI_EnableIT_ERR(spi);
if (rx_bufs) {
LL_SPI_EnableIT_RXNE(spi);
}
LL_SPI_EnableIT_TXE(spi);
spi_context_wait_for_completion(&data->ctx);
ret = spi_context_wait_for_completion(&data->ctx);
#else
ret = 0; /* FIXME: add error checking. */
do {
/* Keep transmitting NOP data until RX data left */
if ((spi_context_tx_on(&data->ctx) ||
@ -305,7 +337,11 @@ static int transceive(struct spi_config *config,
spi_context_release(&data->ctx, 0);
return 0;
if (ret) {
SYS_LOG_ERR("error mask 0x%x", ret);
}
return ret ? -EIO : 0;
}
static int spi_stm32_transceive(struct spi_config *config,