Commit ac483697 authored by Francois Ramu's avatar Francois Ramu Committed by Anas Nashif
Browse files

drivers: flash: stm32 xspi driver with DMA support



Add the commands to read and write to/from the xSPI with gpDMA
On the stm32h5 device, the one request for xspi instance

Signed-off-by: default avatarFrancois Ramu <francois.ramu@st.com>
parent be55b2c2
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@
# Copyright (c) 2024 STMicroelectronics
# SPDX-License-Identifier: Apache-2.0

DT_STM32_XSPI_1_HAS_DMA := $(dt_nodelabel_has_prop,xspi1,dmas)
DT_STM32_XSPI_2_HAS_DMA := $(dt_nodelabel_has_prop,xspi2,dmas)

config FLASH_STM32_XSPI
	bool "STM32 XSPI Flash driver"
	default y
@@ -13,6 +16,11 @@ config FLASH_STM32_XSPI
	select FLASH_JESD216
	select FLASH_PAGE_LAYOUT
	select FLASH_HAS_PAGE_LAYOUT

	select DMA if $(DT_STM32_XSPI_1_HAS_DMA) || $(DT_STM32_XSPI_2_HAS_DMA)
	select USE_STM32_HAL_DMA if $(DT_STM32_XSPI_1_HAS_DMA) || \
					$(DT_STM32_XSPI_2_HAS_DMA)
	select USE_STM32_HAL_DMA_EX if SOC_SERIES_STM32H5X && \
					($(DT_STM32_XSPI_1_HAS_DMA) || \
					 $(DT_STM32_XSPI_2_HAS_DMA))
	help
	  Enable XSPI-NOR support on the STM32 family of processors.
+170 −0
Original line number Diff line number Diff line
@@ -44,6 +44,14 @@ LOG_MODULE_REGISTER(flash_stm32_xspi, CONFIG_FLASH_LOG_LEVEL);

#define STM32_XSPI_DLYB_BYPASSED DT_PROP(STM32_XSPI_NODE, dlyb_bypass)

#define STM32_XSPI_USE_DMA DT_NODE_HAS_PROP(STM32_XSPI_NODE, dmas)

#if STM32_XSPI_USE_DMA
#include <zephyr/drivers/dma/dma_stm32.h>
#include <zephyr/drivers/dma.h>
#include <stm32_ll_dma.h>
#endif /* STM32_XSPI_USE_DMA */

#include "flash_stm32_xspi.h"

static inline void xspi_lock_thread(const struct device *dev)
@@ -97,7 +105,11 @@ static int xspi_read_access(const struct device *dev, XSPI_RegularCmdTypeDef *cm
		return -EIO;
	}

#if STM32_XSPI_USE_DMA
	hal_ret = HAL_XSPI_Receive_DMA(&dev_data->hxspi, data);
#else
	hal_ret = HAL_XSPI_Receive_IT(&dev_data->hxspi, data);
#endif

	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to read data", hal_ret);
@@ -135,7 +147,11 @@ static int xspi_write_access(const struct device *dev, XSPI_RegularCmdTypeDef *c
		return -EIO;
	}

#if STM32_XSPI_USE_DMA
	hal_ret = HAL_XSPI_Transmit_DMA(&dev_data->hxspi, (uint8_t *)data);
#else
	hal_ret = HAL_XSPI_Transmit_IT(&dev_data->hxspi, (uint8_t *)data);
#endif

	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to write data", hal_ret);
@@ -1220,6 +1236,24 @@ __weak HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma)
}
#endif /* !CONFIG_SOC_SERIES_STM32H7X */

/* This function is executed in the interrupt context */
#if STM32_XSPI_USE_DMA
static void xspi_dma_callback(const struct device *dev, void *arg,
			 uint32_t channel, int status)
{
	DMA_HandleTypeDef *hdma = arg;

	ARG_UNUSED(dev);

	if (status < 0) {
		LOG_ERR("DMA callback error with channel %d.", channel);
	}

	HAL_DMA_IRQHandler(hdma);
}
#endif


/*
 * Transfer Error callback.
 */
@@ -1707,6 +1741,85 @@ static int spi_nor_process_bfp(const struct device *dev,
	return 0;
}

