[PATCH v3 54/58] mtd: rawnand: fsl_ifc: retain chip state across removal

From: James Hilliard

Date: Wed Nov 11 2026 - 20:48:56 EST


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.

Run NAND cleanup during disconnection; the final core-owned put only
releases retained allocation and layout data.

Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/fsl_ifc_nand.c | 46 +++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 20ae012adbaa..8b48c94ea8c5 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -40,6 +40,7 @@ struct fsl_ifc_mtd {
/* overview of the fsl ifc controller */
struct fsl_ifc_nand_ctrl {
struct nand_controller controller;
+ unsigned int users;
struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT];

void __iomem *addr; /* Address of assigned IFC buffer */
@@ -961,11 +962,20 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
return 0;
}

-static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
-{
- ifc_nand_ctrl->chips[priv->bank] = NULL;
+static DEFINE_MUTEX(fsl_ifc_nand_mutex);

- return 0;
+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;
+ }
+ nand_storage_put(&ctrl->controller.storage);
}

static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
@@ -981,8 +991,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 +1024,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);
+ nand_storage_init(&ifc_nand_ctrl->controller.storage, ifc_nand_ctrl);
} else {
ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand;
+ nand_storage_get(&ifc_nand_ctrl->controller.storage);
}
+ 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 +1084,19 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
ret = nand_scan(&priv->chip, 1);
if (ret)
goto err;
+ ret = nand_set_lifetime(&priv->chip, priv);
+ 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 +1107,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
nand_cleanup(&priv->chip);
err:
fsl_ifc_chip_remove(priv);
+ kfree(priv);

return ret;
}
@@ -1099,9 +1120,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