generic NAND driver for TS-72[56]0 and TS-7800
Alexander Clouter
alex at digriz.org.uk
Thu Jun 12 16:54:53 EDT 2008
Hi,
Really just a request for comments on an alternative driver I have put
together that works on the TS-72[56]0 (currently covered by the ts7250.c
driver) and the TS-7800 that I submitted to the ARM mailing list the other
week[1].
The NAND on the TS boards is more or less identical except for:
* the partitioning layout
* the io memory port addresses
* the TS-7800 supports DMA and HW ECC
So to tackle this I did the following:
1. moved the paritioning handling to become a runtime decision and to come
from a function called lurking in the platform code
2. IO addresses get passed in the platform_device struct
3. [coming soon] to enable (if on a TS-78xx) the DMA HW ECC code; you can
see how I'm adding the hooks, it's just the code currently does not
work properly yet ;)
For those familiar with the ts7250.c code, you will notice my ts7xxx.c code
is rather similar...unsurprisingly it's what I deried it from.
Comments, flames, and explanations on how to properly do all this MTD
malarkey welcomed. I already chatted to the ts7250.c author who no longer
uses the board and he said I should just go right ahead and post my patch
here. I own and have obviously tested this on the TS-7800 and got some Joe
Public's on the TS-7000 mailing list with TS-7[56]0's to test it too[2] for
me.
Cheers
Alex
N.B. worth noting that the parition layout in the current ts7250.c driver is
wrong. My patch sets it corrrectly to what it should be.
[1] http://marc.info/?l=linux-arm-kernel&m=121280018602505&w=2
[2] http://tech.groups.yahoo.com/group/ts-7000/message/11616?var=0&l=1
--
_________________________________________
/ Walking on water wasn't built in a day. \
| |
\ -- Jack Kerouac /
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index 3a4bf90..2bbfbd4 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -17,6 +17,8 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/mtd/physmap.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/ts7xxx.h>
#include <linux/platform_device.h>
#include <linux/m48t86.h>
#include <asm/io.h>
@@ -131,6 +133,84 @@ static struct platform_device ts72xx_flash = {
.resource = &ts72xx_flash_resource,
};
+/*****************************************************************************
+ * NAND Flash
+ ****************************************************************************/
+static struct mtd_partition ts72xx_nand_parts32[] = {
+ {
+ .name = "TS-BOOTROM",
+ .offset = 0,
+ .size = SZ_128K,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "Linux",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 29 * SZ_1M,
+ }, {
+ .name = "RedBoot",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct mtd_partition ts72xx_nand_parts128[] = {
+ {
+ .name = "TS-BOOTROM",
+ .offset = 0,
+ .size = SZ_128K,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "Linux",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 125 * SZ_1M,
+ }, {
+ .name = "RedBoot",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct ts7xxx_nand_data ts72xx_nand_data = {
+ .partitions = NULL,
+ .nr_partitions = 0,
+
+ .ioports = {
+ .ctrl = (void *__iomem) TS72XX_NAND_CONTROL_VIRT_BASE,
+ .data = (void *__iomem) TS72XX_NAND_DATA_VIRT_BASE,
+
+ .busy = (void *__iomem) TS72XX_NAND_BUSY_VIRT_BASE,
+ },
+};
+
+static struct ts7xxx_nand_data *ts72xx_nand(unsigned int size)
+{
+ /*
+ * The TS-72[56]0's come with either 32MiB or 128MiB of NAND
+ */
+ if (size == 32 * SZ_1M) {
+ ts72xx_nand_data.partitions = ts72xx_nand_parts32;
+ ts72xx_nand_data.nr_partitions
+ = ARRAY_SIZE(ts72xx_nand_parts32);
+ } else if (size == 128 * SZ_1M) {
+ ts72xx_nand_data.partitions = ts72xx_nand_parts128;
+ ts72xx_nand_data.nr_partitions
+ = ARRAY_SIZE(ts72xx_nand_parts128);
+ }
+
+ return &ts72xx_nand_data;
+};
+
+static struct platform_device ts72xx_nand_flash = {
+ .name = "ts7xxx_nand",
+ .id = -1,
+ .dev = {
+ .platform_data = &ts72xx_nand,
+ },
+ .num_resources = 0,
+};
+
+/****************************************************************************/
+
static unsigned char ts72xx_rtc_readbyte(unsigned long addr)
{
__raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
@@ -188,6 +268,8 @@ static void __init ts72xx_init_machine(void)
ep93xx_init_devices();
if (board_is_ts7200())
platform_device_register(&ts72xx_flash);
+ if (!board_is_ts7200())
+ platform_device_register(&ts72xx_nand_flash);
platform_device_register(&ts72xx_rtc_device);
memcpy(ts72xx_eth_data.dev_addr,
diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c
index bbf87a4..071ecae 100644
--- a/arch/arm/mach-orion5x/ts78xx-setup.c
+++ b/arch/arm/mach-orion5x/ts78xx-setup.c
@@ -12,6 +12,8 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/ts7xxx.h>
#include <linux/mv643xx_eth.h>
#include <linux/ata_platform.h>
#include <linux/m48t86.h>
@@ -37,6 +39,10 @@
#define TS78XX_FPGA_REGS_SYSCON_LCDI (TS78XX_FPGA_REGS_VIRT_BASE | 0x004)
#define TS78XX_FPGA_REGS_SYSCON_LCDO (TS78XX_FPGA_REGS_VIRT_BASE | 0x008)
+#define TS78XX_FPGA_REGS_DMA (TS78XX_FPGA_REGS_VIRT_BASE | 0x400)
+
+#define TS78XX_FPGA_REGS_NAND_CTRL (TS78XX_FPGA_REGS_VIRT_BASE | 0x800)
+#define TS78XX_FPGA_REGS_NAND_DATA (TS78XX_FPGA_REGS_VIRT_BASE | 0x804)
#define TS78XX_FPGA_REGS_RTC_CTRL (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
#define TS78XX_FPGA_REGS_RTC_DATA (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
@@ -100,6 +106,68 @@ static struct platform_device ts78xx_nor_boot_flash = {
};
/*****************************************************************************
+ * NAND Flash
+ ****************************************************************************/
+static struct mtd_partition ts78xx_nand_parts512[] = {
+ {
+ .name = "mbr",
+ .offset = 0,
+ .size = SZ_128K,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_4M,
+ }, {
+ .name = "initrd",
+ .offset = MTDPART_OFS_APPEND,
+ .size = SZ_4M,
+ }, {
+ .name = "rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct ts7xxx_nand_data ts78xx_nand_data = {
+ .partitions = NULL,
+ .nr_partitions = 0,
+
+ .ioports = {
+ .ctrl = (void *__iomem) TS78XX_FPGA_REGS_NAND_CTRL,
+ .data = (void *__iomem) TS78XX_FPGA_REGS_NAND_DATA,
+
+ .busy = (void *__iomem) TS78XX_FPGA_REGS_NAND_CTRL,
+
+ .dma = (void *__iomem) TS78XX_FPGA_REGS_DMA,
+ .hcdr = (void *__iomem) ORION5X_BRIDGE_REG(0x400),
+ },
+};
+
+static struct ts7xxx_nand_data *ts78xx_nand(unsigned int size)
+{
+ /*
+ * currently only the TS-7800 is out there with a 512MiB NAND
+ */
+ if (size == 512 * SZ_1M) {
+ ts78xx_nand_data.partitions = ts78xx_nand_parts512;
+ ts78xx_nand_data.nr_partitions
+ = ARRAY_SIZE(ts78xx_nand_parts512);
+ }
+
+ return &ts78xx_nand_data;
+};
+
+static struct platform_device ts78xx_nand_flash = {
+ .name = "ts7xxx_nand",
+ .id = -1,
+ .dev = {
+ .platform_data = &ts78xx_nand,
+ },
+ .num_resources = 0,
+};
+
+/*****************************************************************************
* Ethernet
****************************************************************************/
static struct mv643xx_eth_platform_data ts78xx_eth_data = {
@@ -256,6 +324,10 @@ static void __init ts78xx_init(void)
TS78XX_NOR_BOOT_SIZE);
platform_device_register(&ts78xx_nor_boot_flash);
+#ifdef CONFIG_MTD_NAND_TS7XXX
+ platform_device_register(&ts78xx_nand_flash);
+#endif
+
if (!ts78xx_rtc_init())
printk(KERN_INFO "TS-78xx RTC not detected or enabled\n");
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5076faf..9079051 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -81,6 +81,12 @@ config MTD_NAND_TS7250
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 a6e74a4..a565a12 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -21,6 +21,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..0f3d0f3
--- /dev/null
+++ b/drivers/mtd/nand/ts7xxx.c
@@ -0,0 +1,268 @@
+/*
+ * drivers/mtd/nand/ts7xxx.c
+ *
+ * Copyright (C) 2008 Alexander Clouter (alex at digriz.org.uk)
+ *
+ * Derived from drivers/mtd/nand/ts7250.c
+ * Copyright (C) 2004 Technologic Systems (support at embeddedARM.com)
+ *
+ * More information of course gleaned from Technologic Systems driver
+ * in their supplied kernel drivers/mtd/nand/ts7800.c
+ *
+ * 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/slab.h>
+#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/ts7xxx.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/cacheflush.h>
+
+/*
+ * MTD structure for TS7xxx board
+ */
+static struct mtd_info *ts7xxx_mtd;
+
+static struct ts7xxx_nand_data *ts7xxx_nand_data;
+
+/*
+ * 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_nand_data->ioports.ctrl) & ~0x7) | bits,
+ ts7xxx_nand_data->ioports.ctrl);
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+static int ts7xxx_device_ready(struct mtd_info *mtd)
+{
+ return readb(ts7xxx_nand_data->ioports.busy) & 0x20;
+}
+
+static void ts7xxx_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ unsigned int dummy;
+
+ /* we loop as the FIFO might need emptying */
+ dummy = readl(ts7xxx_nand_data->ioports.ctrl);
+ while ((dummy & 0x18) != 0x18) {
+ writel(dummy | 0x8, ts7xxx_nand_data->ioports.ctrl);
+ dummy = readl(ts7xxx_nand_data->ioports.ctrl);
+ }
+}
+
+static int ts7xxx_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ unsigned int ecc, i;
+
+ for (i = 0; i < 8; i++) {
+ ecc = (readl(ts7xxx_nand_data->ioports.ctrl) >> 8) & 0xffffff;
+
+ *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;
+}
+
+/*
+ * Main initialization routine
+ */
+static int ts7xxx_nand_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct ts7xxx_nand_data *(*ts7xxx_nand_data_func)(unsigned int)
+ = pdev->dev.platform_data;
+#ifdef CONFIG_MTD_PARTITIONS
+ static const char *part_probes[] = { "cmdlinepart", NULL };
+ 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;
+ }
+
+ /* 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;
+
+ /*
+ * 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);
+
+ /* insert callbacks */
+ this->IO_ADDR_R = this->IO_ADDR_W = ts7xxx_nand_data->ioports.data;
+ this->cmd_ctrl = ts7xxx_hwcontrol;
+ this->dev_ready = ts7xxx_device_ready;
+ this->chip_delay = 15;
+
+ do {
+ /* if (machine_is_ts78xx()) {
+ 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;
+ } */
+
+ this->ecc.mode = NAND_ECC_SOFT;
+ } while (0);
+
+ printk(KERN_NOTICE "Searching for NAND flash...\n");
+ /* Scan to find existence of the device */
+ if (nand_scan(ts7xxx_mtd, 1)) {
+ kfree(ts7xxx_mtd);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ ts7xxx_mtd->name = "ts7xxx-nand";
+ 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;
+
+ 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");
+ /* 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 ts7xxx_nand_remove(struct platform_device *pdev)
+{
+ kfree(ts7xxx_mtd);
+ return 0;
+}
+
+/* driver device registration */
+
+static struct platform_driver ts7xxx_nand_driver = {
+ .probe = ts7xxx_nand_probe,
+ .remove = ts7xxx_nand_remove,
+ .driver = {
+ .name = "ts7xxx_nand",
+ .owner = THIS_MODULE,
+ },
+};
+
+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_init(ts7xxx_nand_init);
+module_exit(ts7xxx_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex at digriz.org.uk>");
+MODULE_DESCRIPTION("MTD NAND driver for Technologic Systems TS-7xxx boards");
+
diff --git a/include/linux/mtd/ts7xxx.h b/include/linux/mtd/ts7xxx.h
new file mode 100644
index 0000000..d79aeb7
--- /dev/null
+++ b/include/linux/mtd/ts7xxx.h
@@ -0,0 +1,36 @@
+/*
+ * linux/include/linux/mtd/ts7xxx.h
+ *
+ * Copyright (c) 2008 Alexander Clouter <alex at 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>
+
+/*
+ * io ports listing
+ */
+struct ts7xxx_ioports {
+ void __iomem *ctrl;
+ void __iomem *data;
+
+ void __iomem *busy;
+
+ void __iomem *dma;
+ void __iomem *hcdr;
+};
+
+/*
+ * 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;
+};
More information about the linux-mtd
mailing list