Patchwork [MTD] NAND: add ts7xxx driver

login
register
mail settings
Submitter Alexander Clouter
Date Feb. 8, 2009, 8:59 a.m.
Message ID <20090208085919.GD11872@woodchuck>
Download mbox | patch
Permalink /patch/22578/
State New, archived
Headers show

Comments

Alexander Clouter - Feb. 8, 2009, 8:59 a.m.
This patch adds support for the NAND found in Technologic Systems ARM
boards[1].  The platform specific parts (IO address and parititoning
schemes) have been moved into platform specific files whilst the driver
it's self can be used as a complete replacement for the ts7250 NAND
driver.

[1] http://www.embeddedarm.com/products/arm-sbc.php

Signed-off-by: Alexander Clouter <alex@digriz.org.uk>
---
 drivers/mtd/nand/Kconfig        |    8 +-
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/ts7xxx.c       |  334 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/nand-ts7xxx.h |   35 ++++
 4 files changed, 377 insertions(+), 1 deletions(-)
 create mode 100644 drivers/mtd/nand/ts7xxx.c
 create mode 100644 include/linux/mtd/nand-ts7xxx.h
Lennert Buytenhek - Feb. 8, 2009, 10:21 a.m.
On Sun, Feb 08, 2009 at 08:59:19AM +0000, Alexander Clouter wrote:

> This patch adds support for the NAND found in Technologic Systems ARM
> boards[1].  The platform specific parts (IO address and parititoning
> schemes) have been moved into platform specific files whilst the driver
> it's self can be used as a complete replacement for the ts7250 NAND
> driver.

Why don't you morph the ts7250 into the form you want instead of adding
a parallel driver for the same thing?
Alexander Clouter - Feb. 8, 2009, 10:39 a.m.
Hi,

* Lennert Buytenhek <buytenh@wantstofly.org> [2009-02-08 11:21:47+0100]:
> 
> On Sun, Feb 08, 2009 at 08:59:19AM +0000, Alexander Clouter wrote:
> >
> > This patch adds support for the NAND found in Technologic Systems ARM
> > boards[1].  The platform specific parts (IO address and parititoning
> > schemes) have been moved into platform specific files whilst the driver
> > it's self can be used as a complete replacement for the ts7250 NAND
> > driver.
> 
> Why don't you morph the ts7250 into the form you want instead of adding
> a parallel driver for the same thing?
>
...because when I sent this to you last year for some offlist comment 
action you did not grumble that it was a separate driver.  Then after a 
year of reposting twice to this list with no comment or feedback what so 
ever I guess I made the mistake of assuming it was not 'displeasing' to 
the community.

Sorry to sound grumpy, but after trying to post cpufreq, timeriomem, 
nand and arm patches over the past two years with a 90% resulting in the 
silent treatment it feels like a brick has hit me in the face with "hey 
instead just morph the current driver".

Bah.

</rant>

The driver I threw together makes a rather radical change to put all the 
partitioning and iomem resources ranges back into the platform specific 
code where they belong.  It was just easier to start clean, plus I 
learnt a good deal about the MTD subsystem in the process, although 
that's not a good reason for submission I know.

As the feedback from ts72[056]0 users has been pretty...well...dire and 
I have no access to the platform I cannot be certain this patch safely 
works for those users.  I had the view that once I can get a solid 
confirmation that the ts72[056]0 users are happy I was hoping that the 
7250.c driver would just become obsolete?

So, is it back to square one for me?

Cheers
hartleys - Feb. 9, 2009, 4:56 p.m.
Sunday, February 08, 2009 1:59 AM, Alexander Clouter wrote:
> This patch adds support for the NAND found in Technologic
> Systems ARM boards[1].  The platform specific parts (IO 
>address and parititoning schemes) have been moved into
> platform specific files whilst the driver it's self can
> be used as a complete replacement for the ts7250 NAND driver.
>
> [1] http://www.embeddedarm.com/products/arm-sbc.php
>
> Signed-off-by: Alexander Clouter <alex@digriz.org.uk>

With a little work the generic plat_nand driver could probably be used
instead of this new driver or the original ts7250 driver.

The only thing special about the original ts7250 driver appears to be
the fall-back partitioning scheme that uses the size of the device to
figure out what static mtd_partition table to use.

This driver uses a similar approach but passes a callback to the
platform driver to use instead.  It also adds some conditional ecc
support.

It might be more useful to create a generic patch for plat_nand that
adds this functionality and then obsolete the old ts7250 driver.

