-
Notifications
You must be signed in to change notification settings - Fork 7.4k
stm32: add memory-mapped mode for QUADSPI #72339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
998d5b0
bbbd842
ff7863f
d5ced65
582f702
d46b248
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,8 +12,10 @@ | |
#include <zephyr/kernel.h> | ||
#include <zephyr/toolchain.h> | ||
#include <zephyr/arch/common/ffs.h> | ||
#include <zephyr/sys/__assert.h> | ||
#include <zephyr/sys/util.h> | ||
#include <soc.h> | ||
#include <string.h> | ||
#include <zephyr/drivers/pinctrl.h> | ||
#include <zephyr/drivers/clock_control/stm32_clock_control.h> | ||
#include <zephyr/drivers/clock_control.h> | ||
|
@@ -385,6 +387,66 @@ static bool qspi_address_is_valid(const struct device *dev, off_t addr, | |
return (addr >= 0) && ((uint64_t)addr + (uint64_t)size <= flash_size); | ||
} | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
/* Must be called inside qspi_lock_thread(). */ | ||
static int stm32_qspi_set_memory_mapped(const struct device *dev) | ||
{ | ||
int ret; | ||
HAL_StatusTypeDef hal_ret; | ||
struct flash_stm32_qspi_data *dev_data = dev->data; | ||
|
||
QSPI_CommandTypeDef cmd = { | ||
.Instruction = SPI_NOR_CMD_READ, | ||
.Address = 0, | ||
.InstructionMode = QSPI_INSTRUCTION_1_LINE, | ||
.AddressMode = QSPI_ADDRESS_1_LINE, | ||
.DataMode = QSPI_DATA_1_LINE, | ||
}; | ||
|
||
qspi_set_address_size(dev, &cmd); | ||
if (IS_ENABLED(STM32_QSPI_USE_QUAD_IO)) { | ||
ret = qspi_prepare_quad_read(dev, &cmd); | ||
if (ret < 0) { | ||
return ret; | ||
} | ||
} | ||
|
||
QSPI_MemoryMappedTypeDef mem_mapped = { | ||
.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE, | ||
}; | ||
|
||
hal_ret = HAL_QSPI_MemoryMapped(&dev_data->hqspi, &cmd, &mem_mapped); | ||
if (hal_ret != 0) { | ||
LOG_ERR("%d: Failed to enable memory mapped", hal_ret); | ||
return -EIO; | ||
} | ||
arbrauns marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
LOG_DBG("MemoryMap mode enabled"); | ||
return 0; | ||
} | ||
|
||
static bool stm32_qspi_is_memory_mapped(const struct device *dev) | ||
{ | ||
struct flash_stm32_qspi_data *dev_data = dev->data; | ||
|
||
return READ_BIT(dev_data->hqspi.Instance->CCR, QUADSPI_CCR_FMODE) == QUADSPI_CCR_FMODE; | ||
} | ||
|
||
static int stm32_qspi_abort(const struct device *dev) | ||
{ | ||
struct flash_stm32_qspi_data *dev_data = dev->data; | ||
HAL_StatusTypeDef hal_ret; | ||
|
||
hal_ret = HAL_QSPI_Abort(&dev_data->hqspi); | ||
if (hal_ret != HAL_OK) { | ||
LOG_ERR("%d: QSPI abort failed", hal_ret); | ||
return -EIO; | ||
} | ||
|
||
return 0; | ||
} | ||
#endif | ||
|
||
static int flash_stm32_qspi_read(const struct device *dev, off_t addr, | ||
void *data, size_t size) | ||
{ | ||
|
@@ -401,6 +463,27 @@ static int flash_stm32_qspi_read(const struct device *dev, off_t addr, | |
return 0; | ||
} | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
qspi_lock_thread(dev); | ||
|
||
/* Do reads through memory-mapping instead of indirect */ | ||
if (!stm32_qspi_is_memory_mapped(dev)) { | ||
ret = stm32_qspi_set_memory_mapped(dev); | ||
if (ret != 0) { | ||
LOG_ERR("READ: failed to set memory mapped"); | ||
goto end; | ||
} | ||
} | ||
|
||
__ASSERT_NO_MSG(stm32_qspi_is_memory_mapped(dev)); | ||
|
||
uintptr_t mmap_addr = STM32_QSPI_BASE_ADDRESS + addr; | ||
|
||
LOG_DBG("Memory-mapped read from 0x%08lx, len %zu", mmap_addr, size); | ||
memcpy(data, (void *)mmap_addr, size); | ||
ret = 0; | ||
goto end; | ||
#else | ||
QSPI_CommandTypeDef cmd = { | ||
.Instruction = SPI_NOR_CMD_READ, | ||
.Address = addr, | ||
|
@@ -420,7 +503,10 @@ static int flash_stm32_qspi_read(const struct device *dev, off_t addr, | |
qspi_lock_thread(dev); | ||
|
||
ret = qspi_read_access(dev, &cmd, data, size); | ||
goto end; | ||
#endif | ||
|
||
end: | ||
qspi_unlock_thread(dev); | ||
|
||
return ret; | ||
|
@@ -482,6 +568,17 @@ static int flash_stm32_qspi_write(const struct device *dev, off_t addr, | |
|
||
qspi_lock_thread(dev); | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
if (stm32_qspi_is_memory_mapped(dev)) { | ||
/* Abort ongoing transfer to force CS high/BUSY deasserted */ | ||
ret = stm32_qspi_abort(dev); | ||
if (ret != 0) { | ||
LOG_ERR("Failed to abort memory-mapped access before write"); | ||
goto end; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion : There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That feels much less clear to me. For one it doesn't even mention that something went wrong, and it's also not the memory-mapping that's being aborted, just a latent read access, if any. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the suggestion was to put it after, once abort is completed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@arbrauns Can you check this comment ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that makes sense, but then that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's already a LOG_DBG on flash_stm32_qspi_read so, that would be coherent. |
||
} | ||
#endif | ||
|
||
while (size > 0) { | ||
size_t to_write = size; | ||
|
||
|
@@ -517,7 +614,9 @@ static int flash_stm32_qspi_write(const struct device *dev, off_t addr, | |
break; | ||
} | ||
} | ||
goto end; | ||
|
||
end: | ||
qspi_unlock_thread(dev); | ||
|
||
return ret; | ||
|
@@ -555,6 +654,17 @@ static int flash_stm32_qspi_erase(const struct device *dev, off_t addr, | |
qspi_set_address_size(dev, &cmd_erase); | ||
qspi_lock_thread(dev); | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
if (stm32_qspi_is_memory_mapped(dev)) { | ||
/* Abort ongoing transfer to force CS high/BUSY deasserted */ | ||
ret = stm32_qspi_abort(dev); | ||
if (ret != 0) { | ||
LOG_ERR("Failed to abort memory-mapped access before erase"); | ||
goto end; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion : |
||
} | ||
#endif | ||
|
||
while ((size > 0) && (ret == 0)) { | ||
cmd_erase.Address = addr; | ||
qspi_send_cmd(dev, &cmd_write_en); | ||
|
@@ -596,7 +706,9 @@ static int flash_stm32_qspi_erase(const struct device *dev, off_t addr, | |
} | ||
qspi_wait_until_ready(dev); | ||
} | ||
goto end; | ||
|
||
end: | ||
qspi_unlock_thread(dev); | ||
|
||
return ret; | ||
|
@@ -1367,9 +1479,20 @@ static int flash_stm32_qspi_init(const struct device *dev) | |
} | ||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */ | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
ret = stm32_qspi_set_memory_mapped(dev); | ||
if (ret != 0) { | ||
LOG_ERR("Failed to enable memory-mapped mode: %d", ret); | ||
return ret; | ||
} | ||
LOG_INF("Memory-mapped NOR quad-flash at 0x%lx (0x%x bytes)", | ||
(long)(STM32_QSPI_BASE_ADDRESS), | ||
dev_cfg->flash_size); | ||
#else | ||
LOG_INF("NOR quad-flash at 0x%lx (0x%x bytes)", | ||
(long)(STM32_QSPI_BASE_ADDRESS), | ||
dev_cfg->flash_size); | ||
#endif | ||
|
||
return 0; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
CONFIG_FLASH=y | ||
CONFIG_STM32_MEMMAP=y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to update board documentation to provide some details on how this could be used.