mirror of https://lore.kernel.org/linux-amlogic/
 help / color / mirror / Atom feed
* [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal
@ 2026-11-07 10:15 James Hilliard
  2026-11-07 10:15 ` [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them James Hilliard
                   ` (57 more replies)
  0 siblings, 58 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:15 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard, stable

An MTD handle can outlive controller unbind. Its metadata and NAND locks
must remain valid, including when a writable file closes and calls sync,
while further hardware access must be rejected after disconnection.

This builds on earlier MTD removal-lifetime work. Commit 19bfa9ebebb5
("mtd: use refcount to prevent corruption") added MTD reference counting
[0], but retaining an MTD reference does not by itself retain the
driver-owned allocation containing the master. The 2025 read/unbind
discussion also covered raw NAND [1] and highlighted the need to drain
active operations before releasing resources [2]. Tze Yee Ng's SPI-NOR
series addresses the corresponding SPI-NOR paths [3]. This series adds
shared backing-object ownership and applies it to raw-NAND drivers.

Introduce an opt-in backing-object lifetime for MTD masters and separate
raw-NAND hardware disconnection from final cleanup. Registered MTD nodes,
device references and open handles retain the backing chip; nested
partitions also retain their parent metadata. Removal notifiers can
finish I/O before disconnection drains admitted operations. Hardware
resources are then released without waiting for open files to close.

OTP providers retain their callback context through final NVMEM device
release while keeping their existing physical-device parent. Preparatory
patches clear unregistered OTP provider pointers and add opt-in NVMEM
context references.

Share the chip and controller ownership helpers, serialize Macronix OTP
access through the same operation locks, and convert the raw-NAND
controller drivers. Each driver conversion keeps metadata cleanup
separate from hardware teardown, including partially published probes.
Shared controllers remain allocated until their last retained chip is
released. R852 allocates a new chip for each inserted card, and NDFC
allocates controller state per binding, so old handles cannot refer to
replacement media or a new binding.

The series adds the common lifetime support and converts its driver
users. It is based on NAND next at 30daaf3e7897 and Tze Yee Ng's posted
module-reference ordering fix [4], whose patch ID is a prerequisite.

TLA+ models of retained-handle, controller and OTP ownership, including
their assumptions and coverage limits, are available in [5].

[0] https://lore.kernel.org/r/20230620131905.648089-2-alexander.usyskin@intel.com
[1] https://lore.kernel.org/r/CAND4H7dz94Gsi_tXSQmLNme9uROnouOUwuCX9BW_+RCc2ZFDqA@mail.gmail.com
[2] https://lore.kernel.org/r/mafs0bjs73tbq.fsf@kernel.org
[3] https://lore.kernel.org/r/20261022035352.52129-1-tze.yee.ng@altera.com
[4] https://lore.kernel.org/r/20261022035352.52129-2-tze.yee.ng@altera.com
[5] https://github.com/jameshilliard/linux-h616-nand-tla

Assisted-by: Codex:gpt-6-astra
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
James Hilliard (58):
      mtd: clear OTP provider pointers after unregistering them
      nvmem: retain provider context through final device release
      mtd: add opt-in lifetime ownership for backing objects
      mtd: rawnand: separate hardware disconnection from cleanup
      mtd: rawnand: share retained chip and controller ownership
      mtd: rawnand: macronix: serialize OTP access with NAND operations
      mtd: rawnand: sunxi: retain chip state until the final MTD put
      mtd: rawnand: plat_nand: retain open-handle state on removal
      mtd: rawnand: gpio: retain open-handle state on removal
      mtd: rawnand: stm32_fmc2: retain open-handle state on removal
      mtd: rawnand: denali: retain chips across controller removal
      mtd: rawnand: arasan: retain chips across controller removal
      mtd: rawnand: mxic: retain open-handle state on removal
      mtd: rawnand: intel: retain open-handle state on removal
      mtd: rawnand: tegra: retain open-handle state on removal
      mtd: rawnand: mtk: retain chips across controller removal
      mtd: rawnand: rockchip: retain chips across controller removal
      mtd: rawnand: atmel: retain chips across controller removal
      mtd: rawnand: orion: retain open-handle state on removal
      mtd: rawnand: socrates: retain open-handle state on removal
      mtd: rawnand: fsl_upm: retain open-handle state on removal
      mtd: rawnand: pasemi: retain open-handle state on removal
      mtd: rawnand: ams-delta: retain open-handle state on removal
      mtd: rawnand: xway: retain open-handle state on removal
      mtd: rawnand: hisi504: retain open-handle state on removal
      mtd: rawnand: vf610: retain open-handle state on removal
      mtd: rawnand: technologic: retain open-handle state on removal
      mtd: rawnand: loongson: retain open-handle state on removal
      mtd: rawnand: ma35d1: retain open-handle state on removal
      mtd: rawnand: pl35x: retain open-handle state on removal
      mtd: rawnand: mxc: retain open-handle state on removal
      mtd: rawnand: fsmc: retain open-handle state on removal
      mtd: rawnand: gpmi: retain open-handle state on removal
      mtd: rawnand: omap2: retain open-handle state on removal
      mtd: rawnand: marvell: retain open-handle state on removal
      mtd: rawnand: renesas: retain open-handle state on removal
      mtd: rawnand: meson: retain open-handle state on removal
      mtd: rawnand: cadence: retain open-handle state on removal
      mtd: rawnand: qcom: retain open-handle state on removal
      mtd: rawnand: cafe: retain open-handle state on removal
      mtd: rawnand: brcmnand: retain open-handle state on removal
      mtd: rawnand: au1550nd: retain chip state across removal
      mtd: rawnand: sharpsl: retain chip state across removal
      mtd: rawnand: davinci: retain chip state across removal
      mtd: rawnand: sh_flctl: retain chip state across removal
      mtd: rawnand: lpc32xx_slc: retain chip state across removal
      mtd: rawnand: lpc32xx_mlc: retain chip state across removal
      mtd: rawnand: ndfc: retain chip state across removal
      mtd: rawnand: txx9ndfmc: retain chip state across removal
      mtd: rawnand: ingenic: retain chip state across removal
      mtd: rawnand: mpc5121: retain chip state across removal
      mtd: rawnand: bcm47xxnflash: retain chip state across removal
      mtd: rawnand: fsl_elbc: retain chip state across removal
      mtd: rawnand: fsl_ifc: retain chip state across removal
      mtd: rawnand: r852: retain chip state across removal
      mtd: rawnand: nandsim: retain chip state across removal
      mtd: rawnand: cs553x: retain chip state across removal
      mtd: rawnand: diskonchip: retain chip state across removal

 drivers/mtd/mtdcore.c                              | 214 +++++++++++++++++----
 drivers/mtd/nand/raw/Makefile                      |   1 +
 drivers/mtd/nand/raw/ams-delta.c                   |  29 ++-
 drivers/mtd/nand/raw/arasan-nand-controller.c      |  57 ++++--
 drivers/mtd/nand/raw/atmel/nand-controller.c       |  59 ++++--
 drivers/mtd/nand/raw/au1550nd.c                    |  26 ++-
 drivers/mtd/nand/raw/bcm47xxnflash/main.c          |  23 ++-
 drivers/mtd/nand/raw/brcmnand/brcmnand.c           |  72 +++++--
 drivers/mtd/nand/raw/cadence-nand-controller.c     |  60 ++++--
 drivers/mtd/nand/raw/cafe_nand.c                   |  37 +++-
 drivers/mtd/nand/raw/cs553x_nand.c                 |  46 +++--
 drivers/mtd/nand/raw/davinci_nand.c                |  29 ++-
 drivers/mtd/nand/raw/denali.c                      |  31 ++-
 drivers/mtd/nand/raw/denali_dt.c                   |  21 +-
 drivers/mtd/nand/raw/denali_pci.c                  |  15 +-
 drivers/mtd/nand/raw/diskonchip.c                  |  21 +-
 drivers/mtd/nand/raw/fsl_elbc_nand.c               |  68 ++++---
 drivers/mtd/nand/raw/fsl_ifc_nand.c                |  58 ++++--
 drivers/mtd/nand/raw/fsl_upm.c                     |  23 ++-
 drivers/mtd/nand/raw/fsmc_nand.c                   |  22 ++-
 drivers/mtd/nand/raw/gpio.c                        |  21 +-
 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c         |  22 ++-
 drivers/mtd/nand/raw/hisi504_nand.c                |  23 ++-
 drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c    |  49 +++--
 drivers/mtd/nand/raw/intel-nand-controller.c       |  22 ++-
 drivers/mtd/nand/raw/internals.h                   |   2 +
 drivers/mtd/nand/raw/loongson-nand-controller.c    |  23 ++-
 drivers/mtd/nand/raw/lpc32xx_mlc.c                 |  26 ++-
 drivers/mtd/nand/raw/lpc32xx_slc.c                 |  26 ++-
 drivers/mtd/nand/raw/marvell_nand.c                |  54 ++++--
 drivers/mtd/nand/raw/meson_nand.c                  |  55 ++++--
 drivers/mtd/nand/raw/mpc5121_nfc.c                 |  29 ++-
 drivers/mtd/nand/raw/mtk_nand.c                    |  44 ++++-
 drivers/mtd/nand/raw/mxc_nand.c                    |  24 ++-
 drivers/mtd/nand/raw/mxic_nand.c                   |  26 ++-
 drivers/mtd/nand/raw/nand_base.c                   |  78 ++++++--
 drivers/mtd/nand/raw/nand_lifetime.c               | 118 ++++++++++++
 drivers/mtd/nand/raw/nand_macronix.c               |   4 +
 drivers/mtd/nand/raw/nandsim.c                     |  24 ++-
 drivers/mtd/nand/raw/ndfc.c                        |  37 +++-
 .../mtd/nand/raw/nuvoton-ma35d1-nand-controller.c  |  44 ++++-
 drivers/mtd/nand/raw/omap2.c                       |  45 ++++-
 drivers/mtd/nand/raw/orion_nand.c                  |  27 ++-
 drivers/mtd/nand/raw/pasemi_nand.c                 |  25 ++-
 drivers/mtd/nand/raw/pl35x-nand-controller.c       |  46 ++++-
 drivers/mtd/nand/raw/plat_nand.c                   |  24 ++-
 drivers/mtd/nand/raw/qcom_nandc.c                  |  74 ++++---
 drivers/mtd/nand/raw/r852.c                        | 111 +++++++----
 drivers/mtd/nand/raw/r852.h                        |   1 +
 drivers/mtd/nand/raw/renesas-nand-controller.c     |  45 +++--
 drivers/mtd/nand/raw/rockchip-nand-controller.c    |  49 +++--
 drivers/mtd/nand/raw/sh_flctl.c                    |  25 ++-
 drivers/mtd/nand/raw/sharpsl.c                     |  25 ++-
 drivers/mtd/nand/raw/sm_common.c                   |  15 +-
 drivers/mtd/nand/raw/sm_common.h                   |   2 +-
 drivers/mtd/nand/raw/socrates_nand.c               |  22 ++-
 drivers/mtd/nand/raw/stm32_fmc2_nand.c             |  22 ++-
 drivers/mtd/nand/raw/sunxi_nand.c                  |  59 ++++--
 drivers/mtd/nand/raw/technologic-nand-controller.c |  25 ++-
 drivers/mtd/nand/raw/tegra_nand.c                  |  37 +++-
 drivers/mtd/nand/raw/txx9ndfmc.c                   |  42 +++-
 drivers/mtd/nand/raw/vf610_nfc.c                   |  22 ++-
 drivers/mtd/nand/raw/xway_nand.c                   |  27 ++-
 drivers/nvmem/core.c                               |  12 +-
 drivers/nvmem/internals.h                          |   1 +
 include/linux/mtd/mtd.h                            |  16 ++
 include/linux/mtd/rawnand.h                        |  21 +-
 include/linux/nvmem-provider.h                     |  10 +
 68 files changed, 1949 insertions(+), 544 deletions(-)
---
base-commit: 30daaf3e7897b4a8d9b72e5b0c86dd6e2a4d7d85
change-id: 20261104-submit-rawnand-retained-handle-lifetime-v1-0b4225e5ce0b
prerequisite-patch-id: a51b9cb4077105499133c413d58c7ad21d973b38

Best regards,
--  
James Hilliard <james.hilliard1@gmail.com>


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
@ 2026-11-07 10:15 ` James Hilliard
  2026-11-07 20:21   ` sashiko-bot
  2026-11-07 10:15 ` [PATCH 02/58] nvmem: retain provider context through final device release James Hilliard
                   ` (56 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:15 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard, stable

If factory OTP discovery or registration fails after user OTP was
registered, mtd_otp_nvmem_add() unregisters the user provider but leaves
its pointer set. mtd_device_parse_register() then unregisters it again
while unwinding the error, accessing a freed provider or dropping a
reference still owned by a consumer.

The same stale pointer survives when the factory OTP error is
-EOPNOTSUPP, which mtd_otp_nvmem_add() treats as success. In that case
mtd_device_unregister() can release the provider a second time later.

Centralize removal of the OTP providers and clear each pointer after
unregistering it so subsequent cleanup cannot repeat the put.

Fixes: e0489f6e221f ("mtd: core: fix error path for nvmem provider")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/mtdcore.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 8efa2d88556e..558a605ba749 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1016,6 +1016,14 @@ static int mtd_nvmem_fact_otp_reg_read(void *priv, unsigned int offset,
 	return retlen == bytes ? 0 : -EIO;
 }
 
+static void mtd_otp_nvmem_remove(struct mtd_info *mtd)
+{
+	nvmem_unregister(mtd->otp_user_nvmem);
+	mtd->otp_user_nvmem = NULL;
+	nvmem_unregister(mtd->otp_factory_nvmem);
+	mtd->otp_factory_nvmem = NULL;
+}
+
 static int mtd_otp_nvmem_add(struct mtd_info *mtd)
 {
 	struct device *dev = mtd->dev.parent;
@@ -1082,7 +1090,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd)
 	return 0;
 
 err:
-	nvmem_unregister(mtd->otp_user_nvmem);
+	mtd_otp_nvmem_remove(mtd);
 	/* Don't report error if OTP is not supported. */
 	if (err == -EOPNOTSUPP)
 		return 0;
@@ -1180,10 +1188,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
 	}
 
 out:
-	if (ret) {
-		nvmem_unregister(mtd->otp_user_nvmem);
-		nvmem_unregister(mtd->otp_factory_nvmem);
-	}
+	if (ret)
+		mtd_otp_nvmem_remove(mtd);
 
 	if (ret && device_is_registered(&mtd->dev)) {
 		err = del_mtd_device(mtd);
@@ -1210,8 +1216,7 @@ int mtd_device_unregister(struct mtd_info *master)
 		memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier));
 	}
 
-	nvmem_unregister(master->otp_user_nvmem);
-	nvmem_unregister(master->otp_factory_nvmem);
+	mtd_otp_nvmem_remove(master);
 
 	if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
 		err = mtd_virt_concat_destroy(master);

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 02/58] nvmem: retain provider context through final device release
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
  2026-11-07 10:15 ` [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them James Hilliard
@ 2026-11-07 10:15 ` James Hilliard
  2026-11-07 20:26   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects James Hilliard
                   ` (55 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:15 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

An NVMEM provider can keep its read/write context in an object separate
from its parent device. Retaining the parent does not keep that object
alive, and nvmem_unregister() can return while consumers still hold the
provider. A device reference can also delay release after the last
consumer is gone.

Add optional paired callbacks to take a reference to the private context
before publishing the provider and drop it from the final device release
callback. Balance the reference on registration errors as well. Require
both callbacks together and document the associated module lifetime
requirement.

MTD OTP providers need this ownership because they are children of the
physical flash device, while their callback context is the separately
allocated MTD master.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/nvmem/core.c           | 12 +++++++++++-
 drivers/nvmem/internals.h      |  1 +
 include/linux/nvmem-provider.h | 10 ++++++++++
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 0556d140170a..851fa5cabecb 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -541,6 +541,8 @@ static void nvmem_release(struct device *dev)
 
 	ida_free(&nvmem_ida, nvmem->id);
 	gpiod_put(nvmem->wp_gpio);
+	if (nvmem->ops->priv_put)
+		nvmem->ops->priv_put(nvmem->priv);
 	kfree(nvmem->ops);
 	kfree(nvmem);
 }
@@ -896,6 +898,9 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	if (!config->reg_read && !config->reg_write)
 		return ERR_PTR(-EINVAL);
 
+	if (!!config->priv_get != !!config->priv_put)
+		return ERR_PTR(-EINVAL);
+
 	nvmem = kzalloc_obj(*nvmem);
 	if (!nvmem)
 		return ERR_PTR(-ENOMEM);
@@ -922,6 +927,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 
 	device_initialize(&nvmem->dev);
 
+	nvmem->priv = config->priv;
+	if (config->priv_get) {
+		config->priv_get(config->priv);
+		ops->priv_put = config->priv_put;
+	}
+
 	if (!config->ignore_wp)
 		nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
 						    GPIOD_OUT_HIGH);
@@ -945,7 +956,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 	nvmem->word_size = config->word_size ?: 1;
 	nvmem->size = config->size;
 	nvmem->root_only = config->root_only;
-	nvmem->priv = config->priv;
 	nvmem->type = config->type;
 	nvmem->keepout = config->keepout;
 	nvmem->nkeepout = config->nkeepout;
diff --git a/drivers/nvmem/internals.h b/drivers/nvmem/internals.h
index 4e610deeaa7b..dcf5d81ca1b9 100644
--- a/drivers/nvmem/internals.h
+++ b/drivers/nvmem/internals.h
@@ -11,6 +11,7 @@
 struct nvmem_operations {
 	nvmem_reg_read_t	reg_read;
 	nvmem_reg_write_t	reg_write;
+	void		(*priv_put)(void *priv);
 };
 
 struct nvmem_device {
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index 6063fe5b7784..03e8522bab17 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -98,6 +98,8 @@ struct nvmem_cell_info {
  * @word_size:	Minimum read/write access granularity.
  * @stride:	Minimum read/write access stride.
  * @priv:	User context passed to read/write callbacks.
+ * @priv_get:	Optional callback taking a reference to @priv before publication.
+ * @priv_put:	Matching callback releasing @priv at final device release.
  * @ignore_wp:  Write Protect pin is managed by the provider.
  * @layout:	Fixed layout associated with this nvmem device.
  *
@@ -107,6 +109,12 @@ struct nvmem_cell_info {
  *
  * Note: Specifying name and setting id to -1 implies a unique device
  * whose name is provided as-is (kept unaltered).
+ *
+ * Note: @priv_get and @priv_put must be supplied together. Each @priv_get
+ * call is balanced by one @priv_put, including on registration failure.
+ * The put can run after nvmem_unregister() returns, when consumers and
+ * device references have gone away. These callbacks must also retain any
+ * module needed to execute @priv_put until it returns.
  */
 struct nvmem_config {
 	struct device		*dev;
@@ -132,6 +140,8 @@ struct nvmem_config {
 	int	word_size;
 	int	stride;
 	void	*priv;
+	void	(*priv_get)(void *priv);
+	void	(*priv_put)(void *priv);
 	/* To be only used by old driver/misc/eeprom drivers */
 	bool			compat;
 	struct device		*base_dev;

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
  2026-11-07 10:15 ` [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them James Hilliard
  2026-11-07 10:15 ` [PATCH 02/58] nvmem: retain provider context through final device release James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:50   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup James Hilliard
                   ` (54 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

MTD references retain registered nodes, but do not retain the backing
object allocated by a controller driver. Keeping a partition open across
driver unbind can therefore leave its master and driver metadata freed.
Device references held outside get_mtd_device(), such as NVMEM
consumers, also need the backing object and the partition ancestry to
remain valid.

Add an optional master lifetime initialized by mtd_set_release(). The
driver owns the initial reference. Each registered node and acquired MTD
handle holds another reference. The node reference lasts through final
device release, and nested partitions retain their parent device until
then. mtd_put_master() drops the driver's reference after hardware has
been disconnected and the MTD devices unregistered.

Give OTP NVMEM providers their own backing-object references through the
NVMEM context callbacks. They are parented to the physical device rather
than the MTD node, so the node reference does not cover them. Keep these
references through final NVMEM device release, including after failed MTD
registration, and use the backing driver's module as the consumer owner.

Pin the release callback's module when the driver drops its reference.
If the module is initializing or unloading, wait for the remaining
references instead: failed module initialization can discard module text
despite reference pins, and unload cannot acquire a new pin. Keep the
MTD core module pinned through its device and OTP release callbacks too.

Distinguish registration from retained references so an unregistered
node cannot be opened again. Reference a registered master even when
MTD_PARTITIONED_MASTER is disabled: an unpartitioned device is still
registered in that configuration. Remove the lookup entry under
mtd_table_mutex at the last MTD put, but reserve its number until final
device release. A retained NVMEM provider still uses that number in its
device name, so reusing it earlier would make rebind fail with -EEXIST.

On registration failure, wait for the overridden device release callback
before returning ownership to the caller. This also covers delayed
kobject release, when an immediate caller free would be premature.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/mtdcore.c   | 195 +++++++++++++++++++++++++++++++++++++++++-------
 include/linux/mtd/mtd.h |  16 ++++
 2 files changed, 184 insertions(+), 27 deletions(-)

diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 558a605ba749..42c06a02cd99 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -31,6 +31,7 @@
 #include <linux/nvmem-provider.h>
 #include <linux/root_dev.h>
 #include <linux/error-injection.h>
+#include <linux/completion.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -70,6 +71,7 @@ static struct class mtd_class = {
 };
 
 static DEFINE_IDR(mtd_idr);
+static DEFINE_IDA(mtd_ids);
 
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
@@ -78,7 +80,15 @@ EXPORT_SYMBOL_GPL(mtd_table_mutex);
 
 struct mtd_info *__mtd_next_device(int i)
 {
-	return idr_get_next(&mtd_idr, &i);
+	struct mtd_info *mtd;
+
+	while ((mtd = idr_get_next(&mtd_idr, &i))) {
+		if (mtd->registered)
+			return mtd;
+		i++;
+	}
+
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(__mtd_next_device);
 
@@ -87,37 +97,118 @@ static LIST_HEAD(mtd_notifiers);
 
 #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
 
-/* REVISIT once MTD uses the driver model better, whoever allocates
- * the mtd_info will probably want to use the release() hook...
+static void mtd_master_release(struct kref *ref)
+{
+	struct mtd_info *master = container_of(ref, struct mtd_info,
+					     master.life_refcnt);
+	struct completion *done = master->master.release_done;
+	struct module *owner = master->master.release_owner;
+
+	master->master.release(master);
+	if (done)
+		complete(done);
+	else
+		module_put(owner);
+}
+
+static void mtd_get_lifetime(struct mtd_info *master)
+{
+	if (master->master.release)
+		kref_get(&master->master.life_refcnt);
+}
+
+static void mtd_put_lifetime(struct mtd_info *master)
+{
+	if (master->master.release)
+		kref_put(&master->master.life_refcnt, mtd_master_release);
+}
+
+/**
+ * mtd_set_release - initialize the lifetime of a dynamically allocated master
+ * @master: master MTD device
+ * @release: free the backing object and its retained metadata
+ *
+ * Call once before registration. The driver owns the initial reference and
+ * drops it with mtd_put_master() after unregistering and disconnecting hardware.
+ * Registered MTD devices and acquired handles retain the backing object too.
+ * The release callback must not access resources released by driver unbind.
  */
+void mtd_set_release(struct mtd_info *master,
+		     void (*release)(struct mtd_info *mtd))
+{
+	kref_init(&master->master.life_refcnt);
+	master->master.release = release;
+}
+EXPORT_SYMBOL_GPL(mtd_set_release);
+
+/**
+ * mtd_put_master - drop the driver's initial lifetime reference
+ * @master: master initialized by mtd_set_release()
+ *
+ * Hardware access must already be disconnected and drained. Unbind may return
+ * while handles retain metadata. Keep the release callback's module loaded
+ * until that metadata is freed. During module unload there can be no acquired
+ * handles, since they pin the module; wait for remaining device references
+ * instead of scheduling a callback into the departing module. Also wait during
+ * module initialization, which may fail and discard the module despite pins.
+ */
+void mtd_put_master(struct mtd_info *master)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	if ((!master->owner || READ_ONCE(master->owner->state) == MODULE_STATE_LIVE) &&
+	    try_module_get(master->owner)) {
+		master->master.release_owner = master->owner;
+		mtd_put_lifetime(master);
+	} else {
+		master->master.release_done = &done;
+		mtd_put_lifetime(master);
+		wait_for_completion(&done);
+	}
+}
+EXPORT_SYMBOL_GPL(mtd_put_master);
+
 static void mtd_release(struct device *dev)
 {
 	struct mtd_info *mtd = dev_get_drvdata(dev);
-	dev_t index = MTD_DEVT(mtd->index);
+	struct mtd_info *master = mtd->ref_master;
+	struct mtd_info *parent = mtd->parent;
 
-	idr_remove(&mtd_idr, mtd->index);
+	/* Retained NVMEM providers use the MTD number in their device name. */
+	ida_free(&mtd_ids, mtd->index);
 	of_node_put(mtd_get_of_node(mtd));
 
 	if (mtd_is_partition(mtd))
 		release_mtd_partition(mtd);
 
-	/* remove /dev/mtdXro node */
-	device_destroy(&mtd_class, index + 1);
+	if (master) {
+		/* Nested partitions can outlive device_del() of their parents. */
+		if (parent && parent != master)
+			put_device(&parent->dev);
+		mtd_put_lifetime(master);
+	}
+	module_put(THIS_MODULE);
 }
 
 /*
- * No-op device release used in add_mtd_device() error paths.
+ * Hand failed registrations back to their caller only after device release.
  * Prevents mtd_release() from being called via device_release(),
  * which would free the mtd_info that the caller still manages.
  */
-static void mtd_dev_release_nop(struct device *dev)
+static void mtd_dev_release_done(struct device *dev)
 {
+	complete(dev_get_drvdata(dev));
 }
 
 static void mtd_device_release(struct kref *kref)
 {
 	struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
 	bool is_partition = mtd_is_partition(mtd);
+	bool managed = mtd->ref_master;
+
+	/* The table mutex is held by the registration or handle put path. */
+	idr_remove(&mtd_idr, mtd->index);
+	device_destroy(&mtd_class, MTD_DEVT(mtd->index) + 1);
 
 	debugfs_remove_recursive(mtd->dbg.dfs_dir);
 
@@ -131,10 +222,8 @@ static void mtd_device_release(struct kref *kref)
 	 *  Should not be done for partition,
 	 *  as it was already destroyed in device_unregister().
 	 */
-	if (!is_partition)
+	if (!is_partition && !managed)
 		memset(&mtd->dev, 0, sizeof(mtd->dev));
-
-	module_put(THIS_MODULE);
 }
 
 #define MTD_DEVICE_ATTR_RO(name) \
@@ -612,7 +701,7 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
 	config.id = NVMEM_DEVID_NONE;
 	config.dev = &mtd->dev;
 	config.name = dev_name(&mtd->dev);
-	config.owner = THIS_MODULE;
+	config.owner = mtd_get_master(mtd)->owner;
 	config.add_legacy_fixed_of_cells = of_device_is_compatible(node, "nvmem-cells");
 	config.reg_read = mtd_nvmem_reg_read;
 	config.size = mtd->size;
@@ -702,10 +791,13 @@ static void mtd_check_of_node(struct mtd_info *mtd)
 
 int add_mtd_device(struct mtd_info *mtd)
 {
+	DECLARE_COMPLETION_ONSTACK(released);
 	struct device_node *np = mtd_get_of_node(mtd);
 	struct mtd_info *master = mtd_get_master(mtd);
 	struct mtd_notifier *not;
 	int i, error, ofidx;
+	u32 id;
+	bool wait_for_release = false;
 
 	/*
 	 * May occur, for instance, on buggy drivers which call
@@ -748,16 +840,28 @@ int add_mtd_device(struct mtd_info *mtd)
 	if (np)
 		ofidx = of_alias_get_id(np, "mtd");
 	if (ofidx >= 0)
-		i = idr_alloc(&mtd_idr, mtd, ofidx, ofidx + 1, GFP_KERNEL);
+		i = ida_alloc_range(&mtd_ids, ofidx, ofidx, GFP_KERNEL);
 	else
-		i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
+		i = ida_alloc(&mtd_ids, GFP_KERNEL);
 	if (i < 0) {
 		error = i;
 		goto fail_locked;
 	}
+	id = i;
+	error = idr_alloc_u32(&mtd_idr, mtd, &id, id, GFP_KERNEL);
+	if (error) {
+		ida_free(&mtd_ids, i);
+		goto fail_locked;
+	}
 
 	mtd->index = i;
 	kref_init(&mtd->refcnt);
+	if (master->master.release) {
+		mtd_get_lifetime(master);
+		mtd->ref_master = master;
+		if (mtd->parent && mtd->parent != master)
+			get_device(&mtd->parent->dev);
+	}
 
 	/* default value if not set by driver */
 	if (mtd->bitflip_threshold == 0)
@@ -821,7 +925,10 @@ int add_mtd_device(struct mtd_info *mtd)
 	device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
 		      "mtd%dro", i);
 
+	/* Keep the release callback loaded until the final device reference. */
+	__module_get(THIS_MODULE);
 	pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
+	mtd->registered = true;
 	/* No need to get a refcount on the module containing
 	   the notifier, since we hold the mtd_table_mutex */
 	list_for_each_entry(not, &mtd_notifiers, list)
@@ -839,29 +946,38 @@ int add_mtd_device(struct mtd_info *mtd)
 		}
 	}
 
-	/* We _know_ we aren't being removed, because
-	   our caller is still holding us here. So none
-	   of this try_ nonsense, and no bitching about it
-	   either. :) */
-	__module_get(THIS_MODULE);
 	return 0;
 
 fail_nvmem_add:
 	device_del(&mtd->dev);
 fail_added:
 	/*
-	 * Clear type and set nop release to prevent mtd_release() ->
+	 * Clear type and override release to prevent mtd_release() ->
 	 * release_mtd_partition() -> free_partition() from freeing mtd.
 	 * The caller handles cleanup on failure.
 	 */
 	mtd->dev.type = NULL;
-	mtd->dev.release = mtd_dev_release_nop;
+	mtd->dev.release = mtd_dev_release_done;
+	dev_set_drvdata(&mtd->dev, &released);
+	wait_for_release = true;
 	put_device(&mtd->dev);
 	of_node_put(mtd_get_of_node(mtd));
 fail_devname:
 	idr_remove(&mtd_idr, i);
+	ida_free(&mtd_ids, i);
+	if (mtd->ref_master) {
+		mtd->ref_master = NULL;
+		if (mtd->parent && mtd->parent != master)
+			put_device(&mtd->parent->dev);
+		mtd_put_lifetime(master);
+	}
 fail_locked:
 	mutex_unlock(&mtd_table_mutex);
+	if (wait_for_release) {
+		wait_for_completion(&released);
+		mtd->dev.release = NULL;
+		dev_set_drvdata(&mtd->dev, mtd);
+	}
 	return error;
 }
 
@@ -882,7 +998,7 @@ int del_mtd_device(struct mtd_info *mtd)
 
 	mutex_lock(&mtd_table_mutex);
 
-	if (idr_find(&mtd_idr, mtd->index) != mtd) {
+	if (idr_find(&mtd_idr, mtd->index) != mtd || !mtd->registered) {
 		ret = -ENODEV;
 		goto out_error;
 	}
@@ -892,6 +1008,7 @@ int del_mtd_device(struct mtd_info *mtd)
 	list_for_each_entry(not, &mtd_notifiers, list)
 		not->remove(mtd);
 
+	mtd->registered = false;
 	kref_put(&mtd->refcnt, mtd_device_release);
 	ret = 0;
 
@@ -952,6 +1069,18 @@ static ssize_t mtd_otp_size(struct mtd_info *mtd, bool is_user)
 	return ret == -ENODATA ? 0 : ret;
 }
 
