[PATCH 11/58] mtd: rawnand: denali: retain chips across controller removal
James Hilliard
james.hilliard1 at gmail.com
Sat Nov 7 02:16:08 PST 2026
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 at 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
More information about the linux-mtd
mailing list