Skip to content

Commit

Permalink
drivers: stm32: SPI: Check that SPI buffers are in a nocache region
Browse files Browse the repository at this point in the history
DMA only works with non-cached memory regions in H7. Check them
and return an error if they don't match this condition.

Signed-off-by: Daniel Gaston Ochoa <[email protected]>
  • Loading branch information
dgastonochoa committed May 16, 2023
1 parent b07be57 commit fdc1dc7
Showing 1 changed file with 82 additions and 0 deletions.
82 changes: 82 additions & 0 deletions drivers/spi/spi_ll_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,80 @@ static int transceive(const struct device *dev,

#ifdef CONFIG_SPI_STM32_DMA

#ifdef CONFIG_SOC_SERIES_STM32H7X
#define STM32_SPI_GET_MEM_REGION(node_id) \
{ \
.start = (const void*)DT_REG_ADDR(node_id), \
.end = (const void*)(DT_REG_ADDR(node_id) + DT_REG_SIZE(node_id)), \
.region_mpu = COND_CODE_1( \
DT_NODE_HAS_PROP(node_id, zephyr_memory_region_mpu), \
(DT_PROP(node_id, zephyr_memory_region_mpu)), \
(NULL) \
) \
},

#define NOCACHE_ID "RAM_NOCACHE"
#define NOCACHE_ID_LEN sizeof(NOCACHE_ID)

struct spi_stm32_mem_region {
const uint8_t *start;
const uint8_t *end;
const char *region_mpu;
};

static struct spi_stm32_mem_region stm32_mem_regions[] = {
DT_FOREACH_STATUS_OKAY(mmio_sram, STM32_SPI_GET_MEM_REGION)
};

/*
* DMA in H7 must only access non-cacheable RAM regions or it won't work
* properly. Use this function to check if the SPI buffers are in a nocache
* region.
*
* Return true if @param{buf} is not within nocache RAM bounds, false
* otherwise.
*
* TODO In H7, DMA can only access certain memory areas regardless of if they
* are nocache or not (e.g. SRAM1, SRAM2). It would be good to check this as
* well, but different H7 SoCs can access different areas.
*/
static bool buf_in_non_cacheable_sram(const uint8_t *buf, size_t size)
{
const size_t mem_regions_cnt =
sizeof(stm32_mem_regions) / sizeof(*stm32_mem_regions);

for (size_t i = 0; i < mem_regions_cnt; i++) {
const struct spi_stm32_mem_region* mem_reg = &stm32_mem_regions[i];
const char* reg_mpu = mem_reg->region_mpu;

const bool buf_within_bounds =
(buf >= mem_reg->start) && ((buf + size) <= mem_reg->end);
if (buf_within_bounds &&
reg_mpu != NULL &&
strncmp(reg_mpu, NOCACHE_ID, NOCACHE_ID_LEN) == 0) {
return false;
}
}
return true;
}

/*
* Checks that all buffers in `bufs` are in a valid area to be used
* with DMA.
*
*/
static bool all_bufs_in_valid_dma_ram(const struct spi_buf_set *bufs)
{
for (size_t i = 0; i < bufs->count; i++) {
const struct spi_buf *buf = &bufs->buffers[i];
if (buf_in_non_cacheable_sram(buf->buf, buf->len)) {
return false;
}
}
return true;
}
#endif /* CONFIG_SOC_SERIES_STM32H7X */

static int wait_dma_rx_tx_done(const struct device *dev)
{
struct spi_stm32_data *data = dev->data;
Expand Down Expand Up @@ -723,6 +797,13 @@ static int transceive_dma(const struct device *dev,
return -ENOTSUP;
}

#ifdef CONFIG_SOC_SERIES_STM32H7X
if ((tx_bufs != NULL && !all_bufs_in_valid_dma_ram(tx_bufs)) ||
(rx_bufs != NULL && !all_bufs_in_valid_dma_ram(rx_bufs))) {
return -EFAULT;
}
#endif /* CONFIG_SOC_SERIES_STM32H7X */

spi_context_lock(&data->ctx, asynchronous, cb, userdata, config);

k_sem_reset(&data->status_sem);
Expand Down Expand Up @@ -1015,6 +1096,7 @@ static void spi_stm32_irq_config_func_##id(const struct device *dev) \




#define STM32_SPI_INIT(id) \
STM32_SPI_IRQ_HANDLER_DECL(id); \
\
Expand Down

0 comments on commit fdc1dc7

Please sign in to comment.