+static void mtd_otp_nvmem_get(void *priv)
+{
+	mtd_get_lifetime(priv);
+	__module_get(THIS_MODULE);
+}
+
+static void mtd_otp_nvmem_put(void *priv)
+{
+	mtd_put_lifetime(priv);
+	module_put(THIS_MODULE);
+}
+
 static struct nvmem_device *mtd_otp_nvmem_register(struct mtd_info *mtd,
 						   const char *compatible,
 						   int size,
@@ -968,7 +1097,7 @@ static struct nvmem_device *mtd_otp_nvmem_register(struct mtd_info *mtd,
 	config.dev = mtd->dev.parent;
 	config.name = compatible;
 	config.id = NVMEM_DEVID_AUTO;
-	config.owner = THIS_MODULE;
+	config.owner = mtd->owner;
 	config.add_legacy_fixed_of_cells = !mtd_type_is_nand(mtd);
 	config.type = NVMEM_TYPE_OTP;
 	config.root_only = true;
@@ -977,6 +1106,10 @@ static struct nvmem_device *mtd_otp_nvmem_register(struct mtd_info *mtd,
 	config.size = size;
 	config.of_node = np;
 	config.priv = mtd;
+	if (mtd->master.release) {
+		config.priv_get = mtd_otp_nvmem_get;
+		config.priv_put = mtd_otp_nvmem_put;
+	}
 
 	nvmem = nvmem_register(&config);
 	/* Just ignore if there is no NVMEM support in the kernel */
@@ -1336,6 +1469,9 @@ int __get_mtd_device(struct mtd_info *mtd)
 	struct mtd_info *master = mtd_get_master(mtd);
 	int err;
 
+	if (!mtd->registered)
+		return -ENODEV;
+
 	if (master->_get_device) {
 		err = master->_get_device(mtd);
 		if (err)
@@ -1348,13 +1484,14 @@ int __get_mtd_device(struct mtd_info *mtd)
 		return -ENODEV;
 	}
 
+	mtd_get_lifetime(master);
 	while (mtd) {
 		if (mtd != master)
 			kref_get(&mtd->refcnt);
 		mtd = mtd->parent;
 	}
 
-	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+	if (master->dev.type)
 		kref_get(&master->refcnt);
 
 	return 0;
@@ -1441,6 +1578,7 @@ void __put_mtd_device(struct mtd_info *mtd)
 	struct mtd_info *master = mtd_get_master(mtd);
 	/* master can be freed in _put_device */
 	struct module *owner = master->owner;
+	bool managed = master->master.release;
 
 	while (mtd) {
 		/* kref_put() can relese mtd, so keep a reference mtd->parent */
@@ -1451,12 +1589,14 @@ void __put_mtd_device(struct mtd_info *mtd)
 		mtd = parent;
 	}
 
-	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
+	if (master->dev.type)
 		kref_put(&master->refcnt, mtd_device_release);
 
-	/* must be the last use of master */
+	/* Legacy _put_device() callbacks can free an unmanaged master. */
 	if (master->_put_device)
 		master->_put_device(master);
+	if (managed)
+		mtd_put_lifetime(master);
 
 	/* _put_device() runs code from @owner, so drop the module last */
 	module_put(owner);
@@ -2673,6 +2813,7 @@ static void __exit cleanup_mtd(void)
 	bdi_unregister(mtd_bdi);
 	bdi_put(mtd_bdi);
 	idr_destroy(&mtd_idr);
+	ida_destroy(&mtd_ids);
 }
 
 module_init(init_mtd);
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 8d10d9d2e830..5a1bdae00665 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -187,6 +187,7 @@ struct mtd_pairing_scheme {
 };
 
 struct module;	/* only needed for owner field in mtd_info */
+struct completion;
 
 /**
  * struct mtd_debug_info - debugging information for an MTD device.
@@ -224,6 +225,10 @@ struct mtd_part {
  *		     not only the master partition list, but also all
  *		     sub-partitions.
  * @suspended: set to 1 when the device is suspended, 0 otherwise
+ * @life_refcnt: optional lifetime reference count for the backing object
+ * @release: free the backing object after its last lifetime reference
+ * @release_owner: module reference held during deferred release
+ * @release_done: completion used when the owner module is already unloading
  *
  * This struct is embedded in mtd_info and contains master-specific
  * properties/fields. The master is the root MTD device from the MTD partition
@@ -233,6 +238,10 @@ struct mtd_master {
 	struct mutex partitions_lock;
 	struct mutex chrdev_lock;
 	unsigned int suspended : 1;
+	struct kref life_refcnt;
+	void (*release)(struct mtd_info *mtd);
+	struct module *release_owner;
+	struct completion *release_done;
 };
 
 struct mtd_info {
@@ -380,6 +389,10 @@ struct mtd_info {
 	struct module *owner;
 	struct device dev;
 	struct kref refcnt;
+	/* Protected by mtd_table_mutex; false after del_mtd_device(). */
+	bool registered;
+	/* Lifetime reference held until the device release callback. */
+	struct mtd_info *ref_master;
 	struct mtd_debug_info dbg;
 	struct nvmem_device *nvmem;
 	struct nvmem_device *otp_user_nvmem;
@@ -681,6 +694,9 @@ extern int mtd_device_parse_register(struct mtd_info *mtd,
 #define mtd_device_register(master, parts, nr_parts)	\
 	mtd_device_parse_register(master, NULL, NULL, parts, nr_parts)
 extern int mtd_device_unregister(struct mtd_info *master);
+void mtd_set_release(struct mtd_info *master,
+		     void (*release)(struct mtd_info *mtd));
+void mtd_put_master(struct mtd_info *master);
 extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
 extern int __get_mtd_device(struct mtd_info *mtd);
 extern void __put_mtd_device(struct mtd_info *mtd);

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (2 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:42   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 05/58] mtd: rawnand: share retained chip and controller ownership James Hilliard
                   ` (53 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

A retained MTD handle can still enter NAND operations after its
controller is unbound. Closing a writable character device also calls
nand_sync(), which takes the chip and controller locks. Freeing their
storage during remove is therefore unsafe even without another explicit
read or write.

Add nand_disconnect() to serialize disconnection with regular operations
under the chip and controller locks. Make nand_get_device() reject a
disconnected chip and propagate that error through its callers. A sync
after disconnection becomes a no-op. Suspend and resume also honor the
disconnected state.

Unregister MTD devices first so removal notifiers can finish their I/O,
then disconnect before releasing controller resources. Wake any
remaining suspended waiters when disconnecting. Panic writes also check
the disconnected flag; their notifier clients must be drained by MTD
unregistration before controller resources are released.

Keep nand_cleanup() separate. Controllers adopting this interface must
retain the chip, controller lock storage and NAND metadata until their
last MTD and device references are gone.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/nand_base.c | 76 +++++++++++++++++++++++++++++++++-------
 include/linux/mtd/rawnand.h      |  9 +++--
 2 files changed, 69 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index a5b278ab9384..8f7d2571e8d9 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -336,21 +336,47 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
  *
  * Lock the device and its controller for exclusive access
  */
-static void nand_get_device(struct nand_chip *chip)
+static int nand_get_device(struct nand_chip *chip)
 {
 	/* Wait until the device is resumed. */
 	while (1) {
 		mutex_lock(&chip->lock);
+		if (chip->disconnected) {
+			mutex_unlock(&chip->lock);
+			return -ENODEV;
+		}
 		if (!chip->suspended) {
 			mutex_lock(&chip->controller->lock);
-			return;
+			return 0;
 		}
 		mutex_unlock(&chip->lock);
 
-		wait_event(chip->resume_wq, !chip->suspended);
+		wait_event(chip->resume_wq, !READ_ONCE(chip->suspended) ||
+			   READ_ONCE(chip->disconnected));
 	}
 }
 
+/**
+ * nand_disconnect - drain hardware access and reject further operations
+ * @chip: NAND chip object
+ *
+ * Unregister the MTD device first so removal notifiers can finish their I/O.
+ * Call before releasing controller resources. Operations waiting for resume
+ * are woken and fail with -ENODEV.
+ * The chip, controller lock and NAND metadata must remain alive
+ * until existing MTD handles are released; nand_cleanup() is a separate step.
+ */
+void nand_disconnect(struct nand_chip *chip)
+{
+	mutex_lock(&chip->lock);
+	mutex_lock(&chip->controller->lock);
+	WRITE_ONCE(chip->disconnected, true);
+	mutex_unlock(&chip->controller->lock);
+	mutex_unlock(&chip->lock);
+	wake_up_all(&chip->resume_wq);
+}
+EXPORT_SYMBOL_GPL(nand_disconnect);
+
 /**
  * nand_check_wp - [GENERIC] check if the chip is write protected
  * @chip: NAND chip object
@@ -581,7 +607,9 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
 		nand_erase_nand(chip, &einfo, 0);
 
 		/* Write bad block marker to OOB */
-		nand_get_device(chip);
+		ret = nand_get_device(chip);
+		if (ret)
+			return ret;
 
 		ret = nand_markbad_bbm(chip, ofs);
 		nand_release_device(chip);
@@ -3879,7 +3907,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
 	    ops->mode != MTD_OPS_RAW)
 		return -ENOTSUPP;
 
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 
 	old_stats = mtd->ecc_stats;
 
@@ -4449,6 +4479,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
 	struct mtd_oob_ops ops;
 	int ret;
 
+	if (READ_ONCE(chip->disconnected))
+		return -ENODEV;
+
 	nand_select_target(chip, chipnr);
 
 	/* Wait for the device to get ready */
@@ -4479,7 +4512,9 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 
 	ops->retlen = 0;
 
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 
 	switch (ops->mode) {
 	case MTD_OPS_PLACE_OOB:
@@ -4539,7 +4574,9 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
 		return -EIO;
 
 	/* Grab the lock and see if the device is available */
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 
 	/* Shift to get first page */
 	page = (int)(instr->addr >> chip->page_shift);
@@ -4627,7 +4664,8 @@ static void nand_sync(struct mtd_info *mtd)
 	pr_debug("%s: called\n", __func__);
 
 	/* Grab the lock and see if the device is available */
-	nand_get_device(chip);
+	if (nand_get_device(chip))
+		return;
 	/* Release it and go back */
 	nand_release_device(chip);
 }
@@ -4644,7 +4682,9 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
 	int ret;
 
 	/* Select the NAND device */
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 
 	nand_select_target(chip, chipnr);
 
@@ -4688,10 +4728,14 @@ static int nand_suspend(struct mtd_info *mtd)
 	int ret = 0;
 
 	mutex_lock(&chip->lock);
+	if (chip->disconnected) {
+		mutex_unlock(&chip->lock);
+		return -ENODEV;
+	}
 	if (chip->ops.suspend)
 		ret = chip->ops.suspend(chip);
 	if (!ret)
-		chip->suspended = 1;
+		WRITE_ONCE(chip->suspended, true);
 	mutex_unlock(&chip->lock);
 
 	return ret;
@@ -4706,10 +4750,12 @@ static void nand_resume(struct mtd_info *mtd)
 	struct nand_chip *chip = mtd_to_nand(mtd);
 
 	scoped_guard(mutex, &chip->lock) {
+		if (chip->disconnected)
+			return;
 		if (chip->suspended) {
 			if (chip->ops.resume)
 				chip->ops.resume(chip);
-			chip->suspended = 0;
+			WRITE_ONCE(chip->suspended, false);
 		} else {
 			pr_err("%s called for a chip which is not in suspended state\n",
 				__func__);
@@ -4743,7 +4789,9 @@ static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	if (!chip->ops.lock_area)
 		return -ENOTSUPP;
 
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 	ret = chip->ops.lock_area(chip, ofs, len);
 	nand_release_device(chip);
 
@@ -4764,7 +4812,9 @@ static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	if (!chip->ops.unlock_area)
 		return -ENOTSUPP;
 
-	nand_get_device(chip);
+	ret = nand_get_device(chip);
+	if (ret)
+		return ret;
 	ret = chip->ops.unlock_area(chip, ofs, len);
 	nand_release_device(chip);
 
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index 5c70e7bd3ed5..90b2169cc745 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -1264,9 +1264,10 @@ struct nand_secure_region {
  * @pagecache.page: Page number currently in the cache. -1 means no page is
  *                  currently cached
  * @buf_align: Minimum buffer alignment required by a platform
- * @lock: Lock protecting the suspended field. Also used to serialize accesses
- *        to the NAND device
+ * @lock: Lock protecting the suspended and disconnected fields. Also serializes
+ *        accesses to the NAND device
  * @suspended: Set to 1 when the device is suspended, 0 when it's not
+ * @disconnected: Controller resources are unavailable; reject hardware access
  * @resume_wq: wait queue to sleep if rawnand is in suspended state.
  * @cur_cs: Currently selected target. -1 means no target selected, otherwise we
  *          should always have cur_cs >= 0 && cur_cs < nanddev_ntargets().
@@ -1326,7 +1327,8 @@ struct nand_chip {
 
 	/* Internals */
 	struct mutex lock;
-	unsigned int suspended : 1;
+	bool suspended;
+	bool disconnected;
 	wait_queue_head_t resume_wq;
 	int cur_cs;
 	int read_retries;
@@ -1595,6 +1597,7 @@ void nand_wait_ready(struct nand_chip *chip);
  * sucessful nand_scan().
  */
 void nand_cleanup(struct nand_chip *chip);
+void nand_disconnect(struct nand_chip *chip);
 
 /*
  * External helper for controller drivers that have to implement the WAITRDY

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 05/58] mtd: rawnand: share retained chip and controller ownership
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (3 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 06/58] mtd: rawnand: macronix: serialize OTP access with NAND operations James Hilliard
                   ` (52 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Controller drivers need to keep chip metadata and serialization locks
alive after unregistering MTD, without retaining their hardware
resources. Share the name, firmware-node and parent-device references,
the final NAND cleanup, and the disconnected-device open check.

Provide reference-counted controller storage with an initial
device-managed reference. Register that reference before managed
hardware resources so their teardown finishes before the controller can
be freed. Each retained chip pins the controller independently, allowing
multi-chip controllers and embedded chip structures to use the same
lifetime rules.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/Makefile        |   1 +
 drivers/mtd/nand/raw/nand_lifetime.c | 118 +++++++++++++++++++++++++++++++++++
 include/linux/mtd/rawnand.h          |  12 ++++
 3 files changed, 131 insertions(+)

diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 619760138d32..7ea275a40fcd 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_MTD_NAND_NUVOTON_MA35)	+= nuvoton-ma35d1-nand-controller.o
 obj-$(CONFIG_MTD_NAND_LOONGSON)		+= loongson-nand-controller.o
 
 nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
+nand-objs += nand_lifetime.o
 nand-objs += nand_onfi.o
 nand-objs += nand_jedec.o
 nand-objs += nand_amd.o
diff --git a/drivers/mtd/nand/raw/nand_lifetime.c b/drivers/mtd/nand/raw/nand_lifetime.c
new file mode 100644
index 000000000000..9518c9a4bc63
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_lifetime.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Storage lifetime for disconnected NAND controllers and chips. */
+
+#include <linux/device.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/slab.h>
+
+static void nand_controller_release(struct kref *ref)
+{
+	struct nand_controller *controller;
+
+	controller = container_of(ref, struct nand_controller, refcnt);
+	controller->release(controller);
+}
+
+static void nand_controller_put(void *data)
+{
+	struct nand_controller *controller = data;
+
+	kref_put(&controller->refcnt, nand_controller_release);
+}
+
+/**
+ * devm_nand_controller_init - initialize reference-counted controller storage
+ * @dev: controller device
+ * @controller: controller embedded in driver-owned, non-devm storage
+ * @release: callback freeing that storage, without accessing hardware
+ *
+ * Call before acquiring managed hardware resources. Device teardown drops its
+ * storage reference after releasing those resources. Chips prepared with
+ * nand_set_release() hold additional references until their final MTD put.
+ *
+ * Takes ownership of the storage even on failure.
+ * Return: zero on success or a negative error code.
+ */
+int devm_nand_controller_init(struct device *dev, struct nand_controller *controller,
+			      void (*release)(struct nand_controller *controller))
+{
+	nand_controller_init(controller);
+	kref_init(&controller->refcnt);
+	controller->release = release;
+
+	return devm_add_action_or_reset(dev, nand_controller_put, controller);
+}
+EXPORT_SYMBOL_GPL(devm_nand_controller_init);
+
+static int nand_mtd_get_device(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd_get_master(mtd));
+
+	guard(mutex)(&chip->lock);
+	return chip->disconnected ? -ENODEV : 0;
+}
+
+static void nand_mtd_release(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_controller *controller = chip->controller;
+	struct device *parent = mtd->dev.parent;
+	struct device_node *np = nand_get_flash_node(chip);
+	const char *name = mtd->name;
+
+	if (!controller->release)
+		controller = NULL;
+	nand_cleanup(chip);
+	of_node_put(np);
+	kfree(name);
+	if (chip->release)
+		chip->release(chip);
+	if (controller)
+		nand_controller_put(controller);
+	put_device(parent);
+}
+
+/**
+ * nand_set_release - retain a scanned chip until its final MTD reference
+ * @chip: successfully scanned, unpublished NAND chip
+ * @release: callback freeing chip storage, or NULL for a controller-embedded chip
+ *
+ * Copies the MTD name and pins the parent device, flash node and, when managed
+ * with devm_nand_controller_init(), controller storage. The original name stays
+ * owned by its original allocator. The caller must set the name or parent before
+ * calling this function, and preserve any other metadata used by MTD callbacks.
+ *
+ * After success, unregister MTD, call nand_disconnect(), and drop the driver's
+ * initial reference with mtd_put_master(). Do the same disconnect/put on MTD
+ * registration failure. nand_cleanup() runs at final release, so controller
+ * detach callbacks must not depend on resources released during removal.
+ *
+ * Return: zero on success or -ENOMEM, leaving ownership unchanged on failure.
+ */
+int nand_set_release(struct nand_chip *chip,
+		     void (*release)(struct nand_chip *chip))
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	const char *name = mtd->name;
+	char *copy;
+
+	if (!name)
+		name = dev_name(mtd->dev.parent);
+	copy = kstrdup(name, GFP_KERNEL);
+	if (!copy)
+		return -ENOMEM;
+
+	mtd->name = copy;
+	mtd->_get_device = nand_mtd_get_device;
+	get_device(mtd->dev.parent);
+	of_node_get(nand_get_flash_node(chip));
+	if (chip->controller->release)
+		kref_get(&chip->controller->refcnt);
+	chip->release = release;
+	mtd_set_release(mtd, nand_mtd_release);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nand_set_release);
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index 90b2169cc745..313e0d88e19a 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -22,6 +22,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/types.h>
+#include <linux/kref.h>
 
 struct nand_chip;
 struct gpio_desc;
@@ -1116,6 +1117,8 @@ struct nand_controller_ops {
  *			changing the column.
  * @supported_op.cont_read: The controller supports sequential cache reads.
  * @controller_wp:	the controller is in charge of handling the WP pin.
+ * @refcnt:		controller storage references for managed lifetimes
+ * @release:		free controller storage after the last chip is released
  */
 struct nand_controller {
 	struct mutex lock;
@@ -1125,6 +1128,8 @@ struct nand_controller {
 		unsigned int cont_read: 1;
 	} supported_op;
 	bool controller_wp;
+	struct kref refcnt;
+	void (*release)(struct nand_controller *controller);
 };
 
 static inline void nand_controller_init(struct nand_controller *nfc)
@@ -1132,6 +1137,9 @@ static inline void nand_controller_init(struct nand_controller *nfc)
 	mutex_init(&nfc->lock);
 }
 
+int devm_nand_controller_init(struct device *dev, struct nand_controller *controller,
+			      void (*release)(struct nand_controller *controller));
+
 /**
  * struct nand_legacy - NAND chip legacy fields/hooks
  * @IO_ADDR_R: address to read the 8 I/O lines of the flash device
@@ -1285,6 +1293,7 @@ struct nand_secure_region {
  *              independent devices
  * @ecc: The ECC controller structure
  * @priv: Chip private data
+ * @release: Free chip storage after the last MTD reference is dropped
  */
 struct nand_chip {
 	struct nand_device base;
@@ -1345,6 +1354,7 @@ struct nand_chip {
 	struct nand_controller *controller;
 	struct nand_ecc_ctrl ecc;
 	void *priv;
+	void (*release)(struct nand_chip *chip);
 };
 
 static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
@@ -1598,6 +1608,8 @@ void nand_wait_ready(struct nand_chip *chip);
  */
 void nand_cleanup(struct nand_chip *chip);
 void nand_disconnect(struct nand_chip *chip);
+int nand_set_release(struct nand_chip *chip,
+		     void (*release)(struct nand_chip *chip));
 
 /*
  * External helper for controller drivers that have to implement the WAITRDY

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 06/58] mtd: rawnand: macronix: serialize OTP access with NAND operations
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (4 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 05/58] mtd: rawnand: share retained chip and controller ownership James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 07/58] mtd: rawnand: sunxi: retain chip state until the final MTD put James Hilliard
                   ` (51 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

The Macronix OTP callbacks select a target and issue feature and page
commands without taking the chip or controller locks. A retained OTP
handle can therefore issue commands after controller disconnection, and
normal NAND I/O can also interfere with the temporary OTP mode.

Take the common NAND operation locks around the entire OTP transaction,
including restoring normal mode. This also rejects operations on a
disconnected chip and waits for suspended chips using the normal NAND
rules.

Fixes: e87161321a40 ("mtd: rawnand: macronix: OTP access for MX30LFxG18AC")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/internals.h     | 2 ++
 drivers/mtd/nand/raw/nand_base.c     | 4 ++--
 drivers/mtd/nand/raw/nand_macronix.c | 4 ++++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h
index b8b29341b88c..dcd9536da449 100644
--- a/drivers/mtd/nand/raw/internals.h
+++ b/drivers/mtd/nand/raw/internals.h
@@ -81,6 +81,8 @@ extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
 extern const struct mtd_pairing_scheme dist3_pairing_scheme;
 
 /* Core functions */
+int nand_get_device(struct nand_chip *chip);
+void nand_release_device(struct nand_chip *chip);
 const struct nand_manufacturer_desc *nand_get_manufacturer_desc(u8 id);
 int nand_bbm_get_next_page(struct nand_chip *chip, int page);
 int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 8f7d2571e8d9..2667faf02a91 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(nand_deselect_target);
  *
  * Release chip lock and wake up anyone waiting on the device.
  */
-static void nand_release_device(struct nand_chip *chip)
+void nand_release_device(struct nand_chip *chip)
 {
 	/* Release the controller and the chip */
 	mutex_unlock(&chip->controller->lock);
@@ -336,7 +336,7 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
  *
  * Lock the device and its controller for exclusive access
  */
-static int nand_get_device(struct nand_chip *chip)
+int nand_get_device(struct nand_chip *chip)
 {
 	/* Wait until the device is resumed. */
 	while (1) {
diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c
index 03237310852c..1ffe2fc4943e 100644
--- a/drivers/mtd/nand/raw/nand_macronix.c
+++ b/drivers/mtd/nand/raw/nand_macronix.c
@@ -380,6 +380,9 @@ static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd,
 	int ret;
 
 	nand = mtd_to_nand(mtd);
+	ret = nand_get_device(nand);
+	if (ret)
+		return ret;
 	nand_select_target(nand, 0);
 
 	ret = macronix_30lfxg18ac_otp_enable(nand);
@@ -426,6 +429,7 @@ static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd,
 			write ? "write" : "read");
 
 	nand_deselect_target(nand);
+	nand_release_device(nand);
 
 	return ret;
 }

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 07/58] mtd: rawnand: sunxi: retain chip state until the final MTD put
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (5 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 06/58] mtd: rawnand: macronix: serialize OTP access with NAND operations James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 08/58] mtd: rawnand: plat_nand: retain open-handle state on removal James Hilliard
                   ` (50 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

sunxi_nand_chips_cleanup() unregisters each MTD device and immediately
calls nand_cleanup(). Devres subsequently frees the chip and controller.
An open MTD handle can outlive this sequence: another operation or even
closing a writable handle reaches freed metadata and lock storage.

Unregister each MTD device while removal notifiers can still finish I/O,
then disconnect the chip and release the driver's initial MTD lifetime
reference. Defer nand_cleanup() and chip destruction until the final MTD
or device reference is released. Retain the chip name, flash node and
per-sector user-data lengths along with the NAND metadata.

Give the controller a reference for each retained chip and one owned by
devres. Register that devres action first so the IRQ and hardware
resources are released before the initial controller reference. The
shared lock and parent device remain valid until the last chip is
destroyed, while the disconnect gate prevents access to released
hardware resources.

Unwind heap allocations on probe failures, including attach failures for
which nand_scan() does not invoke the detach callback.

Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/sunxi_nand.c | 59 +++++++++++++++++++++++++++++----------
 1 file changed, 45 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index f41feebe4257..57894ce45065 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -27,6 +27,7 @@
 #include <linux/dmaengine.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
+#include <linux/mutex.h>
 #include <linux/reset.h>
 
 #define NFC_REG_CTL		0x0000
@@ -405,6 +406,13 @@ static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
 	return container_of(ctrl, struct sunxi_nfc, controller);
 }
 
+static void sunxi_nfc_release(struct nand_controller *controller)
+{
+	struct sunxi_nfc *nfc = to_sunxi_nfc(controller);
+
+	kfree(nfc);
+}
+
 static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
 {
 	struct sunxi_nfc *nfc = dev_id;
@@ -2027,9 +2035,8 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
 static void sunxi_nand_detach_chip(struct nand_chip *nand)
 {
 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
-	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
 
-	devm_kfree(nfc->dev, sunxi_nand->user_data_bytes);
+	kfree(sunxi_nand->user_data_bytes);
 	sunxi_nand->user_data_bytes = NULL;
 }
 
@@ -2045,8 +2052,7 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize
 	if (nsectors <= 0)
 		return -EINVAL;
 
-	sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors,
-						   GFP_KERNEL);
+	sunxi_nand->user_data_bytes = kzalloc(nsectors, GFP_KERNEL);
 	if (!sunxi_nand->user_data_bytes)
 		return -ENOMEM;
 
@@ -2427,6 +2433,13 @@ static const struct nand_controller_ops sunxi_nand_controller_ops = {
 	.exec_op = sunxi_nfc_exec_op,
 };
 
+static void sunxi_nand_release(struct nand_chip *chip)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
+
+	kfree(sunxi_nand);
+}
+
 static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
 {
 	struct sunxi_nand_chip *sunxi_nand;
@@ -2440,8 +2453,9 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
 		chip = &sunxi_nand->nand;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&sunxi_nand->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -2465,8 +2479,7 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 		return -EINVAL;
 	}
 
-	sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels),
-				  GFP_KERNEL);
+	sunxi_nand = kzalloc(struct_size(sunxi_nand, sels, nsels), GFP_KERNEL);
 	if (!sunxi_nand)
 		return -ENOMEM;
 
@@ -2477,19 +2490,21 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 		if (ret) {
 			dev_err(dev, "could not retrieve reg property: %d\n",
 				ret);
-			return ret;
+			goto err_free_chip;
 		}
 
 		if (tmp > NFC_MAX_CS) {
 			dev_err(dev,
 				"invalid reg value: %u (max CS = 7)\n",
 				tmp);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto err_free_chip;
 		}
 
 		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
 			dev_err(dev, "CS %d already assigned\n", tmp);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto err_free_chip;
 		}
 
 		sunxi_nand->sels[i].cs = tmp;
@@ -2518,18 +2533,32 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 
 	ret = nand_scan(nand, nsels);
 	if (ret)
-		return ret;
+		goto err_free_chip;
+
+	ret = nand_set_release(nand, sunxi_nand_release);
+	if (ret)
+		goto err_cleanup_nand;
+	mtd->owner = THIS_MODULE;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "failed to register mtd device: %d\n", ret);
-		nand_cleanup(nand);
+		nand_disconnect(nand);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&sunxi_nand->node, &nfc->chips);
 
 	return 0;
+
+err_cleanup_nand:
+	nand_cleanup(nand);
+err_free_chip:
+	/* nand_scan() does not detach a chip whose attach callback failed. */
+	kfree(sunxi_nand->user_data_bytes);
+	kfree(sunxi_nand);
+	return ret;
 }
 
 static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
@@ -2610,12 +2639,14 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
 	int irq;
 	int ret;
 
-	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
 
 	nfc->dev = dev;
-	nand_controller_init(&nfc->controller);
+	ret = devm_nand_controller_init(dev, &nfc->controller, sunxi_nfc_release);
+	if (ret)
+		return ret;
 	INIT_LIST_HEAD(&nfc->chips);
 
 	nfc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 08/58] mtd: rawnand: plat_nand: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (6 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 07/58] mtd: rawnand: sunxi: retain chip state until the final MTD put James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 09/58] mtd: rawnand: gpio: " James Hilliard
                   ` (49 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Unregister MTD while notifier clients can finish their I/O, then
disconnect the NAND chip before invoking the platform hardware teardown.
Keep the chip, controller lock and software ECC metadata until the last
MTD reference is dropped.

Handle registration failures through the same reference-counted cleanup,
since partitions can have become visible before registration fails.

Fixes: 711fdf627ce1 ("[MTD] [NAND] platform NAND driver: add driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/plat_nand.c | 24 +++++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c
index fe31551bcf5f..8321667fae2f 100644
--- a/drivers/mtd/nand/raw/plat_nand.c
+++ b/drivers/mtd/nand/raw/plat_nand.c
@@ -21,6 +21,11 @@ struct plat_nand_data {
 	struct gpio_desc	*ready_gpio;
 };
 
+static void plat_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct plat_nand_data, controller));
+}
+
 static int plat_nand_attach_chip(struct nand_chip *chip)
 {
 	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
@@ -65,10 +70,13 @@ static int plat_nand_probe(struct platform_device *pdev)
 	}
 
 	/* Allocate memory for the device structure (and zero it) */
-	data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data),
-			    GFP_KERNEL);
+	data = kzalloc_obj(*data);
 	if (!data)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &data->controller,
+					plat_nand_release);
+	if (err)
+		return err;
 
 	data->ready_gpio = devm_gpiod_get_optional(&pdev->dev, "ready",
 						   GPIOD_IN);
@@ -77,7 +85,6 @@ static int plat_nand_probe(struct platform_device *pdev)
 				     "could not get READY GPIO\n");
 
 	data->controller.ops = &plat_nand_ops;
-	nand_controller_init(&data->controller);
 	data->chip.controller = &data->controller;
 	chip = &data->chip;
 	nand_set_controller_data(chip, data);
@@ -126,6 +133,11 @@ static int plat_nand_probe(struct platform_device *pdev)
 		goto out;
 
 	part_types = pdata->chip.part_probe_types;
+	err = nand_set_release(chip, NULL);
+	if (err) {
+		nand_cleanup(chip);
+		goto out;
+	}
 
 	err = mtd_device_parse_register(mtd, part_types, NULL,
 					pdata->chip.partitions,
@@ -134,7 +146,8 @@ static int plat_nand_probe(struct platform_device *pdev)
 	if (!err)
 		return err;
 
-	nand_cleanup(&data->chip);
+	nand_disconnect(chip);
+	mtd_put_master(mtd);
 out:
 	if (pdata->ctrl.remove)
 		pdata->ctrl.remove(pdev);
@@ -153,9 +166,10 @@ static void plat_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	if (pdata->ctrl.remove)
 		pdata->ctrl.remove(pdev);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id plat_nand_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 09/58] mtd: rawnand: gpio: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (7 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 08/58] mtd: rawnand: plat_nand: retain open-handle state on removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 10/58] mtd: rawnand: stm32_fmc2: " James Hilliard
                   ` (48 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

A retained MTD handle can outlive GPIO NAND unbind. Drain and disconnect
NAND operations before disabling the chip, and keep the chip metadata
and controller lock alive until the final MTD reference is released.
GPIO and MMIO resources are still released during unbind.

Also release scanned NAND metadata when MTD registration fails, using
retained ownership once a device could have been published.

Fixes: aaf7ea200004 ("[MTD] [NAND] GPIO NAND flash driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/gpio.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c
index 69e5e43532a4..b6c98c374e39 100644
--- a/drivers/mtd/nand/raw/gpio.c
+++ b/drivers/mtd/nand/raw/gpio.c
@@ -265,6 +265,11 @@ gpio_nand_get_io_sync(struct platform_device *pdev)
 	return platform_get_resource(pdev, IORESOURCE_MEM, 1);
 }
 
+static void gpio_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct gpiomtd, base));
+}
+
 static void gpio_nand_remove(struct platform_device *pdev)
 {
 	struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
@@ -273,13 +278,14 @@ static void gpio_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	/* Enable write protection and disable the chip */
 	if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))
 		gpiod_set_value(gpiomtd->nwp, 0);
 	if (gpiomtd->nce && !IS_ERR(gpiomtd->nce))
 		gpiod_set_value(gpiomtd->nce, 0);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static int gpio_nand_probe(struct platform_device *pdev)
@@ -294,9 +300,12 @@ static int gpio_nand_probe(struct platform_device *pdev)
 	if (!dev->of_node && !dev_get_platdata(dev))
 		return -EINVAL;
 
-	gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL);
+	gpiomtd = kzalloc_obj(*gpiomtd);
 	if (!gpiomtd)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &gpiomtd->base, gpio_nand_release);
+	if (ret)
+		return ret;
 
 	chip = &gpiomtd->nand_chip;
 
@@ -345,7 +354,6 @@ static int gpio_nand_probe(struct platform_device *pdev)
 		goto out_ce;
 	}
 
-	nand_controller_init(&gpiomtd->base);
 	gpiomtd->base.ops = &gpio_nand_ops;
 
 	nand_set_flash_node(chip, pdev->dev.of_node);
@@ -374,11 +382,18 @@ static int gpio_nand_probe(struct platform_device *pdev)
 
 	if (gpiomtd->plat.adjust_parts)
 		gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
+	ret = nand_set_release(chip, NULL);
+	if (ret) {
+		nand_cleanup(chip);
+		goto err_wp;
+	}
 
 	ret = mtd_device_register(mtd, gpiomtd->plat.parts,
 				  gpiomtd->plat.num_parts);
 	if (!ret)
 		return 0;
+	nand_disconnect(chip);
+	mtd_put_master(mtd);
 
 err_wp:
 	if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp))

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 10/58] mtd: rawnand: stm32_fmc2: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (8 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 09/58] mtd: rawnand: gpio: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal James Hilliard
                   ` (47 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Keep the embedded NAND chip and controller serialization state alive for
MTD references that survive unbind. Disconnect operations before
releasing DMA channels and scatterlists and asserting write protection;
defer NAND metadata cleanup and controller storage release until the
final reference.

Preserve immediate cleanup before publication and use the retained
lifetime after MTD registration has been attempted.

Fixes: 2cd457f328c1 ("mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/stm32_fmc2_nand.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
index 9f42f8ab3212..4b89094c89e6 100644
--- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -1902,6 +1902,11 @@ static int stm32_fmc2_nfc_set_cdev(struct stm32_fmc2_nfc *nfc)
 	return 0;
 }
 
+static void stm32_fmc2_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct stm32_fmc2_nfc, base));
+}
+
 static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1915,12 +1920,14 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
 	int chip_cs, mem_region, ret, irq;
 	int start_region = 0;
 
