[PATCH v4 50/58] mtd: rawnand: ingenic: retain chip state across removal
From: James Hilliard
Date: Fri Nov 13 2026 - 14:09:42 EST
Unregister and disconnect all chips before releasing the ECC provider.
Retain each chip and its shared controller until the final MTD put,
while releasing hardware resources during device removal.
Run NAND cleanup during disconnection; the final core-owned put only
releases retained allocation and layout data.
Cc: Harvey Hunt <harveyhuntnexus@xxxxxxxxx>
Cc: linux-mips@xxxxxxxxxxxxxxx
Cc: Paul Cercueil <paul@xxxxxxxxxxxxxxx>
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 39 +++++++++++++++++--------
1 file changed, 27 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
index bdda06ae0d72..2818ffe76796 100644
--- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
+++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
@@ -368,7 +368,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
if (IS_ERR(cs->base))
return PTR_ERR(cs->base);
- nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
+ nand = kzalloc_obj(*nand);
if (!nand)
return -ENOMEM;
@@ -377,7 +377,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
if (IS_ERR(nand->busy_gpio)) {
ret = PTR_ERR(nand->busy_gpio);
dev_err(dev, "failed to request busy GPIO: %d\n", ret);
- return ret;
+ goto free_chip;
}
nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
@@ -385,15 +385,17 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
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;
mtd = nand_to_mtd(chip);
mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev),
cs->bank);
- if (!mtd->name)
- return -ENOMEM;
+ if (!mtd->name) {
+ ret = -ENOMEM;
+ goto free_chip;
+ }
mtd->parent_dev = dev;
chip->options = NAND_NO_SUBPAGE_WRITE;
@@ -404,17 +406,27 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
chip->controller->ops = &ingenic_nand_controller_ops;
ret = nand_scan(chip, 1);
if (ret)
- return ret;
+ goto free_chip;
+ ret = nand_set_lifetime(chip, to_ingenic_nand(nand_to_mtd(chip)));
+ if (ret) {
+ nand_cleanup(chip);
+ goto free_chip;
+ }
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
- nand_cleanup(chip);
+ nand_disconnect(chip);
+ mtd_put_master(mtd);
return ret;
}
list_add_tail(&nand->chip_list, &nfc->chips);
return 0;
+
+free_chip:
+ kfree(nand);
+ return ret;
}
static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc)
@@ -429,8 +441,9 @@ static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc)
chip = &ingenic_chip->chip;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
- nand_cleanup(chip);
+ nand_disconnect(chip);
list_del(&ingenic_chip->chip_list);
+ mtd_put_master(nand_to_mtd(chip));
}
}
@@ -474,9 +487,12 @@ static int ingenic_nand_probe(struct platform_device *pdev)
return -ENODEV;
}
- nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL);
+ nfc = kzalloc(struct_size(nfc, cs, num_banks), GFP_KERNEL);
if (!nfc)
return -ENOMEM;
+ ret = devm_nand_controller_init(dev, &nfc->controller, nfc);
+ if (ret)
+ return ret;
nfc->soc_info = device_get_match_data(dev);
if (!nfc->soc_info)
@@ -493,7 +509,6 @@ static int ingenic_nand_probe(struct platform_device *pdev)
nfc->dev = dev;
nfc->num_banks = num_banks;
- nand_controller_init(&nfc->controller);
INIT_LIST_HEAD(&nfc->chips);
ret = ingenic_nand_init_chips(nfc, pdev);
@@ -511,10 +526,10 @@ static void ingenic_nand_remove(struct platform_device *pdev)
{
struct ingenic_nfc *nfc = platform_get_drvdata(pdev);
+ ingenic_nand_cleanup_chips(nfc);
+
if (nfc->ecc)
ingenic_ecc_release(nfc->ecc);
-
- ingenic_nand_cleanup_chips(nfc);
}
static const struct jz_soc_info jz4740_soc_info = {
--
2.53.0