drivers: flash: nrf_qspi_nor: Add support for device power management

Deinitialize the nrfx_qspi driver for periods when the device
is suspended. For flash chips with "has-dpd" property set, when
suspending/resuming the device, issue also the enter/exit Deep
Power-down Mode command, respectively.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
diff --git a/drivers/flash/nrf_qspi_nor.c b/drivers/flash/nrf_qspi_nor.c
index e6ce773..18aa211 100644
--- a/drivers/flash/nrf_qspi_nor.c
+++ b/drivers/flash/nrf_qspi_nor.c
@@ -1034,6 +1034,110 @@
 #endif /* CONFIG_FLASH_JESD216_API */
 };
 
+#ifdef CONFIG_PM_DEVICE
+static int enter_dpd(const struct device *const dev)
+{
+	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
+		struct qspi_cmd cmd = {
+			.op_code = SPI_NOR_CMD_DPD,
+		};
+		uint32_t t_enter_dpd = DT_INST_PROP_OR(0, t_enter_dpd, 0);
+		int ret;
+
+		ret = qspi_send_cmd(dev, &cmd, false);
+		if (ret < 0) {
+			return ret;
+		}
+
+		if (t_enter_dpd) {
+			uint32_t t_enter_dpd_us =
+				ceiling_fraction(t_enter_dpd, NSEC_PER_USEC);
+
+			k_busy_wait(t_enter_dpd_us);
+		}
+	}
+
+	return 0;
+}
+
+static int exit_dpd(const struct device *const dev)
+{
+	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
+		struct qspi_cmd cmd = {
+			.op_code = SPI_NOR_CMD_RDPD,
+		};
+		uint32_t t_exit_dpd = DT_INST_PROP_OR(0, t_exit_dpd, 0);
+		int ret;
+
+		ret = qspi_send_cmd(dev, &cmd, false);
+		if (ret < 0) {
+			return ret;
+		}
+
+		if (t_exit_dpd) {
+			uint32_t t_exit_dpd_us =
+				ceiling_fraction(t_exit_dpd, NSEC_PER_USEC);
+
+			k_busy_wait(t_exit_dpd_us);
+		}
+	}
+
+	return 0;
+}
+
+static int qspi_nor_pm_action(const struct device *dev,
+			      enum pm_device_action action)
+{
+	struct qspi_nor_data *dev_data = dev->data;
+	const struct qspi_nor_config *dev_config = dev->config;
+	int ret;
+	nrfx_err_t err;
+
+	switch (action) {
+	case PM_DEVICE_ACTION_SUSPEND:
+		ret = ANOMALY_122_INIT(dev);
+		if (ret < 0) {
+			return ret;
+		}
+
+		if (nrfx_qspi_mem_busy_check() != NRFX_SUCCESS) {
+			return -EBUSY;
+		}
+
+		ret = enter_dpd(dev);
+		if (ret < 0) {
+			return ret;
+		}
+
+		nrfx_qspi_uninit();
+		break;
+
+	case PM_DEVICE_ACTION_RESUME:
+		err = nrfx_qspi_init(&dev_config->nrfx_cfg,
+				     qspi_handler,
+				     dev_data);
+		if (err != NRFX_SUCCESS) {
+			return -EIO;
+		}
+
+		ret = exit_dpd(dev);
+		if (ret < 0) {
+			return ret;
+		}
+
+		ANOMALY_122_UNINIT(dev);
+		break;
+
+	default:
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+#else
+#define qspi_nor_pm_action NULL
+#endif /* CONFIG_PM_DEVICE */
+
 static struct qspi_nor_data qspi_nor_dev_data = {
 #ifdef CONFIG_MULTITHREADING
 	.trans = Z_SEM_INITIALIZER(qspi_nor_dev_data.trans, 1, 1),
@@ -1087,7 +1191,7 @@
 	.id = DT_INST_PROP(0, jedec_id),
 };
 
-DEVICE_DT_INST_DEFINE(0, qspi_nor_init, NULL,
+DEVICE_DT_INST_DEFINE(0, qspi_nor_init, qspi_nor_pm_action,
 		      &qspi_nor_dev_data, &qspi_nor_dev_config,
 		      POST_KERNEL, CONFIG_NORDIC_QSPI_NOR_INIT_PRIORITY,
 		      &qspi_nor_api);