-	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nfc->base, stm32_fmc2_nfc_release);
+	if (ret)
+		return ret;
 
 	nfc->dev = dev;
-	nand_controller_init(&nfc->base);
 	nfc->base.ops = &stm32_fmc2_nfc_controller_ops;
 
 	nfc->data = of_device_get_match_data(dev);
@@ -2024,9 +2031,15 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_wp_enable;
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(chip, NULL);
 	if (ret)
 		goto err_nand_cleanup;
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		goto err_wp_enable;
+	}
 
 	platform_set_drvdata(pdev, nfc);
 
@@ -2061,7 +2074,7 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	if (nfc->dma_ecc_ch)
 		dma_release_channel(nfc->dma_ecc_ch);
@@ -2074,6 +2087,7 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev)
 	sg_free_table(&nfc->dma_ecc_sg);
 
 	stm32_fmc2_nfc_wp_enable(nand);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static int stm32_fmc2_nfc_suspend(struct device *dev)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (9 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 10/58] mtd: rawnand: stm32_fmc2: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:25   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 12/58] mtd: rawnand: arasan: " James Hilliard
                   ` (46 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Both Denali frontends allocate chip and controller objects with
device-managed storage that disappears on unbind even when MTD
references remain. Give each chip a retained lifetime and a reference to
the controller, and keep the initial controller reference until IRQ and
mapped hardware resources have been released.

Unregister and disconnect each chip before disabling the controller.
Remove its list entry before dropping ownership, and use the same
release path for partially published probe failures. Denali chip
initialization now consumes its chip allocation on both success and
failure.

Fixes: ce082596ae43 ("mtd/nand: Add Intel Moorestown/Denali NAND support")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/denali.c     | 31 +++++++++++++++++++++++--------
 drivers/mtd/nand/raw/denali_dt.c  | 21 ++++++++++++++++-----
 drivers/mtd/nand/raw/denali_pci.c | 15 ++++++++++++---
 3 files changed, 51 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index fa2439cb4daa..4525cec3cae8 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -1176,6 +1176,12 @@ static const struct nand_controller_ops denali_controller_ops = {
 	.setup_interface = denali_setup_interface,
 };
 
+static void denali_chip_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct denali_chip, chip));
+}
+
+/* Takes ownership of dchip, including on failure. */
 int denali_chip_init(struct denali_controller *denali,
 		     struct denali_chip *dchip)
 {
@@ -1192,7 +1198,8 @@ int denali_chip_init(struct denali_controller *denali,
 
 		if (bank >= denali->nbanks) {
 			dev_err(denali->dev, "unsupported bank %d\n", bank);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		for (j = 0; j < i; j++) {
@@ -1200,7 +1207,8 @@ int denali_chip_init(struct denali_controller *denali,
 				dev_err(denali->dev,
 					"bank %d is assigned twice in the same chip\n",
 					bank);
-				return -EINVAL;
+				ret = -EINVAL;
+				goto free_chip;
 			}
 		}
 
@@ -1210,7 +1218,8 @@ int denali_chip_init(struct denali_controller *denali,
 					dev_err(denali->dev,
 						"bank %d is already used\n",
 						bank);
-					return -EINVAL;
+					ret = -EINVAL;
+					goto free_chip;
 				}
 			}
 		}
@@ -1250,12 +1259,17 @@ int denali_chip_init(struct denali_controller *denali,
 
 	ret = nand_scan(chip, dchip->nsels);
 	if (ret)
-		return ret;
+		goto free_chip;
+	ret = nand_set_release(chip, denali_chip_release);
+	if (ret)
+		goto cleanup_nand;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
-		goto cleanup_nand;
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		return ret;
 	}
 
 	list_add_tail(&dchip->node, &denali->chips);
@@ -1264,7 +1278,8 @@ int denali_chip_init(struct denali_controller *denali,
 
 cleanup_nand:
 	nand_cleanup(chip);
-
+free_chip:
+	kfree(dchip);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(denali_chip_init);
@@ -1274,7 +1289,6 @@ int denali_init(struct denali_controller *denali)
 	u32 features = ioread32(denali->reg + FEATURES);
 	int ret;
 
-	nand_controller_init(&denali->controller);
 	denali->controller.ops = &denali_controller_ops;
 	init_completion(&denali->complete);
 	spin_lock_init(&denali->irq_lock);
@@ -1368,8 +1382,9 @@ void denali_remove(struct denali_controller *denali)
 		chip = &dchip->chip;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&dchip->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 
 	denali_disable_irq(denali);
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index 8c822eae72e7..8d813cc05d04 100644
--- a/drivers/mtd/nand/raw/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -15,6 +15,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
+#include <linux/slab.h>
 
 #include "denali.h"
 
@@ -81,7 +82,7 @@ MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 static int denali_dt_chip_init(struct denali_controller *denali,
 			       struct device_node *chip_np)
 {
-	struct denali_chip *dchip;
+	struct denali_chip *dchip __free(kfree) = NULL;
 	u32 bank;
 	int nsels, i, ret;
 
@@ -89,8 +90,7 @@ static int denali_dt_chip_init(struct denali_controller *denali,
 	if (nsels < 0)
 		return nsels;
 
-	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
-			     GFP_KERNEL);
+	dchip = kzalloc(struct_size(dchip, sels, nsels), GFP_KERNEL);
 	if (!dchip)
 		return -ENOMEM;
 
@@ -106,7 +106,15 @@ static int denali_dt_chip_init(struct denali_controller *denali,
 		nand_set_flash_node(&dchip->chip, chip_np);
 	}
 
-	return denali_chip_init(denali, dchip);
+	return denali_chip_init(denali, no_free_ptr(dchip));
+}
+
+static void denali_dt_release(struct nand_controller *controller)
+{
+	struct denali_controller *denali;
+
+	denali = container_of(controller, struct denali_controller, controller);
+	kfree(container_of(denali, struct denali_dt, controller));
 }
 
 static int denali_dt_probe(struct platform_device *pdev)
@@ -117,10 +125,13 @@ static int denali_dt_probe(struct platform_device *pdev)
 	struct denali_controller *denali;
 	int ret;
 
-	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
+	dt = kzalloc_obj(*dt);
 	if (!dt)
 		return -ENOMEM;
 	denali = &dt->controller;
+	ret = devm_nand_controller_init(dev, &denali->controller, denali_dt_release);
+	if (ret)
+		return ret;
 
 	data = of_device_get_match_data(dev);
 	if (WARN_ON(!data))
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index f53df1b32dda..c39abe71941f 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/slab.h>
 
 #include "denali.h"
 
@@ -27,6 +28,11 @@ MODULE_DEVICE_TABLE(pci, denali_pci_ids);
 
 NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
 
+static void denali_pci_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct denali_controller, controller));
+}
+
 static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
 	resource_size_t csr_base, mem_base;
@@ -35,9 +41,13 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	struct denali_chip *dchip;
 	int nsels, ret, i;
 
-	denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
+	denali = kzalloc_obj(*denali);
 	if (!denali)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&dev->dev, &denali->controller,
+					denali_pci_release);
+	if (ret)
+		return ret;
 
 	ret = pcim_enable_device(dev);
 	if (ret) {
@@ -92,8 +102,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 	nsels = denali->nbanks;
 
-	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
-			     GFP_KERNEL);
+	dchip = kzalloc(struct_size(dchip, sels, nsels), GFP_KERNEL);
 	if (!dchip) {
 		ret = -ENOMEM;
 		goto out_remove_denali;

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 12/58] mtd: rawnand: arasan: retain chips across controller removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (10 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 13/58] mtd: rawnand: mxic: retain open-handle state on removal James Hilliard
                   ` (45 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Keep chip metadata, BCH state and controller storage until the last MTD
reference is released. Unregister and disconnect the chips before
releasing chip-select GPIOs, so notifier I/O and active NAND operations
finish while the hardware is usable.

Free unpublished chip allocations directly and use retained cleanup
after MTD registration has been attempted.

Fixes: 197b88fecc50 ("mtd: rawnand: arasan: Add new Arasan NAND controller")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/arasan-nand-controller.c | 57 ++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
index 865754737f5f..57b5d6119b70 100644
--- a/drivers/mtd/nand/raw/arasan-nand-controller.c
+++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
@@ -1269,6 +1269,11 @@ static const struct nand_controller_ops anfc_ops = {
 	.detach_chip = anfc_detach_chip,
 };
 
+static void anfc_chip_release(struct nand_chip *chip)
+{
+	kfree(to_anand(chip));
+}
+
 static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
 {
 	struct anand *anand;
@@ -1276,7 +1281,7 @@ static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
 	struct mtd_info *mtd;
 	int rb, ret, i;
 
-	anand = devm_kzalloc(nfc->dev, sizeof(*anand), GFP_KERNEL);
+	anand = kzalloc_obj(*anand);
 	if (!anand)
 		return -ENOMEM;
 
@@ -1284,31 +1289,35 @@ static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
 	anand->ncs_idx = of_property_count_elems_of_size(np, "reg", sizeof(u32));
 	if (anand->ncs_idx <= 0 || anand->ncs_idx > nfc->ncs) {
 		dev_err(nfc->dev, "Invalid reg property\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto free_chip;
 	}
 
 	anand->cs_idx = devm_kcalloc(nfc->dev, anand->ncs_idx,
 				     sizeof(*anand->cs_idx), GFP_KERNEL);
-	if (!anand->cs_idx)
-		return -ENOMEM;
+	if (!anand->cs_idx) {
+		ret = -ENOMEM;
+		goto free_chip;
+	}
 
 	for (i = 0; i < anand->ncs_idx; i++) {
 		ret = of_property_read_u32_index(np, "reg", i,
 						 &anand->cs_idx[i]);
 		if (ret) {
 			dev_err(nfc->dev, "invalid CS property: %d\n", ret);
-			return ret;
+			goto free_chip;
 		}
 	}
 
 	/* Ready-busy init */
 	ret = of_property_read_u32(np, "nand-rb", &rb);
 	if (ret)
-		return ret;
+		goto free_chip;
 
 	if (rb >= ANFC_MAX_CS) {
 		dev_err(nfc->dev, "Wrong RB %d\n", rb);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto free_chip;
 	}
 
 	anand->rb = rb;
@@ -1323,24 +1332,35 @@ static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
 	nand_set_flash_node(chip, np);
 	if (!mtd->name) {
 		dev_err(nfc->dev, "NAND label property is mandatory\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto free_chip;
 	}
 
 	ret = nand_scan(chip, anand->ncs_idx);
 	if (ret) {
 		dev_err(nfc->dev, "Scan operation failed\n");
-		return ret;
+		goto free_chip;
 	}
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(chip, anfc_chip_release);
 	if (ret) {
 		nand_cleanup(chip);
+		goto free_chip;
+	}
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&anand->node, &nfc->chips);
 
 	return 0;
+
+free_chip:
+	kfree(anand);
+	return ret;
 }
 
 static void anfc_chips_cleanup(struct arasan_nfc *nfc)
@@ -1353,8 +1373,9 @@ static void anfc_chips_cleanup(struct arasan_nfc *nfc)
 		chip = &anand->chip;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&anand->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -1431,17 +1452,24 @@ static int anfc_parse_cs(struct arasan_nfc *nfc)
 	return 0;
 }
 
+static void anfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct arasan_nfc, controller));
+}
+
 static int anfc_probe(struct platform_device *pdev)
 {
 	struct arasan_nfc *nfc;
 	int ret;
 
-	nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &nfc->controller, anfc_release);
+	if (ret)
+		return ret;
 
 	nfc->dev = &pdev->dev;
-	nand_controller_init(&nfc->controller);
 	nfc->controller.ops = &anfc_ops;
 	INIT_LIST_HEAD(&nfc->chips);
 
@@ -1481,13 +1509,12 @@ static void anfc_remove(struct platform_device *pdev)
 	int i;
 	struct arasan_nfc *nfc = platform_get_drvdata(pdev);
 
+	anfc_chips_cleanup(nfc);
 	for (i = 0; i < nfc->ncs; i++) {
 		if (nfc->cs_array[i]) {
 			gpiod_put(nfc->cs_array[i]);
 		}
 	}
-
-	anfc_chips_cleanup(nfc);
 }
 
 static const struct of_device_id anfc_ids[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 13/58] mtd: rawnand: mxic: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (11 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 12/58] mtd: rawnand: arasan: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 14/58] mtd: rawnand: intel: " James Hilliard
                   ` (44 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

A retained MTD handle must not access controller storage freed by
unbind. Disconnect NAND operations before disabling clocks and defer
NAND metadata and controller storage cleanup until the final MTD
reference.

Release scanned NAND metadata on registration failure as well, using
retained ownership once publication has been attempted.

Fixes: 738b0ca55f4f ("mtd: rawnand: Add Macronix raw NAND controller driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/mxic_nand.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/mxic_nand.c b/drivers/mtd/nand/raw/mxic_nand.c
index 92de26697359..0d20e6ba76a2 100644
--- a/drivers/mtd/nand/raw/mxic_nand.c
+++ b/drivers/mtd/nand/raw/mxic_nand.c
@@ -15,6 +15,7 @@
 #include <linux/mtd/nand-ecc-sw-hamming.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include "internals.h"
 
@@ -483,6 +484,11 @@ static const struct nand_controller_ops mxic_nand_controller_ops = {
 	.setup_interface = mxic_nfc_setup_interface,
 };
 
+static void mxic_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct mxic_nand_ctlr, controller));
+}
+
 static int mxic_nfc_probe(struct platform_device *pdev)
 {
 	struct device_node *nand_np, *np = pdev->dev.of_node;
@@ -492,10 +498,12 @@ static int mxic_nfc_probe(struct platform_device *pdev)
 	int err;
 	int irq;
 
-	nfc = devm_kzalloc(&pdev->dev, sizeof(struct mxic_nand_ctlr),
-			   GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &nfc->controller, mxic_nfc_release);
+	if (err)
+		return err;
 
 	nfc->ps_clk = devm_clk_get(&pdev->dev, "ps");
 	if (IS_ERR(nfc->ps_clk))
@@ -523,7 +531,6 @@ static int mxic_nfc_probe(struct platform_device *pdev)
 	nand_chip->priv = nfc;
 	nfc->dev = &pdev->dev;
 	nfc->controller.ops = &mxic_nand_controller_ops;
-	nand_controller_init(&nfc->controller);
 	nand_chip->controller = &nfc->controller;
 
 	irq = platform_get_irq(pdev, 0);
@@ -541,9 +548,17 @@ static int mxic_nfc_probe(struct platform_device *pdev)
 	if (err)
 		goto fail;
 
+	err = nand_set_release(nand_chip, NULL);
+	if (err) {
+		nand_cleanup(nand_chip);
+		goto fail;
+	}
 	err = mtd_device_register(mtd, NULL, 0);
-	if (err)
+	if (err) {
+		nand_disconnect(nand_chip);
+		mtd_put_master(mtd);
 		goto fail;
+	}
 
 	platform_set_drvdata(pdev, nfc);
 	return 0;
@@ -561,9 +576,10 @@ static void mxic_nfc_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	mxic_nfc_clk_disable(nfc);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id mxic_nfc_of_ids[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 14/58] mtd: rawnand: intel: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (12 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 13/58] mtd: rawnand: mxic: retain open-handle state on removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:31   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 15/58] mtd: rawnand: tegra: " James Hilliard
                   ` (43 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Disconnect NAND operations after unregistering MTD and before disabling
the controller or releasing DMA channels. Retain the embedded chip,
controller lock and NAND metadata until the last MTD reference is
released, including references obtained during a partially successful
probe.

Fixes: 0b1039f016e8 ("mtd: rawnand: Add NAND controller support on Intel LGM SoC")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/intel-nand-controller.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c
index 477a427dac1f..e4a24e69ebbc 100644
--- a/drivers/mtd/nand/raw/intel-nand-controller.c
+++ b/drivers/mtd/nand/raw/intel-nand-controller.c
@@ -573,6 +573,11 @@ static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
 		dma_release_channel(ebu_host->dma_tx);
 }
 
+static void ebu_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct ebu_nand_controller, controller));
+}
+
 static int ebu_nand_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -585,12 +590,14 @@ static int ebu_nand_probe(struct platform_device *pdev)
 	int ret;
 	u32 cs;
 
-	ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
+	ebu_host = kzalloc_obj(*ebu_host);
 	if (!ebu_host)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &ebu_host->controller, ebu_nand_release);
+	if (ret)
+		return ret;
 
 	ebu_host->dev = dev;
-	nand_controller_init(&ebu_host->controller);
 
 	ebu_host->ebu = devm_platform_ioremap_resource_byname(pdev, "ebunand");
 	if (IS_ERR(ebu_host->ebu))
@@ -692,9 +699,15 @@ static int ebu_nand_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_cleanup_dma;
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(nand, NULL);
 	if (ret)
 		goto err_clean_nand;
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(nand);
+		mtd_put_master(mtd);
+		goto err_cleanup_dma;
+	}
 
 	return 0;
 
@@ -715,9 +728,10 @@ static void ebu_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
 	WARN_ON(ret);
-	nand_cleanup(&ebu_host->chip);
+	nand_disconnect(&ebu_host->chip);
 	ebu_nand_disable(&ebu_host->chip);
 	ebu_dma_cleanup(ebu_host);
+	mtd_put_master(nand_to_mtd(&ebu_host->chip));
 }
 
 static const struct of_device_id ebu_nand_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 15/58] mtd: rawnand: tegra: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (13 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 14/58] mtd: rawnand: intel: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:32   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 16/58] mtd: rawnand: mtk: retain chips across controller removal James Hilliard
                   ` (42 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Retain the chip and controller allocations until their final MTD
references are released. Disconnect the chip before suspending the
controller so retained handles cannot reach powered-down hardware. Leave
metadata cleanup to the final put, including registration-failure
unwinding.

Fixes: d7d9f8ec77fe ("mtd: rawnand: add NVIDIA Tegra NAND Flash controller driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/tegra_nand.c | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
index 7f9eb5f042a7..da7791741b9d 100644
--- a/drivers/mtd/nand/raw/tegra_nand.c
+++ b/drivers/mtd/nand/raw/tegra_nand.c
@@ -1064,6 +1064,11 @@ static const struct nand_controller_ops tegra_nand_controller_ops = {
 	.setup_interface = tegra_nand_setup_interface,
 };
 
+static void tegra_nand_chip_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct tegra_nand_chip, chip));
+}
+
 static int tegra_nand_chips_init(struct device *dev,
 				 struct tegra_nand_controller *ctrl)
 {
@@ -1096,7 +1101,7 @@ static int tegra_nand_chips_init(struct device *dev,
 		return ret;
 	}
 
-	nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
+	nand = kzalloc_obj(*nand);
 	if (!nand)
 		return -ENOMEM;
 
@@ -1107,7 +1112,7 @@ static int tegra_nand_chips_init(struct device *dev,
 	if (IS_ERR(nand->wp_gpio)) {
 		ret = PTR_ERR(nand->wp_gpio);
 		dev_err(dev, "Failed to request WP GPIO: %d\n", ret);
-		return ret;
+		goto free_chip;
 	}
 
 	chip = &nand->chip;
@@ -1127,20 +1132,35 @@ static int tegra_nand_chips_init(struct device *dev,
 
 	ret = nand_scan(chip, 1);
 	if (ret)
-		return ret;
+		goto free_chip;
 
 	mtd_ooblayout_ecc(mtd, 0, &nand->ecc);
+	ret = nand_set_release(chip, tegra_nand_chip_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "Failed to register mtd device: %d\n", ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	ctrl->chip = chip;
 
 	return 0;
+
+free_chip:
+	kfree(nand);
+	return ret;
+}
+
+static void tegra_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct tegra_nand_controller, controller));
 }
 
 static int tegra_nand_probe(struct platform_device *pdev)
@@ -1149,13 +1169,15 @@ static int tegra_nand_probe(struct platform_device *pdev)
 	struct tegra_nand_controller *ctrl;
 	int err = 0;
 
-	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+	ctrl = kzalloc_obj(*ctrl);
 	if (!ctrl)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &ctrl->controller, tegra_nand_release);
+	if (err)
+		return err;
 
 	ctrl->dev = &pdev->dev;
 	platform_set_drvdata(pdev, ctrl);
-	nand_controller_init(&ctrl->controller);
 	ctrl->controller.ops = &tegra_nand_controller_ops;
 
 	ctrl->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1232,10 +1254,11 @@ static void tegra_nand_remove(struct platform_device *pdev)
 
 	WARN_ON(mtd_device_unregister(mtd));
 
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	pm_runtime_put_sync_suspend(ctrl->dev);
 	pm_runtime_force_suspend(ctrl->dev);
+	mtd_put_master(mtd);
 }
 
 static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 16/58] mtd: rawnand: mtk: retain chips across controller removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (14 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 15/58] mtd: rawnand: tegra: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 17/58] mtd: rawnand: rockchip: " James Hilliard
                   ` (41 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Give scanned chips their own storage lifetime and retain the shared
controller until all chip references have been dropped. Unregister and
disconnect each chip before releasing the ECC provider and clocks.
Unlink chips before the final put and unwind unpublished allocations
directly.

Fixes: 1d6b1e464950 ("mtd: mediatek: driver for MTK Smart Device")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/mtk_nand.c | 44 +++++++++++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c
index 21c7e1102746..bdc3d1519631 100644
--- a/drivers/mtd/nand/raw/mtk_nand.c
+++ b/drivers/mtd/nand/raw/mtk_nand.c
@@ -1336,6 +1336,11 @@ static const struct nand_controller_ops mtk_nfc_controller_ops = {
 	.exec_op = mtk_nfc_exec_op,
 };
 
+static void mtk_nfc_nand_release(struct nand_chip *nand)
+{
+	kfree(container_of(nand, struct mtk_nfc_nand_chip, nand));
+}
+
 static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
 				  struct device_node *np)
 {
@@ -1356,8 +1361,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
 		return -EINVAL;
 	}
 
-	chip = devm_kzalloc(dev, struct_size(chip, sels, nsels),
-			    GFP_KERNEL);
+	chip = kzalloc(struct_size(chip, sels, nsels), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
 
@@ -1366,17 +1370,19 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_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 >= MTK_NAND_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;
 		}
 
 		chip->sels[i] = tmp;
@@ -1415,18 +1421,28 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
 
 	ret = nand_scan(nand, nsels);
 	if (ret)
-		return ret;
+		goto free_chip;
+	ret = nand_set_release(nand, mtk_nfc_nand_release);
+	if (ret) {
+		nand_cleanup(nand);
+		goto free_chip;
+	}
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "mtd parse partition error\n");
-		nand_cleanup(nand);
+		nand_disconnect(nand);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&chip->node, &nfc->chips);
 
 	return 0;
+
+free_chip:
+	kfree(chip);
+	return ret;
 }
 
 static void mtk_nfc_nand_chips_cleanup(struct mtk_nfc *nfc)
@@ -1441,8 +1457,9 @@ static void mtk_nfc_nand_chips_cleanup(struct mtk_nfc *nfc)
 		chip = &mtk_chip->nand;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&mtk_chip->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -1504,6 +1521,11 @@ static const struct of_device_id mtk_nfc_id_table[] = {
 };
 MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
 
+static void mtk_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct mtk_nfc, controller));
+}
+
 static int mtk_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1511,11 +1533,13 @@ static int mtk_nfc_probe(struct platform_device *pdev)
 	struct mtk_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, mtk_nfc_release);
+	if (ret)
+		return ret;
 	INIT_LIST_HEAD(&nfc->chips);
 	nfc->controller.ops = &mtk_nfc_controller_ops;
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 17/58] mtd: rawnand: rockchip: retain chips across controller removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (15 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 16/58] mtd: rawnand: mtk: retain chips across controller removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 18/58] mtd: rawnand: atmel: " James Hilliard
                   ` (40 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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@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


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 18/58] mtd: rawnand: atmel: retain chips across controller removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (16 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 17/58] mtd: rawnand: rockchip: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:31   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal James Hilliard
                   ` (39 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Keep NAND chip metadata and SMC or HSMC controller storage until
retained MTD references have been released. Disconnect registered chips
before releasing DMA, SRAM and clock resources. Give chip initialization
ownership of its allocation on both success and failure, including
absent SmartMedia cards.

Fixes: f88fc122cc34 ("mtd: nand: Cleanup/rework the atmel_nand driver")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/atmel/nand-controller.c | 59 ++++++++++++++++++++++------
 1 file changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index 0bdbcbb50d94..10b725fa61d8 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -62,6 +62,7 @@
 #include <linux/iopoll.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include <soc/at91/atmel-sfr.h>
 
 #include "pmecc.h"
@@ -1628,8 +1629,9 @@ static int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
 	if (ret)
 		return ret;
 
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	list_del(&nand->node);
+	mtd_put_master(mtd);
 
 	return 0;
 }
@@ -1638,7 +1640,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
 					    struct device_node *np,
 					    int reg_cells)
 {
-	struct atmel_nand *nand;
+	struct atmel_nand *nand __free(kfree) = NULL;
 	struct gpio_desc *gpio;
 	int numcs, ret, i;
 
@@ -1649,7 +1651,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
 		return ERR_PTR(-EINVAL);
 	}
 
-	nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL);
+	nand = kzalloc(struct_size(nand, cs, numcs), GFP_KERNEL);
 	if (!nand)
 		return ERR_PTR(-ENOMEM);
 
@@ -1734,7 +1736,12 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
 
 	nand_set_flash_node(&nand->base, np);
 
-	return nand;
+	return_ptr(nand);
+}
+
+static void atmel_nand_release(struct nand_chip *chip)
+{
+	kfree(to_atmel_nand(chip));
 }
 
 static int
@@ -1748,6 +1755,7 @@ atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
 	/* No card inserted, skip this NAND. */
 	if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
 		dev_info(nc->dev, "No SmartMedia card inserted.\n");
+		kfree(nand);
 		return 0;
 	}
 
@@ -1756,19 +1764,29 @@ atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
 	ret = nand_scan(chip, nand->numcs);
 	if (ret) {
 		dev_err(nc->dev, "NAND scan failed: %d\n", ret);
-		return ret;
+		goto free_chip;
 	}
 
+	ret = nand_set_release(chip, atmel_nand_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&nand->node, &nc->chips);
 
 	return 0;
+
+free_chip:
+	kfree(nand);
+	return ret;
 }
 
 static int
@@ -1791,7 +1809,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
 {
 	struct device *dev = nc->dev;
 	struct platform_device *pdev = to_platform_device(dev);
-	struct atmel_nand *nand;
+	struct atmel_nand *nand __free(kfree) = NULL;
 	struct gpio_desc *gpio;
 	struct resource *res;
 
@@ -1799,7 +1817,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
 	 * Legacy bindings only allow connecting a single NAND with a unique CS
 	 * line to the controller.
 	 */
-	nand = devm_kzalloc(nc->dev, struct_size(nand, cs, 1), GFP_KERNEL);
+	nand = kzalloc(struct_size(nand, cs, 1), GFP_KERNEL);
 	if (!nand)
 		return -ENOMEM;
 
@@ -1857,7 +1875,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
 
 	nand_set_flash_node(&nand->base, nc->dev->of_node);
 
-	return atmel_nand_controller_add_nand(nc, nand);
+	return atmel_nand_controller_add_nand(nc, no_free_ptr(nand));
 }
 
 static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
@@ -2043,7 +2061,6 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
 	struct device_node *np = dev->of_node;
 	int ret;
 
-	nand_controller_init(&nc->base);
 	nc->base.ops = &atmel_nand_controller_ops;
 	INIT_LIST_HEAD(&nc->chips);
 	nc->dev = dev;
@@ -2349,6 +2366,11 @@ atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
 	return 0;
 }
 
+static void atmel_hsmc_nand_controller_release(struct nand_controller *controller)
+{
+	kfree(to_hsmc_nand_controller(controller));
+}
+
 static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
 				const struct atmel_nand_controller_caps *caps)
 {
@@ -2356,9 +2378,13 @@ static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
 	struct atmel_hsmc_nand_controller *nc;
 	int ret;
 
-	nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+	nc = kzalloc_obj(*nc);
 	if (!nc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nc->base.base,
+					atmel_hsmc_nand_controller_release);
+	if (ret)
+		return ret;
 
 	ret = atmel_nand_controller_init(&nc->base, pdev, caps);
 	if (ret)
@@ -2426,6 +2452,11 @@ static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
 	.legacy_of_bindings = true,
 };
 
