[PATCH v4 17/58] mtd: rawnand: rockchip: retain chips across controller removal
From: James Hilliard
Date: Fri Nov 13 2026 - 15:58:24 EST
Keep chip metadata and controller storage alive for retained MTD
handles. Disconnect each chip after unregistering it and before
releasing transfer buffers or clocks. This also lets unregister
notifiers finish using those buffers before hardware teardown. Free chip
metadata on the final MTD put.
Run NAND cleanup during disconnection; the final core-owned put only
releases retained allocation and layout data.
Fixes: 058e0e847d54 ("mtd: rawnand: rockchip: NFC driver for RK3308, RK2928 and others")
Cc: Heiko Stuebner <heiko@xxxxxxxxx>
Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
Cc: linux-rockchip@xxxxxxxxxxxxxxxxxxx
Cc: Yifeng Zhao <yifeng.zhao@xxxxxxxxxxxxxx>
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/rockchip-nand-controller.c | 41 +++++++++++++++++--------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c
index 1e56e5e41f79..1616c9335e38 100644
--- a/drivers/mtd/nand/raw/rockchip-nand-controller.c
+++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c
@@ -1134,8 +1134,7 @@ static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
return -EINVAL;
}
- rknand = devm_kzalloc(dev, struct_size(rknand, sels, nsels),
- GFP_KERNEL);
+ rknand = kzalloc(struct_size(rknand, sels, nsels), GFP_KERNEL);
if (!rknand)
return -ENOMEM;
@@ -1144,17 +1143,19 @@ static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
ret = of_property_read_u32_index(np, "reg", i, &tmp);
if (ret) {
dev_err(dev, "reg property failure : %d\n", ret);
- return ret;
+ goto free_chip;
}
if (tmp >= NFC_MAX_NSELS) {
dev_err(dev, "invalid CS: %u\n", tmp);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
dev_err(dev, "CS %u already assigned\n", tmp);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
rknand->sels[i] = tmp;
@@ -1174,19 +1175,20 @@ static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
mtd = nand_to_mtd(chip);
- mtd->owner = THIS_MODULE;
+
mtd->parent_dev = dev;
if (!mtd->name) {
dev_err(nfc->dev, "NAND label property is mandatory\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_chip;
}
mtd_set_ooblayout(mtd, &rk_nfc_ooblayout_ops);
rk_nfc_hw_init(nfc);
ret = nand_scan(chip, nsels);
if (ret)
- return ret;
+ goto free_chip;
if (chip->options & NAND_IS_BOOT_MEDIUM) {
ret = of_property_read_u32(np, "rockchip,boot-blks", &tmp);
@@ -1197,16 +1199,26 @@ static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
rknand->boot_ecc = ret ? chip->ecc.strength : tmp;
}
+ ret = nand_set_lifetime(chip, container_of(chip, struct rk_nfc_nand_chip, chip));
+ if (ret) {
+ nand_cleanup(chip);
+ goto free_chip;
+ }
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(dev, "MTD parse partition error\n");
- nand_cleanup(chip);
+ nand_disconnect(chip);
+ mtd_put_master(mtd);
return ret;
}
list_add_tail(&rknand->node, &nfc->chips);
return 0;
+
+free_chip:
+ kfree(rknand);
+ return ret;
}
static void rk_nfc_chips_cleanup(struct rk_nfc *nfc)
@@ -1219,8 +1231,9 @@ static void rk_nfc_chips_cleanup(struct rk_nfc *nfc)
chip = &rknand->chip;
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
- nand_cleanup(chip);
+ nand_disconnect(chip);
list_del(&rknand->node);
+ mtd_put_master(nand_to_mtd(chip));
}
}
@@ -1381,11 +1394,13 @@ static int rk_nfc_probe(struct platform_device *pdev)
struct rk_nfc *nfc;
int ret, irq;
- nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+ nfc = kzalloc_obj(*nfc);
if (!nfc)
return -ENOMEM;
- nand_controller_init(&nfc->controller);
+ ret = devm_nand_controller_init(dev, &nfc->controller, nfc);
+ if (ret)
+ return ret;
INIT_LIST_HEAD(&nfc->chips);
nfc->controller.ops = &rk_nfc_controller_ops;
@@ -1449,9 +1464,9 @@ static void rk_nfc_remove(struct platform_device *pdev)
{
struct rk_nfc *nfc = platform_get_drvdata(pdev);
+ rk_nfc_chips_cleanup(nfc);
kfree(nfc->page_buf);
kfree(nfc->oob_buf);
- rk_nfc_chips_cleanup(nfc);
rk_nfc_disable_clks(nfc);
}
--
2.53.0