[PATCH 3/5] mtd OMAP NAND: Use prefetch engine

Sascha Hauer s.hauer at pengutronix.de
Thu Aug 2 06:10:14 EDT 2012


Use the prefetch engine to improve NAND performance. The howto
is derived from the Kernel. Unlike the kernel we do not make
the access mode configurable.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/mach-omap/gpmc.c              |   61 +++++++++++++++++++
 arch/arm/mach-omap/include/mach/gpmc.h |   16 ++++-
 drivers/mtd/nand/nand_omap_gpmc.c      |  100 ++++++++++++++++++++++++++++++++
 3 files changed, 174 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-omap/gpmc.c b/arch/arm/mach-omap/gpmc.c
index 92a8ae0..5b4daaf 100644
--- a/arch/arm/mach-omap/gpmc.c
+++ b/arch/arm/mach-omap/gpmc.c
@@ -31,6 +31,7 @@
 #include <clock.h>
 #include <init.h>
 #include <io.h>
+#include <errno.h>
 #include <mach/silicon.h>
 #include <mach/gpmc.h>
 #include <mach/sys_info.h>
@@ -125,3 +126,63 @@ void gpmc_cs_config(char cs, struct gpmc_config *config)
 	mdelay(1);		/* Settling time */
 }
 EXPORT_SYMBOL(gpmc_cs_config);
+
+#define CS_NUM_SHIFT		24
+#define ENABLE_PREFETCH		(0x1 << 7)
+#define DMA_MPU_MODE		2
+
+/**
+ * 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 fifo_th, int dma_mode,
+				unsigned int u32_count, int is_write)
+{
+	if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) {
+		pr_err("gpmc: fifo threshold is not supported\n");
+		return -EINVAL;
+	}
+
+	/* Set the amount of bytes to be prefetched */
+	writel(u32_count, GPMC_REG(PREFETCH_CONFIG2));
+
+	/* Set dma/mpu mode, the prefetch read / post write and
+	 * enable the engine. Set which cs is has requested for.
+	 */
+	writel(((cs << CS_NUM_SHIFT) | PREFETCH_FIFOTHRESHOLD(fifo_th) |
+				ENABLE_PREFETCH | (dma_mode << DMA_MPU_MODE) |
+				(0x1 & is_write)),
+			GPMC_REG(PREFETCH_CONFIG1));
+
+	/*  Start the prefetch engine */
+	writel(0x1, GPMC_REG(PREFETCH_CONTROL));
+
+	return 0;
+}
+EXPORT_SYMBOL(gpmc_prefetch_enable);
+
+/**
+ * gpmc_prefetch_reset - disables and stops the prefetch engine
+ */
+int gpmc_prefetch_reset(int cs)
+{
+	u32 config1;
+
+	/* check if the same module/cs is trying to reset */
+	config1 = readl(GPMC_REG(PREFETCH_CONFIG1));
+	if (((config1 >> CS_NUM_SHIFT) & 0x7) != cs)
+		return -EINVAL;
+
+	/* Stop the PFPW engine */
+	writel(0x0, GPMC_REG(PREFETCH_CONTROL));
+
+	/* Reset/disable the PFPW engine */
+	writel(0x0, GPMC_REG(PREFETCH_CONFIG1));
+
+	return 0;
+}
+EXPORT_SYMBOL(gpmc_prefetch_reset);
diff --git a/arch/arm/mach-omap/include/mach/gpmc.h b/arch/arm/mach-omap/include/mach/gpmc.h
index 3ddc5f5..ed4e82d 100644
--- a/arch/arm/mach-omap/include/mach/gpmc.h
+++ b/arch/arm/mach-omap/include/mach/gpmc.h
@@ -51,9 +51,10 @@
 #define GPMC_TIMEOUT_CONTROL	(0x40)
 #define GPMC_CFG		(0x50)
 #define GPMC_STATUS		(0x54)
-#define GPMC_PREFETCH_CONFIG_1	(0x1E0)
-#define GPMC_PREFETCH_CONFIG_2	(0x1E4)
-#define GPMC_PREFETCH_CTRL	(0x1EC)
+#define GPMC_PREFETCH_CONFIG1	(0x1E0)
+#define GPMC_PREFETCH_CONFIG2	(0x1E4)
+#define GPMC_PREFETCH_CONTROL	(0x1EC)
+#define GPMC_PREFETCH_STATUS	(0x1f0)
 #define GPMC_ECC_CONFIG		(0x1F4)
 #define GPMC_ECC_CONTROL	(0x1F8)
 #define GPMC_ECC_SIZE_CONFIG	(0x1FC)
@@ -138,6 +139,15 @@
 #define GPMC_SIZE_32M		0x0E
 #define GPMC_SIZE_16M		0x0F
 