+static void atmel_smc_nand_controller_release(struct nand_controller *controller)
+{
+	kfree(to_smc_nand_controller(controller));
+}
+
 static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
 				const struct atmel_nand_controller_caps *caps)
 {
@@ -2433,9 +2464,13 @@ static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
 	struct atmel_smc_nand_controller *nc;
 	int ret;
 
-	nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+	nc = kzalloc_obj(*nc);
 	if (!nc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nc->base.base,
+					atmel_smc_nand_controller_release);
+	if (ret)
+		return ret;
 
 	ret = atmel_nand_controller_init(&nc->base, pdev, caps);
 	if (ret)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (17 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 18/58] mtd: rawnand: atmel: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:29   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 20/58] mtd: rawnand: socrates: " James Hilliard
                   ` (38 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Retain the embedded chip and its copied name through the shared
allocation.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/orion_nand.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c
index 47e80d5e58c5..aae054e47db5 100644
--- a/drivers/mtd/nand/raw/orion_nand.c
+++ b/drivers/mtd/nand/raw/orion_nand.c
@@ -96,6 +96,11 @@ static const struct nand_controller_ops orion_nand_ops = {
 	.attach_chip = orion_nand_attach_chip,
 };
 
+static void orion_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct orion_nand_info, controller));
+}
+
 static int __init orion_nand_probe(struct platform_device *pdev)
 {
 	struct orion_nand_info *info;
@@ -106,15 +111,15 @@ static int __init orion_nand_probe(struct platform_device *pdev)
 	int ret = 0;
 	u32 val = 0;
 
-	info = devm_kzalloc(&pdev->dev,
-			sizeof(struct orion_nand_info),
-			GFP_KERNEL);
+	info = kzalloc_obj(*info);
 	if (!info)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &info->controller, orion_nand_release);
+	if (ret)
+		return ret;
 	nc = &info->chip;
 	mtd = nand_to_mtd(nc);
 
-	nand_controller_init(&info->controller);
 	info->controller.ops = &orion_nand_ops;
 	nc->controller = &info->controller;
 
@@ -186,9 +191,16 @@ static int __init orion_nand_probe(struct platform_device *pdev)
 		return ret;
 
 	mtd->name = "orion_nand";
-	ret = mtd_device_register(mtd, board->parts, board->nr_parts);
-	if (ret)
+	ret = nand_set_release(nc, NULL);
+	if (ret) {
 		nand_cleanup(nc);
+		return ret;
+	}
+	ret = mtd_device_register(mtd, board->parts, board->nr_parts);
+	if (ret) {
+		nand_disconnect(nc);
+		mtd_put_master(mtd);
+	}
 
 	return ret;
 }
@@ -202,7 +214,8 @@ static void orion_nand_remove(struct platform_device *pdev)
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
 
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 #ifdef CONFIG_OF

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 20/58] mtd: rawnand: socrates: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (18 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 21/58] mtd: rawnand: fsl_upm: " James Hilliard
                   ` (37 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Drain operations before unmapping the FPGA registers.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/socrates_nand.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c
index 668584683ce5..508e3afac4f2 100644
--- a/drivers/mtd/nand/raw/socrates_nand.c
+++ b/drivers/mtd/nand/raw/socrates_nand.c
@@ -134,6 +134,11 @@ static const struct nand_controller_ops socrates_ops = {
 /*
  * Probe for the NAND device.
  */
+static void socrates_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct socrates_nand_host, controller));
+}
+
 static int socrates_nand_probe(struct platform_device *ofdev)
 {
 	struct socrates_nand_host *host;
@@ -142,9 +147,12 @@ static int socrates_nand_probe(struct platform_device *ofdev)
 	int res;
 
 	/* Allocate memory for the device structure (and zero it) */
-	host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	res = devm_nand_controller_init(&ofdev->dev, &host->controller, socrates_nand_release);
+	if (res)
+		return res;
 
 	host->io_base = of_iomap(ofdev->dev.of_node, 0);
 	if (host->io_base == NULL) {
@@ -156,7 +164,6 @@ static int socrates_nand_probe(struct platform_device *ofdev)
 	mtd = nand_to_mtd(nand_chip);
 	host->dev = &ofdev->dev;
 
-	nand_controller_init(&host->controller);
 	host->controller.ops = &socrates_ops;
 	nand_chip->controller = &host->controller;
 
@@ -188,11 +195,17 @@ static int socrates_nand_probe(struct platform_device *ofdev)
 	if (res)
 		goto out;
 
+	res = nand_set_release(nand_chip, NULL);
+	if (res) {
+		nand_cleanup(nand_chip);
+		goto out;
+	}
 	res = mtd_device_register(mtd, NULL, 0);
 	if (!res)
 		return res;
 
-	nand_cleanup(nand_chip);
+	nand_disconnect(nand_chip);
+	mtd_put_master(mtd);
 
 out:
 	iounmap(host->io_base);
@@ -210,9 +223,10 @@ static void socrates_nand_remove(struct platform_device *ofdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	iounmap(host->io_base);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id socrates_nand_match[] =

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 21/58] mtd: rawnand: fsl_upm: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (19 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 20/58] mtd: rawnand: socrates: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 22/58] mtd: rawnand: pasemi: " James Hilliard
                   ` (36 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Also clean up scanned chips when partition registration fails.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/fsl_upm.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
index f4dc990a8da1..bfa4f450d7c6 100644
--- a/drivers/mtd/nand/raw/fsl_upm.c
+++ b/drivers/mtd/nand/raw/fsl_upm.c
@@ -69,7 +69,16 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
 	if (ret)
 		goto err;
 
+	ret = nand_set_release(&fun->chip, NULL);
+	if (ret) {
+		nand_cleanup(&fun->chip);
+		goto err;
+	}
 	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(&fun->chip);
+		mtd_put_master(mtd);
+	}
 err:
 	of_node_put(flash_np);
 	return ret;
@@ -160,6 +169,11 @@ static const struct nand_controller_ops fun_ops = {
 	.exec_op = fun_exec_op,
 };
 
+static void fun_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct fsl_upm_nand, base));
+}
+
 static int fun_probe(struct platform_device *ofdev)
 {
 	struct fsl_upm_nand *fun;
@@ -169,9 +183,12 @@ static int fun_probe(struct platform_device *ofdev)
 	int size;
 	int i;
 
-	fun = devm_kzalloc(&ofdev->dev, sizeof(*fun), GFP_KERNEL);
+	fun = kzalloc_obj(*fun);
 	if (!fun)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&ofdev->dev, &fun->base, fun_release);
+	if (ret)
+		return ret;
 
 	fun->io_base = devm_platform_get_and_ioremap_resource(ofdev, 0, &io_res);
 	if (IS_ERR(fun->io_base))
@@ -222,7 +239,6 @@ static int fun_probe(struct platform_device *ofdev)
 		}
 	}
 
-	nand_controller_init(&fun->base);
 	fun->base.ops = &fun_ops;
 	fun->dev = &ofdev->dev;
 
@@ -244,7 +260,8 @@ static void fun_remove(struct platform_device *ofdev)
 
 	ret = mtd_device_unregister(mtd);
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(mtd);
 }
 
 static const struct of_device_id of_fun_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 22/58] mtd: rawnand: pasemi: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (20 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 21/58] mtd: rawnand: fsl_upm: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 23/58] mtd: rawnand: ams-delta: " James Hilliard
                   ` (35 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Release the LPC region and NAND mapping after disconnection.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/pasemi_nand.c | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c
index 09409b703d93..d00d57cbb0f1 100644
--- a/drivers/mtd/nand/raw/pasemi_nand.c
+++ b/drivers/mtd/nand/raw/pasemi_nand.c
@@ -93,6 +93,11 @@ static const struct nand_controller_ops pasemi_ops = {
 	.attach_chip = pasemi_attach_chip,
 };
 
+static void pasemi_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct pasemi_ddata, controller));
+}
+
 static int pasemi_nand_probe(struct platform_device *ofdev)
 {
 	struct device *dev = &ofdev->dev;
@@ -122,8 +127,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
 	chip = &ddata->chip;
 	controller = &ddata->controller;
 
+	err = devm_nand_controller_init(dev, controller, pasemi_nand_release);
+	if (err)
+		return err;
 	controller->ops = &pasemi_ops;
-	nand_controller_init(controller);
 	chip->controller = controller;
 
 	pasemi_nand_mtd = nand_to_mtd(chip);
@@ -136,7 +143,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
 
 	if (!chip->legacy.IO_ADDR_R) {
 		err = -EIO;
-		goto out_mtd;
+		goto out;
 	}
 
 	pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa008, NULL);
@@ -174,10 +181,15 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
 	if (err)
 		goto out_lpc;
 
+	err = nand_set_release(chip, NULL);
+	if (err)
+		goto out_cleanup_nand;
 	if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) {
 		dev_err(dev, "Unable to register MTD device\n");
 		err = -ENODEV;
-		goto out_cleanup_nand;
+		nand_disconnect(chip);
+		mtd_put_master(pasemi_nand_mtd);
+		goto out_lpc;
 	}
 
 	dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res,
@@ -191,8 +203,6 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
 	release_region(ddata->lpcctl, 4);
  out_ior:
 	iounmap(chip->legacy.IO_ADDR_R);
- out_mtd:
-	kfree(ddata);
  out:
 	return err;
 }
@@ -210,14 +220,13 @@ static void pasemi_nand_remove(struct platform_device *ofdev)
 	/* Release resources, unregister device */
 	ret = mtd_device_unregister(pasemi_nand_mtd);
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	release_region(ddata->lpcctl, 4);
 
 	iounmap(chip->legacy.IO_ADDR_R);
 
-	/* Free the MTD device structure */
-	kfree(ddata);
+	mtd_put_master(pasemi_nand_mtd);
 }
 
 static const struct of_device_id pasemi_nand_match[] =

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 23/58] mtd: rawnand: ams-delta: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (21 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 22/58] mtd: rawnand: pasemi: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 24/58] mtd: rawnand: xway: " James Hilliard
                   ` (34 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Apply write protection after unregister notifiers finish their writes.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/ams-delta.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index fb2b7db70297..ca3fd966c4ff 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -233,6 +233,11 @@ static const struct nand_controller_ops gpio_nand_ops = {
 /*
  * Main initialization routine
  */
+static void gpio_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct gpio_nand, base));
+}
+
 static int gpio_nand_probe(struct platform_device *pdev)
 {
 	struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev);
@@ -250,10 +255,12 @@ static int gpio_nand_probe(struct platform_device *pdev)
 	}
 
 	/* Allocate memory for MTD device structure and private data */
-	priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand),
-			    GFP_KERNEL);
+	priv = kzalloc_obj(*priv);
 	if (!priv)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &priv->base, gpio_nand_release);
+	if (err)
+		return err;
 
 	this = &priv->nand_chip;
 
@@ -355,7 +362,6 @@ static int gpio_nand_probe(struct platform_device *pdev)
 
 	/* Initialize the NAND controller object embedded in gpio_nand. */
 	priv->base.ops = &gpio_nand_ops;
-	nand_controller_init(&priv->base);
 	this->controller = &priv->base;
 
 	/*
@@ -382,9 +388,15 @@ static int gpio_nand_probe(struct platform_device *pdev)
 		return err;
 
 	/* Register the partitions */
-	err = mtd_device_register(mtd, partitions, num_partitions);
+	err = nand_set_release(this, NULL);
 	if (err)
 		goto err_nand_cleanup;
+	err = mtd_device_register(mtd, partitions, num_partitions);
+	if (err) {
+		nand_disconnect(this);
+		mtd_put_master(mtd);
+		return err;
+	}
 
 	return 0;
 
@@ -403,13 +415,14 @@ static void gpio_nand_remove(struct platform_device *pdev)
 	struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
 	int ret;
 
-	/* Apply write protection */
-	gpiod_set_value(priv->gpiod_nwp, 1);
-
 	/* Unregister device */
 	ret = mtd_device_unregister(mtd);
 	WARN_ON(ret);
-	nand_cleanup(mtd_to_nand(mtd));
+	nand_disconnect(mtd_to_nand(mtd));
+
+	/* Apply write protection after draining outstanding operations. */
+	gpiod_set_value(priv->gpiod_nwp, 1);
+	mtd_put_master(mtd);
 }
 
 #ifdef CONFIG_OF

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 24/58] mtd: rawnand: xway: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (22 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 23/58] mtd: rawnand: ams-delta: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 27/58] mtd: rawnand: technologic: " James Hilliard
                   ` (33 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Retain the embedded controller and chip across platform-device removal.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/xway_nand.c | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c
index af84395dc66e..4686f28797bc 100644
--- a/drivers/mtd/nand/raw/xway_nand.c
+++ b/drivers/mtd/nand/raw/xway_nand.c
@@ -8,6 +8,7 @@
 #include <linux/mtd/rawnand.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include <lantiq_soc.h>
 
@@ -162,6 +163,11 @@ static const struct nand_controller_ops xway_nand_ops = {
 /*
  * Probe for the NAND device.
  */
+static void xway_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct xway_nand_data, controller));
+}
+
 static int xway_nand_probe(struct platform_device *pdev)
 {
 	struct xway_nand_data *data;
@@ -171,10 +177,12 @@ static int xway_nand_probe(struct platform_device *pdev)
 	u32 cs_flag = 0;
 
 	/* Allocate memory for the device structure (and zero it) */
-	data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data),
-			    GFP_KERNEL);
+	data = kzalloc_obj(*data);
 	if (!data)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &data->controller, xway_nand_release);
+	if (err)
+		return err;
 
 	data->nandaddr = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(data->nandaddr))
@@ -192,7 +200,6 @@ static int xway_nand_probe(struct platform_device *pdev)
 	data->chip.legacy.read_byte = xway_read_byte;
 	data->chip.legacy.chip_delay = 30;
 
-	nand_controller_init(&data->controller);
 	data->controller.ops = &xway_nand_ops;
 	data->chip.controller = &data->controller;
 
@@ -228,9 +235,16 @@ static int xway_nand_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	err = mtd_device_register(mtd, NULL, 0);
-	if (err)
+	err = nand_set_release(&data->chip, NULL);
+	if (err) {
 		nand_cleanup(&data->chip);
+		return err;
+	}
+	err = mtd_device_register(mtd, NULL, 0);
+	if (err) {
+		nand_disconnect(&data->chip);
+		mtd_put_master(mtd);
+	}
 
 	return err;
 }
@@ -246,7 +260,8 @@ static void xway_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id xway_nand_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 27/58] mtd: rawnand: technologic: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (23 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 24/58] mtd: rawnand: xway: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 28/58] mtd: rawnand: loongson: " James Hilliard
                   ` (32 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Transfer flash-node ownership to the retained-chip helper.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/technologic-nand-controller.c | 25 ++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/raw/technologic-nand-controller.c b/drivers/mtd/nand/raw/technologic-nand-controller.c
index a3294aaf43bd..a67f4d45f57b 100644
--- a/drivers/mtd/nand/raw/technologic-nand-controller.c
+++ b/drivers/mtd/nand/raw/technologic-nand-controller.c
@@ -132,6 +132,11 @@ static const struct nand_controller_ops ts72xx_nand_ops = {
 	.exec_op = ts72xx_nand_exec_op,
 };
 
+static void ts72xx_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct ts72xx_nand_data, controller));
+}
+
 static int ts72xx_nand_probe(struct platform_device *pdev)
 {
 	struct ts72xx_nand_data *data;
@@ -139,11 +144,13 @@ static int ts72xx_nand_probe(struct platform_device *pdev)
 	struct mtd_info *mtd;
 	int err;
 
-	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	data = kzalloc_obj(*data);
 	if (!data)
 		return -ENOMEM;
 
-	nand_controller_init(&data->controller);
+	err = devm_nand_controller_init(&pdev->dev, &data->controller, ts72xx_nand_release);
+	if (err)
+		return err;
 	data->controller.ops = &ts72xx_nand_ops;
 	data->chip.controller = &data->controller;
 
@@ -175,10 +182,17 @@ static int ts72xx_nand_probe(struct platform_device *pdev)
 	if (err)
 		goto err_handle_put;
 
-	err = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
+	err = nand_set_release(&data->chip, NULL);
 	if (err)
 		goto err_clean_nand;
+	err = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
+	if (err) {
+		nand_disconnect(&data->chip);
+		mtd_put_master(mtd);
+		goto err_handle_put;
+	}
 
+	fwnode_handle_put(child);
 	return 0;
 
 err_clean_nand:
@@ -191,14 +205,13 @@ static int ts72xx_nand_probe(struct platform_device *pdev)
 static void ts72xx_nand_remove(struct platform_device *pdev)
 {
 	struct ts72xx_nand_data *data = platform_get_drvdata(pdev);
-	struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
 	struct nand_chip *chip = &data->chip;
 	int ret;
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
-	fwnode_handle_put(fwnode);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id ts72xx_id_table[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 28/58] mtd: rawnand: loongson: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (24 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 27/58] mtd: rawnand: technologic: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 29/58] mtd: rawnand: ma35d1: " James Hilliard
                   ` (31 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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 the chip before DMA resource cleanup.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/loongson-nand-controller.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/loongson-nand-controller.c b/drivers/mtd/nand/raw/loongson-nand-controller.c
index 8490412d5be1..49938c7b9453 100644
--- a/drivers/mtd/nand/raw/loongson-nand-controller.c
+++ b/drivers/mtd/nand/raw/loongson-nand-controller.c
@@ -869,15 +869,26 @@ static int loongson_nand_chip_init(struct loongson_nand_host *host)
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to scan NAND chip\n");
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(chip, NULL);
 	if (ret) {
 		nand_cleanup(chip);
+		return ret;
+	}
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return dev_err_probe(dev, ret, "failed to register MTD device\n");
 	}
 
 	return 0;
 }
 
+static void loongson_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct loongson_nand_host, controller));
+}
+
 static int loongson_nand_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -890,9 +901,12 @@ static int loongson_nand_probe(struct platform_device *pdev)
 	if (!data)
 		return -ENODEV;
 
-	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &host->controller, loongson_nand_release);
+	if (ret)
+		return ret;
 
 	host->reg_base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(host->reg_base))
@@ -911,8 +925,6 @@ static int loongson_nand_probe(struct platform_device *pdev)
 	host->data = data;
 	host->controller.ops = &loongson_nand_controller_ops;
 
-	nand_controller_init(&host->controller);
-
 	ret = loongson_nand_controller_init(host);
 	if (ret)
 		goto err;
@@ -938,8 +950,9 @@ static void loongson_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	loongson_nand_controller_cleanup(host);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct loongson_nand_data ls1b_nand_data = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 29/58] mtd: rawnand: ma35d1: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (25 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 28/58] mtd: rawnand: loongson: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 30/58] mtd: rawnand: pl35x: " James Hilliard
                   ` (30 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Unlink each chip before dropping its initial lifetime reference.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 .../mtd/nand/raw/nuvoton-ma35d1-nand-controller.c  | 44 +++++++++++++++++-----
 1 file changed, 34 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
index f659d3a6b9ef..90bdf2635a44 100644
--- a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
+++ b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
@@ -849,6 +849,11 @@ static const struct nand_controller_ops ma35_nfc_ops = {
 	.exec_op = ma35_nfc_exec_op,
 };
 
+static void ma35_nand_chip_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct ma35_nand_chip, chip));
+}
+
 static int ma35_nand_chip_init(struct device *dev, struct ma35_nand_info *nand,
 			       struct device_node *np)
 {
@@ -866,8 +871,7 @@ static int ma35_nand_chip_init(struct device *dev, struct ma35_nand_info *nand,
 		return -EINVAL;
 	}
 
-	nvtnand = devm_kzalloc(dev, struct_size(nvtnand, sels, nsels),
-			       GFP_KERNEL);
+	nvtnand = kzalloc(struct_size(nvtnand, sels, nsels), GFP_KERNEL);
 	if (!nvtnand)
 		return -ENOMEM;
 
@@ -876,17 +880,19 @@ static int ma35_nand_chip_init(struct device *dev, struct ma35_nand_info *nand,
 		ret = of_property_read_u32_index(np, "reg", i, &cs);
 		if (ret) {
 			dev_err(dev, "reg property failure : %d\n", ret);
-			return ret;
+			goto free_chip;
 		}
 
 		if (cs >= MA35_MAX_NSELS) {
 			dev_err(dev, "invalid CS: %u\n", cs);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		if (test_and_set_bit(cs, &nand->assigned_cs)) {
 			dev_err(dev, "CS %u already assigned\n", cs);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		nvtnand->sels[i] = cs;
@@ -905,17 +911,27 @@ static int ma35_nand_chip_init(struct device *dev, struct ma35_nand_info *nand,
 	mtd_set_ooblayout(mtd, &ma35_ooblayout_ops);
 	ret = nand_scan(chip, nsels);
 	if (ret)
-		return ret;
+		goto free_chip;
+	ret = nand_set_release(chip, ma35_nand_chip_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&nvtnand->node, &nand->chips);
 
 	return 0;
+
+free_chip:
+	kfree(nvtnand);
+	return ret;
 }
 
 static void ma35_chips_cleanup(struct ma35_nand_info *nand)
@@ -928,8 +944,9 @@ static void ma35_chips_cleanup(struct ma35_nand_info *nand)
 		chip = &nvtnand->chip;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&nvtnand->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -948,16 +965,23 @@ static int ma35_nand_chips_init(struct device *dev, struct ma35_nand_info *nand)
 	return 0;
 }
 
+static void ma35_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct ma35_nand_info, controller));
+}
+
 static int ma35_nand_probe(struct platform_device *pdev)
 {
 	struct ma35_nand_info *nand;
 	int ret = 0;
 
-	nand = devm_kzalloc(&pdev->dev, sizeof(*nand), GFP_KERNEL);
+	nand = kzalloc_obj(*nand);
 	if (!nand)
 		return -ENOMEM;
 
-	nand_controller_init(&nand->controller);
+	ret = devm_nand_controller_init(&pdev->dev, &nand->controller, ma35_nand_release);
+	if (ret)
+		return ret;
 	INIT_LIST_HEAD(&nand->chips);
 	nand->controller.ops = &ma35_nfc_ops;
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 30/58] mtd: rawnand: pl35x: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (26 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 29/58] mtd: rawnand: ma35d1: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 31/58] mtd: rawnand: mxc: " James Hilliard
                   ` (29 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Unwind chip ownership on registration failures as well as removal.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/pl35x-nand-controller.c | 46 ++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c
index 26b61d856143..952fe5dcc84d 100644
--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
+++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
@@ -1049,6 +1049,11 @@ static int pl35x_nand_reset_state(struct pl35x_nandc *nfc)
 	return 0;
 }
 
+static void pl35x_nand_chip_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct pl35x_nand, chip));
+}
+
 static int pl35x_nand_chip_init(struct pl35x_nandc *nfc,
 				struct device_node *np)
 {
@@ -1057,22 +1062,24 @@ static int pl35x_nand_chip_init(struct pl35x_nandc *nfc,
 	struct mtd_info *mtd;
 	int cs, ret;
 
-	plnand = devm_kzalloc(nfc->dev, sizeof(*plnand), GFP_KERNEL);
+	plnand = kzalloc_obj(*plnand);
 	if (!plnand)
 		return -ENOMEM;
 
 	ret = of_property_read_u32(np, "reg", &cs);
 	if (ret)
-		return ret;
+		goto free_chip;
 
 	if (cs >= PL35X_NAND_MAX_CS) {
 		dev_err(nfc->dev, "Wrong CS %d\n", cs);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto free_chip;
 	}
 
 	if (test_and_set_bit(cs, &nfc->assigned_cs)) {
 		dev_err(nfc->dev, "Already assigned CS %d\n", cs);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto free_chip;
 	}
 
 	plnand->cs = cs;
@@ -1089,23 +1096,34 @@ static int pl35x_nand_chip_init(struct pl35x_nandc *nfc,
 					   "%s", PL35X_NANDC_DRIVER_NAME);
 		if (!mtd->name) {
 			dev_err(nfc->dev, "Failed to allocate mtd->name\n");
-			return -ENOMEM;
+			ret = -ENOMEM;
+			goto free_chip;
 		}
 	}
 
 	ret = nand_scan(chip, 1);
 	if (ret)
-		return ret;
+		goto free_chip;
+	ret = nand_set_release(chip, pl35x_nand_chip_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&plnand->node, &nfc->chips);
 
 	return ret;
+
+free_chip:
+	kfree(plnand);
+	return ret;
 }
 
 static void pl35x_nand_chips_cleanup(struct pl35x_nandc *nfc)
@@ -1118,8 +1136,9 @@ static void pl35x_nand_chips_cleanup(struct pl35x_nandc *nfc)
 		chip = &plnand->chip;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&plnand->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -1146,6 +1165,11 @@ static int pl35x_nand_chips_init(struct pl35x_nandc *nfc)
 	return ret;
 }
 
+static void pl35x_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct pl35x_nandc, controller));
+}
+
 static int pl35x_nand_probe(struct platform_device *pdev)
 {
 	struct device *smc_dev = pdev->dev.parent;
@@ -1153,12 +1177,14 @@ static int pl35x_nand_probe(struct platform_device *pdev)
 	struct pl35x_nandc *nfc;
 	int ret;
 
-	nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &nfc->controller, pl35x_nand_release);
+	if (ret)
+		return ret;
 
 	nfc->dev = &pdev->dev;
-	nand_controller_init(&nfc->controller);
 	nfc->controller.ops = &pl35x_nandc_ops;
 	INIT_LIST_HEAD(&nfc->chips);
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 31/58] mtd: rawnand: mxc: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (27 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 30/58] mtd: rawnand: pl35x: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 32/58] mtd: rawnand: fsmc: " James Hilliard
                   ` (28 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Retain the embedded legacy controller and drain I/O before clock
disable.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/mxc_nand.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
index 4d8b92e7e672..d9f1e57c1ebf 100644
--- a/drivers/mtd/nand/raw/mxc_nand.c
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -1686,6 +1686,11 @@ static const struct nand_controller_ops mxcnd_controller_ops = {
 	.exec_op = mxcnd_exec_op,
 };
 
+static void mxcnd_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct mxc_nand_host, nand.legacy.dummy_controller));
+}
+
 static int mxcnd_probe(struct platform_device *pdev)
 {
 	struct nand_chip *this;
@@ -1694,10 +1699,14 @@ static int mxcnd_probe(struct platform_device *pdev)
 	int err = 0;
 
 	/* Allocate memory for MTD device structure and private data */
-	host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host),
-			GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	this = &host->nand;
+	this->controller = &this->legacy.dummy_controller;
+	err = devm_nand_controller_init(&pdev->dev, this->controller, mxcnd_release);
+	if (err)
+		return err;
 
 	/* allocate a temporary buffer for the nand_scan_ident() */
 	host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL);
@@ -1796,9 +1805,15 @@ static int mxcnd_probe(struct platform_device *pdev)
 		goto escan;
 
 	/* Register the partitions */
-	err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+	err = nand_set_release(this, NULL);
 	if (err)
 		goto cleanup_nand;
+	err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+	if (err) {
+		nand_disconnect(this);
+		mtd_put_master(mtd);
+		goto escan;
+	}
 
 	platform_set_drvdata(pdev, host);
 
@@ -1821,9 +1836,10 @@ static void mxcnd_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	if (host->clk_act)
 		clk_disable_unprepare(host->clk);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver mxcnd_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 32/58] mtd: rawnand: fsmc: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (28 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 31/58] mtd: rawnand: mxc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 33/58] mtd: rawnand: gpmi: " James Hilliard
                   ` (27 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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 before controller disable and DMA channel release.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/fsmc_nand.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index 0b1817cdfc5a..ef0738c4cca1 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -1024,6 +1024,11 @@ static void fsmc_nand_disable(struct fsmc_nand_data *host)
  * fsmc_nand_probe - Probe function
  * @pdev:       platform device structure
  */
+static void fsmc_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct fsmc_nand_data, base));
+}
+
 static int __init fsmc_nand_probe(struct platform_device *pdev)
 {
 	struct fsmc_nand_data *host;
@@ -1037,9 +1042,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 	int i;
 
 	/* Allocate memory for the device structure (and zero it) */
-	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &host->base, fsmc_nand_release);
+	if (ret)
+		return ret;
 
 	nand = &host->nand;
 
@@ -1128,7 +1136,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 		nand->options |= NAND_KEEP_TIMINGS;
 	}
 
-	nand_controller_init(&host->base);
 	host->base.ops = &fsmc_nand_controller_ops;
 	nand->controller = &host->base;
 
@@ -1140,9 +1147,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 		goto release_dma_write_chan;
 
 	mtd->name = "nand";
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(nand, NULL);
 	if (ret)
 		goto cleanup_nand;
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(nand);
+		mtd_put_master(mtd);
+		goto release_dma_write_chan;
+	}
 
 	platform_set_drvdata(pdev, host);
 	dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
