[PATCH v4 38/58] mtd: rawnand: cadence: retain open-handle state on removal
From: James Hilliard
Date: Fri Nov 13 2026 - 13:44:02 EST
Keep chip metadata and controller storage alive until the final MTD
reference is released. Unregister and disconnect each published chip
before releasing the resources used by its hardware operations.
Allocate the final shared buffer before publishing any chip.
Run NAND cleanup during disconnection; the final core-owned put only
releases retained allocation and layout data.
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/cadence-nand-controller.c | 49 +++++++++++++++++---------
1 file changed, 33 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index 05d0659e6527..4e365ffdf905 100644
--- a/drivers/mtd/nand/raw/cadence-nand-controller.c
+++ b/drivers/mtd/nand/raw/cadence-nand-controller.c
@@ -562,6 +562,7 @@ struct cdns_nand_chip {
struct cadence_nand_timings timings;
struct nand_chip chip;
u8 nsels;
+ bool registered;
struct list_head node;
/*
@@ -3007,9 +3008,7 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
}
/* Allocate the nand chip structure. */
- cdns_chip = devm_kzalloc(cdns_ctrl->dev, sizeof(*cdns_chip) +
- (nsels * sizeof(u8)),
- GFP_KERNEL);
+ cdns_chip = kzalloc(struct_size(cdns_chip, cs, nsels), GFP_KERNEL);
if (!cdns_chip) {
dev_err(cdns_ctrl->dev, "could not allocate chip structure\n");
return -ENOMEM;
@@ -3024,20 +3023,22 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
dev_err(cdns_ctrl->dev,
"could not retrieve reg property: %d\n",
ret);
- return ret;
+ goto free_chip;
}
if (cs >= cdns_ctrl->caps2.max_banks) {
dev_err(cdns_ctrl->dev,
"invalid reg value: %u (max CS = %d)\n",
cs, cdns_ctrl->caps2.max_banks);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
if (test_and_set_bit(cs, &cdns_ctrl->assigned_cs)) {
dev_err(cdns_ctrl->dev,
"CS %d already assigned\n", cs);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
cdns_chip->cs[i] = cs;
@@ -3059,20 +3060,22 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
ret = nand_scan(chip, cdns_chip->nsels);
if (ret) {
dev_err(cdns_ctrl->dev, "could not scan the nand chip\n");
- return ret;
+ goto free_chip;
}
- ret = mtd_device_register(mtd, NULL, 0);
+ ret = nand_set_lifetime(chip, cdns_chip);
if (ret) {
- dev_err(cdns_ctrl->dev,
- "failed to register mtd device: %d\n", ret);
nand_cleanup(chip);
- return ret;
+ goto free_chip;
}
list_add_tail(&cdns_chip->node, &cdns_ctrl->chips);
return 0;
+
+free_chip:
+ kfree(cdns_chip);
+ return ret;
}
static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl)
@@ -3083,10 +3086,13 @@ static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl)
list_for_each_entry_safe(entry, temp, &cdns_ctrl->chips, node) {
chip = &entry->chip;
- ret = mtd_device_unregister(nand_to_mtd(chip));
- WARN_ON(ret);
- nand_cleanup(chip);
+ if (entry->registered) {
+ ret = mtd_device_unregister(nand_to_mtd(chip));
+ WARN_ON(ret);
+ }
+ nand_disconnect(chip);
list_del(&entry->node);
+ mtd_put_master(nand_to_mtd(chip));
}
}
@@ -3127,6 +3133,7 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
{
dma_cap_mask_t mask;
struct dma_device *dma_dev;
+ struct cdns_nand_chip *entry;
int ret;
cdns_ctrl->cdma_desc = dma_alloc_coherent(cdns_ctrl->dev,
@@ -3181,7 +3188,6 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
goto dma_release_chnl;
}
- nand_controller_init(&cdns_ctrl->controller);
INIT_LIST_HEAD(&cdns_ctrl->chips);
cdns_ctrl->controller.ops = &cadence_nand_controller_ops;
@@ -3201,9 +3207,17 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
goto unmap_dma_resource;
}
+ list_for_each_entry(entry, &cdns_ctrl->chips, node) {
+ ret = mtd_device_register(nand_to_mtd(&entry->chip), NULL, 0);
+ if (ret)
+ goto unmap_dma_resource;
+ entry->registered = true;
+ }
+
return 0;
unmap_dma_resource:
+ cadence_nand_chips_cleanup(cdns_ctrl);
dma_unmap_resource(dma_dev->dev, cdns_ctrl->io.iova_dma,
cdns_ctrl->io.size, DMA_BIDIRECTIONAL, 0);
@@ -3275,11 +3289,14 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
return -ENOMEM;
}
- dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL);
+ dt = kzalloc_obj(*dt);
if (!dt)
return -ENOMEM;
cdns_ctrl = &dt->cdns_ctrl;
+ ret = devm_nand_controller_init(&ofdev->dev, &cdns_ctrl->controller, dt);
+ if (ret)
+ return ret;
cdns_ctrl->caps1 = devdata;
cdns_ctrl->dev = &ofdev->dev;
--
2.53.0