Hi,
Do you have any plans to use the prefetch engine's feature to
synchronise to ready/busy pin? (GPMC_PREFETCH_CONFIG1's SYNCHROMODE=1)
That way we could avoid the busy-wait for the flash to be ready.
- Juha
On Thu, Nov 6, 2008 at 5:57 AM, vimal singh <[EMAIL PROTECTED]> wrote:
> This patch adds prefetch support to access nand flash in both mpu and dma
> mode.
> This patch also adds 8-bit nand support (omap_read/write_buf8).
> Prefetch can be used for both 8- and 16-bit devices.
>
> Signed-off-by: Vimal Singh <vimalsingh at ti.com>
> ---
> API's to access 8- and 16-bit NAND devices (omap_read/wirte_buf8/16)can be
> removed after sometime (once sufficient amount of testing is done for both
> kind
> of devices).
>
> vimal
> ---
> arch/arm/mach-omap2/gpmc.c | 95 +++++++++++
> arch/arm/plat-omap/include/mach/gpmc.h | 4
> drivers/mtd/nand/Kconfig | 17 ++
> drivers/mtd/nand/omap2.c | 277
> ++++++++++++++++++++++++++++++++-
> 4 files changed, 386 insertions(+), 7 deletions(-)
>
> Index: omapkernel/arch/arm/mach-omap2/gpmc.c
> ===================================================================
> --- omapkernel.orig/arch/arm/mach-omap2/gpmc.c 2008-11-06 12:49:34.000000000
> +0530
> +++ omapkernel/arch/arm/mach-omap2/gpmc.c 2008-11-06 12:49:57.000000000
> +0530
> @@ -54,6 +54,12 @@
> #define GPMC_CHUNK_SHIFT 24 /* 16 MB */
> #define GPMC_SECTION_SHIFT 28 /* 128 MB */
>
> +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
> +#define CS_NUM_SHIFT 24
> +#define ENABLE_PREFETCH 7
> +#define DMA_MPU_MODE 2
> +#endif
> +
> #ifdef CONFIG_OMAP3_PM
> /*
> * Structure to save/restore gpmc context
> @@ -407,6 +413,92 @@
> }
> EXPORT_SYMBOL(gpmc_cs_free);
>
> +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
> +/*
> + * gpmc_prefetch_init - configures default configuration for prefetch engine
> + */
> +static void gpmc_prefetch_init(void)
> +{
> + /* Setting the default threshold to 64 */
> + gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x40 << 8);
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG2, 0x0);
> +}
> +
> +/*
> + * gpmc_prefetch_start - configures and starts prefetch transfer
> + * @cs - nand cs (chip select) number
> + * @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
> + */
> +void gpmc_prefetch_start(int cs, int dma_mode,
> + unsigned int u32_count, int is_write)
> +{
> + uint32_t prefetch_config1;
> + if (is_write) {
> + /* Set the amount of bytes to be prefetched */
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
> +
> + /* Set dma/mpu mode, the post write and enable the engine
> + * Set which cs is using the post write
> + */
> + prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
> + prefetch_config1 |= ((cs << CS_NUM_SHIFT) |
> + (dma_mode << DMA_MPU_MODE) |
> + (1 << ENABLE_PREFETCH) | 0x1);
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
> + } else {
> + /* Set the amount of bytes to be prefetched */
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
> +
> + /* Set dma/mpu mode, the prefech read and enable the engine
> + * Set which cs is using the prefetch
> + */
> + prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
> + prefetch_config1 |= (((cs << CS_NUM_SHIFT) |
> + (dma_mode << DMA_MPU_MODE) |
> + (1 << ENABLE_PREFETCH)) & ~0x1);
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
> + }
> + /* Start the prefetch engine */
> + gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
> +}
> +EXPORT_SYMBOL(gpmc_prefetch_start);
> +
> +/*
> + * gpmc_prefetch_stop - disables and stops the prefetch engine
> + */
> +void gpmc_prefetch_stop(void)
> +{
> + uint32_t prefetch_config1;
> + /* stop the PFPW engine */
> + gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
> +
> + /* Disable the PFPW engine */
> + prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);
> + prefetch_config1 &= ~((0x07 << CS_NUM_SHIFT) |
> + (1 << ENABLE_PREFETCH) |
> + (1 << DMA_MPU_MODE) | 0x1);
> + gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
> +}
> +EXPORT_SYMBOL(gpmc_prefetch_stop);
> +
> +/*
> + * gpmc_prefetch_status - reads prefetch status of engine
> + */
> +int gpmc_prefetch_status(void)
> +{
> + return gpmc_read_reg(GPMC_PREFETCH_STATUS);
> +}
> +EXPORT_SYMBOL(gpmc_prefetch_status);
> +#else
> +int gpmc_prefetch_status(void) {return 0; }
> +void gpmc_prefetch_stop(void) {}
> +void gpmc_prefetch_start(int cs, int dma_mode, unsigned int u32_count,
> + int is_write) {}
> +#endif
> +
> static void __init gpmc_mem_init(void)
> {
> int cs;
> @@ -474,6 +566,9 @@
> gpmc_freq_cfg.freq_cfg = NULL;
> gpmc_freq_cfg.total_no_of_freq = 0;
> #endif
> +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
> + gpmc_prefetch_init();
> +#endif
> gpmc_mem_init();
> }
>
> Index: omapkernel/arch/arm/plat-omap/include/mach/gpmc.h
> ===================================================================
> --- omapkernel.orig/arch/arm/plat-omap/include/mach/gpmc.h 2008-11-06
> 12:49:34.000000000 +0530
> +++ omapkernel/arch/arm/plat-omap/include/mach/gpmc.h 2008-11-06
> 12:49:57.000000000 +0530
> @@ -149,6 +149,10 @@
> 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 void gpmc_prefetch_start(int cs, int dma_mode,
> + unsigned int u32_count, int is_write);
> +extern void gpmc_prefetch_stop(void);
> +extern int gpmc_prefetch_status(void);
> extern void __init gpmc_init(void);
>
> #endif
> Index: omapkernel/drivers/mtd/nand/Kconfig
> ===================================================================
> --- omapkernel.orig/drivers/mtd/nand/Kconfig 2008-11-06 12:49:34.000000000
> +0530
> +++ omapkernel/drivers/mtd/nand/Kconfig 2008-11-06 12:49:57.000000000 +0530
> @@ -82,6 +82,23 @@
> The ECC compuatation for the data to be written/read can be either
> by
> software or omap has Hw ecc engine which calculates it.
>
> +config MTD_NAND_OMAP_PREFETCH
> + bool "GPMC prefetch support for NAND Flash device"
> + depends on MTD_NAND && 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_OMAP
> tristate "NAND Flash device on OMAP H3/H2/P2 boards"
> depends on ARM && ARCH_OMAP1 && MTD_NAND && (MACH_OMAP_H2 ||
> MACH_OMAP_H3 ||
> MACH_OMAP_PERSEUS2)
> Index: omapkernel/drivers/mtd/nand/omap2.c
> ===================================================================
> --- omapkernel.orig/drivers/mtd/nand/omap2.c 2008-11-06 12:49:34.000000000
> +0530
> +++ omapkernel/drivers/mtd/nand/omap2.c 2008-11-06 18:57:52.000000000 +0530
> @@ -111,6 +111,27 @@
> 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
> +const int use_dma;
> +#endif
> +#else
> +const int use_prefetch;
> +const int use_dma;
> +#endif
> +
> struct omap_nand_info {
> struct nand_hw_control controller;
> struct omap_nand_platform_data *pdata;
> @@ -123,6 +144,9 @@
> unsigned long phys_base;
> void __iomem *gpmc_cs_baseaddr;
> void __iomem *gpmc_baseaddr;
> + void __iomem *nand_pref_fifo_add;
> + struct completion comp;
> + int dma_ch;
> };
>
> /*
> @@ -186,6 +210,124 @@
> __raw_writeb(cmd, info->nand.IO_ADDR_W);
> }
>
> +#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
> +/*
> + * omap_nand_dma_cb: callback on the completion of dma transfer
> + * @lch: logical channel
> + * @ch_satuts: channel status
> + * @data: pointer to completion data structure
> + */
> +static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
> +{
> + complete((struct completion *) data);
> +}
> +
> +/*
> + * omap_nand_dma_transfer: configer and start dma transfer
> + * @mtd: MTD device structure
> + * @addr: virtual address in RAM of source/destination
> + * @len: number of data bytes to be transferred
> + * @is_write: flag for read/write operation
> + */
> +static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
> + unsigned int len, int is_write)
> +{
> + 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;
> +
> + /* The fifo depth is 64 bytes. We have a sync at each frame and frame
> + * length is 64 bytes.
> + */
> + int buf_len = len/64;
> +
> + dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
> +
> + if (is_write) {
> + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> + info->phys_base, 0, 0);
> + omap_set_dma_dest_burst_mode(info->dma_ch,
> OMAP_DMA_DATA_BURST_16);
> + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> + dma_addr, 0, 0);
> + omap_set_dma_src_burst_mode(info->dma_ch, OMAP_DMA_DATA_BURST_16);
> + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
> + 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
> + OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
> + } else {
> + omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> + info->phys_base, 0, 0);
> + omap_set_dma_src_burst_mode(info->dma_ch, OMAP_DMA_DATA_BURST_16);
> + omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> + dma_addr, 0, 0);
> + omap_set_dma_dest_burst_mode(info->dma_ch,
> OMAP_DMA_DATA_BURST_16);
> + omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
> + 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
> + OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
> + }
> + /* configure and start prefetch transfer */
> + gpmc_prefetch_start(info->gpmc_cs, 0x1, len, is_write);
> + init_completion(&info->comp);
> +
> + omap_start_dma(info->dma_ch);
> +
> + /* setup and start DMA using dma_addr */
> + wait_for_completion(&info->comp);
> +
> + while (0x3fff & (prefetch_status = gpmc_prefetch_status()))
> + ;
> + /* disable and stop the PFPW engine */
> + gpmc_prefetch_stop();
> +
> + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
> + 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_buf8 - 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_buf8(struct mtd_info *mtd, u_char *buf, int len)
> +{
> + struct nand_chip *nand = mtd->priv;
> + u_char *p = (u_char *)buf;
> +
> + while (len--)
> + *p++ = __raw_readb(nand->IO_ADDR_R);
> +}
> +
> +/*
> + * omap_write_buf8 - write buffer to NAND controller
> + * @mtd: MTD device structure
> + * @buf: data buffer
> + * @len: number of bytes to write
> + */
> +static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
> +{
> + struct omap_nand_info *info = container_of(mtd,
> + struct omap_nand_info, mtd);
> + u_char *p = (u_char *)buf;
> +
> + while (len--) {
> + writeb(*p++, info->nand.IO_ADDR_W);
> +
> + while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
> + GPMC_STATUS) &
> GPMC_BUF_FULL));
> + }
> +}
> +
> /*
> * omap_read_buf16 - read data from NAND controller into buffer
> * @mtd: MTD device structure
> @@ -221,6 +363,94 @@
> GPMC_STATUS) & GPMC_BUF_FULL));
> }
> }
> +
> +/*
> + * omap_read_buf_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_pref(struct mtd_info *mtd, u_char *buf, int len)
> +{
> + struct omap_nand_info *info = container_of(mtd,
> + struct omap_nand_info, mtd);
> + uint32_t prefetch_status = 0, read_count = 0;
> + u16 *p = (u16 *)buf;
> +
> + if ((use_dma && len <= mtd->oobsize) || (!use_dma)) {
> +
> + /* take care of subpage reads */
> + if (len % 2 != 0) {
> + *buf++ = __raw_readb(info->nand.IO_ADDR_R);
> + p = (u16 *) buf;
> + len--;
> + }
> +
> + /* configure and start prefetch transfer */
> + gpmc_prefetch_start(info->gpmc_cs, 0x0, len, 0x0);
> +
> + do {
> + prefetch_status = gpmc_prefetch_status();
> + read_count = ((prefetch_status >> 24) & 0x7F) >> 1;
> + __raw_readsw(info->nand_pref_fifo_add, p,
> + read_count);
> + p += read_count;
> + len -= read_count << 1;
> + } while (len);
> +
> + /* disable and stop the PFPW engine */
> + gpmc_prefetch_stop();
> + } else if (use_dma) {
> + if (info->dma_ch >= 0)
> + /* start transfer in DMA mode */
> + omap_nand_dma_transfer(mtd, p, len, 0x0);
> + }
> +}
> +
> +/*
> + * omap_write_buf_pref - write buffer to NAND controller
> + * @mtd: MTD device structure
> + * @buf: data buffer
> + * @len: number of bytes to write
> + */
> +static void omap_write_buf_pref(struct mtd_info *mtd,
> + const u_char *buf, int len)
> +{
> + struct omap_nand_info *info = container_of(mtd,
> + struct omap_nand_info, mtd);
> + uint32_t prefetch_status = 0, write_count = 0;
> + int i = 0;
> + u16 *p = (u16 *) buf;
> +
> + if ((use_dma && len <= mtd->oobsize) || (!use_dma)) {
> +
> + /* take care of subpage writes */
> + if (len % 2 != 0) {
> + writeb(*buf, info->nand.IO_ADDR_R);
> + p = (u16 *)(buf + 1);
> + len--;
> + }
> +
> + /* configure and start prefetch transfer */
> + gpmc_prefetch_start(info->gpmc_cs, 0x0, len, 0x1);
> +
> + prefetch_status = gpmc_prefetch_status();
> + while (prefetch_status & 0x3FFF) {
> + write_count = ((prefetch_status >> 24) & 0x7F) >> 1;
> + for (i = 0; (i < write_count) && len; i++, len -= 2)
> + __raw_writew(*p++, info->nand_pref_fifo_add);
> + prefetch_status = gpmc_prefetch_status();
> + }
> +
> + /* disable and stop the PFPW engine */
> + gpmc_prefetch_stop();
> + } else if (use_dma) {
> + if (info->dma_ch >= 0)
> + /* start transfer in DMA mode */
> + omap_nand_dma_transfer(mtd, p, len, 0x1);
> + }
> +}
> +
> /*
> * omap_verify_buf - Verify chip data against buffer
> * @mtd: MTD device structure
> @@ -635,17 +865,24 @@
> err = -ENOMEM;
> goto out_release_mem_region;
> }
> + if (use_prefetch) {
> + /* copy the virtual address of nand base for fifo access */
> + info->nand_pref_fifo_add = info->nand.IO_ADDR_R;
> + 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");
> + }
> + }
> + }
> info->nand.controller = &info->controller;
>
> info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
> info->nand.cmd_ctrl = omap_hwcontrol;
>
> - /* REVISIT: only supports 16-bit NAND flash */
> -
> - info->nand.read_buf = omap_read_buf16;
> - info->nand.write_buf = omap_write_buf16;
> - info->nand.verify_buf = omap_verify_buf;
> -
> /*
> * If RDY/BSY line is connected to OMAP then use the omap ready
> funcrtion
> * and the generic nand_wait function which reads the status register
> @@ -666,6 +903,20 @@
> == 0x1000)
> info->nand.options |= NAND_BUSWIDTH_16;
>
> + if (use_prefetch) {
> + info->nand.read_buf = omap_read_buf_pref;
> + info->nand.write_buf = omap_write_buf_pref;
> + } else {
> + if (info->nand.options & NAND_BUSWIDTH_16) {
> + info->nand.read_buf = omap_read_buf16;
> + info->nand.write_buf = omap_write_buf16;
> + } else {
> + info->nand.read_buf = omap_read_buf8;
> + info->nand.write_buf = omap_write_buf8;
> + }
> + }
> + info->nand.verify_buf = omap_verify_buf;
> +
> #ifdef CONFIG_MTD_NAND_OMAP_HWECC
> info->nand.ecc.bytes = 3;
> info->nand.ecc.size = 512;
> @@ -721,9 +972,12 @@
> struct omap_nand_info *info = mtd->priv;
>
> platform_set_drvdata(pdev, NULL);
> + if (use_dma)
> + omap_free_dma(info->dma_ch);
> +
> /* Release NAND device, its internal structures and partitions */
> nand_release(&info->mtd);
> - iounmap(info->nand.IO_ADDR_R);
> + iounmap(info->nand_pref_fifo_add);
> kfree(&info->mtd);
> return 0;
> }
> @@ -741,6 +995,15 @@
> static int __init omap_nand_init(void)
> {
> printk(KERN_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);
> }
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [EMAIL PROTECTED]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Madness takes it's toll. Please have exact change.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html