@@ -1176,13 +1189,14 @@ static void fsmc_nand_remove(struct platform_device *pdev)
 
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		fsmc_nand_disable(host);
 
 		if (host->mode == USE_DMA_ACCESS) {
 			dma_release_channel(host->write_dma_chan);
 			dma_release_channel(host->read_dma_chan);
 		}
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 33/58] mtd: rawnand: gpmi: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (29 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 32/58] mtd: rawnand: fsmc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 34/58] mtd: rawnand: omap2: " James Hilliard
                   ` (26 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Keep geometry available after removal while releasing DMA and clocks.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index 527165ccc839..cda3bbdca928 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -2744,7 +2744,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
 	if (ret)
 		return ret;
 
-	nand_controller_init(&this->base);
 	this->base.ops = &gpmi_nand_controller_ops;
 	chip->controller = &this->base;
 
@@ -2759,9 +2758,15 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
 	if (ret)
 		goto err_nand_cleanup;
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(chip, NULL);
 	if (ret)
 		goto err_nand_cleanup;
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		goto err_out;
+	}
 	return 0;
 
 err_nand_cleanup:
@@ -2782,14 +2787,22 @@ static const struct of_device_id gpmi_nand_id_table[] = {
 };
 MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
 
+static void gpmi_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct gpmi_nand_data, base));
+}
+
 static int gpmi_nand_probe(struct platform_device *pdev)
 {
 	struct gpmi_nand_data *this;
 	int ret;
 
-	this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
+	this = kzalloc_obj(*this);
 	if (!this)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &this->base, gpmi_nand_release);
+	if (ret)
+		return ret;
 
 	this->devdata = of_device_get_match_data(&pdev->dev);
 	platform_set_drvdata(pdev, this);
@@ -2838,7 +2851,7 @@ static void gpmi_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	gpmi_free_dma_buffer(this);
 	release_resources(this);
 	pm_runtime_dont_use_autosuspend(&pdev->dev);
@@ -2846,6 +2859,7 @@ static void gpmi_nand_remove(struct platform_device *pdev)
 #ifndef CONFIG_PM
 	gpmi_disable_clk(this);
 #endif
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static int gpmi_pm_suspend(struct device *dev)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 34/58] mtd: rawnand: omap2: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (30 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 33/58] mtd: rawnand: gpmi: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 35/58] mtd: rawnand: marvell: " James Hilliard
                   ` (25 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Preserve the shared GPMC lock and separately retain each device's state.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/omap2.c | 45 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 37 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index 4909e62dc11e..9949d4d53299 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -142,6 +142,7 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 
 struct omap_nand_info {
+	struct kref			refcnt;
 	struct nand_chip		nand;
 	struct platform_device		*pdev;
 
@@ -2208,6 +2209,24 @@ static const struct nand_controller_ops omap_nand_controller_ops = {
 static struct nand_controller omap_gpmc_controller;
 static bool omap_gpmc_controller_initialized;
 
+static void omap_nand_info_release(struct kref *ref)
+{
+	kfree(container_of(ref, struct omap_nand_info, refcnt));
+}
+
+static void omap_nand_info_put(void *data)
+{
+	struct omap_nand_info *info = data;
+
+	kref_put(&info->refcnt, omap_nand_info_release);
+}
+
+static void omap_nand_release(struct nand_chip *chip)
+{
+	rawnand_sw_bch_cleanup(chip);
+	omap_nand_info_put(mtd_to_omap(nand_to_mtd(chip)));
+}
+
 static int omap_nand_probe(struct platform_device *pdev)
 {
 	struct omap_nand_info		*info;
@@ -2218,10 +2237,13 @@ static int omap_nand_probe(struct platform_device *pdev)
 	struct device			*dev = &pdev->dev;
 	void __iomem *vaddr;
 
-	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
-				GFP_KERNEL);
+	info = kzalloc_obj(*info);
 	if (!info)
 		return -ENOMEM;
+	kref_init(&info->refcnt);
+	err = devm_add_action_or_reset(dev, omap_nand_info_put, info);
+	if (err)
+		return err;
 
 	info->pdev = pdev;
 
@@ -2282,9 +2304,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 	if (err)
 		goto return_error;
 
-	err = mtd_device_register(mtd, NULL, 0);
+	err = nand_set_release(nand_chip, omap_nand_release);
 	if (err)
 		goto cleanup_nand;
+	kref_get(&info->refcnt);
+	err = mtd_device_register(mtd, NULL, 0);
+	if (err) {
+		nand_disconnect(nand_chip);
+		mtd_put_master(mtd);
+		goto release_dma;
+	}
 
 	platform_set_drvdata(pdev, mtd);
 
@@ -2294,11 +2323,11 @@ static int omap_nand_probe(struct platform_device *pdev)
 	nand_cleanup(nand_chip);
 
 return_error:
+	rawnand_sw_bch_cleanup(nand_chip);
+release_dma:
 	if (!IS_ERR_OR_NULL(info->dma))
 		dma_release_channel(info->dma);
 
-	rawnand_sw_bch_cleanup(nand_chip);
-
 	return err;
 }
 
@@ -2308,12 +2337,12 @@ static void omap_nand_remove(struct platform_device *pdev)
 	struct nand_chip *nand_chip = mtd_to_nand(mtd);
 	struct omap_nand_info *info = mtd_to_omap(mtd);
 
-	rawnand_sw_bch_cleanup(nand_chip);
+	WARN_ON(mtd_device_unregister(mtd));
+	nand_disconnect(nand_chip);
 
 	if (info->dma)
 		dma_release_channel(info->dma);
-	WARN_ON(mtd_device_unregister(mtd));
-	nand_cleanup(nand_chip);
+	mtd_put_master(mtd);
 }
 
 static const struct of_device_id omap_nand_ids[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 35/58] mtd: rawnand: marvell: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (31 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 34/58] mtd: rawnand: omap2: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 36/58] mtd: rawnand: renesas: " James Hilliard
                   ` (24 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Retain the layout metadata used by OOB queries and unlink before put.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/marvell_nand.c | 54 ++++++++++++++++++++++++++-----------
 1 file changed, 39 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index 38b7eb5b992c..f3967024411c 100644
--- a/drivers/mtd/nand/raw/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -2600,6 +2600,11 @@ static const struct nand_controller_ops marvell_nand_controller_ops = {
 	.setup_interface = marvell_nfc_setup_interface,
 };
 
+static void marvell_nand_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct marvell_nand_chip, chip));
+}
+
 static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 				  struct device_node *np)
 {
@@ -2630,9 +2635,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 	}
 
 	/* Alloc the nand chip structure */
-	marvell_nand = devm_kzalloc(dev,
-				    struct_size(marvell_nand, sels, nsels),
-				    GFP_KERNEL);
+	marvell_nand = kzalloc(struct_size(marvell_nand, sels, nsels), GFP_KERNEL);
 	if (!marvell_nand) {
 		dev_err(dev, "could not allocate chip structure\n");
 		return -ENOMEM;
@@ -2654,19 +2657,21 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 			if (ret) {
 				dev_err(dev, "could not retrieve reg property: %d\n",
 					ret);
-				return ret;
+				goto free_chip;
 			}
 		}
 
 		if (cs >= nfc->caps->max_cs_nb) {
 			dev_err(dev, "invalid reg value: %u (max CS = %d)\n",
 				cs, nfc->caps->max_cs_nb);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		if (test_and_set_bit(cs, &nfc->assigned_cs)) {
 			dev_err(dev, "CS %d already assigned\n", cs);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		/*
@@ -2688,7 +2693,8 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 			marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL;
 			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		/* Retrieve RB id */
@@ -2702,14 +2708,15 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 				dev_err(dev,
 					"could not retrieve RB property: %d\n",
 					ret);
-				return ret;
+				goto free_chip;
 			}
 		}
 
 		if (rb >= nfc->caps->max_rb_nb) {
 			dev_err(dev, "invalid reg value: %u (max RB = %d)\n",
 				rb, nfc->caps->max_rb_nb);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		marvell_nand->sels[i].rb = rb;
@@ -2737,9 +2744,14 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 	ret = nand_scan(chip, marvell_nand->nsels);
 	if (ret) {
 		dev_err(dev, "could not scan the nand chip\n");
-		return ret;
+		goto free_chip;
 	}
 
+	ret = nand_set_release(chip, marvell_nand_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 	if (pdata)
 		/* Legacy bindings support only one chip */
 		ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
@@ -2747,13 +2759,18 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 		ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "failed to register mtd device: %d\n", ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&marvell_nand->node, &nfc->chips);
 
 	return 0;
+
+free_chip:
+	kfree(marvell_nand);
+	return ret;
 }
 
 static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc)
@@ -2766,8 +2783,9 @@ static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc)
 		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));
 	}
 }
 
@@ -2932,6 +2950,11 @@ static int marvell_nfc_init(struct marvell_nfc *nfc)
 	return 0;
 }
 
+static void marvell_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct marvell_nfc, controller));
+}
+
 static int marvell_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -2939,13 +2962,14 @@ static int marvell_nfc_probe(struct platform_device *pdev)
 	int ret;
 	int irq;
 
-	nfc = devm_kzalloc(&pdev->dev, sizeof(struct marvell_nfc),
-			   GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nfc->controller, marvell_nfc_release);
+	if (ret)
+		return ret;
 
 	nfc->dev = dev;
-	nand_controller_init(&nfc->controller);
 	nfc->controller.ops = &marvell_nand_controller_ops;
 	INIT_LIST_HEAD(&nfc->chips);
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 36/58] mtd: rawnand: renesas: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (32 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 35/58] mtd: rawnand: marvell: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 37/58] mtd: rawnand: meson: " James Hilliard
                   ` (23 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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@gmail.com>
---
 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


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 37/58] mtd: rawnand: meson: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (33 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 36/58] mtd: rawnand: renesas: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 38/58] mtd: rawnand: cadence: " James Hilliard
                   ` (22 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

The deferred detach callback only frees heap-allocated transfer buffers.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/meson_nand.c | 55 +++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index b8834aa96e81..4f33ea7a1b4d 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -1381,6 +1381,11 @@ static const struct nand_controller_ops meson_nand_controller_ops = {
 	.exec_op = meson_nfc_exec_op,
 };
 
+static void meson_nfc_nand_release(struct nand_chip *nand)
+{
+	kfree(to_meson_nand(nand));
+}
+
 static int
 meson_nfc_nand_chip_init(struct device *dev,
 			 struct meson_nfc *nfc, struct device_node *np)
@@ -1398,8 +1403,7 @@ meson_nfc_nand_chip_init(struct device *dev,
 		return -EINVAL;
 	}
 
-	meson_chip = devm_kzalloc(dev, struct_size(meson_chip, sels, nsels),
-				  GFP_KERNEL);
+	meson_chip = kzalloc(struct_size(meson_chip, sels, nsels), GFP_KERNEL);
 	if (!meson_chip)
 		return -ENOMEM;
 
@@ -1410,12 +1414,13 @@ meson_nfc_nand_chip_init(struct device *dev,
 		if (ret) {
 			dev_err(dev, "could not retrieve register property: %d\n",
 				ret);
-			return ret;
+			goto free_chip;
 		}
 
 		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
 			dev_err(dev, "CS %d already assigned\n", tmp);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 	}
 
@@ -1434,14 +1439,16 @@ meson_nfc_nand_chip_init(struct device *dev,
 	if (ret == -EINVAL)
 		nfc->no_rb_pin = true;
 	else if (ret)
-		return ret;
+		goto free_chip;
 
-	if (nand_rb_val)
-		return -EINVAL;
+	if (nand_rb_val) {
+		ret = -EINVAL;
+		goto free_chip;
+	}
 
 	ret = nand_scan(nand, nsels);
 	if (ret)
-		return ret;
+		goto free_chip;
 
 	if (nand->options & NAND_IS_BOOT_MEDIUM) {
 		ret = of_property_read_u32(np, "amlogic,boot-pages",
@@ -1449,8 +1456,7 @@ meson_nfc_nand_chip_init(struct device *dev,
 		if (ret) {
 			dev_err(dev, "could not retrieve 'amlogic,boot-pages' property: %d",
 				ret);
-			nand_cleanup(nand);
-			return ret;
+			goto cleanup_nand;
 		}
 
 		ret = of_property_read_u32(np, "amlogic,boot-page-step",
@@ -1458,21 +1464,30 @@ meson_nfc_nand_chip_init(struct device *dev,
 		if (ret) {
 			dev_err(dev, "could not retrieve 'amlogic,boot-page-step' property: %d",
 				ret);
-			nand_cleanup(nand);
-			return ret;
+			goto cleanup_nand;
 		}
 	}
 
+	ret = nand_set_release(nand, meson_nfc_nand_release);
+	if (ret)
+		goto cleanup_nand;
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "failed to register MTD device: %d\n", ret);
-		nand_cleanup(nand);
+		nand_disconnect(nand);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&meson_chip->node, &nfc->chips);
 
 	return 0;
+
+cleanup_nand:
+	nand_cleanup(nand);
+free_chip:
+	kfree(meson_chip);
+	return ret;
 }
 
 static void meson_nfc_nand_chips_cleanup(struct meson_nfc *nfc)
@@ -1486,8 +1501,9 @@ static void meson_nfc_nand_chips_cleanup(struct meson_nfc *nfc)
 		mtd = nand_to_mtd(&meson_chip->nand);
 		WARN_ON(mtd_device_unregister(mtd));
 
-		nand_cleanup(&meson_chip->nand);
+		nand_disconnect(&meson_chip->nand);
 		list_del(&meson_chip->node);
+		mtd_put_master(mtd);
 	}
 }
 
@@ -1544,21 +1560,28 @@ static const struct of_device_id meson_nfc_id_table[] = {
 };
 MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
 
+static void meson_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct meson_nfc, controller));
+}
+
 static int meson_nfc_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct meson_nfc *nfc;
 	int ret, irq;
 
-	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nfc->controller, meson_nfc_release);
+	if (ret)
+		return ret;
 
 	nfc->data = of_device_get_match_data(&pdev->dev);
 	if (!nfc->data)
 		return -ENODEV;
 
-	nand_controller_init(&nfc->controller);
 	INIT_LIST_HEAD(&nfc->chips);
 	init_completion(&nfc->completion);
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 38/58] mtd: rawnand: cadence: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (34 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 37/58] mtd: rawnand: meson: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 39/58] mtd: rawnand: qcom: " James Hilliard
                   ` (21 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Allocate the final shared buffer before publishing any chip.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/cadence-nand-controller.c | 60 +++++++++++++++++++-------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index d53b35a8b3cb..65c4eab600af 100644
--- a/drivers/mtd/nand/raw/cadence-nand-controller.c
+++ b/drivers/mtd/nand/raw/cadence-nand-controller.c
@@ -562,6 +562,7 @@ struct cdns_nand_chip {
 	struct cadence_nand_timings timings;
 	struct nand_chip chip;
 	u8 nsels;
+	bool registered;
 	struct list_head node;
 
 	/*
@@ -2991,6 +2992,11 @@ static const struct nand_controller_ops cadence_nand_controller_ops = {
 	.setup_interface = cadence_nand_setup_interface,
 };
 
+static void cadence_nand_chip_release(struct nand_chip *chip)
+{
+	kfree(to_cdns_nand_chip(chip));
+}
+
 static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
 				  struct device_node *np)
 {
@@ -3007,9 +3013,7 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
 	}
 
 	/* Allocate the nand chip structure. */
-	cdns_chip = devm_kzalloc(cdns_ctrl->dev, sizeof(*cdns_chip) +
-				 (nsels * sizeof(u8)),
-				 GFP_KERNEL);
+	cdns_chip = kzalloc(struct_size(cdns_chip, cs, nsels), GFP_KERNEL);
 	if (!cdns_chip) {
 		dev_err(cdns_ctrl->dev, "could not allocate chip structure\n");
 		return -ENOMEM;
@@ -3024,20 +3028,22 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
 			dev_err(cdns_ctrl->dev,
 				"could not retrieve reg property: %d\n",
 				ret);
-			return ret;
+			goto free_chip;
 		}
 
 		if (cs >= cdns_ctrl->caps2.max_banks) {
 			dev_err(cdns_ctrl->dev,
 				"invalid reg value: %u (max CS = %d)\n",
 				cs, cdns_ctrl->caps2.max_banks);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		if (test_and_set_bit(cs, &cdns_ctrl->assigned_cs)) {
 			dev_err(cdns_ctrl->dev,
 				"CS %d already assigned\n", cs);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto free_chip;
 		}
 
 		cdns_chip->cs[i] = cs;
@@ -3059,20 +3065,22 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl,
 	ret = nand_scan(chip, cdns_chip->nsels);
 	if (ret) {
 		dev_err(cdns_ctrl->dev, "could not scan the nand chip\n");
-		return ret;
+		goto free_chip;
 	}
 
-	ret = mtd_device_register(mtd, NULL, 0);
+	ret = nand_set_release(chip, cadence_nand_chip_release);
 	if (ret) {
-		dev_err(cdns_ctrl->dev,
-			"failed to register mtd device: %d\n", ret);
 		nand_cleanup(chip);
-		return ret;
+		goto free_chip;
 	}
 
 	list_add_tail(&cdns_chip->node, &cdns_ctrl->chips);
 
 	return 0;
+
+free_chip:
+	kfree(cdns_chip);
+	return ret;
 }
 
 static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl)
@@ -3083,10 +3091,13 @@ static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl)
 
 	list_for_each_entry_safe(entry, temp, &cdns_ctrl->chips, node) {
 		chip = &entry->chip;
-		ret = mtd_device_unregister(nand_to_mtd(chip));
-		WARN_ON(ret);
-		nand_cleanup(chip);
+		if (entry->registered) {
+			ret = mtd_device_unregister(nand_to_mtd(chip));
+			WARN_ON(ret);
+		}
+		nand_disconnect(chip);
 		list_del(&entry->node);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -3127,6 +3138,7 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
 {
 	dma_cap_mask_t mask;
 	struct dma_device *dma_dev;
+	struct cdns_nand_chip *entry;
 	int ret;
 
 	cdns_ctrl->cdma_desc = dma_alloc_coherent(cdns_ctrl->dev,
@@ -3181,7 +3193,6 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
 		goto dma_release_chnl;
 	}
 
-	nand_controller_init(&cdns_ctrl->controller);
 	INIT_LIST_HEAD(&cdns_ctrl->chips);
 
 	cdns_ctrl->controller.ops = &cadence_nand_controller_ops;
@@ -3201,9 +3212,17 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
 		goto unmap_dma_resource;
 	}
 
+	list_for_each_entry(entry, &cdns_ctrl->chips, node) {
+		ret = mtd_device_register(nand_to_mtd(&entry->chip), NULL, 0);
+		if (ret)
+			goto unmap_dma_resource;
+		entry->registered = true;
+	}
+
 	return 0;
 
 unmap_dma_resource:
+	cadence_nand_chips_cleanup(cdns_ctrl);
 	dma_unmap_resource(dma_dev->dev, cdns_ctrl->io.iova_dma,
 			   cdns_ctrl->io.size, DMA_BIDIRECTIONAL, 0);
 
@@ -3260,6 +3279,11 @@ static const struct of_device_id cadence_nand_dt_ids[] = {
 
 MODULE_DEVICE_TABLE(of, cadence_nand_dt_ids);
 
+static void cadence_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct cadence_nand_dt, cdns_ctrl.controller));
+}
+
 static int cadence_nand_dt_probe(struct platform_device *ofdev)
 {
 	struct resource *res;
@@ -3275,11 +3299,15 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
 		return -ENOMEM;
 	}
 
-	dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL);
+	dt = kzalloc_obj(*dt);
 	if (!dt)
 		return -ENOMEM;
 
 	cdns_ctrl = &dt->cdns_ctrl;
+	ret = devm_nand_controller_init(&ofdev->dev, &cdns_ctrl->controller,
+					cadence_nand_release);
+	if (ret)
+		return ret;
 	cdns_ctrl->caps1 = devdata;
 
 	cdns_ctrl->dev = &ofdev->dev;

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 39/58] mtd: rawnand: qcom: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (35 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 38/58] mtd: rawnand: cadence: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:47   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 40/58] mtd: rawnand: cafe: " James Hilliard
                   ` (20 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Validate boot partitions before publication and unwind partial probes.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/qcom_nandc.c | 74 +++++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index 2d98f79b492d..f812de1d776f 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -1900,7 +1900,6 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
 {
 	u32 nand_ctrl;
 
-	nand_controller_init(&nandc->controller);
 	nandc->controller.ops = &qcom_nandc_ops;
 
 	/* kill onenand */
@@ -2007,6 +2006,11 @@ static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nan
 	return 0;
 }
 
+static void qcom_nand_host_release(struct nand_chip *chip)
+{
+	kfree(to_qcom_nand_host(chip));
+}
+
 static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
 					    struct qcom_nand_host *host,
 					    struct device_node *dn)
@@ -2019,13 +2023,16 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
 	ret = of_property_read_u32(dn, "reg", &host->cs);
 	if (ret) {
 		dev_err(dev, "can't get chip-select\n");
-		return -ENXIO;
+		ret = -ENXIO;
+		goto free_host;
 	}
 
 	nand_set_flash_node(chip, dn);
 	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "qcom_nand.%d", host->cs);
-	if (!mtd->name)
-		return -ENOMEM;
+	if (!mtd->name) {
+		ret = -ENOMEM;
+		goto free_host;
+	}
 
 	mtd->owner = THIS_MODULE;
 	mtd->dev.parent = dev;
@@ -2050,11 +2057,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
 
 	ret = nand_scan(chip, 1);
 	if (ret)
-		return ret;
-
-	ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
-	if (ret)
-		goto err;
+		goto free_host;
 
 	if (nandc->props->use_codeword_fixup) {
 		ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn);
@@ -2062,10 +2065,21 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
 			goto err;
 	}
 
+	ret = nand_set_release(chip, qcom_nand_host_release);
+	if (ret)
+		goto err;
+	ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		return ret;
+	}
 	return 0;
 
 err:
 	nand_cleanup(chip);
+free_host:
+	kfree(host);
 	return ret;
 }
 
@@ -2077,15 +2091,13 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
 	int ret = -ENODEV;
 
 	for_each_available_child_of_node_scoped(dn, child) {
-		host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+		host = kzalloc_obj(*host);
 		if (!host)
 			return -ENOMEM;
 
 		ret = qcom_nand_host_init_and_register(nandc, host, child);
-		if (ret) {
-			devm_kfree(dev, host);
+		if (ret)
 			continue;
-		}
 
 		list_add_tail(&host->node, &nandc->host_list);
 	}
@@ -2119,6 +2131,26 @@ static int qcom_nandc_parse_dt(struct platform_device *pdev)
 	return 0;
 }
 
+static void qcom_nandc_chips_cleanup(struct qcom_nand_controller *nandc)
+{
+	struct qcom_nand_host *host, *tmp;
+
+	list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
+		struct nand_chip *chip = &host->chip;
+		struct mtd_info *mtd = nand_to_mtd(chip);
+
+		WARN_ON(mtd_device_unregister(mtd));
+		nand_disconnect(chip);
+		list_del(&host->node);
+		mtd_put_master(mtd);
+	}
+}
+
+static void qcom_nandc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct qcom_nand_controller, controller));
+}
+
 static int qcom_nandc_probe(struct platform_device *pdev)
 {
 	struct qcom_nand_controller *nandc;
@@ -2127,9 +2159,12 @@ static int qcom_nandc_probe(struct platform_device *pdev)
 	struct resource *res;
 	int ret;
 
-	nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
+	nandc = kzalloc_obj(*nandc);
 	if (!nandc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nandc->controller, qcom_nandc_release);
+	if (ret)
+		return ret;
 
 	platform_set_drvdata(pdev, nandc);
 	nandc->dev = dev;
@@ -2188,6 +2223,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
 	return 0;
 
 err_setup:
+	qcom_nandc_chips_cleanup(nandc);
 	qcom_nandc_unalloc(nandc);
 err_nandc_alloc:
 	clk_disable_unprepare(nandc->aon_clk);
@@ -2203,16 +2239,8 @@ static void qcom_nandc_remove(struct platform_device *pdev)
 {
 	struct qcom_nand_controller *nandc = platform_get_drvdata(pdev);
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	struct qcom_nand_host *host;
-	struct nand_chip *chip;
-	int ret;
 
-	list_for_each_entry(host, &nandc->host_list, node) {
-		chip = &host->chip;
-		ret = mtd_device_unregister(nand_to_mtd(chip));
-		WARN_ON(ret);
-		nand_cleanup(chip);
-	}
+	qcom_nandc_chips_cleanup(nandc);
 
 	qcom_nandc_unalloc(nandc);
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 40/58] mtd: rawnand: cafe: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (36 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 39/58] mtd: rawnand: qcom: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 41/58] mtd: rawnand: brcmnand: " James Hilliard
                   ` (19 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Free DMA memory at teardown and clear its pointer before deferred
detach.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/cafe_nand.c | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c
index f3117b031cd2..bcf7482410e7 100644
--- a/drivers/mtd/nand/raw/cafe_nand.c
+++ b/drivers/mtd/nand/raw/cafe_nand.c
@@ -651,7 +651,10 @@ static void cafe_nand_detach_chip(struct nand_chip *chip)
 {
 	struct cafe_priv *cafe = nand_get_controller_data(chip);
 
-	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+	if (cafe->dmabuf) {
+		dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+		cafe->dmabuf = NULL;
+	}
 }
 
 static const struct nand_controller_ops cafe_nand_controller_ops = {
@@ -659,6 +662,11 @@ static const struct nand_controller_ops cafe_nand_controller_ops = {
 	.detach_chip = cafe_nand_detach_chip,
 };
 
+static void cafe_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct cafe_priv, nand.legacy.dummy_controller));
+}
+
 static int cafe_nand_probe(struct pci_dev *pdev,
 				     const struct pci_device_id *ent)
 {
@@ -683,6 +691,10 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 		err = -ENOMEM;
 		goto out_disable_device;
 	}
+	cafe->nand.controller = &cafe->nand.legacy.dummy_controller;
+	err = devm_nand_controller_init(&pdev->dev, cafe->nand.controller, cafe_nand_release);
+	if (err)
+		goto out_disable_device;
 
 	mtd = nand_to_mtd(&cafe->nand);
 	mtd->dev.parent = &pdev->dev;
@@ -693,7 +705,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 	if (!cafe->mmio) {
 		dev_warn(&pdev->dev, "failed to iomap\n");
 		err = -ENOMEM;
-		goto out_free_mtd;
+		goto out_disable_device;
 	}
 
 	cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
@@ -785,9 +797,16 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 	pci_set_drvdata(pdev, mtd);
 
 	mtd->name = "cafe_nand";
-	err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+	err = nand_set_release(&cafe->nand, NULL);
 	if (err)
 		goto out_cleanup_nand;
+	err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0);
+	if (err) {
+		nand_disconnect(&cafe->nand);
+		cafe_nand_detach_chip(&cafe->nand);
+		mtd_put_master(mtd);
+		goto out_irq;
+	}
 
 	goto out;
 
@@ -801,8 +820,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 	free_rs(cafe->rs);
  out_ior:
 	pci_iounmap(pdev, cafe->mmio);
- out_free_mtd:
-	kfree(cafe);
  out_disable_device:
 	pci_disable_device(pdev);
  out:
@@ -816,17 +833,17 @@ static void cafe_nand_remove(struct pci_dev *pdev)
 	struct cafe_priv *cafe = nand_get_controller_data(chip);
 	int ret;
 
+	ret = mtd_device_unregister(mtd);
+	WARN_ON(ret);
+	nand_disconnect(chip);
 	/* Disable NAND IRQ in global IRQ mask register */
 	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
 	free_irq(pdev->irq, mtd);
-	ret = mtd_device_unregister(mtd);
-	WARN_ON(ret);
-	nand_cleanup(chip);
+	cafe_nand_detach_chip(chip);
 	free_rs(cafe->rs);
 	pci_iounmap(pdev, cafe->mmio);
-	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
-	kfree(cafe);
 	pci_disable_device(pdev);
+	mtd_put_master(mtd);
 }
 
 static const struct pci_device_id cafe_nand_tbl[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 41/58] mtd: rawnand: brcmnand: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (37 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 40/58] mtd: rawnand: cafe: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal James Hilliard
                   ` (18 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Unwind previously registered chip-selects on probe failure.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/brcmnand/brcmnand.c | 72 +++++++++++++++++++++++---------
 1 file changed, 52 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
index 4a1f5e736a1c..eaf0279518ca 100644
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
@@ -3077,6 +3077,11 @@ static const struct nand_controller_ops brcmnand_controller_ops = {
 	.exec_op = brcmnand_exec_op,
 };
 
+static void brcmnand_host_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct brcmnand_host, chip));
+}
+
 static int brcmnand_init_cs(struct brcmnand_host *host,
 			    const char * const *part_probe_types)
 {
@@ -3093,8 +3098,10 @@ static int brcmnand_init_cs(struct brcmnand_host *host,
 	nand_set_controller_data(chip, host);
 	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
 				   host->cs);
-	if (!mtd->name)
-		return -ENOMEM;
+	if (!mtd->name) {
+		ret = -ENOMEM;
+		goto free_host;
+	}
 
 	mtd->owner = THIS_MODULE;
 	mtd->dev.parent = dev;
@@ -3123,12 +3130,23 @@ static int brcmnand_init_cs(struct brcmnand_host *host,
 
 	ret = nand_scan(chip, 1);
 	if (ret)
-		return ret;
+		goto free_host;
 
-	ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
-	if (ret)
+	ret = nand_set_release(chip, brcmnand_host_release);
+	if (ret) {
 		nand_cleanup(chip);
+		goto free_host;
+	}
+	ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+	}
+
+	return ret;
 
+free_host:
+	kfree(host);
 	return ret;
 }
 
@@ -3294,6 +3312,26 @@ static int brcmnand_edu_setup(struct platform_device *pdev)
 	return 0;
 }
 
+static void brcmnand_chips_cleanup(struct brcmnand_controller *ctrl)
+{
+	struct brcmnand_host *host, *tmp;
+
+	list_for_each_entry_safe(host, tmp, &ctrl->host_list, node) {
+		struct nand_chip *chip = &host->chip;
+		struct mtd_info *mtd = nand_to_mtd(chip);
+
+		WARN_ON(mtd_device_unregister(mtd));
+		nand_disconnect(chip);
+		list_del(&host->node);
+		mtd_put_master(mtd);
+	}
+}
+
+static void brcmnand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct brcmnand_controller, controller));
+}
+
 int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 {
 	struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
@@ -3307,9 +3345,12 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 	if (dn && !of_match_node(brcmnand_of_match, dn))
 		return -ENODEV;
 
-	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	ctrl = kzalloc_obj(*ctrl);
 	if (!ctrl)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &ctrl->controller, brcmnand_release);
+	if (ret)
+		return ret;
 
 	dev_set_drvdata(dev, ctrl);
 	ctrl->dev = dev;
@@ -3324,7 +3365,6 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 	init_completion(&ctrl->done);
 	init_completion(&ctrl->dma_done);
 	init_completion(&ctrl->edu_done);
-	nand_controller_init(&ctrl->controller);
 	ctrl->controller.ops = &brcmnand_controller_ops;
 	INIT_LIST_HEAD(&ctrl->host_list);
 
@@ -3489,7 +3529,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 	for_each_available_child_of_node_scoped(dn, child) {
 		if (of_device_is_compatible(child, "brcm,nandcs")) {
 
-			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+			host = kzalloc_obj(*host);
 			if (!host) {
 				ret = -ENOMEM;
 				goto err;
@@ -3500,7 +3540,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 			ret = of_property_read_u32(child, "reg", &host->cs);
 			if (ret) {
 				dev_err(dev, "can't get chip-select\n");
-				devm_kfree(dev, host);
+				kfree(host);
 				continue;
 			}
 
@@ -3511,7 +3551,6 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 				if (ret == -EPROBE_DEFER)
 					goto err;
 
-				devm_kfree(dev, host);
 				continue; /* Try all chip-selects */
 			}
 
@@ -3528,7 +3567,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 	}
 
 	/* If we got there we must have been probing via platform data */
-	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host) {
 		ret = -ENOMEM;
 		goto err;
@@ -3554,6 +3593,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 	return 0;
 
 err:
+	brcmnand_chips_cleanup(ctrl);
 	clk_disable_unprepare(ctrl->clk);
 	return ret;
 
@@ -3563,16 +3603,8 @@ EXPORT_SYMBOL_GPL(brcmnand_probe);
 void brcmnand_remove(struct platform_device *pdev)
 {
 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
-	struct brcmnand_host *host;
-	struct nand_chip *chip;
-	int ret;
 
-	list_for_each_entry(host, &ctrl->host_list, node) {
-		chip = &host->chip;
-		ret = mtd_device_unregister(nand_to_mtd(chip));
-		WARN_ON(ret);
-		nand_cleanup(chip);
-	}
+	brcmnand_chips_cleanup(ctrl);
 
 	clk_disable_unprepare(ctrl->clk);
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (38 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 41/58] mtd: rawnand: brcmnand: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:44   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 43/58] mtd: rawnand: sharpsl: " James Hilliard
                   ` (17 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Drain NAND access before unmapping the chip-select window, and retain
the chip and controller until the final MTD reference is released. Check
MTD registration errors so unsuccessful publication releases the same
ownership.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/au1550nd.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c
index f1e99197ab84..bd78bcc84282 100644
--- a/drivers/mtd/nand/raw/au1550nd.c
+++ b/drivers/mtd/nand/raw/au1550nd.c
@@ -251,6 +251,11 @@ static const struct nand_controller_ops au1550nd_ops = {
 	.attach_chip = au1550nd_attach_chip,
 };
 
+static void au1550nd_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct au1550nd_ctx, controller));
+}
+
 static int au1550nd_probe(struct platform_device *pdev)
 {
 	struct au1550nd_platdata *pd;
@@ -269,6 +274,9 @@ static int au1550nd_probe(struct platform_device *pdev)
 	ctx = kzalloc_obj(*ctx);
 	if (!ctx)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &ctx->controller, au1550nd_release);
+	if (ret)
+		return ret;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -302,7 +310,6 @@ static int au1550nd_probe(struct platform_device *pdev)
 	}
 	ctx->cs = cs;
 
-	nand_controller_init(&ctx->controller);
 	ctx->controller.ops = &au1550nd_ops;
 	this->controller = &ctx->controller;
 
@@ -322,7 +329,17 @@ static int au1550nd_probe(struct platform_device *pdev)
 		goto out3;
 	}
 
-	mtd_device_register(mtd, pd->parts, pd->num_parts);
+	ret = nand_set_release(this, NULL);
+	if (ret) {
+		nand_cleanup(this);
+		goto out3;
+	}
+	ret = mtd_device_register(mtd, pd->parts, pd->num_parts);
+	if (ret) {
+		nand_disconnect(this);
+		mtd_put_master(mtd);
+		goto out3;
+	}
 
 	platform_set_drvdata(pdev, ctx);
 
@@ -333,7 +350,6 @@ static int au1550nd_probe(struct platform_device *pdev)
 out2:
 	release_mem_region(r->start, resource_size(r));
 out1:
-	kfree(ctx);
 	return ret;
 }
 
@@ -346,10 +362,10 @@ static void au1550nd_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	iounmap(ctx->base);
 	release_mem_region(r->start, 0x1000);
-	kfree(ctx);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver au1550nd_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 43/58] mtd: rawnand: sharpsl: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (39 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 44/58] mtd: rawnand: davinci: " James Hilliard
                   ` (16 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Separate chip-select unmapping from chip storage reclamation.
Outstanding MTD references retain the scanned chip and layout metadata
after removal; new hardware operations fail once the chip is
disconnected.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/sharpsl.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
index 4154ab74f169..1ffd2b210d20 100644
--- a/drivers/mtd/nand/raw/sharpsl.c
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -114,6 +114,11 @@ static const struct nand_controller_ops sharpsl_ops = {
 	.attach_chip = sharpsl_attach_chip,
 };
 
+static void sharpsl_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct sharpsl_nand, controller));
+}
+
 /*
  * Main initialization routine
  */
@@ -135,6 +140,9 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
 	sharpsl = kzalloc_obj(struct sharpsl_nand);
 	if (!sharpsl)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &sharpsl->controller, sharpsl_nand_release);
+	if (err)
+		return err;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -154,7 +162,6 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
 	/* Get pointer to private data */
 	this = (struct nand_chip *)(&sharpsl->chip);
 
-	nand_controller_init(&sharpsl->controller);
 	sharpsl->controller.ops = &sharpsl_ops;
 	this->controller = &sharpsl->controller;
 
@@ -188,10 +195,16 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
 	/* Register the partitions */
 	mtd->name = "sharpsl-nand";
 
-	err = mtd_device_parse_register(mtd, data->part_parsers, NULL,
-					data->partitions, data->nr_partitions);
+	err = nand_set_release(this, NULL);
 	if (err)
 		goto err_add;
+	err = mtd_device_parse_register(mtd, data->part_parsers, NULL,
+					data->partitions, data->nr_partitions);
+	if (err) {
+		nand_disconnect(this);
+		mtd_put_master(mtd);
+		goto err_scan;
+	}
 
 	/* Return happy */
 	return 0;
@@ -203,7 +216,6 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
 	iounmap(sharpsl->io);
 err_ioremap:
 err_get_res:
-	kfree(sharpsl);
 	return err;
 }
 
@@ -221,12 +233,11 @@ static void sharpsl_nand_remove(struct platform_device *pdev)
 	WARN_ON(ret);
 
 	/* Release resources */
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	iounmap(sharpsl->io);
 
-	/* Free the driver's structure */
-	kfree(sharpsl);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver sharpsl_nand_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 44/58] mtd: rawnand: davinci: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (40 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 43/58] mtd: rawnand: sharpsl: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 45/58] mtd: rawnand: sh_flctl: " James Hilliard
                   ` (15 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Disconnect the chip before releasing the shared four-bit ECC engine.
Retain the chip and controller while MTD references remain, with final
NAND cleanup independent of the device-managed hardware resources.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/davinci_nand.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index 3986553881d0..2231e6a45114 100644
--- a/drivers/mtd/nand/raw/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -895,6 +895,11 @@ static const struct nand_controller_ops davinci_nand_controller_ops = {
 	.setup_interface = davinci_nand_setup_interface,
 };
 
+static void nand_davinci_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct davinci_nand_info, controller));
+}
+
 static int nand_davinci_probe(struct platform_device *pdev)
 {
 	struct davinci_nand_pdata	*pdata;
@@ -919,9 +924,12 @@ static int nand_davinci_probe(struct platform_device *pdev)
 	if (pdata->core_chipsel > 3)
 		return -ENODEV;
 
-	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+	info = kzalloc_obj(*info);
 	if (!info)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&pdev->dev, &info->controller, nand_davinci_release);
+	if (ret)
+		return ret;
 
 	platform_set_drvdata(pdev, info);
 
@@ -986,7 +994,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
 	spin_unlock_irq(&davinci_nand_lock);
 
 	/* Scan to find existence of the device(s) */
-	nand_controller_init(&info->controller);
 	info->controller.ops = &davinci_nand_controller_ops;
 	info->chip.controller = &info->controller;
 	ret = nand_scan(&info->chip, pdata->mask_chipsel ? 2 : 1);
@@ -995,12 +1002,18 @@ static int nand_davinci_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = nand_set_release(&info->chip, NULL);
+	if (ret)
+		goto err_cleanup_nand;
 	if (pdata->parts)
 		ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
 	else
 		ret = mtd_device_register(mtd, NULL, 0);
