diff mbox

[U-Boot,1/6] nand: denali: add Denali NAND driver for SPL

Message ID 1404469158-22703-2-git-send-email-yamada.m@jp.panasonic.com
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Commit Message

Masahiro Yamada July 4, 2014, 10:19 a.m. UTC
The SPL-mode driver for Denali(Cadence) NAND Flash Memory Controller IP.

This driver requires two CONFIG macros:
 - CONFIG_NAND_DENALI
     Define to enable this driver.
 - CONFIG_SYS_NAND_BAD_BLOCK_POS
     Specify bad block mark position in the oob space. Typically 0.

Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Cc: Chin Liang See <clsee@altera.com>
Cc: Scott Wood <scottwood@freescale.com>
---

 drivers/mtd/nand/Makefile     |   1 +
 drivers/mtd/nand/denali_spl.c | 201 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 202 insertions(+)
 create mode 100644 drivers/mtd/nand/denali_spl.c

Comments

Marek Vasut July 4, 2014, 2:34 p.m. UTC | #1
On Friday, July 04, 2014 at 12:19:13 PM, Masahiro Yamada wrote:
> The SPL-mode driver for Denali(Cadence) NAND Flash Memory Controller IP.
> 
> This driver requires two CONFIG macros:
>  - CONFIG_NAND_DENALI
>      Define to enable this driver.
>  - CONFIG_SYS_NAND_BAD_BLOCK_POS
>      Specify bad block mark position in the oob space. Typically 0.
> 
> Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
> Cc: Chin Liang See <clsee@altera.com>
> Cc: Scott Wood <scottwood@freescale.com>

[...]

> +static void read_data_from_flash_mem(uint8_t *buf, int len)
> +{
> +	int i;
> +	uint32_t *buf32;
> +
> +	/* transfer the data from the flash */
> +	buf32 = (uint32_t *)buf;
> +	for (i = 0; i < len / 4; i++)
> +		*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);

Won't this trigger unaligned access if $buf is not aligned to 4-byte boundary ?

> +}
> +
> +int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
> +{
> +	uint32_t addr, cmd;
> +	static uint32_t page_count = 1;
> +
> +	writel(ecc_en, denali_flash_reg + ECC_ENABLE);
> +
> +	/* clear all bits of intr_status. */
> +	writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
> +
> +	addr = BANK(flash_bank) | page;
> +
> +	/* setup the acccess type */
> +	cmd = MODE_10 | addr;
> +	index_addr(cmd, access_type);
> +
> +	/* setup the pipeline command */
> +	index_addr(cmd, 0x2000 | page_count);

Magic value 0x2000 should be fixed here.

> +	cmd = MODE_01 | addr;
> +	writel(cmd, denali_flash_mem + INDEX_CTRL_REG);

Somehow, eventually, this should be migrated to "struct" based register access 
instead of such offset computation. If you feel like doing it, that'd be nice 
;-)

> +	return wait_for_irq(INTR_STATUS__LOAD_COMP);
> +}
[...]

Thanks!
Masahiro Yamada July 10, 2014, 10:06 a.m. UTC | #2
Hi Marek,


On Fri, 4 Jul 2014 16:34:11 +0200
Marek Vasut <marex@denx.de> wrote:

> 
> > +static void read_data_from_flash_mem(uint8_t *buf, int len)
> > +{
> > +	int i;
> > +	uint32_t *buf32;
> > +
> > +	/* transfer the data from the flash */
> > +	buf32 = (uint32_t *)buf;
> > +	for (i = 0; i < len / 4; i++)
> > +		*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
> 
> Won't this trigger unaligned access if $buf is not aligned to 4-byte boundary ?


Thanks for catching this.

Actually, it would seldom happen because the aligned load address is generally given.

But I will add a sanity check here in v2, just in case.



> > +
> > +	/* setup the pipeline command */
> > +	index_addr(cmd, 0x2000 | page_count);
> 
> Magic value 0x2000 should be fixed here.


Actually, this part is the same as that of the normal Denali driver
by Chin Liang See
http://patchwork.ozlabs.org/patch/357717/

Moreover, it is the same as in Linux Kernel.

I understand what you mean, but I guess we can excuse here.




> > +	cmd = MODE_01 | addr;
> > +	writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
> 
> Somehow, eventually, this should be migrated to "struct" based register access 
> instead of such offset computation. If you feel like doing it, that'd be nice 
> ;-)

Dito.
I am following the coding style of the normal Denali driver and the Linux one.

We might perhaps do that migration as the separated task in future, but not now.



Best Regards
Masahiro Yamada
Marek Vasut July 10, 2014, 10:10 a.m. UTC | #3
On Thursday, July 10, 2014 at 12:06:20 PM, Masahiro Yamada wrote:
> Hi Marek,

Hi!

