Commit 718004a5 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Miquel Raynal
Browse files

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: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20230102124051.1508424-1-u.kleine-koenig@pengutronix.de
parent 34569d86
Loading
Loading
Loading
Loading
+35 −28
Original line number Original line Diff line number Diff line
@@ -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;
}
}