-	if (ret < 0)
-		goto err_cleanup_nand;
+	if (ret < 0) {
+		nand_disconnect(&info->chip);
+		mtd_put_master(mtd);
+		return ret;
+	}
 
 	val = davinci_nand_readl(info, NRCSR_OFFSET);
 	dev_info(&pdev->dev, "controller rev. %d.%d\n",
@@ -1020,14 +1033,16 @@ static void nand_davinci_remove(struct platform_device *pdev)
 	struct nand_chip *chip = &info->chip;
 	int ret;
 
+	ret = mtd_device_unregister(nand_to_mtd(chip));
+	WARN_ON(ret);
+	nand_disconnect(chip);
+
 	spin_lock_irq(&davinci_nand_lock);
 	if (chip->ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED)
 		ecc4_busy = false;
 	spin_unlock_irq(&davinci_nand_lock);
 
-	ret = mtd_device_unregister(nand_to_mtd(chip));
-	WARN_ON(ret);
-	nand_cleanup(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver nand_davinci_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 45/58] mtd: rawnand: sh_flctl: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (41 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 44/58] mtd: rawnand: davinci: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 46/58] mtd: rawnand: lpc32xx_slc: " James Hilliard
                   ` (14 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Unregister and disconnect the NAND chip before releasing the DMA
channel. Retain chip and controller storage until the last MTD reference
is dropped.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/sh_flctl.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c
index 97f733e481ff..291d809d0ea1 100644
--- a/drivers/mtd/nand/raw/sh_flctl.c
+++ b/drivers/mtd/nand/raw/sh_flctl.c
@@ -1109,6 +1109,11 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
 	return pdata;
 }
 
+static void flctl_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct sh_flctl, chip.legacy.dummy_controller));
+}
+
 static int flctl_probe(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -1119,9 +1124,14 @@ static int flctl_probe(struct platform_device *pdev)
 	int ret;
 	int irq;
 
-	flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
+	flctl = kzalloc_obj(*flctl);
 	if (!flctl)
 		return -ENOMEM;
+	nand = &flctl->chip;
+	nand->controller = &nand->legacy.dummy_controller;
+	ret = devm_nand_controller_init(&pdev->dev, nand->controller, flctl_release);
+	if (ret)
+		return ret;
 
 	flctl->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 	if (IS_ERR(flctl->reg))
@@ -1187,9 +1197,15 @@ static int flctl_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_chip;
 
-	ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
+	ret = nand_set_release(nand, NULL);
 	if (ret)
 		goto cleanup_nand;
+	ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
+	if (ret) {
+		nand_disconnect(nand);
+		mtd_put_master(flctl_mtd);
+		goto err_chip;
+	}
 
 	return 0;
 
@@ -1207,11 +1223,12 @@ static void flctl_remove(struct platform_device *pdev)
 	struct nand_chip *chip = &flctl->chip;
 	int ret;
 
-	flctl_release_dma(flctl);
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	flctl_release_dma(flctl);
 	pm_runtime_disable(&pdev->dev);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver flctl_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 46/58] mtd: rawnand: lpc32xx_slc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (42 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 45/58] mtd: rawnand: sh_flctl: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 47/58] mtd: rawnand: lpc32xx_mlc: " James Hilliard
                   ` (13 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Drain chip operations before releasing DMA and changing the controller
and write-protect signals. Retain the embedded NAND chip and controller
until the final MTD reference releases their metadata.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/lpc32xx_slc.c | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index b4d90afa9267..85945e1dd25a 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -817,6 +817,12 @@ static const struct nand_controller_ops lpc32xx_nand_controller_ops = {
 	.attach_chip = lpc32xx_nand_attach_chip,
 };
 
+static void lpc32xx_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct lpc32xx_nand_host,
+			   nand_chip.legacy.dummy_controller));
+}
+
 /*
  * Probe for NAND controller
  */
@@ -829,9 +835,14 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
 	int res;
 
 	/* Allocate memory for the device structure (and zero it) */
-	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	chip = &host->nand_chip;
+	chip->controller = &chip->legacy.dummy_controller;
+	res = devm_nand_controller_init(&pdev->dev, chip->controller, lpc32xx_nand_release);
+	if (res)
+		return res;
 
 	host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc);
 	if (IS_ERR(host->io_base))
@@ -915,10 +926,16 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
 		goto release_dma;
 
 	mtd->name = "nxp_lpc3220_slc";
-	res = mtd_device_register(mtd, host->ncfg->parts,
-				  host->ncfg->num_parts);
+	res = nand_set_release(chip, NULL);
 	if (res)
 		goto cleanup_nand;
+	res = mtd_device_register(mtd, host->ncfg->parts,
+				  host->ncfg->num_parts);
+	if (res) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		goto release_dma;
+	}
 
 	return 0;
 
@@ -944,7 +961,7 @@ static void lpc32xx_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 	dma_release_channel(host->dma_chan);
 
 	/* Force CE high */
@@ -953,6 +970,7 @@ static void lpc32xx_nand_remove(struct platform_device *pdev)
 	writel(tmp, SLC_CTRL(host->io_base));
 
 	lpc32xx_wp_enable(host);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static int lpc32xx_nand_resume(struct platform_device *pdev)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 47/58] mtd: rawnand: lpc32xx_mlc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (43 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 46/58] mtd: rawnand: lpc32xx_slc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 48/58] mtd: rawnand: ndfc: " James Hilliard
                   ` (12 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Drain chip operations before releasing interrupts, DMA and controller
resources. Retain the embedded NAND chip and controller until the final
MTD reference releases their metadata.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/lpc32xx_mlc.c | 26 ++++++++++++++++++++++----
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index 8436015edcb4..93ee8cfc0345 100644
--- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
@@ -675,6 +675,12 @@ static const struct nand_controller_ops lpc32xx_nand_controller_ops = {
 	.attach_chip = lpc32xx_nand_attach_chip,
 };
 
+static void lpc32xx_nand_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct lpc32xx_nand_host,
+			   nand_chip.legacy.dummy_controller));
+}
+
 /*
  * Probe for NAND controller
  */
@@ -687,9 +693,14 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
 	int res;
 
 	/* Allocate memory for the device structure (and zero it) */
-	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	nand_chip = &host->nand_chip;
+	nand_chip->controller = &nand_chip->legacy.dummy_controller;
+	res = devm_nand_controller_init(&pdev->dev, nand_chip->controller, lpc32xx_nand_release);
+	if (res)
+		return res;
 
 	host->pdev = pdev;
 
@@ -794,10 +805,16 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
 
 	mtd->name = DRV_NAME;
 
-	res = mtd_device_register(mtd, host->ncfg->parts,
-				  host->ncfg->num_parts);
+	res = nand_set_release(nand_chip, NULL);
 	if (res)
 		goto cleanup_nand;
+	res = mtd_device_register(mtd, host->ncfg->parts,
+				  host->ncfg->num_parts);
+	if (res) {
+		nand_disconnect(nand_chip);
+		mtd_put_master(mtd);
+		goto free_irq;
+	}
 
 	return 0;
 
@@ -830,7 +847,7 @@ static void lpc32xx_nand_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	free_irq(host->irq, host);
 	if (use_dma)
@@ -841,6 +858,7 @@ static void lpc32xx_nand_remove(struct platform_device *pdev)
 
 	lpc32xx_wp_enable(host);
 	gpiod_put(host->wp_gpio);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static int lpc32xx_nand_resume(struct platform_device *pdev)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 48/58] mtd: rawnand: ndfc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (44 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 47/58] mtd: rawnand: lpc32xx_mlc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 49/58] mtd: rawnand: txx9ndfmc: " James Hilliard
                   ` (11 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Allocate controller state per binding instead of reusing the static
chip-select slots. A retained MTD handle must keep its original chip
state when the platform device is rebound. Disconnect the old chip
before device resources disappear and release its state at the final MTD
put.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/ndfc.c | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index d7b119028a71..50506ad87fd4 100644
--- a/drivers/mtd/nand/raw/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
@@ -37,8 +37,6 @@ struct ndfc_controller {
 	struct nand_controller ndfc_control;
 };
 
-static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS];
-
 static void ndfc_select_chip(struct nand_chip *nchip, int chip)
 {
 	uint32_t ccr;
@@ -173,13 +171,27 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
 	if (ret)
 		goto err;
 
+	ret = nand_set_release(chip, NULL);
+	if (ret) {
+		nand_cleanup(chip);
+		goto err;
+	}
 	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+	}
 
 err:
 	of_node_put(flash_np);
 	return ret;
 }
 
+static void ndfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct ndfc_controller, ndfc_control));
+}
+
 static int ndfc_probe(struct platform_device *ofdev)
 {
 	struct ndfc_controller *ndfc;
@@ -187,11 +199,7 @@ static int ndfc_probe(struct platform_device *ofdev)
 	const __be32 *reg;
 	u32 ccr;
 	u32 cs;
-	int len = 0;
-
-	ndfcbase = devm_platform_ioremap_resource(ofdev, 0);
-	if (IS_ERR(ndfcbase))
-		return PTR_ERR(ndfcbase);
+	int len = 0, ret;
 
 	/* Read the reg property to get the chip select */
 	reg = of_get_property(ofdev->dev.of_node, "reg", &len);
@@ -206,10 +214,18 @@ static int ndfc_probe(struct platform_device *ofdev)
 		return -EINVAL;
 	}
 
-	ndfc = &ndfc_ctrl[cs];
+	ndfc = kzalloc_obj(*ndfc);
+	if (!ndfc)
+		return -ENOMEM;
+	ret = devm_nand_controller_init(&ofdev->dev, &ndfc->ndfc_control, ndfc_release);
+	if (ret)
+		return ret;
+
+	ndfcbase = devm_platform_ioremap_resource(ofdev, 0);
+	if (IS_ERR(ndfcbase))
+		return PTR_ERR(ndfcbase);
 	ndfc->chip_select = cs;
 
-	nand_controller_init(&ndfc->ndfc_control);
 	ndfc->ofdev = ofdev;
 	dev_set_drvdata(&ofdev->dev, ndfc);
 
@@ -243,7 +259,8 @@ static void ndfc_remove(struct platform_device *ofdev)
 
 	ret = mtd_device_unregister(mtd);
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(mtd);
 }
 
 static const struct of_device_id ndfc_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 49/58] mtd: rawnand: txx9ndfmc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (45 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 48/58] mtd: rawnand: ndfc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 50/58] mtd: rawnand: ingenic: " James Hilliard
                   ` (10 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Retain each chip and its shared controller while MTD references remain.
Unregister and disconnect before dropping driver ownership, and check
MTD registration failure without publishing partially initialized chip
slots.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/txx9ndfmc.c | 42 +++++++++++++++++++++++++++++++---------
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c
index 4cc6e91dbc23..9eae5002e389 100644
--- a/drivers/mtd/nand/raw/txx9ndfmc.c
+++ b/drivers/mtd/nand/raw/txx9ndfmc.c
@@ -276,17 +276,33 @@ static const struct nand_controller_ops txx9ndfmc_controller_ops = {
 	.attach_chip = txx9ndfmc_attach_chip,
 };
 
+static void txx9ndfmc_chip_release(struct nand_chip *chip)
+{
+	struct txx9ndfmc_priv *priv = nand_get_controller_data(chip);
+
+	kfree(priv->mtdname);
+	kfree(priv);
+}
+
+static void txx9ndfmc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct txx9ndfmc_drvdata, controller));
+}
+
 static int txx9ndfmc_probe(struct platform_device *dev)
 {
 	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
 	int hold, spw;
-	int i;
+	int i, ret;
 	struct txx9ndfmc_drvdata *drvdata;
 	unsigned long gbusclk = plat->gbus_clock;
 
-	drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
+	drvdata = kzalloc_obj(*drvdata);
 	if (!drvdata)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(&dev->dev, &drvdata->controller, txx9ndfmc_release);
+	if (ret)
+		return ret;
 	drvdata->base = devm_platform_ioremap_resource(dev, 0);
 	if (IS_ERR(drvdata->base))
 		return PTR_ERR(drvdata->base);
@@ -306,7 +322,6 @@ static int txx9ndfmc_probe(struct platform_device *dev)
 	dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
 		 (gbusclk + 500000) / 1000000, hold, spw);
 
-	nand_controller_init(&drvdata->controller);
 	drvdata->controller.ops = &txx9ndfmc_controller_ops;
 
 	platform_set_drvdata(dev, drvdata);
@@ -361,7 +376,18 @@ static int txx9ndfmc_probe(struct platform_device *dev)
 		}
 		mtd->name = txx9_priv->mtdname;
 
-		mtd_device_register(mtd, NULL, 0);
+		ret = nand_set_release(chip, txx9ndfmc_chip_release);
+		if (ret) {
+			nand_cleanup(chip);
+			txx9ndfmc_chip_release(chip);
+			continue;
+		}
+		ret = mtd_device_register(mtd, NULL, 0);
+		if (ret) {
+			nand_disconnect(chip);
+			mtd_put_master(mtd);
+			continue;
+		}
 		drvdata->mtds[i] = mtd;
 	}
 
@@ -376,18 +402,16 @@ static void txx9ndfmc_remove(struct platform_device *dev)
 	for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
 		struct mtd_info *mtd = drvdata->mtds[i];
 		struct nand_chip *chip;
-		struct txx9ndfmc_priv *txx9_priv;
 
 		if (!mtd)
 			continue;
 		chip = mtd_to_nand(mtd);
-		txx9_priv = nand_get_controller_data(chip);
 
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
-		kfree(txx9_priv->mtdname);
-		kfree(txx9_priv);
+		nand_disconnect(chip);
+		drvdata->mtds[i] = NULL;
+		mtd_put_master(mtd);
 	}
 }
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 50/58] mtd: rawnand: ingenic: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (46 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 49/58] mtd: rawnand: txx9ndfmc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 51/58] mtd: rawnand: mpc5121: " James Hilliard
                   ` (9 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Unregister and disconnect all chips before releasing the ECC provider.
Retain each chip and its shared controller until the final MTD put,
while releasing hardware resources during device removal.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 49 +++++++++++++++++++------
 1 file changed, 37 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
index f1e2c82936b3..775ebf4dd3b9 100644
--- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
+++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
@@ -341,6 +341,11 @@ static const struct nand_controller_ops ingenic_nand_controller_ops = {
 	.exec_op = ingenic_nand_exec_op,
 };
 
+static void ingenic_nand_release(struct nand_chip *chip)
+{
+	kfree(to_ingenic_nand(nand_to_mtd(chip)));
+}
+
 static int ingenic_nand_init_chip(struct platform_device *pdev,
 				  struct ingenic_nfc *nfc,
 				  struct device_node *np,
@@ -368,7 +373,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
 	if (IS_ERR(cs->base))
 		return PTR_ERR(cs->base);
 
-	nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
+	nand = kzalloc_obj(*nand);
 	if (!nand)
 		return -ENOMEM;
 
@@ -377,7 +382,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
 	if (IS_ERR(nand->busy_gpio)) {
 		ret = PTR_ERR(nand->busy_gpio);
 		dev_err(dev, "failed to request busy GPIO: %d\n", ret);
-		return ret;
+		goto free_chip;
 	}
 
 	nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
@@ -385,15 +390,17 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
 	if (IS_ERR(nand->wp_gpio)) {
 		ret = PTR_ERR(nand->wp_gpio);
 		dev_err(dev, "failed to request WP GPIO: %d\n", ret);
-		return ret;
+		goto free_chip;
 	}
 
 	chip = &nand->chip;
 	mtd = nand_to_mtd(chip);
 	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev),
 				   cs->bank);
-	if (!mtd->name)
-		return -ENOMEM;
+	if (!mtd->name) {
+		ret = -ENOMEM;
+		goto free_chip;
+	}
 	mtd->dev.parent = dev;
 
 	chip->options = NAND_NO_SUBPAGE_WRITE;
@@ -404,17 +411,27 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
 	chip->controller->ops = &ingenic_nand_controller_ops;
 	ret = nand_scan(chip, 1);
 	if (ret)
-		return ret;
+		goto free_chip;
+	ret = nand_set_release(chip, ingenic_nand_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
 	list_add_tail(&nand->chip_list, &nfc->chips);
 
 	return 0;
+
+free_chip:
+	kfree(nand);
+	return ret;
 }
 
 static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc)
@@ -429,8 +446,9 @@ static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc)
 		chip = &ingenic_chip->chip;
 		ret = mtd_device_unregister(nand_to_mtd(chip));
 		WARN_ON(ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
 		list_del(&ingenic_chip->chip_list);
+		mtd_put_master(nand_to_mtd(chip));
 	}
 }
 
@@ -461,6 +479,11 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc,
 	return 0;
 }
 
+static void ingenic_nfc_release(struct nand_controller *controller)
+{
+	kfree(to_ingenic_nfc(controller));
+}
+
 static int ingenic_nand_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -474,9 +497,12 @@ static int ingenic_nand_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL);
+	nfc = kzalloc(struct_size(nfc, cs, num_banks), GFP_KERNEL);
 	if (!nfc)
 		return -ENOMEM;
+	ret = devm_nand_controller_init(dev, &nfc->controller, ingenic_nfc_release);
+	if (ret)
+		return ret;
 
 	nfc->soc_info = device_get_match_data(dev);
 	if (!nfc->soc_info)
@@ -493,7 +519,6 @@ static int ingenic_nand_probe(struct platform_device *pdev)
 	nfc->dev = dev;
 	nfc->num_banks = num_banks;
 
-	nand_controller_init(&nfc->controller);
 	INIT_LIST_HEAD(&nfc->chips);
 
 	ret = ingenic_nand_init_chips(nfc, pdev);
@@ -511,10 +536,10 @@ static void ingenic_nand_remove(struct platform_device *pdev)
 {
 	struct ingenic_nfc *nfc = platform_get_drvdata(pdev);
 
+	ingenic_nand_cleanup_chips(nfc);
+
 	if (nfc->ecc)
 		ingenic_ecc_release(nfc->ecc);
-
-	ingenic_nand_cleanup_chips(nfc);
 }
 
 static const struct jz_soc_info jz4740_soc_info = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 51/58] mtd: rawnand: mpc5121: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (47 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 50/58] mtd: rawnand: ingenic: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:48   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 52/58] mtd: rawnand: bcm47xxnflash: " James Hilliard
                   ` (8 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Drain NAND access before unmapping the external chip-select registers.
Keep the embedded chip and controller alive until the final MTD
reference, and unwind scanned NAND state on unsupported geometry and
publication failure.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/mpc5121_nfc.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c
index e6594548b7e0..1618f46f69df 100644
--- a/drivers/mtd/nand/raw/mpc5121_nfc.c
+++ b/drivers/mtd/nand/raw/mpc5121_nfc.c
@@ -611,6 +611,11 @@ static const struct nand_controller_ops mpc5121_nfc_ops = {
 	.attach_chip = mpc5121_nfc_attach_chip,
 };
 
+static void mpc5121_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct mpc5121_nfc_prv, controller));
+}
+
 static int mpc5121_nfc_probe(struct platform_device *op)
 {
 	struct device_node *dn = op->dev.of_node;
@@ -636,6 +641,13 @@ static int mpc5121_nfc_probe(struct platform_device *op)
 		return -ENXIO;
 	}
 
+	prv = kzalloc_obj(*prv);
+	if (!prv)
+		return -ENOMEM;
+	retval = devm_nand_controller_init(dev, &prv->controller, mpc5121_nfc_release);
+	if (retval)
+		return retval;
+
 	regs = devm_platform_ioremap_resource(op, 0);
 	if (IS_ERR(regs))
 		return PTR_ERR(regs);
@@ -644,14 +656,9 @@ static int mpc5121_nfc_probe(struct platform_device *op)
 	if (irq < 0)
 		return irq;
 
-	prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
-	if (!prv)
-		return -ENOMEM;
-
 	chip = &prv->chip;
 	mtd = nand_to_mtd(chip);
 
-	nand_controller_init(&prv->controller);
 	prv->controller.ops = &mpc5121_nfc_ops;
 	chip->controller = &prv->controller;
 
@@ -782,19 +789,26 @@ static int mpc5121_nfc_probe(struct platform_device *op)
 	default:
 		dev_err(dev, "Unsupported NAND flash!\n");
 		retval = -ENXIO;
-		goto error;
+		goto cleanup_nand;
 	}
 
 	dev_set_drvdata(dev, mtd);
 
 	/* Register device in MTD */
+	retval = nand_set_release(chip, NULL);
+	if (retval)
+		goto cleanup_nand;
 	retval = mtd_device_register(mtd, NULL, 0);
 	if (retval) {
 		dev_err(dev, "Error adding MTD device!\n");
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		goto error;
 	}
 
 	return 0;
+cleanup_nand:
+	nand_cleanup(chip);
 error:
 	mpc5121_nfc_free(dev, mtd);
 	return retval;
@@ -808,8 +822,9 @@ static void mpc5121_nfc_remove(struct platform_device *op)
 
 	ret = mtd_device_unregister(mtd);
 	WARN_ON(ret);
-	nand_cleanup(mtd_to_nand(mtd));
+	nand_disconnect(mtd_to_nand(mtd));
 	mpc5121_nfc_free(dev, mtd);
+	mtd_put_master(mtd);
 }
 
 static const struct of_device_id mpc5121_nfc_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 52/58] mtd: rawnand: bcm47xxnflash: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (48 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 51/58] mtd: rawnand: mpc5121: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 53/58] mtd: rawnand: fsl_elbc: " James Hilliard
                   ` (7 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Disconnect the scanned chip on removal before the BCMA-backed hardware
state becomes unavailable. Retain the embedded NAND controller and chip
until the final MTD put, including registration failure cleanup.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/bcm47xxnflash/main.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/main.c b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
index 4d4e185c22e5..60fc440d4515 100644
--- a/drivers/mtd/nand/raw/bcm47xxnflash/main.c
+++ b/drivers/mtd/nand/raw/bcm47xxnflash/main.c
@@ -19,6 +19,12 @@ MODULE_AUTHOR("Rafał Miłecki");
 
 static const char *probes[] = { "bcm47xxpart", NULL };
 
+static void bcm47xxnflash_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct bcm47xxnflash,
+			   nand_chip.legacy.dummy_controller));
+}
+
 static int bcm47xxnflash_probe(struct platform_device *pdev)
 {
 	struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
@@ -26,9 +32,14 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
 	struct mtd_info *mtd;
 	int err = 0;
 
-	b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL);
+	b47n = kzalloc_obj(*b47n);
 	if (!b47n)
 		return -ENOMEM;
+	b47n->nand_chip.controller = &b47n->nand_chip.legacy.dummy_controller;
+	err = devm_nand_controller_init(&pdev->dev, b47n->nand_chip.controller,
+					bcm47xxnflash_release);
+	if (err)
+		return err;
 
 	nand_set_controller_data(&b47n->nand_chip, b47n);
 	mtd = nand_to_mtd(&b47n->nand_chip);
@@ -48,9 +59,16 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, b47n);
 
+	err = nand_set_release(&b47n->nand_chip, NULL);
+	if (err) {
+		nand_cleanup(&b47n->nand_chip);
+		return err;
+	}
 	err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
 	if (err) {
 		pr_err("Failed to register MTD device: %d\n", err);
+		nand_disconnect(&b47n->nand_chip);
+		mtd_put_master(mtd);
 		return err;
 	}
 
@@ -65,7 +83,8 @@ static void bcm47xxnflash_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static struct platform_driver bcm47xxnflash_driver = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 53/58] mtd: rawnand: fsl_elbc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (49 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 52/58] mtd: rawnand: bcm47xxnflash: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 54/58] mtd: rawnand: fsl_ifc: " James Hilliard
                   ` (6 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Keep a reference to the shared FCM controller for each scanned chip,
including chips retained after unbind. Count active banks separately so
a new binding cannot reuse controller state still owned by old MTD
handles.

Unregister and drain the chip before unmapping its chip-select window.
Release NAND metadata, chip storage and the shared controller reference
only after the final MTD put.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/fsl_elbc_nand.c | 68 ++++++++++++++++++++++++------------
 1 file changed, 45 insertions(+), 23 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
index c655d27bc4cc..c27e89050270 100644
--- a/drivers/mtd/nand/raw/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -48,6 +48,7 @@ struct fsl_elbc_mtd {
 
 struct fsl_elbc_fcm_ctrl {
 	struct nand_controller controller;
+	struct kref refcnt;
 	struct fsl_elbc_mtd *chips[MAX_BANKS];
 
 	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
@@ -837,23 +838,35 @@ static const struct nand_controller_ops fsl_elbc_controller_ops = {
 	.attach_chip = fsl_elbc_attach_chip,
 };
 
-static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
+static void fsl_elbc_controller_release(struct kref *ref)
 {
-	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
-	struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+	kfree(container_of(ref, struct fsl_elbc_fcm_ctrl, refcnt));
+}
+
+static void fsl_elbc_chip_release(struct nand_chip *chip)
+{
+	struct fsl_elbc_fcm_ctrl *ctrl;
 
-	kfree(mtd->name);
+	ctrl = container_of(chip->controller, struct fsl_elbc_fcm_ctrl, controller);
+	kfree(nand_get_controller_data(chip));
+	kref_put(&ctrl->refcnt, fsl_elbc_controller_release);
+}
+
+static DEFINE_MUTEX(fsl_elbc_nand_mutex);
+
+static void fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
+{
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
 	if (priv->vbase)
 		iounmap(priv->vbase);
 
+	guard(mutex)(&fsl_elbc_nand_mutex);
 	elbc_fcm_ctrl->chips[priv->bank] = NULL;
-	kfree(priv);
-	return 0;
+	if (!--elbc_fcm_ctrl->counter)
+		priv->ctrl->nand = NULL;
 }
 
-static DEFINE_MUTEX(fsl_elbc_nand_mutex);
-
 static int fsl_elbc_nand_probe(struct platform_device *pdev)
 {
 	struct fsl_lbc_regs __iomem *lbc;
@@ -867,6 +880,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
 	struct device *dev;
 	struct device_node *node = pdev->dev.of_node;
 	struct mtd_info *mtd;
+	const char *name;
 
 	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
 		return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "lbc_ctrl_dev missing\n");
@@ -904,21 +918,25 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
 		elbc_fcm_ctrl = kzalloc_obj(*elbc_fcm_ctrl);
 		if (!elbc_fcm_ctrl) {
 			mutex_unlock(&fsl_elbc_nand_mutex);
-			ret = -ENOMEM;
-			goto err;
+			kfree(priv);
+			return -ENOMEM;
 		}
-		elbc_fcm_ctrl->counter++;
+		kref_init(&elbc_fcm_ctrl->refcnt);
 
 		nand_controller_init(&elbc_fcm_ctrl->controller);
 		fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
 	} else {
 		elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
+		kref_get(&elbc_fcm_ctrl->refcnt);
 	}
+	elbc_fcm_ctrl->counter++;
 	mutex_unlock(&fsl_elbc_nand_mutex);
 
 	elbc_fcm_ctrl->chips[bank] = priv;
 	priv->bank = bank;
 	priv->ctrl = fsl_lbc_ctrl_dev;
+	priv->chip.controller = &elbc_fcm_ctrl->controller;
+	nand_set_controller_data(&priv->chip, priv);
 	priv->dev = &pdev->dev;
 	dev_set_drvdata(priv->dev, priv);
 
@@ -945,11 +963,21 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
 	if (ret)
 		goto err;
 
+	name = mtd->name;
+	ret = nand_set_release(&priv->chip, fsl_elbc_chip_release);
+	if (ret)
+		goto cleanup_nand;
+	kfree(name);
+
 	/* 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_elbc_chip_remove(priv);
+		mtd_put_master(mtd);
+		return ret;
+	}
 
 	pr_info("eLBC NAND device at 0x%llx, bank %d\n",
 		(unsigned long long)res.start, priv->bank);
@@ -959,32 +987,26 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
 cleanup_nand:
 	nand_cleanup(&priv->chip);
 err:
+	kfree(nand_to_mtd(&priv->chip)->name);
 	fsl_elbc_chip_remove(priv);
+	fsl_elbc_chip_release(&priv->chip);
 
 	return ret;
 }
 
 static void fsl_elbc_nand_remove(struct platform_device *pdev)
 {
-	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
 	struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
 	struct nand_chip *chip = &priv->chip;
 	int ret;
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
 
 	fsl_elbc_chip_remove(priv);
 
-	mutex_lock(&fsl_elbc_nand_mutex);
-	elbc_fcm_ctrl->counter--;
-	if (!elbc_fcm_ctrl->counter) {
-		fsl_lbc_ctrl_dev->nand = NULL;
-		kfree(elbc_fcm_ctrl);
-	}
-	mutex_unlock(&fsl_elbc_nand_mutex);
-
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 static const struct of_device_id fsl_elbc_nand_match[] = {

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 54/58] mtd: rawnand: fsl_ifc: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (50 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 53/58] mtd: rawnand: fsl_elbc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 55/58] mtd: rawnand: r852: " James Hilliard
                   ` (5 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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@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


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 55/58] mtd: rawnand: r852: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (51 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 54/58] mtd: rawnand: fsl_ifc: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:59   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 56/58] mtd: rawnand: nandsim: " James Hilliard
                   ` (4 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

A removed card can still have open MTD handles. Reinitializing the same
nand_chip for a newly inserted card lets those handles access
replacement media, while freeing it during PCI removal leaves dangling
references.

Allocate a fresh chip for every insertion and separate SmartMedia
scanning from publication so lifetime ownership is installed first.
Disconnect the old chip before disabling its hardware, and retain it and
the controller until their final MTD reference is released. Stop
IRQ-driven card-work scheduling before destroying the workqueue during
PCI removal.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/r852.c      | 111 ++++++++++++++++++++++++---------------
 drivers/mtd/nand/raw/r852.h      |   1 +
 drivers/mtd/nand/raw/sm_common.c |  15 ++----
 drivers/mtd/nand/raw/sm_common.h |   2 +-
 4 files changed, 75 insertions(+), 54 deletions(-)

diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
index 92c47d351c27..0fb320cd7b77 100644
--- a/drivers/mtd/nand/raw/r852.c
+++ b/drivers/mtd/nand/raw/r852.c
@@ -625,16 +625,34 @@ static void r852_update_media_status(struct r852_device *dev)
 	spin_unlock_irqrestore(&dev->irqlock, flags);
 }
 
-/*
- * Register the nand device
- * Called when the card is detected
- */
+static void r852_chip_release(struct nand_chip *chip)
+{
+	kfree(chip);
+}
+
+/* Register a new chip instance for each inserted card. */
 static int r852_register_nand_device(struct r852_device *dev)
 {
-	struct mtd_info *mtd = nand_to_mtd(dev->chip);
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	int ret;
 
 	WARN_ON(dev->card_registered);
 
+	chip = kzalloc_obj(*chip);
+	if (!chip)
+		goto error1;
+
+	chip->legacy.cmd_ctrl = r852_cmdctl;
+	chip->legacy.waitfunc = r852_wait;
+	chip->legacy.dev_ready = r852_ready;
+	chip->legacy.read_byte = r852_read_byte;
+	chip->legacy.read_buf = r852_read_buf;
+	chip->legacy.write_buf = r852_write_buf;
+	chip->controller = &dev->controller;
+	nand_set_controller_data(chip, dev);
+	dev->chip = chip;
+	mtd = nand_to_mtd(chip);
 	mtd->dev.parent = &dev->pci_dev->dev;
 
 	if (dev->readonly)
@@ -642,8 +660,17 @@ static int r852_register_nand_device(struct r852_device *dev)
 
 	r852_engine_enable(dev);
 
-	if (sm_register_device(mtd, dev->sm))
-		goto error1;
+	ret = sm_scan_device(mtd, dev->sm);
+	if (ret)
+		goto free_chip;
+	ret = nand_set_release(chip, r852_chip_release);
+	if (ret) {
+		nand_cleanup(chip);
+		goto free_chip;
+	}
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret)
+		goto put_chip;
 
 	if (device_create_file(&mtd->dev, &dev_attr_media_type)) {
 		message("can't create media type sysfs attribute");
@@ -653,8 +680,17 @@ static int r852_register_nand_device(struct r852_device *dev)
 	dev->card_registered = 1;
 	return 0;
 error3:
-	WARN_ON(mtd_device_unregister(nand_to_mtd(dev->chip)));
-	nand_cleanup(dev->chip);
+	WARN_ON(mtd_device_unregister(mtd));
+put_chip:
+	nand_disconnect(chip);
+	r852_engine_disable(dev);
+	dev->chip = NULL;
+	mtd_put_master(mtd);
+	goto error1;
+free_chip:
+	r852_engine_disable(dev);
+	dev->chip = NULL;
+	kfree(chip);
 error1:
 	/* Force card redetect */
 	dev->card_detected = 0;
@@ -667,16 +703,19 @@ static int r852_register_nand_device(struct r852_device *dev)
 
 static void r852_unregister_nand_device(struct r852_device *dev)
 {
-	struct mtd_info *mtd = nand_to_mtd(dev->chip);
+	struct mtd_info *mtd;
 
 	if (!dev->card_registered)
 		return;
+	mtd = nand_to_mtd(dev->chip);
 
 	device_remove_file(&mtd->dev, &dev_attr_media_type);
 	WARN_ON(mtd_device_unregister(mtd));
-	nand_cleanup(dev->chip);
+	nand_disconnect(dev->chip);
 	r852_engine_disable(dev);
+	dev->chip = NULL;
 	dev->card_registered = 0;
+	mtd_put_master(mtd);
 }
 
 /* Card state updater */
@@ -734,7 +773,8 @@ static irqreturn_t r852_irq(int irq, void *data)
 	card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
 	r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
 
-	if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) {
+	if (!dev->removing &&
+	    (card_status & (R852_CARD_IRQ_INSERT | R852_CARD_IRQ_REMOVE))) {
 
 		ret = IRQ_HANDLED;
 		dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);
@@ -841,10 +881,14 @@ static const struct nand_controller_ops r852_ops = {
 	.attach_chip = r852_attach_chip,
 };
 
+static void r852_controller_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct r852_device, controller));
+}
+
 static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 {
 	int error;
-	struct nand_chip *chip;
 	struct r852_device *dev;
 
 	/* pci initialization */
@@ -866,42 +910,28 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 
 	error = -ENOMEM;
 
-	/* init nand chip, but register it only on card insert */
-	chip = kzalloc_obj(struct nand_chip);
-
-	if (!chip)
-		goto error4;
-
-	/* commands */
-	chip->legacy.cmd_ctrl = r852_cmdctl;
-	chip->legacy.waitfunc = r852_wait;
-	chip->legacy.dev_ready = r852_ready;
-
-	/* I/O */
-	chip->legacy.read_byte = r852_read_byte;
-	chip->legacy.read_buf = r852_read_buf;
-	chip->legacy.write_buf = r852_write_buf;
-
 	/* init our device structure */
 	dev = kzalloc_obj(struct r852_device);
 
 	if (!dev)
-		goto error5;
+		goto error4;
+	error = devm_nand_controller_init(&pci_dev->dev, &dev->controller,
+					  r852_controller_release);
+	if (error)
+		goto error4;
 
-	nand_set_controller_data(chip, dev);
-	dev->chip = chip;
 	dev->pci_dev = pci_dev;
 	pci_set_drvdata(pci_dev, dev);
 
-	nand_controller_init(&dev->controller);
 	dev->controller.ops = &r852_ops;
-	chip->controller = &dev->controller;
 
 	dev->bounce_buffer = dma_alloc_coherent(&pci_dev->dev, R852_DMA_LEN,
 		&dev->phys_bounce_buffer, GFP_KERNEL);
 
-	if (!dev->bounce_buffer)
+	if (!dev->bounce_buffer) {
+		error = -ENOMEM;
 		goto error6;
+	}
 
 
 	error = -ENODEV;
@@ -961,9 +991,6 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 	dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer,
 			  dev->phys_bounce_buffer);
 error6:
-	kfree(dev);
-error5:
-	kfree(chip);
 error4:
 	pci_release_regions(pci_dev);
 error3:
@@ -976,6 +1003,11 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 static void r852_remove(struct pci_dev *pci_dev)
 {
 	struct r852_device *dev = pci_get_drvdata(pci_dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	dev->removing = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
 
 	/* Stop detect workqueue -
 		we are going to unregister the device anyway*/
@@ -995,9 +1027,6 @@ static void r852_remove(struct pci_dev *pci_dev)
 	dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer,
 			  dev->phys_bounce_buffer);
 
-	kfree(dev->chip);
-	kfree(dev);
-
 	/* Shutdown the PCI device */
 	pci_release_regions(pci_dev);
 	pci_disable_device(pci_dev);
diff --git a/drivers/mtd/nand/raw/r852.h b/drivers/mtd/nand/raw/r852.h
index 96fe301d15da..a89ab61ea51e 100644
--- a/drivers/mtd/nand/raw/r852.h
+++ b/drivers/mtd/nand/raw/r852.h
@@ -128,6 +128,7 @@ struct r852_device {
 	struct delayed_work card_detect_work;
 	struct workqueue_struct *card_workqueue;
 	int card_registered;		/* card registered with mtd */
+	bool removing;			/* stop card work during PCI removal */
 	int card_detected;		/* card detected in slot */
 	int card_unstable;		/* whenever the card is inserted,
 					   is not known yet */
diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c
index e238784c8c3e..a79e3c3c77d1 100644
--- a/drivers/mtd/nand/raw/sm_common.c
+++ b/drivers/mtd/nand/raw/sm_common.c
@@ -182,28 +182,19 @@ static const struct nand_controller_ops sm_controller_ops = {
 	.attach_chip = sm_attach_chip,
 };
 
-int sm_register_device(struct mtd_info *mtd, int smartmedia)
+int sm_scan_device(struct mtd_info *mtd, int smartmedia)
 {
 	struct nand_chip *chip = mtd_to_nand(mtd);
 	struct nand_flash_dev *flash_ids;
-	int ret;
 
 	chip->options |= NAND_SKIP_BBTSCAN;
 
 	/* Scan for card properties */
 	chip->legacy.dummy_controller.ops = &sm_controller_ops;
 	flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids;
-	ret = nand_scan_with_ids(chip, 1, flash_ids);
-	if (ret)
-		return ret;
-
-	ret = mtd_device_register(mtd, NULL, 0);
-	if (ret)
-		nand_cleanup(chip);
-
-	return ret;
+	return nand_scan_with_ids(chip, 1, flash_ids);
 }
-EXPORT_SYMBOL_GPL(sm_register_device);
+EXPORT_SYMBOL_GPL(sm_scan_device);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
diff --git a/drivers/mtd/nand/raw/sm_common.h b/drivers/mtd/nand/raw/sm_common.h
index 57fc9f86f9ee..d8a330d6b730 100644
--- a/drivers/mtd/nand/raw/sm_common.h
+++ b/drivers/mtd/nand/raw/sm_common.h
@@ -33,7 +33,7 @@ struct sm_oob {
 #define SM_SMALL_OOB_SIZE	8
 
 
-int sm_register_device(struct mtd_info *mtd, int smartmedia);
+int sm_scan_device(struct mtd_info *mtd, int smartmedia);
 
 
 static inline int sm_sector_valid(struct sm_oob *oob)

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 56/58] mtd: rawnand: nandsim: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (52 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 55/58] mtd: rawnand: r852: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 20:52   ` sashiko-bot
  2026-11-07 10:16 ` [PATCH 57/58] mtd: rawnand: cs553x: " James Hilliard
                   ` (3 subsequent siblings)
  57 siblings, 1 reply; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

