diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2011-03-25 17:41:20 +0200 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2011-03-25 17:41:20 +0200 |
commit | 7bf7e370d5919112c223a269462cd0b546903829 (patch) | |
tree | 03ccc715239df14ae168277dbccc9d9cf4d8a2c8 /drivers/mtd | |
parent | 68b1a1e786f29c900fa1c516a402e24f0ece622a (diff) | |
parent | d39dd11c3e6a7af5c20bfac40594db36cf270f42 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into for-linus-1
* 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6: (9356 commits)
[media] rc: update for bitop name changes
fs: simplify iget & friends
fs: pull inode->i_lock up out of writeback_single_inode
fs: rename inode_lock to inode_hash_lock
fs: move i_wb_list out from under inode_lock
fs: move i_sb_list out from under inode_lock
fs: remove inode_lock from iput_final and prune_icache
fs: Lock the inode LRU list separately
fs: factor inode disposal
fs: protect inode->i_state with inode->i_lock
lib, arch: add filter argument to show_mem and fix private implementations
SLUB: Write to per cpu data when allocating it
slub: Fix debugobjects with lockless fastpath
autofs4: Do not potentially dereference NULL pointer returned by fget() in autofs_dev_ioctl_setpipefd()
autofs4 - remove autofs4_lock
autofs4 - fix d_manage() return on rcu-walk
autofs4 - fix autofs4_expire_indirect() traversal
autofs4 - fix dentry leak in autofs4_expire_direct()
autofs4 - reinstate last used update on access
vfs - check non-mountpoint dentry might block in __follow_mount_rcu()
...
NOTE!
This merge commit was created to fix compilation error. The block
tree was merged upstream and removed the 'elv_queue_empty()'
function which the new 'mtdswap' driver is using. So a simple
merge of the mtd tree with upstream does not compile. And the
mtd tree has already be published, so re-basing it is not an option.
To fix this unfortunate situation, I had to merge upstream into the
mtd-2.6.git tree without committing, put the fixup patch on top of
this, and then commit this. The result is that we do not have commits
which do not compile.
In other words, this merge commit "merges" 3 things: the MTD tree, the
upstream tree, and the fixup patch.
Diffstat (limited to 'drivers/mtd')
29 files changed, 654 insertions, 445 deletions
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index c85be831248..bd483f0c57e 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -214,8 +214,7 @@ static void __devinit of_free_probes(const char **probes) } #endif -static int __devinit of_flash_probe(struct platform_device *dev, - const struct of_device_id *match) +static int __devinit of_flash_probe(struct platform_device *dev) { #ifdef CONFIG_MTD_PARTITIONS const char **part_probe_types; @@ -223,7 +222,7 @@ static int __devinit of_flash_probe(struct platform_device *dev, struct device_node *dp = dev->dev.of_node; struct resource res; struct of_flash *info; - const char *probe_type = match->data; + const char *probe_type; const __be32 *width; int err; int i; @@ -233,6 +232,10 @@ static int __devinit of_flash_probe(struct platform_device *dev, struct mtd_info **mtd_list = NULL; resource_size_t res_size; + if (!dev->dev.of_match) + return -EINVAL; + probe_type = dev->dev.of_match->data; + reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); /* @@ -410,7 +413,7 @@ static struct of_device_id of_flash_match[] = { }; MODULE_DEVICE_TABLE(of, of_flash_match); -static struct of_platform_driver of_flash_driver = { +static struct platform_driver of_flash_driver = { .driver = { .name = "of-flash", .owner = THIS_MODULE, @@ -422,12 +425,12 @@ static struct of_platform_driver of_flash_driver = { static int __init of_flash_init(void) { - return of_register_platform_driver(&of_flash_driver); + return platform_driver_register(&of_flash_driver); } static void __exit of_flash_exit(void) { - of_unregister_platform_driver(&of_flash_driver); + platform_driver_unregister(&of_flash_driver); } module_init(of_flash_init); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 3582ba1f9b0..3f1cb328a57 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -108,7 +108,7 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp) return 0; } -static int __devinit uflash_probe(struct platform_device *op, const struct of_device_id *match) +static int __devinit uflash_probe(struct platform_device *op) { struct device_node *dp = op->dev.of_node; @@ -148,7 +148,7 @@ static const struct of_device_id uflash_match[] = { MODULE_DEVICE_TABLE(of, uflash_match); -static struct of_platform_driver uflash_driver = { +static struct platform_driver uflash_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, @@ -160,12 +160,12 @@ static struct of_platform_driver uflash_driver = { static int __init uflash_init(void) { - return of_register_platform_driver(&uflash_driver); + return platform_driver_register(&uflash_driver); } static void __exit uflash_exit(void) { - of_unregister_platform_driver(&uflash_driver); + platform_driver_unregister(&uflash_driver); } module_init(uflash_init); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 65051130403..a534e1f0c34 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -124,7 +124,7 @@ int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev) if (kthread_should_stop()) return 1; - return !elv_queue_empty(dev->rq); + return dev->bg_stop; } EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background); @@ -141,6 +141,7 @@ static int mtd_blktrans_thread(void *arg) while (!kthread_should_stop()) { int res; + dev->bg_stop = false; if (!req && !(req = blk_fetch_request(rq))) { if (tr->background && !background_done) { spin_unlock_irq(rq->queue_lock); @@ -152,7 +153,7 @@ static int mtd_blktrans_thread(void *arg) * Do background processing just once per idle * period. */ - background_done = 1; + background_done = !dev->bg_stop; continue; } set_current_state(TASK_INTERRUPTIBLE); @@ -198,8 +199,10 @@ static void mtd_blktrans_request(struct request_queue *rq) if (!dev) while ((req = blk_fetch_request(rq)) != NULL) __blk_end_request_all(req, -ENODEV); - else + else { + dev->bg_stop = true; wake_up_process(dev->thread); + } } static int blktrans_open(struct block_device *bdev, fmode_t mode) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 78205ac2b10..a92054e945e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -121,23 +121,6 @@ config MTD_NAND_OMAP2 help Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. -config MTD_NAND_OMAP_PREFETCH - bool "GPMC prefetch support for NAND Flash device" - depends on MTD_NAND_OMAP2 - default y - help - The NAND device can be accessed for Read/Write using GPMC PREFETCH engine - to improve the performance. - -config MTD_NAND_OMAP_PREFETCH_DMA - depends on MTD_NAND_OMAP_PREFETCH - bool "DMA mode" - default n - help - The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode - or in DMA interrupt mode. - Say y for DMA mode or MPU mode will be used - config MTD_NAND_IDS tristate @@ -491,7 +474,7 @@ config MTD_NAND_MPC5121_NFC config MTD_NAND_MXC tristate "MXC NAND support" - depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51 + depends on IMX_HAVE_PLATFORM_MXC_NAND help This enables the driver for the NAND flash controller on the MXC processors. diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index efdcca94ce5..073ee026a17 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -217,8 +217,7 @@ err: return ret; } -static int __devinit fun_probe(struct platform_device *ofdev, - const struct of_device_id *ofid) +static int __devinit fun_probe(struct platform_device *ofdev) { struct fsl_upm_nand *fun; struct resource io_res; @@ -360,7 +359,7 @@ static const struct of_device_id of_fun_match[] = { }; MODULE_DEVICE_TABLE(of, of_fun_match); -static struct of_platform_driver of_fun_driver = { +static struct platform_driver of_fun_driver = { .driver = { .name = "fsl,upm-nand", .owner = THIS_MODULE, @@ -372,13 +371,13 @@ static struct of_platform_driver of_fun_driver = { static int __init fun_module_init(void) { - return of_register_platform_driver(&of_fun_driver); + return platform_driver_register(&of_fun_driver); } module_init(fun_module_init); static void __exit fun_module_exit(void) { - of_unregister_platform_driver(&of_fun_driver); + platform_driver_unregister(&of_fun_driver); } module_exit(fun_module_exit); diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index ddaf0011aa8..0b81b5b499d 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -651,8 +651,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) iounmap(prv->csreg); } -static int __devinit mpc5121_nfc_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit mpc5121_nfc_probe(struct platform_device *op) { struct device_node *rootnode, *dn = op->dev.of_node; struct device *dev = &op->dev; @@ -892,7 +891,7 @@ static struct of_device_id mpc5121_nfc_match[] __devinitdata = { {}, }; -static struct of_platform_driver mpc5121_nfc_driver = { +static struct platform_driver mpc5121_nfc_driver = { .probe = mpc5121_nfc_probe, .remove = __devexit_p(mpc5121_nfc_remove), .driver = { @@ -904,14 +903,14 @@ static struct of_platform_driver mpc5121_nfc_driver = { static int __init mpc5121_nfc_init(void) { - return of_register_platform_driver(&mpc5121_nfc_driver); + return platform_driver_register(&mpc5121_nfc_driver); } module_init(mpc5121_nfc_init); static void __exit mpc5121_nfc_cleanup(void) { - of_unregister_platform_driver(&mpc5121_nfc_driver); + platform_driver_unregister(&mpc5121_nfc_driver); } module_exit(mpc5121_nfc_cleanup); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b7d5a5b9a54..42a95fb4150 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -747,9 +747,8 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) /* * MXC NANDFC can only perform full page+spare or * spare-only read/write. When the upper layers - * layers perform a read/write buf operation, - * we will used the saved column address to index into - * the full page. + * perform a read/write buf operation, the saved column + * address is used to index into the full page. */ host->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index c9ae0a5023b..bbe6d451290 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -225,8 +225,7 @@ err: return ret; } -static int __devinit ndfc_probe(struct platform_device *ofdev, - const struct of_device_id *match) +static int __devinit ndfc_probe(struct platform_device *ofdev) { struct ndfc_controller *ndfc = &ndfc_ctrl; const __be32 *reg; @@ -292,7 +291,7 @@ static const struct of_device_id ndfc_match[] = { }; MODULE_DEVICE_TABLE(of, ndfc_match); -static struct of_platform_driver ndfc_driver = { +static struct platform_driver ndfc_driver = { .driver = { .name = "ndfc", .owner = THIS_MODULE, @@ -304,12 +303,12 @@ static struct of_platform_driver ndfc_driver = { static int __init ndfc_nand_init(void) { - return of_register_platform_driver(&ndfc_driver); + return platform_driver_register(&ndfc_driver); } static void __exit ndfc_nand_exit(void) { - of_unregister_platform_driver(&ndfc_driver); + platform_driver_unregister(&ndfc_driver); } module_init(ndfc_nand_init); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index d7a4e2550b1..da9a351c9d7 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -11,6 +11,7 @@ #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/sched.h> #include <linux/mtd/mtd.h> @@ -24,6 +25,7 @@ #include <plat/nand.h> #define DRIVER_NAME "omap2-nand" +#define OMAP_NAND_TIMEOUT_MS 5000 #define NAND_Ecc_P1e (1 << 0) #define NAND_Ecc_P2e (1 << 1) @@ -96,26 +98,19 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; #endif -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH -static int use_prefetch = 1; - -/* "modprobe ... use_prefetch=0" etc */ -module_param(use_prefetch, bool, 0); -MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); - -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA -static int use_dma = 1; +/* oob info generated runtime depending on ecc algorithm and layout selected */ +static struct nand_ecclayout omap_oobinfo; +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks + */ +static uint8_t scan_ff_pattern[] = { 0xff }; +static struct nand_bbt_descr bb_descrip_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; -/* "modprobe ... use_dma=0" etc */ -module_param(use_dma, bool, 0); -MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); -#else -static const int use_dma; -#endif -#else -const int use_prefetch; -static const int use_dma; -#endif struct omap_nand_info { struct nand_hw_control controller; @@ -129,6 +124,13 @@ struct omap_nand_info { unsigned long phys_base; struct completion comp; int dma_ch; + int gpmc_irq; + enum { + OMAP_NAND_IO_READ = 0, /* read */ + OMAP_NAND_IO_WRITE, /* write */ + } iomode; + u_char *buf; + int buf_len; }; /** @@ -256,7 +258,8 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) } /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); + ret = gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0); if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) @@ -288,9 +291,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t pref_count = 0, w_count = 0; + uint32_t w_count = 0; int i = 0, ret = 0; u16 *p; + unsigned long tim, limit; /* take care of subpage writes */ if (len % 2 != 0) { @@ -300,7 +304,8 @@ static void omap_write_buf_pref(struct mtd_info *mtd, } /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); + ret = gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1); if (ret) { /* PFPW engine is busy, use cpu copy method */ if (info->nand.options & NAND_BUSWIDTH_16) @@ -316,15 +321,17 @@ static void omap_write_buf_pref(struct mtd_info *mtd, iowrite16(*p++, info->nand.IO_ADDR_W); } /* wait for data to flushed-out before reset the prefetch */ - do { - pref_count = gpmc_read_status(GPMC_PREFETCH_COUNT); - } while (pref_count); + tim = 0; + limit = (loops_per_jiffy * + msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit)) + cpu_relax(); + /* disable and stop the PFPW engine */ gpmc_prefetch_reset(info->gpmc_cs); } } -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA /* * omap_nand_dma_cb: callback on the completion of dma transfer * @lch: logical channel @@ -348,14 +355,15 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t prefetch_status = 0; enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; dma_addr_t dma_addr; int ret; + unsigned long tim, limit; - /* The fifo depth is 64 bytes. We have a sync at each frame and frame - * length is 64 bytes. + /* The fifo depth is 64 bytes max. + * But configure the FIFO-threahold to 32 to get a sync at each frame + * and frame length is 32 bytes. */ int buf_len = len >> 6; @@ -396,9 +404,10 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); } /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); + ret = gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); if (ret) - /* PFPW engine is busy, use cpu copy methode */ + /* PFPW engine is busy, use cpu copy method */ goto out_copy; init_completion(&info->comp); @@ -407,10 +416,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, /* setup and start DMA using dma_addr */ wait_for_completion(&info->comp); + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit)) + cpu_relax(); - do { - prefetch_status = gpmc_read_status(GPMC_PREFETCH_COUNT); - } while (prefetch_status); /* disable and stop the PFPW engine */ gpmc_prefetch_reset(info->gpmc_cs); @@ -426,14 +436,6 @@ out_copy: : omap_write_buf8(mtd, (u_char *) addr, len); return 0; } -#else -static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} -static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, - unsigned int len, int is_write) -{ - return 0; -} -#endif /** * omap_read_buf_dma_pref - read data from NAND controller into buffer @@ -466,6 +468,157 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd, omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); } +/* + * omap_nand_irq - GMPC irq handler + * @this_irq: gpmc irq number + * @dev: omap_nand_info structure pointer is passed here + */ +static irqreturn_t omap_nand_irq(int this_irq, void *dev) +{ + struct omap_nand_info *info = (struct omap_nand_info *) dev; + u32 bytes; + u32 irq_stat; + + irq_stat = gpmc_read_status(GPMC_GET_IRQ_STATUS); + bytes = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); + bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ + if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ + if (irq_stat & 0x2) + goto done; + + if (info->buf_len && (info->buf_len < bytes)) + bytes = info->buf_len; + else if (!info->buf_len) + bytes = 0; + iowrite32_rep(info->nand.IO_ADDR_W, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + info->buf_len -= bytes; + + } else { + ioread32_rep(info->nand.IO_ADDR_R, + (u32 *)info->buf, bytes >> 2); + info->buf = info->buf + bytes; + + if (irq_stat & 0x2) + goto done; + } + gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat); + + return IRQ_HANDLED; + +done: + complete(&info->comp); + /* disable irq */ + gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ, 0); + + /* clear status */ + gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat); + + return IRQ_HANDLED; +} + +/* + * omap_read_buf_irq_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + int ret = 0; + + if (len <= mtd->oobsize) { + omap_read_buf_pref(mtd, buf, len); + return; + } + + info->iomode = OMAP_NAND_IO_READ; + info->buf = buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer */ + ret = gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + /* enable irq */ + gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ, + (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT)); + + /* waiting for read to complete */ + wait_for_completion(&info->comp); + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(info->gpmc_cs); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len); + else + omap_read_buf8(mtd, buf, len); +} + +/* + * omap_write_buf_irq_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_irq_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + int ret = 0; + unsigned long tim, limit; + + if (len <= mtd->oobsize) { + omap_write_buf_pref(mtd, buf, len); + return; + } + + info->iomode = OMAP_NAND_IO_WRITE; + info->buf = (u_char *) buf; + init_completion(&info->comp); + + /* configure and start prefetch transfer : size=24 */ + ret = gpmc_prefetch_enable(info->gpmc_cs, + (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1); + if (ret) + /* PFPW engine is busy, use cpu copy method */ + goto out_copy; + + info->buf_len = len; + /* enable irq */ + gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ, + (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT)); + + /* waiting for write to complete */ + wait_for_completion(&info->comp); + /* wait for data to flushed-out before reset the prefetch */ + tim = 0; + limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); + while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit)) + cpu_relax(); + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(info->gpmc_cs); + return; + +out_copy: + if (info->nand.options & NAND_BUSWIDTH_16) + omap_write_buf16(mtd, buf, len); + else + omap_write_buf8(mtd, buf, len); +} + /** * omap_verify_buf - Verify chip data against buffer * @mtd: MTD device structure @@ -487,8 +640,6 @@ static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) return 0; } -#ifdef CONFIG_MTD_NAND_OMAP_HWECC - /** * gen_true_ecc - This function will generate true ECC value * @ecc_buf: buffer to store ecc code @@ -716,8 +867,6 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size); } -#endif - /** * omap_wait - wait until the command is done * @mtd: MTD device structure @@ -787,6 +936,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) struct omap_nand_info *info; struct omap_nand_platform_data *pdata; int err; + int i, offset; pdata = pdev->dev.platform_data; if (pdata == NULL) { @@ -812,7 +962,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->mtd.name = dev_name(&pdev->dev); info->mtd.owner = THIS_MODULE; - info->nand.options |= pdata->devsize ? NAND_BUSWIDTH_16 : 0; + info->nand.options = pdata->devsize; info->nand.options |= NAND_SKIP_BBTSCAN; /* NAND write protect off */ @@ -850,28 +1000,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.chip_delay = 50; } - if (use_prefetch) { - + switch (pdata->xfer_type) { + case NAND_OMAP_PREFETCH_POLLED: info->nand.read_buf = omap_read_buf_pref; info->nand.write_buf = omap_write_buf_pref; - if (use_dma) { - err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", - omap_nand_dma_cb, &info->comp, &info->dma_ch); - if (err < 0) { - info->dma_ch = -1; - printk(KERN_WARNING "DMA request failed." - " Non-dma data transfer mode\n"); - } else { - omap_set_dma_dest_burst_mode(info->dma_ch, - OMAP_DMA_DATA_BURST_16); - omap_set_dma_src_burst_mode(info->dma_ch, - OMAP_DMA_DATA_BURST_16); - - info->nand.read_buf = omap_read_buf_dma_pref; - info->nand.write_buf = omap_write_buf_dma_pref; - } - } - } else { + break; + + case NAND_OMAP_POLLED: if (info->nand.options & NAND_BUSWIDTH_16) { info->nand.read_buf = omap_read_buf16; info->nand.write_buf = omap_write_buf16; @@ -879,20 +1014,61 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.read_buf = omap_read_buf8; info->nand.write_buf = omap_write_buf8; } + break; + + case NAND_OMAP_PREFETCH_DMA: + err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", + omap_nand_dma_cb, &info->comp, &info->dma_ch); + if (err < 0) { + info->dma_ch = -1; + dev_err(&pdev->dev, "DMA request failed!\n"); + goto out_release_mem_region; + } else { + omap_set_dma_dest_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + omap_set_dma_src_burst_mode(info->dma_ch, + OMAP_DMA_DATA_BURST_16); + + info->nand.read_buf = omap_read_buf_dma_pref; + info->nand.write_buf = omap_write_buf_dma_pref; + } + break; + + case NAND_OMAP_PREFETCH_IRQ: + err = request_irq(pdata->gpmc_irq, + omap_nand_irq, IRQF_SHARED, "gpmc-nand", info); + if (err) { + dev_err(&pdev->dev, "requesting irq(%d) error:%d", + pdata->gpmc_irq, err); + goto out_release_mem_region; + } else { + info->gpmc_irq = pdata->gpmc_irq; + info->nand.read_buf = omap_read_buf_irq_pref; + info->nand.write_buf = omap_write_buf_irq_pref; + } + break; + + default: + dev_err(&pdev->dev, + "xfer_type(%d) not supported!\n", pdata->xfer_type); + err = -EINVAL; + goto out_release_mem_region; } - info->nand.verify_buf = omap_verify_buf; -#ifdef CONFIG_MTD_NAND_OMAP_HWECC - info->nand.ecc.bytes = 3; - info->nand.ecc.size = 512; - info->nand.ecc.calculate = omap_calculate_ecc; - info->nand.ecc.hwctl = omap_enable_hwecc; - info->nand.ecc.correct = omap_correct_data; - info->nand.ecc.mode = NAND_ECC_HW; + info->nand.verify_buf = omap_verify_buf; -#else - info->nand.ecc.mode = NAND_ECC_SOFT; -#endif + /* selsect the ecc type */ + if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) + info->nand.ecc.mode = NAND_ECC_SOFT; + else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { + info->nand.ecc.bytes = 3; + info->nand.ecc.size = 512; + info->nand.ecc.calculate = omap_calculate_ecc; + info->nand.ecc.hwctl = omap_enable_hwecc; + info->nand.ecc.correct = omap_correct_data; + info->nand.ecc.mode = NAND_ECC_HW; + } /* DIP switches on some boards change between 8 and 16 bit * bus widths for flash. Try the other width if the first try fails. @@ -905,6 +1081,26 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) } } + /* rom code layout */ + if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { + + if (info->nand.options & NAND_BUSWIDTH_16) + offset = 2; + else { + offset = 1; + info->nand.badblock_pattern = &bb_descrip_flashbased; + } + omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16); + for (i = 0; i < omap_oobinfo.eccbytes; i++) + omap_oobinfo.eccpos[i] = i+offset; + + omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; + omap_oobinfo.oobfree->length = info->mtd.oobsize - + (offset + omap_oobinfo.eccbytes); + + info->nand.ecc.layout = &omap_oobinfo; + } + #ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) @@ -934,9 +1130,12 @@ static int omap_nand_remove(struct platform_device *pdev) mtd); platform_set_drvdata(pdev, NULL); - if (use_dma) + if (info->dma_ch != -1) omap_free_dma(info->dma_ch); + if |