[U-Boot] [PATCH v2 1/2] mtd: OMAP: Enable GPMC prefetch mode

Guido Martínez guido at vanguardiasur.com.ar
Fri Dec 19 17:27:57 CET 2014


Hi Daniel,

While trying to enable GPMC prefetch myself I ran into your patch and
tested it. Here's some comments.

On Wed, Jun 25, 2014 at 02:43:32PM +0200, Daniel Mack wrote:
> 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.
> 
> Signed-off-by: Daniel Mack <zonque at gmail.com>
> ---
>  doc/README.nand               |   5 ++
>  drivers/mtd/nand/omap_gpmc.c  | 115 +++++++++++++++++++++++++++++++++++++++++-
>  include/linux/mtd/omap_gpmc.h |   6 ++-
>  3 files changed, 123 insertions(+), 3 deletions(-)
> 
> diff --git a/doc/README.nand b/doc/README.nand
> index 70cf768..6459f2a 100644
> --- a/doc/README.nand
> +++ b/doc/README.nand
> @@ -292,6 +292,11 @@ Platform specific options
>  		Thus BCH16 can be supported on 4K page NAND.
>  
>  
> +    CONFIG_NAND_OMAP_PREFETCH
This doesn't match the actual config in omap_gpmc.c

> +	On OMAP platforms that use the GPMC controller (CONFIG_NAND_OMAP_GPMC),
> +	this options enables the code that uses the prefetch mode to speed up
> +	read operations.
> +
>  NOTE:
>  =====
>  
> diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
> index 1acf06b..e2d57bd 100644
> --- a/drivers/mtd/nand/omap_gpmc.c
> +++ b/drivers/mtd/nand/omap_gpmc.c
> @@ -446,6 +446,113 @@ 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
> + */
> +static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write)
> +{
> +	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) |
On a current U-boot "cs" doesn't exist. I think you might want to pass a
"struct omap_nand_info *" and use info->cs.

I fixed this last bit, and tested it. It results in 2.5x speed
improvements on a Micron NAND. So, thanks :) !

If you resend, you have my Reviewed-by and Tested-by.

> +		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;
> +
> +	ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0);
> +	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
> @@ -883,11 +990,15 @@ 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
> +
> +#ifdef CONFIG_SPL_BUILD
>  	nand->dev_ready = omap_spl_dev_ready;
>  #endif
>  
> diff --git a/include/linux/mtd/omap_gpmc.h b/include/linux/mtd/omap_gpmc.h
> index 9a86582..6cbae45 100644
> --- a/include/linux/mtd/omap_gpmc.h
> +++ b/include/linux/mtd/omap_gpmc.h
> @@ -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 */

-- 
Guido Martínez, VanguardiaSur
www.vanguardiasur.com.ar


More information about the U-Boot mailing list