MTD device references can outlive device unregistration even when no
open file pins the simulator module. Disconnect before freeing the
simulated media, and retain the chip metadata until the final MTD put.
The lifetime core waits for remaining device references during module
unload.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/nandsim.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
index fe968037f75a..39fefd83cd85 100644
--- a/drivers/mtd/nand/raw/nandsim.c
+++ b/drivers/mtd/nand/raw/nandsim.c
@@ -2253,6 +2253,11 @@ static const struct nand_controller_ops ns_controller_ops = {
 	.exec_op = ns_exec_op,
 };
 
+static void ns_chip_release(struct nand_chip *chip)
+{
+	kfree(nand_get_controller_data(chip));
+}
+
 /*
  * Module initialization function
  */
@@ -2262,6 +2267,7 @@ static int __init ns_init_module(void)
 	struct nand_chip *chip;
 	struct nandsim *ns;
 	int ret;
+	bool retained = false;
 
 	if (bus_width != 8 && bus_width != 16) {
 		NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
@@ -2376,6 +2382,10 @@ static int __init ns_init_module(void)
 	ret = ns_parse_badblocks(ns, nsmtd);
 	if (ret)
 		goto free_ns_object;
+	ret = nand_set_release(chip, ns_chip_release);
+	if (ret)
+		goto free_ns_object;
+	retained = true;
 
 	/* Register NAND partitions */
 	ret = mtd_device_register(nsmtd, &ns->partitions[0], ns->nbparts);
@@ -2391,11 +2401,16 @@ static int __init ns_init_module(void)
 unregister_mtd:
 	WARN_ON(mtd_device_unregister(nsmtd));
 free_ns_object:
+	if (retained)
+		nand_disconnect(chip);
 	ns_free(ns);
 free_ebw:
 	kfree(erase_block_wear);
 cleanup_nand:
-	nand_cleanup(chip);
+	if (retained)
+		mtd_put_master(nsmtd);
+	else
+		nand_cleanup(chip);
 free_gp_list:
 	list_for_each_safe(pos, n, &grave_pages) {
 		list_del(pos);
@@ -2412,7 +2427,8 @@ static int __init ns_init_module(void)
 		kfree(list_entry(pos, struct weak_block, list));
 	}
 free_ns_struct:
-	kfree(ns);
+	if (!retained)
+		kfree(ns);
 
 	return ret;
 }
@@ -2430,9 +2446,9 @@ static void __exit ns_cleanup_module(void)
 
 	ns_debugfs_remove(ns);
 	WARN_ON(mtd_device_unregister(nsmtd));
+	nand_disconnect(chip);
 	ns_free(ns);
 	kfree(erase_block_wear);
-	nand_cleanup(chip);
 
 	list_for_each_safe(pos, n, &grave_pages) {
 		list_del(pos);
@@ -2449,7 +2465,7 @@ static void __exit ns_cleanup_module(void)
 		kfree(list_entry(pos, struct weak_block, list));
 	}
 
-	kfree(ns);
+	mtd_put_master(nsmtd);
 }
 
 module_exit(ns_cleanup_module);

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 57/58] mtd: rawnand: cs553x: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (53 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 56/58] mtd: rawnand: nandsim: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 10:16 ` [PATCH 58/58] mtd: rawnand: diskonchip: " James Hilliard
                   ` (2 subsequent siblings)
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

Keep chip metadata alive while MTD device references remain after module
removal unregisters the device. Disconnect before unmapping the
controller, and release the backing allocation at the final MTD put.

Check registration failures and release the corresponding controller
without leaving it in the registered-device array.

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

diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c
index 0b872b1e3b04..7419308a740d 100644
--- a/drivers/mtd/nand/raw/cs553x_nand.c
+++ b/drivers/mtd/nand/raw/cs553x_nand.c
@@ -257,12 +257,18 @@ static const struct nand_controller_ops cs553x_nand_controller_ops = {
 	.attach_chip = cs553x_attach_chip,
 };
 
+static void cs553x_chip_release(struct nand_chip *chip)
+{
+	kfree(container_of(chip, struct cs553x_nand_controller, chip));
+}
+
 static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
 {
 	struct cs553x_nand_controller *controller;
 	int err = 0;
 	struct nand_chip *this;
 	struct mtd_info *new_mtd;
+	const char *name;
 
 	pr_notice("Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n",
 		  cs, mmio ? "MM" : "P", adr);
@@ -309,6 +315,13 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
 	err = nand_scan(this, 1);
 	if (err)
 		goto out_free;
+	name = new_mtd->name;
+	err = nand_set_release(this, cs553x_chip_release);
+	if (err) {
+		nand_cleanup(this);
+		goto out_free;
+	}
+	kfree(name);
 
 	controllers[cs] = controller;
 	goto out;
@@ -344,6 +357,7 @@ static int __init cs553x_init(void)
 {
 	int err = -ENXIO;
 	int i;
+	int registered = 0;
 	uint64_t val;
 
 	/* If the CPU isn't a Geode GX or LX, abort */
@@ -374,14 +388,22 @@ static int __init cs553x_init(void)
 	   do mtdconcat etc. if we want to. */
 	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
 		if (controllers[i]) {
-			/* If any devices registered, return success. Else the last error. */
-			mtd_device_register(nand_to_mtd(&controllers[i]->chip),
-					    NULL, 0);
-			err = 0;
+			struct cs553x_nand_controller *controller = controllers[i];
+			struct mtd_info *mtd = nand_to_mtd(&controller->chip);
+
+			err = mtd_device_register(mtd, NULL, 0);
+			if (err) {
+				nand_disconnect(&controller->chip);
+				iounmap(controller->mmio);
+				controllers[i] = NULL;
+				mtd_put_master(mtd);
+			} else {
+				registered++;
+			}
 		}
 	}
 
-	return err;
+	return registered ? 0 : err;
 }
 
 module_init(cs553x_init);
@@ -392,25 +414,25 @@ static void __exit cs553x_cleanup(void)
 
 	for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
 		struct cs553x_nand_controller *controller = controllers[i];
-		struct nand_chip *this = &controller->chip;
-		struct mtd_info *mtd = nand_to_mtd(this);
+		struct nand_chip *this;
+		struct mtd_info *mtd;
 		int ret;
 
-		if (!mtd)
+		if (!controller)
 			continue;
+		this = &controller->chip;
+		mtd = nand_to_mtd(this);
 
 		/* Release resources, unregister device */
 		ret = mtd_device_unregister(mtd);
 		WARN_ON(ret);
-		nand_cleanup(this);
-		kfree(mtd->name);
+		nand_disconnect(this);
 		controllers[i] = NULL;
 
 		/* unmap physical address */
 		iounmap(controller->mmio);
 
-		/* Free the MTD device structure */
-		kfree(controller);
+		mtd_put_master(mtd);
 	}
 }
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 58/58] mtd: rawnand: diskonchip: retain chip state across removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (54 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 57/58] mtd: rawnand: cs553x: " James Hilliard
@ 2026-11-07 10:16 ` James Hilliard
  2026-11-07 19:33 ` [PATCH 25/58] mtd: rawnand: hisi504: retain open-handle state on removal James Hilliard
  2026-11-07 19:33 ` [PATCH 26/58] mtd: rawnand: vf610: " James Hilliard
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 10:16 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

MTD device references may outlive unregistration during module removal.
Disconnect before unmapping the device and freeing the hardware ECC
codec, while retaining chip metadata until the final MTD reference is
released. Install lifetime ownership before the late initializer
publishes partitions.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/diskonchip.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c
index 540b6baf8bb1..a4338b51b49d 100644
--- a/drivers/mtd/nand/raw/diskonchip.c
+++ b/drivers/mtd/nand/raw/diskonchip.c
@@ -1296,6 +1296,11 @@ static const struct nand_controller_ops doc2001plus_ops = {
 	.attach_chip = doc200x_attach_chip,
 };
 
+static void doc_chip_release(struct nand_chip *chip)
+{
+	kfree(chip);
+}
+
 static int __init doc_probe(unsigned long physadr)
 {
 	struct nand_chip *nand = NULL;
@@ -1494,13 +1499,21 @@ static int __init doc_probe(unsigned long physadr)
 	ret = nand_scan(nand, numchips);
 	if (ret)
 		goto fail;
-
-	ret = doc->late_init(mtd);
+	ret = nand_set_release(nand, doc_chip_release);
 	if (ret) {
 		nand_cleanup(nand);
 		goto fail;
 	}
 
+	ret = doc->late_init(mtd);
+	if (ret) {
+		nand_disconnect(nand);
+		free_rs(doc->rs_decoder);
+		iounmap(virtadr);
+		mtd_put_master(mtd);
+		goto error_ioremap;
+	}
+
 	/* Success! */
 	doclist = mtd;
 	return 0;
@@ -1535,11 +1548,11 @@ static void release_nanddoc(void)
 		nextmtd = doc->nextdoc;
 		ret = mtd_device_unregister(mtd);
 		WARN_ON(ret);
-		nand_cleanup(nand);
+		nand_disconnect(nand);
 		iounmap(doc->virtadr);
 		release_mem_region(doc->physadr, DOC_IOREMAP_LEN);
 		free_rs(doc->rs_decoder);
-		kfree(nand);
+		mtd_put_master(mtd);
 	}
 }
 

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 25/58] mtd: rawnand: hisi504: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (55 preceding siblings ...)
  2026-11-07 10:16 ` [PATCH 58/58] mtd: rawnand: diskonchip: " James Hilliard
@ 2026-11-07 19:33 ` James Hilliard
  2026-11-07 19:33 ` [PATCH 26/58] mtd: rawnand: vf610: " James Hilliard
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 19:33 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Retain the host through its embedded legacy controller.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/hisi504_nand.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c
index d97270ec1185..36427a242c2b 100644
--- a/drivers/mtd/nand/raw/hisi504_nand.c
+++ b/drivers/mtd/nand/raw/hisi504_nand.c
@@ -731,6 +731,11 @@ static const struct nand_controller_ops hisi_nfc_controller_ops = {
 	.attach_chip = hisi_nfc_attach_chip,
 };
 
+static void hisi_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct hinfc_host, chip.legacy.dummy_controller));
+}
+
 static int hisi_nfc_probe(struct platform_device *pdev)
 {
 	int ret = 0, irq, max_chips = HINFC504_MAX_CHIP;
@@ -740,9 +745,14 @@ static int hisi_nfc_probe(struct platform_device *pdev)
 	struct mtd_info   *mtd;
 	struct device_node *np = dev->of_node;
 
-	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+	host = kzalloc_obj(*host);
 	if (!host)
 		return -ENOMEM;
+	chip = &host->chip;
+	chip->controller = &chip->legacy.dummy_controller;
+	ret = devm_nand_controller_init(dev, chip->controller, hisi_nfc_release);
+	if (ret)
+		return ret;
 	host->dev = dev;
 
 	platform_set_drvdata(pdev, host);
@@ -788,10 +798,16 @@ static int hisi_nfc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	ret = nand_set_release(chip, NULL);
+	if (ret) {
+		nand_cleanup(chip);
+		return ret;
+	}
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(dev, "Err MTD partition=%d\n", ret);
-		nand_cleanup(chip);
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
 		return ret;
 	}
 
@@ -806,7 +822,8 @@ static void hisi_nfc_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 #ifdef CONFIG_PM_SLEEP

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 26/58] mtd: rawnand: vf610: retain open-handle state on removal
  2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
                   ` (56 preceding siblings ...)
  2026-11-07 19:33 ` [PATCH 25/58] mtd: rawnand: hisi504: retain open-handle state on removal James Hilliard
@ 2026-11-07 19:33 ` James Hilliard
  57 siblings, 0 replies; 73+ messages in thread
From: James Hilliard @ 2026-11-07 19:33 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Brian Norris, Boris Brezillon, Arseniy Krasnov, Chen-Yu Tsai,
	Jernej Skrabec, Samuel Holland, Thomas Gleixner, Vitaly Wool,
	David Woodhouse, Russell King, David Woodhouse, Mike Rapoport,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello,
	Jason Roberts, Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-sunxi, linux-tegra, linux-mediatek, linux-rockchip,
	linux-mips, imx, linux-renesas-soc, linux-amlogic, linux-arm-msm,
	James Hilliard

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.

Keep managed register, IRQ and clock teardown after chip disconnection.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/mtd/nand/raw/vf610_nfc.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c
index 9940681810cf..903a25c549d8 100644
--- a/drivers/mtd/nand/raw/vf610_nfc.c
+++ b/drivers/mtd/nand/raw/vf610_nfc.c
@@ -805,6 +805,11 @@ static const struct nand_controller_ops vf610_nfc_controller_ops = {
 
 };
 
+static void vf610_nfc_release(struct nand_controller *controller)
+{
+	kfree(container_of(controller, struct vf610_nfc, base));
+}
+
 static int vf610_nfc_probe(struct platform_device *pdev)
 {
 	struct vf610_nfc *nfc;
@@ -813,9 +818,12 @@ static int vf610_nfc_probe(struct platform_device *pdev)
 	int err;
 	int irq;
 
-	nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+	nfc = kzalloc_obj(*nfc);
 	if (!nfc)
 		return -ENOMEM;
+	err = devm_nand_controller_init(&pdev->dev, &nfc->base, vf610_nfc_release);
+	if (err)
+		return err;
 
 	nfc->dev = &pdev->dev;
 	chip = &nfc->chip;
@@ -873,7 +881,6 @@ static int vf610_nfc_probe(struct platform_device *pdev)
 
 	vf610_nfc_preinit_controller(nfc);
 
-	nand_controller_init(&nfc->base);
 	nfc->base.ops = &vf610_nfc_controller_ops;
 	chip->controller = &nfc->base;
 
@@ -885,9 +892,15 @@ static int vf610_nfc_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, nfc);
 
 	/* Register device in MTD */
-	err = mtd_device_register(mtd, NULL, 0);
+	err = nand_set_release(chip, NULL);
 	if (err)
 		goto err_cleanup_nand;
+	err = mtd_device_register(mtd, NULL, 0);
+	if (err) {
+		nand_disconnect(chip);
+		mtd_put_master(mtd);
+		return err;
+	}
 	return 0;
 
 err_cleanup_nand:
@@ -903,7 +916,8 @@ static void vf610_nfc_remove(struct platform_device *pdev)
 
 	ret = mtd_device_unregister(nand_to_mtd(chip));
 	WARN_ON(ret);
-	nand_cleanup(chip);
+	nand_disconnect(chip);
+	mtd_put_master(nand_to_mtd(chip));
 }
 
 #ifdef CONFIG_PM_SLEEP

-- 
2.53.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them
  2026-11-07 10:15 ` [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them James Hilliard
@ 2026-11-07 20:21   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:21 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Critical] mtd: memory leak and use-after-free on
  mtd_device_parse_register() failure [1]
--

commit d004139d8d5ef4bea351eaa0703331cff456a77d
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: clear OTP provider pointers after unregistering them

This commit centralizes OTP nvmem provider removal into
mtd_otp_nvmem_remove() and clears the provider pointers after
unregistering them to prevent duplicate unregistrations on error and
device teardown paths.

> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
> index 8efa2d88556e9..558a605ba7495 100644
> --- a/drivers/mtd/mtdcore.c
> +++ b/drivers/mtd/mtdcore.c

[ ... ]

> @@ -1180,10 +1188,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
>  	}
>  
>  out:
> -	if (ret) {
> -		nvmem_unregister(mtd->otp_user_nvmem);
> -		nvmem_unregister(mtd->otp_factory_nvmem);
> -	}
> +	if (ret)
> +		mtd_otp_nvmem_remove(mtd);
>  
>  	if (ret && device_is_registered(&mtd->dev)) {
>  		err = del_mtd_device(mtd);

[Severity: Critical]
This problem wasn't introduced by this patch, but can a failure in
mtd_virt_concat_create_join() leak partitions and leave dangling pointers
in concat->subdev[] [1]?

In mtd_device_parse_register(), parse_mtd_partitions() or
add_mtd_partitions() runs before mtd_virt_concat_create_join(). When
mtd_virt_concat_add() claims a child partition, it stores the pointer in
concat->subdev[] and add_mtd_partitions() skips adding the child to
parent->partitions.

If mtd_virt_concat_create_join() then fails and jumps to out, this unwind
path returns without calling mtd_virt_concat_destroy() or
del_mtd_partitions(mtd). Because callers free the master mtd_info when
mtd_device_parse_register() fails, registered partitions are leaked and
concat->subdev[] retains partitions whose parent pointers reference the
freed master mtd_info, causing a use-after-free via mtd_get_master() when
a later device registration calls mtd_virt_concat_create_join(). (The
err_del_partitions path in add_mtd_partitions() similarly calls only
del_mtd_partitions(), leaving any partitions already claimed by
mtd_virt_concat_add() in concat->subdev[].)

[1] https://sashiko.dev/#/bug/linux-eb184d91-0c17-4015-99a4-10b54060a747

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=1

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal
  2026-11-07 10:16 ` [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal James Hilliard
@ 2026-11-07 20:25   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:25 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] mtd: rawnand: denali: separate __free(kfree) init in
  denali_dt_chip_init()
--

commit 6e755ee59ce57be3f5cf80ff3dfee0f73764c61b
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: denali: retain chips across controller removal

This commit switches the Denali NAND controller and chip allocations from
device-managed storage to reference-counted objects so that chips and their
controller remain live until all MTD references are dropped.

> diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
> index 8c822eae72e78..8d813cc05d049 100644
> --- a/drivers/mtd/nand/raw/denali_dt.c
> +++ b/drivers/mtd/nand/raw/denali_dt.c
[ ... ]
> @@ -81,7 +82,7 @@ MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
>  static int denali_dt_chip_init(struct denali_controller *denali,
>  			       struct device_node *chip_np)
>  {
> -	struct denali_chip *dchip;
> +	struct denali_chip *dchip __free(kfree) = NULL;
>  	u32 bank;
>  	int nsels, i, ret;
>  
> @@ -89,8 +90,7 @@ static int denali_dt_chip_init(struct denali_controller *denali,
>  	if (nsels < 0)
>  		return nsels;
>  
> -	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
> -			     GFP_KERNEL);
> +	dchip = kzalloc(struct_size(dchip, sels, nsels), GFP_KERNEL);

[Severity: Low]
This isn't a bug, but cleanup.h convention is to define and initialize
__free() variables in a single statement at the allocation site rather than
initializing dchip to NULL at the top of denali_dt_chip_init().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=11

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 02/58] nvmem: retain provider context through final device release
  2026-11-07 10:15 ` [PATCH 02/58] nvmem: retain provider context through final device release James Hilliard
@ 2026-11-07 20:26   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:26 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] nvmem: unpinned module callback execution in nvmem_release()
--

commit 6223a607fc304676fa9e070c9a5930603931e0e7
Author: James Hilliard <james.hilliard1@gmail.com>

nvmem: retain provider context through final device release

This commit adds optional priv_get() and priv_put() callbacks to struct
nvmem_config so providers can retain their private context until final
device release in nvmem_release().

> diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
> index 0556d140170a4..851fa5cabecb2 100644
> --- a/drivers/nvmem/core.c
> +++ b/drivers/nvmem/core.c
> @@ -541,6 +541,8 @@ static void nvmem_release(struct device *dev)
>  
>  	ida_free(&nvmem_ida, nvmem->id);
>  	gpiod_put(nvmem->wp_gpio);
> +	if (nvmem->ops->priv_put)
> +		nvmem->ops->priv_put(nvmem->priv);
>  	kfree(nvmem->ops);
>  	kfree(nvmem);
>  }

[Severity: Medium]
Can executing nvmem->ops->priv_put() here race with module unload and run
unmapped module text?

The kernel-doc note in include/linux/nvmem-provider.h states that the
callbacks must retain any module needed to execute priv_put() until it
returns, and commit "mtd: add opt-in lifetime ownership for backing
objects" implements this by calling __module_get(THIS_MODULE) in
mtd_otp_nvmem_get() and module_put(THIS_MODULE) in mtd_otp_nvmem_put().
However, a callback in a loadable module cannot safely drop the last
reference to its own module before returning:

CPU0                              CPU1
nvmem_release()
  mtd_otp_nvmem_put()
    module_put(THIS_MODULE)
                                  delete_module()
                                    free_module()
    return;  <-- use-after-free

nvmem->owner is only held by active consumers and is already dropped in
nvmem_device_put() before nvmem_release() runs.  The NVMEM core should pin
the module owning priv_put() in nvmem_register() and call module_put() in
nvmem_release() after nvmem->ops->priv_put() returns.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=2

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal
  2026-11-07 10:16 ` [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal James Hilliard
@ 2026-11-07 20:29   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:29 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] mtd: rawnand: orion: missing Fixes trailer for removal UAF fix
--

commit 9465d514f949c22e40663ef5664153c0883e1d59
Author: James Hilliard <james.hilliard1@gmail.com>

> mtd: rawnand: orion: retain open-handle state on removal
>
> 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.
>
> Retain the embedded chip and its copied name through the shared
> allocation.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>

[Severity: Low]
This fixes a use-after-free when the device is removed while MTD handles
remain open, which has been present since the driver was introduced.
Should it include a Fixes tag?

  Fixes: 2a1dba2931dc ("[MTD] [NAND] Marvell Orion device bus NAND controller")

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=19

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 18/58] mtd: rawnand: atmel: retain chips across controller removal
  2026-11-07 10:16 ` [PATCH 18/58] mtd: rawnand: atmel: " James Hilliard
@ 2026-11-07 20:31   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:31 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] mtd: rawnand: atmel: initialize __free(kfree) variables at
  declaration
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [High] mtd: rawnand: atmel: unclocked register access in
  atmel_nfc_interrupt() [1]
--

commit b77082ebe154b5b59053fa6c8fd897f19a5685b6
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: atmel: retain chips across controller removal

This commit retains NAND chip and controller allocations until all MTD
references are released and disconnects chips before tearing down controller
resources.

> diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
> index 0bdbcbb50d94c..10b725fa61d86 100644
> --- a/drivers/mtd/nand/raw/atmel/nand-controller.c
> +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c

[ ... ]

> @@ -1638,7 +1640,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
>  					    struct device_node *np,
>  					    int reg_cells)
>  {
> -	struct atmel_nand *nand;
> +	struct atmel_nand *nand __free(kfree) = NULL;
>  	struct gpio_desc *gpio;
>  	int numcs, ret, i;
>  
> @@ -1649,7 +1651,7 @@ static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
>  		return ERR_PTR(-EINVAL);
>  	}
>  
> -	nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL);
> +	nand = kzalloc(struct_size(nand, cs, numcs), GFP_KERNEL);
>  	if (!nand)
>  		return ERR_PTR(-ENOMEM);

[ ... ]

> @@ -1791,7 +1809,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
>  {
>  	struct device *dev = nc->dev;
>  	struct platform_device *pdev = to_platform_device(dev);
> -	struct atmel_nand *nand;
> +	struct atmel_nand *nand __free(kfree) = NULL;
>  	struct gpio_desc *gpio;
>  	struct resource *res;
>  
> @@ -1799,7 +1817,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
>  	 * Legacy bindings only allow connecting a single NAND with a unique CS
>  	 * line to the controller.
>  	 */
> -	nand = devm_kzalloc(nc->dev, struct_size(nand, cs, 1), GFP_KERNEL);
> +	nand = kzalloc(struct_size(nand, cs, 1), GFP_KERNEL);
>  	if (!nand)
>  		return -ENOMEM;

[Severity: Low]
This isn't a bug, but per the Cleanup API conventions in
include/linux/cleanup.h, __free() variables should be defined and
initialized in a single statement at their allocation point rather than
declared as "= NULL" at the top of atmel_nand_create() and
atmel_nand_controller_legacy_add_nands() and assigned later.

[ ... ]

> @@ -2349,6 +2366,11 @@ atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
>  	return 0;
>  }
>  
> +static void atmel_hsmc_nand_controller_release(struct nand_controller *controller)
> +{
> +	kfree(to_hsmc_nand_controller(controller));
> +}
> +
>  static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
>  				const struct atmel_nand_controller_caps *caps)
>  {
> @@ -2356,9 +2378,13 @@ static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
>  	struct atmel_hsmc_nand_controller *nc;
>  	int ret;
>  
> -	nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
> +	nc = kzalloc_obj(*nc);
>  	if (!nc)
>  		return -ENOMEM;
> +	ret = devm_nand_controller_init(dev, &nc->base.base,
> +					atmel_hsmc_nand_controller_release);
> +	if (ret)
> +		return ret;

[Severity: High]
This problem wasn't introduced by this patch, but can atmel_nfc_interrupt()
perform an unclocked register read during teardown [1]?

atmel_hsmc_nand_controller_probe() registers atmel_nfc_interrupt() with
devm_request_irq(..., IRQF_SHARED, ...), while
atmel_hsmc_nand_controller_remove() manually disables hsmc_nc->clk before
devres releases the IRQ:

drivers/mtd/nand/raw/atmel/nand-controller.c:atmel_hsmc_nand_controller_remove() {
    ...
	if (hsmc_nc->clk) {
		clk_disable_unprepare(hsmc_nc->clk);
		clk_put(hsmc_nc->clk);
	}
    ...
}

CPU0                                    CPU1
atmel_hsmc_nand_controller_remove()
  clk_disable_unprepare(hsmc_nc->clk)
                                        <shared IRQ fires>
                                        atmel_nfc_interrupt()
                                          regmap_read(..., ATMEL_HSMC_NFC_SR, ...)
                                            <-- unclocked register access
<devres cleanup>
  devm_irq_release()

If another device on the shared IRQ line raises an interrupt before devres
frees the handler, reading ATMEL_HSMC_NFC_SR with the clock disabled can
trigger a synchronous external abort.

[1] https://sashiko.dev/#/bug/linux-94d27cd6-f6fe-452a-863e-36cbb87e9adc

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=18

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 14/58] mtd: rawnand: intel: retain open-handle state on removal
  2026-11-07 10:16 ` [PATCH 14/58] mtd: rawnand: intel: " James Hilliard
@ 2026-11-07 20:31   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:31 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Medium] mtd: rawnand: intel: missing hardware disable in
  ebu_nand_probe() [1]
--

commit 42115b2b815dc0a43fa249f1ae95d7eeefbb02a5
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: intel: retain open-handle state on removal

This commit disconnects NAND operations on removal and ties the lifetime of
the Intel EBU NAND controller and chip state to the last MTD reference.

> diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c
> index 477a427dac1f0..e4a24e69ebbcf 100644
> --- a/drivers/mtd/nand/raw/intel-nand-controller.c
> +++ b/drivers/mtd/nand/raw/intel-nand-controller.c

[ ... ]

> @@ -692,9 +699,15 @@ static int ebu_nand_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto err_cleanup_dma;
>  
> -	ret = mtd_device_register(mtd, NULL, 0);
> +	ret = nand_set_release(nand, NULL);
>  	if (ret)
>  		goto err_clean_nand;
> +	ret = mtd_device_register(mtd, NULL, 0);
> +	if (ret) {
> +		nand_disconnect(nand);
> +		mtd_put_master(mtd);
> +		goto err_cleanup_dma;
> +	}

[Severity: Medium]
This problem wasn't introduced by this patch, but if probe fails during or
after nand_scan() [1], doesn't jumping to err_clean_nand or
err_cleanup_dma leave EBU_CON_NANDM_EN enabled in EBU_CON?

nand_scan() runs ebu_nand_exec_op() -> ebu_select_chip(), which enables the
controller in EBU_CON, but unlike ebu_nand_remove(), the probe error paths
never call ebu_nand_disable(&ebu_host->chip) before cleaning up DMA and
returning.

[1] https://sashiko.dev/#/bug/linux-34984827-527c-4bf7-baa8-10dffee6916d

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=14

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/58] mtd: rawnand: tegra: retain open-handle state on removal
  2026-11-07 10:16 ` [PATCH 15/58] mtd: rawnand: tegra: " James Hilliard
