mtd: rawnand: pasemi: Don't use static data to track per-device state

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
This commit is contained in:
Uwe Kleine-König 2023-01-02 13:40:51 +01:00 committed by Miquel Raynal
parent 34569d8695
commit 718004a597

View File

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