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

From: James Hilliard

Date: Tue Nov 10 2026 - 02:47:00 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
metadata cleanup to the final 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 | 39 +++++++++++++++++++++++++++++++--------
1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
index 1104d75b6cbe..5130bee46d60 100644
--- a/drivers/mtd/nand/raw/tegra_nand.c
+++ b/drivers/mtd/nand/raw/tegra_nand.c
@@ -1064,11 +1064,16 @@ static const struct nand_controller_ops tegra_nand_controller_ops = {
.setup_interface = tegra_nand_setup_interface,
};

+static void tegra_nand_chip_release(struct nand_chip *chip)
+{
+ kfree(container_of(chip, struct tegra_nand_chip, chip));
+}
+
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;
+ struct device_node *np_nand __free(device_node) = NULL;
int nsels, nchips = of_get_child_count(np);
struct tegra_nand_chip *nand;
struct mtd_info *mtd;
@@ -1096,7 +1101,7 @@ static int tegra_nand_chips_init(struct device *dev,
return ret;
}

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

@@ -1107,7 +1112,7 @@ static int tegra_nand_chips_init(struct device *dev,
if (IS_ERR(nand->wp_gpio)) {
ret = PTR_ERR(nand->wp_gpio);
dev_err(dev, "Failed to request WP GPIO: %d\n", ret);
- return ret;
+ goto free_chip;
}

chip = &nand->chip;
@@ -1127,20 +1132,35 @@ static int tegra_nand_chips_init(struct device *dev,

ret = nand_scan(chip, 1);
if (ret)
- return ret;
+ goto free_chip;

mtd_ooblayout_ecc(mtd, 0, &nand->ecc);
+ ret = nand_set_release(chip, tegra_nand_chip_release);
+ if (ret) {
+ nand_cleanup(chip);
+ goto free_chip;
+ }

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;
}

ctrl->chip = chip;

return 0;
+
+free_chip:
+ kfree(nand);
+ return ret;
+}
+
+static void tegra_nand_release(struct nand_controller *controller)
+{
+ kfree(container_of(controller, struct tegra_nand_controller, controller));
}

static int tegra_nand_probe(struct platform_device *pdev)
@@ -1149,13 +1169,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, tegra_nand_release);
+ 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 +1257,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