mtd: rawnand: pasemi: Don't use static data to track per-device state
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Mon, 2 Jan 2023 12:40:51 +0000 (13:40 +0100)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Sat, 7 Jan 2023 14:19:40 +0000 (15:19 +0100)
Up to now the pasemi nand driver only supported a single device
instance. However the check for that was racy because two parallel calls
of pasemi_nand_probe() could pass the check

if (pasemi_nand_mtd)
return -ENODEV;

before any of them assigns a non-NULL value to it.

So rework the driver to make use of per-device driver data.

As an intended side effect the driver can bind more than one device and
also gets rid of the check

if (!pasemi_nand_mtd)
return 0;

in the remove callback that could only ever trigger after the above race
happened.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20230102124051.1508424-1-u.kleine-koenig@pengutronix.de
drivers/mtd/nand/raw/pasemi_nand.c

index c176036453ed9b2a6592b2d629a15d8b971de820..f7ef6ca06ca92020a754b62a322f6803fb1c76cb 100644 (file)
 #define CLE_PIN_CTL                    15
 #define ALE_PIN_CTL                    14
 
-static unsigned int lpcctl;
-static struct mtd_info *pasemi_nand_mtd;
-static struct nand_controller controller;
+struct pasemi_ddata {
+       struct nand_chip chip;
+       unsigned int lpcctl;
+       struct nand_controller controller;
+};
+
 static const char driver_name[] = "pasemi-nand";
 
 static void pasemi_read_buf(struct nand_chip *chip, u_char *buf, int len)
@@ -55,6 +58,8 @@ static void pasemi_write_buf(struct nand_chip *chip, const u_char *buf,
 static void pasemi_hwcontrol(struct nand_chip *chip, int cmd,
                             unsigned int ctrl)
 {
+       struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip);
+
        if (cmd == NAND_CMD_NONE)
                return;
 
@@ -65,12 +70,14 @@ static void pasemi_hwcontrol(struct nand_chip *chip, int cmd,
 
        /* Push out posted writes */
        eieio();
-       inl(lpcctl);
+       inl(ddata->lpcctl);
 }
 
 static int pasemi_device_ready(struct nand_chip *chip)
 {
-       return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR);
+       struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip);
+
+       return !!(inl(ddata->lpcctl) & LBICTRL_LPCCTL_NR);
 }
 
 static int pasemi_attach_chip(struct nand_chip *chip)
@@ -93,29 +100,31 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        struct device_node *np = dev->of_node;
        struct resource res;
        struct nand_chip *chip;
+       struct nand_controller *controller;
        int err = 0;
+       struct pasemi_ddata *ddata;
+       struct mtd_info *pasemi_nand_mtd;
 
        err = of_address_to_resource(np, 0, &res);
 
        if (err)
                return -EINVAL;
 
-       /* We only support one device at the moment */
-       if (pasemi_nand_mtd)
-               return -ENODEV;
-
        dev_dbg(dev, "pasemi_nand at %pR\n", &res);
 
        /* Allocate memory for MTD device structure and private data */
-       chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
-       if (!chip) {
+       ddata = kzalloc(sizeof(*ddata), GFP_KERNEL);
+       if (!ddata) {
                err = -ENOMEM;
                goto out;
        }
+       platform_set_drvdata(ofdev, ddata);
+       chip = &ddata->chip;
+       controller = &ddata->controller;
 
-       controller.ops = &pasemi_ops;
-       nand_controller_init(&controller);
-       chip->controller = &controller;
+       controller->ops = &pasemi_ops;
+       nand_controller_init(controller);
+       chip->controller = controller;
 
        pasemi_nand_mtd = nand_to_mtd(chip);
 
@@ -136,10 +145,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
                goto out_ior;
        }
 
-       lpcctl = pci_resource_start(pdev, 0);
+       ddata->lpcctl = pci_resource_start(pdev, 0);
        pci_dev_put(pdev);
 
-       if (!request_region(lpcctl, 4, driver_name)) {
+       if (!request_region(ddata->lpcctl, 4, driver_name)) {
                err = -EBUSY;
                goto out_ior;
        }
@@ -172,45 +181,43 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
        }
 
        dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res,
-                lpcctl);
+                ddata->lpcctl);
 
        return 0;
 
  out_cleanup_nand:
        nand_cleanup(chip);
  out_lpc:
-       release_region(lpcctl, 4);
+       release_region(ddata->lpcctl, 4);
  out_ior:
        iounmap(chip->legacy.IO_ADDR_R);
  out_mtd:
-       kfree(chip);
+       kfree(ddata);
  out:
        return err;
 }
 
 static int pasemi_nand_remove(struct platform_device *ofdev)
 {
-       struct nand_chip *chip;
+       struct pasemi_ddata *ddata = platform_get_drvdata(ofdev);
+       struct mtd_info *pasemi_nand_mtd;
        int ret;
+       struct nand_chip *chip;
 
-       if (!pasemi_nand_mtd)
-               return 0;
-
-       chip = mtd_to_nand(pasemi_nand_mtd);
+       chip = &ddata->chip;
+       pasemi_nand_mtd = nand_to_mtd(chip);
 
        /* Release resources, unregister device */
        ret = mtd_device_unregister(pasemi_nand_mtd);
        WARN_ON(ret);
        nand_cleanup(chip);
 
-       release_region(lpcctl, 4);
+       release_region(ddata->lpcctl, 4);
 
        iounmap(chip->legacy.IO_ADDR_R);
 
        /* Free the MTD device structure */
-       kfree(chip);
-
-       pasemi_nand_mtd = NULL;
+       kfree(ddata);
 
        return 0;
 }