#if STM32_XSPI_USE_DMA
static int flash_stm32_xspi_dma_init(DMA_HandleTypeDef *hdma, struct stream *dma_stream)
{
	int ret;
	/*
	 * DMA configuration
	 * Due to use of XSPI HAL API in current driver,
	 * both HAL and Zephyr DMA drivers should be configured.
	 * The required configuration for Zephyr DMA driver should only provide
	 * the minimum information to inform the DMA slot will be in used and
	 * how to route callbacks.
	 */

	if (!device_is_ready(dma_stream->dev)) {
		LOG_ERR("DMA %s device not ready", dma_stream->dev->name);
		return -ENODEV;
	}
	/* Proceed to the minimum Zephyr DMA driver init of the channel */
	dma_stream->cfg.user_data = hdma;
	/* HACK: This field is used to inform driver that it is overridden */
	dma_stream->cfg.linked_channel = STM32_DMA_HAL_OVERRIDE;
	/* Because of the STREAM OFFSET, the DMA channel given here is from 1 - 8 */
	ret = dma_config(dma_stream->dev,
			 (dma_stream->channel + STM32_DMA_STREAM_OFFSET), &dma_stream->cfg);
	if (ret != 0) {
		LOG_ERR("Failed to configure DMA channel %d",
			dma_stream->channel + STM32_DMA_STREAM_OFFSET);
		return ret;
	}

	/* Proceed to the HAL DMA driver init */
	if (dma_stream->cfg.source_data_size != dma_stream->cfg.dest_data_size) {
		LOG_ERR("DMA Source and destination data sizes not aligned");
		return -EINVAL;
	}

	hdma->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; /* Fixed value */
	hdma->Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; /* Fixed value */
	hdma->Init.SrcInc = (dma_stream->src_addr_increment)
		? DMA_SINC_INCREMENTED
		: DMA_SINC_FIXED;
	hdma->Init.DestInc = (dma_stream->dst_addr_increment)
		? DMA_DINC_INCREMENTED
		: DMA_DINC_FIXED;
	hdma->Init.SrcBurstLength = 4;
	hdma->Init.DestBurstLength = 4;
	hdma->Init.Priority = table_priority[dma_stream->cfg.channel_priority];
	hdma->Init.Direction = table_direction[dma_stream->cfg.channel_direction];
	hdma->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_SRC_ALLOCATED_PORT1;
	hdma->Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
	hdma->Init.Mode = DMA_NORMAL;
	hdma->Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
	hdma->Init.Request = dma_stream->cfg.dma_slot;

	/*
	 * HAL expects a valid DMA channel (not DMAMUX).
	 * The channel is from 0 to 7 because of the STM32_DMA_STREAM_OFFSET
	 * in the dma_stm32 driver
	 */
	hdma->Instance = LL_DMA_GET_CHANNEL_INSTANCE(dma_stream->reg,
						    dma_stream->channel);

	/* Initialize DMA HAL */
	if (HAL_DMA_Init(hdma) != HAL_OK) {
		LOG_ERR("XSPI DMA Init failed");
		return -EIO;
	}

	if (HAL_DMA_ConfigChannelAttributes(hdma, DMA_CHANNEL_NPRIV) != HAL_OK) {
		LOG_ERR("XSPI DMA Init failed");
		return -EIO;
	}

	LOG_DBG("XSPI with DMA transfer");
	return 0;
}
#endif /* STM32_XSPI_USE_DMA */


static int flash_stm32_xspi_init(const struct device *dev)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
@@ -1859,6 +1972,28 @@ static int flash_stm32_xspi_init(const struct device *dev)

#endif /* DLYB_ */

#if STM32_XSPI_USE_DMA
	/* Configure and enable the DMA channels after XSPI config */
	static DMA_HandleTypeDef hdma_tx;
	static DMA_HandleTypeDef hdma_rx;

	if (flash_stm32_xspi_dma_init(&hdma_tx, &dev_data->dma_tx) != 0) {
		LOG_ERR("XSPI DMA Tx init failed");
		return -EIO;
	}

	/* The dma_tx handle is hold by the dma_stream.cfg.user_data */
	__HAL_LINKDMA(&dev_data->hxspi, hdmatx, hdma_tx);

	if (flash_stm32_xspi_dma_init(&hdma_rx, &dev_data->dma_rx) != 0) {
		LOG_ERR("XSPI DMA Rx init failed");
		return -EIO;
	}

	/* The dma_rx handle is hold by the dma_stream.cfg.user_data */
	__HAL_LINKDMA(&dev_data->hxspi, hdmarx, hdma_rx);

#endif /* CONFIG_USE_STM32_HAL_DMA */
	/* Initialize semaphores */
	k_sem_init(&dev_data->sem, 1, 1);
	k_sem_init(&dev_data->sync, 0, 1);
@@ -2004,6 +2139,39 @@ static int flash_stm32_xspi_init(const struct device *dev)
	return 0;
}


