[PATCH 36/58] mtd: rawnand: renesas: retain open-handle state on removal
From: James Hilliard
Date: Sat Nov 07 2026 - 15:14:21 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.
Disconnect all chips before dropping runtime PM and transfer buffers.
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/renesas-nand-controller.c | 45 +++++++++++++++++++-------
1 file changed, 33 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
index 201dd62b9990..a8d9e701d7d7 100644
--- a/drivers/mtd/nand/raw/renesas-nand-controller.c
+++ b/drivers/mtd/nand/raw/renesas-nand-controller.c
@@ -1201,6 +1201,11 @@ static int rnandc_alloc_dma_buf(struct rnandc *rnandc,
return 0;
}
+static void rnandc_chip_release(struct nand_chip *chip)
+{
+ kfree(container_of(chip, struct rnand_chip, chip));
+}
+
static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
{
struct rnand_chip *rnand;
@@ -1217,8 +1222,7 @@ static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
}
/* Alloc the driver's NAND chip structure */
- rnand = devm_kzalloc(rnandc->dev, struct_size(rnand, sels, nsels),
- GFP_KERNEL);
+ rnand = kzalloc(struct_size(rnand, sels, nsels), GFP_KERNEL);
if (!rnand)
return -ENOMEM;
@@ -1229,17 +1233,19 @@ static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
ret = of_property_read_u32_index(np, "reg", i, &cs);
if (ret) {
dev_err(rnandc->dev, "Incomplete reg property (%d)\n", ret);
- return ret;
+ goto free_chip;
}
if (cs >= RNANDC_CS_NUM) {
dev_err(rnandc->dev, "Invalid reg property (%d)\n", cs);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
if (test_and_set_bit(cs, &rnandc->assigned_cs)) {
dev_err(rnandc->dev, "CS %d already assigned\n", cs);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
/*
@@ -1257,23 +1263,29 @@ static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
mtd->dev.parent = rnandc->dev;
if (!mtd->name) {
dev_err(rnandc->dev, "Missing MTD label\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
ret = nand_scan(chip, rnand->nsels);
if (ret) {
dev_err(rnandc->dev, "Failed to scan the NAND chip (%d)\n", ret);
- return ret;
+ goto free_chip;
}
ret = rnandc_alloc_dma_buf(rnandc, mtd);
if (ret)
goto cleanup_nand;
+ ret = nand_set_release(chip, rnandc_chip_release);
+ if (ret)
+ goto cleanup_nand;
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(rnandc->dev, "Failed to register MTD device (%d)\n", ret);
- goto cleanup_nand;
+ nand_disconnect(chip);
+ mtd_put_master(mtd);
+ return ret;
}
list_add_tail(&rnand->node, &rnandc->chips);
@@ -1282,7 +1294,8 @@ static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
cleanup_nand:
nand_cleanup(chip);
-
+free_chip:
+ kfree(rnand);
return ret;
}
@@ -1296,8 +1309,9 @@ static void rnandc_chips_cleanup(struct rnandc *rnandc)
chip = &entry->chip;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
- nand_cleanup(chip);
+ nand_disconnect(chip);
list_del(&entry->node);
+ mtd_put_master(nand_to_mtd(chip));
}
}
@@ -1316,18 +1330,25 @@ static int rnandc_chips_init(struct rnandc *rnandc)
return 0;
}
+static void rnandc_release(struct nand_controller *controller)
+{
+ kfree(container_of(controller, struct rnandc, controller));
+}
+
static int rnandc_probe(struct platform_device *pdev)
{
struct rnandc *rnandc;
struct clk *eclk;
int irq, ret;
- rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
+ rnandc = kzalloc_obj(*rnandc);
if (!rnandc)
return -ENOMEM;
+ ret = devm_nand_controller_init(&pdev->dev, &rnandc->controller, rnandc_release);
+ if (ret)
+ return ret;
rnandc->dev = &pdev->dev;
- nand_controller_init(&rnandc->controller);
rnandc->controller.ops = &rnandc_ops;
INIT_LIST_HEAD(&rnandc->chips);
init_completion(&rnandc->complete);
--
2.53.0