flash: spi_nor: automatically run device runtime PM

Automatically handle device runtime PM for all flash API calls.
Asynchronously release the device after a small delay to minimise power
state transitions under multiple sequential API calls (e.g. NVS).

Signed-off-by: Jordan Yates <jordan@embeint.com>
This commit is contained in:
Jordan Yates 2024-05-24 10:36:19 +10:00 committed by Anas Nashif
commit 986fd88416
3 changed files with 63 additions and 0 deletions

View file

@ -196,6 +196,11 @@ Other Subsystems
Flash map Flash map
========= =========
* ``CONFIG_SPI_NOR_IDLE_IN_DPD`` has been removed from the :kconfig:option:`CONFIG_SPI_NOR`
driver. An enhanced version of this functionality can be obtained by enabling
:ref:`pm-device-runtime` on the device (Tunable with
:kconfig:option:`CONFIG_SPI_NOR_ACTIVE_DWELL_MS`).
hawkBit hawkBit
======= =======

View file

@ -78,4 +78,15 @@ config SPI_NOR_FLASH_LAYOUT_PAGE_SIZE
(32768), the sector size (4096), or any non-zero multiple of the (32768), the sector size (4096), or any non-zero multiple of the
sector size. sector size.
config SPI_NOR_ACTIVE_DWELL_MS
int "Dwell period (ms) after last use to stay in active mode"
depends on PM_DEVICE_RUNTIME
default 10
help
Flash accesses commonly occur in bursts, where entering and exiting DPD
mode between each access adds significantly to the total operation time.
This option controls how long to remain in active mode after each API
call, eliminating the active->idle->active transition sequence if another
transaction occurs before the dwell period expires.
endif # SPI_NOR endif # SPI_NOR

View file

@ -19,6 +19,7 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/sys_clock.h> #include <zephyr/sys_clock.h>
#include <zephyr/pm/device.h> #include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include "spi_nor.h" #include "spi_nor.h"
#include "jesd216.h" #include "jesd216.h"
@ -65,6 +66,12 @@ LOG_MODULE_REGISTER(spi_nor, CONFIG_FLASH_LOG_LEVEL);
#define ANY_INST_HAS_WP_GPIOS ANY_INST_HAS_PROP(wp_gpios) #define ANY_INST_HAS_WP_GPIOS ANY_INST_HAS_PROP(wp_gpios)
#define ANY_INST_HAS_HOLD_GPIOS ANY_INST_HAS_PROP(hold_gpios) #define ANY_INST_HAS_HOLD_GPIOS ANY_INST_HAS_PROP(hold_gpios)
#ifdef CONFIG_SPI_NOR_ACTIVE_DWELL_MS
#define ACTIVE_DWELL_MS CONFIG_SPI_NOR_ACTIVE_DWELL_MS
#else
#define ACTIVE_DWELL_MS 0
#endif
#define DEV_CFG(_dev_) ((const struct spi_nor_config * const) (_dev_)->config) #define DEV_CFG(_dev_) ((const struct spi_nor_config * const) (_dev_)->config)
/* MXICY Related defines*/ /* MXICY Related defines*/
@ -762,11 +769,19 @@ static int spi_nor_read(const struct device *dev, off_t addr, void *dest,
return -EINVAL; return -EINVAL;
} }
/* Ensure flash is powered before read */
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size); ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size);
release_device(dev); release_device(dev);
/* Release flash power requirement */
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }
@ -779,6 +794,10 @@ static int flash_spi_nor_ex_op(const struct device *dev, uint16_t code,
ARG_UNUSED(in); ARG_UNUSED(in);
ARG_UNUSED(out); ARG_UNUSED(out);
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
switch (code) { switch (code) {
@ -794,6 +813,7 @@ static int flash_spi_nor_ex_op(const struct device *dev, uint16_t code,
} }
release_device(dev); release_device(dev);
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }
#endif #endif
@ -811,6 +831,11 @@ static int spi_nor_write(const struct device *dev, off_t addr,
return -EINVAL; return -EINVAL;
} }
/* Ensure flash is powered before write */
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
ret = spi_nor_write_protection_set(dev, false); ret = spi_nor_write_protection_set(dev, false);
if (ret == 0) { if (ret == 0) {
@ -858,6 +883,9 @@ static int spi_nor_write(const struct device *dev, off_t addr,
} }
release_device(dev); release_device(dev);
/* Release flash power requirement */
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }
@ -881,6 +909,11 @@ static int spi_nor_erase(const struct device *dev, off_t addr, size_t size)
return -EINVAL; return -EINVAL;
} }
/* Ensure flash is powered before erase */
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
ret = spi_nor_write_protection_set(dev, false); ret = spi_nor_write_protection_set(dev, false);
@ -936,6 +969,8 @@ static int spi_nor_erase(const struct device *dev, off_t addr, size_t size)
release_device(dev); release_device(dev);
/* Release flash power requirement */
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }
@ -977,12 +1012,18 @@ static int spi_nor_write_protection_set(const struct device *dev,
static int spi_nor_sfdp_read(const struct device *dev, off_t addr, static int spi_nor_sfdp_read(const struct device *dev, off_t addr,
void *dest, size_t size) void *dest, size_t size)
{ {
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
int ret = read_sfdp(dev, addr, dest, size); int ret = read_sfdp(dev, addr, dest, size);
release_device(dev); release_device(dev);
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }
@ -995,12 +1036,18 @@ static int spi_nor_read_jedec_id(const struct device *dev,
return -EINVAL; return -EINVAL;
} }
if (pm_device_runtime_get(dev) < 0) {
return -EIO;
}
acquire_device(dev); acquire_device(dev);
int ret = spi_nor_cmd_read(dev, SPI_NOR_CMD_RDID, id, SPI_NOR_MAX_ID_LEN); int ret = spi_nor_cmd_read(dev, SPI_NOR_CMD_RDID, id, SPI_NOR_MAX_ID_LEN);
release_device(dev); release_device(dev);
(void)pm_device_runtime_put_async(dev, K_MSEC(ACTIVE_DWELL_MS));
return ret; return ret;
} }