diff options
-rw-r--r-- | drivers/mtd/nand/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/orion_nand.c | 171 |
3 files changed, 181 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f8db6251209..0a840d5d75a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -312,4 +312,13 @@ config MTD_ALAUDA These two (and possibly other) Alauda-based cardreaders for SmartMedia and xD allow raw flash access. +config MTD_NAND_ORION + tristate "NAND Flash support for Marvell Orion SoC" + depends on ARCH_ORION && MTD_NAND + help + This enables the NAND flash controller on Orion machines. + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index fd5004795ec..e35f5ea3a7a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -30,5 +30,6 @@ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o +obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c new file mode 100644 index 00000000000..9162cca0182 --- /dev/null +++ b/drivers/mtd/nand/orion_nand.c @@ -0,0 +1,171 @@ +/* + * drivers/mtd/nand/orion_nand.c + * + * NAND support for Marvell Orion SoC platforms + * + * Tzachi Perelstein <tzachi@marvell.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/sizes.h> +#include <asm/arch/platform.h> +#include <asm/arch/hardware.h> + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nc = mtd->priv; + struct orion_nand_data *board = nc->priv; + u32 offs; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + offs = (1 << board->cle); + else if (ctrl & NAND_ALE) + offs = (1 << board->ale); + else + return; + + if (nc->options & NAND_BUSWIDTH_16) + offs <<= 1; + + writeb(cmd, nc->IO_ADDR_W + offs); +} + +static int __init orion_nand_probe(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nc; + struct orion_nand_data *board; + void __iomem *io_base; + int ret = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_part = 0; +#endif + + nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL); + if (!nc) { + printk(KERN_ERR "orion_nand: failed to allocate device structure.\n"); + ret = -ENOMEM; + goto no_res; + } + mtd = (struct mtd_info *)(nc + 1); + + io_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + if (!io_base) { + printk(KERN_ERR "orion_nand: ioremap failed\n"); + ret = -EIO; + goto no_res; + } + + board = pdev->dev.platform_data; + + mtd->priv = nc; + mtd->owner = THIS_MODULE; + + nc->priv = board; + nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; + nc->cmd_ctrl = orion_nand_cmd_ctrl; + nc->ecc.mode = NAND_ECC_SOFT; + + if (board->width == 16) + nc->options |= NAND_BUSWIDTH_16; + + platform_set_drvdata(pdev, mtd); + + if (nand_scan(mtd, 1)) { + ret = -ENXIO; + goto no_dev; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd->name = "orion_nand"; + num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0); +#endif + /* If cmdline partitions have been passed, let them be used */ + if (num_part <= 0) { + num_part = board->nr_parts; + partitions = board->parts; + } + + if (partitions && num_part > 0) + ret = add_mtd_partitions(mtd, partitions, num_part); + else + ret = add_mtd_device(mtd); +#else + ret = add_mtd_device(mtd); +#endif + + if (ret) { + nand_release(mtd); + goto no_dev; + } + + return 0; + +no_dev: + platform_set_drvdata(pdev, NULL); + iounmap(io_base); +no_res: + kfree(nc); + + return ret; +} + +static int __devexit orion_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nc = mtd->priv; + + nand_release(mtd); + + iounmap(nc->IO_ADDR_W); + + kfree(nc); + + return 0; +} + +static struct platform_driver orion_nand_driver = { + .probe = orion_nand_probe, + .remove = orion_nand_remove, + .driver = { + .name = "orion_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init orion_nand_init(void) +{ + return platform_driver_register(&orion_nand_driver); +} + +static void __exit orion_nand_exit(void) +{ + platform_driver_unregister(&orion_nand_driver); +} + +module_init(orion_nand_init); +module_exit(orion_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tzachi Perelstein"); +MODULE_DESCRIPTION("NAND glue for Orion platforms"); |