All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexander Clouter <alex@digriz.org.uk>
To: linux-mtd@lists.infradead.org
Subject: [PATCH] [MTD] NAND: add ts7xxx driver
Date: Sun, 8 Feb 2009 08:59:19 +0000	[thread overview]
Message-ID: <20090208085919.GD11872@woodchuck> (raw)

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

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;
+};
-- 
1.5.6.5

             reply	other threads:[~2009-02-08  8:59 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-08  8:59 Alexander Clouter [this message]
2009-02-08 10:21 ` [PATCH] [MTD] NAND: add ts7xxx driver Lennert Buytenhek
2009-02-08 10:39   ` Alexander Clouter
2009-02-08 10:52     ` Alexander Clouter
2009-02-08 10:56       ` Alexander Clouter
2009-02-09 16:56 ` hartleys
2009-02-09 17:05   ` Alexander Clouter
2009-02-09 19:00 ` hartleys
2009-02-09 19:56   ` Alexander Clouter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090208085919.GD11872@woodchuck \
    --to=alex@digriz.org.uk \
    --cc=linux-mtd@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.