diff --git a/Makefile b/Makefile index 34a12b415..82a1a4c1e 100644 --- a/Makefile +++ b/Makefile @@ -972,8 +972,8 @@ omap730p2_cs3boot_config : unconfig sciphone_g2_config: unconfig @mkdir -p $(obj)include - @ > $(obj)include/config.h @$(MKCONFIG) -n $@ -a sciphone_g2 arm arm926ejs sciphone_g2 mtk mtk + @echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk spear300_config \ spear310_config \ diff --git a/drivers/mtd/nand/mt62xx_nand.c b/drivers/mtd/nand/mt62xx_nand.c index a25a18c88..7c5786ad2 100644 --- a/drivers/mtd/nand/mt62xx_nand.c +++ b/drivers/mtd/nand/mt62xx_nand.c @@ -92,6 +92,24 @@ */ #define ECC_SPARE_BYTE_POS 8 +/* + * Macro which counts zeroes until first set bit. + * This is used to avoid dividing, so no additional library is needed. + * It's important as this file is compiled also in SPL and there is no need + * to link additional library. + */ +#ifdef CONFIG_ARM926EJS + #define COUNT_ZEROES(x) __builtin_ctz(x) +#else + #define COUNT_ZEROES(x) (ffs(x) - 1) +#endif + +#ifdef CONFIG_PRELOADER + #define nand_print(x) +#else + #define nand_print(x) printk(KERN_ERR (x)) +#endif + static void nand_ctrl_change(int cmd, int address, int *addr_cycle, int write_size) { @@ -192,10 +210,12 @@ static int mt62xx_nand_dev_ready(struct mtd_info *mtd) static void mt62xx_nand_select_chip(struct mtd_info *mtd, int chip) { if (chip > NAND_CHIPS_MAX) { - printk(KERN_ERR "Wrong NAND chip number!\n"); + nand_print("Wrong NAND chip number!\n"); return; } - writel(chip, MTK_NFI_CSEL); + + if (chip != -1) + writel(chip, MTK_NFI_CSEL); } static void mt62xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) @@ -205,7 +225,7 @@ static void mt62xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) int i; if (len % 4) - printk(KERN_ERR "Length parameter is not aligned\n"); + nand_print("Length parameter is not aligned\n"); for (i = 0; i < len/4; ++i) buf_32[i] = readl(chip->IO_ADDR_R); @@ -232,10 +252,10 @@ static void mt62xx_nand_write_ecc(struct mtd_info *mtd, int len) uint8_t ecc[8]; /* - * Two ECC blocks are combined in Sciphone G2 format, - * that's why there is division by 2. + * Two ECC blocks are combined on MT62xx platform, + * that's why writesize is multiplied by 2. */ - ecc_blocks = mtd->writesize/chip->ecc.size/2; + ecc_blocks = (mtd->writesize * 2) >> COUNT_ZEROES(chip->ecc.size); for (ecc_nr = 0; ecc_nr < ecc_blocks; ++ecc_nr) { int ecc_p, ecc_c, pos = 0; @@ -286,7 +306,7 @@ static void mt62xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, mt62xx_nand_write_ecc(mtd, len); } else { if (len % 4) - printk(KERN_ERR "Length parameter is not aligned\n"); + nand_print("Length parameter is not aligned\n"); for (i = 0; i < len/4; ++i) writel(buf_32[i], chip->IO_ADDR_W); @@ -307,7 +327,7 @@ static int mt62xx_nand_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, * which is currently read. We can use it to calculate which ECC block * has been already read. */ - *ecc_code = readw(MTK_NFI_ADDRCNTR)/chip->ecc.size; + *ecc_code = readw(MTK_NFI_ADDRCNTR) >> COUNT_ZEROES(chip->ecc.size); return 0; } @@ -370,6 +390,12 @@ int board_nand_init(struct nand_chip *chip) 7 << NFI_ACCCON_LCD2NAND_SHIFT, MTK_NFI_ACCCON); + /* + * Reset NFI page format control register. + * After processor reset it's not always zero what renders problems. + */ + writel(0, MTK_NFI_PAGEFMT); + /* Flush and reset NFI FIFO */ writel(NFI_OPCON_FIFO_FLUSH | NFI_OPCON_FIFO_RST, MTK_NFI_OPCON); while(readl(MTK_NFI_OPCON)) diff --git a/include/configs/sciphone_g2.h b/include/configs/sciphone_g2.h index b81a9601e..9ce0c9ea8 100644 --- a/include/configs/sciphone_g2.h +++ b/include/configs/sciphone_g2.h @@ -20,6 +20,18 @@ * */ +/* + * U-Boot configuration is split in two parts: + * - SPL configuration + * - normal configuration + * + * MT62XX platform has IPL (Initial Program Loader) in ROM. + * After processor power up IPL execution is started. IPL tries to load + * code from NAND to internal RAM (64KB) and starts executing loaded code. + * SBL (Secondary Program Loader) is loaded by IPL. SBL configures basic + * peripherals (PLL, SDRAM memory) and loads U-Boot from NAND to SDRAM. + */ + #ifndef __CONFIG_H #define __CONFIG_H @@ -27,6 +39,29 @@ #define CONFIG_ARM926EJS +/* DRAM memory related configurations */ +#define CONFIG_NR_DRAM_BANKS 1 +#define PHYS_SDRAM_1 0x00000000 +#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */ +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 + +/* NAND memory related configurations */ +#define CONFIG_NAND_MT62XX +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_BASE MTK_NFI_BASE + +/* There is no NOR flash, so undefine these commands */ +#undef CONFIG_CMD_FLASH +#undef CONFIG_CMD_IMLS +#define CONFIG_SYS_NO_FLASH + + +#ifndef CONFIG_PRELOADER + +/* + * Configuration of U-Boot when running from DRAM (normal operation). + */ + /* We have already been loaded to RAM */ #define CONFIG_SKIP_LOWLEVEL_INIT @@ -40,16 +75,8 @@ #define CONFIG_BAUDRATE 115200 #define CONFIG_SYS_BAUDRATE_TABLE {9600, 19200, 38400, 57600, 115200} -/* There is no NOR flash, so undefine these commands */ -#undef CONFIG_CMD_FLASH -#undef CONFIG_CMD_IMLS -#define CONFIG_SYS_NO_FLASH - -/* Configure NAND storage */ -#define CONFIG_NAND_MT62XX +/* Turn on some U-Boot commands */ #define CONFIG_CMD_NAND -#define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_BASE MTK_NFI_BASE #define CONFIG_CMD_MEMORY #define CONFIG_CMD_LOADB #define CONFIG_CMD_RUN @@ -81,14 +108,6 @@ #define CONFIG_BOOTARGS "console=ttyMTK0,115200n8 mem=64M@0" #define CONFIG_BOOTCOMMAND "bootm 0x800000" -/* Memory related information */ -#define CONFIG_NR_DRAM_BANKS 1 -#define PHYS_SDRAM_1 0x00000000 -#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */ -#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 -#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000) -#define CONFIG_MAX_RAM_BANK_SIZE 0x10000000 - #define CONFIG_STACKSIZE (128 * 1024) #ifdef CONFIG_USE_IRQ # define CONFIG_STACKSIZE_IRQ (4 * 1024) /* IRQ stack */ @@ -97,8 +116,11 @@ #define CONFIG_SYS_MEMTEST_START 0x00000000 #define CONFIG_SYS_MEMTEST_END 0x02FFFFFF -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 256 * 1024) #define CONFIG_SYS_GBL_DATA_SIZE 128 +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 256 * 1024) + +#define CONFIG_MAX_RAM_BANK_SIZE 0x10000000 +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + CONFIG_STACKSIZE) /* This is needed to make hello_world.c happy */ #define CONFIG_SYS_MAX_FLASH_SECT 512 @@ -132,4 +154,22 @@ #endif /* CONFIG_MMC */ +#else /* CONFIG_PRELOADER */ + +/* + * Configuration of U-Boot for SPL. + */ +#define CONFIG_STACKSIZE 1024 +#define CONFIG_SYS_MALLOC_LEN 1024 +#define INTERNAL_RAM_BASE 0x40000000 +#define CONFIG_SYS_INIT_SP_ADDR (INTERNAL_RAM_BASE + CONFIG_STACKSIZE) +#define CONFIG_ENV_SIZE 0x0 + +/* Address of U-Boot in NAND */ +#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x20000 +#define CONFIG_SYS_NAND_U_BOOT_SIZE 1000000 /* 1MB */ +#define CONFIG_SYS_NAND_U_BOOT_DST 0x500000 +#define CONFIG_SYS_NAND_U_BOOT_START 0x500000 + +#endif /* CONFIG_PRELOADER */ #endif /* __CONFIG_H */ diff --git a/nand_spl/board/mtk/sciphone_g2/Makefile b/nand_spl/board/mtk/sciphone_g2/Makefile new file mode 100644 index 000000000..6bc47a8b6 --- /dev/null +++ b/nand_spl/board/mtk/sciphone_g2/Makefile @@ -0,0 +1,82 @@ +# +# (C) Copyright 2006-2007 +# Stefan Roese, DENX Software Engineering, sr@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, 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 +# + +CONFIG_NAND_SPL = y + +include $(TOPDIR)/config.mk +include $(TOPDIR)/nand_spl/board/$(BOARDDIR)/config.mk + +LDSCRIPT= $(TOPDIR)/nand_spl/board/$(BOARDDIR)/u-boot.lds +LDFLAGS = -Bstatic -T $(nandobj)u-boot.lds -Ttext $(CONFIG_SYS_TEXT_BASE) $(PLATFORM_LDFLAGS) +AFLAGS += -DCONFIG_PRELOADER -DCONFIG_NAND_SPL -DCONFIG_SYS_TEXT_BASE=$(CONFIG_SYS_TEXT_BASE) +CFLAGS += -DCONFIG_PRELOADER -DCONFIG_NAND_SPL -DCONFIG_SYS_TEXT_BASE=$(CONFIG_SYS_TEXT_BASE) + +SOBJS = start.o lowlevel_init.o +COBJS = nand_boot_detect.o mt62xx_nand.o nand_ids.o + +SRCS := $(addprefix $(obj),$(SOBJS:.o=.S) $(COBJS:.o=.c)) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) +__OBJS := $(SOBJS) $(COBJS) +LNDIR := $(OBJTREE)/nand_spl/board/$(BOARDDIR) + +nandobj := $(OBJTREE)/nand_spl/ + +ALL = $(nandobj)u-boot-spl $(nandobj)u-boot-spl.bin $(nandobj)u-boot-spl-16k.bin + +#all: $(obj).depend $(ALL) +all: $(ALL) + +$(nandobj)u-boot-spl-16k.bin: $(nandobj)u-boot-spl + $(OBJCOPY) ${OBJCFLAGS} --pad-to=$(PAD_TO) -O binary $< $@ + +$(nandobj)u-boot-spl.bin: $(nandobj)u-boot-spl + $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ + +$(nandobj)u-boot-spl: $(OBJS) $(nandobj)u-boot.lds + cd $(LNDIR) && $(LD) $(LDFLAGS) $(__OBJS) \ + -Map $(nandobj)u-boot-spl.map \ + -o $(nandobj)u-boot-spl + +$(nandobj)u-boot.lds: $(LDSCRIPT) + $(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@ + +######################################################################### + +$(obj)%.o: $(SRCTREE)/arch/arm/cpu/arm926ejs/%.S + $(CC) $(AFLAGS) -c -o $@ $< + +$(obj)%.o: $(SRCTREE)/nand_spl/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(obj)%.o: $(SRCTREE)/drivers/mtd/nand/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/nand_spl/board/mtk/sciphone_g2/config.mk b/nand_spl/board/mtk/sciphone_g2/config.mk new file mode 100644 index 000000000..49ae951d6 --- /dev/null +++ b/nand_spl/board/mtk/sciphone_g2/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2006 +# Stefan Roese, DENX Software Engineering, sr@denx.de. +# +# 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 + +# On Siphone G2 the SPL is located in SRAM. +# CONFIG_SYS_TEXT_BASE for SPL: + +CONFIG_SYS_TEXT_BASE = 0x40002000 diff --git a/nand_spl/board/mtk/sciphone_g2/lowlevel_init.S b/nand_spl/board/mtk/sciphone_g2/lowlevel_init.S new file mode 100644 index 000000000..e191961da --- /dev/null +++ b/nand_spl/board/mtk/sciphone_g2/lowlevel_init.S @@ -0,0 +1,221 @@ +/* + * (C) 2010 by Tieto + * Marcin Mielczarczyk + * + * All Rights Reserved + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include + +.globl lowlevel_init +.type lowlevel_init,function +lowlevel_init: + +/* ----------------------------- + * Initialize PLL + * ----------------------------- + */ + /* Power on PLL */ + ldr r0, =MTK_PLL_PDN_CON + mov r1, #0 + str r1, [r0] + /* Turn on MCU and DSP dividers, mark that SYSCLK is 26MHz */ + ldr r0, =MTK_PLL_CLK_CON + mov r1, #0x83 + str r1, [r0] + /* Reset PLL */ + ldr r0, =MTK_PLL_PLL + mov r1, #0x80 + str r1, [r0] + mov r1, #0 + str r1, [r0] + mov r1, #0xFF +PLL_DELAY_loop: + subs r1, r1, #1 + bgt PLL_DELAY_loop + /* Turn on PLL for MCU, DSP and USB */ + mov r1, #0x70 + str r1, [r0] + /* + * Setup MCU clock register: + * ARMCLK = 208MHz, AHBx4CLK = 52MHz, AHBx8CLK = 104MHz + * we have to write to the read-only part (EMICLK) as well, otherwise + * the EMI won't work! (datasheet lies) + */ + ldr r0, =MTK_CONFG_MCUCLK_CON + ldr r1, =0x7F37 + str r1, [r0] + +/* ----------------------------- + * Initialize SDRAM controller + * ----------------------------- + */ + /* Reset index of SDRAM config table */ + mov r7, #0 +SDRAM_TRY_CONFIG: + adr r1, SDRAM_CONFIG_TABLE + /* Calculate config table offset (index * element_size) */ + lsl r2, r7, #3 + /* Calculate address of element in config table */ + add r1, r1, r2 + /* Load start address of SDRAM configuration */ + ldr r0, [r1] + /* Check if end of SDRAM config table is reached */ + cmp r0, #0 + beq SDRAM_PASSED + /* Load end address of SDRAM configuration */ + ldr r6, [r1, #4] + +/* Configure SDRAM controller */ +SDRAM_REGSET_LOOP: + ldr r1, [r0] + ldr r2, [r0, #4] + str r1, [r2] + add r0, r0, #8 + mov r1, #0xFF +SDRAM_DELAY_LOOP: + subs r1, r1, #1 + bgt SDRAM_DELAY_LOOP + cmp r0, r6 + blt SDRAM_REGSET_LOOP + +/* Perform test to check if SDRAM is properly configured */ + mov r0, #0x1000 + mov r1, #0x0 + mov r2, #16384 +SDRAM_WRITE_LOOP: + str r1, [r0] + add r0, r0, #4 + add r1, r1, #1 + cmp r1, r2 + blt SDRAM_WRITE_LOOP + + mov r0, #0x1000 + mov r3, #0x0 +SDRAM_VERIFY_LOOP: + ldr r1, [r0] + add r0, r0, #4 + cmp r1, r3 + bne SDRAM_FAILED + add r3, r3, #1 + cmp r3, r2 + blt SDRAM_VERIFY_LOOP + +SDRAM_PASSED: + /* return from function */ + mov pc, lr + +/* SDRAM configuration failed, try another one */ +SDRAM_FAILED: + /* Increment SDRAM config table index */ + add r7, r7, #1 + b SDRAM_TRY_CONFIG + +SDRAM_CONFIG_TABLE: + .word SDRAM_64MB_CONFIG + .word SDRAM_64MB_CONFIG_END + .word SDRAM_32MB_CONFIG + .word SDRAM_32MB_CONFIG_END + .word 0 + +/* This configuration is for 64MB SDRAM memory */ +SDRAM_64MB_CONFIG: + .word 0x00088E3A + .word MTK_EMI_GENA + .word 0x000000C0 + .word MTK_EMI_GENB + .word 0x18C618C6 + .word MTK_EMI_GENC + .word 0x0001000E + .word MTK_EMI_GEND + .word 0x00332000 + .word MTK_EMI_CONI + .word 0x3CD24431 + .word MTK_EMI_CONJ + .word 0x02000000 + .word MTK_EMI_CONK + .word 0x18007505 + .word MTK_EMI_CONL + .word 0x00002828 + .word MTK_EMI_CONM + .word 0x1500013 + .word MTK_EMI_CONN + .word 0x500013 + .word MTK_EMI_CONN + .word 0x2500013 + .word MTK_EMI_CONN + .word 0x500013 + .word MTK_EMI_CONN + .word 0x4500013 + .word MTK_EMI_CONN + .word 0x500013 + .word MTK_EMI_CONN + .word 0x8500013 + .word MTK_EMI_CONN + .word 0x500013 + .word MTK_EMI_CONN + .word 0x80500013 + .word MTK_EMI_CONN + .word 0x500013 + .word MTK_EMI_CONN +SDRAM_64MB_CONFIG_END: + +/* This configuration is for 32MB SDRAM memory */ +SDRAM_32MB_CONFIG: + .word 0x00088a0a + .word MTK_EMI_GENA + .word 0x00000280 + .word MTK_EMI_GENB + .word 0x52945294 + .word MTK_EMI_GENC + .word 0x0001000E + .word MTK_EMI_GEND + .word 0x02334000 + .word MTK_EMI_CONI + .word 0x16c12212 + .word MTK_EMI_CONJ + .word 0x032d0000 + .word MTK_EMI_CONK + .word 0x1c016605 + .word MTK_EMI_CONL + .word 0x00002828 + .word MTK_EMI_CONM + .word 0x1400013 + .word MTK_EMI_CONN + .word 0x400013 + .word MTK_EMI_CONN + .word 0x2400013 + .word MTK_EMI_CONN + .word 0x400013 + .word MTK_EMI_CONN + .word 0x4400013 + .word MTK_EMI_CONN + .word 0x400013 + .word MTK_EMI_CONN + .word 0x8400013 + .word MTK_EMI_CONN + .word 0x400013 + .word MTK_EMI_CONN + .word 0x80400013 + .word MTK_EMI_CONN + .word 0x400013 + .word MTK_EMI_CONN +SDRAM_32MB_CONFIG_END: diff --git a/nand_spl/board/mtk/sciphone_g2/u-boot.lds b/nand_spl/board/mtk/sciphone_g2/u-boot.lds new file mode 100644 index 000000000..d97122542 --- /dev/null +++ b/nand_spl/board/mtk/sciphone_g2/u-boot.lds @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, 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 + */ + +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + + . = ALIGN(4); + .text : + { + start.o (.text) + *(.text) + } + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(4); + .data : { *(.data) } + + . = ALIGN(4); + .got : { *(.got) } + + . = ALIGN(4); + __bss_start = .; + .bss : { *(.bss) . = ALIGN(4); } + _end = .; +} diff --git a/nand_spl/nand_boot_detect.c b/nand_spl/nand_boot_detect.c new file mode 100644 index 000000000..01f8e70f3 --- /dev/null +++ b/nand_spl/nand_boot_detect.c @@ -0,0 +1,484 @@ +/* + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * Rework by Marcin Mielczarczyk + * + * 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 +#include +#include +#include + +/* + * SPL has no malloc availble and below are definitions of some temporary + * buffers in RAM for general purpose. + */ +#define RAM_BUF1 (CONFIG_SYS_SDRAM_BASE + 0x1000) +#define RAM_BUF2 (RAM_BUF1 + 0x1000) +#define RAM_BUF3 (RAM_BUF2 + 0x1000) + +/* + * Macro which counts zeroes until first set bit. + * This is used to avoid dividing, so no additional library is needed. + * It's important as this file is compiled also in SPL and there is no need + * to link additional library. + */ +#ifdef CONFIG_ARM926EJS + #define COUNT_ZEROES(x) __builtin_ctz(x) +#else + #define COUNT_ZEROES(x) (ffs(x) - 1) +#endif + +nand_info_t nand_info[1]; + +#define CONFIG_SYS_NAND_READ_DELAY \ + { volatile int dummy; int i; for (i=0; i<10000; i++) dummy = i; } + +/* + * NAND command for small page NAND devices (512) + */ +static void nand_command(struct mtd_info *mtd, unsigned int cmd, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; + + if (chip->dev_ready) + while (!chip->dev_ready(mtd)) + ; + else + CONFIG_SYS_NAND_READ_DELAY; + + /* Begin command latch cycle */ + chip->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + /* One more address cycle for devices > 32MiB */ + if (chip->chipsize > (32 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + if (chip->dev_ready) + while (!chip->dev_ready(mtd)) + ; + else + CONFIG_SYS_NAND_READ_DELAY; +} + +/* + * NAND command for large page NAND devices (2k) + */ +static void nand_command_lp(struct mtd_info *mtd, unsigned int cmd, int column, + int page_addr) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->dev_ready) + while (!chip->dev_ready(mtd)) + ; + else + CONFIG_SYS_NAND_READ_DELAY; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + page_addr += mtd->writesize; + cmd = NAND_CMD_READ0; + } + + /* Command latch cycle */ + chip->cmd_ctrl(mtd, cmd & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + if (chip->dev_ready) + while (!chip->dev_ready(mtd)) + ; + else + CONFIG_SYS_NAND_READ_DELAY; +} + +static int nand_is_bad_block(struct mtd_info *mtd, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + + if ((page_addr & (mtd->erasesize - 1)) != 0) + /* + * Page address is not aligned to block address, + * in this case there is no reason to check bad block. + */ + return 0; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page_addr); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return (chip->oob_poi[5] != 0xFF) ? 1: 0; +} + +static int nand_read_page(struct mtd_info *mtd, int page_addr, + unsigned char *dst) +{ + struct nand_chip *chip = mtd->priv; + uint8_t *ecc_calc; + uint8_t *ecc_code; + int i; + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = dst; + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page_addr); + + /* + * No malloc available for now, just use some temporary locations + * in SDRAM. + */ + ecc_calc = (uint8_t *)RAM_BUF2; + ecc_code = (uint8_t *)RAM_BUF3; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* + * No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} + +static void nand_load(struct mtd_info *mtd, unsigned int offset, + unsigned int size, uint8_t *dst) +{ + unsigned int page, end_page, pages_per_block; + + /* + * Offset should be aligned to page and block size, otherwise + * there will be no chance to detect bad block of start offset. + */ + page = offset >> COUNT_ZEROES(mtd->writesize); + end_page = page + (size >> COUNT_ZEROES(mtd->writesize)); + /* If size is not aligned to page then read one more page */ + if (size & (mtd->writesize - 1)) + end_page++; + pages_per_block = mtd->erasesize >> COUNT_ZEROES(mtd->writesize); + + while (page < end_page) { + /* If this read is in new block, check for babd block*/ + if ((page & (pages_per_block - 1)) == 0) { + if (nand_is_bad_block(mtd, page)) { + /* + * Bad block is detected. + * Jump to next block and continue code loading. + * In this case not full image will be loaded. + */ + page += pages_per_block; + continue; + } + } + + nand_read_page(mtd, page, dst); + page++; + dst += mtd->writesize; + } +} + +#if defined(CONFIG_ARM) +void board_init_f (ulong bootflag) +{ + relocate_code (CONFIG_SYS_TEXT_BASE - TOTAL_MALLOC_LEN, NULL, + CONFIG_SYS_TEXT_BASE); +} +#endif + +/* + * Get the flash and manufacturer id and lookup if the type is supported + */ +static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + struct nand_chip *chip, + int busw, int *maf_id) +{ + const struct nand_flash_dev *type = NULL; + int i, dev_id, maf_idx; + int tmp_id, tmp_manf; + + /* Select the device */ + chip->select_chip(mtd, 0); + + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + *maf_id = chip->read_byte(mtd); + dev_id = chip->read_byte(mtd); + + /* Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + tmp_manf = chip->read_byte(mtd); + tmp_id = chip->read_byte(mtd); + + if (tmp_manf != *maf_id || tmp_id != dev_id) + return ERR_PTR(-ENODEV); + + /* Lookup the flash id */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (dev_id == nand_flash_ids[i].id) { + type = &nand_flash_ids[i]; + break; + } + } + + if (!type) + return ERR_PTR(-ENODEV); + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = (uint64_t)type->chipsize << 20; + + /* Newer devices have all the information in additional id bytes */ + if (!type->pagesize) { + int extid; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = chip->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = chip->read_byte(mtd); + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* + * Old devices have chip data hardcoded in the device id table + */ + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + busw = type->options & NAND_BUSWIDTH_16; + } + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } + + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct ! + */ + if (busw != (chip->options & NAND_BUSWIDTH_16)) + return ERR_PTR(-EINVAL); + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31; + + /* Set the bad block position */ + chip->badblockpos = mtd->writesize > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= type->options & NAND_CHIPOPTIONS_MSK; + + /* + * Set chip as a default. Board drivers can override it, if necessary + */ + chip->options |= NAND_NO_AUTOINCR; + + /* Check if chip is a not a samsung device. Do not clear the + * options for chips which are not having an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + + /* Do not replace user supplied command function ! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + return type; +} + +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + const struct nand_flash_dev *table) +{ + int busw, nand_maf_id; + struct nand_chip *chip = mtd->priv; + const struct nand_flash_dev *type; + + /* Get buswidth to select the correct functions */ + busw = chip->options & NAND_BUSWIDTH_16; + + /* Read the flash type */ + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + + if (IS_ERR(type)) { + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + /* Store the number of chips and calc total size for mtd */ + chip->numchips = 1; + mtd->size = chip->chipsize; + + return 0; +} + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + struct nand_chip chip; + nand_info_t *mtd = nand_info; + __attribute__((noreturn)) void (*uboot)(void); + + /* + * Init board specific nand support + */ + mtd->priv = &chip; + chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; + chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; + chip.dev_ready = NULL; + chip.cmdfunc = nand_command; + chip.options = 0; + + board_nand_init(&chip); + + chip.ecc.steps = mtd->writesize >> COUNT_ZEROES(chip.ecc.size); + chip.oob_poi = (uint8_t *)RAM_BUF1; + + if (chip.select_chip) + chip.select_chip(mtd, 0); + + /* + * Load U-Boot image from NAND into RAM + */ + nand_load(mtd, CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, + (uint8_t *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_load(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (uint8_t *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_load(mtd, CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (uint8_t *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + + if (chip.select_chip) + chip.select_chip(mtd, -1); + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +}