[PATCH V12 2/2] mtd: spi-nor: Add driver for Cadence Quad SPI Flash Controller

Brian Norris computersforpeace at gmail.com
Mon Jul 18 09:58:00 PDT 2016


Hi,

On Mon, Jul 18, 2016 at 11:35:59AM +0200, Marek Vasut wrote:
> On 07/18/2016 02:52 AM, Brian Norris wrote:
> >Some trivial comments. I have some proposed diffs, and if y'all are OK
> >with them, I might apply this with some fixups.
> 
> [...]
> 
> >>+static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear)
> >>+{
> >>+	unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
> >>+	u32 val;
> >>+
> >>+	while (1) {
> >>+		val = readl(reg);
> >>+		if (clear)
> >>+			val = ~val;
> >>+		val &= mask;
> >>+
> >>+		if (val == mask)
> >>+			return 0;
> >>+
> >>+		if (time_after(jiffies, end))
> >>+			return -ETIMEDOUT;
> >>+	}
> >
> >This could all be replaced with readl_poll_timeout(), couldn't it?
> 
> Oh nice, I didn't know we had generic implementation. Yes, let's use it.
> Minor nit to it inline.

Yeah, I think the generic implementation is helpful for getting some of
the finer details correct and for reducing boilerplate.

> >Like
> >the following diff (I won't apply this one now; if anything, maybe I'll
> >send a follow-up patch for review):
> >
> >diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
> >index d403ba7b8f43..87586baaae9e 100644
> >--- a/drivers/mtd/spi-nor/cadence-quadspi.c
> >+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
> >@@ -22,6 +22,7 @@
> > #include <linux/errno.h>
> > #include <linux/interrupt.h>
> > #include <linux/io.h>
> >+#include <linux/iopoll.h>
> > #include <linux/jiffies.h>
> > #include <linux/kernel.h>
> > #include <linux/module.h>
> >@@ -226,21 +227,11 @@ struct cqspi_st {
> >
> > static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear)
> > {
> >-	unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
> > 	u32 val;
> >
> >-	while (1) {
> >-		val = readl(reg);
> >-		if (clear)
> >-			val = ~val;
> >-		val &= mask;
> >-
> >-		if (val == mask)
> >-			return 0;
> >-
> >-		if (time_after(jiffies, end))
> >-			return -ETIMEDOUT;
> >-	}
> >+	return readl_poll_timeout(reg, val,
> >+				  (val & mask) == (clear ? ~mask : mask),
> 
> You probably mean "((clear ? ~val : val) & mask) == mask" here.

Yes, that's better.

I probably don't want to do that until someone gets to test it anyway.
And obviously, it's easy enough to get wrong. If anything, I'll send a
follow up patch after merging, since this certainly isn't critical.

> >+				  0, CQSPI_TIMEOUT_MS * 1000);
> > }
> 
> [...]
> 
> >>+static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
> >>+{
> >>+	struct platform_device *pdev = cqspi->pdev;
> >>+	struct device *dev = &pdev->dev;
> >>+	struct cqspi_flash_pdata *f_pdata;
> >>+	struct spi_nor *nor;
> >>+	struct mtd_info *mtd;
> >>+	unsigned int cs;
> >>+	int i, ret;
> >>+
> >>+	/* Get flash device data */
> >>+	for_each_available_child_of_node(dev->of_node, np) {
> >>+		if (of_property_read_u32(np, "reg", &cs)) {
> >>+			dev_err(dev, "Couldn't determine chip select.\n");
> >>+			goto err;
> >>+		}
> >>+
> >>+		if (cs > CQSPI_MAX_CHIPSELECT) {
> >>+			dev_err(dev, "Chip select %d out of range.\n", cs);
> >>+			goto err;
> >>+		}
> >>+
> >>+		f_pdata = &cqspi->f_pdata[cs];
> >>+		f_pdata->cqspi = cqspi;
> >>+		f_pdata->cs = cs;
> >>+
> >>+		ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np);
> >>+		if (ret)
> >>+			goto err;
> >>+
> >>+		nor = &f_pdata->nor;
> >>+		mtd = &nor->mtd;
> >>+
> >>+		mtd->priv = nor;
> >>+
> >>+		nor->dev = dev;
> >>+		spi_nor_set_flash_node(nor, np);
> >>+		nor->priv = f_pdata;
> >>+
> >>+		nor->read_reg = cqspi_read_reg;
> >>+		nor->write_reg = cqspi_write_reg;
> >>+		nor->read = cqspi_read;
> >>+		nor->write = cqspi_write;
> >>+		nor->erase = cqspi_erase;
> >>+		nor->prepare = cqspi_prep;
> >>+		nor->unprepare = cqspi_unprep;
> >>+
> >>+		mtd->name = kasprintf(GFP_KERNEL, "%s.%d", dev_name(dev), cs);
> >
> >Might be easier to just use devm_kasprintf()?
> 
> Yes, I think that'd work.
> 
> >>+		if (!mtd->name) {
> >>+			ret = -ENOMEM;
> >>+			goto err;
> >>+		}
> >>+
> >>+		ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> >>+		if (ret)
> >>+			goto err;
> >>+
> >>+		ret = mtd_device_register(mtd, NULL, 0);
> >>+		if (ret)
> >>+			goto err;
> >>+	}
> >>+
> >>+	return 0;
> >>+
> >>+err:
> >>+	for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++)
> >>+		if (cqspi->f_pdata[i].nor.mtd.name) {
> >
> >Chekcing for 'name' doens't really handle all error paths right. If
> >spi_nor_scan() or mtd_device_register() fails, then you'll be trying to
> >unregister an unregistered MTD.
> 
> Oh, snap. OK.
> 
> >>+			mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd);
> >>+			kfree(cqspi->f_pdata[i].nor.mtd.name);
> >>+		}
> >>+	return ret;
> >>+}
> >>+
> >>+static int cqspi_probe(struct platform_device *pdev)
> >>+{
> >>+	struct device_node *np = pdev->dev.of_node;
> >>+	struct device *dev = &pdev->dev;
> >>+	struct cqspi_st *cqspi;
> >>+	struct resource *res;
> >>+	struct resource *res_ahb;
> >>+	int ret;
> >>+	int irq;
> >>+
> >>+	cqspi = devm_kzalloc(dev, sizeof(*cqspi), GFP_KERNEL);
> >>+	if (!cqspi)
> >>+		return -ENOMEM;
> >>+
> >>+	mutex_init(&cqspi->bus_mutex);
> >>+	cqspi->pdev = pdev;
> >>+	platform_set_drvdata(pdev, cqspi);
> >>+
> >>+	/* Obtain configuration from OF. */
> >>+	ret = cqspi_of_get_pdata(pdev);
> >>+	if (ret) {
> >>+		dev_err(dev, "Cannot get mandatory OF data.\n");
> >>+		return -ENODEV;
> >>+	}
> >>+
> >>+	/* Obtain QSPI clock. */
> >>+	cqspi->clk = devm_clk_get(dev, NULL);
> >>+	if (IS_ERR(cqspi->clk)) {
> >>+		dev_err(dev, "Cannot claim QSPI clock.\n");
> >>+		return PTR_ERR(cqspi->clk);
> >>+	}
> >>+
> >>+	/* Obtain and remap controller address. */
> >>+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>+	cqspi->iobase = devm_ioremap_resource(dev, res);
> >>+	if (IS_ERR(cqspi->iobase)) {
> >>+		dev_err(dev, "Cannot remap controller address.\n");
> >>+		return PTR_ERR(cqspi->iobase);
> >>+	}
> >>+
> >>+	/* Obtain and remap AHB address. */
> >>+	res_ahb = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> >>+	cqspi->ahb_base = devm_ioremap_resource(dev, res_ahb);
> >>+	if (IS_ERR(cqspi->ahb_base)) {
> >>+		dev_err(dev, "Cannot remap AHB address.\n");
> >>+		return PTR_ERR(cqspi->ahb_base);
> >>+	}
> >>+
> >>+	init_completion(&cqspi->transfer_complete);
> >>+
> >>+	/* Obtain IRQ line. */
> >>+	irq = platform_get_irq(pdev, 0);
> >>+	if (irq < 0) {
> >>+		dev_err(dev, "Cannot obtain IRQ.\n");
> >>+		return -ENXIO;
> >>+	}
> >>+
> >>+	ret = clk_prepare_enable(cqspi->clk);
> >>+	if (ret) {
> >>+		dev_err(dev, "Cannot enable QSPI clock.\n");
> >>+		return ret;
> >>+	}
> >>+
> >>+	cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk);
> >>+
> >>+	ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
> >>+			       pdev->name, cqspi);
> >>+	if (ret) {
> >>+		dev_err(dev, "Cannot request IRQ.\n");
> >>+		goto probe_irq_failed;
> >>+	}
> >>+
> >>+	cqspi_wait_idle(cqspi);
> >>+	cqspi_controller_init(cqspi);
> >>+	cqspi->current_cs = -1;
> >>+	cqspi->sclk = 0;
> >>+
> >>+	ret = cqspi_setup_flash(cqspi, np);
> >>+	if (ret) {
> >>+		dev_err(dev, "Cadence QSPI NOR probe failed %d\n", ret);
> >>+		goto probe_setup_failed;
> >>+	}
> >>+
> >>+	return ret;
> >>+probe_irq_failed:
> >>+	cqspi_controller_enable(cqspi, 0);
> >>+probe_setup_failed:
> >>+	clk_disable_unprepare(cqspi->clk);
> >>+	return ret;
> >>+}
> >>+
> >>+static int cqspi_remove(struct platform_device *pdev)
> >>+{
> >>+	struct cqspi_st *cqspi = platform_get_drvdata(pdev);
> >>+	int i;
> >>+
> >>+	cqspi_controller_enable(cqspi, 0);
> >
> >I think you want to disable the controller *after* unregistering the
> >MTDs, no?
> 
> That's a bit of a misnomer, the second parameter is "bool enable",
> so this disables the controller.

Correct, that's for disabling the controller. Did I say something wrong?
I just meant we want to move that after this next loop, since
technically, there could be new MTD transactions being initiated all the
way until we actually unregister the MTD.

> >>+
> >>+	for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++)
> >>+		if (cqspi->f_pdata[i].nor.mtd.name) {
> >>+			mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd);
> >>+			kfree(cqspi->f_pdata[i].nor.mtd.name);
> >>+		}
> >>+
> >>+	clk_disable_unprepare(cqspi->clk);
> >>+
> >>+	return 0;
> >>+}
> >>+
> >>+#ifdef CONFIG_PM_SLEEP
> >>+static int cqspi_suspend(struct device *dev)
> >>+{
> >>+	struct cqspi_st *cqspi = dev_get_drvdata(dev);
> >>+
> >>+	cqspi_controller_enable(cqspi, 0);
> >>+	return 0;
> >>+}
> >>+
> >>+static int cqspi_resume(struct device *dev)
> >>+{
> >>+	struct cqspi_st *cqspi = dev_get_drvdata(dev);
> >>+
> >>+	cqspi_controller_enable(cqspi, 1);
> >>+	return 0;
> >>+}
> >>+
> >>+static const struct dev_pm_ops cqspi__dev_pm_ops = {
> >>+	.suspend = cqspi_suspend,
> >>+	.resume = cqspi_resume,
> >>+};
> >>+
> >>+#define CQSPI_DEV_PM_OPS	(&cqspi__dev_pm_ops)
> >>+#else
> >>+#define CQSPI_DEV_PM_OPS	NULL
> >>+#endif
> >>+
> >>+static struct of_device_id const cqspi_dt_ids[] = {
> >>+	{.compatible = "cdns,qspi-nor",},
> >>+	{ /* end of table */ }
> >>+};
> >>+
> >>+MODULE_DEVICE_TABLE(of, cqspi_dt_ids);
> >>+
> >>+static struct platform_driver cqspi_platform_driver = {
> >>+	.probe = cqspi_probe,
> >>+	.remove = cqspi_remove,
> >>+	.driver = {
> >>+		.name = CQSPI_NAME,
> >>+		.pm = CQSPI_DEV_PM_OPS,
> >>+		.of_match_table = cqspi_dt_ids,
> >>+	},
> >>+};
> >>+
> >>+module_platform_driver(cqspi_platform_driver);
> >>+
> >>+MODULE_DESCRIPTION("Cadence QSPI Controller Driver");
> >>+MODULE_LICENSE("GPL v2");
> >>+MODULE_ALIAS("platform:" CQSPI_NAME);
> >>+MODULE_AUTHOR("Ley Foon Tan <lftan at altera.com>");
> >>+MODULE_AUTHOR("Graham Moore <grmoore at opensource.altera.com>");
> >
> >So, I'm thinking of applying this patch with the following diff:
> >
> >diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
> >index 2fff31c0b9c3..d403ba7b8f43 100644
> >--- a/drivers/mtd/spi-nor/cadence-quadspi.c
> >+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
> >@@ -53,6 +53,7 @@ struct cqspi_flash_pdata {
> > 	u8		addr_width;
> > 	u8		data_width;
> > 	u8		cs;
> >+	bool		registered;
> 
> This feels a bit odd, but I don't have a better solution now.

Yeah, there wasn't really a super clear solution IMO. A possibly slight
improvement could be to just keep a local array in cqspi_setup_flash()
to denote which devices have been registered.

Really, I'd like to improve the spi-nor framework so it handles some of
the boilerplate details like this for us; so rather than walking the
child DT node(s) ourselves and registering/unregistering devices,
spi-nor.c could do the accounting, and just call a driver call-back for
each flash (if needed). So that could kill off this variable and several
other bits of registration code in this driver and others.

[...]

Regards,
Brian



More information about the linux-mtd mailing list