* [U-Boot] [PATCH 0/3] Monahan/PXA3xx: integrate Linux NAND controller driver
@ 2009-12-24 3:29 Marcel Ziswiler
2009-12-24 3:33 ` [U-Boot] [PATCH 1/3] " Marcel Ziswiler
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Marcel Ziswiler @ 2009-12-24 3:29 UTC (permalink / raw)
To: u-boot
From: Marcel Ziswiler <marcel.ziswiler@noser.com>
This patch-set integrates the PXA3xx Linux NAND controller driver and adapts
the two Monahan/PXA3xx boards delta and zylonite accordingly.
Marcel Ziswiler (3):
Monahan/PXA3xx: integrate Linux NAND controller driver
delta: use new generic NAND controller driver
zylonite: use new generic NAND controller driver
board/delta/Makefile | 2 +-
board/delta/delta.c | 50 +++++
board/delta/nand.c | 554 --------------------------------
board/zylonite/Makefile | 2 +-
board/zylonite/nand.c | 558 --------------------------------
board/zylonite/zylonite.c | 50 ++++
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/pxa3xx_nand.c | 1860 ++++++++++++++++++++++++++++++++
include/asm-arm/arch-pxa/pxa-regs.h | 95 +--
include/asm-arm/arch-pxa/pxa3xx_nand.h | 67 ++
include/configs/delta.h | 32 +--
include/configs/zylonite.h | 34 +--
12 files changed, 2049 insertions(+), 1256 deletions(-)
create mode 100644 drivers/mtd/nand/pxa3xx_nand.c
create mode 100644 include/asm-arm/arch-pxa/pxa3xx_nand.h
delete mode 100644 board/delta/nand.c
delete mode 100644 board/zylonite/nand.c
^ permalink raw reply [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 1/3] Monahan/PXA3xx: integrate Linux NAND controller driver
2009-12-24 3:29 [U-Boot] [PATCH 0/3] Monahan/PXA3xx: integrate Linux NAND controller driver Marcel Ziswiler
@ 2009-12-24 3:33 ` Marcel Ziswiler
2010-01-07 19:44 ` Scott Wood
2009-12-24 3:36 ` [U-Boot] [PATCH 2/3] delta: use new generic " Marcel Ziswiler
2009-12-24 3:38 ` [U-Boot] [PATCH 3/3] zylonite: " Marcel Ziswiler
2 siblings, 1 reply; 7+ messages in thread
From: Marcel Ziswiler @ 2009-12-24 3:33 UTC (permalink / raw)
To: u-boot
This patch integrates the PXA3xx Linux NAND controller driver.
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@noser.com>
---
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/pxa3xx_nand.c | 1860 ++++++++++++++++++++++++++++++++
include/asm-arm/arch-pxa/pxa-regs.h | 95 +--
include/asm-arm/arch-pxa/pxa3xx_nand.h | 67 ++
4 files changed, 1931 insertions(+), 92 deletions(-)
create mode 100644 drivers/mtd/nand/pxa3xx_nand.c
create mode 100644 include/asm-arm/arch-pxa/pxa3xx_nand.h
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 02449ee..63568ee 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,6 +49,7 @@ COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
+COBJS-$(CONFIG_NAND_PXA3xx) += pxa3xx_nand.o
endif
COBJS := $(COBJS-y)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
new file mode 100644
index 0000000..03709f7
--- /dev/null
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -0,0 +1,1860 @@
+/*
+ * drivers/mtd/nand/pxa3xx_nand.c
+ *
+ * Copyright ? 2005 Intel Corporation
+ * Copyright ? 2006 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* XXX U-BOOT XXX */
+#define __U_BOOT__
+
+#ifndef __U_BOOT__
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <mach/dma.h>
+#include <plat/pxa3xx_nand.h>
+#else /* __U_BOOT__ */
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/pxa3xx_nand.h>
+
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_NAND_SPL
+#define printf(fmt, args...)
+#endif /* CONFIG_NAND_SPL */
+
+#undef DEBUG
+#ifdef DEBUG
+#define DEBUGF(fmt, args...) printf(fmt, ##args)
+#else /* DEBUG */
+#define DEBUGF(x...)
+#endif /* DEBUG */
+
+#define CONFIG_MTD_NAND_PXA3xx_BUILTIN
+#define CONFIG_MTD_NAND_PXA3xx_POLLING
+
+static unsigned char static_data_buff[2112];
+static struct pxa3xx_nand_info static_info;
+
+#ifdef CONFIG_NAND_SPL
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void *memset(void *s, int c, size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void *memcpy(void *dest, const void *src, size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+#endif /* CONFIG_NAND_SPL */
+
+void msleep(int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ udelay(1000);
+}
+#endif /* __U_BOOT__ */
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+/* registers and bit definitions */
+#define NDCR (0x00) /* Control register */
+#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */
+#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */
+#define NDSR (0x14) /* Status Register */
+#define NDPCR (0x18) /* Page Count Register */
+#define NDBDR0 (0x1C) /* Bad Block Register 0 */
+#define NDBDR1 (0x20) /* Bad Block Register 1 */
+#define NDDB (0x40) /* Data Buffer */
+#define NDCB0 (0x48) /* Command Buffer0 */
+#define NDCB1 (0x4C) /* Command Buffer1 */
+#define NDCB2 (0x50) /* Command Buffer2 */
+
+#define NDCR_SPARE_EN (0x1 << 31)
+#define NDCR_ECC_EN (0x1 << 30)
+#define NDCR_DMA_EN (0x1 << 29)
+#define NDCR_ND_RUN (0x1 << 28)
+#define NDCR_DWIDTH_C (0x1 << 27)
+#define NDCR_DWIDTH_M (0x1 << 26)
+#define NDCR_PAGE_SZ (0x1 << 24)
+#define NDCR_NCSX (0x1 << 23)
+#define NDCR_ND_MODE (0x3 << 21)
+#define NDCR_NAND_MODE (0x0)
+#define NDCR_CLR_PG_CNT (0x1 << 20)
+#define NDCR_CLR_ECC (0x1 << 19)
+#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
+#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
+
+#define NDCR_RA_START (0x1 << 15)
+#define NDCR_PG_PER_BLK (0x1 << 14)
+#define NDCR_ND_ARB_EN (0x1 << 12)
+
+#define NDSR_MASK (0xfff)
+#define NDSR_RDY (0x1 << 11)
+#define NDSR_CS0_PAGED (0x1 << 10)
+#define NDSR_CS1_PAGED (0x1 << 9)
+#define NDSR_CS0_CMDD (0x1 << 8)
+#define NDSR_CS1_CMDD (0x1 << 7)
+#define NDSR_CS0_BBD (0x1 << 6)
+#define NDSR_CS1_BBD (0x1 << 5)
+#define NDSR_DBERR (0x1 << 4)
+#define NDSR_SBERR (0x1 << 3)
+#define NDSR_WRDREQ (0x1 << 2)
+#define NDSR_RDDREQ (0x1 << 1)
+#define NDSR_WRCMDREQ (0x1)
+
+#define NDCB0_AUTO_RS (0x1 << 25)
+#define NDCB0_CSEL (0x1 << 24)
+#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
+#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC (0x1 << 20)
+#define NDCB0_DBC (0x1 << 19)
+#define NDCB0_ADDR_CYC_MASK (0x7 << 16)
+#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK (0xff << 8)
+#define NDCB0_CMD1_MASK (0xff)
+#define NDCB0_ADDR_CYC_SHIFT (16)
+
+/* macros for registers read/write */
+#ifdef __U_BOOT__
+#define nand_writel(info, off, val) \
+ ((*(volatile u32 *) ((info)->mmio_base + (off))) = (val))
+
+static void nand_writesl(volatile u32 *dest, u8 *src, int size)
+{
+ int i;
+ u32 *source = (u32 *)src;
+
+ for (i = 0; i < size; i++)
+ *dest = *(source++);
+}
+
+#define nand_readl(info, off) \
+ (*(volatile u32 *)((info)->mmio_base + (off)))
+
+static void nand_readsl(volatile u32 *source, u8 *dst, int size)
+{
+ u32 *dest = (u32 *)dst;
+ int i;
+
+ for (i = 0; i < size; i++)
+ *(dest++) = *source;
+}
+#else /* __U_BOOT__ */
+#define nand_writel(info, off, val) \
+ __raw_writel((val), (info)->mmio_base + (off))
+
+#define nand_readl(info, off) \
+ __raw_readl((info)->mmio_base + (off))
+#endif /* __U_BOOT__ */
+
+/* error code and state */
+enum {
+ ERR_NONE = 0,
+ ERR_DMABUSERR = -1,
+ ERR_SENDCMD = -2,
+ ERR_DBERR = -3,
+ ERR_BBERR = -4,
+ ERR_SBERR = -5,
+};
+
+enum {
+ STATE_READY = 0,
+ STATE_CMD_HANDLE,
+ STATE_DMA_READING,
+ STATE_DMA_WRITING,
+ STATE_DMA_DONE,
+ STATE_PIO_READING,
+ STATE_PIO_WRITING,
+};
+
+struct pxa3xx_nand_info {
+ struct platform_device *pdev;
+ const struct pxa3xx_nand_flash *flash_info;
+
+ struct clk *clk;
+ void __iomem *mmio_base;
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ unsigned long mmio_phys;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ unsigned int buf_start;
+ unsigned int buf_count;
+
+ /* DMA information */
+ int drcmr_dat;
+ int drcmr_cmd;
+
+ unsigned char *data_buff;
+ dma_addr_t data_buff_phys;
+ size_t data_buff_size;
+ int data_dma_ch;
+ struct pxa_dma_desc *data_desc;
+ dma_addr_t data_desc_addr;
+
+ uint32_t reg_ndcr;
+
+ /* saved column/page_addr during CMD_SEQIN */
+ int seqin_column;
+ int seqin_page_addr;
+
+ /* relate to the command */
+ unsigned int state;
+
+ int use_ecc; /* use HW ECC ? */
+ int use_dma; /* use DMA ? */
+
+ size_t data_size; /* data size in FIFO */
+ int retcode;
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ struct completion cmd_complete;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ /* generated NDCBx register values */
+ uint32_t ndcb0;
+ uint32_t ndcb1;
+ uint32_t ndcb2;
+
+ /* calculated from pxa3xx_nand_flash data */
+ size_t oob_size;
+ size_t read_id_bytes;
+
+ unsigned int col_addr_cycles;
+ unsigned int row_addr_cycles;
+};
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_POLLING
+static int use_dma = 0;
+#else /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+static int use_dma = 1;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+#ifndef __U_BOOT__
+module_param(use_dma, bool, 0444);
+MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
+
+/*
+ * Default NAND flash controller configuration setup by the
+ * bootloader. This configuration is used only when pdata->keep_config is set
+ */
+static struct pxa3xx_nand_timing default_timing;
+static struct pxa3xx_nand_flash default_flash;
+#endif /* __U_BOOT__ */
+
+/* small page NAND command codes (2nd cycle, 1st cycle) */
+static struct pxa3xx_nand_cmdset smallpage_cmdset = {
+ .read1 = 0x0000, /* Read */
+ .read2 = 0x0050, /* Read2 unused, current DFC doesn't
+ support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Block erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock block range */
+ .lock_status = 0x007A, /* Read block lock status */
+};
+
+/* large page NAND command codes (2nd cycle, 1st cycle) */
+static struct pxa3xx_nand_cmdset largepage_cmdset = {
+ .read1 = 0x3000, /* Read */
+ .read2 = 0x0050, /* Read2 unused, current DFC doesn't
+ support */
+ .program = 0x1080, /* Write, two cycle command */
+ .read_status = 0x0070, /* Read status */
+ .read_id = 0x0090, /* Read ID */
+ .erase = 0xD060, /* Block erase, two cycle command */
+ .reset = 0x00FF, /* Reset */
+ .lock = 0x002A, /* Lock whole flash */
+ .unlock = 0x2423, /* Unlock block range */
+ .lock_status = 0x007A, /* Read block lock status */
+};
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
+/* Samsung K9K8G08U0A and K9K8G08U0B on Toradex Colibri XScale PXA320 V1.2d,
+ V1.2e, Colibri XScale PXA320 IT V1.2b, Limestone PDA V1.2b and V2.0c */
+static struct pxa3xx_nand_timing samsung8GbX8_timing = {
+ .tCH = 5, /* tCH, Enable signal hold time */
+ .tCS = 20, /* tCS, Enable signal setup time */
+ .tWH = 10, /* tWH, ND_nWE high duration */
+ .tWP = 12, /* tWP, ND_nWE pulse time */
+ .tRH = 100, /* tRHZ, ND_nRE high duration */
+ .tRP = 12, /* tRP, ND_nRE pulse width */
+ .tR = 20000, /* tR, ND_nWE high to ND_nRE low for read */
+ .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay for
+ status read */
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+};
+
+static struct pxa3xx_nand_flash samsung8GbX8 = {
+ .timing = &samsung8GbX8_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64, /* Pages per block */
+ .page_size = 2048, /* Page size in bytes */
+ .flash_width = 8, /* Width of Flash memory */
+ .dfc_width = 8, /* Width of flash controller */
+ .num_blocks = 8192, /* Number of physical blocks in
+ Flash */
+ .chip_id = 0xd3ec, /* chip ID, vendor ID */
+};
+
+/* Samsung K9F4G08U0A on Toradex Colibri XScale PXA310 V1.2a and V1.3a */
+static struct pxa3xx_nand_timing samsung4GbX8_timing = {
+ .tCH = 5, /* tCH, Enable signal hold time */
+ .tCS = 20, /* tCS, Enable signal setup time */
+ .tWH = 10, /* tWH, ND_nWE high duration */
+ .tWP = 12, /* tWP, ND_nWE pulse time */
+ .tRH = 100, /* tRHZ, ND_nRE high duration */
+ .tRP = 12, /* tRP, ND_nRE pulse width */
+ .tR = 25000, /* tR, ND_nWE high to ND_nRE low for read */
+ .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay for
+ status read */
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+};
+
+static struct pxa3xx_nand_flash samsung4GbX8 = {
+ .timing = &samsung4GbX8_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64, /* Pages per block */
+ .page_size = 2048, /* Page size in bytes */
+ .flash_width = 8, /* Width of Flash memory */
+ .dfc_width = 8, /* Width of flash controller */
+ .num_blocks = 4096, /* Number of physical blocks in
+ Flash */
+ .chip_id = 0xdcec, /* chip ID, vendor ID */
+};
+
+/* Samsung K9F1G08U0A on Toradex Colibri XScale PXA300 V1.1a and V1.1b */
+static struct pxa3xx_nand_timing samsung1GbX8_timing = {
+ .tCH = 5, /* tCH, Enable signal hold time */
+ .tCS = 20, /* tCS, Enable signal setup time */
+ .tWH = 10, /* tWH, ND_nWE high duration */
+ .tWP = 15, /* tWP, ND_nWE pulse time */
+ .tRH = 30, /* tRHZ, ND_nRE high duration */
+ .tRP = 15, /* tRP, ND_nRE pulse width */
+ .tR = 25000, /* tR, ND_nWE high to ND_nRE low for read */
+ .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay for
+ status read */
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+};
+
+static struct pxa3xx_nand_flash samsung1GbX8 = {
+ .timing = &samsung1GbX8_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 1024,
+ .chip_id = 0xf1ec,
+};
+
+/* Hynix HY27UG088G5M on Toradex Colibri XScale PXA320 V1.2b and V1.2c */
+static struct pxa3xx_nand_timing hynix_timing = {
+ .tCH = 5, /* tCH, Enable signal hold time */
+ .tCS = 25, /* tCS, Enable signal setup time */
+ .tWH = 10, /* tWH, ND_nWE high duration */
+ .tWP = 15, /* tWP, ND_nWE pulse time */
+ .tRH = 10, /* tREH, ND_nRE high duration */
+ .tRP = 15, /* tRP, ND_nRE pulse width */
+ .tR = 25000, /* tR, ND_nWE high to ND_nRE low for read */
+ .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay */
+ .tAR = 15, /* tAR, ND_ALE low to ND_nRE low delay */
+};
+
+static struct pxa3xx_nand_flash hynix8GbX8 = {
+ .timing = &hynix_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64, /* Pages per block */
+ .page_size = 2048, /* Page size in bytes */
+ .flash_width = 8, /* Width of Flash memory */
+ .dfc_width = 8, /* Width of flash controller */
+ .num_blocks = 8192, /* Number of physical blocks in
+ Flash */
+ .chip_id = 0xdcad, /* chip ID, vendor ID */
+};
+
+/* Samsung K9F1208U0B on Toradex Colibri XScale PXA290 V1.0a, V1.0c and
+ V1.0d */
+static struct pxa3xx_nand_timing samsung512MbX8_timing = {
+ .tCH = 10, /* tCH, Enable signal hold time */
+ .tCS = 10, /* tCS, Enable signal setup time */
+ .tWH = 15, /* tWH, ND_nWE high duration */
+ .tWP = 25, /* tWP, ND_nWE pulse time */
+ .tRH = 15, /* tRHZ, ND_nRE high duration */
+ .tRP = 25, /* tRP, ND_nRE pulse width */
+ .tR = 15000, /* tR, ND_nWE high to ND_nRE low for read */
+ .tWHR = 60, /* tWHR, ND_nWE high to ND_nRE low delay for
+ status read */
+ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
+};
+
+static struct pxa3xx_nand_flash samsung512MbX8 = {
+ .timing = &samsung512MbX8_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 32,
+ .page_size = 512,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 4096,
+ .chip_id = 0x76ec,
+};
+
+/* BenQ Delta and Marvell Zylonite */
+static struct pxa3xx_nand_timing samsung512MbX16_timing = {
+ .tCH = 10,
+ .tCS = 0,
+ .tWH = 20,
+ .tWP = 40,
+ .tRH = 30,
+ .tRP = 40,
+ .tR = 11123,
+ .tWHR = 110,
+ .tAR = 10,
+};
+
+static struct pxa3xx_nand_flash samsung512MbX16 = {
+ .timing = &samsung512MbX16_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 32,
+ .page_size = 512,
+ .flash_width = 16,
+ .dfc_width = 16,
+ .num_blocks = 4096,
+ .chip_id = 0x46ec,
+};
+
+static struct pxa3xx_nand_flash samsung2GbX8 = {
+ .timing = &samsung512MbX16_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 2048,
+ .chip_id = 0xdaec,
+};
+
+static struct pxa3xx_nand_flash samsung32GbX8 = {
+ .timing = &samsung512MbX16_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 128,
+ .page_size = 4096,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 8192,
+ .chip_id = 0xd7ec,
+};
+
+static struct pxa3xx_nand_timing micron_timing = {
+ .tCH = 10,
+ .tCS = 25,
+ .tWH = 15,
+ .tWP = 25,
+ .tRH = 15,
+ .tRP = 30,
+ .tR = 25000,
+ .tWHR = 60,
+ .tAR = 10,
+};
+
+static struct pxa3xx_nand_flash micron1GbX8 = {
+ .timing = µn_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 1024,
+ .chip_id = 0xa12c,
+};
+
+static struct pxa3xx_nand_flash micron1GbX16 = {
+ .timing = µn_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 16,
+ .dfc_width = 16,
+ .num_blocks = 1024,
+ .chip_id = 0xb12c,
+};
+
+static struct pxa3xx_nand_flash micron4GbX8 = {
+ .timing = µn_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 4096,
+ .chip_id = 0xdc2c,
+};
+
+static struct pxa3xx_nand_flash micron4GbX16 = {
+ .timing = µn_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 16,
+ .dfc_width = 16,
+ .num_blocks = 4096,
+ .chip_id = 0xcc2c,
+};
+
+static struct pxa3xx_nand_timing stm2GbX16_timing = {
+ .tCH = 10,
+ .tCS = 35,
+ .tWH = 15,
+ .tWP = 25,
+ .tRH = 15,
+ .tRP = 25,
+ .tR = 25000,
+ .tWHR = 60,
+ .tAR = 10,
+};
+
+static struct pxa3xx_nand_flash stm2GbX16 = {
+ .timing = &stm2GbX16_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 16,
+ .dfc_width = 16,
+ .num_blocks = 2048,
+ .chip_id = 0xba20,
+};
+
+static struct pxa3xx_nand_flash *builtin_flash_types[] = {
+ &samsung8GbX8,
+ &samsung4GbX8,
+ &samsung1GbX8,
+ &hynix8GbX8,
+ &samsung512MbX8,
+ &samsung512MbX16,
+ &samsung2GbX8,
+ &samsung32GbX8,
+ µn1GbX8,
+ µn1GbX16,
+ µn4GbX8,
+ µn4GbX16,
+ &stm2GbX16,
+};
+#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */
+
+#define NDTR0_tCH(c) (min((c), 7) << 19)
+#define NDTR0_tCS(c) (min((c), 7) << 16)
+#define NDTR0_tWH(c) (min((c), 7) << 11)
+#define NDTR0_tWP(c) (min((c), 7) << 8)
+#define NDTR0_tRH(c) (min((c), 7) << 3)
+#define NDTR0_tRP(c) (min((c), 7) << 0)
+
+#define NDTR1_tR(c) (min((c), 65535) << 16)
+#define NDTR1_tWHR(c) (min((c), 15) << 4)
+#define NDTR1_tAR(c) (min((c), 15) << 0)
+
+#define tCH_NDTR0(r) (((r) >> 19) & 0x7)
+#define tCS_NDTR0(r) (((r) >> 16) & 0x7)
+#define tWH_NDTR0(r) (((r) >> 11) & 0x7)
+#define tWP_NDTR0(r) (((r) >> 8) & 0x7)
+#define tRH_NDTR0(r) (((r) >> 3) & 0x7)
+#define tRP_NDTR0(r) (((r) >> 0) & 0x7)
+
+#define tR_NDTR1(r) (((r) >> 16) & 0xffff)
+#define tWHR_NDTR1(r) (((r) >> 4) & 0xf)
+#define tAR_NDTR1(r) (((r) >> 0) & 0xf)
+
+/* convert nano-seconds to nand flash controller clock cycles */
+#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1)
+
+/* convert nand flash controller clock cycles to nano-seconds */
+#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / \
+ (clk / 1000))
+
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
+ const struct pxa3xx_nand_timing *t)
+{
+#ifndef __U_BOOT__
+ unsigned long nand_clk = clk_get_rate(info->clk);
+#else /* __U_BOOT__ */
+ unsigned long nand_clk = CONFIG_NAND_PXA3xx_CLK;
+#endif /* __U_BOOT__ */
+ uint32_t ndtr0, ndtr1;
+
+ ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
+ NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
+ NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
+ NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) |
+ NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) |
+ NDTR0_tRP(ns2cycle(t->tRP, nand_clk));
+
+ ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) |
+ NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
+ NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
+
+ nand_writel(info, NDTR0CS0, ndtr0);
+ nand_writel(info, NDTR1CS0, ndtr1);
+}
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_POLLING
+/* Note: erase needs that long! */
+#define WAIT_EVENT_TIMEOUT 200
+#else /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+#define WAIT_EVENT_TIMEOUT 10
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event)
+{
+ int timeout = WAIT_EVENT_TIMEOUT;
+ uint32_t ndsr;
+
+ DEBUGF("wait_for_event(");
+
+#ifdef DEBUG
+ if (event & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR))
+ DEBUGF("event = NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)\n");
+ else if (event & NDSR_WRDREQ)
+ DEBUGF("event = NDSR_WRDREQ)\n");
+ else if (event & (NDSR_CS0_BBD | NDSR_CS0_CMDD))
+ DEBUGF("event = NDSR_CS0_BBD | NDSR_CS0_CMDD)\n");
+#endif /* DEBUG */
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_POLLING
+ if (event & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR))
+ info->state = STATE_PIO_READING;
+ else if (event & NDSR_WRDREQ)
+ info->state = STATE_PIO_WRITING;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ while (timeout--) {
+ ndsr = nand_readl(info, NDSR) & NDSR_MASK;
+
+#ifdef DEBUG
+ if (ndsr & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR))
+ DEBUGF("NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR\n");
+ else if (ndsr & NDSR_WRDREQ)
+ DEBUGF("NDSR_WRDREQ\n");
+ else if (ndsr & (NDSR_CS0_BBD | NDSR_CS0_CMDD))
+ DEBUGF("NDSR_CS0_BBD | NDSR_CS0_CMDD\n");
+#endif /* DEBUG */
+
+ if (ndsr & event) {
+ nand_writel(info, NDSR, ndsr);
+ return 0;
+ }
+ udelay(10);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
+ uint16_t cmd, int column, int page_addr)
+{
+ const struct pxa3xx_nand_flash *f = info->flash_info;
+ const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
+
+ /* calculate data size */
+ switch (f->page_size) {
+ case 2048:
+ info->data_size = (info->use_ecc) ? 2088 : 2112;
+ break;
+ case 512:
+ info->data_size = (info->use_ecc) ? 520 : 528;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* generate values for NDCBx registers */
+ info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
+ info->ndcb1 = 0;
+ info->ndcb2 = 0;
+ info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles +
+ info->col_addr_cycles);
+
+ if (info->col_addr_cycles == 2) {
+ /* large block, 2 cycles for column address
+ * row address starts from 3rd cycle
+ */
+ info->ndcb1 |= page_addr << 16;
+ if (info->row_addr_cycles == 3)
+ info->ndcb2 = (page_addr >> 16) & 0xff;
+ } else
+ /* small block, 1 cycles for column address
+ * row address starts from 2nd cycle
+ */
+ info->ndcb1 = page_addr << 8;
+
+ if (cmd == cmdset->program)
+ info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+
+ return 0;
+}
+
+static int prepare_erase_cmd(struct pxa3xx_nand_info *info,
+ uint16_t cmd, int page_addr)
+{
+ info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
+ info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3);
+ info->ndcb1 = page_addr;
+ info->ndcb2 = 0;
+
+/* TODO: When programming an erase command the contents of ADDR1 must be
+ replicated in ADDR5 in order to save the full address during a bad block
+ detect since only fields ADDR2 through ADDR5 are saved. */
+
+ return 0;
+}
+
+static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
+{
+ const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset;
+
+ DEBUGF("prepare_other_cmd(");
+
+ info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
+ info->ndcb1 = 0;
+ info->ndcb2 = 0;
+
+ if (cmd == cmdset->read_id) {
+ DEBUGF("cmd = read_id)\n");
+ /* 1 dummy address cycle */
+ info->ndcb0 |= NDCB0_CMD_TYPE(3);
+ info->data_size = 8;
+ } else if (cmd == cmdset->read_status) {
+ DEBUGF("cmd = read_status)\n");
+ info->ndcb0 |= NDCB0_CMD_TYPE(4);
+ info->data_size = 8;
+ } else if (cmd == cmdset->reset || cmd == cmdset->lock ||
+ cmd == cmdset->unlock) {
+ DEBUGF("cmd = reset | lock | unlock)\n");
+ info->ndcb0 |= NDCB0_CMD_TYPE(5);
+ } else {
+ DEBUGF("cmd = invalid)\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr & ~int_mask);
+}
+
+static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr | int_mask);
+}
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+/* NOTE: it is a must to set ND_RUN firstly, then write command buffer
+ * otherwise, it does not work
+ */
+static int write_cmd(struct pxa3xx_nand_info *info)
+{
+ uint32_t ndcr;
+
+ DEBUGF("write_cmd\n");
+
+ /* clear status bits and run */
+ nand_writel(info, NDSR, NDSR_MASK);
+
+ ndcr = info->reg_ndcr;
+
+ ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
+ ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
+ ndcr |= NDCR_ND_RUN;
+
+ nand_writel(info, NDCR, ndcr);
+
+ if (wait_for_event(info, NDSR_WRCMDREQ)) {
+ printk(KERN_ERR "timed out writing command\n");
+ return -ETIMEDOUT;
+ }
+
+ nand_writel(info, NDCB0, info->ndcb0);
+ nand_writel(info, NDCB0, info->ndcb1);
+ nand_writel(info, NDCB0, info->ndcb2);
+ return 0;
+}
+
+static int handle_data_pio(struct pxa3xx_nand_info *info)
+{
+ int ret;
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ int timeout = CHIP_DELAY_TIMEOUT;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ DEBUGF("handle_data_pio\n");
+
+ switch (info->state) {
+ case STATE_PIO_WRITING:
+ nand_writesl(info->mmio_base + NDDB, info->data_buff,
+ DIV_ROUND_UP(info->data_size, 4));
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_POLLING
+ ret = wait_for_event(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+ if (ret) {
+#else /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+ enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+
+ ret = wait_for_completion_timeout(&info->cmd_complete, timeout);
+ if (!ret) {
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+ printk(KERN_ERR "program command time out\n");
+ return -1;
+ }
+ break;
+ case STATE_PIO_READING:
+ nand_readsl(info->mmio_base + NDDB, info->data_buff,
+ DIV_ROUND_UP(info->data_size, 4));
+ break;
+ default:
+ printk(KERN_ERR "%s: invalid state %d\n", __func__,
+ info->state);
+ return -EINVAL;
+ }
+
+ info->state = STATE_READY;
+ return 0;
+}
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
+{
+ struct pxa_dma_desc *desc = info->data_desc;
+ int dma_len = ALIGN(info->data_size, 32);
+
+ desc->ddadr = DDADR_STOP;
+ desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
+
+ if (dir_out) {
+ desc->dsadr = info->data_buff_phys;
+ desc->dtadr = info->mmio_phys + NDDB;
+ desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
+ } else {
+ desc->dtadr = info->data_buff_phys;
+ desc->dsadr = info->mmio_phys + NDDB;
+ desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
+ }
+
+ DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
+ DDADR(info->data_dma_ch) = info->data_desc_addr;
+ DCSR(info->data_dma_ch) |= DCSR_RUN;
+}
+
+static void pxa3xx_nand_data_dma_irq(int channel, void *data)
+{
+ struct pxa3xx_nand_info *info = data;
+ uint32_t dcsr;
+
+ dcsr = DCSR(channel);
+ DCSR(channel) = dcsr;
+
+ if (dcsr & DCSR_BUSERR) {
+ info->retcode = ERR_DMABUSERR;
+ complete(&info->cmd_complete);
+ }
+
+ if (info->state == STATE_DMA_WRITING) {
+ info->state = STATE_DMA_DONE;
+ enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+ } else {
+ info->state = STATE_READY;
+ complete(&info->cmd_complete);
+ }
+}
+
+static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
+{
+ struct pxa3xx_nand_info *info = devid;
+ unsigned int status;
+
+ DEBUGF("pxa3xx_nand_irq\n");
+
+ status = nand_readl(info, NDSR);
+
+ if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) {
+ if (status & NDSR_DBERR)
+ info->retcode = ERR_DBERR;
+ else if (status & NDSR_SBERR)
+ info->retcode = ERR_SBERR;
+
+ disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
+
+ if (info->use_dma) {
+ info->state = STATE_DMA_READING;
+ start_data_dma(info, 0);
+ } else {
+ info->state = STATE_PIO_READING;
+ complete(&info->cmd_complete);
+ }
+ } else if (status & NDSR_WRDREQ) {
+ disable_int(info, NDSR_WRDREQ);
+ if (info->use_dma) {
+ info->state = STATE_DMA_WRITING;
+ start_data_dma(info, 1);
+ } else {
+ info->state = STATE_PIO_WRITING;
+ complete(&info->cmd_complete);
+ }
+ } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) {
+ if (status & NDSR_CS0_BBD)
+ info->retcode = ERR_BBERR;
+
+ disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+ info->state = STATE_READY;
+ complete(&info->cmd_complete);
+ }
+ nand_writel(info, NDSR, status);
+ return IRQ_HANDLED;
+}
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event)
+{
+ uint32_t ndcr;
+ int ret;
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ int timeout = CHIP_DELAY_TIMEOUT;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ DEBUGF("pxa3xx_nand_do_cmd\n");
+
+ if (write_cmd(info)) {
+ info->retcode = ERR_SENDCMD;
+ goto fail_stop;
+ }
+
+ info->state = STATE_CMD_HANDLE;
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_POLLING
+ ret = wait_for_event(info, event);
+ if (ret) {
+#else /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+ enable_int(info, event);
+
+ ret = wait_for_completion_timeout(&info->cmd_complete, timeout);
+ if (!ret) {
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+ printk(KERN_ERR "command execution timed out\n");
+ info->retcode = ERR_SENDCMD;
+ goto fail_stop;
+ }
+
+ if (info->use_dma == 0 && info->data_size > 0)
+ if (handle_data_pio(info))
+ goto fail_stop;
+
+ return 0;
+
+fail_stop:
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
+ udelay(10);
+ return -ETIMEDOUT;
+}
+
+static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0;
+}
+
+static inline int is_buf_blank(uint8_t *buf, size_t len)
+{
+ for (; len > 0; len--)
+ if (*buf++ != 0xff)
+ return 0;
+ return 1;
+}
+
+static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ const struct pxa3xx_nand_flash *flash_info = info->flash_info;
+ const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset;
+ int ret;
+
+ DEBUGF("pxa3xx_nand_cmdfunc(");
+
+ info->use_dma = (use_dma) ? 1 : 0;
+ info->use_ecc = 0;
+ info->data_size = 0;
+ info->state = STATE_READY;
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ init_completion(&info->cmd_complete);
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ switch (command) {
+ case NAND_CMD_READOOB:
+ DEBUGF("command = NAND_CMD_READOOB)\n");
+ /* disable HW ECC to get all the OOB data */
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ info->buf_start = mtd->writesize + column;
+ memset(info->data_buff, 0xFF, info->buf_count);
+
+ if (prepare_read_prog_cmd(info, cmdset->read1, column,
+ page_addr))
+ break;
+
+ pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
+
+ /* We only are OOB, so if the data has error, does not matter */
+ if (info->retcode == ERR_DBERR)
+ info->retcode = ERR_NONE;
+ break;
+
+ case NAND_CMD_READ0:
+ DEBUGF("command = NAND_CMD_READ0)\n");
+ info->use_ecc = 1;
+ info->retcode = ERR_NONE;
+ info->buf_start = column;
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ memset(info->data_buff, 0xFF, info->buf_count);
+
+ if (prepare_read_prog_cmd(info, cmdset->read1, column,
+ page_addr))
+ break;
+
+ pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
+
+ if (info->retcode == ERR_DBERR) {
+ /* for blank page (all 0xff), HW will calculate its ECC
+ * as 0, which is different from the ECC information
+ * within OOB, ignore such double bit errors
+ */
+ if (is_buf_blank(info->data_buff, mtd->writesize))
+ info->retcode = ERR_NONE;
+ }
+ break;
+ case NAND_CMD_SEQIN:
+ DEBUGF("command = NAND_CMD_SEQIN)\n");
+ info->buf_start = column;
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ memset(info->data_buff, 0xff, info->buf_count);
+
+ /* save column/page_addr for next CMD_PAGEPROG */
+ info->seqin_column = column;
+ info->seqin_page_addr = page_addr;
+ break;
+ case NAND_CMD_PAGEPROG:
+ DEBUGF("command = NAND_CMD_PAGEPROG)\n");
+ info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1;
+
+ if (prepare_read_prog_cmd(info, cmdset->program,
+ info->seqin_column, info->seqin_page_addr))
+ break;
+
+ pxa3xx_nand_do_cmd(info, NDSR_WRDREQ);
+ break;
+ case NAND_CMD_ERASE1:
+ DEBUGF("command = NAND_CMD_ERASE1)\n");
+ if (prepare_erase_cmd(info, cmdset->erase, page_addr))
+ break;
+
+ pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
+ break;
+ case NAND_CMD_ERASE2:
+ DEBUGF("command = NAND_CMD_ERASE2)\n");
+ break;
+ case NAND_CMD_READID:
+ DEBUGF("command = NAND_CMD_READID)\n");
+ case NAND_CMD_STATUS:
+ if (command == NAND_CMD_STATUS)
+ DEBUGF("command = NAND_CMD_STATUS)\n");
+ info->use_dma = 0; /* force PIO read */
+ info->buf_start = 0;
+ info->buf_count = (command == NAND_CMD_READID) ?
+ info->read_id_bytes : 1;
+
+ if (prepare_other_cmd(info, (command == NAND_CMD_READID) ?
+ cmdset->read_id : cmdset->read_status))
+ break;
+
+ pxa3xx_nand_do_cmd(info, NDSR_RDDREQ);
+ break;
+ case NAND_CMD_RESET:
+ DEBUGF("command = NAND_CMD_RESET)\n");
+ if (prepare_other_cmd(info, cmdset->reset))
+ break;
+
+ ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD);
+ if (ret == 0) {
+ int timeout = 2;
+ uint32_t ndcr;
+
+ while (timeout--) {
+ if (nand_readl(info, NDSR) & NDSR_RDY)
+ break;
+ msleep(10);
+ }
+
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
+ }
+ break;
+ default:
+ printk(KERN_ERR "non-supported command.\n");
+ break;
+ }
+
+ if (info->retcode == ERR_DBERR) {
+ printk(KERN_ERR "double bit error @ page %08x\n", page_addr);
+ info->retcode = ERR_NONE;
+ }
+}
+
+static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ char retval = 0xFF;
+
+ if (info->buf_start < info->buf_count)
+ /* Has just send a new command? */
+ retval = info->data_buff[info->buf_start++];
+
+ return retval;
+}
+
+static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ u16 retval = 0xFFFF;
+
+ if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
+ retval = *((u16 *)(info->data_buff+info->buf_start));
+ info->buf_start += 2;
+ }
+ return retval;
+}
+
+static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+
+ memcpy(buf, info->data_buff + info->buf_start, real_len);
+ info->buf_start += real_len;
+}
+
+static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+
+ memcpy(info->data_buff + info->buf_start, buf, real_len);
+ info->buf_start += real_len;
+}
+
+static int pxa3xx_nand_verify_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ return 0;
+}
+
+static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ return;
+}
+
+static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+ struct pxa3xx_nand_info *info = this->priv;
+
+ /* pxa3xx_nand_send_command has waited for command complete */
+ if (this->state == FL_WRITING || this->state == FL_ERASING) {
+ if (info->retcode == ERR_NONE)
+ return 0;
+ else {
+ /*
+ * any error make it return 0x01 which will tell
+ * the caller the erase and write fail
+ */
+ return 0x01;
+ }
+ }
+
+ return 0;
+}
+
+static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+ return;
+}
+
+static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd,
+ const uint8_t *dat, uint8_t *ecc_code)
+{
+ return 0;
+}
+
+static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
+ uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct pxa3xx_nand_info *info = chip->priv;
+ /*
+ * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
+ * consider it as a ecc error which will tell the caller the
+ * read fail We have distinguish all the errors, but the
+ * nand_read_ecc only check this function return value
+ *
+ * Corrected (single-bit) errors must also be noted.
+ */
+ if (info->retcode == ERR_SBERR)
+ return 1;
+ else if (info->retcode != ERR_NONE)
+ return -1;
+
+ return 0;
+}
+
+static int __readid(struct pxa3xx_nand_info *info, uint32_t *id)
+{
+ const struct pxa3xx_nand_flash *f = info->flash_info;
+ const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
+ uint32_t ndcr;
+ uint8_t id_buff[8];
+
+ DEBUGF("__readid\n");
+
+ if (prepare_other_cmd(info, cmdset->read_id)) {
+ printk(KERN_ERR "failed to prepare command\n");
+ return -EINVAL;
+ }
+
+ /* Send command */
+ if (write_cmd(info))
+ goto fail_timeout;
+
+ /* Wait for CMDDM(command done successfully) */
+ if (wait_for_event(info, NDSR_RDDREQ))
+ goto fail_timeout;
+
+ nand_readsl(info->mmio_base + NDDB, id_buff, 2);
+ *id = id_buff[0] | (id_buff[1] << 8);
+ return 0;
+
+fail_timeout:
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
+ udelay(10);
+ return -ETIMEDOUT;
+}
+
+static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
+ const struct pxa3xx_nand_flash *f)
+{
+#ifndef __U_BOOT__
+ struct platform_device *pdev = info->pdev;
+ struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+#endif /* __U_BOOT__ */
+ uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+
+ if (f->page_size != 2048 && f->page_size != 512)
+ return -EINVAL;
+
+ if (f->flash_width != 16 && f->flash_width != 8)
+ return -EINVAL;
+
+ /* calculate flash information */
+ info->oob_size = (f->page_size == 2048) ? 64 : 16;
+/* TODO: what about 5 id byte or ONFI flash? */
+ info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
+
+ /* calculate addressing information */
+ info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;
+
+ if (f->num_blocks * f->page_per_block > 65536)
+ info->row_addr_cycles = 3;
+ else
+ info->row_addr_cycles = 2;
+
+#ifdef __U_BOOT__
+ ndcr |= NDCR_ND_ARB_EN;
+#else /* __U_BOOT__ */
+ ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
+#endif /* __U_BOOT__ */
+ ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+ ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;
+ ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;
+ ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;
+ ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
+
+ ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes);
+ ndcr |= NDCR_SPARE_EN; /* enable spare by default */
+
+ info->reg_ndcr = ndcr;
+
+ pxa3xx_nand_set_timing(info, f->timing);
+ info->flash_info = f;
+ return 0;
+}
+
+#ifndef __U_BOOT__
+static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
+ struct pxa3xx_nand_timing *t)
+{
+ unsigned long nand_clk = clk_get_rate(info->clk);
+ uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
+ uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
+
+ t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
+ t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
+ t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
+ t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
+ t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
+ t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
+
+ t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
+ t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
+ t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
+}
+
+static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+{
+ uint32_t ndcr = nand_readl(info, NDCR);
+ struct nand_flash_dev *type = NULL;
+ uint32_t id = -1;
+ int i;
+
+ default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
+ default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
+ default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
+ default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
+
+ if (default_flash.page_size == 2048)
+ default_flash.cmdset = &largepage_cmdset;
+ else
+ default_flash.cmdset = &smallpage_cmdset;
+
+ /* set info fields needed to __readid */
+ info->flash_info = &default_flash;
+ info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
+ info->reg_ndcr = ndcr;
+
+ if (__readid(info, &id))
+ return -ENODEV;
+
+ /* Lookup the flash id */
+ id = (id >> 8) & 0xff; /* device id is byte 2 */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (id == nand_flash_ids[i].id) {
+ type = &nand_flash_ids[i];
+ break;
+ }
+ }
+
+ if (!type)
+ return -ENODEV;
+
+ /* fill the missing flash information */
+ i = __ffs(default_flash.page_per_block * default_flash.page_size);
+ default_flash.num_blocks = type->chipsize << (20 - i);
+
+ info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
+
+ /* calculate addressing information */
+ info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
+
+ if (default_flash.num_blocks * default_flash.page_per_block > 65536)
+ info->row_addr_cycles = 3;
+ else
+ info->row_addr_cycles = 2;
+
+ pxa3xx_nand_detect_timing(info, &default_timing);
+ default_flash.timing = &default_timing;
+
+ return 0;
+}
+
+static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, const struct
+ pxa3xx_nand_platform_data *pdata)
+#else /* __U_BOOT__ */
+static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info)
+#endif /* __U_BOOT__ */
+{
+ const struct pxa3xx_nand_flash *f;
+ uint32_t id = -1;
+ int i;
+
+#ifndef __U_BOOT__
+ if (pdata->keep_config)
+ if (pxa3xx_nand_detect_config(info) == 0)
+ return 0;
+
+ for (i = 0; i < pdata->num_flash; ++i) {
+ f = pdata->flash + i;
+
+ if (pxa3xx_nand_config_flash(info, f))
+ continue;
+
+ if (__readid(info, &id))
+ continue;
+
+ if (id == f->chip_id)
+ return 0;
+ }
+#endif /* __U_BOOT__ */
+
+#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
+ for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) {
+
+ f = builtin_flash_types[i];
+
+ if (pxa3xx_nand_config_flash(info, f))
+ continue;
+
+ if (__readid(info, &id))
+ continue;
+
+ if (id == f->chip_id)
+ return 0;
+ }
+#endif
+
+#ifdef __U_BOOT__
+ printk(KERN_WARNING
+#else /* __U_BOOT__ */
+ dev_warn(&info->pdev->dev,
+#endif /* __U_BOOT__ */
+ "failed to detect configured NAND flash; found id %04x\n",
+ id);
+ return -ENODEV;
+}
+
+/* the maximum possible buffer size for large page with OOB data
+ * is: 2048 + 64 = 2112 bytes, allocate a page here for both the
+ * data buffer and the DMA descriptor
+ */
+#define MAX_BUFF_SIZE PAGE_SIZE
+
+static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+{
+#ifdef __U_BOOT__
+ info->data_buff = &static_data_buff[0];
+#else /* __U_BOOT__ */
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ struct platform_device *pdev = info->pdev;
+ int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc);
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+ if (use_dma == 0) {
+ info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
+ if (info->data_buff == NULL)
+ return -ENOMEM;
+ return 0;
+ }
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+ &info->data_buff_phys, GFP_KERNEL);
+ if (info->data_buff == NULL) {
+ dev_err(&pdev->dev, "failed to allocate dma buffer\n");
+ return -ENOMEM;
+ }
+
+ info->data_buff_size = MAX_BUFF_SIZE;
+ info->data_desc = (void *)info->data_buff + data_desc_offset;
+ info->data_desc_addr = info->data_buff_phys + data_desc_offset;
+
+ info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
+ pxa3xx_nand_data_dma_irq, info);
+ if (info->data_dma_ch < 0) {
+ dev_err(&pdev->dev, "failed to request data dma\n");
+ dma_free_coherent(&pdev->dev, info->data_buff_size,
+ info->data_buff, info->data_buff_phys);
+ return info->data_dma_ch;
+ }
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+#endif /* __U_BOOT__ */
+
+ return 0;
+}
+
+static struct nand_ecclayout hw_smallpage_ecclayout = {
+ .eccbytes = 6,
+ .eccpos = {8, 9, 10, 11, 12, 13 },
+ .oobfree = { {2, 6} }
+};
+
+static struct nand_ecclayout hw_largepage_ecclayout = {
+ .eccbytes = 24,
+ .eccpos = {
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = { {2, 38} }
+};
+
+static void pxa3xx_nand_init_mtd(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct pxa3xx_nand_info *info = this->priv;
+ const struct pxa3xx_nand_flash *f = info->flash_info;
+
+ this->options = NAND_USE_FLASH_BBT |
+ ((f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0);
+
+ this->waitfunc = pxa3xx_nand_waitfunc;
+ this->select_chip = pxa3xx_nand_select_chip;
+ this->dev_ready = pxa3xx_nand_dev_ready;
+ this->cmdfunc = pxa3xx_nand_cmdfunc;
+ this->read_word = pxa3xx_nand_read_word;
+ this->read_byte = pxa3xx_nand_read_byte;
+ this->read_buf = pxa3xx_nand_read_buf;
+ this->write_buf = pxa3xx_nand_write_buf;
+ this->verify_buf = pxa3xx_nand_verify_buf;
+
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.hwctl = pxa3xx_nand_ecc_hwctl;
+ this->ecc.calculate = pxa3xx_nand_ecc_calculate;
+ this->ecc.correct = pxa3xx_nand_ecc_correct;
+ this->ecc.size = f->page_size;
+
+ if (f->page_size == 2048)
+ this->ecc.layout = &hw_largepage_ecclayout;
+ else
+ this->ecc.layout = &hw_smallpage_ecclayout;
+
+ this->chip_delay = 25;
+}
+
+#ifdef __U_BOOT__
+/* To be called from board-specific NAND initialization board_nand_init()
+ after having set up DFC GPIOs. */
+int pxa3xx_nand_probe(struct nand_chip *chip)
+{
+ struct pxa3xx_nand_info *info = &static_info;
+ struct mtd_info temp_mtd;
+ struct mtd_info *mtd = &temp_mtd;
+#else /* __U_BOOT__ */
+static int pxa3xx_nand_probe(struct platform_device *pdev)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ struct pxa3xx_nand_info *info;
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct resource *r;
+ int irq;
+#endif /* __U_BOOT__ */
+ int ret = 0;
+
+#ifndef __U_BOOT__
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -ENODEV;
+ }
+
+ mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
+ GFP_KERNEL);
+ if (!mtd) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ info = (struct pxa3xx_nand_info *)(&mtd[1]);
+ info->pdev = pdev;
+
+ this = &info->nand_chip;
+ mtd->priv = info;
+ mtd->owner = THIS_MODULE;
+
+ info->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to get nand clock\n");
+ ret = PTR_ERR(info->clk);
+ goto fail_free_mtd;
+ }
+ clk_enable(info->clk);
+#else /* __U_BOOT__ */
+ mtd->priv = chip;
+ chip->priv = info;
+
+ /* turn on the NAND controller clock */
+ CKENA |= (CKENA_4_NAND | CKENA_9_SMC);
+#endif /* __U_BOOT__ */
+
+#ifndef __U_BOOT__
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no resource defined for data DMA\n");
+ ret = -ENXIO;
+ goto fail_put_clk;
+ }
+ info->drcmr_dat = r->start;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no resource defined for command DMA\n");
+ ret = -ENXIO;
+ goto fail_put_clk;
+ }
+ info->drcmr_cmd = r->start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource defined\n");
+ ret = -ENXIO;
+ goto fail_put_clk;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no IO memory resource defined\n");
+ ret = -ENODEV;
+ goto fail_put_clk;
+ }
+
+ r = request_mem_region(r->start, resource_size(r), pdev->name);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "failed to request memory resource\n");
+ ret = -EBUSY;
+ goto fail_put_clk;
+ }
+
+ info->mmio_base = ioremap(r->start, resource_size(r));
+ if (info->mmio_base == NULL) {
+ dev_err(&pdev->dev, "ioremap() failed\n");
+ ret = -ENODEV;
+ goto fail_free_res;
+ }
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ info->mmio_phys = r->start;
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+#else /* __U_BOOT__ */
+ info->mmio_base = (void __iomem *)0x43100000;
+#endif /* __U_BOOT__ */
+
+ ret = pxa3xx_nand_init_buff(info);
+ if (ret)
+ goto fail_free_io;
+
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto fail_free_buf;
+ }
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+
+#ifdef __U_BOOT__
+ ret = pxa3xx_nand_detect_flash(info);
+ if (ret) {
+ printk(KERN_ERR "failed to detect flash\n");
+#else /* __U_BOOT__ */
+ ret = pxa3xx_nand_detect_flash(info, pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to detect flash\n");
+#endif /* __U_BOOT__ */
+ ret = -ENODEV;
+ goto fail_free_irq;
+ }
+
+ pxa3xx_nand_init_mtd(mtd);
+
+#ifndef __U_BOOT__
+ platform_set_drvdata(pdev, mtd);
+
+ if (nand_scan(mtd, 1)) {
+ dev_err(&pdev->dev, "failed to scan nand\n");
+ ret = -ENXIO;
+ goto fail_free_irq;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ ret = add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts);
+#endif
+#endif /* __U_BOOT__ */
+ return ret;
+
+fail_free_irq:
+#ifndef __U_BOOT__
+ free_irq(IRQ_NAND, info);
+#endif /* __U_BOOT__ */
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+fail_free_buf:
+ if (use_dma) {
+ pxa_free_dma(info->data_dma_ch);
+ dma_free_coherent(&pdev->dev, info->data_buff_size,
+ info->data_buff, info->data_buff_phys);
+ } else
+ kfree(info->data_buff);
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+fail_free_io:
+#ifndef __U_BOOT__
+ iounmap(info->mmio_base);
+fail_free_res:
+ release_mem_region(r->start, resource_size(r));
+fail_put_clk:
+ clk_disable(info->clk);
+ clk_put(info->clk);
+fail_free_mtd:
+ kfree(mtd);
+#endif /* __U_BOOT__ */
+ return ret;
+}
+
+#ifndef __U_BOOT__
+static int pxa3xx_nand_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct pxa3xx_nand_info *info = mtd->priv;
+ struct resource *r;
+ int irq;
+
+ platform_set_drvdata(pdev, NULL);
+
+ del_mtd_device(mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+ del_mtd_partitions(mtd);
+#endif
+#ifndef CONFIG_MTD_NAND_PXA3xx_POLLING
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ free_irq(irq, info);
+#endif /* CONFIG_MTD_NAND_PXA3xx_POLLING */
+ if (use_dma) {
+ pxa_free_dma(info->data_dma_ch);
+ dma_free_writecombine(&pdev->dev, info->data_buff_size,
+ info->data_buff, info->data_buff_phys);
+ } else
+ kfree(info->data_buff);
+
+ iounmap(info->mmio_base);
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(r->start, resource_size(r));
+
+ clk_disable(info->clk);
+ clk_put(info->clk);
+
+ kfree(mtd);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
+ struct pxa3xx_nand_info *info = mtd->priv;
+
+ if (info->state != STATE_READY) {
+ dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int pxa3xx_nand_resume(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
+ struct pxa3xx_nand_info *info = mtd->priv;
+
+ clk_enable(info->clk);
+
+ return pxa3xx_nand_config_flash(info, info->flash_info);
+}
+#else /* CONFIG_PM */
+#define pxa3xx_nand_suspend NULL
+#define pxa3xx_nand_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver pxa3xx_nand_driver = {
+ .driver = {
+ .name = "pxa3xx-nand",
+ },
+ .probe = pxa3xx_nand_probe,
+ .remove = pxa3xx_nand_remove,
+ .suspend = pxa3xx_nand_suspend,
+ .resume = pxa3xx_nand_resume,
+};
+
+static int __init pxa3xx_nand_init(void)
+{
+ return platform_driver_register(&pxa3xx_nand_driver);
+}
+module_init(pxa3xx_nand_init);
+
+static void __exit pxa3xx_nand_exit(void)
+{
+ platform_driver_unregister(&pxa3xx_nand_driver);
+}
+module_exit(pxa3xx_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PXA3xx NAND controller driver");
+#endif /* __U_BOOT__ */
diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h
index a25d4c5..1a00acb 100644
--- a/include/asm-arm/arch-pxa/pxa-regs.h
+++ b/include/asm-arm/arch-pxa/pxa-regs.h
@@ -1230,13 +1230,13 @@ typedef void (*ExcpHndlr) (void) ;
#define GPIO4 __REG(0x40e10134)
#define nXCVREN __REG(0x40e10138)
-#define DF_CLE_NOE __REG(0x40e10204)
-#define DF_ALE_WE1 __REG(0x40e10208)
+#define DF_CLE_nOE __REG(0x40e10204)
+#define DF_ALE_nWE1 __REG(0x40e10208)
#define DF_SCLK_E __REG(0x40e10210)
#define nBE0 __REG(0x40e10214)
#define nBE1 __REG(0x40e10218)
-#define DF_ALE_WE2 __REG(0x40e1021c)
+#define DF_ALE_nWE2 __REG(0x40e1021c)
#define DF_INT_RnB __REG(0x40e10220)
#define DF_nCS0 __REG(0x40e10224)
#define DF_nCS1 __REG(0x40e10228)
@@ -2273,95 +2273,6 @@ typedef void (*ExcpHndlr) (void) ;
#define MDCNFG_DCSE1 0x2 /* SDRAM CS 1 Enable */
#define MDCNFG_DCSE0 0x1 /* SDRAM CS 0 Enable */
-
-/* Data Flash Controller Registers */
-
-#define NDCR __REG(0x43100000) /* Data Flash Control register */
-#define NDTR0CS0 __REG(0x43100004) /* Data Controller Timing Parameter 0 Register for ND_nCS0 */
-/* #define NDTR0CS1 __REG(0x43100008) /\* Data Controller Timing Parameter 0 Register for ND_nCS1 *\/ */
-#define NDTR1CS0 __REG(0x4310000C) /* Data Controller Timing Parameter 1 Register for ND_nCS0 */
-/* #define NDTR1CS1 __REG(0x43100010) /\* Data Controller Timing Parameter 1 Register for ND_nCS1 *\/ */
-#define NDSR __REG(0x43100014) /* Data Controller Status Register */
-#define NDPCR __REG(0x43100018) /* Data Controller Page Count Register */
-#define NDBDR0 __REG(0x4310001C) /* Data Controller Bad Block Register 0 */
-#define NDBDR1 __REG(0x43100020) /* Data Controller Bad Block Register 1 */
-#define NDDB __REG(0x43100040) /* Data Controller Data Buffer */
-#define NDCB0 __REG(0x43100048) /* Data Controller Command Buffer0 */
-#define NDCB1 __REG(0x4310004C) /* Data Controller Command Buffer1 */
-#define NDCB2 __REG(0x43100050) /* Data Controller Command Buffer2 */
-
-#define NDCR_SPARE_EN (0x1<<31)
-#define NDCR_ECC_EN (0x1<<30)
-#define NDCR_DMA_EN (0x1<<29)
-#define NDCR_ND_RUN (0x1<<28)
-#define NDCR_DWIDTH_C (0x1<<27)
-#define NDCR_DWIDTH_M (0x1<<26)
-#define NDCR_PAGE_SZ (0x3<<24)
-#define NDCR_NCSX (0x1<<23)
-#define NDCR_ND_STOP (0x1<<22)
-/* reserved:
- * #define NDCR_ND_MODE (0x3<<21)
- * #define NDCR_NAND_MODE 0x0 */
-#define NDCR_CLR_PG_CNT (0x1<<20)
-#define NDCR_CLR_ECC (0x1<<19)
-#define NDCR_RD_ID_CNT (0x7<<16)
-#define NDCR_RA_START (0x1<<15)
-#define NDCR_PG_PER_BLK (0x1<<14)
-#define NDCR_ND_ARB_EN (0x1<<12)
-#define NDCR_RDYM (0x1<<11)
-#define NDCR_CS0_PAGEDM (0x1<<10)
-#define NDCR_CS1_PAGEDM (0x1<<9)
-#define NDCR_CS0_CMDDM (0x1<<8)
-#define NDCR_CS1_CMDDM (0x1<<7)
-#define NDCR_CS0_BBDM (0x1<<6)
-#define NDCR_CS1_BBDM (0x1<<5)
-#define NDCR_DBERRM (0x1<<4)
-#define NDCR_SBERRM (0x1<<3)
-#define NDCR_WRDREQM (0x1<<2)
-#define NDCR_RDDREQM (0x1<<1)
-#define NDCR_WRCMDREQM (0x1)
-
-#define NDSR_RDY (0x1<<11)
-#define NDSR_CS0_PAGED (0x1<<10)
-#define NDSR_CS1_PAGED (0x1<<9)
-#define NDSR_CS0_CMDD (0x1<<8)
-#define NDSR_CS1_CMDD (0x1<<7)
-#define NDSR_CS0_BBD (0x1<<6)
-#define NDSR_CS1_BBD (0x1<<5)
-#define NDSR_DBERR (0x1<<4)
-#define NDSR_SBERR (0x1<<3)
-#define NDSR_WRDREQ (0x1<<2)
-#define NDSR_RDDREQ (0x1<<1)
-#define NDSR_WRCMDREQ (0x1)
-
-#define NDCB0_AUTO_RS (0x1<<25)
-#define NDCB0_CSEL (0x1<<24)
-#define NDCB0_CMD_TYPE (0x7<<21)
-#define NDCB0_NC (0x1<<20)
-#define NDCB0_DBC (0x1<<19)
-#define NDCB0_ADDR_CYC (0x7<<16)
-#define NDCB0_CMD2 (0xff<<8)
-#define NDCB0_CMD1 (0xff)
-#define MCMEM(s) MCMEM0
-#define MCATT(s) MCATT0
-#define MCIO(s) MCIO0
-#define MECR_CIT (1 << 1)/* Card Is There: 0 -> no card, 1 -> card inserted */
-
-/* Maximum values for NAND Interface Timing Registers in DFC clock
- * periods */
-#define DFC_MAX_tCH 7
-#define DFC_MAX_tCS 7
-#define DFC_MAX_tWH 7
-#define DFC_MAX_tWP 7
-#define DFC_MAX_tRH 7
-#define DFC_MAX_tRP 15
-#define DFC_MAX_tR 65535
-#define DFC_MAX_tWHR 15
-#define DFC_MAX_tAR 15
-
-#define DFC_CLOCK 104 /* DFC Clock is 104 MHz */
-#define DFC_CLK_PER_US DFC_CLOCK/1000 /* clock period in ns */
-
#else /* CONFIG_CPU_MONAHANS */
#define MEMC_BASE __REG(0x48000000) /* Base of Memory Controller */
diff --git a/include/asm-arm/arch-pxa/pxa3xx_nand.h b/include/asm-arm/arch-pxa/pxa3xx_nand.h
new file mode 100644
index 0000000..078dd02
--- /dev/null
+++ b/include/asm-arm/arch-pxa/pxa3xx_nand.h
@@ -0,0 +1,67 @@
+#ifndef __ASM_ARCH_PXA3XX_NAND_H
+#define __ASM_ARCH_PXA3XX_NAND_H
+
+#include <linux/mtd/mtd.h>
+#ifndef __U_BOOT__
+#include <linux/mtd/partitions.h>
+#endif /* __U_BOOT__ */
+
+struct pxa3xx_nand_timing {
+ unsigned int tCH; /* Enable signal hold time */
+ unsigned int tCS; /* Enable signal setup time */
+ unsigned int tWH; /* ND_nWE high duration */
+ unsigned int tWP; /* ND_nWE pulse time */
+ unsigned int tRH; /* ND_nRE high duration */
+ unsigned int tRP; /* ND_nRE pulse width */
+ unsigned int tR; /* ND_nWE high to ND_nRE low for read */
+ unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
+ unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
+};
+
+struct pxa3xx_nand_cmdset {
+ uint16_t read1;
+ uint16_t read2;
+ uint16_t program;
+ uint16_t read_status;
+ uint16_t read_id;
+ uint16_t erase;
+ uint16_t reset;
+ uint16_t lock;
+ uint16_t unlock;
+ uint16_t lock_status;
+};
+
+struct pxa3xx_nand_flash {
+ const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
+ const struct pxa3xx_nand_cmdset *cmdset;
+
+ uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
+ uint32_t page_size; /* Page size in bytes (PAGE_SZ) */
+ uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
+ uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
+ uint32_t num_blocks; /* Number of physical blocks in Flash */
+ uint32_t chip_id;
+};
+
+#ifndef __U_BOOT__
+struct pxa3xx_nand_platform_data {
+
+ /* the data flash bus is shared between the Static Memory
+ * Controller and the Data Flash Controller, the arbiter
+ * controls the ownership of the bus
+ */
+ int enable_arbiter;
+
+ /* allow platform code to keep OBM/bootloader defined NFC config */
+ int keep_config;
+
+ const struct mtd_partition *parts;
+ unsigned int nr_parts;
+
+ const struct pxa3xx_nand_flash * flash;
+ size_t num_flash;
+};
+
+extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info);
+#endif /* __U_BOOT__ */
+#endif /* __ASM_ARCH_PXA3XX_NAND_H */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 2/3] delta: use new generic NAND controller driver
2009-12-24 3:29 [U-Boot] [PATCH 0/3] Monahan/PXA3xx: integrate Linux NAND controller driver Marcel Ziswiler
2009-12-24 3:33 ` [U-Boot] [PATCH 1/3] " Marcel Ziswiler
@ 2009-12-24 3:36 ` Marcel Ziswiler
2009-12-24 3:38 ` [U-Boot] [PATCH 3/3] zylonite: " Marcel Ziswiler
2 siblings, 0 replies; 7+ messages in thread
From: Marcel Ziswiler @ 2009-12-24 3:36 UTC (permalink / raw)
To: u-boot
This patch adapts the Monahan/PXA3xx board delta to use the new generic NAND
controller driver.
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@noser.com>
---
board/delta/Makefile | 2 +-
board/delta/delta.c | 50 +++++
board/delta/nand.c | 554 -----------------------------------------------
include/configs/delta.h | 32 +--
4 files changed, 59 insertions(+), 579 deletions(-)
delete mode 100644 board/delta/nand.c
diff --git a/board/delta/Makefile b/board/delta/Makefile
index 648e00c..9b5545d 100644
--- a/board/delta/Makefile
+++ b/board/delta/Makefile
@@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS := delta.o nand.o
+COBJS := delta.o
SOBJS := lowlevel_init.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/board/delta/delta.c b/board/delta/delta.c
index a294213..680f846 100644
--- a/board/delta/delta.c
+++ b/board/delta/delta.c
@@ -28,9 +28,12 @@
#include <malloc.h>
#include <command.h>
#include <asm/arch/pxa-regs.h>
+#include <nand.h>
DECLARE_GLOBAL_DATA_PTR;
+extern int pxa3xx_nand_probe(struct nand_chip *chip);
+
/* ------------------------------------------------------------------------- */
static void init_DA9030(void);
@@ -96,6 +99,53 @@ int board_late_init(void)
}
/*
+ * Board-specific NAND initialization.
+ */
+int board_nand_init(struct nand_chip *chip)
+{
+ /* no idea what is done here, see zylonite.c */
+ GPIO4 = 0x1;
+
+ /* Setting up DFC GPIOs. */
+
+ /* ND_IO<15:0> alternate function 1 */
+ DF_IO0 = 0x00000001;
+ DF_IO1 = 0x00000001;
+ DF_IO2 = 0x00000001;
+ DF_IO3 = 0x00000001;
+ DF_IO4 = 0x00000001;
+ DF_IO5 = 0x00000001;
+ DF_IO6 = 0x00000001;
+ DF_IO7 = 0x00000001;
+ DF_IO8 = 0x00000001;
+ DF_IO9 = 0x00000001;
+ DF_IO10 = 0x00000001;
+ DF_IO11 = 0x00000001;
+ DF_IO12 = 0x00000001;
+ DF_IO13 = 0x00000001;
+ DF_IO14 = 0x00000001;
+ DF_IO15 = 0x00000001;
+ /* ND_nCS0 and ND_nCS1 alternate function 1 */
+ DF_nCS0 = 0x00000001;
+ DF_nCS1 = 0x00000001;
+ /* ND_nWE and ND_nRE alternate function 1, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_nWE = 0x00001901;
+ DF_nRE = 0x00001901;
+ /* ND_CLE alternate function 0 */
+ DF_CLE_nOE = 0x00001900;
+ /* ND_ALE alternate function 1, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_ALE_nWE1 = 0x00001901;
+ DF_ALE_nWE2 = 0x00001901;
+ /* ND_RnB alternate function 0, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_INT_RnB = 0x00001900;
+
+ return pxa3xx_nand_probe(chip);
+}
+
+/*
* Magic Key Handling, mainly copied from board/lwmon/lwmon.c
*/
#ifdef DELTA_CHECK_KEYBD
diff --git a/board/delta/nand.c b/board/delta/nand.c
deleted file mode 100644
index 85a6ba2..0000000
--- a/board/delta/nand.c
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * (C) Copyright 2006 DENX Software Engineering
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-
-#if defined(CONFIG_CMD_NAND)
-
-#include <nand.h>
-#include <asm/arch/pxa-regs.h>
-
-#ifdef CONFIG_SYS_DFC_DEBUG1
-# define DFC_DEBUG1(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG1(fmt, args...)
-#endif
-
-#ifdef CONFIG_SYS_DFC_DEBUG2
-# define DFC_DEBUG2(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG2(fmt, args...)
-#endif
-
-#ifdef CONFIG_SYS_DFC_DEBUG3
-# define DFC_DEBUG3(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG3(fmt, args...)
-#endif
-
-/* These really don't belong here, as they are specific to the NAND Model */
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr delta_bbt_descr = {
- .options = 0,
- .offs = 0,
- .len = 2,
- .pattern = scan_ff_pattern
-};
-
-static struct nand_ecclayout delta_oob = {
- .eccbytes = 6,
- .eccpos = {2, 3, 4, 5, 6, 7},
- .oobfree = { {8, 2}, {12, 4} }
-};
-
-/*
- * not required for Monahans DFC
- */
-static void dfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- return;
-}
-
-#if 0
-/* read device ready pin */
-static int dfc_device_ready(struct mtd_info *mtdinfo)
-{
- if(NDSR & NDSR_RDY)
- return 1;
- else
- return 0;
- return 0;
-}
-#endif
-
-/*
- * Write buf to the DFC Controller Data Buffer
- */
-static void dfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- unsigned long bytes_multi = len & 0xfffffffc;
- unsigned long rest = len & 0x3;
- unsigned long *long_buf;
- int i;
-
- DFC_DEBUG2("dfc_write_buf: writing %d bytes starting with 0x%x.\n", len, *((unsigned long*) buf));
- if(bytes_multi) {
- for(i=0; i<bytes_multi; i+=4) {
- long_buf = (unsigned long*) &buf[i];
- NDDB = *long_buf;
- }
- }
- if(rest) {
- printf("dfc_write_buf: ERROR, writing non 4-byte aligned data.\n");
- }
- return;
-}
-
-
-static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
-{
- int i=0, j;
-
- /* we have to be carefull not to overflow the buffer if len is
- * not a multiple of 4 */
- unsigned long bytes_multi = len & 0xfffffffc;
- unsigned long rest = len & 0x3;
- unsigned long *long_buf;
-
- DFC_DEBUG3("dfc_read_buf: reading %d bytes.\n", len);
- /* if there are any, first copy multiple of 4 bytes */
- if(bytes_multi) {
- for(i=0; i<bytes_multi; i+=4) {
- long_buf = (unsigned long*) &buf[i];
- *long_buf = NDDB;
- }
- }
-
- /* ...then the rest */
- if(rest) {
- unsigned long rest_data = NDDB;
- for(j=0;j<rest; j++)
- buf[i+j] = (u_char) ((rest_data>>j) & 0xff);
- }
-
- return;
-}
-
-/*
- * read a word. Not implemented as not used in NAND code.
- */
-static u16 dfc_read_word(struct mtd_info *mtd)
-{
- printf("dfc_read_word: UNIMPLEMENTED.\n");
- return 0;
-}
-
-/* global var, too bad: mk at tbd: move to ->priv pointer */
-static unsigned long read_buf = 0;
-static int bytes_read = -1;
-
-/*
- * read a byte from NDDB Because we can only read 4 bytes from NDDB at
- * a time, we buffer the remaining bytes. The buffer is reset when a
- * new command is sent to the chip.
- *
- * WARNING:
- * This function is currently only used to read status and id
- * bytes. For these commands always 8 bytes need to be read from
- * NDDB. So we read and discard these bytes right now. In case this
- * function is used for anything else in the future, we must check
- * what was the last command issued and read the appropriate amount of
- * bytes respectively.
- */
-static u_char dfc_read_byte(struct mtd_info *mtd)
-{
- unsigned char byte;
- unsigned long dummy;
-
- if(bytes_read < 0) {
- read_buf = NDDB;
- dummy = NDDB;
- bytes_read = 0;
- }
- byte = (unsigned char) (read_buf>>(8 * bytes_read++));
- if(bytes_read >= 4)
- bytes_read = -1;
-
- DFC_DEBUG2("dfc_read_byte: byte %u: 0x%x of (0x%x).\n", bytes_read - 1, byte, read_buf);
- return byte;
-}
-
-/* calculate delta between OSCR values start and now */
-static unsigned long get_delta(unsigned long start)
-{
- unsigned long cur = OSCR;
-
- if(cur < start) /* OSCR overflowed */
- return (cur + (start^0xffffffff));
- else
- return (cur - start);
-}
-
-/* delay function, this doesn't belong here */
-static void wait_us(unsigned long us)
-{
- unsigned long start = OSCR;
- us = DIV_ROUND_UP(us * OSCR_CLK_FREQ, 1000);
-
- while (get_delta(start) < us) {
- /* do nothing */
- }
-}
-
-static void dfc_clear_nddb(void)
-{
- NDCR &= ~NDCR_ND_RUN;
- wait_us(CONFIG_SYS_NAND_OTHER_TO);
-}
-
-/* wait_event with timeout */
-static unsigned long dfc_wait_event(unsigned long event)
-{
- unsigned long ndsr, timeout, start = OSCR;
-
- if(!event)
- return 0xff000000;
- else if(event & (NDSR_CS0_CMDD | NDSR_CS0_BBD))
- timeout = DIV_ROUND_UP(CONFIG_SYS_NAND_PROG_ERASE_TO
- * OSCR_CLK_FREQ, 1000);
- else
- timeout = DIV_ROUND_UP(CONFIG_SYS_NAND_OTHER_TO
- * OSCR_CLK_FREQ, 1000);
-
- while(1) {
- ndsr = NDSR;
- if(ndsr & event) {
- NDSR |= event;
- break;
- }
- if(get_delta(start) > timeout) {
- DFC_DEBUG1("dfc_wait_event: TIMEOUT waiting for event: 0x%lx.\n", event);
- return 0xff000000;
- }
-
- }
- return ndsr;
-}
-
-/* we don't always wan't to do this */
-static void dfc_new_cmd(void)
-{
- int retry = 0;
- unsigned long status;
-
- while(retry++ <= CONFIG_SYS_NAND_SENDCMD_RETRY) {
- /* Clear NDSR */
- NDSR = 0xFFF;
-
- /* set NDCR[NDRUN] */
- if(!(NDCR & NDCR_ND_RUN))
- NDCR |= NDCR_ND_RUN;
-
- status = dfc_wait_event(NDSR_WRCMDREQ);
-
- if(status & NDSR_WRCMDREQ)
- return;
-
- DFC_DEBUG2("dfc_new_cmd: FAILED to get WRITECMDREQ, retry: %d.\n", retry);
- dfc_clear_nddb();
- }
- DFC_DEBUG1("dfc_new_cmd: giving up after %d retries.\n", retry);
-}
-
-/* this function is called after Programm and Erase Operations to
- * check for success or failure */
-static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this)
-{
- unsigned long ndsr=0, event=0;
- int state = this->state;
-
- if(state == FL_WRITING) {
- event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
- } else if(state == FL_ERASING) {
- event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
- }
-
- ndsr = dfc_wait_event(event);
-
- if((ndsr & NDSR_CS0_BBD) || (ndsr & 0xff000000))
- return(0x1); /* Status Read error */
- return 0;
-}
-
-/* cmdfunc send commands to the DFC */
-static void dfc_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- /* register struct nand_chip *this = mtd->priv; */
- unsigned long ndcb0=0, ndcb1=0, ndcb2=0, event=0;
-
- /* clear the ugly byte read buffer */
- bytes_read = -1;
- read_buf = 0;
-
- switch (command) {
- case NAND_CMD_READ0:
- DFC_DEBUG3("dfc_cmdfunc: NAND_CMD_READ0, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- dfc_new_cmd();
- ndcb0 = (NAND_CMD_READ0 | (4<<16));
- column >>= 1; /* adjust for 16 bit bus */
- ndcb1 = (((column>>1) & 0xff) |
- ((page_addr<<8) & 0xff00) |
- ((page_addr<<8) & 0xff0000) |
- ((page_addr<<8) & 0xff000000)); /* make this 0x01000000 ? */
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_READ1:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_READ1 unimplemented!\n");
- goto end;
- case NAND_CMD_READOOB:
- DFC_DEBUG1("dfc_cmdfunc: NAND_CMD_READOOB unimplemented!\n");
- goto end;
- case NAND_CMD_READID:
- dfc_new_cmd();
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_READID.\n");
- ndcb0 = (NAND_CMD_READID | (3 << 21) | (1 << 16)); /* addr cycles*/
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_PAGEPROG:
- /* sent as a multicommand in NAND_CMD_SEQIN */
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_PAGEPROG empty due to multicmd.\n");
- goto end;
- case NAND_CMD_ERASE1:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_ERASE1, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- dfc_new_cmd();
- ndcb0 = (0xd060 | (1<<25) | (2<<21) | (1<<19) | (3<<16));
- ndcb1 = (page_addr & 0x00ffffff);
- goto write_cmd;
- case NAND_CMD_ERASE2:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_ERASE2 empty due to multicmd.\n");
- goto end;
- case NAND_CMD_SEQIN:
- /* send PAGE_PROG command(0x1080) */
- dfc_new_cmd();
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- ndcb0 = (0x1080 | (1<<25) | (1<<21) | (1<<19) | (4<<16));
- column >>= 1; /* adjust for 16 bit bus */
- ndcb1 = (((column>>1) & 0xff) |
- ((page_addr<<8) & 0xff00) |
- ((page_addr<<8) & 0xff0000) |
- ((page_addr<<8) & 0xff000000)); /* make this 0x01000000 ? */
- event = NDSR_WRDREQ;
- goto write_cmd;
- case NAND_CMD_STATUS:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_STATUS.\n");
- dfc_new_cmd();
- ndcb0 = NAND_CMD_STATUS | (4<<21);
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_RESET:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_RESET.\n");
- ndcb0 = NAND_CMD_RESET | (5<<21);
- event = NDSR_CS0_CMDD;
- goto write_cmd;
- default:
- printk("dfc_cmdfunc: error, unsupported command.\n");
- goto end;
- }
-
- write_cmd:
- NDCB0 = ndcb0;
- NDCB0 = ndcb1;
- NDCB0 = ndcb2;
-
- /* wait_event: */
- dfc_wait_event(event);
- end:
- return;
-}
-
-static void dfc_gpio_init(void)
-{
- DFC_DEBUG2("Setting up DFC GPIO's.\n");
-
- /* no idea what is done here, see zylonite.c */
- GPIO4 = 0x1;
-
- DF_ALE_WE1 = 0x00000001;
- DF_ALE_WE2 = 0x00000001;
- DF_nCS0 = 0x00000001;
- DF_nCS1 = 0x00000001;
- DF_nWE = 0x00000001;
- DF_nRE = 0x00000001;
- DF_IO0 = 0x00000001;
- DF_IO8 = 0x00000001;
- DF_IO1 = 0x00000001;
- DF_IO9 = 0x00000001;
- DF_IO2 = 0x00000001;
- DF_IO10 = 0x00000001;
- DF_IO3 = 0x00000001;
- DF_IO11 = 0x00000001;
- DF_IO4 = 0x00000001;
- DF_IO12 = 0x00000001;
- DF_IO5 = 0x00000001;
- DF_IO13 = 0x00000001;
- DF_IO6 = 0x00000001;
- DF_IO14 = 0x00000001;
- DF_IO7 = 0x00000001;
- DF_IO15 = 0x00000001;
-
- DF_nWE = 0x1901;
- DF_nRE = 0x1901;
- DF_CLE_NOE = 0x1900;
- DF_ALE_WE1 = 0x1901;
- DF_INT_RnB = 0x1900;
-}
-
-/*
- * Board-specific NAND initialization. The following members of the
- * argument are board-specific (per include/linux/mtd/nand_new.h):
- * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
- * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
- * - hwcontrol: hardwarespecific function for accesing control-lines
- * - dev_ready: hardwarespecific function for accesing device ready/busy line
- * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
- * only be provided if a hardware ECC is available
- * - ecc.mode: mode of ecc, see defines
- * - chip_delay: chip dependent delay for transfering data from array to
- * read regs (tR)
- * - options: various chip options. They can partly be set to inform
- * nand_scan about special functionality. See the defines for further
- * explanation
- * Members with a "?" were not set in the merged testing-NAND branch,
- * so they are not set here either.
- */
-int board_nand_init(struct nand_chip *nand)
-{
- unsigned long tCH, tCS, tWH, tWP, tRH, tRP, tRP_high, tR, tWHR, tAR;
-
- /* set up GPIO Control Registers */
- dfc_gpio_init();
-
- /* turn on the NAND Controller Clock (104 MHz @ D0) */
- CKENA |= (CKENA_4_NAND | CKENA_9_SMC);
-
-#undef CONFIG_SYS_TIMING_TIGHT
-#ifndef CONFIG_SYS_TIMING_TIGHT
- tCH = MIN(((unsigned long) (NAND_TIMING_tCH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tCH);
- tCS = MIN(((unsigned long) (NAND_TIMING_tCS * DFC_CLK_PER_US) + 1),
- DFC_MAX_tCS);
- tWH = MIN(((unsigned long) (NAND_TIMING_tWH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWH);
- tWP = MIN(((unsigned long) (NAND_TIMING_tWP * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWP);
- tRH = MIN(((unsigned long) (NAND_TIMING_tRH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tRH);
- tRP = MIN(((unsigned long) (NAND_TIMING_tRP * DFC_CLK_PER_US) + 1),
- DFC_MAX_tRP);
- tR = MIN(((unsigned long) (NAND_TIMING_tR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tR);
- tWHR = MIN(((unsigned long) (NAND_TIMING_tWHR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWHR);
- tAR = MIN(((unsigned long) (NAND_TIMING_tAR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tAR);
-#else /* this is the tight timing */
-
- tCH = MIN(((unsigned long) (NAND_TIMING_tCH * DFC_CLK_PER_US)),
- DFC_MAX_tCH);
- tCS = MIN(((unsigned long) (NAND_TIMING_tCS * DFC_CLK_PER_US)),
- DFC_MAX_tCS);
- tWH = MIN(((unsigned long) (NAND_TIMING_tWH * DFC_CLK_PER_US)),
- DFC_MAX_tWH);
- tWP = MIN(((unsigned long) (NAND_TIMING_tWP * DFC_CLK_PER_US)),
- DFC_MAX_tWP);
- tRH = MIN(((unsigned long) (NAND_TIMING_tRH * DFC_CLK_PER_US)),
- DFC_MAX_tRH);
- tRP = MIN(((unsigned long) (NAND_TIMING_tRP * DFC_CLK_PER_US)),
- DFC_MAX_tRP);
- tR = MIN(((unsigned long) (NAND_TIMING_tR * DFC_CLK_PER_US) - tCH - 2),
- DFC_MAX_tR);
- tWHR = MIN(((unsigned long) (NAND_TIMING_tWHR * DFC_CLK_PER_US) - tCH - 2),
- DFC_MAX_tWHR);
- tAR = MIN(((unsigned long) (NAND_TIMING_tAR * DFC_CLK_PER_US) - 2),
- DFC_MAX_tAR);
-#endif /* CONFIG_SYS_TIMING_TIGHT */
-
-
- DFC_DEBUG2("tCH=%u, tCS=%u, tWH=%u, tWP=%u, tRH=%u, tRP=%u, tR=%u, tWHR=%u, tAR=%u.\n", tCH, tCS, tWH, tWP, tRH, tRP, tR, tWHR, tAR);
-
- /* tRP value is split in the register */
- if(tRP & (1 << 4)) {
- tRP_high = 1;
- tRP &= ~(1 << 4);
- } else {
- tRP_high = 0;
- }
-
- NDTR0CS0 = (tCH << 19) |
- (tCS << 16) |
- (tWH << 11) |
- (tWP << 8) |
- (tRP_high << 6) |
- (tRH << 3) |
- (tRP << 0);
-
- NDTR1CS0 = (tR << 16) |
- (tWHR << 4) |
- (tAR << 0);
-
- /* If it doesn't work (unlikely) think about:
- * - ecc enable
- * - chip select don't care
- * - read id byte count
- *
- * Intentionally enabled by not setting bits:
- * - dma (DMA_EN)
- * - page size = 512
- * - cs don't care, see if we can enable later!
- * - row address start position (after second cycle)
- * - pages per block = 32
- * - ND_RDY : clears command buffer
- */
- /* NDCR_NCSX | /\* Chip select busy don't care *\/ */
-
- NDCR = (NDCR_SPARE_EN | /* use the spare area */
- NDCR_DWIDTH_C | /* 16bit DFC data bus width */
- NDCR_DWIDTH_M | /* 16 bit Flash device data bus width */
- (2 << 16) | /* read id count = 7 ???? mk@tbd */
- NDCR_ND_ARB_EN | /* enable bus arbiter */
- NDCR_RDYM | /* flash device ready ir masked */
- NDCR_CS0_PAGEDM | /* ND_nCSx page done ir masked */
- NDCR_CS1_PAGEDM |
- NDCR_CS0_CMDDM | /* ND_CSx command done ir masked */
- NDCR_CS1_CMDDM |
- NDCR_CS0_BBDM | /* ND_CSx bad block detect ir masked */
- NDCR_CS1_BBDM |
- NDCR_DBERRM | /* double bit error ir masked */
- NDCR_SBERRM | /* single bit error ir masked */
- NDCR_WRDREQM | /* write data request ir masked */
- NDCR_RDDREQM | /* read data request ir masked */
- NDCR_WRCMDREQM); /* write command request ir masked */
-
-
- /* wait 10 us due to cmd buffer clear reset */
- /* wait(10); */
-
-
- nand->cmd_ctrl = dfc_hwcontrol;
-/* nand->dev_ready = dfc_device_ready; */
- nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.layout = &delta_oob;
- nand->options = NAND_BUSWIDTH_16;
- nand->waitfunc = dfc_wait;
- nand->read_byte = dfc_read_byte;
- nand->read_word = dfc_read_word;
- nand->read_buf = dfc_read_buf;
- nand->write_buf = dfc_write_buf;
-
- nand->cmdfunc = dfc_cmdfunc;
- nand->badblock_pattern = &delta_bbt_descr;
- return 0;
-}
-
-#endif
diff --git a/include/configs/delta.h b/include/configs/delta.h
index 9c46c5b..cea41b5 100644
--- a/include/configs/delta.h
+++ b/include/configs/delta.h
@@ -178,7 +178,6 @@
#define CONFIG_SYS_MONAHANS_RUN_MODE_OSC_RATIO 16 /* valid values: 8, 16, 24, 31 */
#define CONFIG_SYS_MONAHANS_TURBO_RUN_MODE_RATIO 1 /* valid values: 1, 2 */
-
/* valid baudrates */
#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 }
@@ -220,10 +219,13 @@
/*
* NAND Flash
*/
-#define CONFIG_SYS_NAND0_BASE 0x0 /* 0x43100040 */ /* 0x10000000 */
-#undef CONFIG_SYS_NAND1_BASE
+#define CONFIG_NAND_PXA3xx
+#define CONFIG_NAND_PXA3xx_CLK 104000000UL
+#define CONFIG_NEW_NAND_CODE
+#define CONFIG_SYS_NAND_HW_ECC
+
+#define CONFIG_SYS_NAND_BASE 0x0
-#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND0_BASE }
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */
/* nand timeout values */
@@ -232,32 +234,14 @@
#define CONFIG_SYS_NAND_SENDCMD_RETRY 3
#undef NAND_ALLOW_ERASE_ALL /* Allow erasing bad blocks - don't use */
-/* NAND Timing Parameters (in ns) */
-#define NAND_TIMING_tCH 10
-#define NAND_TIMING_tCS 0
-#define NAND_TIMING_tWH 20
-#define NAND_TIMING_tWP 40
-
-#define NAND_TIMING_tRH 20
-#define NAND_TIMING_tRP 40
-
-#define NAND_TIMING_tR 11123
-#define NAND_TIMING_tWHR 100
-#define NAND_TIMING_tAR 10
-
-/* NAND debugging */
-#define CONFIG_SYS_DFC_DEBUG1 /* usefull */
-#undef CONFIG_SYS_DFC_DEBUG2 /* noisy */
-#undef CONFIG_SYS_DFC_DEBUG3 /* extremly noisy */
-
#define CONFIG_MTD_DEBUG
#define CONFIG_MTD_DEBUG_VERBOSE 1
#define CONFIG_SYS_NO_FLASH 1
-#define CONFIG_ENV_IS_IN_NAND 1
+#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_ENV_OFFSET 0x40000
#define CONFIG_ENV_OFFSET_REDUND 0x44000
-#define CONFIG_ENV_SIZE 0x4000
+#define CONFIG_ENV_SIZE 0x4000
#endif /* __CONFIG_H */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 3/3] zylonite: use new generic NAND controller driver
2009-12-24 3:29 [U-Boot] [PATCH 0/3] Monahan/PXA3xx: integrate Linux NAND controller driver Marcel Ziswiler
2009-12-24 3:33 ` [U-Boot] [PATCH 1/3] " Marcel Ziswiler
2009-12-24 3:36 ` [U-Boot] [PATCH 2/3] delta: use new generic " Marcel Ziswiler
@ 2009-12-24 3:38 ` Marcel Ziswiler
2 siblings, 0 replies; 7+ messages in thread
From: Marcel Ziswiler @ 2009-12-24 3:38 UTC (permalink / raw)
To: u-boot
This patch adapts the Monahan/PXA3xx board zylonite to use the new generic NAND
controller driver.
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@noser.com>
---
board/zylonite/Makefile | 2 +-
board/zylonite/nand.c | 558 --------------------------------------------
board/zylonite/zylonite.c | 50 ++++
include/configs/zylonite.h | 34 +--
4 files changed, 59 insertions(+), 585 deletions(-)
delete mode 100644 board/zylonite/nand.c
diff --git a/board/zylonite/Makefile b/board/zylonite/Makefile
index 8954235..8a803f9 100644
--- a/board/zylonite/Makefile
+++ b/board/zylonite/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS := zylonite.o nand.o
+COBJS := zylonite.o
SOBJS := lowlevel_init.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/board/zylonite/nand.c b/board/zylonite/nand.c
deleted file mode 100644
index 7cad1ac..0000000
--- a/board/zylonite/nand.c
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * (C) Copyright 2006 DENX Software Engineering
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-
-#if defined(CONFIG_CMD_NAND)
-
-#include <nand.h>
-#include <asm/arch/pxa-regs.h>
-
-#ifdef CONFIG_SYS_DFC_DEBUG1
-# define DFC_DEBUG1(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG1(fmt, args...)
-#endif
-
-#ifdef CONFIG_SYS_DFC_DEBUG2
-# define DFC_DEBUG2(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG2(fmt, args...)
-#endif
-
-#ifdef CONFIG_SYS_DFC_DEBUG3
-# define DFC_DEBUG3(fmt, args...) printf(fmt, ##args)
-#else
-# define DFC_DEBUG3(fmt, args...)
-#endif
-
-/* These really don't belong here, as they are specific to the NAND Model */
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr delta_bbt_descr = {
- .options = 0,
- .offs = 0,
- .len = 2,
- .pattern = scan_ff_pattern
-};
-
-static struct nand_ecclayout delta_oob = {
- .eccbytes = 6,
- .eccpos = {2, 3, 4, 5, 6, 7},
- .oobfree = { {8, 2}, {12, 4} }
-};
-
-/*
- * not required for Monahans DFC
- */
-static void dfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- return;
-}
-
-#if 0
-/* read device ready pin */
-static int dfc_device_ready(struct mtd_info *mtdinfo)
-{
- if(NDSR & NDSR_RDY)
- return 1;
- else
- return 0;
- return 0;
-}
-#endif
-
-/*
- * Write buf to the DFC Controller Data Buffer
- */
-static void dfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
-{
- unsigned long bytes_multi = len & 0xfffffffc;
- unsigned long rest = len & 0x3;
- unsigned long *long_buf;
- int i;
-
- DFC_DEBUG2("dfc_write_buf: writing %d bytes starting with 0x%x.\n", len, *((unsigned long*) buf));
- if(bytes_multi) {
- for(i=0; i<bytes_multi; i+=4) {
- long_buf = (unsigned long*) &buf[i];
- NDDB = *long_buf;
- }
- }
- if(rest) {
- printf("dfc_write_buf: ERROR, writing non 4-byte aligned data.\n");
- }
- return;
-}
-
-
-/* The original:
- * static void dfc_read_buf(struct mtd_info *mtd, const u_char *buf, int len)
- *
- * Shouldn't this be "u_char * const buf" ?
- */
-static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
-{
- int i=0, j;
-
- /* we have to be carefull not to overflow the buffer if len is
- * not a multiple of 4 */
- unsigned long bytes_multi = len & 0xfffffffc;
- unsigned long rest = len & 0x3;
- unsigned long *long_buf;
-
- DFC_DEBUG3("dfc_read_buf: reading %d bytes.\n", len);
- /* if there are any, first copy multiple of 4 bytes */
- if(bytes_multi) {
- for(i=0; i<bytes_multi; i+=4) {
- long_buf = (unsigned long*) &buf[i];
- *long_buf = NDDB;
- }
- }
-
- /* ...then the rest */
- if(rest) {
- unsigned long rest_data = NDDB;
- for(j=0;j<rest; j++)
- buf[i+j] = (u_char) ((rest_data>>j) & 0xff);
- }
-
- return;
-}
-
-/*
- * read a word. Not implemented as not used in NAND code.
- */
-static u16 dfc_read_word(struct mtd_info *mtd)
-{
- printf("dfc_read_word: UNIMPLEMENTED.\n");
- return 0;
-}
-
-/* global var, too bad: mk at tbd: move to ->priv pointer */
-static unsigned long read_buf = 0;
-static int bytes_read = -1;
-
-/*
- * read a byte from NDDB Because we can only read 4 bytes from NDDB at
- * a time, we buffer the remaining bytes. The buffer is reset when a
- * new command is sent to the chip.
- *
- * WARNING:
- * This function is currently only used to read status and id
- * bytes. For these commands always 8 bytes need to be read from
- * NDDB. So we read and discard these bytes right now. In case this
- * function is used for anything else in the future, we must check
- * what was the last command issued and read the appropriate amount of
- * bytes respectively.
- */
-static u_char dfc_read_byte(struct mtd_info *mtd)
-{
- unsigned char byte;
- unsigned long dummy;
-
- if(bytes_read < 0) {
- read_buf = NDDB;
- dummy = NDDB;
- bytes_read = 0;
- }
- byte = (unsigned char) (read_buf>>(8 * bytes_read++));
- if(bytes_read >= 4)
- bytes_read = -1;
-
- DFC_DEBUG2("dfc_read_byte: byte %u: 0x%x of (0x%x).\n", bytes_read - 1, byte, read_buf);
- return byte;
-}
-
-/* calculate delta between OSCR values start and now */
-static unsigned long get_delta(unsigned long start)
-{
- unsigned long cur = OSCR;
-
- if(cur < start) /* OSCR overflowed */
- return (cur + (start^0xffffffff));
- else
- return (cur - start);
-}
-
-/* delay function, this doesn't belong here */
-static void wait_us(unsigned long us)
-{
- unsigned long start = OSCR;
- us = DIV_ROUND_UP(us * OSCR_CLK_FREQ, 1000);
-
- while (get_delta(start) < us) {
- /* do nothing */
- }
-}
-
-static void dfc_clear_nddb(void)
-{
- NDCR &= ~NDCR_ND_RUN;
- wait_us(CONFIG_SYS_NAND_OTHER_TO);
-}
-
-/* wait_event with timeout */
-static unsigned long dfc_wait_event(unsigned long event)
-{
- unsigned long ndsr, timeout, start = OSCR;
-
- if(!event)
- return 0xff000000;
- else if(event & (NDSR_CS0_CMDD | NDSR_CS0_BBD))
- timeout = DIV_ROUND_UP(CONFIG_SYS_NAND_PROG_ERASE_TO
- * OSCR_CLK_FREQ, 1000);
- else
- timeout = DIV_ROUND_UP(CONFIG_SYS_NAND_OTHER_TO
- * OSCR_CLK_FREQ, 1000);
-
- while(1) {
- ndsr = NDSR;
- if(ndsr & event) {
- NDSR |= event;
- break;
- }
- if(get_delta(start) > timeout) {
- DFC_DEBUG1("dfc_wait_event: TIMEOUT waiting for event: 0x%lx.\n", event);
- return 0xff000000;
- }
-
- }
- return ndsr;
-}
-
-/* we don't always wan't to do this */
-static void dfc_new_cmd(void)
-{
- int retry = 0;
- unsigned long status;
-
- while(retry++ <= CONFIG_SYS_NAND_SENDCMD_RETRY) {
- /* Clear NDSR */
- NDSR = 0xFFF;
-
- /* set NDCR[NDRUN] */
- if(!(NDCR & NDCR_ND_RUN))
- NDCR |= NDCR_ND_RUN;
-
- status = dfc_wait_event(NDSR_WRCMDREQ);
-
- if(status & NDSR_WRCMDREQ)
- return;
-
- DFC_DEBUG2("dfc_new_cmd: FAILED to get WRITECMDREQ, retry: %d.\n", retry);
- dfc_clear_nddb();
- }
- DFC_DEBUG1("dfc_new_cmd: giving up after %d retries.\n", retry);
-}
-
-/* this function is called after Programm and Erase Operations to
- * check for success or failure */
-static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this)
-{
- unsigned long ndsr=0, event=0;
- int state = this->state;
-
- if(state == FL_WRITING) {
- event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
- } else if(state == FL_ERASING) {
- event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
- }
-
- ndsr = dfc_wait_event(event);
-
- if((ndsr & NDSR_CS0_BBD) || (ndsr & 0xff000000))
- return(0x1); /* Status Read error */
- return 0;
-}
-
-/* cmdfunc send commands to the DFC */
-static void dfc_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
-{
- /* register struct nand_chip *this = mtd->priv; */
- unsigned long ndcb0=0, ndcb1=0, ndcb2=0, event=0;
-
- /* clear the ugly byte read buffer */
- bytes_read = -1;
- read_buf = 0;
-
- switch (command) {
- case NAND_CMD_READ0:
- DFC_DEBUG3("dfc_cmdfunc: NAND_CMD_READ0, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- dfc_new_cmd();
- ndcb0 = (NAND_CMD_READ0 | (4<<16));
- column >>= 1; /* adjust for 16 bit bus */
- ndcb1 = (((column>>1) & 0xff) |
- ((page_addr<<8) & 0xff00) |
- ((page_addr<<8) & 0xff0000) |
- ((page_addr<<8) & 0xff000000)); /* make this 0x01000000 ? */
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_READ1:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_READ1 unimplemented!\n");
- goto end;
- case NAND_CMD_READOOB:
- DFC_DEBUG1("dfc_cmdfunc: NAND_CMD_READOOB unimplemented!\n");
- goto end;
- case NAND_CMD_READID:
- dfc_new_cmd();
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_READID.\n");
- ndcb0 = (NAND_CMD_READID | (3 << 21) | (1 << 16)); /* addr cycles*/
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_PAGEPROG:
- /* sent as a multicommand in NAND_CMD_SEQIN */
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_PAGEPROG empty due to multicmd.\n");
- goto end;
- case NAND_CMD_ERASE1:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_ERASE1, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- dfc_new_cmd();
- ndcb0 = (0xd060 | (1<<25) | (2<<21) | (1<<19) | (3<<16));
- ndcb1 = (page_addr & 0x00ffffff);
- goto write_cmd;
- case NAND_CMD_ERASE2:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_ERASE2 empty due to multicmd.\n");
- goto end;
- case NAND_CMD_SEQIN:
- /* send PAGE_PROG command(0x1080) */
- dfc_new_cmd();
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, page_addr: 0x%x, column: 0x%x.\n", page_addr, (column>>1));
- ndcb0 = (0x1080 | (1<<25) | (1<<21) | (1<<19) | (4<<16));
- column >>= 1; /* adjust for 16 bit bus */
- ndcb1 = (((column>>1) & 0xff) |
- ((page_addr<<8) & 0xff00) |
- ((page_addr<<8) & 0xff0000) |
- ((page_addr<<8) & 0xff000000)); /* make this 0x01000000 ? */
- event = NDSR_WRDREQ;
- goto write_cmd;
- case NAND_CMD_STATUS:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_STATUS.\n");
- dfc_new_cmd();
- ndcb0 = NAND_CMD_STATUS | (4<<21);
- event = NDSR_RDDREQ;
- goto write_cmd;
- case NAND_CMD_RESET:
- DFC_DEBUG2("dfc_cmdfunc: NAND_CMD_RESET.\n");
- ndcb0 = NAND_CMD_RESET | (5<<21);
- event = NDSR_CS0_CMDD;
- goto write_cmd;
- default:
- printk("dfc_cmdfunc: error, unsupported command.\n");
- goto end;
- }
-
- write_cmd:
- NDCB0 = ndcb0;
- NDCB0 = ndcb1;
- NDCB0 = ndcb2;
-
- /* wait_event: */
- dfc_wait_event(event);
- end:
- return;
-}
-
-static void dfc_gpio_init(void)
-{
- DFC_DEBUG2("Setting up DFC GPIO's.\n");
-
- /* no idea what is done here, see zylonite.c */
- GPIO4 = 0x1;
-
- DF_ALE_WE1 = 0x00000001;
- DF_ALE_WE2 = 0x00000001;
- DF_nCS0 = 0x00000001;
- DF_nCS1 = 0x00000001;
- DF_nWE = 0x00000001;
- DF_nRE = 0x00000001;
- DF_IO0 = 0x00000001;
- DF_IO8 = 0x00000001;
- DF_IO1 = 0x00000001;
- DF_IO9 = 0x00000001;
- DF_IO2 = 0x00000001;
- DF_IO10 = 0x00000001;
- DF_IO3 = 0x00000001;
- DF_IO11 = 0x00000001;
- DF_IO4 = 0x00000001;
- DF_IO12 = 0x00000001;
- DF_IO5 = 0x00000001;
- DF_IO13 = 0x00000001;
- DF_IO6 = 0x00000001;
- DF_IO14 = 0x00000001;
- DF_IO7 = 0x00000001;
- DF_IO15 = 0x00000001;
-
- DF_nWE = 0x1901;
- DF_nRE = 0x1901;
- DF_CLE_NOE = 0x1900;
- DF_ALE_WE1 = 0x1901;
- DF_INT_RnB = 0x1900;
-}
-
-/*
- * Board-specific NAND initialization. The following members of the
- * argument are board-specific (per include/linux/mtd/nand_new.h):
- * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
- * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
- * - cmd_ctrl: hardwarespecific function for accesing control-lines
- * - dev_ready: hardwarespecific function for accesing device ready/busy line
- * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
- * only be provided if a hardware ECC is available
- * - ecc.mode: mode of ecc, see defines
- * - chip_delay: chip dependent delay for transfering data from array to
- * read regs (tR)
- * - options: various chip options. They can partly be set to inform
- * nand_scan about special functionality. See the defines for further
- * explanation
- * Members with a "?" were not set in the merged testing-NAND branch,
- * so they are not set here either.
- */
-int board_nand_init(struct nand_chip *nand)
-{
- unsigned long tCH, tCS, tWH, tWP, tRH, tRP, tRP_high, tR, tWHR, tAR;
-
- /* set up GPIO Control Registers */
- dfc_gpio_init();
-
- /* turn on the NAND Controller Clock (104 MHz @ D0) */
- CKENA |= (CKENA_4_NAND | CKENA_9_SMC);
-
-#undef CONFIG_SYS_TIMING_TIGHT
-#ifndef CONFIG_SYS_TIMING_TIGHT
- tCH = MIN(((unsigned long) (NAND_TIMING_tCH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tCH);
- tCS = MIN(((unsigned long) (NAND_TIMING_tCS * DFC_CLK_PER_US) + 1),
- DFC_MAX_tCS);
- tWH = MIN(((unsigned long) (NAND_TIMING_tWH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWH);
- tWP = MIN(((unsigned long) (NAND_TIMING_tWP * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWP);
- tRH = MIN(((unsigned long) (NAND_TIMING_tRH * DFC_CLK_PER_US) + 1),
- DFC_MAX_tRH);
- tRP = MIN(((unsigned long) (NAND_TIMING_tRP * DFC_CLK_PER_US) + 1),
- DFC_MAX_tRP);
- tR = MIN(((unsigned long) (NAND_TIMING_tR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tR);
- tWHR = MIN(((unsigned long) (NAND_TIMING_tWHR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tWHR);
- tAR = MIN(((unsigned long) (NAND_TIMING_tAR * DFC_CLK_PER_US) + 1),
- DFC_MAX_tAR);
-#else /* this is the tight timing */
-
- tCH = MIN(((unsigned long) (NAND_TIMING_tCH * DFC_CLK_PER_US)),
- DFC_MAX_tCH);
- tCS = MIN(((unsigned long) (NAND_TIMING_tCS * DFC_CLK_PER_US)),
- DFC_MAX_tCS);
- tWH = MIN(((unsigned long) (NAND_TIMING_tWH * DFC_CLK_PER_US)),
- DFC_MAX_tWH);
- tWP = MIN(((unsigned long) (NAND_TIMING_tWP * DFC_CLK_PER_US)),
- DFC_MAX_tWP);
- tRH = MIN(((unsigned long) (NAND_TIMING_tRH * DFC_CLK_PER_US)),
- DFC_MAX_tRH);
- tRP = MIN(((unsigned long) (NAND_TIMING_tRP * DFC_CLK_PER_US)),
- DFC_MAX_tRP);
- tR = MIN(((unsigned long) (NAND_TIMING_tR * DFC_CLK_PER_US) - tCH - 2),
- DFC_MAX_tR);
- tWHR = MIN(((unsigned long) (NAND_TIMING_tWHR * DFC_CLK_PER_US) - tCH - 2),
- DFC_MAX_tWHR);
- tAR = MIN(((unsigned long) (NAND_TIMING_tAR * DFC_CLK_PER_US) - 2),
- DFC_MAX_tAR);
-#endif /* CONFIG_SYS_TIMING_TIGHT */
-
-
- DFC_DEBUG2("tCH=%u, tCS=%u, tWH=%u, tWP=%u, tRH=%u, tRP=%u, tR=%u, tWHR=%u, tAR=%u.\n", tCH, tCS, tWH, tWP, tRH, tRP, tR, tWHR, tAR);
-
- /* tRP value is split in the register */
- if(tRP & (1 << 4)) {
- tRP_high = 1;
- tRP &= ~(1 << 4);
- } else {
- tRP_high = 0;
- }
-
- NDTR0CS0 = (tCH << 19) |
- (tCS << 16) |
- (tWH << 11) |
- (tWP << 8) |
- (tRP_high << 6) |
- (tRH << 3) |
- (tRP << 0);
-
- NDTR1CS0 = (tR << 16) |
- (tWHR << 4) |
- (tAR << 0);
-
- /* If it doesn't work (unlikely) think about:
- * - ecc enable
- * - chip select don't care
- * - read id byte count
- *
- * Intentionally enabled by not setting bits:
- * - dma (DMA_EN)
- * - page size = 512
- * - cs don't care, see if we can enable later!
- * - row address start position (after second cycle)
- * - pages per block = 32
- * - ND_RDY : clears command buffer
- */
- /* NDCR_NCSX | /\* Chip select busy don't care *\/ */
-
- NDCR = (NDCR_SPARE_EN | /* use the spare area */
- NDCR_DWIDTH_C | /* 16bit DFC data bus width */
- NDCR_DWIDTH_M | /* 16 bit Flash device data bus width */
- (2 << 16) | /* read id count = 7 ???? mk@tbd */
- NDCR_ND_ARB_EN | /* enable bus arbiter */
- NDCR_RDYM | /* flash device ready ir masked */
- NDCR_CS0_PAGEDM | /* ND_nCSx page done ir masked */
- NDCR_CS1_PAGEDM |
- NDCR_CS0_CMDDM | /* ND_CSx command done ir masked */
- NDCR_CS1_CMDDM |
- NDCR_CS0_BBDM | /* ND_CSx bad block detect ir masked */
- NDCR_CS1_BBDM |
- NDCR_DBERRM | /* double bit error ir masked */
- NDCR_SBERRM | /* single bit error ir masked */
- NDCR_WRDREQM | /* write data request ir masked */
- NDCR_RDDREQM | /* read data request ir masked */
- NDCR_WRCMDREQM); /* write command request ir masked */
-
-
- /* wait 10 us due to cmd buffer clear reset */
- /* wait(10); */
-
- nand->cmd_ctrl = dfc_hwcontrol;
-/* nand->dev_ready = dfc_device_ready; */
- nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.layout = &delta_oob;
- nand->options = NAND_BUSWIDTH_16;
- nand->waitfunc = dfc_wait;
- nand->read_byte = dfc_read_byte;
- nand->read_word = dfc_read_word;
- nand->read_buf = dfc_read_buf;
- nand->write_buf = dfc_write_buf;
-
- nand->cmdfunc = dfc_cmdfunc;
- nand->badblock_pattern = &delta_bbt_descr;
- return 0;
-}
-
-#endif
diff --git a/board/zylonite/zylonite.c b/board/zylonite/zylonite.c
index 749a40f..8243dc9 100644
--- a/board/zylonite/zylonite.c
+++ b/board/zylonite/zylonite.c
@@ -27,9 +27,13 @@
#include <common.h>
#include <netdev.h>
+#include <asm/arch/pxa-regs.h>
+#include <nand.h>
DECLARE_GLOBAL_DATA_PTR;
+extern int pxa3xx_nand_probe(struct nand_chip *chip);
+
/*
* Miscelaneous platform dependent initialisations
*/
@@ -55,6 +59,52 @@ int board_late_init(void)
return 0;
}
+/*
+ * Board-specific NAND initialization.
+ */
+int board_nand_init(struct nand_chip *chip)
+{
+ /* no idea what is done here, see zylonite.c */
+ GPIO4 = 0x1;
+
+ /* Setting up DFC GPIOs. */
+
+ /* ND_IO<15:0> alternate function 1 */
+ DF_IO0 = 0x00000001;
+ DF_IO1 = 0x00000001;
+ DF_IO2 = 0x00000001;
+ DF_IO3 = 0x00000001;
+ DF_IO4 = 0x00000001;
+ DF_IO5 = 0x00000001;
+ DF_IO6 = 0x00000001;
+ DF_IO7 = 0x00000001;
+ DF_IO8 = 0x00000001;
+ DF_IO9 = 0x00000001;
+ DF_IO10 = 0x00000001;
+ DF_IO11 = 0x00000001;
+ DF_IO12 = 0x00000001;
+ DF_IO13 = 0x00000001;
+ DF_IO14 = 0x00000001;
+ DF_IO15 = 0x00000001;
+ /* ND_nCS0 and ND_nCS1 alternate function 1 */
+ DF_nCS0 = 0x00000001;
+ DF_nCS1 = 0x00000001;
+ /* ND_nWE and ND_nRE alternate function 1, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_nWE = 0x00001901;
+ DF_nRE = 0x00001901;
+ /* ND_CLE alternate function 0 */
+ DF_CLE_nOE = 0x00001900;
+ /* ND_ALE alternate function 1, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_ALE_nWE1 = 0x00001901;
+ DF_ALE_nWE2 = 0x00001901;
+ /* ND_RnB alternate function 0, data on pad sent during
+ D1-D3 low power mode, slow 10 mA drive */
+ DF_INT_RnB = 0x00001900;
+
+ return pxa3xx_nand_probe(chip);
+}
int dram_init (void)
{
diff --git a/include/configs/zylonite.h b/include/configs/zylonite.h
index d0fc138..42b0d4a 100644
--- a/include/configs/zylonite.h
+++ b/include/configs/zylonite.h
@@ -59,7 +59,6 @@
/*
* Hardware drivers
*/
-
#undef TURN_ON_ETHERNET
#ifdef TURN_ON_ETHERNET
# define CONFIG_SMC91111 1
@@ -189,14 +188,16 @@
#undef CONFIG_SYS_SKIP_DRAM_SCRUB
-
/*
* NAND Flash
*/
-#define CONFIG_SYS_NAND0_BASE 0x0
-#undef CONFIG_SYS_NAND1_BASE
+#define CONFIG_NAND_PXA3xx
+#define CONFIG_NAND_PXA3xx_CLK 104000000UL
+#define CONFIG_NEW_NAND_CODE
+#define CONFIG_SYS_NAND_HW_ECC
+
+#define CONFIG_SYS_NAND_BASE 0x0
-#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND0_BASE }
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices */
/* nand timeout values */
@@ -205,33 +206,14 @@
#define CONFIG_SYS_NAND_SENDCMD_RETRY 3
#undef NAND_ALLOW_ERASE_ALL /* Allow erasing bad blocks - don't use */
-/* NAND Timing Parameters (in ns) */
-#define NAND_TIMING_tCH 10
-#define NAND_TIMING_tCS 0
-#define NAND_TIMING_tWH 20
-#define NAND_TIMING_tWP 40
-
-#define NAND_TIMING_tRH 20
-#define NAND_TIMING_tRP 40
-
-#define NAND_TIMING_tR 11123
-#define NAND_TIMING_tWHR 100
-#define NAND_TIMING_tAR 10
-
-/* NAND debugging */
-#define CONFIG_SYS_DFC_DEBUG1 /* usefull */
-#undef CONFIG_SYS_DFC_DEBUG2 /* noisy */
-#undef CONFIG_SYS_DFC_DEBUG3 /* extremly noisy */
-
#define CONFIG_MTD_DEBUG
#define CONFIG_MTD_DEBUG_VERBOSE 1
#define CONFIG_SYS_NO_FLASH 1
-#define CONFIG_ENV_IS_IN_NAND 1
+#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_ENV_OFFSET 0x40000
#define CONFIG_ENV_OFFSET_REDUND 0x44000
-#define CONFIG_ENV_SIZE 0x4000
-
+#define CONFIG_ENV_SIZE 0x4000
#endif /* __CONFIG_H */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 1/3] Monahan/PXA3xx: integrate Linux NAND controller driver
2009-12-24 3:33 ` [U-Boot] [PATCH 1/3] " Marcel Ziswiler
@ 2010-01-07 19:44 ` Scott Wood
2010-01-08 22:34 ` Marcel Ziswiler
0 siblings, 1 reply; 7+ messages in thread
From: Scott Wood @ 2010-01-07 19:44 UTC (permalink / raw)
To: u-boot
On Thu, Dec 24, 2009 at 04:33:49AM +0100, Marcel Ziswiler wrote:
> +/* XXX U-BOOT XXX */
> +#define __U_BOOT__
> +
> +#ifndef __U_BOOT__
I don't think we need this.
In fact, it could make it harder to spot conflicts when merging new versions
of the file from linux, since patch could find the context it's looking for
in the middle of an #ifndef __U_BOOT__ section when the change really needs
to be applied to the other side of the #else.
Plus, it makes it harder to read the code. Let's share what we reasonably
can, and get rid of the rest.
> +#undef DEBUG
Don't undef it, just don't define it. Maybe someone wants to turn debugging
on system-wide.
> +#ifdef DEBUG
> +#define DEBUGF(fmt, args...) printf(fmt, ##args)
> +#else /* DEBUG */
> +#define DEBUGF(x...)
> +#endif /* DEBUG */
We've already got MTDDEBUG.
> +#define CONFIG_MTD_NAND_PXA3xx_BUILTIN
This is marked deprecated in Linux; perhaps we should take the preferred
approach of platform-specified data here as well?
> +#define CONFIG_MTD_NAND_PXA3xx_POLLING
I don't see this upstream.
> +#ifdef CONFIG_NAND_SPL
> +/**
> + * memset - Fill a region of memory with the given value
> + * @s: Pointer to the start of the area.
> + * @c: The byte to fill the area with
> + * @count: The size of the area.
> + *
> + * Do not use memset() to access IO space, use memset_io() instead.
> + */
> +void *memset(void *s, int c, size_t count)
> +{
> + char *xs = (char *) s;
> +
> + while (count--)
> + *xs++ = c;
> +
> + return s;
> +}
> +
> +/**
> + * memcpy - Copy one area of memory to another
> + * @dest: Where to copy to
> + * @src: Where to copy from
> + * @count: The size of the area.
> + *
> + * You should not use this function to access IO space, use memcpy_toio()
> + * or memcpy_fromio() instead.
> + */
> +void *memcpy(void *dest, const void *src, size_t count)
> +{
> + char *tmp = (char *) dest, *s = (char *) src;
> +
> + while (count--)
> + *tmp++ = *s++;
> +
> + return dest;
> +}
> +#endif /* CONFIG_NAND_SPL */
> +
> +void msleep(int count)
> +{
> + int i;
> +
> + for (i = 0; i < count; i++)
> + udelay(1000);
> +}
Can we arrange to pull in the relevant functions from the rest of u-boot
rather than duplicate them?
Or try to avoid the need in the NAND loader -- for example, msleep is only
used in handling NAND_CMD_RESET, which you shouldn't need.
In fact, I'm not sure how this driver can be used at all with NAND_SPL. The
normal nand_spl/nand_boot.c uses cmd_ctrl. More complicated controllers
that can't use cmd_ctrl generally need their own nand_spl driver (such as
nand_spl/nand_boot_fsl_elbc.c). If you have a particularly large NAND boot
region, you might be able to create a nand_spl driver that uses this driver,
but typically space is too tight.
> +/* macros for registers read/write */
> +#ifdef __U_BOOT__
> +#define nand_writel(info, off, val) \
> + ((*(volatile u32 *) ((info)->mmio_base + (off))) = (val))
> +
> +static void nand_writesl(volatile u32 *dest, u8 *src, int size)
> +{
> + int i;
> + u32 *source = (u32 *)src;
> +
> + for (i = 0; i < size; i++)
> + *dest = *(source++);
> +}
> +
> +#define nand_readl(info, off) \
> + (*(volatile u32 *)((info)->mmio_base + (off)))
> +
> +static void nand_readsl(volatile u32 *source, u8 *dst, int size)
> +{
> + u32 *dest = (u32 *)dst;
> + int i;
> +
> + for (i = 0; i < size; i++)
> + *(dest++) = *source;
> +}
> +#else /* __U_BOOT__ */
> +#define nand_writel(info, off, val) \
> + __raw_writel((val), (info)->mmio_base + (off))
> +
> +#define nand_readl(info, off) \
> + __raw_readl((info)->mmio_base + (off))
> +#endif /* __U_BOOT__ */
You can (and should) use __raw_writel/__raw_readl in U-Boot as well.
There is also a prototype for__raw_writesl/__raw_readsl, but the
implementation seems to be missing -- perhaps you could add it generically,
rather than just in one driver.
-Scott
^ permalink raw reply [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 1/3] Monahan/PXA3xx: integrate Linux NAND controller driver
2010-01-07 19:44 ` Scott Wood
@ 2010-01-08 22:34 ` Marcel Ziswiler
2010-01-08 23:13 ` Scott Wood
0 siblings, 1 reply; 7+ messages in thread
From: Marcel Ziswiler @ 2010-01-08 22:34 UTC (permalink / raw)
To: u-boot
On Thu, 2010-01-07 at 13:44 -0600, Scott Wood wrote:
> I don't think we need this.
>
> In fact, it could make it harder to spot conflicts when merging new versions
> of the file from linux, since patch could find the context it's looking for
> in the middle of an #ifndef __U_BOOT__ section when the change really needs
> to be applied to the other side of the #else.
>
> Plus, it makes it harder to read the code. Let's share what we reasonably
> can, and get rid of the rest.
OK, but it es exactly how it is done with the rest of the linux MTD
stuff pulled into U-Boot. I don't quite see any other practical way of
achieving such shared code where we have rather diverse requirements
otherwise.
> Don't undef it, just don't define it. Maybe someone wants to turn debugging
> on system-wide.
>
> > +#ifdef DEBUG
> > +#define DEBUGF(fmt, args...) printf(fmt, ##args)
> > +#else /* DEBUG */
> > +#define DEBUGF(x...)
> > +#endif /* DEBUG */
>
> We've already got MTDDEBUG.
OK, agreed.
> This is marked deprecated in Linux; perhaps we should take the preferred
> approach of platform-specified data here as well?
Hm, I thought platform data would be an over kill for U-Boot especially
if it comes down to the NAND SPL case. Will look into how I could
integrate that.
> I don't see this upstream.
That's because linux generally uses interrupts, but I wanted pure
polling for U-Boot.
I actually thought about first getting some of my customization upstream
but the last time I submitted a mere 2 line bug fix to this driver it
got completely ignored. Don't quite know how to get any MTD stuff
mainline in a timely manner!
> Can we arrange to pull in the relevant functions from the rest of u-boot
> rather than duplicate them?
Generally yes, but that would completely bloat the NAND SPL case. OK as
later targets do no more have a 4K limit but rather 16K it might
actually work out. I am just fearing further dependencies.
> Or try to avoid the need in the NAND loader -- for example, msleep is only
> used in handling NAND_CMD_RESET, which you shouldn't need.
>
> In fact, I'm not sure how this driver can be used at all with NAND_SPL. The
> normal nand_spl/nand_boot.c uses cmd_ctrl. More complicated controllers
> that can't use cmd_ctrl generally need their own nand_spl driver (such as
> nand_spl/nand_boot_fsl_elbc.c). If you have a particularly large NAND boot
> region, you might be able to create a nand_spl driver that uses this driver,
> but typically space is too tight.
It actually works just fine and boots various PXA3xx targets with my
slightly modified nand_spl/nand_boot.c which I plan to submit as soon as
we agree on this driver.
> You can (and should) use __raw_writel/__raw_readl in U-Boot as well.
>
> There is also a prototype for__raw_writesl/__raw_readsl, but the
> implementation seems to be missing -- perhaps you could add it generically,
> rather than just in one driver.
I actually had a version doing it that way but it had some nasty further
dependencies so I finally decided against doing it that way. Will look
into it again.
Thanks Scott!
-Marcel
^ permalink raw reply [flat|nested] 7+ messages in thread
* [U-Boot] [PATCH 1/3] Monahan/PXA3xx: integrate Linux NAND controller driver
2010-01-08 22:34 ` Marcel Ziswiler
@ 2010-01-08 23:13 ` Scott Wood
0 siblings, 0 replies; 7+ messages in thread
From: Scott Wood @ 2010-01-08 23:13 UTC (permalink / raw)
To: u-boot
Marcel Ziswiler wrote:
> On Thu, 2010-01-07 at 13:44 -0600, Scott Wood wrote:
>> I don't think we need this.
>>
>> In fact, it could make it harder to spot conflicts when merging new versions
>> of the file from linux, since patch could find the context it's looking for
>> in the middle of an #ifndef __U_BOOT__ section when the change really needs
>> to be applied to the other side of the #else.
>>
>> Plus, it makes it harder to read the code. Let's share what we reasonably
>> can, and get rid of the rest.
>
> OK, but it es exactly how it is done with the rest of the linux MTD
> stuff pulled into U-Boot.
There is some of this in the common NAND code, which was there when I
took over the subsystem. I'd rather move in the direction of having
less of it than more.
I haven't seen much of this in the drivers.
> I don't quite see any other practical way of
> achieving such shared code where we have rather diverse requirements
> otherwise.
But you're not actually sharing the code. You're reusing parts of it
with lots of changes (which I'm guessing aren't going to make it back
into Linux) and dead code.
I don't see how keeping the dead bits around helps keep things in sync.
It's usually easy enough to look at conflicts and figure out whether a
change needs to be brought over, and how. The information about what is
different can be obtained at any point using diff. One thing that would
help is to keep track in the comments of which Linux version/SHA1 it
corresponds to.
Note that stuff like mtd/compat.h is different, in that it's allowing
some very commonly used Linux facilities to be used by the shared code
as-is without needing any ifdefs in the main code.
>> This is marked deprecated in Linux; perhaps we should take the preferred
>> approach of platform-specified data here as well?
>
> Hm, I thought platform data would be an over kill for U-Boot especially
> if it comes down to the NAND SPL case. Will look into how I could
> integrate that.
I suppose it depends what it ends up looking like -- but simply having
platform code provide a struct with some timings and such seems pretty
reasonable. The method of getting that data to the driver would likely
be different than in Linux.
>> I don't see this upstream.
>
> That's because linux generally uses interrupts, but I wanted pure
> polling for U-Boot.
Right, the implication was that unless there's a plan to support polling
in Linux, this amounts to another #ifdef __U_BOOT__.
> I actually thought about first getting some of my customization upstream
> but the last time I submitted a mere 2 line bug fix to this driver it
> got completely ignored. Don't quite know how to get any MTD stuff
> mainline in a timely manner!
It might have just been overlooked -- try pinging the maintainer and the
MTD list.
>> Can we arrange to pull in the relevant functions from the rest of u-boot
>> rather than duplicate them?
>
> Generally yes, but that would completely bloat the NAND SPL case.
I was thinking we could identify a subset that NAND SPL and similar
things are generally going to need, and #ifndef the rest (similar to
CONFIG_NS16550_MIN_FUNCTIONS).
Or, at least have a NAND SPL mini-library so that there's only one
duplication instead of one per board/driver/etc. At least memcpy can
replace some open-coding done in existing NAND SPL code.
> It actually works just fine and boots various PXA3xx targets with my
> slightly modified nand_spl/nand_boot.c which I plan to submit as soon as
> we agree on this driver.
OK, that modified nand_boot.c is what I was wondering about (other than
the size).
-Scott
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-01-08 23:13 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-12-24 3:29 [U-Boot] [PATCH 0/3] Monahan/PXA3xx: integrate Linux NAND controller driver Marcel Ziswiler
2009-12-24 3:33 ` [U-Boot] [PATCH 1/3] " Marcel Ziswiler
2010-01-07 19:44 ` Scott Wood
2010-01-08 22:34 ` Marcel Ziswiler
2010-01-08 23:13 ` Scott Wood
2009-12-24 3:36 ` [U-Boot] [PATCH 2/3] delta: use new generic " Marcel Ziswiler
2009-12-24 3:38 ` [U-Boot] [PATCH 3/3] zylonite: " Marcel Ziswiler
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox