[PATCH 54/58] mtd: rawnand: fsl_ifc: retain chip state across removal
James Hilliard
james.hilliard1 at gmail.com
Sat Nov 7 02:16:51 PST 2026
Separate active bank membership from ownership of the shared IFC NAND
controller. Retained chip references keep the controller lock and chip
metadata alive after removal, without retaining hardware mappings.
Disconnect before device-managed resources disappear and drop the shared
controller reference from each chip's final release callback.
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/mtd/nand/raw/fsl_ifc_nand.c | 58 ++++++++++++++++++++++++++++++-------
1 file changed, 47 insertions(+), 11 deletions(-)
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index cc75b6a67e07..18b747e73c3b 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -40,6 +40,8 @@ struct fsl_ifc_mtd {
/* overview of the fsl ifc controller */
struct fsl_ifc_nand_ctrl {
struct nand_controller controller;
+ struct kref refcnt;
+ unsigned int users;
struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];
void __iomem *addr; /* Address of assigned IFC buffer */
@@ -961,11 +963,33 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
return 0;
}
-static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
+static void fsl_ifc_controller_release(struct kref *ref)
{
- ifc_nand_ctrl->chips[priv->bank] = NULL;
+ kfree(container_of(ref, struct fsl_ifc_nand_ctrl, refcnt));
+}
- return 0;
+static void fsl_ifc_chip_release(struct nand_chip *chip)
+{
+ struct fsl_ifc_nand_ctrl *ctrl;
+
+ ctrl = container_of(chip->controller, struct fsl_ifc_nand_ctrl, controller);
+ kfree(nand_get_controller_data(chip));
+ kref_put(&ctrl->refcnt, fsl_ifc_controller_release);
+}
+
+static DEFINE_MUTEX(fsl_ifc_nand_mutex);
+
+static void fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
+{
+ struct fsl_ifc_nand_ctrl *ctrl;
+
+ ctrl = container_of(priv->chip.controller, struct fsl_ifc_nand_ctrl, controller);
+ guard(mutex)(&fsl_ifc_nand_mutex);
+ ctrl->chips[priv->bank] = NULL;
+ if (!--ctrl->users) {
+ priv->ctrl->nand = NULL;
+ ifc_nand_ctrl = NULL;
+ }
}
static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
@@ -981,8 +1005,6 @@ static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
return (cspr & CSPR_BA) == convert_ifc_address(addr);
}
-static DEFINE_MUTEX(fsl_ifc_nand_mutex);
-
static int fsl_ifc_nand_probe(struct platform_device *dev)
{
struct fsl_ifc_runtime __iomem *ifc;
@@ -1016,30 +1038,35 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
return -ENODEV;
}
- priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+ priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
mutex_lock(&fsl_ifc_nand_mutex);
if (!fsl_ifc_ctrl_dev->nand) {
- ifc_nand_ctrl = devm_kzalloc(fsl_ifc_ctrl_dev->dev, sizeof(*ifc_nand_ctrl),
- GFP_KERNEL);
+ ifc_nand_ctrl = kzalloc_obj(*ifc_nand_ctrl);
if (!ifc_nand_ctrl) {
mutex_unlock(&fsl_ifc_nand_mutex);
+ kfree(priv);
return -ENOMEM;
}
fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
nand_controller_init(&ifc_nand_ctrl->controller);
+ kref_init(&ifc_nand_ctrl->refcnt);
} else {
ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
+ kref_get(&ifc_nand_ctrl->refcnt);
}
+ ifc_nand_ctrl->users++;
mutex_unlock(&fsl_ifc_nand_mutex);
ifc_nand_ctrl->chips[bank] = priv;
priv->bank = bank;
priv->ctrl = fsl_ifc_ctrl_dev;
+ priv->chip.controller = &ifc_nand_ctrl->controller;
+ nand_set_controller_data(&priv->chip, priv);
priv->dev = &dev->dev;
priv->vbase = vbase;
@@ -1071,12 +1098,19 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
ret = nand_scan(&priv->chip, 1);
if (ret)
goto err;
+ ret = nand_set_release(&priv->chip, fsl_ifc_chip_release);
+ if (ret)
+ goto cleanup_nand;
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
- if (ret)
- goto cleanup_nand;
+ if (ret) {
+ nand_disconnect(&priv->chip);
+ fsl_ifc_chip_remove(priv);
+ mtd_put_master(mtd);
+ return ret;
+ }
dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res->start, priv->bank);
@@ -1087,6 +1121,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
nand_cleanup(&priv->chip);
err:
fsl_ifc_chip_remove(priv);
+ fsl_ifc_chip_release(&priv->chip);
return ret;
}
@@ -1099,9 +1134,10 @@ static void fsl_ifc_nand_remove(struct platform_device *dev)
ret = mtd_device_unregister(nand_to_mtd(chip));
WARN_ON(ret);
- nand_cleanup(chip);
+ nand_disconnect(chip);
fsl_ifc_chip_remove(priv);
+ mtd_put_master(nand_to_mtd(chip));
}
static const struct of_device_id fsl_ifc_nand_match[] = {
--
2.53.0
More information about the linux-mtd
mailing list