From d5ce2b6592c49935462cba7317fa67fe8ee474ec Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:03 +0530 Subject: omap3630: nand: fix device size to work in polled mode zoom3 and 3630-sdp having the x16 nand device. This patch configure gpmc as x16 and select the currect function in driver for polled mode (without prefetch enable) transfer. Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- drivers/mtd/nand/omap2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 15682ec8530e..7c04cd6fb7aa 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -804,7 +804,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 */ -- cgit v1.2.3 From 1b0b323c70cd5fdca967d89ed3a03dfebd84ada7 Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:04 +0530 Subject: omap3: nand: configurable transfer type per board nand transfer type (sDMA, Polled, prefetch) can be select from board file, enabling all transfer type in driver, by default. this helps in multi-omap build and to select different transfer type for different board. Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- arch/arm/plat-omap/include/plat/nand.h | 7 +++ drivers/mtd/nand/Kconfig | 17 ------ drivers/mtd/nand/omap2.c | 94 ++++++++++++---------------------- 3 files changed, 41 insertions(+), 77 deletions(-) (limited to 'drivers/mtd') diff --git a/arch/arm/plat-omap/include/plat/nand.h b/arch/arm/plat-omap/include/plat/nand.h index 6562cd082bb1..78c0bdb98c18 100644 --- a/arch/arm/plat-omap/include/plat/nand.h +++ b/arch/arm/plat-omap/include/plat/nand.h @@ -10,6 +10,12 @@ #include +enum nand_io { + NAND_OMAP_PREFETCH_POLLED = 0, /* prefetch polled mode, default */ + NAND_OMAP_POLLED, /* polled mode, without prefetch */ + NAND_OMAP_PREFETCH_DMA /* prefetch enabled sDMA mode */ +}; + struct omap_nand_platform_data { unsigned int options; int cs; @@ -20,6 +26,7 @@ struct omap_nand_platform_data { int (*nand_setup)(void); int (*dev_ready)(struct omap_nand_platform_data *); int dma_channel; + enum nand_io xfer_type; unsigned long phys_base; int devsize; }; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c89592239bc7..178e2006063d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -106,23 +106,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 diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7c04cd6fb7aa..60bac8e6e9fa 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -96,27 +96,6 @@ 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; - -/* "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; struct omap_nand_platform_data *pdata; @@ -324,7 +303,6 @@ static void omap_write_buf_pref(struct mtd_info *mtd, } } -#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA /* * omap_nand_dma_cb: callback on the completion of dma transfer * @lch: logical channel @@ -426,14 +404,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 @@ -842,28 +812,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; @@ -871,7 +826,33 @@ 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; + + 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 @@ -897,6 +878,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) } } + #ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) @@ -926,7 +908,7 @@ 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); /* Release NAND device, its internal structures and partitions */ @@ -947,16 +929,8 @@ static struct platform_driver omap_nand_driver = { static int __init omap_nand_init(void) { - printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); + pr_info("%s driver initializing\n", DRIVER_NAME); - /* This check is required if driver is being - * loaded run time as a module - */ - if ((1 == use_dma) && (0 == use_prefetch)) { - printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " - "without use_prefetch'. Prefetch will not be" - " used in either mode (mpu or dma)\n"); - } return platform_driver_register(&omap_nand_driver); } -- cgit v1.2.3 From 4e070376165a9b7f245fada77645b81352c6ec78 Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:06 +0530 Subject: omap3: nand: prefetch in irq mode support This patch enable prefetch-irq mode for nand transfer(read, write) Signed-off-by: Vimal Singh Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-flash.c | 2 + arch/arm/plat-omap/include/plat/nand.h | 4 +- drivers/mtd/nand/omap2.c | 198 +++++++++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c index f6b72533c089..19645095d597 100644 --- a/arch/arm/mach-omap2/board-flash.c +++ b/arch/arm/mach-omap2/board-flash.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -147,6 +148,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, board_nand_data.nr_parts = nr_parts; board_nand_data.devsize = nand_type; + board_nand_data.gpmc_irq = OMAP_GPMC_IRQ_BASE + cs; gpmc_nand_init(&board_nand_data); } #else diff --git a/arch/arm/plat-omap/include/plat/nand.h b/arch/arm/plat-omap/include/plat/nand.h index 78c0bdb98c18..ae5e05383031 100644 --- a/arch/arm/plat-omap/include/plat/nand.h +++ b/arch/arm/plat-omap/include/plat/nand.h @@ -13,7 +13,8 @@ enum nand_io { NAND_OMAP_PREFETCH_POLLED = 0, /* prefetch polled mode, default */ NAND_OMAP_POLLED, /* polled mode, without prefetch */ - NAND_OMAP_PREFETCH_DMA /* prefetch enabled sDMA mode */ + NAND_OMAP_PREFETCH_DMA, /* prefetch enabled sDMA mode */ + NAND_OMAP_PREFETCH_IRQ /* prefetch enabled irq mode */ }; struct omap_nand_platform_data { @@ -26,6 +27,7 @@ struct omap_nand_platform_data { int (*nand_setup)(void); int (*dev_ready)(struct omap_nand_platform_data *); int dma_channel; + int gpmc_irq; enum nand_io xfer_type; unsigned long phys_base; int devsize; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 60bac8e6e9fa..fbe841467175 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #define DRIVER_NAME "omap2-nand" +#define OMAP_NAND_TIMEOUT_MS 5000 #define NAND_Ecc_P1e (1 << 0) #define NAND_Ecc_P2e (1 << 1) @@ -108,6 +110,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; }; /** @@ -267,9 +276,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) { @@ -295,9 +305,12 @@ 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); } @@ -326,11 +339,11 @@ 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. @@ -376,7 +389,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, /* configure and start prefetch transfer */ ret = gpmc_prefetch_enable(info->gpmc_cs, 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); @@ -385,10 +398,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); @@ -436,6 +450,155 @@ 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, 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 */ + ret = gpmc_prefetch_enable(info->gpmc_cs, 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 @@ -846,6 +1009,20 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) } 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); @@ -911,6 +1088,9 @@ static int omap_nand_remove(struct platform_device *pdev) if (info->dma_ch != -1) omap_free_dma(info->dma_ch); + if (info->gpmc_irq) + free_irq(info->gpmc_irq, info); + /* Release NAND device, its internal structures and partitions */ nand_release(&info->mtd); iounmap(info->nand.IO_ADDR_R); -- cgit v1.2.3 From 317379a975c07fe63bc4f86dabd668df96ff3df2 Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:07 +0530 Subject: omap3: nand: configurable fifo threshold to gain the throughput Configure the FIFO THREASHOLD value different for read and write to keep busy both filling and to drain out of FIFO at reading and writing. Signed-off-by: Vimal Singh Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc.c | 11 +++++++---- arch/arm/plat-omap/include/plat/gpmc.h | 5 ++++- drivers/mtd/nand/omap2.c | 22 ++++++++++++++-------- 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 382dea83e4f0..674174365f78 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -60,7 +60,6 @@ #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ #define GPMC_SECTION_SHIFT 28 /* 128 MB */ -#define PREFETCH_FIFOTHRESHOLD (0x40 << 8) #define CS_NUM_SHIFT 24 #define ENABLE_PREFETCH (0x1 << 7) #define DMA_MPU_MODE 2 @@ -606,15 +605,19 @@ EXPORT_SYMBOL(gpmc_nand_write); /** * gpmc_prefetch_enable - configures and starts prefetch transfer * @cs: cs (chip select) number + * @fifo_th: fifo threshold to be used for read/ write * @dma_mode: dma mode enable (1) or disable (0) * @u32_count: number of bytes to be transferred * @is_write: prefetch read(0) or write post(1) mode */ -int gpmc_prefetch_enable(int cs, int dma_mode, +int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode, unsigned int u32_count, int is_write) { - if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) { + pr_err("gpmc: fifo threshold is not supported\n"); + return -1; + } else if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { /* Set the amount of bytes to be prefetched */ gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count); @@ -622,7 +625,7 @@ int gpmc_prefetch_enable(int cs, int dma_mode, * enable the engine. Set which cs is has requested for. */ gpmc_write_reg(GPMC_PREFETCH_CONFIG1, ((cs << CS_NUM_SHIFT) | - PREFETCH_FIFOTHRESHOLD | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | (dma_mode << DMA_MPU_MODE) | (0x1 & is_write))); diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index 9c060da0a873..a2434639063d 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h @@ -83,6 +83,9 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) + /* * Note that all values in this struct are in nanoseconds except sync_clk * (which is in picoseconds), while the register values are in gpmc_fck cycles. @@ -134,7 +137,7 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); extern void gpmc_cs_free(int cs); extern int gpmc_cs_set_reserved(int cs, int reserved); extern int gpmc_cs_reserved(int cs); -extern int gpmc_prefetch_enable(int cs, int dma_mode, +extern int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode, unsigned int u32_count, int is_write); extern int gpmc_prefetch_reset(int cs); extern void omap3_gpmc_save_context(void); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index fbe841467175..f1648fd5924a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -244,7 +244,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) @@ -289,7 +290,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) @@ -345,8 +347,9 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *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; @@ -387,7 +390,8 @@ 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 method */ goto out_copy; @@ -522,7 +526,8 @@ static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len) init_completion(&info->comp); /* 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/2, 0x0, len, 0x0); if (ret) /* PFPW engine is busy, use cpu copy method */ goto out_copy; @@ -569,8 +574,9 @@ static void omap_write_buf_irq_pref(struct mtd_info *mtd, info->buf = (u_char *) buf; init_completion(&info->comp); - /* configure and start prefetch transfer */ - ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); + /* 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; -- cgit v1.2.3 From f3d73f362d689a1d044e77964864f0a8ea0217f3 Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:08 +0530 Subject: omap3: nand: ecc layout select from board file This patch makes it possible to select sw or hw (different layout options) ecc scheme supported by omap nand driver. Signed-off-by: Vimal Singh Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-flash.c | 1 + arch/arm/plat-omap/include/plat/gpmc.h | 6 ++++++ arch/arm/plat-omap/include/plat/nand.h | 2 ++ drivers/mtd/nand/omap2.c | 26 +++++++++++--------------- 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers/mtd') diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c index 19645095d597..a76819812019 100644 --- a/arch/arm/mach-omap2/board-flash.c +++ b/arch/arm/mach-omap2/board-flash.c @@ -148,6 +148,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, board_nand_data.nr_parts = nr_parts; board_nand_data.devsize = nand_type; + board_nand_data.ecc_opt = OMAP_ECC_HAMMING_CODE_DEFAULT; board_nand_data.gpmc_irq = OMAP_GPMC_IRQ_BASE + cs; gpmc_nand_init(&board_nand_data); } diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index a2434639063d..773351bc25a2 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h @@ -86,6 +86,12 @@ #define PREFETCH_FIFOTHRESHOLD_MAX 0x40 #define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +enum omap_ecc { + /* 1-bit ecc: stored at end of spare area */ + OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */ + OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ +}; + /* * Note that all values in this struct are in nanoseconds except sync_clk * (which is in picoseconds), while the register values are in gpmc_fck cycles. diff --git a/arch/arm/plat-omap/include/plat/nand.h b/arch/arm/plat-omap/include/plat/nand.h index ae5e05383031..d86d1ecf0068 100644 --- a/arch/arm/plat-omap/include/plat/nand.h +++ b/arch/arm/plat-omap/include/plat/nand.h @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ +#include #include enum nand_io { @@ -31,6 +32,7 @@ struct omap_nand_platform_data { enum nand_io xfer_type; unsigned long phys_base; int devsize; + enum omap_ecc ecc_opt; }; /* minimum size for IO mapping */ diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index f1648fd5924a..6d4a42e39380 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -626,8 +626,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 @@ -847,8 +845,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 @@ -1038,17 +1034,17 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) 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; - -#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) { + 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. -- cgit v1.2.3 From f040d33253b2daf6f305fc35fca2399047ced028 Mon Sep 17 00:00:00 2001 From: Sukumar Ghorai Date: Fri, 28 Jan 2011 15:42:09 +0530 Subject: omap3: nand: making ecc layout as compatible with romcode ecc This patch overrides nand ecc layout and bad block descriptor (for 8-bit device) to support hw ecc in romcode layout. So as to have in sync with ecc layout throughout; i.e. x-loader, u-boot and kernel. This enables to flash x-loader, u-boot, kernel, FS images from kernel itself and compatiable with other tools. This patch does not enables this feature by default and need to pass from board file to enable for any board. Signed-off-by: Vimal Singh Signed-off-by: Sukumar Ghorai Signed-off-by: Tony Lindgren --- arch/arm/plat-omap/include/plat/gpmc.h | 2 ++ drivers/mtd/nand/omap2.c | 37 +++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index 773351bc25a2..12b316165037 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h @@ -90,6 +90,8 @@ enum omap_ecc { /* 1-bit ecc: stored at end of spare area */ OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */ OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ + /* 1-bit ecc: stored at begining of spare area as romcode */ + OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ }; /* diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 6d4a42e39380..4e33972ad17a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -98,6 +98,20 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; #endif +/* 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, +}; + + struct omap_nand_info { struct nand_hw_control controller; struct omap_nand_platform_data *pdata; @@ -914,6 +928,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) { @@ -1037,7 +1052,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) /* 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) { + 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; @@ -1057,6 +1073,25 @@ 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); -- cgit v1.2.3 From 3ad2d861362031dac8b2bba78a8f4c575300948f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Feb 2011 10:46:59 +0200 Subject: OMAP: OneNAND: determine frequency in one place OneNAND frequency is determined when calculating GPMC timings. Return that value instead of determining it again in the OMAP OneNAND driver. Signed-off-by: Adrian Hunter Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-onenand.c | 10 ++++++---- arch/arm/plat-omap/include/plat/onenand.h | 2 +- drivers/mtd/onenand/omap2.c | 28 +++++----------------------- 3 files changed, 12 insertions(+), 28 deletions(-) (limited to 'drivers/mtd') diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 3a4307b8f7cf..46786a606e90 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -123,7 +123,7 @@ static void set_onenand_cfg(void __iomem *onenand_base, int latency, static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, void __iomem *onenand_base, - int freq) + int *freq_ptr) { struct gpmc_timings t; const int t_cer = 15; @@ -136,7 +136,7 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency; int first_time = 0, hf = 0, vhf = 0, sync_read = 0, sync_write = 0; int err, ticks_cez; - int cs = cfg->cs; + int cs = cfg->cs, freq = *freq_ptr; u32 reg; if (cfg->flags & ONENAND_SYNC_READ) { @@ -330,16 +330,18 @@ static int omap2_onenand_set_sync_mode(struct omap_onenand_platform_data *cfg, set_onenand_cfg(onenand_base, latency, sync_read, sync_write, hf, vhf); + *freq_ptr = freq; + return 0; } -static int gpmc_onenand_setup(void __iomem *onenand_base, int freq) +static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) { struct device *dev = &gpmc_onenand_device.dev; /* Set sync timings in GPMC */ if (omap2_onenand_set_sync_mode(gpmc_onenand_data, onenand_base, - freq) < 0) { + freq_ptr) < 0) { dev_err(dev, "Unable to set synchronous mode\n"); return -EINVAL; } diff --git a/arch/arm/plat-omap/include/plat/onenand.h b/arch/arm/plat-omap/include/plat/onenand.h index affe87e9ece7..86118dc3f19a 100644 --- a/arch/arm/plat-omap/include/plat/onenand.h +++ b/arch/arm/plat-omap/include/plat/onenand.h @@ -20,7 +20,7 @@ struct omap_onenand_platform_data { int gpio_irq; struct mtd_partition *parts; int nr_parts; - int (*onenand_setup)(void __iomem *, int freq); + int (*onenand_setup)(void __iomem *, int *freq_ptr); int dma_channel; u8 flags; u8 regulator_can_sleep; diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index ac31f461cc1c..3e1bb9568823 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -63,7 +63,7 @@ struct omap2_onenand { struct completion dma_done; int dma_channel; int freq; - int (*setup)(void __iomem *base, int freq); + int (*setup)(void __iomem *base, int *freq_ptr); struct regulator *regulator; }; @@ -581,7 +581,7 @@ static int __adjust_timing(struct device *dev, void *data) /* DMA is not in use so this is all that is needed */ /* Revisit for OMAP3! */ - ret = c->setup(c->onenand.base, c->freq); + ret = c->setup(c->onenand.base, &c->freq); return ret; } @@ -673,7 +673,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) } if (pdata->onenand_setup != NULL) { - r = pdata->onenand_setup(c->onenand.base, c->freq); + r = pdata->onenand_setup(c->onenand.base, &c->freq); if (r < 0) { dev_err(&pdev->dev, "Onenand platform setup failed: " "%d\n", r); @@ -718,8 +718,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual " - "base %p\n", c->gpmc_cs, c->phys_base, - c->onenand.base); + "base %p, freq %d MHz\n", c->gpmc_cs, c->phys_base, + c->onenand.base, c->freq); c->pdev = pdev; c->mtd.name = dev_name(&pdev->dev); @@ -754,24 +754,6 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_regulator; - switch ((c->onenand.version_id >> 4) & 0xf) { - case 0: - c->freq = 40; - break; - case 1: - c->freq = 54; - break; - case 2: - c->freq = 66; - break; - case 3: - c->freq = 83; - break; - case 4: - c->freq = 104; - break; - } - #ifdef CONFIG_MTD_PARTITIONS r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0); if (r > 0) -- cgit v1.2.3 From c497dd5594ed3ef97bc563b07e8c050618f745a3 Mon Sep 17 00:00:00 2001 From: Roman Tereshonkov Date: Mon, 7 Feb 2011 10:47:01 +0200 Subject: mtd: OneNAND: OMAP2: increase multiblock erase verify timeout The current multiblock erase verify read timeout 100us is the maximum for none-error case. If errors happen during multibock erase then the specification recommends to run multiblock erase verify command with maximum timeout 10ms (see specs. for KFM4G16Q2A and KFN8G16Q2A). For the most common non-error case we wait 100us in udelay polling loop. In case of timeout the interrupt mode is used to wait for the command end. Signed-off-by: Roman Tereshonkov Acked-by: Artem Bityutskiy Signed-off-by: Tony Lindgren --- drivers/mtd/onenand/omap2.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 3e1bb9568823..ec26399e3cf2 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) wait_err("controller error", state, ctrl, intr); return -EIO; } - if ((intr & intr_flags) != intr_flags) { - wait_err("timeout", state, ctrl, intr); - return -EIO; - } - return 0; + if ((intr & intr_flags) == intr_flags) + return 0; + /* Continue in wait for interrupt branch */ } if (state != FL_READING) { -- cgit v1.2.3