[PATCH v2] mtd: intel-dg: wake card on operations

Alexander Usyskin posted 1 patch 3 months, 2 weeks ago
There is a newer version of this series
drivers/mtd/devices/mtd_intel_dg.c | 70 +++++++++++++++++++++++++-----
1 file changed, 58 insertions(+), 12 deletions(-)
[PATCH v2] mtd: intel-dg: wake card on operations
Posted by Alexander Usyskin 3 months, 2 weeks ago
The Intel DG cards do not have separate power control for
persistent memory.
The memory is available when the whole card is awake.

Enable runtime PM in mtd driver to notify parent graphics driver
that whole card should be kept awake while nvm operations are
performed through this driver.

CC: Lucas De Marchi <lucas.demarchi@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
---

V2: Address review comments (Andrey S)

 drivers/mtd/devices/mtd_intel_dg.c | 70 +++++++++++++++++++++++++-----
 1 file changed, 58 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c
index b438ee5aacc3..d1916ec5e087 100644
--- a/drivers/mtd/devices/mtd_intel_dg.c
+++ b/drivers/mtd/devices/mtd_intel_dg.c
@@ -15,14 +15,18 @@
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/pm_runtime.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/sizes.h>
 #include <linux/types.h>
 
+#define INTEL_DG_NVM_RPM_TIMEOUT_MS 500
+
 struct intel_dg_nvm {
 	struct kref refcnt;
 	struct mtd_info mtd;
+	struct device *dev;
 	struct mutex lock; /* region access lock */
 	void __iomem *base;
 	void __iomem *base2;
@@ -421,6 +425,8 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
 	unsigned int i, n;
 	int ret;
 
+	nvm->dev = device;
+
 	/* clean error register, previous errors are ignored */
 	idg_nvm_error(nvm);
 
@@ -498,6 +504,7 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
 	size_t len;
 	u8 region;
 	u64 addr;
+	int ret;
 
 	if (WARN_ON(!nvm))
 		return -EINVAL;
@@ -512,20 +519,29 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
 	total_len = info->len;
 	addr = info->addr;
 
+	ret = pm_runtime_resume_and_get(nvm->dev);
+	if (ret < 0) {
+		dev_err(&mtd->dev, "rpm: get failed %d\n", ret);
+		return ret;
+	}
+
+	ret = 0;
 	guard(mutex)(&nvm->lock);
 
 	while (total_len > 0) {
 		if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) {
 			dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len);
 			info->fail_addr = addr;
-			return -ERANGE;
+			ret = -ERANGE;
+			break;
 		}
 
 		idx = idg_nvm_get_region(nvm, addr);
 		if (idx >= nvm->nregions) {
 			dev_err(&mtd->dev, "out of range");
 			info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
-			return -ERANGE;
+			ret = -ERANGE;
+			break;
 		}
 
 		from = addr - nvm->regions[idx].offset;
@@ -541,14 +557,16 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
 		if (bytes < 0) {
 			dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes);
 			info->fail_addr += nvm->regions[idx].offset;
-			return bytes;
+			ret = bytes;
+			break;
 		}
 
 		addr += len;
 		total_len -= len;
 	}
 
-	return 0;
+	pm_runtime_put_autosuspend(nvm->dev);
+	return ret;
 }
 
 static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -577,17 +595,24 @@ static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
 	if (len > nvm->regions[idx].size - from)
 		len = nvm->regions[idx].size - from;
 
+	ret = pm_runtime_resume_and_get(nvm->dev);
+	if (ret < 0) {
+		dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
+		return ret;
+	}
+
 	guard(mutex)(&nvm->lock);
 
 	ret = idg_read(nvm, region, from, len, buf);
 	if (ret < 0) {
 		dev_dbg(&mtd->dev, "read failed with %zd\n", ret);
-		return ret;
+	} else {
+		*retlen = ret;
+		ret = 0;
 	}
 
-	*retlen = ret;
-
-	return 0;
+	pm_runtime_put_autosuspend(nvm->dev);
+	return ret;
 }
 
 static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
@@ -616,17 +641,24 @@ static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
 	if (len > nvm->regions[idx].size - to)
 		len = nvm->regions[idx].size - to;
 
+	ret = pm_runtime_resume_and_get(nvm->dev);
+	if (ret < 0) {
+		dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
+		return ret;
+	}
+
 	guard(mutex)(&nvm->lock);
 
 	ret = idg_write(nvm, region, to, len, buf);
 	if (ret < 0) {
 		dev_dbg(&mtd->dev, "write failed with %zd\n", ret);
-		return ret;
+	} else {
+		*retlen = ret;
+		ret = 0;
 	}
 