+#define PREFETCH_FIFOTHRESHOLD_MAX	0x40
+#define PREFETCH_FIFOTHRESHOLD(val)	((val) << 8)
+#define GPMC_PREFETCH_STATUS_FIFO_CNT(val)	((val >> 24) & 0x7F)
+#define GPMC_PREFETCH_STATUS_COUNT(val)	(val & 0x00003fff)
+
+int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode,
+				unsigned int u32_count, int is_write);
+int gpmc_prefetch_reset(int cs);
+
 #define NAND_WP_BIT		0x00000010
 
 #ifndef __ASSEMBLY__
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 08008e7..39b4dd9 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -112,6 +112,7 @@ struct gpmc_nand_info {
 	unsigned inuse:1;
 	unsigned char ecc_parity_pairs;
 	enum gpmc_ecc_mode ecc_mode;
+	void *cs_base;
 };
 
 /* Typical BOOTROM oob layouts-requires hwecc **/
@@ -583,6 +584,101 @@ static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chi
 	return bytes;
 }
 
+/**
+ * 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 gpmc_nand_info *info = container_of(mtd,
+						struct gpmc_nand_info, minfo);
+	u32 r_count = 0;
+	u32 *p = (u32 *)buf;
+
+	/* take care of subpage reads */
+	if (len % 4) {
+		if (info->nand.options & NAND_BUSWIDTH_16)
+			readsw(info->cs_base, buf, (len % 4) / 2);
+		else
+			readsb(info->cs_base, buf, len % 4);
+		p = (u32 *) (buf + len % 4);
+		len -= len % 4;
+	}
+
+	/* configure and start prefetch transfer */
+	gpmc_prefetch_enable(info->gpmc_cs,
+			PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0);
+
+	do {
+		r_count = readl(info->gpmc_base + GPMC_PREFETCH_STATUS);
+		r_count = GPMC_PREFETCH_STATUS_FIFO_CNT(r_count);
+		r_count = r_count >> 2;
+		readsl(info->cs_base, p, r_count);
+		p += r_count;
+		len -= r_count << 2;
+	} while (len);
+
+	/* disable and stop the PFPW engine */
+	gpmc_prefetch_reset(info->gpmc_cs);
+}
+
+/**
+ * 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 gpmc_nand_info *info = container_of(mtd,
+						struct gpmc_nand_info, minfo);
+	u32 w_count = 0;
+	u_char *buf1 = (u_char *)buf;
+	u32 *p32 = (u32 *)buf;
+	uint64_t start;
+
+	/* take care of subpage writes */
+	while (len % 4 != 0) {
+		writeb(*buf, info->nand.IO_ADDR_W);
+		buf1++;
+		p32 = (u32 *)buf1;
+		len--;
+	}
+
+	/*  configure and start prefetch transfer */
+	gpmc_prefetch_enable(info->gpmc_cs,
+			PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1);
+
+	while (len >= 0) {
+		w_count = readl(info->gpmc_base + GPMC_PREFETCH_STATUS);
+		w_count = GPMC_PREFETCH_STATUS_FIFO_CNT(w_count);
+		w_count = w_count >> 2;
+		writesl(info->cs_base, p32, w_count);
+		p32 += w_count;
+		len -= w_count << 2;
+	}
+
+	/* wait for data to flushed-out before reset the prefetch */
+	start = get_time_ns();
+	while (1) {
+		u32 regval, status;
+		regval = readl(info->gpmc_base + GPMC_PREFETCH_STATUS);
+		status = GPMC_PREFETCH_STATUS_COUNT(regval);
+		if (!status)
+			break;
+		if (is_timeout(start, 100 * MSECOND)) {
+			dev_err(mtd->dev, "prefetch flush timed out\n");
+			break;
+		}
+	}
+
+	/* disable and stop the PFPW engine */
+	gpmc_prefetch_reset(info->gpmc_cs);
+}
+
 /*
  * read a page with the ecc layout used by the OMAP4 romcode. The
  * romcode expects an unusual ecc layout (f = free, e = ecc):
@@ -833,6 +929,7 @@ static int gpmc_nand_probe(struct device_d *pdev)
 	oinfo->gpmc_command = (void *)(cs_base + GPMC_CS_NAND_COMMAND);
 	oinfo->gpmc_address = (void *)(cs_base + GPMC_CS_NAND_ADDRESS);
 	oinfo->gpmc_data = (void *)(cs_base + GPMC_CS_NAND_DATA);
+	oinfo->cs_base = (void *)pdata->nand_cfg->base;
 	dev_dbg(pdev, "GPMC base=0x%p cmd=0x%p address=0x%p data=0x%p cs_base=0x%p\n",
 		oinfo->gpmc_base, oinfo->gpmc_command, oinfo->gpmc_address,
 		oinfo->gpmc_data, cs_base);
@@ -933,6 +1030,9 @@ static int gpmc_nand_probe(struct device_d *pdev)
 		goto out_release_mem;
 	}
 
+	nand->read_buf   = omap_read_buf_pref;
+	nand->write_buf  = omap_write_buf_pref;
+
 	nand->options |= NAND_SKIP_BBTSCAN;
 
 	dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0);
-- 
1.7.10.4




More information about the barebox mailing list