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 Jun 27, 2023
1 parent b07be57 commit c119ee7
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
35 changes: 35 additions & 0 deletions drivers/spi/spi_ll_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,33 @@ static int transceive(const struct device *dev,

#ifdef CONFIG_SPI_STM32_DMA

#ifdef CONFIG_SOC_SERIES_STM32H7X

static struct dma_mem_region stm32_mem_regions[] = {
DT_FOREACH_STATUS_OKAY(mmio_sram, DT_GET_MEM_REGION)
};

/*
* 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 (dma_buf_in_non_cacheable_sram(
buf->buf, buf->len,
stm32_mem_regions,
sizeof(stm32_mem_regions) / sizeof(*stm32_mem_regions))) {
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 +750,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 +1049,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
76 changes: 76 additions & 0 deletions include/zephyr/drivers/dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#ifndef ZEPHYR_INCLUDE_DRIVERS_DMA_H_
#define ZEPHYR_INCLUDE_DRIVERS_DMA_H_

#include <string.h>

#include <zephyr/kernel.h>
#include <zephyr/device.h>

Expand Down Expand Up @@ -256,6 +258,50 @@ struct dma_context {
/* magic code to identify context content */
#define DMA_MAGIC 0x47494749

/* Inidicates that a ram region is nocache. */
/* TODO Obtained from zephyr,memory-region.yaml. This should be defined in
* just one place.
*/
#define MEM_NOCACHE_ATTR "RAM_NOCACHE"

/**
* @brief Container for MMIO SRAM information specified in devicetree
*
* This type contains pointers to the begin and end of the memory region and
* a string that specifies such region's properties.
*
*/
struct dma_mem_region {
const uint8_t *start;
const uint8_t *end;
const char *region_mpu;
};

/**
* @brief Can be used to initialize a @see{struct dma_mem_region}.
*
* @param node_id Node ID of the memory region declared in device tree from
* which the information will be obtained.
*
* @example
*
* struct dma_mem_region mem_r = {
* DT_GET_MEM_REGION(DT_NODELABEL(<node_label>))
* };
*
*/
#define DT_GET_MEM_REGION(node_id) \
{ \
.start = (const uint8_t *)DT_REG_ADDR(node_id), \
.end = \
(const uint8_t *)(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) \
) \
},

/**
* @cond INTERNAL_HIDDEN
*
Expand Down Expand Up @@ -684,6 +730,36 @@ static inline uint32_t dma_burst_index(uint32_t burst)
return find_msb_set(burst);
}

/**
* Return true if buf is in a nocache memory region
* among mem_regions.
*
* @param buf Buffer to be checked
* @param size Size of the buffer above
* @param mem_regions Memory regions agaisnt which buf will be checked.
* @param mem_regions_cnt Number of memory regions
*/
static inline bool dma_buf_in_non_cacheable_sram(
const uint8_t *buf,
size_t size,
struct dma_mem_region mem_regions[],
size_t mem_regions_cnt)
{
for (size_t i = 0; i < mem_regions_cnt; i++) {
const struct dma_mem_region *mem_reg = &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, MEM_NOCACHE_ATTR, sizeof(MEM_NOCACHE_ATTR)) == 0) {
return false;
}
}
return true;
}

/**
* Get the device tree property describing the buffer address alignment
*
Expand Down

0 comments on commit c119ee7

Please sign in to comment.