#if STM32_XSPI_USE_DMA
#define DMA_CHANNEL_CONFIG(node, dir)						\
		DT_DMAS_CELL_BY_NAME(node, dir, channel_config)

#define XSPI_DMA_CHANNEL_INIT(node, dir, dir_cap, src_dev, dest_dev)		\
	.dev = DEVICE_DT_GET(DT_DMAS_CTLR(node)),				\
	.channel = DT_DMAS_CELL_BY_NAME(node, dir, channel),			\
	.reg = (DMA_TypeDef *)DT_REG_ADDR(					\
				   DT_PHANDLE_BY_NAME(node, dmas, dir)),	\
	.cfg = {								\
		.dma_slot = DT_DMAS_CELL_BY_NAME(node, dir, slot),		\
		.channel_direction = STM32_DMA_CONFIG_DIRECTION(		\
					DMA_CHANNEL_CONFIG(node, dir)),	\
		.channel_priority = STM32_DMA_CONFIG_PRIORITY(			\
					DMA_CHANNEL_CONFIG(node, dir)),		\
		.dma_callback = xspi_dma_callback,				\
	},									\
	.src_addr_increment = STM32_DMA_CONFIG_##src_dev##_ADDR_INC(		\
				DMA_CHANNEL_CONFIG(node, dir)),			\
	.dst_addr_increment = STM32_DMA_CONFIG_##dest_dev##_ADDR_INC(		\
				DMA_CHANNEL_CONFIG(node, dir)),

#define XSPI_DMA_CHANNEL(node, dir, DIR, src, dest)				\
	.dma_##dir = {								\
		COND_CODE_1(DT_DMAS_HAS_NAME(node, dir),			\
			(XSPI_DMA_CHANNEL_INIT(node, dir, DIR, src, dest)),	\
			(NULL))							\
		},
#else
#define XSPI_DMA_CHANNEL(node, dir, DIR, src, dest)
#endif /* CONFIG_USE_STM32_HAL_DMA */

#define XSPI_FLASH_MODULE(drv_id, flash_id)				\
		(DT_DRV_INST(drv_id), xspi_nor_flash_##flash_id)

@@ -2065,6 +2233,8 @@ static struct flash_stm32_xspi_data flash_stm32_xspi_dev_data = {
#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_ospi_nor), jedec_id)
	.jedec_id = DT_INST_PROP(0, jedec_id),
#endif /* jedec_id */
	XSPI_DMA_CHANNEL(STM32_XSPI_NODE, tx, TX, MEMORY, PERIPHERAL)
	XSPI_DMA_CHANNEL(STM32_XSPI_NODE, rx, RX, PERIPHERAL, MEMORY)
};

static void flash_stm32_xspi_irq_config_func(const struct device *dev)
+31 −0
Original line number Diff line number Diff line
@@ -37,6 +37,33 @@
/* used as default value for DTS writeoc */
#define SPI_NOR_WRITEOC_NONE 0xFF

#if STM32_XSPI_USE_DMA
/* Lookup table to set dma priority from the DTS */
static const uint32_t table_priority[] = {
	DMA_LOW_PRIORITY_LOW_WEIGHT,
	DMA_LOW_PRIORITY_MID_WEIGHT,
	DMA_LOW_PRIORITY_HIGH_WEIGHT,
	DMA_HIGH_PRIORITY,
};

/* Lookup table to set dma channel direction from the DTS */
static const uint32_t table_direction[] = {
	DMA_MEMORY_TO_MEMORY,
	DMA_MEMORY_TO_PERIPH,
	DMA_PERIPH_TO_MEMORY,
};

struct stream {
	DMA_TypeDef *reg;
	const struct device *dev;
	uint32_t channel;
	struct dma_config cfg;
	uint8_t priority;
	bool src_addr_increment;
	bool dst_addr_increment;
};
#endif /* STM32_XSPI_USE_DMA */

typedef void (*irq_config_func_t)(const struct device *dev);

struct flash_stm32_xspi_config {
@@ -77,6 +104,10 @@ struct flash_stm32_xspi_data {
	uint8_t jedec_id[JESD216_READ_ID_LEN];
#endif /* CONFIG_FLASH_JESD216_API */
	int cmd_status;
#if STM32_XSPI_USE_DMA
	struct stream dma_tx;
	struct stream dma_rx;
#endif /* STM32_XSPI_USE_DMA */
};

#endif /* ZEPHYR_DRIVERS_FLASH_XSPI_STM32_H_ */