-
Notifications
You must be signed in to change notification settings - Fork 7.8k
drivers: flash_stm32_ospi: enable basic support for PM #94939
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -20,6 +20,9 @@ | |
#include <zephyr/dt-bindings/flash_controller/ospi.h> | ||
#include <zephyr/drivers/gpio.h> | ||
#include <zephyr/irq.h> | ||
#include <zephyr/pm/device.h> | ||
#include <zephyr/pm/device_runtime.h> | ||
#include <zephyr/pm/policy.h> | ||
|
||
#include "spi_nor.h" | ||
#include "jesd216.h" | ||
|
@@ -180,8 +183,30 @@ struct flash_stm32_ospi_data { | |
#if STM32_OSPI_USE_DMA | ||
struct stream dma; | ||
#endif /* STM32_OSPI_USE_DMA */ | ||
#if CONFIG_FLASH_STM32_ERRATUM_U5_STOP2_3_CORRUPT_READ | ||
bool errata_u59_2_5_13; | ||
#endif | ||
}; | ||
|
||
|
||
static inline void ospi_pm_get(const struct device *dev) | ||
{ | ||
#ifdef CONFIG_PM_DEVICE_RUNTIME | ||
(void)pm_device_runtime_get(dev); | ||
#endif | ||
|
||
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); | ||
} | ||
|
||
static inline void ospi_pm_put(const struct device *dev) | ||
{ | ||
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES); | ||
|
||
#ifdef CONFIG_PM_DEVICE_RUNTIME | ||
(void)pm_device_runtime_put(dev); | ||
#endif | ||
} | ||
|
||
static inline void ospi_lock_thread(const struct device *dev) | ||
{ | ||
struct flash_stm32_ospi_data *dev_data = dev->data; | ||
|
@@ -222,6 +247,18 @@ static int ospi_read_access(const struct device *dev, OSPI_RegularCmdTypeDef *cm | |
struct flash_stm32_ospi_data *dev_data = dev->data; | ||
HAL_StatusTypeDef hal_ret; | ||
|
||
#if CONFIG_FLASH_STM32_ERRATUM_U5_STOP2_3_CORRUPT_READ | ||
int err; | ||
|
||
if (dev_data->errata_u59_2_5_13) { | ||
dev_data->errata_u59_2_5_13 = false; | ||
err = ospi_read_access(dev, cmd, data, 1); | ||
if (err) { | ||
return err; | ||
} | ||
} | ||
Comment on lines
+253
to
+259
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. Can't we just trigger a dummy read straight from the PM callback? 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. what if the user only intends to write for a certain time? 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. The user is going to read at some point, and my understanding of the erratum is that the first read after Stop 2/3 exit will return incorrect data regardless of what has been done since Stop 2/3 exit (i.e., you'll always pay for the dummy read at some point) If we could start an asynchronous read whose result is discarded, it would be better, but I don't think a synchronous one-byte read will add much latency to resume path (this is a wet finger guess - please prove me wrong if I am) 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.
just that we talk about the same thing. Your idea was to do the read in
not necessarily. if you intent to simply store data for a certain time, there's no need to read before write after each wakeup. |
||
#endif | ||
|
||
LOG_DBG("Instruction 0x%x", cmd->Instruction); | ||
|
||
cmd->NbData = size; | ||
|
@@ -1197,6 +1234,8 @@ static int flash_stm32_ospi_erase(const struct device *dev, off_t addr, | |
.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, | ||
}; | ||
|
||
ospi_pm_get(dev); | ||
|
||
if (stm32_ospi_mem_ready(dev_data, | ||
dev_cfg->data_mode, dev_cfg->data_rate) != 0) { | ||
LOG_ERR("Erase failed : flash busy"); | ||
|
@@ -1311,6 +1350,8 @@ static int flash_stm32_ospi_erase(const struct device *dev, off_t addr, | |
goto end_erase; | ||
|
||
end_erase: | ||
ospi_pm_put(dev); | ||
|
||
ospi_unlock_thread(dev); | ||
|
||
return ret; | ||
|
@@ -1413,8 +1454,12 @@ static int flash_stm32_ospi_read(const struct device *dev, off_t addr, | |
LOG_DBG("OSPI: read %zu data", size); | ||
ospi_lock_thread(dev); | ||
|
||
ospi_pm_get(dev); | ||
|
||
ret = ospi_read_access(dev, &cmd, data, size); | ||
|
||
ospi_pm_put(dev); | ||
|
||
ospi_unlock_thread(dev); | ||
|
||
#endif /* CONFIG_STM32_MEMMAP */ | ||
|
@@ -1446,6 +1491,8 @@ static int flash_stm32_ospi_write(const struct device *dev, off_t addr, | |
|
||
ospi_lock_thread(dev); | ||
|
||
ospi_pm_get(dev); | ||
|
||
#ifdef CONFIG_STM32_MEMMAP | ||
if (stm32_ospi_is_memorymap(dev)) { | ||
/* Abort ongoing transfer to force CS high/BUSY deasserted */ | ||
|
@@ -1505,6 +1552,8 @@ static int flash_stm32_ospi_write(const struct device *dev, off_t addr, | |
ret = stm32_ospi_mem_ready(dev_data, | ||
dev_cfg->data_mode, dev_cfg->data_rate); | ||
if (ret != 0) { | ||
ospi_pm_put(dev); | ||
|
||
ospi_unlock_thread(dev); | ||
LOG_ERR("OSPI: write not ready"); | ||
return -EIO; | ||
|
@@ -1552,6 +1601,8 @@ static int flash_stm32_ospi_write(const struct device *dev, off_t addr, | |
goto end_write; | ||
|
||
end_write: | ||
ospi_pm_put(dev); | ||
|
||
ospi_unlock_thread(dev); | ||
|
||
return ret; | ||
|
@@ -2160,6 +2211,49 @@ static int spi_nor_process_bfp(const struct device *dev, | |
return 0; | ||
} | ||
|
||
#ifdef CONFIG_PM_DEVICE | ||
|
||
static int flash_stm32_ospi_suspend(const struct device *dev) | ||
{ | ||
const struct flash_stm32_ospi_config *dev_cfg = dev->config; | ||
|
||
#if DT_CLOCKS_HAS_NAME(STM32_OSPI_NODE, ospi_mgr) | ||
if (clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken_mgr) != 0) { | ||
LOG_ERR("Could not disable OSPI Manager clock"); | ||
return -EIO; | ||
} | ||
#endif | ||
if (clock_control_off(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken) != 0) { | ||
LOG_ERR("Could not enable OSPI clock"); | ||
return -EIO; | ||
} | ||
return 0; | ||
} | ||
|
||
#endif | ||
|
||
static int flash_stm32_ospi_activate(const struct device *dev) | ||
{ | ||
const struct flash_stm32_ospi_config *dev_cfg = dev->config; | ||
|
||
/* Clock configuration */ | ||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken) != 0) { | ||
LOG_ERR("Could not enable OSPI clock"); | ||
return -EIO; | ||
} | ||
#if DT_CLOCKS_HAS_NAME(STM32_OSPI_NODE, ospi_mgr) | ||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken_mgr) != 0) { | ||
LOG_ERR("Could not enable OSPI Manager clock"); | ||
return -EIO; | ||
} | ||
#endif | ||
return 0; | ||
} | ||
|
||
static int flash_stm32_ospi_init(const struct device *dev) | ||
{ | ||
const struct flash_stm32_ospi_config *dev_cfg = dev->config; | ||
|
@@ -2289,12 +2383,12 @@ static int flash_stm32_ospi_init(const struct device *dev) | |
|
||
#endif /* STM32_OSPI_USE_DMA */ | ||
|
||
/* Clock configuration */ | ||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken) != 0) { | ||
LOG_ERR("Could not enable OSPI clock"); | ||
return -EIO; | ||
ret = flash_stm32_ospi_activate(dev); | ||
if (ret < 0) { | ||
LOG_ERR("OSPI clock activation failed (%d)", ret); | ||
return ret; | ||
} | ||
|
||
/* Alternate clock config for peripheral if any */ | ||
#if DT_CLOCKS_HAS_NAME(STM32_OSPI_NODE, ospi_ker) | ||
if (clock_control_configure(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
|
@@ -2317,13 +2411,6 @@ static int flash_stm32_ospi_init(const struct device *dev) | |
return -EIO; | ||
} | ||
#endif | ||
#if DT_CLOCKS_HAS_NAME(STM32_OSPI_NODE, ospi_mgr) | ||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), | ||
(clock_control_subsys_t) &dev_cfg->pclken_mgr) != 0) { | ||
LOG_ERR("Could not enable OSPI Manager clock"); | ||
return -EIO; | ||
} | ||
#endif | ||
|
||
for (; prescaler <= STM32_OSPI_CLOCK_PRESCALER_MAX; prescaler++) { | ||
uint32_t clk = STM32_OSPI_CLOCK_COMPUTE(ahb_clock_freq, prescaler); | ||
|
@@ -2593,9 +2680,40 @@ static int flash_stm32_ospi_init(const struct device *dev) | |
dev_cfg->flash_size); | ||
#endif /* CONFIG_STM32_MEMMAP */ | ||
|
||
#ifdef CONFIG_PM_DEVICE_RUNTIME | ||
(void)pm_device_runtime_enable(dev); | ||
#endif | ||
|
||
return 0; | ||
} | ||
|
||
#ifdef CONFIG_PM_DEVICE | ||
|
||
static int flash_stm32_ospi_pm_action(const struct device *dev, enum pm_device_action action) | ||
{ | ||
struct flash_stm32_ospi_data *dev_data = dev->data; | ||
|
||
int err; | ||
|
||
switch (action) { | ||
case PM_DEVICE_ACTION_RESUME: | ||
#if CONFIG_FLASH_STM32_ERRATUM_U5_STOP2_3_CORRUPT_READ | ||
dev_data->errata_u59_2_5_13 = true; | ||
#endif | ||
err = flash_stm32_ospi_activate(dev); | ||
break; | ||
case PM_DEVICE_ACTION_SUSPEND: | ||
err = flash_stm32_ospi_suspend(dev); | ||
break; | ||
default: | ||
return -ENOTSUP; | ||
} | ||
|
||
return err; | ||
} | ||
|
||
#endif | ||
|
||
#if STM32_OSPI_USE_DMA | ||
#define DMA_CHANNEL_CONFIG(node, dir) \ | ||
DT_DMAS_CELL_BY_NAME(node, dir, channel_config) | ||
|
@@ -2692,7 +2810,10 @@ static struct flash_stm32_ospi_data flash_stm32_ospi_dev_data = { | |
OSPI_DMA_CHANNEL(STM32_OSPI_NODE, tx_rx) | ||
}; | ||
|
||
DEVICE_DT_INST_DEFINE(0, &flash_stm32_ospi_init, NULL, | ||
PM_DEVICE_DT_INST_DEFINE(0, flash_stm32_ospi_pm_action); | ||
|
||
|
||
DEVICE_DT_INST_DEFINE(0, &flash_stm32_ospi_init, PM_DEVICE_DT_INST_GET(0), | ||
&flash_stm32_ospi_dev_data, &flash_stm32_ospi_cfg, | ||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, | ||
&flash_stm32_ospi_driver_api); | ||
|
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.
@FRASTM can you confirm this is fine ?
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.
according to the errata sheet, it is only needed after exiting stop 2 or 3. but then we need a way to get notified about that, which AFAIK may be possible with PM hooks. should we go that way?