[PATCH 38/58] mtd: rawnand: cadence: retain open-handle state on removal
James Hilliard
james.hilliard1 at gmail.com
Sat Nov 7 02:16:35 PST 2026
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.
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/mtd/nand/raw/cadence-nand-controller.c | 60 +++++++++++++++++++-------
1 file changed, 44 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index d53b35a8b3cb..65c4eab600af 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;
/*
@@ -2991,6 +2992,11 @@ static const struct nand_controller_ops cadence_nand_controller_ops = {
.setup_interface = cadence_nand_setup_interface,
};
+static void cadence_nand_chip_release(struct nand_chip *chip)
+{
+ kfree(to_cdns_nand_chip(chip));
+}
+
static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
struct device_node *np)
{
@@ -3007,9 +3013,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 +3028,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 +3065,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_release(chip, cadence_nand_chip_release);
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 +3091,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 +3138,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 +3193,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 +3212,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);
@@ -3260,6 +3279,11 @@ static const struct of_device_id cadence_nand_dt_ids[] = {
MODULE_DEVICE_TABLE(of, cadence_nand_dt_ids);
+static void cadence_nand_release(struct nand_controller *controller)
+{
+ kfree(container_of(controller, struct cadence_nand_dt, cdns_ctrl.controller));
+}
+
static int cadence_nand_dt_probe(struct platform_device *ofdev)
{
struct resource *res;
@@ -3275,11 +3299,15 @@ 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,
+ cadence_nand_release);
+ if (ret)
+ return ret;
cdns_ctrl->caps1 = devdata;
cdns_ctrl->dev = &ofdev->dev;
--
2.53.0
More information about the linux-mtd
mailing list