I have been using the plat_nand driver on a custom ep93xx based board
for quite a while with no problems.

Regards,
Hartley
Alexander Clouter - Feb. 9, 2009, 5:05 p.m.
Hi,

* hartleys <hartleys@visionengravers.com> [2009-02-09 11:56:25-0500]:
> 
> Sunday, February 08, 2009 1:59 AM, Alexander Clouter wrote:
> >
> > This patch adds support for the NAND found in Technologic
> > Systems ARM boards[1].  The platform specific parts (IO 
> > address and parititoning schemes) have been moved into
> > platform specific files whilst the driver it's self can
> > be used as a complete replacement for the ts7250 NAND driver.
> >
> > [1] http://www.embeddedarm.com/products/arm-sbc.php
> 
> With a little work the generic plat_nand driver could probably be used
> instead of this new driver or the original ts7250 driver.
>
I was always curious if that was the way to go, but as there were more 
than one platform (TS-7800 and TS-72X0) I thought it would spare the 
duplication of code in the kernel; as the code between the two platforms 
is really just in the HW ECC and soon-one-day-maybe DMA support in the 
TS-7800.
 
> The only thing special about the original ts7250 driver appears to be
> the fall-back partitioning scheme that uses the size of the device to
> figure out what static mtd_partition table to use.
> 
I would have not really put too much effort in that if I was going for a 
TS-7800 only (plat_nand based driver) as it would have not been worth 
it.  It was only useless in the case of the TS-72X0 as it comes with 
several different NAND sizes, unlike currently the TS-7800 which only 
has the one.

> This driver uses a similar approach but passes a callback to the
> platform driver to use instead.  It also adds some conditional ecc
> support.
> 
> It might be more useful to create a generic patch for plat_nand that
> adds this functionality and then obsolete the old ts7250 driver.
> 
Well, from a practical level it would make mainlining the code much 
easier as it would lurk in the TS-7800 platform code and no one would 
care other than me.  The downside is the duplication of code surely, 
well I guess the duplicate code is really already in the plat_nand 
driver anyway?

> I have been using the plat_nand driver on a custom ep93xx based board
> for quite a while with no problems.
> 
If you think it's better to go with the plat_nand driver than I'll go 
for it, I would be interested in seeing your one if you can send me a 
copy?

I guess there would be no code duplication really, shame, I was quite 
proud of the parititoning code :)

Cheers
hartleys - Feb. 9, 2009, 7 p.m.
Sunday, February 08, 2009 1:59 AM, Alexander Clouter wrote:
> This patch adds support for the NAND found in Technologic
> Systems ARM boards[1].  The platform specific parts (IO 
> address and parititoning schemes) have been moved into
> platform specific files whilst the driver it's self can 
> be used as a complete replacement for the ts7250 NAND
> driver.
>
> [1] http://www.embeddedarm.com/products/arm-sbc.php
>
> Signed-off-by: Alexander Clouter <alex@digriz.org.uk>

I think the TS-7800 series is Marvell 88F5182 based but the TS-7200
series is Cirrus ep93xx based.

There is are already a nand driver for each of these, orion_nand.c and
ts7250.c.  Both appear to be just variations of the plat_nand.c driver.

Hartley
Alexander Clouter - Feb. 9, 2009, 7:56 p.m.
Hi,

* hartleys <hartleys@visionengravers.com> [2009-02-09 14:00:48-0500]:
> 
> Sunday, February 08, 2009 1:59 AM, Alexander Clouter wrote:
>
> > This patch adds support for the NAND found in Technologic
> > Systems ARM boards[1].  The platform specific parts (IO 
> > address and parititoning schemes) have been moved into
> > platform specific files whilst the driver it's self can 
> > be used as a complete replacement for the ts7250 NAND
> > driver.
>
> I think the TS-7800 series is Marvell 88F5182 based but the TS-7200
> series is Cirrus ep93xx based.
>
> There is are already a nand driver for each of these, orion_nand.c and
> ts7250.c.  Both appear to be just variations of the plat_nand.c driver.
> 
You would have hoped TS would use the NAND silicon already on the 
Marvell chip, but no they decided to use up FPGA LUT's to re-invent the 
wheel; although I do think their just pulled over the FPGA bitstream 
they were using in the TS-7200 series.

Cheers

Patch

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f8ae040..6c9d344 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -76,10 +76,16 @@  config MTD_NAND_AMS_DELTA
 
 config MTD_NAND_TS7250
 	tristate "NAND Flash device on TS-7250 board"