@ 2026-11-07 20:32   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:32 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [High] mtd: rawnand: tegra: unclocked register access in
  tegra_nand_irq() [1]
--

commit 6164eede324ed80f4bb2c1a0eea0f8635af28e57
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: tegra: retain open-handle state on removal

This commit switches the Tegra NAND chip and controller allocations to
reference-counted release callbacks and disconnects the chip before
suspending the controller on removal.

> diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
> index 7f9eb5f042a7e..da7791741b9d4 100644
> --- a/drivers/mtd/nand/raw/tegra_nand.c
> +++ b/drivers/mtd/nand/raw/tegra_nand.c

[ ... ]

> @@ -1232,10 +1254,11 @@ static void tegra_nand_remove(struct platform_device *pdev)
>  
>  	WARN_ON(mtd_device_unregister(mtd));
>  
> -	nand_cleanup(chip);
> +	nand_disconnect(chip);
>  
>  	pm_runtime_put_sync_suspend(ctrl->dev);
>  	pm_runtime_force_suspend(ctrl->dev);
> +	mtd_put_master(mtd);
>  }

[Severity: High]
This problem wasn't introduced by this patch, but can tegra_nand_irq()
access unclocked registers after pm_runtime_force_suspend() [1]?

tegra_nand_probe() registers tegra_nand_irq() with devm_request_irq(), so
the IRQ remains active until devres cleanup runs after tegra_nand_remove()
(and after the err_put_pm path in tegra_nand_probe()) returns.  In the
meantime, pm_runtime_force_suspend() disables ctrl->clk, and any pending or
spurious interrupt before devres frees the IRQ causes tegra_nand_irq() to
read ISR and DMA_MST_CTRL while the controller is unclocked, triggering an
asynchronous external abort on Tegra.

[1] https://sashiko.dev/#/bug/linux-ae385c5a-5cfb-462a-ab95-dd13d6f5e107

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=15

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup
  2026-11-07 10:16 ` [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup James Hilliard
@ 2026-11-07 20:42   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:42 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] mtd: rawnand: unlocked hardware access after nand_disconnect()
  in write_bbt()
- [Medium] mtd: rawnand: nand_suspend() returning -ENODEV aborts system
  suspend
--

commit 0d69828030d21f2e8c3f391544ee5a56406e7996
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: separate hardware disconnection from cleanup

This commit adds nand_disconnect() to mark a NAND chip as disconnected
under the chip and controller locks and makes nand_get_device(),
nand_suspend(), nand_resume(), and panic_nand_write() reject hardware
access once disconnected.

> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index a5b278ab9384f..8f7d2571e8d9a 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c

[ ... ]

> @@ -581,7 +607,9 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
>  		nand_erase_nand(chip, &einfo, 0);
>  
>  		/* Write bad block marker to OOB */
> -		nand_get_device(chip);
> +		ret = nand_get_device(chip);
> +		if (ret)
> +			return ret;
>  
>  		ret = nand_markbad_bbm(chip, ofs);
>  		nand_release_device(chip);

[Severity: High]
Can a concurrent nand_disconnect() cause write_bbt() to issue unlocked
hardware operations on the disconnected controller when updating an
on-flash BBT?

nand_block_markbad_lowlevel() releases the chip and controller locks
before calling nand_markbad_bbt() -> nand_update_bbt() -> write_bbt().
If nand_disconnect() runs before write_bbt() calls nand_erase_nand() or
scan_write_bbt(), nand_get_device() returns -ENODEV, which write_bbt()
treats as a bad BBT eraseblock:

drivers/mtd/nand/raw/nand_bbt.c:write_bbt() {
	...
		res = nand_erase_nand(this, &einfo, 1);
		if (res < 0) {
			pr_warn("nand_bbt: error while erasing BBT block %d\n",
				res);
			mark_bbt_block_bad(this, td, chip, block);
			continue;
		}
	...
}

mark_bbt_block_bad() then marks each reserved BBT block worn in memory
and calls nand_markbad_bbm() -> nand_default_block_markbad() ->
nand_do_write_oob() without calling nand_get_device(), accessing
controller resources that have already been freed after
nand_disconnect() returned.

[ ... ]

> @@ -4688,10 +4728,14 @@ static int nand_suspend(struct mtd_info *mtd)
>  	int ret = 0;
>  
>  	mutex_lock(&chip->lock);
> +	if (chip->disconnected) {
> +		mutex_unlock(&chip->lock);
> +		return -ENODEV;
> +	}
>  	if (chip->ops.suspend)
>  		ret = chip->ops.suspend(chip);
>  	if (!ret)
> -		chip->suspended = 1;
> +		WRITE_ONCE(chip->suspended, true);
>  	mutex_unlock(&chip->lock);
>  
>  	return ret;

[Severity: Medium]
Does returning -ENODEV from nand_suspend() abort system suspend when an
open MTD handle outlives controller unbind?

While an MTD handle remains open after del_mtd_device() and
nand_disconnect(), mtd->dev stays registered on dpm_list until the last
reference is put in mtd_device_release().  During system suspend,
mtd_cls_suspend() calls mtd_suspend() -> nand_suspend(), and returning
-ENODEV fails dpm_suspend() instead of treating the disconnected chip as
a no-op like nand_resume() and nand_sync() do.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=4

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal
  2026-11-07 10:16 ` [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal James Hilliard
@ 2026-11-07 20:44   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:44 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [High] mtd: rawnand: au1550nd: inverted request_mem_region check in
  au1550nd_probe() [1]
--

commit be3bdca7aa37bf9e70e43be29b0b99a9af4bfa13
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: au1550nd: retain chip state across removal

This commit switches the au1550nd driver to reference-counted NAND
controller and chip lifetime management, disconnecting the chip before
unmapping MMIO and checking mtd_device_register() errors.

> diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c
> index f1e99197ab845..bd78bcc842822 100644
> --- a/drivers/mtd/nand/raw/au1550nd.c
> +++ b/drivers/mtd/nand/raw/au1550nd.c

[ ... ]

> @@ -333,7 +350,6 @@ static int au1550nd_probe(struct platform_device *pdev)
>  out2:
>  	release_mem_region(r->start, resource_size(r));
>  out1:
> -	kfree(ctx);
>  	return ret;
>  }

[Severity: High]
This problem wasn't introduced by this patch, but isn't the
request_mem_region() check in au1550nd_probe() inverted [1]?

drivers/mtd/nand/raw/au1550nd.c:au1550nd_probe() {
    ...
	if (request_mem_region(r->start, resource_size(r), "au1550-nand")) {
		dev_err(&pdev->dev, "cannot claim NAND memory area\n");
		ret = -ENOMEM;
		goto out1;
	}
    ...
}

request_mem_region() returns a non-NULL pointer on success, so a
successful reservation jumps to out1, fails probe with -ENOMEM, and leaks
the claimed memory region. Conversely, a failed reservation proceeds to
ioremap() unowned memory.

[1] https://sashiko.dev/#/bug/linux-4e401697-4998-4e40-a7e4-60cc21983fb4

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=42

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 39/58] mtd: rawnand: qcom: retain open-handle state on removal
  2026-11-07 10:16 ` [PATCH 39/58] mtd: rawnand: qcom: " James Hilliard
@ 2026-11-07 20:47   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:47 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] mtd: rawnand: qcom: broken error handling in
  qcom_probe_nand_devices()
--

commit 0a9084cf5fec792ff4833fe6c2d5c8f9780c60a1
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: qcom: retain open-handle state on removal

This commit ties the lifetime of qcom_nand_host and qcom_nand_controller to
the MTD and NAND controller release callbacks, validates boot partitions
before registering MTD devices, and adds cleanup of registered chips on
probe failure and removal.

> diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
> index 2d98f79b492d6..f812de1d776fc 100644
> --- a/drivers/mtd/nand/raw/qcom_nandc.c
> +++ b/drivers/mtd/nand/raw/qcom_nandc.c

[ ... ]

> @@ -2077,15 +2091,13 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
>  	int ret = -ENODEV;
>  
>  	for_each_available_child_of_node_scoped(dn, child) {
> -		host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
> +		host = kzalloc_obj(*host);
>  		if (!host)
>  			return -ENOMEM;
>  
>  		ret = qcom_nand_host_init_and_register(nandc, host, child);
> -		if (ret) {
> -			devm_kfree(dev, host);
> +		if (ret)
>  			continue;
> -		}
>  
>  		list_add_tail(&host->node, &nandc->host_list);
>  	}

[Severity: Medium]
Should qcom_probe_nand_devices() return ret instead of continuing when
qcom_nand_host_init_and_register() fails?

Continuing the loop means that if an earlier child node fails (for example
with -EPROBE_DEFER or invalid boot partitions) and a later child succeeds,
ret is overwritten with 0 and the partial probe is not unwound via
err_setup.

Worse, if the earlier child fails in qcom_nand_attach_chip() when
qcom_alloc_bam_transaction() returns NULL after qcom_free_bam_transaction()
freed nandc->bam_txn:

drivers/mtd/nand/raw/qcom_nandc.c:qcom_nand_attach_chip() {
    ...
	if (nandc->props->supports_bam)
		qcom_free_bam_transaction(nandc);

	nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
				     cwperpage);

	/* Now allocate the BAM transaction based on updated max_cwperpage */
	if (nandc->props->supports_bam) {
		nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
		if (!nandc->bam_txn) {
			dev_err(nandc->dev,
				"failed to allocate bam transaction\n");
			return -ENOMEM;
		}
	}
    ...
}

continuing to the next child node calls nand_scan(), which runs
qcom_clear_bam_transaction() and dereferences nandc->bam_txn while it is
NULL.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=39

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 51/58] mtd: rawnand: mpc5121: retain chip state across removal
  2026-11-07 10:16 ` [PATCH 51/58] mtd: rawnand: mpc5121: " James Hilliard
@ 2026-11-07 20:48   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:48 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] mtd: rawnand: mpc5121: missing Fixes trailer for probe leak and
  removal UAF
--

commit 648cd21cc2eac3c4001033a11e9ac4de646c25b6
Author: James Hilliard <james.hilliard1@gmail.com>

> mtd: rawnand: mpc5121: retain chip state across removal
>
> Drain NAND access before unmapping the external chip-select registers.
> Keep the embedded chip and controller alive until the final MTD
> reference, and unwind scanned NAND state on unsupported geometry and
> publication failure.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>

[Severity: Low]
This fixes a probe-time leak of nand_scan() allocations and a
use-after-free on device removal while MTD handles remain open, both
present since the driver was added.  Should it include a Fixes: tag?

  Fixes: bb315f749f8c ("mtd: nand: Add MPC5121 NAND Flash Controller driver")

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=51

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects
  2026-11-07 10:16 ` [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects James Hilliard
@ 2026-11-07 20:50   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:50 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] mtd: refcount imbalance in __put_mtd_device() when
  master->dev.type changes
- [Medium] mtd: mtd_add_partition() allows adding partitions to an
  unregistered master
- [Medium] mtd: add_mtd_device() leaves mtd->dev.type set when
  dev_set_name() fails
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [Critical] mtd: core: use-after-free in add_mtd_device() [1]
--

commit da12394511d438335fb25ce60156c3307163ed8f
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: add opt-in lifetime ownership for backing objects

This commit introduces opt-in reference-counted lifetime management for
master MTD backing objects via mtd_set_release() and mtd_put_master(),
retaining the master and partition hierarchy while MTD handles, device
nodes, or OTP NVMEM providers remain active.

> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
> index 558a605ba7495..42c06a02cd99d 100644
> --- a/drivers/mtd/mtdcore.c
> +++ b/drivers/mtd/mtdcore.c
[ ... ]
> @@ -748,16 +840,28 @@ int add_mtd_device(struct mtd_info *mtd)
[ ... ]
>  	mtd->index = i;
>  	kref_init(&mtd->refcnt);
> +	if (master->master.release) {
> +		mtd_get_lifetime(master);
> +		mtd->ref_master = master;
> +		if (mtd->parent && mtd->parent != master)
> +			get_device(&mtd->parent->dev);
> +	}

[Severity: Medium]
Can a partition still be added through mtd_add_partition() after the
parent or master has been unregistered?

If a process keeps an open file descriptor to /dev/mtd0 across driver
unbind (mtd_device_unregister() and mtd_put_master()) and issues
BLKPG_ADD_PARTITION, mtdchar_blkpg_ioctl() -> mtd_add_partition() calls
add_mtd_device(child) without checking whether parent->registered is still
true.  Because del_mtd_partitions() has already run, the new partition is
never cleaned up, leaking the partition, its MTD ID, THIS_MODULE, and
master.life_refcnt.  Furthermore, once the open handle on the managed
master is closed, mtd_device_release() leaves master->dev.type set, so
opening and closing the leaked child partition will do kref_get() on a
zero master->refcnt and call mtd_device_release() a second time.

> @@ -839,29 +946,38 @@ int add_mtd_device(struct mtd_info *mtd)
>  		}
>  	}
>  
> -	/* We _know_ we aren't being removed, because
> -	   our caller is still holding us here. So none
> -	   of this try_ nonsense, and no bitching about it
> -	   either. :) */
> -	__module_get(THIS_MODULE);
>  	return 0;
>  
>  fail_nvmem_add:
>  	device_del(&mtd->dev);
>  fail_added:
>  	/*
> -	 * Clear type and set nop release to prevent mtd_release() ->
> +	 * Clear type and override release to prevent mtd_release() ->
>  	 * release_mtd_partition() -> free_partition() from freeing mtd.
>  	 * The caller handles cleanup on failure.
>  	 */
>  	mtd->dev.type = NULL;
> -	mtd->dev.release = mtd_dev_release_nop;
> +	mtd->dev.release = mtd_dev_release_done;
> +	dev_set_drvdata(&mtd->dev, &released);
> +	wait_for_release = true;
>  	put_device(&mtd->dev);
>  	of_node_put(mtd_get_of_node(mtd));
>  fail_devname:
>  	idr_remove(&mtd_idr, i);
> +	ida_free(&mtd_ids, i);
> +	if (mtd->ref_master) {
> +		mtd->ref_master = NULL;
> +		if (mtd->parent && mtd->parent != master)
> +			put_device(&mtd->parent->dev);
> +		mtd_put_lifetime(master);
> +	}

[Severity: Critical]
This problem wasn't introduced by this patch, but can add_mtd_device()
trigger a use-after-free on the device_node [1] when
mtd_check_of_node() matches a partition node?

drivers/mtd/mtdcore.c:mtd_check_of_node() {
    ...
		if (plen == mtd_name_len &&
		    !strncmp(mtd->name, pname + offset, plen)) {
			mtd_set_of_node(mtd, mtd_dn);
			of_node_put(mtd_dn);
			break;
		}
    ...
}

mtd_check_of_node() stores mtd_dn in mtd and immediately drops its
reference via of_node_put(mtd_dn) before add_mtd_device() calls
of_node_get(mtd_get_of_node(mtd)).  In a dynamic DT environment, if the
node is removed in that window, of_node_put() frees mtd_dn and
of_node_get() dereferences freed memory.

[Severity: Medium]
Does jumping to fail_devname when dev_set_name() fails leave mtd->dev.type
set to &mtd_devtype?

mtd->dev.type is assigned right before dev_set_name(), and clearing it only
happens under fail_added.  Leaving mtd->dev.type non-NULL causes any retry
of add_mtd_device() to fail at WARN_ONCE(mtd->dev.type, ...), and causes
__put_mtd_device() on an open partition to call kref_put(&master->refcnt,
mtd_device_release) on a master whose struct device was never registered.

[ ... ]

> @@ -1348,13 +1484,14 @@ int __get_mtd_device(struct mtd_info *mtd)
>  		return -ENODEV;
>  	}
>  
> +	mtd_get_lifetime(master);
>  	while (mtd) {
>  		if (mtd != master)
>  			kref_get(&mtd->refcnt);
>  		mtd = mtd->parent;
>  	}
>  
> -	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
> +	if (master->dev.type)
>  		kref_get(&master->refcnt);
>  
>  	return 0;
> @@ -1441,6 +1578,7 @@ void __put_mtd_device(struct mtd_info *mtd)
>  	struct mtd_info *master = mtd_get_master(mtd);
>  	/* master can be freed in _put_device */
>  	struct module *owner = master->owner;
> +	bool managed = master->master.release;
>  
>  	while (mtd) {
>  		/* kref_put() can relese mtd, so keep a reference mtd->parent */
> @@ -1451,12 +1589,14 @@ void __put_mtd_device(struct mtd_info *mtd)
>  		mtd = parent;
>  	}
>  
> -	if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
> +	if (master->dev.type)
>  		kref_put(&master->refcnt, mtd_device_release);

[Severity: Medium]
Can master->dev.type change from NULL to non-NULL between
__get_mtd_device() and __put_mtd_device(), causing an unmatched
kref_put(&master->refcnt, mtd_device_release)?

When CONFIG_MTD_PARTITIONED_MASTER=n, master->dev.type is NULL while
mtd_device_parse_register() runs parse_mtd_partitions().  If a first
partition is registered and opened by a notifier or userspace (seeing
master->dev.type == NULL in __get_mtd_device()), and a later partition
fails to register in add_mtd_partitions(), del_mtd_partitions() unregisters
the first partition while it remains open:

drivers/mtd/mtdcore.c:mtd_device_parse_register() {
    ...
	ret = parse_mtd_partitions(mtd, types, parser_data);
	if (ret == -EPROBE_DEFER)
		goto out;

	if (ret > 0)
		ret = 0;
	else if (nr_parts)
		ret = add_mtd_partitions(mtd, parts, nr_parts);
	else if (!device_is_registered(&mtd->dev))
		ret = add_mtd_device(mtd);
    ...
}

When nr_parts == 0 and parse_mtd_partitions() returns a non-defer error,
mtd_device_parse_register() falls back to add_mtd_device(mtd), which sets
master->dev.type = &mtd_devtype and initializes master->refcnt to 1.
Closing the open partition handle then sees master->dev.type != NULL in
__put_mtd_device() and drops master->refcnt to 0 while the master is still
registered.

[1] https://sashiko.dev/#/bug/linux-11fdb383-132f-473b-9284-101b4753fcd6

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=3

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 56/58] mtd: rawnand: nandsim: retain chip state across removal
  2026-11-07 10:16 ` [PATCH 56/58] mtd: rawnand: nandsim: " James Hilliard
@ 2026-11-07 20:52   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:52 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [High] mtd: rawnand: nandsim: shift-out-of-bounds in ns_init_module()
  [1]
--

commit c6f7998578d1a1c3b52ebeff5e9664c6e727de3e
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: nandsim: retain chip state across removal

This commit disconnects the NAND chip before freeing simulated media and
retains the chip metadata until the final MTD reference is dropped.

> diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c
> index fe968037f75a2..39fefd83cd850 100644
> --- a/drivers/mtd/nand/raw/nandsim.c
> +++ b/drivers/mtd/nand/raw/nandsim.c
[ ... ]
> @@ -2376,6 +2382,10 @@ static int __init ns_init_module(void)
>  	ret = ns_parse_badblocks(ns, nsmtd);
>  	if (ret)
>  		goto free_ns_object;
> +	ret = nand_set_release(chip, ns_chip_release);
> +	if (ret)
> +		goto free_ns_object;
> +	retained = true;

[Severity: High]
This problem wasn't introduced by this patch, but can setting the
overridesize module parameter to 31 or higher trigger a
shift-out-of-bounds [1] earlier in ns_init_module()?

drivers/mtd/nand/raw/nandsim.c:ns_init_module() {
	...
	if (overridesize) {
		uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize;
		...
		if (new_size >> overridesize != nsmtd->erasesize) {
			NS_ERR("overridesize is too big\n");
			ret = -EINVAL;
			goto cleanup_nand;
		}

		/* N.B. This relies on nand_scan not doing anything with the size before we change it */
		nsmtd->size = new_size;
		memorg->eraseblocks_per_lun = 1 << overridesize;
	...
}

Because new_size is 64-bit, the bounds check passes for overridesize
values up to 63, while "1 << overridesize" shifts a 32-bit signed int.
An overridesize of 31 or more overflows or invokes undefined behavior,
leaving eraseblocks_per_lun zero or bogus and leading to a divide-by-zero
during MTD I/O operations.

[1] https://sashiko.dev/#/bug/linux-85e29763-d721-4e3b-9253-7b65f9dada3a

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=56

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 55/58] mtd: rawnand: r852: retain chip state across removal
  2026-11-07 10:16 ` [PATCH 55/58] mtd: rawnand: r852: " James Hilliard
@ 2026-11-07 20:59   ` sashiko-bot
  0 siblings, 0 replies; 73+ messages in thread
From: sashiko-bot @ 2026-11-07 20:59 UTC (permalink / raw)
  To: James Hilliard, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Brian Norris, Boris Brezillon,
	Arseniy Krasnov, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Thomas Gleixner, Vitaly Wool, David Woodhouse, Russell King,
	David Woodhouse, Mike Rapoport, Maxime Coquelin,
	Alexandre Torgue, Christophe Kerello, Jason Roberts,
	Michal Simek, Boris Brezillon, Mason Yang,
	Ramuthevar Vadivel Murugan, Stefan Agner, Lucas Stach,
	Thierry Reding, Jonathan Hunter, Dmitry Osipenko,
	Matthias Brugger, AngeloGioacchino Del Regno,
	Jorge Ramirez-Ortiz, Heiko Stuebner, Yifeng Zhao, Nicolas Ferre,
	Alexandre Belloni, Claudiu Beznea, Keguang Zhang, Jacky Huang,
	Shan-Chun Hung, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Han Xu, Liang Yang, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, Martin Blumenstingl, Manivannan Sadhasivam,
	Kamal Dasu, Broadcom internal kernel review list,
	Vladimir Zapolskiy, Piotr Wojtaszczyk, Paul Cercueil,
	Harvey Hunt, Maxim Levitsky, Michael Walle, Srinivas Kandagatla
  Cc: Tze Yee Ng, linux-mtd, linux-sunxi, imx, linux-amlogic, Frank.Li,
	wsa+renesas

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] mtd: rawnand: r852: r852_irq() mishandles card IRQs when
  removing is set
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [High] mtd: rawnand: r852: work re-queue race condition in
  r852_shutdown() [1]
--

commit 85c05cbe4afe297d6f7e2a571f936dd2dc915c61
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: rawnand: r852: retain chip state across removal

This commit allocates a separate nand_chip per card insertion, ties chip and
controller lifetimes to MTD release callbacks, and stops card-detection work
scheduling in r852_irq() during PCI removal.

> diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
> index 92c47d351c278..0fb320cd7b771 100644
> --- a/drivers/mtd/nand/raw/r852.c
> +++ b/drivers/mtd/nand/raw/r852.c
[ ... ]
> @@ -734,7 +773,8 @@ static irqreturn_t r852_irq(int irq, void *data)
>  	card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
>  	r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
>  
> -	if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) {
> +	if (!dev->removing &&
> +	    (card_status & (R852_CARD_IRQ_INSERT | R852_CARD_IRQ_REMOVE))) {
>  
>  		ret = IRQ_HANDLED;
>  		dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);

[Severity: Medium]
Does checking !dev->removing on the outer condition cause r852_irq() to
mishandle card interrupts that fire during r852_remove()?

Hardware interrupts remain enabled while r852_remove() cancels the workqueue
and runs r852_unregister_nand_device().  If a card insert or remove interrupt
arrives after dev->removing is set, r852_irq() clears R852_CARD_IRQ_STA in
hardware but skips the handler block, returning IRQ_NONE on a shared IRQ line
and failing to set dev->card_unstable or call r852_disable_irqs() to abort
in-flight I/O.  Should dev->removing only guard queue_delayed_work()?

[ ... ]
> @@ -976,6 +1003,11 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
>  static void r852_remove(struct pci_dev *pci_dev)
>  {
>  	struct r852_device *dev = pci_get_drvdata(pci_dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->irqlock, flags);
> +	dev->removing = true;
> +	spin_unlock_irqrestore(&dev->irqlock, flags);
>  
>  	/* Stop detect workqueue -
>  		we are going to unregister the device anyway*/

[Severity: High]
This problem wasn't introduced by this patch, but can r852_irq() re-queue
dev->card_detect_work during r852_shutdown() and r852_suspend() [1]?

Both functions call cancel_delayed_work_sync() before r852_disable_irqs()
without setting dev->removing:

drivers/mtd/nand/raw/r852.c:r852_shutdown() {
	struct r852_device *dev = pci_get_drvdata(pci_dev);

	cancel_delayed_work_sync(&dev->card_detect_work);
	r852_disable_irqs(dev);
	synchronize_irq(dev->irq);
	pci_disable_device(pci_dev);
}

An interrupt arriving between cancel_delayed_work_sync() and
r852_disable_irqs() can re-queue card_detect_work:

CPU0                              CPU1
r852_shutdown()
  cancel_delayed_work_sync()
                                  r852_irq()
                                    queue_delayed_work()
  r852_disable_irqs()
  synchronize_irq()
  pci_disable_device()
                                  r852_card_detect_work()
                                    r852_read_reg()  <-- MMIO after disable

When the delayed work runs after pci_disable_device(), it accesses disabled
PCI MMIO space.

[1] https://sashiko.dev/#/bug/linux-ce28ca2b-1bf1-4d27-8bcd-a0ec14b99281

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107-submit-rawnand-retained-handle-lifetime-v1-v1-0-0181ef0cc4c8@gmail.com?part=55

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

^ permalink raw reply	[flat|nested] 73+ messages in thread

end of thread, other threads:[~2026-11-07 20:59 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-07 10:15 [PATCH 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
2026-11-07 10:15 ` [PATCH 01/58] mtd: clear OTP provider pointers after unregistering them James Hilliard
2026-11-07 20:21   ` sashiko-bot
2026-11-07 10:15 ` [PATCH 02/58] nvmem: retain provider context through final device release James Hilliard
2026-11-07 20:26   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 03/58] mtd: add opt-in lifetime ownership for backing objects James Hilliard
2026-11-07 20:50   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 04/58] mtd: rawnand: separate hardware disconnection from cleanup James Hilliard
2026-11-07 20:42   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 05/58] mtd: rawnand: share retained chip and controller ownership James Hilliard
2026-11-07 10:16 ` [PATCH 06/58] mtd: rawnand: macronix: serialize OTP access with NAND operations James Hilliard
2026-11-07 10:16 ` [PATCH 07/58] mtd: rawnand: sunxi: retain chip state until the final MTD put James Hilliard
2026-11-07 10:16 ` [PATCH 08/58] mtd: rawnand: plat_nand: retain open-handle state on removal James Hilliard
2026-11-07 10:16 ` [PATCH 09/58] mtd: rawnand: gpio: " James Hilliard
2026-11-07 10:16 ` [PATCH 10/58] mtd: rawnand: stm32_fmc2: " James Hilliard
2026-11-07 10:16 ` [PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal James Hilliard
2026-11-07 20:25   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 12/58] mtd: rawnand: arasan: " James Hilliard
2026-11-07 10:16 ` [PATCH 13/58] mtd: rawnand: mxic: retain open-handle state on removal James Hilliard
2026-11-07 10:16 ` [PATCH 14/58] mtd: rawnand: intel: " James Hilliard
2026-11-07 20:31   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 15/58] mtd: rawnand: tegra: " James Hilliard
2026-11-07 20:32   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 16/58] mtd: rawnand: mtk: retain chips across controller removal James Hilliard
2026-11-07 10:16 ` [PATCH 17/58] mtd: rawnand: rockchip: " James Hilliard
2026-11-07 10:16 ` [PATCH 18/58] mtd: rawnand: atmel: " James Hilliard
2026-11-07 20:31   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 19/58] mtd: rawnand: orion: retain open-handle state on removal James Hilliard
2026-11-07 20:29   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 20/58] mtd: rawnand: socrates: " James Hilliard
2026-11-07 10:16 ` [PATCH 21/58] mtd: rawnand: fsl_upm: " James Hilliard
2026-11-07 10:16 ` [PATCH 22/58] mtd: rawnand: pasemi: " James Hilliard
2026-11-07 10:16 ` [PATCH 23/58] mtd: rawnand: ams-delta: " James Hilliard
2026-11-07 10:16 ` [PATCH 24/58] mtd: rawnand: xway: " James Hilliard
2026-11-07 10:16 ` [PATCH 27/58] mtd: rawnand: technologic: " James Hilliard
2026-11-07 10:16 ` [PATCH 28/58] mtd: rawnand: loongson: " James Hilliard
2026-11-07 10:16 ` [PATCH 29/58] mtd: rawnand: ma35d1: " James Hilliard
2026-11-07 10:16 ` [PATCH 30/58] mtd: rawnand: pl35x: " James Hilliard
2026-11-07 10:16 ` [PATCH 31/58] mtd: rawnand: mxc: " James Hilliard
2026-11-07 10:16 ` [PATCH 32/58] mtd: rawnand: fsmc: " James Hilliard
2026-11-07 10:16 ` [PATCH 33/58] mtd: rawnand: gpmi: " James Hilliard
2026-11-07 10:16 ` [PATCH 34/58] mtd: rawnand: omap2: " James Hilliard
2026-11-07 10:16 ` [PATCH 35/58] mtd: rawnand: marvell: " James Hilliard
2026-11-07 10:16 ` [PATCH 36/58] mtd: rawnand: renesas: " James Hilliard
2026-11-07 10:16 ` [PATCH 37/58] mtd: rawnand: meson: " James Hilliard
2026-11-07 10:16 ` [PATCH 38/58] mtd: rawnand: cadence: " James Hilliard
2026-11-07 10:16 ` [PATCH 39/58] mtd: rawnand: qcom: " James Hilliard
2026-11-07 20:47   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 40/58] mtd: rawnand: cafe: " James Hilliard
2026-11-07 10:16 ` [PATCH 41/58] mtd: rawnand: brcmnand: " James Hilliard
2026-11-07 10:16 ` [PATCH 42/58] mtd: rawnand: au1550nd: retain chip state across removal James Hilliard
2026-11-07 20:44   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 43/58] mtd: rawnand: sharpsl: " James Hilliard
2026-11-07 10:16 ` [PATCH 44/58] mtd: rawnand: davinci: " James Hilliard
2026-11-07 10:16 ` [PATCH 45/58] mtd: rawnand: sh_flctl: " James Hilliard
2026-11-07 10:16 ` [PATCH 46/58] mtd: rawnand: lpc32xx_slc: " James Hilliard
2026-11-07 10:16 ` [PATCH 47/58] mtd: rawnand: lpc32xx_mlc: " James Hilliard
2026-11-07 10:16 ` [PATCH 48/58] mtd: rawnand: ndfc: " James Hilliard
2026-11-07 10:16 ` [PATCH 49/58] mtd: rawnand: txx9ndfmc: " James Hilliard
2026-11-07 10:16 ` [PATCH 50/58] mtd: rawnand: ingenic: " James Hilliard
2026-11-07 10:16 ` [PATCH 51/58] mtd: rawnand: mpc5121: " James Hilliard
2026-11-07 20:48   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 52/58] mtd: rawnand: bcm47xxnflash: " James Hilliard
2026-11-07 10:16 ` [PATCH 53/58] mtd: rawnand: fsl_elbc: " James Hilliard
2026-11-07 10:16 ` [PATCH 54/58] mtd: rawnand: fsl_ifc: " James Hilliard
2026-11-07 10:16 ` [PATCH 55/58] mtd: rawnand: r852: " James Hilliard
2026-11-07 20:59   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 56/58] mtd: rawnand: nandsim: " James Hilliard
2026-11-07 20:52   ` sashiko-bot
2026-11-07 10:16 ` [PATCH 57/58] mtd: rawnand: cs553x: " James Hilliard
2026-11-07 10:16 ` [PATCH 58/58] mtd: rawnand: diskonchip: " James Hilliard
2026-11-07 19:33 ` [PATCH 25/58] mtd: rawnand: hisi504: retain open-handle state on removal James Hilliard
2026-11-07 19:33 ` [PATCH 26/58] mtd: rawnand: vf610: " James Hilliard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®