mtd: OMAP: Enable GPMC prefetch mode
authorDaniel Mack <zonque@gmail.com>
Wed, 25 Jun 2014 12:43:32 +0000 (14:43 +0200)
committerTom Rini <trini@ti.com>
Tue, 13 Jan 2015 16:51:23 +0000 (11:51 -0500)
Enable GPMC's prefetch feature for NAND access. This speeds up NAND read
access a lot by pre-fetching contents in the background and reading them
through the FIFO address.

The current implementation has two limitations:

 a) it only works in 8-bit mode
 b) it only supports read access

Both is easily fixable by someone who has hardware to implement it.

Note that U-Boot code uses non word-aligned buffers to read data into, and
request read lengths that are not multiples of 4, so both partial buffers
(head and tail) have to be addressed.

Tested on AM335x hardware.

Tested-by: Guido Martínez <guido@vanguardiasur.com.ar>
Reviewed-by: Guido Martínez <guido@vanguardiasur.com.ar>
Signed-off-by: Daniel Mack <zonque@gmail.com>
[trini: Make apply again, use 'cs' fix pointed out by Guido]
Signed-off-by: Tom Rini <trini@ti.com>
doc/README.nand
drivers/mtd/nand/omap_gpmc.c
include/linux/mtd/omap_gpmc.h

index e29188f1ec0cd4dd97a38ddb4bb7ed85645f83fe..dee0e00a61708825f347104a4acf9a8d4de9bb9c 100644 (file)
@@ -304,6 +304,11 @@ Platform specific options
                Thus BCH16 can be supported on 4K page NAND.
 
 
+    CONFIG_NAND_OMAP_GPMC_PREFETCH
+       On OMAP platforms that use the GPMC controller
+       (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that
+       uses the prefetch mode to speed up read operations.
+
 NOTE:
 =====
 
index 459904d81c21a2356e353c642ef065151006c257..fc64f4814484e8052410f4769c60240ae67dc510 100644 (file)
@@ -441,6 +441,115 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
        return (err) ? err : error_count;
 }
 
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+
+#define PREFETCH_CONFIG1_CS_SHIFT      24
+#define PREFETCH_FIFOTHRESHOLD_MAX     0x40
+#define PREFETCH_FIFOTHRESHOLD(val)    ((val) << 8)
+#define PREFETCH_STATUS_COUNT(val)     (val & 0x00003fff)
+#define PREFETCH_STATUS_FIFO_CNT(val)  ((val >> 24) & 0x7F)
+#define ENABLE_PREFETCH                        (1 << 7)
+
+/**
+ * omap_prefetch_enable - configures and starts prefetch transfer
+ * @fifo_th: fifo threshold to be used for read/ write
+ * @count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ * @cs: chip select to use
+ */
+static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs)
+{
+       uint32_t val;
+
+       if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
+               return -EINVAL;
+
+       if (readl(&gpmc_cfg->prefetch_control))
+               return -EBUSY;
+
+       /* Set the amount of bytes to be prefetched */
+       writel(count, &gpmc_cfg->prefetch_config2);
+
+       val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) |
+               PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH;
+       writel(val, &gpmc_cfg->prefetch_config1);
+
+       /*  Start the prefetch engine */
+       writel(1, &gpmc_cfg->prefetch_control);
+
+       return 0;
+}
+
+/**
+ * omap_prefetch_reset - disables and stops the prefetch engine
+ */
+static void omap_prefetch_reset(void)
+{
+       writel(0, &gpmc_cfg->prefetch_control);
+       writel(0, &gpmc_cfg->prefetch_config1);
+}
+
+static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len)
+{
+       int ret;
+       uint32_t cnt;
+       struct omap_nand_info *info = chip->priv;
+
+       ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs);
+       if (ret < 0)
+               return ret;
+
+       do {
+               int i;
+
+               cnt = readl(&gpmc_cfg->prefetch_status);
+               cnt = PREFETCH_STATUS_FIFO_CNT(cnt);
+
+               for (i = 0; i < cnt / 4; i++) {
+                       *buf++ = readl(CONFIG_SYS_NAND_BASE);
+                       len -= 4;
+               }
+       } while (len);
+
+       omap_prefetch_reset();
+
+       return 0;
+}
+
+static void omap_nand_read_prefetch8(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int ret;
+       uint32_t head, tail;
+       struct nand_chip *chip = mtd->priv;
+
+       /*
+        * If the destination buffer is unaligned, start with reading
+        * the overlap byte-wise.
+        */
+       head = ((uint32_t) buf) % 4;
+       if (head) {
+               nand_read_buf(mtd, buf, head);
+               buf += head;
+               len -= head;
+       }
+
+       /*
+        * Only transfer multiples of 4 bytes in a pre-fetched fashion.
+        * If there's a residue, care for it byte-wise afterwards.
+        */
+       tail = len % 4;
+
+       ret = __read_prefetch_aligned(chip, (uint32_t *) buf, len - tail);
+       if (ret < 0) {
+               /* fallback in case the prefetch engine is busy */
+               nand_read_buf(mtd, buf, len);
+       } else if (tail) {
+               buf += len - tail;
+               nand_read_buf(mtd, buf, tail);
+       }
+}
+#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */
+
 /**
  * omap_read_page_bch - hardware ecc based page read function
  * @mtd:       mtd info structure
@@ -880,11 +989,12 @@ int board_nand_init(struct nand_chip *nand)
        if (err)
                return err;
 
-#ifdef CONFIG_SPL_BUILD
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+       /* TODO: Implement for 16-bit bus width */
        if (nand->options & NAND_BUSWIDTH_16)
                nand->read_buf = nand_read_buf16;
        else
-               nand->read_buf = nand_read_buf;
+               nand->read_buf = omap_nand_read_prefetch8;
 #endif
 
        nand->dev_ready = omap_dev_ready;
index 9a8658257ff9953b5c410713a36d1315e6e29059..6cbae450221ffc9bec9d63b190bf3cfd77963010 100644 (file)
@@ -66,7 +66,11 @@ struct gpmc {
        u32 status;             /* 0x54 */
        u8 res5[0x8];           /* 0x58 */
        struct gpmc_cs cs[8];   /* 0x60, 0x90, .. */
-       u8 res6[0x14];          /* 0x1E0 */
+       u32 prefetch_config1;   /* 0x1E0 */
+       u32 prefetch_config2;   /* 0x1E4 */
+       u32 res6;               /* 0x1E8 */
+       u32 prefetch_control;   /* 0x1EC */
+       u32 prefetch_status;    /* 0x1F0 */
        u32 ecc_config;         /* 0x1F4 */
        u32 ecc_control;        /* 0x1F8 */
        u32 ecc_size_config;    /* 0x1FC */