-	depends on MACH_TS72XX
+	depends on MACH_TS72XX && !MTD_NAND_TS7XXX
 	help
 	  Support for NAND flash on Technologic Systems TS-7250 platform.
 
+config MTD_NAND_TS7XXX
+	tristate "NAND Flash device on TS-7xxx boards"
+	depends on MACH_TS72XX || MACH_TS78XX
+	help
+	  Support for NAND flash on Technologic Systems TS-7xxx platforms.
+
 config MTD_NAND_IDS
 	tristate
 
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index b661586..68694c7 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -19,6 +19,7 @@  obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)		+= sharpsl.o
 obj-$(CONFIG_MTD_NAND_TS7250)		+= ts7250.o
+obj-$(CONFIG_MTD_NAND_TS7XXX)		+= ts7xxx.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)		+= cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
diff --git a/drivers/mtd/nand/ts7xxx.c b/drivers/mtd/nand/ts7xxx.c
new file mode 100644
index 0000000..a2bc309
--- /dev/null
+++ b/drivers/mtd/nand/ts7xxx.c
@@ -0,0 +1,334 @@ 
+/*
+ * drivers/mtd/nand/ts7xxx.c
+ *
+ * Copyright (C) 2008 Alexander Clouter (alex@digriz.org.uk)
+ *
+ * Derived from drivers/mtd/nand/ts7250.c
+ *   Copyright (C) 2004 Technologic Systems (support@embeddedARM.com)
+ *
+ * More information of course gleaned from Technologic Systems driver
+ * in their supplied kernel drivers/mtd/nand/ts7800.c and
+ * ts-7800-nand-regmap.txt; of course also the existing ts7250.c driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Overview:
+ *   This is a device driver for the NAND flash device found on the TS-7xxx
+ *   board ranges.  DMA and HW ECC support is only available on the TS-78xx
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand-ts7xxx.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/mach-types.h>
+
+/* needed for board_is_ts7200() */
+#ifdef CONFIG_MACH_TS72XX
+#include <mach/hardware.h>
+#endif
+
+/*
+ * MTD structure for TS7xxx board
+ */
+static struct mtd_info *ts7xxx_mtd;
+
+static struct ts7xxx_nand_data *ts7xxx_nand_data;
+static void __iomem **ts7xxx_iobase;
+static void __iomem *ts7xxx_ioctrl;
+static void __iomem *ts7xxx_iodata;
+static void __iomem *ts7xxx_iobusy;
+
+/*
+ * hardware specific access to control-lines
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 -> bit 2
+ * NAND_CLE: bit 1 -> bit 1
+ * NAND_ALE: bit 2 -> bit 0
+ */
+static void ts7xxx_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *this = mtd->priv;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		unsigned char bits;
+
+		bits = (ctrl & NAND_NCE) << 2;
+		bits |= ctrl & NAND_CLE;
+		bits |= (ctrl & NAND_ALE) >> 2;
+
+		writeb((readb(ts7xxx_ioctrl) & ~0x7) | bits, ts7xxx_ioctrl);
+	}
+
+	if (cmd != NAND_CMD_NONE)
+		writeb(cmd, this->IO_ADDR_W);
+}
+
+static int ts7xxx_device_ready(struct mtd_info *mtd)
+{
+	return readb(ts7xxx_iobusy) & 0x20;
+}
+
+#ifdef CONFIG_MACH_TS78XX
+/*
+ * The HW ECC offloading functions, gives about a 9% performance increase
+ * for 'dd if=/dev/mtdblockX' and 5% for nanddump.
+ */
+static void ts7xxx_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	unsigned int ecc;
+
+	/* we loop as the FIFO might need emptying */
+	do {
+		ecc = readl(ts7xxx_ioctrl);
+		writel(ecc | 0x8, ts7xxx_ioctrl);
+	} while ((ecc & 0x18) != 0x18);
+}
+
+static int ts7xxx_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
+				uint8_t *ecc_code)
+{
+	unsigned int i, ecc;
+
+	for (i = 0; i < 8; i++) {
+		/*
+		 * TS told me that their HW ECC implementation spits out on the
+		 * 22 LSB's and not the MSB's which is why we right shift
+		 * trimming the top and not the bottom!
+		 */
+		ecc = (readl(ts7xxx_ioctrl) >> 6) | 0x03;
+
+		*ecc_code++ = (ecc >> 16) & 0xff;
+		*ecc_code++ = (ecc >>  8) & 0xff;
+		*ecc_code++ = (ecc >>  0) & 0xff;
+	};
+
+	return 0;
+}
+
+static int ts7xx_correct_data(struct mtd_info *mtd, uint8_t *dat,
+				uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	int ret = 0;
+	unsigned int i;
+
+	for (i = 0; i < 8; i++) {
+		ret |= nand_correct_data(mtd, dat + (256 * i),
+						&read_ecc[i * 3],
+						&calc_ecc[i * 3]);
+	}
+
+	return ret;
+}
+#endif
+
+static void ts7xxx_nand_cleanup(struct platform_device *pdev)
+{
+	unsigned int i;
+
+	for (i = 0; i < pdev->num_resources; i++) {
+		if (ts7xxx_iobase[i])
+			iounmap(ts7xxx_iobase[i]);
+	}
+	kfree(ts7xxx_iobase);
+	kfree(ts7xxx_mtd);
+}
+
+/*
+ * Main initialization routine
+ */
+static int __devinit ts7xxx_nand_probe(struct platform_device *pdev)
+{
+	struct nand_chip *this;
+	unsigned int i;
+	struct ts7xxx_nand_data	*(*ts7xxx_nand_data_func)(unsigned int)
+						= pdev->dev.platform_data;
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+	struct mtd_partition *mtd_parts = NULL;
+	int mtd_parts_nb = 0;
+	const char *part_type = NULL;
+#endif
+
+#ifdef CONFIG_MACH_TS72XX
+	if (!machine_is_ts72xx() || board_is_ts7200())
+		return -ENXIO;
+#endif
+#ifdef CONFIG_MACH_TS78XX
+	if (!machine_is_ts78xx())
+		return -ENXIO;
+#endif
+
+	/* Allocate memory for MTD device structure and private data */
+	ts7xxx_mtd = kmalloc(sizeof(struct mtd_info)
+					+ sizeof(struct nand_chip),
+				GFP_KERNEL);
+	if (!ts7xxx_mtd) {
+		printk(KERN_ERR "Unable to allocate TS7xxx NAND "
+				"MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * this is runtime as we do not know the size of the NAND, and thus
+	 * its partitioning scheme, until we scan it here
+	 *
+	 * N.B. we use a dummy size of zero to get the currently useful info
+	 */
+	ts7xxx_nand_data = (*ts7xxx_nand_data_func)(0);
+
+	ts7xxx_iobase = kmalloc(pdev->num_resources * sizeof(void __iomem *),
+					GFP_KERNEL);
+	if (!ts7xxx_iobase) {
+		printk(KERN_ERR "ts7xxx: Unable to allocate ioremap array.\n");
+		kfree(ts7xxx_mtd);
+		return -ENOMEM;
+	}
+	memset(ts7xxx_iobase, 0, pdev->num_resources * sizeof(void __iomem *));
+
+	for (i = 0; i < pdev->num_resources; i++) {
+		ts7xxx_iobase[i] = ioremap(pdev->resource[i].start,
+					pdev->resource[i].end
+						- pdev->resource[i].start + 1);
+		if (!ts7xxx_iobase[i]) {
+			printk(KERN_ERR "ts7xxx: ioremap failed\n");
+			ts7xxx_nand_cleanup(pdev);
+			return -EIO;
+		}
+	}
+
+	ts7xxx_ioctrl = ts7xxx_iobase[ts7xxx_nand_data->ioports.ctrl.res]
+				+ ts7xxx_nand_data->ioports.ctrl.offset;
+	ts7xxx_iodata = ts7xxx_iobase[ts7xxx_nand_data->ioports.data.res]
+				+ ts7xxx_nand_data->ioports.data.offset;
+	ts7xxx_iobusy = ts7xxx_iobase[ts7xxx_nand_data->ioports.busy.res]
+				+ ts7xxx_nand_data->ioports.busy.offset;
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *)(&ts7xxx_mtd[1]);
+
+	/* Initialize structures */
+	memset(ts7xxx_mtd, 0, sizeof(struct mtd_info));
+	memset(this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	ts7xxx_mtd->priv = this;
+	ts7xxx_mtd->owner = THIS_MODULE;
+
+	/* insert callbacks */
+	this->IO_ADDR_R = this->IO_ADDR_W = ts7xxx_iodata;
+	this->cmd_ctrl = ts7xxx_hwcontrol;
+	this->dev_ready = ts7xxx_device_ready;
+	this->chip_delay = 15;
+
+	do {
+		this->ecc.mode = NAND_ECC_SOFT;
+
+#ifdef CONFIG_MACH_TS78XX
+		if (!machine_is_ts78xx()) {
+			this->options = NAND_USE_FLASH_BBT;
+			this->ecc.mode = NAND_ECC_HW;
+			/* the HW ECC has an eight entry FIFO */
+			this->ecc.size = 8 * 256;
+			this->ecc.bytes = 8 * 3;
+			this->ecc.hwctl  = ts7xxx_enable_hwecc;
+			this->ecc.calculate = ts7xxx_calculate_ecc;
+			this->ecc.correct  = ts7xx_correct_data;
+			break;
+		}
+#endif
+	} while (0);
+
+	printk(KERN_NOTICE "Searching for NAND flash...\n");
+	/* Scan to find existence of the device */
+	if (nand_scan(ts7xxx_mtd, 1)) {
+		ts7xxx_nand_cleanup(pdev);
+		return -ENODEV;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	ts7xxx_mtd->name = "ts7xxx-nand";
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	mtd_parts_nb = parse_mtd_partitions(ts7xxx_mtd,
+						part_probes, &mtd_parts, 0);
+	if (mtd_parts_nb > 0)
+		part_type = "command line";
+	else
+		mtd_parts_nb = 0;
+#endif
+
+	if (mtd_parts_nb == 0) {
+		/*
+		 * ...and now we can use size to find the partitioning scheme
+		 */
+		ts7xxx_nand_data = (*ts7xxx_nand_data_func)(ts7xxx_mtd->size);
+
+		if (ts7xxx_nand_data->partitions == NULL) {
+			printk(KERN_ERR
+				"ts7xxx_nand: unsupported NAND size, received "
+				"no paritioning scheme from platform\n");
+			ts7xxx_nand_cleanup(pdev);
+			/* FIXME: is this the most suitable return value? */
+			return -ENXIO;
+		}
+
+		mtd_parts = ts7xxx_nand_data->partitions;
+		mtd_parts_nb = ts7xxx_nand_data->nr_partitions;
+		part_type = "static";
+	}
+
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	add_mtd_partitions(ts7xxx_mtd, mtd_parts, mtd_parts_nb);
+#endif
+
+	/* Return happy */
+	return 0;
+}
+
+static int __devexit ts7xxx_nand_remove(struct platform_device *pdev)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+	del_mtd_partitions(ts7xxx_mtd);
+#endif
+	ts7xxx_nand_cleanup(pdev);
+
+	return 0;
+}
+
+/* driver device registration */
+static struct platform_driver ts7xxx_nand_driver = {
+	.driver		= {
+		.name	= "ts7xxx_nand",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ts7xxx_nand_probe,
+	.remove		= __devexit_p(ts7xxx_nand_remove),
+};
+
+static int __init ts7xxx_nand_init(void)
+{
+	return platform_driver_register(&ts7xxx_nand_driver);
+}
+
+static void __exit ts7xxx_nand_exit(void)
+{
+	platform_driver_unregister(&ts7xxx_nand_driver);
+}
+
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("MTD NAND driver for Technologic Systems TS-7xxx boards");
+MODULE_LICENSE("GPL");
+
+module_init(ts7xxx_nand_init);
+module_exit(ts7xxx_nand_exit);
diff --git a/include/linux/mtd/nand-ts7xxx.h b/include/linux/mtd/nand-ts7xxx.h
new file mode 100644
index 0000000..955209a
--- /dev/null
+++ b/include/linux/mtd/nand-ts7xxx.h
@@ -0,0 +1,35 @@ 
+/*
+ * linux/include/linux/mtd/nand-ts7xxx.h
+ *
+ * Copyright (c) 2008 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/partitions.h>
+
+struct ts7xxx_ioport_entry {
+	unsigned int			res;
+	unsigned int			offset;
+};
+
+struct ts7xxx_ioports {
+	struct ts7xxx_ioport_entry	ctrl;
+	struct ts7xxx_ioport_entry	data;
+
+	struct ts7xxx_ioport_entry	busy;
+};
+
+/*
+ * struct ts7xxx_nand_data - chip level device structure
+ * @nr_partitions:	number of partitions pointed to by partitions (or zero)
+ * @partitions:		mtd partition list
+ */
+struct ts7xxx_nand_data {
+	int				nr_partitions;
+	struct mtd_partition		*partitions;
+
+	struct ts7xxx_ioports		ioports;
+};