> On Fri, 4 Jul 2014 16:34:11 +0200
> 
> Marek Vasut <marex@denx.de> wrote:
> > > +static void read_data_from_flash_mem(uint8_t *buf, int len)
> > > +{
> > > +	int i;
> > > +	uint32_t *buf32;
> > > +
> > > +	/* transfer the data from the flash */
> > > +	buf32 = (uint32_t *)buf;
> > > +	for (i = 0; i < len / 4; i++)
> > > +		*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
> > 
> > Won't this trigger unaligned access if $buf is not aligned to 4-byte
> > boundary ?
> 
> Thanks for catching this.
> 
> Actually, it would seldom happen because the aligned load address is
> generally given.
> 
> But I will add a sanity check here in v2, just in case.

Or just use put_unaligned() ?

> > > +
> > > +	/* setup the pipeline command */
> > > +	index_addr(cmd, 0x2000 | page_count);
> > 
> > Magic value 0x2000 should be fixed here.
> 
> Actually, this part is the same as that of the normal Denali driver
> by Chin Liang See
> http://patchwork.ozlabs.org/patch/357717/
> 
> Moreover, it is the same as in Linux Kernel.
> 
> I understand what you mean, but I guess we can excuse here.

OK, I see. Is there any chance to fix this in Linux maybe eventually?

[...]

Best regards,
Marek Vasut
Masahiro Yamada July 10, 2014, 10:52 a.m. UTC | #4
Hi Marek,



On Thu, 10 Jul 2014 12:10:25 +0200
Marek Vasut <marex@denx.de> wrote:

> 
> > On Fri, 4 Jul 2014 16:34:11 +0200
> > 
> > Marek Vasut <marex@denx.de> wrote:
> > > > +static void read_data_from_flash_mem(uint8_t *buf, int len)
> > > > +{
> > > > +	int i;
> > > > +	uint32_t *buf32;
> > > > +
> > > > +	/* transfer the data from the flash */
> > > > +	buf32 = (uint32_t *)buf;
> > > > +	for (i = 0; i < len / 4; i++)
> > > > +		*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
> > > 
> > > Won't this trigger unaligned access if $buf is not aligned to 4-byte
> > > boundary ?
> > 
> > Thanks for catching this.
> > 
> > Actually, it would seldom happen because the aligned load address is
> > generally given.
> > 
> > But I will add a sanity check here in v2, just in case.
> 
> Or just use put_unaligned() ?


Oh, year!  put_unaligned() would be better.
Thanks!


> > > > +
> > > > +	/* setup the pipeline command */
> > > > +	index_addr(cmd, 0x2000 | page_count);
> > > 
> > > Magic value 0x2000 should be fixed here.
> > 
> > Actually, this part is the same as that of the normal Denali driver
> > by Chin Liang See
> > http://patchwork.ozlabs.org/patch/357717/
> > 
> > Moreover, it is the same as in Linux Kernel.
> > 
> > I understand what you mean, but I guess we can excuse here.
> 
> OK, I see. Is there any chance to fix this in Linux maybe eventually?
> 

OK.
I will send a patch to linux-mtd ML
to use  "#define PIPELINE_ACCESS    2000"






Best Regards
Masahiro Yamada
Marek Vasut July 10, 2014, 11:30 a.m. UTC | #5
On Thursday, July 10, 2014 at 12:52:19 PM, Masahiro Yamada wrote:
> Hi Marek,

Hi!

[...]

> > Or just use put_unaligned() ?
> 
> Oh, year!  put_unaligned() would be better.
> Thanks!
> 
> > > > > +
> > > > > +	/* setup the pipeline command */
> > > > > +	index_addr(cmd, 0x2000 | page_count);
> > > > 
> > > > Magic value 0x2000 should be fixed here.
> > > 
> > > Actually, this part is the same as that of the normal Denali driver
> > > by Chin Liang See
> > > http://patchwork.ozlabs.org/patch/357717/
> > > 
> > > Moreover, it is the same as in Linux Kernel.
> > > 
> > > I understand what you mean, but I guess we can excuse here.
> > 
> > OK, I see. Is there any chance to fix this in Linux maybe eventually?
> 
> OK.
> I will send a patch to linux-mtd ML
> to use  "#define PIPELINE_ACCESS    2000"

Ack on both, thank you !

Best regards,
Marek Vasut
Masahiro Yamada July 11, 2014, 2:19 a.m. UTC | #6
Hi Marek,


On Thu, 10 Jul 2014 13:30:38 +0200
Marek Vasut <marex@denx.de> wrote:

> > > > > > +
> > > > > > +	/* setup the pipeline command */
> > > > > > +	index_addr(cmd, 0x2000 | page_count);
> > > > > 
> > > > > Magic value 0x2000 should be fixed here.
> > > > 
> > > > Actually, this part is the same as that of the normal Denali driver
> > > > by Chin Liang See
> > > > http://patchwork.ozlabs.org/patch/357717/
> > > > 
> > > > Moreover, it is the same as in Linux Kernel.
> > > > 
> > > > I understand what you mean, but I guess we can excuse here.
> > > 
> > > OK, I see. Is there any chance to fix this in Linux maybe eventually?
> > 
> > OK.
> > I will send a patch to linux-mtd ML
> > to use  "#define PIPELINE_ACCESS    2000"
> 
> Ack on both, thank you !


Done.

http://patchwork.ozlabs.org/patch/368933/



Best Regards
Masahiro Yamada
diff mbox

Patch

diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f298f84..052c285 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -66,6 +66,7 @@  obj-$(CONFIG_NAND_DOCG4) += docg4.o
 
 else  # minimal SPL drivers
 
+obj-$(CONFIG_NAND_DENALI) += denali_spl.o
 obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
 obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
 obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
diff --git a/drivers/mtd/nand/denali_spl.c b/drivers/mtd/nand/denali_spl.c
new file mode 100644
index 0000000..6eed079
--- /dev/null
+++ b/drivers/mtd/nand/denali_spl.c
@@ -0,0 +1,201 @@ 
+/*
+ * Copyright (C) 2014       Panasonic Corporation
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include "denali.h"
+
+#define SPARE_ACCESS		0x41
+#define MAIN_ACCESS		0x42
+
+#define BANK(x) ((x) << 24)
+
+static void __iomem *denali_flash_mem =
+			(void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
+static void __iomem *denali_flash_reg =
+			(void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
+
+static const int flash_bank;
+static uint8_t page_buffer[NAND_MAX_PAGESIZE];
+static int page_size, oob_size, pages_per_block;
+
+static void index_addr(uint32_t address, uint32_t data)
+{
+	writel(address, denali_flash_mem + INDEX_CTRL_REG);
+	writel(data, denali_flash_mem + INDEX_DATA_REG);
+}
+
+static int wait_for_irq(uint32_t irq_mask)
+{
+	unsigned long timeout = 1000000;
+	uint32_t intr_status;
+
+	do {
+		intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
+
+		if (intr_status & INTR_STATUS__ECC_UNCOR_ERR) {
+			debug("Uncorrected ECC detected\n");
+			return -EIO;
+		}
+
+		if (intr_status & irq_mask)
+			break;
+
+		udelay(1);
+		timeout--;
+	} while (timeout);
+
+	if (!timeout) {
+		debug("Timeout with interrupt status %08x\n", intr_status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void read_data_from_flash_mem(uint8_t *buf, int len)
+{
+	int i;
+	uint32_t *buf32;
+
+	/* transfer the data from the flash */
+	buf32 = (uint32_t *)buf;
+	for (i = 0; i < len / 4; i++)
+		*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
+}
+
+int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
+{
+	uint32_t addr, cmd;
+	static uint32_t page_count = 1;
+
+	writel(ecc_en, denali_flash_reg + ECC_ENABLE);
+
+	/* clear all bits of intr_status. */
+	writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
+
+	addr = BANK(flash_bank) | page;
+
+	/* setup the acccess type */
+	cmd = MODE_10 | addr;
+	index_addr(cmd, access_type);
+
+	/* setup the pipeline command */
+	index_addr(cmd, 0x2000 | page_count);
+
+	cmd = MODE_01 | addr;
+	writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
+
+	return wait_for_irq(INTR_STATUS__LOAD_COMP);
+}
+
+static int nand_read_oob(void *buf, int page)
+{
+	int ret;
+
+	ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
+	if (ret < 0)
+		return ret;
+
+	read_data_from_flash_mem(buf, oob_size);
+
+	return 0;
+}
+
+static int nand_read_page(void *buf, int page)
+{
+	int ret;
+
+	ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
+	if (ret < 0)
+		return ret;
+
+	read_data_from_flash_mem(buf, page_size);
+
+	return 0;
+}
+
+static int nand_block_isbad(int block)
+{
+	int ret;
+
+	ret = nand_read_oob(page_buffer, block * pages_per_block);
+	if (ret < 0)
+		return ret;
+
+	return page_buffer[CONFIG_SYS_NAND_BAD_BLOCK_POS] != 0xff;
+}
+
+/* nand_init() - initialize data to make nand usable by SPL */
+void nand_init(void)
+{
+	/* access to main area */
+	writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
+
+	page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
+	oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
+	pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+	int block, page, column, readlen;
+	int ret;
+	int force_bad_block_check = 1;
+
+	page = offs / page_size;
+	column = offs % page_size;
+
+	block = page / pages_per_block;
+	page = page % pages_per_block;
+
+	while (size) {
+		if (force_bad_block_check || page == 0) {
+			ret = nand_block_isbad(block);
+			if (ret < 0)
+				return ret;
+
+			if (ret) {
+				block++;
+				continue;
+			}
+		}
+
+		force_bad_block_check = 0;
+
+		if (unlikely(column || size < page_size)) {
+			/* Partial page read */
+			ret = nand_read_page(page_buffer,
+					     block * pages_per_block + page);
+			if (ret < 0)
+				return ret;
+
+			readlen = min(page_size - column, size);
+			memcpy(dst, page_buffer, readlen);
+
+			column = 0;
+		} else {
+			ret = nand_read_page(dst,
+					     block * pages_per_block + page);
+			if (ret < 0)
+				return ret;
+
+			readlen = page_size;
+		}
+
+		size -= readlen;
+		dst += readlen;
+		page++;
+		if (page == pages_per_block) {
+			block++;
+			page = 0;
+		}
+	}
+
+	return 0;
+}
+
+void nand_deselect(void) {}