[U-Boot] [PATCH 1/5] Enable PXAFB for PXA27X and PXA3XX

--- arch/arm/cpu/pxa/pxafb.c | 9 ++++++++- common/lcd.c | 12 ++++++------ include/lcd.h | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/arch/arm/cpu/pxa/pxafb.c b/arch/arm/cpu/pxa/pxafb.c index d56c5f0..1cb78f0 100644 --- a/arch/arm/cpu/pxa/pxafb.c +++ b/arch/arm/cpu/pxa/pxafb.c @@ -292,7 +292,9 @@ static int pxafb_init_mem (void *lcdbase, vidinfo_t *vid)
return 0; } - +#ifdef CONFIG_CPU_MONAHANS +static inline void pxafb_setup_gpio (vidinfo_t *vid) {} +#else static void pxafb_setup_gpio (vidinfo_t *vid) { u_long lccr0; @@ -349,6 +351,7 @@ static void pxafb_setup_gpio (vidinfo_t *vid) printf("pxafb_setup_gpio: unable to determine bits per pixel\n"); } } +#endif
static void pxafb_enable_controller (vidinfo_t *vid) { @@ -363,7 +366,11 @@ static void pxafb_enable_controller (vidinfo_t *vid) FDADR1 = vid->pxa.fdadr1; LCCR0 |= LCCR0_ENB;
+#ifdef CONFIG_CPU_MONAHANS + CKENA |= CKENA_1_LCD; +#else CKEN |= CKEN16_LCD; +#endif
debug("FDADR0 = 0x%08x\n", (unsigned int)FDADR0); debug("FDADR1 = 0x%08x\n", (unsigned int)FDADR1); diff --git a/common/lcd.c b/common/lcd.c index 93ddedf..d854c21 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -41,7 +41,7 @@ #include <lcd.h> #include <watchdog.h>
-#if defined(CONFIG_PXA250) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS #include <asm/byteorder.h> #endif
@@ -503,7 +503,7 @@ void bitmap_plot (int x, int y) uchar *bmap; uchar *fb; ushort *fb16; -#if defined(CONFIG_PXA250) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS struct pxafb_info *fbi = &panel_info.pxa; #elif defined(CONFIG_MPC823) volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; @@ -519,7 +519,7 @@ void bitmap_plot (int x, int y)
if (NBITS(panel_info.vl_bpix) < 12) { /* Leave room for default color map */ -#if defined(CONFIG_PXA250) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS cmap = (ushort *)fbi->palette; #elif defined(CONFIG_MPC823) cmap = (ushort *)&(cp->lcd_cmap[BMP_LOGO_OFFSET*sizeof(ushort)]); @@ -615,7 +615,7 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) unsigned long pwidth = panel_info.vl_col; unsigned colors, bpix, bmp_bpix; unsigned long compression; -#if defined(CONFIG_PXA250) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS struct pxafb_info *fbi = &panel_info.pxa; #elif defined(CONFIG_MPC823) volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; @@ -656,7 +656,7 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) #if !defined(CONFIG_MCC200) /* MCC200 LCD doesn't need CMAP, supports 1bpp b&w only */ if (bmp_bpix == 8) { -#if defined(CONFIG_PXA250) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS cmap = (ushort *)fbi->palette; #elif defined(CONFIG_MPC823) cmap = (ushort *)&(cp->lcd_cmap[255*sizeof(ushort)]); @@ -745,7 +745,7 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) WATCHDOG_RESET(); for (j = 0; j < width; j++) { if (bpix != 16) { -#if defined(CONFIG_PXA250) || defined(CONFIG_ATMEL_LCD) +#if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS || defined(CONFIG_ATMEL_LCD) *(fb++) = *(bmap++); #elif defined(CONFIG_MPC823) || defined(CONFIG_MCC200) *(fb++) = 255 - *(bmap++); diff --git a/include/lcd.h b/include/lcd.h index 1f85daa..cd9d49d 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -87,7 +87,7 @@ typedef struct vidinfo { u_char vl_wbf; /* Wait between frames */ } vidinfo_t;
-#elif defined CONFIG_PXA250 +#elif defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS /* * PXA LCD DMA descriptor */

This patch adds macros for the following purposes: - GPIO configuration - SDRAM configuration - Wakeup - Clock configuration - Interrupt controller configuration These macros are intended to replace numerous copies of the same code.
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/include/asm/arch-pxa/macro.h | 324 +++++++++++++++++++++++++++++++++ 1 files changed, 324 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-pxa/macro.h
diff --git a/arch/arm/include/asm/arch-pxa/macro.h b/arch/arm/include/asm/arch-pxa/macro.h new file mode 100644 index 0000000..035a57e --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/macro.h @@ -0,0 +1,324 @@ +/* + * arch/arm/include/asm/arch-pxa/macro.h + * + * Copyright (C) 2010 Marek Vasut marek.vasut@gmail.com + * + * 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 + */ + +#ifndef __ASM_ARCH_PXA_MACRO_H__ +#define __ASM_ARCH_PXA_MACRO_H__ +#ifdef __ASSEMBLY__ + +#include <asm/macro.h> +#include <asm/arch/pxa-regs.h> + +/* + * This macro performs a 32bit write to a memory location and makes sure the + * write operation really happened by performing a read back. + * + * Clobbered regs: r4, r5 + */ +.macro write32rb addr, data + ldr r4, =\addr + ldr r5, =\data + str r5, [r4] + ldr r5, [r4] +.endm + +/* + * This macro waits according to OSCR incrementation + * + * Clobbered regs: r4, r5, r6 + */ +.macro pxa_wait_ticks ticks + ldr r4, =OSCR + mov r5, #0 + str r5, [r4] + ldr r5, =\ticks +1: + ldr r6, [r4] + cmp r5, r6 + bgt 1b +.endm + +/* + * This macro sets up the GPIO pins of the PXA2xx/PXA3xx CPU + * + * Clobbered regs: r4, r5 + */ +.macro pxa_gpio_setup + write32 GPSR0, CONFIG_SYS_GPSR0_VAL + write32 GPSR1, CONFIG_SYS_GPSR1_VAL + write32 GPSR2, CONFIG_SYS_GPSR2_VAL +#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS) + write32 GPSR3, CONFIG_SYS_GPSR3_VAL +#endif + + write32 GPCR0, CONFIG_SYS_GPCR0_VAL + write32 GPCR1, CONFIG_SYS_GPCR1_VAL + write32 GPCR2, CONFIG_SYS_GPCR2_VAL +#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS) + write32 GPCR3, CONFIG_SYS_GPCR3_VAL +#endif + + write32 GPDR0, CONFIG_SYS_GPDR0_VAL + write32 GPDR1, CONFIG_SYS_GPDR1_VAL + write32 GPDR2, CONFIG_SYS_GPDR2_VAL +#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS) + write32 GPDR3, CONFIG_SYS_GPDR3_VAL +#endif + + write32 GAFR0_L, CONFIG_SYS_GAFR0_L_VAL + write32 GAFR0_U, CONFIG_SYS_GAFR0_U_VAL + write32 GAFR1_L, CONFIG_SYS_GAFR1_L_VAL + write32 GAFR1_U, CONFIG_SYS_GAFR1_U_VAL + write32 GAFR2_L, CONFIG_SYS_GAFR2_L_VAL + write32 GAFR2_U, CONFIG_SYS_GAFR2_U_VAL +#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS) + write32 GAFR3_L, CONFIG_SYS_GAFR3_L_VAL + write32 GAFR3_U, CONFIG_SYS_GAFR3_U_VAL +#endif + + write32 PSSR, CONFIG_SYS_PSSR_VAL +.endm + +/* + * This macro sets up the Memory controller of the PXA2xx CPU + * + * Clobbered regs: r3, r4, r5 + */ +.macro pxa_mem_setup + /* This comes handy when setting MDREFR */ + ldr r3, =MEMC_BASE + + /* + * 1) Initialize Asynchronous static memory controller + */ + + /* MSC0: nCS(0,1) */ + write32rb (MEMC_BASE + MSC0_OFFSET), CONFIG_SYS_MSC0_VAL + /* MSC1: nCS(2,3) */ + write32rb (MEMC_BASE + MSC1_OFFSET), CONFIG_SYS_MSC1_VAL + /* MSC2: nCS(4,5) */ + write32rb (MEMC_BASE + MSC2_OFFSET), CONFIG_SYS_MSC2_VAL + + /* + * 2) Initialize Card Interface + */ + + /* MECR: Memory Expansion Card Register */ + write32rb (MEMC_BASE + MECR_OFFSET), CONFIG_SYS_MECR_VAL + /* MCMEM0: Card Interface slot 0 timing */ + write32rb (MEMC_BASE + MCMEM0_OFFSET), CONFIG_SYS_MCMEM0_VAL + /* MCMEM1: Card Interface slot 1 timing */ + write32rb (MEMC_BASE + MCMEM1_OFFSET), CONFIG_SYS_MCMEM1_VAL + /* MCATT0: Card Interface Attribute Space Timing, slot 0 */ + write32rb (MEMC_BASE + MCATT0_OFFSET), CONFIG_SYS_MCATT0_VAL + /* MCATT1: Card Interface Attribute Space Timing, slot 1 */ + write32rb (MEMC_BASE + MCATT1_OFFSET), CONFIG_SYS_MCATT1_VAL + /* MCIO0: Card Interface I/O Space Timing, slot 0 */ + write32rb (MEMC_BASE + MCIO0_OFFSET), CONFIG_SYS_MCIO0_VAL + /* MCIO1: Card Interface I/O Space Timing, slot 1 */ + write32rb (MEMC_BASE + MCIO1_OFFSET), CONFIG_SYS_MCIO1_VAL + + /* + * 3) Configure Fly-By DMA register + */ + + write32rb (MEMC_BASE + FLYCNFG_OFFSET), CONFIG_SYS_FLYCNFG_VAL + + /* + * 4) Initialize Timing for Sync Memory (SDCLK0) + */ + + /* + * Before accessing MDREFR we need a valid DRI field, so we set + * this to power on defaults + DRI field. + */ + ldr r5, [r3, #MDREFR_OFFSET] + bic r5, r5, #0x0ff + bic r5, r5, #0xf00 /* MDREFR user config with zeroed DRI */ + + ldr r4, =CONFIG_SYS_MDREFR_VAL + mov r6, r4 + lsl r4, #20 + lsr r4, #20 /* Get a valid DRI field */ + + orr r5, r5, r4 /* MDREFR user config with correct DRI */ + + orr r5, #MDREFR_K0RUN + orr r5, #MDREFR_SLFRSH + bic r5, #MDREFR_APD + bic r5, #MDREFR_E1PIN + + str r5, [r3, #MDREFR_OFFSET] + ldr r4, [r3, #MDREFR_OFFSET] + + /* + * 5) Initialize Synchronous Static Memory (Flash/Peripherals) + */ + + /* Initialize SXCNFG register. Assert the enable bits. + * + * Write SXMRS to cause an MRS command to all enabled banks of + * synchronous static memory. Note that SXLCR need not be written + * at this time. + */ + write32rb (MEMC_BASE + SXCNFG_OFFSET), CONFIG_SYS_SXCNFG_VAL + + /* + * 6) Initialize SDRAM + */ + + bic r6, #MDREFR_SLFRSH + str r6, [r3, #MDREFR_OFFSET] + ldr r4, [r3, #MDREFR_OFFSET] + + orr r6, #MDREFR_E1PIN + str r6, [r3, #MDREFR_OFFSET] + ldr r4, [r3, #MDREFR_OFFSET] + + /* + * 7) Write MDCNFG with MDCNFG:DEx deasserted (set to 0), to configure + * but not enable each SDRAM partition pair. + */ + + /* Fetch platform value of MDCNFG */ + ldr r4, =CONFIG_SYS_MDCNFG_VAL + /* Disable all sdram banks */ + bic r4, r4, #(MDCNFG_DE0|MDCNFG_DE1) + bic r4, r4, #(MDCNFG_DE2|MDCNFG_DE3) + /* Write initial value of MDCNFG, w/o enabling sdram banks */ + str r4, [r3, #MDCNFG_OFFSET] + ldr r4, [r3, #MDCNFG_OFFSET] + + /* Wait for the clock to the SDRAMs to stabilize, 100..200 usec. */ + pxa_wait_ticks 0x300 + + /* + * 8) Trigger a number (usually 8) refresh cycles by attempting + * non-burst read or write accesses to disabled SDRAM, as commonly + * specified in the power up sequence documented in SDRAM data + * sheets. The address(es) used for this purpose must not be + * cacheable. + */ + + ldr r4, =CONFIG_SYS_DRAM_BASE +.rept 9 + str r5, [r4] +.endr + + /* + * 9) Write MDCNFG with enable bits asserted (MDCNFG:DEx set to 1). + */ + + ldr r5, =CONFIG_SYS_MDCNFG_VAL + ldr r4, =(MDCNFG_DE0 | MDCNFG_DE1 | MDCNFG_DE2 | MDCNFG_DE3) + and r5, r5, r4 + ldr r4, [r3, #MDCNFG_OFFSET] + orr r4, r4, r5 + str r4, [r3, #MDCNFG_OFFSET] + ldr r4, [r3, #MDCNFG_OFFSET] + + /* + * 10) Write MDMRS. + */ + + ldr r4, =CONFIG_SYS_MDMRS_VAL + str r4, [r3, #MDMRS_OFFSET] + ldr r4, [r3, #MDMRS_OFFSET] + + /* + * 11) Enable APD + */ + + ldr r4, [r3, #MDREFR_OFFSET] + and r6, r6, #MDREFR_APD + orr r4, r4, r6 + str r4, [r3, #MDREFR_OFFSET] + ldr r4, [r3, #MDREFR_OFFSET] +.endm + +/* + * This macro tests if the CPU woke up from sleep and eventually resumes + * + * Clobbered regs: r4, r5 + */ +.macro pxa_wakeup + ldr r4, =RCSR + ldr r5, [r4] + and r5, r5, #(RCSR_GPR | RCSR_SMR | RCSR_WDR | RCSR_HWR) + str r5, [r4] + teq r5, #RCSR_SMR + + bne pxa_wakeup_exit + + ldr r4, =PSSR + mov r5, #PSSR_PH + str r5, [r4] + + ldr r4, =PSPR + ldr pc, [r4] +pxa_wakeup_exit: +.endm + +/* + * This macro disables all interupts on PXA2xx/PXA3xx CPU + * + * Clobbered regs: r4, r5 + */ +.macro pxa_intr_setup + write32 ICLR, 0 + write32 ICMR, 0 +#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS) + write32 ICLR2, 0 + write32 ICMR2, 0 +#endif +.endm + +/* + * This macro configures clock on PXA2xx/PXA3xx CPU + * + * Clobbered regs: r4, r5 + */ +.macro pxa_clock_setup + /* Disable the peripheral clocks, and set the core clock frequency */ + + /* Turn Off ALL on-chip peripheral clocks for re-configuration */ + write32 CKEN, CONFIG_SYS_CKEN + + /* Write CCCR */ + write32 CCCR, CONFIG_SYS_CCCR + +#ifdef CONFIG_RTC + /* enable the 32Khz oscillator for RTC and PowerManager */ + write32 OSCC, #OSCC_OON + ldr r4, =OSCC + + /* Spin here until OSCC.OOK get set, meaning the PLL has settled. */ +2: + ldr r5, [r4] + ands r5, r5, #1 + beq 2b +#endif +.endm + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_ARCH_PXA_MACRO_H__ */

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/include/asm/arch-pxa/pxa-regs.h | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index cd7b7f9..d442fb0 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -1132,10 +1132,18 @@ typedef void (*ExcpHndlr) (void) ; #define PWM_PWDUTY0 __REG(0x40B00004) /* PWM 0 Duty Cycle Register */ #define PWM_PERVAL0 __REG(0x40B00008) /* PWM 0 Period Control Register */
-#define PWM_CTRL1 __REG(0x40C00000) /* PWM 1Control Register */ +#define PWM_CTRL1 __REG(0x40C00000) /* PWM 1 Control Register */ #define PWM_PWDUTY1 __REG(0x40C00004) /* PWM 1 Duty Cycle Register */ #define PWM_PERVAL1 __REG(0x40C00008) /* PWM 1 Period Control Register */
+#define PWM_CTRL2 __REG(0x40B00010) /* PWM 2 Control Register */ +#define PWM_PWDUTY2 __REG(0x40B00014) /* PWM 2 Duty Cycle Register */ +#define PWM_PERVAL2 __REG(0x40B00018) /* PWM 2 Period Control Register */ + +#define PWM_CTRL3 __REG(0x40C00010) /* PWM 3 Control Register */ +#define PWM_PWDUTY3 __REG(0x40C00014) /* PWM 3 Duty Cycle Register */ +#define PWM_PERVAL3 __REG(0x40C00018) /* PWM 3 Period Control Register */ + /* * Interrupt Controller */

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/cpu/pxa/start.S | 48 +++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/pxa/start.S b/arch/arm/cpu/pxa/start.S index e07c8c2..8010b0e 100644 --- a/arch/arm/cpu/pxa/start.S +++ b/arch/arm/cpu/pxa/start.S @@ -34,6 +34,25 @@
.globl _start _start: b reset +#ifdef CONFIG_PRELOADER + ldr pc, _hang + ldr pc, _hang + ldr pc, _hang + ldr pc, _hang + ldr pc, _hang + ldr pc, _hang + ldr pc, _hang + +_hang: + .word do_hang + .word 0x12345678 + .word 0x12345678 + .word 0x12345678 + .word 0x12345678 + .word 0x12345678 + .word 0x12345678 + .word 0x12345678 /* now 16*4=64 */ +#else ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort @@ -49,6 +68,7 @@ _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq +#endif /* CONFIG_PRELOADER */
.balignl 16,0xdeadbeef
@@ -117,8 +137,10 @@ reset: relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ +#ifndef CONFIG_PRELOADER cmp r0, r1 /* don't reloc during debug */ beq stack_setup +#endif
ldr r2, _armboot_start ldr r3, _bss_start @@ -135,28 +157,37 @@ copy_loop: /* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ - sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ - sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */ +#ifdef CONFIG_PRELOADER + sub sp, r0, #128 /* leave 32 words for abort-stack */ +#else + sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ + sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif /* CONFIG_USE_IRQ */ sub sp, r0, #12 /* leave 3 words for abort-stack */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ +#endif
clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */
+#ifndef CONFIG_PRELOADER clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l +#endif
ldr pc, _start_armboot
+#ifdef CONFIG_ONENAND_IPL +_start_armboot: .word start_oneboot +#else _start_armboot: .word start_armboot - +#endif
/****************************************************************************/ /* */ @@ -296,7 +327,7 @@ setspeed_done: */ mov pc, lr
- +#ifndef CONFIG_PRELOADER /****************************************************************************/ /* */ /* Interrupt handling */ @@ -394,6 +425,7 @@ setspeed_done: .macro get_fiq_stack @ setup FIQ stack ldr sp, FIQ_STACK_START .endm +#endif /* CONFIG_PRELOADER */
/****************************************************************************/ @@ -402,6 +434,12 @@ setspeed_done: /* */ /****************************************************************************/
+#ifdef CONFIG_PRELOADER + .align 5 +do_hang: + ldr sp, _TEXT_BASE /* use 32 words abort stack */ + bl hang /* hang and never return */ +#else /* !CONFIG_PRELOADER */ .align 5 undefined_instruction: get_bad_stack @@ -461,7 +499,7 @@ fiq: get_bad_stack bad_save_user_regs bl do_fiq - +#endif /* CONFIG_PRELOADER */ #endif /* CONFIG_USE_IRQ */
/****************************************************************************/

From: Compulab uboot none@none
Signed-off-by: Marek Vasut marek.vasut@gmail.com --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/pxa3xx_nand.c | 848 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 849 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/pxa3xx_nand.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 28f27da..cd840cd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.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..380c918 --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,848 @@ +/* + * 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. + */ + +#include <common.h> + +#if defined(CONFIG_CMD_NAND) +#ifdef CONFIG_NEW_NAND_CODE + +#include <nand.h> +#include <asm/arch/pxa-regs.h> + +#include <linux/mtd/compat.h> +#include <linux/types.h> +#include <asm/io.h> +#include <asm/errno.h> + +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDSR_MASK (0xfff) + +#define NDCB0_CMD_TYPE_MASK (0x7 << 21) +#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) + +#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) + +#define msleep mdelay +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_SENDCMD = -2, + ERR_DBERR = -3, + ERR_BBERR = -4, +}; + +enum { + STATE_READY = 0, + STATE_CMD_HANDLE, + STATE_PIO_READING, + STATE_PIO_WRITING, +}; + +/* 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 + +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_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; +}; + +struct pxa3xx_nand_info { + const struct pxa3xx_nand_flash *flash_info; + + unsigned int buf_start; + unsigned int buf_count; + + unsigned char data_buff[MAX_BUFF_SIZE]; + size_t data_buff_size; + 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 ? */ + size_t data_size; /* data size in FIFO */ + int retcode; + + /* 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; +}; + +static struct pxa3xx_nand_info cmx3xx_nand_info = { + .data_buff_size = MAX_BUFF_SIZE, +}; + +static struct pxa3xx_nand_flash default_flash; + +static struct pxa3xx_nand_cmdset smallpage_cmdset = { + .read1 = 0x0000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_cmdset largepage_cmdset = { + .read1 = 0x3000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +#define WAIT_EVENT_TIMEOUT (2 * CONFIG_SYS_HZ/10) + +static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +{ + int timeout = WAIT_EVENT_TIMEOUT; + uint32_t ndsr; + + while (timeout--) { + ndsr = NDSR & NDSR_MASK; + if (ndsr & event) { + 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; + 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; + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + + if (cmd == cmdset->read_id) { + info->ndcb0 |= NDCB0_CMD_TYPE(3); + info->data_size = 8; + } else if (cmd == cmdset->read_status) { + info->ndcb0 |= NDCB0_CMD_TYPE(4); + info->data_size = 8; + } else if (cmd == cmdset->reset || cmd == cmdset->lock || + cmd == cmdset->unlock) { + info->ndcb0 |= NDCB0_CMD_TYPE(5); + } else + return -EINVAL; + + return 0; +} + +/* 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); +} + +/* wait_event with timeout */ +static unsigned int wait_event(unsigned long event) +{ + unsigned long ndsr, timeout, start = OSCR; + + if(!event) + return 0xff000000; + + if(event & (NDSR_CS0_CMDD | NDSR_CS0_BBD)) + timeout = CONFIG_SYS_NAND_PROG_ERASE_TO * OSCR_CLK_FREQ; + else + timeout = CONFIG_SYS_NAND_OTHER_TO * OSCR_CLK_FREQ; + + while(1) { + ndsr = NDSR; + if(ndsr & event) { + NDSR |= event; + break; + } + if(get_delta(start) > timeout) { + printf("wait_event: TIMEOUT waiting for event: 0x%lx.\n", event); + return 0xff000000; + } + } + return ndsr; +} + +/* 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; + + /* clear status bits and run */ + NDSR = NDSR_MASK; + + ndcr = info->reg_ndcr; + ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; + ndcr |= NDCR_ND_RUN; + + NDCR = ndcr; + + if (wait_for_event(info, NDSR_WRCMDREQ)) { + printk(KERN_ERR "timed out writing command\n"); + return -ETIMEDOUT; + } + + NDCB0 = info->ndcb0; + NDCB0 = info->ndcb1; + NDCB0 = info->ndcb2; + return 0; +} + +static int handle_data_pio(struct pxa3xx_nand_info *info) +{ + int i = 0, j, ret; + unsigned int aligned_data_size = info->data_size & 0xfffffffc; + unsigned int rest_data_size = info->data_size & 0x3; + unsigned long *long_buf; + + switch (info->state) { + case STATE_PIO_WRITING: + if (aligned_data_size) { + for (i = 0; i < aligned_data_size; i += 4) { + long_buf = (unsigned long *) &info->data_buff[i]; + NDDB = *long_buf; + } + } + if (rest_data_size) + printf("handle_data_pio: ERROR, writing non 4-byte aligned data.\n"); + + if(wait_for_event(info, NDSR_CS0_CMDD)) { + printk(KERN_ERR "program command time out\n"); + return -1; + } + + break; + case STATE_PIO_READING: + if (aligned_data_size) { + for (i = 0; i < aligned_data_size; i += 4) { + long_buf = (unsigned long *) &info->data_buff[i]; + *long_buf = NDDB; + } + } + if (rest_data_size) { + unsigned int rest_data = NDDB; + for(j = 0; j < rest_data_size; j++) + info->data_buff[i + j] = (u_char) ((rest_data >> j) & 0xff); + } + + break; + default: + printk(KERN_ERR "handle_data_pio: invalid state %d\n", info->state); + return -EINVAL; + } + + info->state = STATE_READY; + return 0; +} + +static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) +{ + unsigned int status; + + if (write_cmd(info)) { + info->retcode = ERR_SENDCMD; + goto fail_stop; + } + + info->state = STATE_CMD_HANDLE; + + status = wait_event(event); + if (status & (NDSR_RDDREQ | NDSR_DBERR)) { + if (status & NDSR_DBERR) + info->retcode = ERR_DBERR; + + info->state = STATE_PIO_READING; + } else if (status & NDSR_WRDREQ) { + info->state = STATE_PIO_WRITING; + } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { + if (status & NDSR_CS0_BBD) + info->retcode = ERR_BBERR; + + info->state = STATE_READY; + } +/* NDSR = status; */ + + if (info->data_size > 0) + if (handle_data_pio(info)) + goto fail_stop; + + return 0; + +fail_stop: + NDCR &= ~NDCR_ND_RUN; + udelay(10); + return -ETIMEDOUT; +} + +static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) +{ + return 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; + int ret; + + info->use_ecc = 0; + info->data_size = 0; + info->state = STATE_READY; + + switch (command) { + case NAND_CMD_READOOB: + /* disable HW ECC to get all the OOB data */ + info->buf_count = mtd->writesize + mtd->oobsize; + info->buf_start = mtd->writesize + column; + + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; + + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); + + /* 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: + 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); + + 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: + 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: + 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: + 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: + break; + case NAND_CMD_READID: + case NAND_CMD_STATUS: + 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: + if (prepare_other_cmd(info, cmdset->reset)) + break; + + ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); + if (ret == 0) { + int timeout = 2; + + while (timeout--) { + if (NDSR & NDSR_RDY) + break; + msleep(10); + } + 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + 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; +} + +/* + * not required for Monahans DFC + */ +static void pxa3xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct pxa3xx_nand_info *info = &cmx3xx_nand_info; + + /* 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 pxa3xx_nand_info *info = &cmx3xx_nand_info; + /* + * 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 + */ + 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; + uint8_t id_buff[8]; + int i; + unsigned long *long_buf; + + 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; + + for (i = 0; i < 8; i += 4) { + long_buf = (unsigned long *) &id_buff[i]; + *long_buf = NDDB; + } + *id = id_buff[0] | (id_buff[1] << 8); + return 0; + +fail_timeout: + 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) +{ + 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; + 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; + + ndcr |= NDCR_ND_ARB_EN; + 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; + + return 0; +} + +static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr = 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) - 1; + + 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; + + return 0; +} + +static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +{ + uint32_t id = -1; + int i; + + if (pxa3xx_nand_detect_config(info) == 0) + return 0; + + for (i = 0; i < 1; ++i) { + if (pxa3xx_nand_config_flash(info, info->flash_info)) + continue; + + if (__readid(info, &id)) + continue; + + return 0; + } + + printf("failed to detect configured nand flash; found %04x instead of\n", id); + + return -ENODEV; +} + +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} } +}; + +int board_nand_init(struct nand_chip *this) +{ + int ret; + struct pxa3xx_nand_info *info = &cmx3xx_nand_info; + const struct pxa3xx_nand_flash *f; + + /* Enable NAND flash clock */ + CKENA |= CKENA_4_NAND; + + ret = pxa3xx_nand_detect_flash(info); + if (ret) { + printf("board_nand_init: failed to detect flash\n"); + return -ENODEV; + } + + f = info->flash_info; + + this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0; + + this->cmd_ctrl = pxa3xx_nand_cmd_ctrl; + 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; + + return 0; +} + +#else +#error "U-Boot legacy NAND support not available for Monahans DFC." +#endif +#endif

On Tue, Jul 06, 2010 at 03:12:49AM +0200, Marek Vasut wrote:
From: Compulab uboot none@none
Hmm?
Signed-off-by: Marek Vasut marek.vasut@gmail.com
drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/pxa3xx_nand.c | 848 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 849 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/pxa3xx_nand.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 28f27da..cd840cd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.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..380c918 --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,848 @@ +/*
- 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.
- */
+#include <common.h>
+#if defined(CONFIG_CMD_NAND)
The makefile already limits compilation of this file to when CONFIG_NAND_PXA3XX is defined.
+#ifdef CONFIG_NEW_NAND_CODE
Hmm?
+#define WAIT_EVENT_TIMEOUT (2 * CONFIG_SYS_HZ/10)
+static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +{
- int timeout = WAIT_EVENT_TIMEOUT;
- uint32_t ndsr;
- while (timeout--) {
ndsr = NDSR & NDSR_MASK;
if (ndsr & event) {
NDSR = ndsr;
return 0;
}
udelay(10);
- }
You're defining WAIT_EVENT_TIMEOUT in terms of CONFIG_SYS_HZ ticks (which are supposed to be 1ms, but in any case you should only use CONFIG_SYS_HZ when you're interacting with get_ticks() or similar), but you're interpreting it as a number of 10us intervals.
- 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 you put braces on one half of the if, please put it on the other half (and also when you have a multi-line body even if it's technically only one statement).
+/* 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);
+}
What's wrong with get_ticks()?
+/* wait_event with timeout */ +static unsigned int wait_event(unsigned long event) +{
- unsigned long ndsr, timeout, start = OSCR;
- if(!event)
return 0xff000000;
- if(event & (NDSR_CS0_CMDD | NDSR_CS0_BBD))
timeout = CONFIG_SYS_NAND_PROG_ERASE_TO * OSCR_CLK_FREQ;
- else
timeout = CONFIG_SYS_NAND_OTHER_TO * OSCR_CLK_FREQ;
- while(1) {
Space after keywords like "if" and "while".
+static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) +{
- unsigned int status;
- if (write_cmd(info)) {
info->retcode = ERR_SENDCMD;
goto fail_stop;
- }
- info->state = STATE_CMD_HANDLE;
- status = wait_event(event);
- if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
if (status & NDSR_DBERR)
info->retcode = ERR_DBERR;
info->state = STATE_PIO_READING;
- } else if (status & NDSR_WRDREQ) {
info->state = STATE_PIO_WRITING;
- } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) {
if (status & NDSR_CS0_BBD)
info->retcode = ERR_BBERR;
info->state = STATE_READY;
- }
+/* NDSR = status; */
Why commented out?
+fail_stop:
- NDCR &= ~NDCR_ND_RUN;
- udelay(10);
- return -ETIMEDOUT;
Why is the udelay needed?
- default:
printk(KERN_ERR "non-supported command.\n");
That's a bit vague -- tell the user this message comes from the NAND driver, and what the bad command is.
+/*
- not required for Monahans DFC
- */
+static void pxa3xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{
- return;
+}
Just don't provide it at all.
+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;
- uint8_t id_buff[8];
- int i;
- unsigned long *long_buf;
- 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;
- for (i = 0; i < 8; i += 4) {
long_buf = (unsigned long *) &id_buff[i];
*long_buf = NDDB;
- }
- *id = id_buff[0] | (id_buff[1] << 8);
- return 0;
+fail_timeout:
- NDCR &= ~NDCR_ND_RUN;
- udelay(10);
- return -ETIMEDOUT;
+}
Why is this done differently here than when the generic layer issues a READID command?
+static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_flash *f)
+{
- 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;
- 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;
This looks duplicative of pxa3xx_nand_detect_config.
- /* 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;
}
- }
Why do you need to do this here? The generic NAND code will look this up.
+static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +{
- uint32_t id = -1;
- int i;
- if (pxa3xx_nand_detect_config(info) == 0)
return 0;
- for (i = 0; i < 1; ++i) {
What's this one-iteration loop for?
if (pxa3xx_nand_config_flash(info, info->flash_info))
continue;
if (__readid(info, &id))
continue;
return 0;
- }
- printf("failed to detect configured nand flash; found %04x instead of\n", id);
Instead of what? Did you really "find" anything in this case?
pxa3xx_nand_detect_config already does the __readid, why do you need to do it again here? You don't do anything with the id if __readid succeeds.
- return -ENODEV;
+}
+static struct nand_ecclayout hw_smallpage_ecclayout = {
- .eccbytes = 6,
- .eccpos = {8, 9, 10, 11, 12, 13 },
- .oobfree = { {2, 6} }
+};
Normally with small page NAND the bad block marker is at byte 5, at least with 8-bit chips.
If you really have bad block information somewhere else, you'll want to provide a non-default badblock_pattern.
+#else +#error "U-Boot legacy NAND support not available for Monahans DFC." +#endif +#endif
Legacy NAND has been removed from u-boot altogether. Please don't reintroduce references to it.
-Scott

Dne Pá 9. července 2010 22:04:00 Scott Wood napsal(a):
On Tue, Jul 06, 2010 at 03:12:49AM +0200, Marek Vasut wrote:
From: Compulab uboot none@none
Hmm?
Well I was unable to figure out who was the author, though the license of the code is GPL so it should be OK ? I'll try poking around a little bit more, but it's unlikely I'll find some more info.
I dropped this patch from -next until this is fixed so -next can be pulled.
Thanks for reviewing.
Signed-off-by: Marek Vasut marek.vasut@gmail.com
drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/pxa3xx_nand.c | 848 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 849 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/pxa3xx_nand.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 28f27da..cd840cd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.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..380c918 --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,848 @@ +/*
- 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.
- */
+#include <common.h>
+#if defined(CONFIG_CMD_NAND)
The makefile already limits compilation of this file to when CONFIG_NAND_PXA3XX is defined.
+#ifdef CONFIG_NEW_NAND_CODE
Hmm?
+#define WAIT_EVENT_TIMEOUT (2 * CONFIG_SYS_HZ/10)
+static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +{
- int timeout = WAIT_EVENT_TIMEOUT;
- uint32_t ndsr;
- while (timeout--) {
ndsr = NDSR & NDSR_MASK;
if (ndsr & event) {
NDSR = ndsr;
return 0;
}
udelay(10);
- }
You're defining WAIT_EVENT_TIMEOUT in terms of CONFIG_SYS_HZ ticks (which are supposed to be 1ms, but in any case you should only use CONFIG_SYS_HZ when you're interacting with get_ticks() or similar), but you're interpreting it as a number of 10us intervals.
- 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 you put braces on one half of the if, please put it on the other half (and also when you have a multi-line body even if it's technically only one statement).
+/* 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);
+}
What's wrong with get_ticks()?
+/* wait_event with timeout */ +static unsigned int wait_event(unsigned long event) +{
- unsigned long ndsr, timeout, start = OSCR;
- if(!event)
return 0xff000000;
- if(event & (NDSR_CS0_CMDD | NDSR_CS0_BBD))
timeout = CONFIG_SYS_NAND_PROG_ERASE_TO * OSCR_CLK_FREQ;
- else
timeout = CONFIG_SYS_NAND_OTHER_TO * OSCR_CLK_FREQ;
- while(1) {
Space after keywords like "if" and "while".
+static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) +{
- unsigned int status;
- if (write_cmd(info)) {
info->retcode = ERR_SENDCMD;
goto fail_stop;
- }
- info->state = STATE_CMD_HANDLE;
- status = wait_event(event);
- if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
if (status & NDSR_DBERR)
info->retcode = ERR_DBERR;
info->state = STATE_PIO_READING;
- } else if (status & NDSR_WRDREQ) {
info->state = STATE_PIO_WRITING;
- } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) {
if (status & NDSR_CS0_BBD)
info->retcode = ERR_BBERR;
info->state = STATE_READY;
- }
+/* NDSR = status; */
Why commented out?
+fail_stop:
- NDCR &= ~NDCR_ND_RUN;
- udelay(10);
- return -ETIMEDOUT;
Why is the udelay needed?
- default:
printk(KERN_ERR "non-supported command.\n");
That's a bit vague -- tell the user this message comes from the NAND driver, and what the bad command is.
+/*
- not required for Monahans DFC
- */
+static void pxa3xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{
- return;
+}
Just don't provide it at all.
+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;
- uint8_t id_buff[8];
- int i;
- unsigned long *long_buf;
- 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;
- for (i = 0; i < 8; i += 4) {
long_buf = (unsigned long *) &id_buff[i];
*long_buf = NDDB;
- }
- *id = id_buff[0] | (id_buff[1] << 8);
- return 0;
+fail_timeout:
- NDCR &= ~NDCR_ND_RUN;
- udelay(10);
- return -ETIMEDOUT;
+}
Why is this done differently here than when the generic layer issues a READID command?
+static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_flash *f)
+{
- 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;
- 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;
This looks duplicative of pxa3xx_nand_detect_config.
- /* 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;
}
- }
Why do you need to do this here? The generic NAND code will look this up.
+static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +{
- uint32_t id = -1;
- int i;
- if (pxa3xx_nand_detect_config(info) == 0)
return 0;
- for (i = 0; i < 1; ++i) {
What's this one-iteration loop for?
if (pxa3xx_nand_config_flash(info, info->flash_info))
continue;
if (__readid(info, &id))
continue;
return 0;
- }
- printf("failed to detect configured nand flash; found %04x instead
of\n", id);
Instead of what? Did you really "find" anything in this case?
pxa3xx_nand_detect_config already does the __readid, why do you need to do it again here? You don't do anything with the id if __readid succeeds.
- return -ENODEV;
+}
+static struct nand_ecclayout hw_smallpage_ecclayout = {
- .eccbytes = 6,
- .eccpos = {8, 9, 10, 11, 12, 13 },
- .oobfree = { {2, 6} }
+};
Normally with small page NAND the bad block marker is at byte 5, at least with 8-bit chips.
If you really have bad block information somewhere else, you'll want to provide a non-default badblock_pattern.
+#else +#error "U-Boot legacy NAND support not available for Monahans DFC." +#endif +#endif
Legacy NAND has been removed from u-boot altogether. Please don't reintroduce references to it.
-Scott

On Sat, Jul 10, 2010 at 12:24:27AM +0200, Marek Vasut wrote:
Dne Pá 9. července 2010 22:04:00 Scott Wood napsal(a):
On Tue, Jul 06, 2010 at 03:12:49AM +0200, Marek Vasut wrote:
From: Compulab uboot none@none
Hmm?
Well I was unable to figure out who was the author, though the license of the code is GPL so it should be OK ? I'll try poking around a little bit more, but it's unlikely I'll find some more info.
I'd just describe where it came from in the commit message, rather than use a From: line. The code is (partly?) from them, but the patch is from you.
-Scott
participants (2)
-
Marek Vasut
-
Scott Wood