[PATCH v2 1/2] mtd: mtd_intel_dg: add survivability partition

From: Alexander Usyskin

Date: Wed Sep 16 2026 - 10:12:36 EST


Add option to expose additional fixed-sized partition starting
from the beginning of storage.
Xe driver can request this partition exposure if firmware or hardware
have detected failure that may involve corrupted partition table.
Fixed-sized partition allows full storage re-write in this situation.

Signed-off-by: Alexander Usyskin <alexander.usyskin@xxxxxxxxx>
---
drivers/mtd/devices/mtd_intel_dg.c | 66 +++++++++++++++++++++++++++++---------
include/linux/intel_dg_nvm_aux.h | 1 +
2 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c
index a566e86eb5e3..962f30779c51 100644
--- a/drivers/mtd/devices/mtd_intel_dg.c
+++ b/drivers/mtd/devices/mtd_intel_dg.c
@@ -31,6 +31,7 @@ struct intel_dg_nvm {
void __iomem *base;
void __iomem *base2;
bool non_posted_erase;
+ bool survivability_enabled;

size_t size;
unsigned int nregions;
@@ -430,6 +431,16 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
/* clean error register, previous errors are ignored */
idg_nvm_error(nvm);

+
+ if (nvm->survivability_enabled) {
+ nvm->size = nvm->regions[0].offset + nvm->regions[0].size - 1;
+ dev_dbg(device, "Registered survivability region %s size=%lld\n",
+ nvm->regions[0].name,
+ nvm->regions[0].size);
+ n = 1;
+ goto out;
+ }
+
ret = idg_nvm_is_valid(nvm);
if (ret) {
dev_err(device, "The MEM is not valid %d\n", ret);
@@ -481,6 +492,7 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
n++;
}

+out:
nvm->non_posted_erase = non_posted_erase;

dev_dbg(device, "Registered %d regions\n", n);
@@ -753,15 +765,19 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,

device = &aux_dev->dev;

- /* count available regions */
- for (nregions = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
- if (invm->regions[i].name)
- nregions++;
- }
+ if (invm->survivability_size) {
+ nregions = 1;
+ } else {
+ /* count available regions */
+ for (nregions = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
+ if (invm->regions[i].name)
+ nregions++;
+ }

- if (!nregions) {
- dev_err(device, "no regions defined\n");
- return -ENODEV;
+ if (!nregions) {
+ dev_err(device, "no regions defined\n");
+ return -ENODEV;
+ }
}

nvm = kzalloc_flex(*nvm, regions, nregions);
@@ -771,21 +787,39 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
kref_init(&nvm->refcnt);
mutex_init(&nvm->lock);
nvm->nregions = nregions;
+ nvm->survivability_enabled = !!invm->survivability_size;

- for (n = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
- if (!invm->regions[i].name)
- continue;
-
+ if (invm->survivability_size) { /* survivability partition */
char *name = kasprintf(GFP_KERNEL, "%s.%s",
- dev_name(&aux_dev->dev), invm->regions[i].name);
+ dev_name(&aux_dev->dev), "DATA");
if (!name) {
ret = -ENOMEM;
goto err_norpm;
}

- nvm->regions[n].name = name;
- nvm->regions[n].id = i;
- n++;
+ nvm->regions[0].name = name;
+ nvm->regions[0].id = 0;
+ nvm->regions[0].offset = 0;
+ nvm->regions[0].size = invm->survivability_size;
+ nvm->regions[0].is_readable = true;
+ nvm->regions[0].is_writable = true;
+ n = 1;
+ } else {
+ for (n = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
+ if (!invm->regions[i].name)
+ continue;
+
+ char *name = kasprintf(GFP_KERNEL, "%s.%s",
+ dev_name(&aux_dev->dev), invm->regions[i].name);
+ if (!name) {
+ ret = -ENOMEM;
+ goto err_norpm;
+ }
+
+ nvm->regions[n].name = name;
+ nvm->regions[n].id = i;
+ n++;
+ }
}

ret = devm_pm_runtime_enable(device);
diff --git a/include/linux/intel_dg_nvm_aux.h b/include/linux/intel_dg_nvm_aux.h
index 625d46a6b96e..84b5b6e0c4ea 100644
--- a/include/linux/intel_dg_nvm_aux.h
+++ b/include/linux/intel_dg_nvm_aux.h
@@ -21,6 +21,7 @@ struct intel_dg_nvm_dev {
struct auxiliary_device aux_dev;
bool writable_override;
bool non_posted_erase;
+ size_t survivability_size;
struct resource bar;
struct resource bar2;
const struct intel_dg_nvm_region *regions;

--
2.53.0