-	*retlen = ret;
-
-	return 0;
+	pm_runtime_put_autosuspend(nvm->dev);
+	return ret;
 }
 
 static void intel_dg_nvm_release(struct kref *kref)
@@ -753,6 +785,17 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
 	}
 	nvm->nregions = n; /* in case where kasprintf fail */
 
+	devm_pm_runtime_enable(device);
+
+	pm_runtime_set_autosuspend_delay(device, INTEL_DG_NVM_RPM_TIMEOUT_MS);
+	pm_runtime_use_autosuspend(device);
+
+	ret = pm_runtime_resume_and_get(device);
+	if (ret < 0) {
+		dev_err(device, "rpm: get failed %d\n", ret);
+		goto err_norpm;
+	}
+
 	nvm->base = devm_ioremap_resource(device, &invm->bar);
 	if (IS_ERR(nvm->base)) {
 		ret = PTR_ERR(nvm->base);
@@ -781,9 +824,12 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
 
 	dev_set_drvdata(&aux_dev->dev, nvm);
 
+	pm_runtime_put(device);
 	return 0;
 
 err:
+	pm_runtime_put(device);
+err_norpm:
 	kref_put(&nvm->refcnt, intel_dg_nvm_release);
 	return ret;
 }
-- 
2.43.0
Re: [PATCH v2] mtd: intel-dg: wake card on operations
Posted by Miquel Raynal 3 months, 2 weeks ago
Hi Alexander,

On 21/10/2025 at 15:32:46 +03, Alexander Usyskin <alexander.usyskin@intel.com> wrote:

> The Intel DG cards do not have separate power control for
> persistent memory.
> The memory is available when the whole card is awake.
>
> Enable runtime PM in mtd driver to notify parent graphics driver
> that whole card should be kept awake while nvm operations are
> performed through this driver.
>
> CC: Lucas De Marchi <lucas.demarchi@intel.com>
> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> ---

I am curious to know why this now works, whereas in your previous
attempt (which had to be reverted) we had to do a lot more.

Thanks,
Miquèl
RE: [PATCH v2] mtd: intel-dg: wake card on operations
Posted by Usyskin, Alexander 3 months, 2 weeks ago
> Subject: Re: [PATCH v2] mtd: intel-dg: wake card on operations
> 
> Hi Alexander,
> 
> On 21/10/2025 at 15:32:46 +03, Alexander Usyskin
> <alexander.usyskin@intel.com> wrote:
> 
> > The Intel DG cards do not have separate power control for
> > persistent memory.
> > The memory is available when the whole card is awake.
> >
> > Enable runtime PM in mtd driver to notify parent graphics driver
> > that whole card should be kept awake while nvm operations are
> > performed through this driver.
> >
> > CC: Lucas De Marchi <lucas.demarchi@intel.com>
> > Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> > ---
> 
> I am curious to know why this now works, whereas in your previous
> attempt (which had to be reverted) we had to do a lot more.
> 

The reverted attempt was to always create mtd master device and use
normal device hierarchy in power management.
Here the power management manipulated directly on the parent device
without involving mtd master that may not be initialized.

- - 
Thanks,
Sasha


> Thanks,
> Miquèl
Re: [PATCH v2] mtd: intel-dg: wake card on operations
Posted by Andy Shevchenko 3 months, 2 weeks ago
On Tue, Oct 21, 2025 at 03:32:46PM +0300, Alexander Usyskin wrote:
> The Intel DG cards do not have separate power control for
> persistent memory.
> The memory is available when the whole card is awake.
> 
> Enable runtime PM in mtd driver to notify parent graphics driver
> that whole card should be kept awake while nvm operations are
> performed through this driver.

> CC: Lucas De Marchi <lucas.demarchi@intel.com>

It's possible to make less noise in the commit message by moving Cc:s to...

> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
> ---

...here. It will have the same effect on email, but commit message will be
cleaner.

> V2: Address review comments (Andrey S)

I think you meant Andy or Andriy here :-)

...

> +	devm_pm_runtime_enable(device);

I consider this as a wrong pattern of devm_*() usage. If one uses devm_*() they
should check for errors and act accordingly. (One way can be a printed warning,
but again, the devm_*() call inside can be implemented differently. It might
make device be enabled for a moment and fail due to upper layer issues, such as
memory allocation. In such case the error is different because it comes from
a different layer and you effectively ignore it without a reason. Hence either
check for errors, or drop devm_*() here.)

-- 
With Best Regards,
Andy Shevchenko