[PATCH v4 15/58] mtd: rawnand: tegra: retain open-handle state on removal

From: James Hilliard

Date: Fri Nov 13 2026 - 15:33:13 EST


Retain the chip and controller allocations until their final MTD
references are released. Disconnect the chip before suspending the
controller so retained handles cannot reach powered-down hardware. Leave
allocation release to the final core-owned put, including
registration-failure unwinding.

Release the probe-time child-node reference on every exit. Lifetime
ownership takes its own reference before publication.

Fixes: d7d9f8ec77fe ("mtd: rawnand: add NVIDIA Tegra NAND Flash controller driver")
Cc: Boris Brezillon <bbrezillon@xxxxxxxxxx>
Cc: Lucas Stach <dev@xxxxxxxxxx>
Cc: Dmitry Osipenko <digetx@xxxxxxxxx>
Cc: Jonathan Hunter <jonathanh@xxxxxxxxxx>
Cc: linux-tegra@xxxxxxxxxxxxxxx
Cc: Stefan Agner <stefan@xxxxxxxx>
Cc: Thierry Reding <thierry.reding@xxxxxxxxxx>
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/tegra_nand.c | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
index ef765fa21e72..4b9416ecbe43 100644
--- a/drivers/mtd/nand/raw/tegra_nand.c
+++ b/drivers/mtd/nand/raw/tegra_nand.c
@@ -1068,9 +1068,7 @@ static int tegra_nand_chips_init(struct device *dev,
struct tegra_nand_controller *ctrl)
{
struct device_node *np = dev->of_node;
- struct device_node *np_nand;
int nsels, nchips = of_get_child_count(np);
- struct tegra_nand_chip *nand;
struct mtd_info *mtd;
struct nand_chip *chip;
int ret;
@@ -1081,7 +1079,7 @@ static int tegra_nand_chips_init(struct device *dev,
return -EINVAL;
}

- np_nand = of_get_next_child(np, NULL);
+ struct device_node *np_nand __free(device_node) = of_get_next_child(np, NULL);

nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32));
if (nsels != 1) {
@@ -1096,7 +1094,7 @@ static int tegra_nand_chips_init(struct device *dev,
return ret;
}

- nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
+ struct tegra_nand_chip *nand __free(kfree) = kzalloc_obj(*nand);
if (!nand)
return -ENOMEM;

@@ -1116,7 +1114,6 @@ static int tegra_nand_chips_init(struct device *dev,
mtd = nand_to_mtd(chip);

mtd->parent_dev = dev;
- mtd->owner = THIS_MODULE;

nand_set_flash_node(chip, np_nand);

@@ -1130,11 +1127,18 @@ static int tegra_nand_chips_init(struct device *dev,
return ret;

mtd_ooblayout_ecc(mtd, 0, &nand->ecc);
+ ret = nand_set_lifetime(chip, container_of(chip, struct tegra_nand_chip, chip));
+ if (ret) {
+ nand_cleanup(chip);
+ return ret;
+ }
+ retain_and_null_ptr(nand);

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

@@ -1149,13 +1153,15 @@ static int tegra_nand_probe(struct platform_device *pdev)
struct tegra_nand_controller *ctrl;
int err = 0;

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

ctrl->dev = &pdev->dev;
platform_set_drvdata(pdev, ctrl);
- nand_controller_init(&ctrl->controller);
ctrl->controller.ops = &tegra_nand_controller_ops;

ctrl->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1235,13 +1241,14 @@ static void tegra_nand_remove(struct platform_device *pdev)

WARN_ON(mtd_device_unregister(mtd));

- nand_cleanup(chip);
+ nand_disconnect(chip);

writel_relaxed(0, ctrl->regs + IER);
devm_free_irq(ctrl->dev, ctrl->irq, ctrl);

pm_runtime_put_sync_suspend(ctrl->dev);
pm_runtime_force_suspend(ctrl->dev);
+ mtd_put_master(mtd);
}

static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)

--
2.53.0