[PATCH 17/58] mtd: rawnand: rockchip: retain chips across controller removal
James Hilliard
james.hilliard1 at gmail.com
Sat Nov 7 02:16:14 PST 2026
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.
Fixes: 058e0e847d54 ("mtd: rawnand: rockchip: NFC driver for RK3308, RK2928 and others")
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/mtd/nand/raw/rockchip-nand-controller.c | 49 +++++++++++++++++++------
1 file changed, 37 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c
index 9444ba02696d..aa31457390cd 100644
--- a/drivers/mtd/nand/raw/rockchip-nand-controller.c
+++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c
@@ -1115,6 +1115,11 @@ static const struct nand_controller_ops rk_nfc_controller_ops = {
.setup_interface = rk_nfc_setup_interface,
};
+static void rk_nfc_nand_release(struct nand_chip *chip)
+{
+ kfree(container_of(chip, struct rk_nfc_nand_chip, chip));
+}
+
static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
struct device_node *np)
{
@@ -1134,8 +1139,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 +1148,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;
@@ -1179,14 +1185,15 @@ static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc,
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 +1204,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_release(chip, rk_nfc_nand_release);
+ 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 +1236,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));
}
}
@@ -1375,17 +1393,24 @@ static const struct of_device_id rk_nfc_id_table[] = {
};
MODULE_DEVICE_TABLE(of, rk_nfc_id_table);
+static void rk_nfc_release(struct nand_controller *controller)
+{
+ kfree(container_of(controller, struct rk_nfc, controller));
+}
+
static int rk_nfc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
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, rk_nfc_release);
+ if (ret)
+ return ret;
INIT_LIST_HEAD(&nfc->chips);
nfc->controller.ops = &rk_nfc_controller_ops;
@@ -1449,9 +1474,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
More information about the linux-mtd
mailing list