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