[PATCH v4 11/58] mtd: rawnand: denali: retain chips across controller removal

From: James Hilliard

Date: Fri Nov 13 2026 - 15:41:18 EST


Both Denali frontends allocate chip and controller objects with
device-managed storage that disappears on unbind even when MTD
references remain. Give each chip a retained lifetime and a reference to
the controller, and keep the initial controller reference until IRQ and
mapped hardware resources have been released.

Unregister and disconnect each chip before disabling the controller.
Remove its list entry before dropping ownership, and use the same
release path for partially published probe failures. Denali chip
initialization now consumes its chip allocation on both success and
failure.

Run NAND cleanup during disconnection; the final core-owned put only
releases retained allocation and layout data.

Fixes: ce082596ae43 ("mtd/nand: Add Intel Moorestown/Denali NAND support")
Cc: David Woodhouse <David.Woodhouse@xxxxxxxxx>
Cc: Jason Roberts <jason.e.roberts@xxxxxxxxx>
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/denali.c | 26 ++++++++++++++++++--------
drivers/mtd/nand/raw/denali_dt.c | 13 ++++++++-----
drivers/mtd/nand/raw/denali_pci.c | 9 ++++++---
3 files changed, 32 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 9c07e4e25a27..098b00724bee 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -1176,6 +1176,7 @@ static const struct nand_controller_ops denali_controller_ops = {
.setup_interface = denali_setup_interface,
};

+/* Takes ownership of dchip, including on failure. */
int denali_chip_init(struct denali_controller *denali,
struct denali_chip *dchip)
{
@@ -1192,7 +1193,8 @@ int denali_chip_init(struct denali_controller *denali,

if (bank >= denali->nbanks) {
dev_err(denali->dev, "unsupported bank %d\n", bank);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}

for (j = 0; j < i; j++) {
@@ -1200,7 +1202,8 @@ int denali_chip_init(struct denali_controller *denali,
dev_err(denali->dev,
"bank %d is assigned twice in the same chip\n",
bank);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
}

@@ -1210,7 +1213,8 @@ int denali_chip_init(struct denali_controller *denali,
dev_err(denali->dev,
"bank %d is already used\n",
bank);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
}
}
@@ -1250,12 +1254,17 @@ int denali_chip_init(struct denali_controller *denali,

ret = nand_scan(chip, dchip->nsels);
if (ret)
- return ret;
+ goto free_chip;
+ ret = nand_set_lifetime(chip, dchip);
+ if (ret)
+ goto cleanup_nand;

ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto cleanup_nand;
+ nand_disconnect(chip);
+ mtd_put_master(mtd);
+ return ret;
}

list_add_tail(&dchip->node, &denali->chips);
@@ -1264,7 +1273,8 @@ int denali_chip_init(struct denali_controller *denali,

cleanup_nand:
nand_cleanup(chip);
-
+free_chip:
+ kfree(dchip);
return ret;
}
EXPORT_SYMBOL_GPL(denali_chip_init);
@@ -1274,7 +1284,6 @@ int denali_init(struct denali_controller *denali)
u32 features = ioread32(denali->reg + FEATURES);
int ret;

- nand_controller_init(&denali->controller);
denali->controller.ops = &denali_controller_ops;
init_completion(&denali->complete);
spin_lock_init(&denali->irq_lock);
@@ -1368,8 +1377,9 @@ void denali_remove(struct denali_controller *denali)
chip = &dchip->chip;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
- nand_cleanup(chip);
+ nand_disconnect(chip);
list_del(&dchip->node);
+ mtd_put_master(nand_to_mtd(chip));
}

denali_disable_irq(denali);
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index 8c822eae72e7..9abe607fc7e7 100644
--- a/drivers/mtd/nand/raw/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
+#include <linux/slab.h>

#include "denali.h"

@@ -81,7 +82,6 @@ MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
static int denali_dt_chip_init(struct denali_controller *denali,
struct device_node *chip_np)
{
- struct denali_chip *dchip;
u32 bank;
int nsels, i, ret;

@@ -89,8 +89,8 @@ static int denali_dt_chip_init(struct denali_controller *denali,
if (nsels < 0)
return nsels;

- dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
- GFP_KERNEL);
+ struct denali_chip *dchip __free(kfree) =
+ kzalloc(struct_size(dchip, sels, nsels), GFP_KERNEL);
if (!dchip)
return -ENOMEM;

@@ -106,7 +106,7 @@ static int denali_dt_chip_init(struct denali_controller *denali,
nand_set_flash_node(&dchip->chip, chip_np);
}

- return denali_chip_init(denali, dchip);
+ return denali_chip_init(denali, no_free_ptr(dchip));
}

static int denali_dt_probe(struct platform_device *pdev)
@@ -117,10 +117,13 @@ static int denali_dt_probe(struct platform_device *pdev)
struct denali_controller *denali;
int ret;

- dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
+ dt = kzalloc_obj(*dt);
if (!dt)
return -ENOMEM;
denali = &dt->controller;
+ ret = devm_nand_controller_init(dev, &denali->controller, dt);
+ if (ret)
+ return ret;

data = of_device_get_match_data(dev);
if (WARN_ON(!data))
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index f53df1b32dda..37ef153ad8d8 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/slab.h>

#include "denali.h"

@@ -35,9 +36,12 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
struct denali_chip *dchip;
int nsels, ret, i;

- denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
+ denali = kzalloc_obj(*denali);
if (!denali)
return -ENOMEM;
+ ret = devm_nand_controller_init(&dev->dev, &denali->controller, denali);
+ if (ret)
+ return ret;

ret = pcim_enable_device(dev);
if (ret) {
@@ -92,8 +96,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

nsels = denali->nbanks;

- dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
- GFP_KERNEL);
+ dchip = kzalloc(struct_size(dchip, sels, nsels), GFP_KERNEL);
if (!dchip) {
ret = -ENOMEM;
goto out_remove_denali;

--
2.53.0