From d7bfef11b8109b5b223c6f0f62757bc16f3b3f34 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Thu, 29 Nov 2007 13:07:53 +0100 Subject: [PATCH] MTD: Add NAND driver for AT32 chips This driver is very similar to the AT91 driver, so they should probably be merged. Signed-off-by: Haavard Skinnemoen --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/at32_nand.c | 294 +++++++++++++++++++++++++++++++++ include/asm-avr32/arch-at32ap/board.h | 12 ++ 4 files changed, 314 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/at32_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 246d451..261e844 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -272,6 +272,13 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". +config MTD_NAND_AT32 + tristate "Support for NAND Flash / SmartMedia on AVR32" + depends on AVR32 + help + Enables support for NAND Flash / Smart Media Card interface + on Atmel AVR32 processors. + config MTD_NAND_AT91 bool "Support for NAND Flash / SmartMedia on AT91" depends on ARCH_AT91 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 3ad6c01..6e6b30c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o +obj-$(CONFIG_MTD_NAND_AT32) += at32_nand.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o diff --git a/drivers/mtd/nand/at32_nand.c b/drivers/mtd/nand/at32_nand.c new file mode 100644 index 0000000..a28957b --- /dev/null +++ b/drivers/mtd/nand/at32_nand.c @@ -0,0 +1,294 @@ +/* + * drivers/mtd/nand/at32.c + * + * Copyright (C) 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * + * 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. + * + */ +#define DEBUG +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct at32_nand_host { + struct nand_chip nand_chip; + struct mtd_info mtd; + void __iomem *io_base; + struct at32_nand_data *board; + struct platform_device *pdev; +}; + +/* + * Hardware specific access to control-lines + */ +static void at32_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at32_nand_host *host = nand_chip->priv; + + if (cmd == NAND_CMD_NONE) + return; + dev_vdbg(&host->pdev->dev, "%s: 0x%02x, ctrl: 0x%02x\n", + (ctrl & NAND_CLE) ? "command" : "address", + (u8)cmd, ctrl); + + if (ctrl & NAND_CLE) { + dev_vdbg(&host->pdev->dev, "write %02x to %p\n", + cmd, host->io_base + (1 << host->board->cle)); + writeb(cmd, host->io_base + (1 << host->board->cle)); + } else { + dev_vdbg(&host->pdev->dev, "write %02x to %p\n", + cmd, host->io_base + (1 << host->board->ale)); + writeb(cmd, host->io_base + (1 << host->board->ale)); + } +} + +/* + * Read the Device Ready pin. + */ +static int at32_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct at32_nand_host *host = nand_chip->priv; + + return gpio_get_value(host->board->rdy_pin); +} + +/* + * Enable NAND. + */ +static void at32_nand_enable(struct at32_nand_host *host) +{ + if (host->board->enable_pin) + gpio_set_value(host->board->enable_pin, 0); +} + +/* + * Disable NAND. + */ +static void at32_nand_disable(struct at32_nand_host *host) +{ + if (host->board->enable_pin) + gpio_set_value(host->board->enable_pin, 1); +} + +/* + * Probe for the NAND device. + */ +static int __init at32_nand_probe(struct platform_device *pdev) +{ + struct at32_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct resource *regs; + struct at32_nand_data *board; + int res; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_partitions = 0; +#endif + + board = pdev->dev.platform_data; + if (!board) { + dev_dbg(&pdev->dev, "no platform data\n"); + return -ENXIO; + } + +#ifdef CONFIG_MTD_PARTITIONS + partitions = board->parts; + num_partitions = board->nr_parts; +#endif + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no memory resource\n"); + return -ENXIO; + } + + if (!board->rdy_pin) { + dev_dbg(&pdev->dev, "ready pin missing\n"); + return -ENXIO; + } + + /* Allocate memory for the device structure (and zero it) */ + res = -ENOMEM; + host = kzalloc(sizeof(struct at32_nand_host), GFP_KERNEL); + if (!host) { + dev_dbg(&pdev->dev, "failed to allocate host\n"); + goto err_alloc_host; + } + + host->io_base = ioremap(regs->start, regs->end - regs->start + 1); + if (!host->io_base) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + goto err_ioremap; + } + + dev_dbg(&pdev->dev, "I/O base: %p\n", host->io_base); + + mtd = &host->mtd; + nand_chip = &host->nand_chip; + host->board = board; + + nand_chip->priv = host; + mtd->priv = nand_chip; + mtd->owner = THIS_MODULE; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = host->io_base; + nand_chip->IO_ADDR_W = host->io_base; + nand_chip->cmd_ctrl = at32_nand_cmd_ctrl; + nand_chip->dev_ready = at32_nand_device_ready; + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + nand_chip->chip_delay = 20; /* 20us command delay time */ + nand_chip->options = 0; + +/* We dont have bus_width_16 at the moment, so disable it for now... */ +#if 0 + if (host->board->bus_width_16) /* 16-bit bus width */ + nand_chip->options |= NAND_BUSWIDTH_16; +#endif + + res = gpio_request(host->board->rdy_pin, "nand_rdy"); + if (res < 0) { + dev_dbg(&pdev->dev, "failed to request rdy pin\n"); + goto err_request_rdy; + } + if (host->board->enable_pin) { + res = gpio_request(host->board->enable_pin, "nand_en"); + if (res < 0) { + dev_dbg(&pdev->dev, "failed to request enable pin\n"); + goto err_request_en; + } + } + + platform_set_drvdata(pdev, host); + + at32_nand_enable(host); + + if (host->board->det_pin) { + res = gpio_request(host->board->det_pin, "nand_det"); + if (res < 0) { + dev_dbg(&pdev->dev, "failed to request det pin\n"); + goto err_request_det; + } + if (gpio_get_value(host->board->det_pin)) { + dev_dbg(&pdev->dev, "No SmartMedia card inserted.\n"); + res = -ENODEV; + goto err_detect; + } + } + + /* Scan to find existance of the device */ + if (nand_scan(mtd, 1)) { + res = -ENODEV; + goto err_scan; + } + +#ifdef CONFIG_MTD_PARTITIONS + if (partitions && num_partitions > 0) + res = add_mtd_partitions(mtd, partitions, num_partitions); + else +#else + res = add_mtd_device(mtd); +#endif + + if (res) { + dev_dbg(&pdev->dev, + "failed to add mtd device/partitions: %d\n", res); + goto err_mtd; + } + + return 0; + +err_mtd: + nand_release(mtd); +err_scan: +err_detect: + if (host->board->det_pin) + gpio_free(host->board->det_pin); +err_request_det: + at32_nand_disable(host); + platform_set_drvdata(pdev, NULL); + if (host->board->enable_pin) + gpio_free(host->board->enable_pin); +err_request_en: + gpio_free(host->board->rdy_pin); +err_request_rdy: + iounmap(host->io_base); +err_ioremap: + kfree(host); +err_alloc_host: + return res; +} + +/* + * Remove a NAND device. + */ +static int __devexit at32_nand_remove(struct platform_device *pdev) +{ + struct at32_nand_host *host = platform_get_drvdata(pdev); + struct mtd_info *mtd = &host->mtd; + +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(mtd); +#else + del_mtd_device(mtd); +#endif + nand_release(mtd); + at32_nand_disable(host); + + if (host->board->det_pin) + gpio_free(host->board->det_pin); + if (host->board->enable_pin) + gpio_free(host->board->enable_pin); + gpio_free(host->board->rdy_pin); + + iounmap(host->io_base); + kfree(host); + + return 0; +} + +static struct platform_driver at32_nand_driver = { + .probe = at32_nand_probe, + .remove = at32_nand_remove, + .driver = { + .name = "at32_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init at32_nand_init(void) +{ + return platform_driver_register(&at32_nand_driver); +} + + +static void __exit at32_nand_exit(void) +{ + platform_driver_unregister(&at32_nand_driver); +} + + +module_init(at32_nand_init); +module_exit(at32_nand_exit); + +MODULE_LICENSE("GPL"); diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h index ab82bd9..dc1930e 100644 --- a/include/asm-avr32/arch-at32ap/board.h +++ b/include/asm-avr32/arch-at32ap/board.h @@ -65,6 +65,18 @@ at32_add_device_ide(unsigned int id, unsigned int extint, struct platform_device * at32_add_device_ssc(unsigned int id, unsigned int flags); +struct at32_nand_data { + u8 enable_pin; + u8 det_pin; + u8 rdy_pin; + u8 ale; + u8 cle; + unsigned int nr_parts; + struct mtd_partition *parts; +}; +struct platform_device * +at32_add_device_nand(unsigned int id, struct at32_nand_data *data); + struct platform_device *at32_add_device_twi(unsigned int id); struct mci_platform_data { -- 1.5.3.6