diff options
author | Justin Waters <justin.waters@timesys.com> | 2012-04-17 13:43:17 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-04-17 13:43:17 -0400 |
commit | 4f60d7e7027af17ceffc1a38e6dbe4e3e95c71ec (patch) | |
tree | dd33f3760e08226d5c05036d664d2d68fb3765dc | |
parent | b1af6f532e0d348b153d5c148369229d24af361a (diff) |
LogicPD Support for OMAP3/DM3/AM3 boards
From Logic BSP-2.0-5-01
164 files changed, 27564 insertions, 753 deletions
@@ -88,7 +88,7 @@ endif ifdef O ifeq ("$(origin O)", "command line") -BUILD_DIR := $(O) +export BUILD_DIR := $(O) endif endif @@ -99,7 +99,7 @@ saved-output := $(BUILD_DIR) $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}) # Verify if it was successful. -BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) +export BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) $(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist)) endif # ifneq ($(BUILD_DIR),) @@ -225,7 +225,7 @@ LIBS += arch/arm/cpu/ixp/npe/libnpe.o endif LIBS += arch/$(ARCH)/lib/lib$(ARCH).o LIBS += fs/cramfs/libcramfs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o \ - fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/yaffs2/libyaffs2.o \ + fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/yaffs2-new/libyaffs2-new.o \ fs/ubifs/libubifs.o LIBS += net/libnet.o LIBS += disk/libdisk.o @@ -356,6 +356,10 @@ ifeq ($(CONFIG_MMC_U_BOOT),y) ALL += $(obj)mmc_spl/u-boot-mmc-spl.bin endif +ifeq ($(CONFIG_TOOL_SIGNGP),y) +ALL += $(obj)u-boot.bin.ift +endif + all: $(ALL) $(obj)u-boot.hex: $(obj)u-boot @@ -368,6 +372,9 @@ $(obj)u-boot.bin: $(obj)u-boot $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ $(BOARD_SIZE_CHECK) +$(obj)u-boot.bin.ift: $(obj)u-boot.bin + tools/sign-nand-image u-boot.bin $(CONFIG_SYS_TEXT_BASE) + $(obj)u-boot.ldr: $(obj)u-boot $(CREATE_LDR_ENV) $(LDR) -T $(CONFIG_BFIN_CPU) -c $@ $< $(LDR_FLAGS) @@ -539,8 +546,8 @@ $(VERSION_FILE): @( localvers='$(shell $(TOPDIR)/tools/setlocalversion $(TOPDIR))' ; \ printf '#define PLAIN_VERSION "%s%s"\n' \ "$(U_BOOT_VERSION)" "$${localvers}" ; \ - printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' \ - "$(U_BOOT_VERSION)" "$${localvers}" ; \ + printf '#define U_BOOT_VERSION "U-Boot %s%s %s"\n' \ + "$(U_BOOT_VERSION)" "$${localvers}" "$(BSP_RELEASE_LEVEL)"; \ ) > $@.tmp @( printf '#define CC_VERSION_STRING "%s"\n' \ '$(shell $(CC) --version | head -n 1)' )>> $@.tmp @@ -565,9 +572,7 @@ include/license.h: tools/bin2header COPYING ######################################################################### unconfig: - @rm -f $(obj)include/config.h $(obj)include/config.mk \ - $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \ - $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep + @rm -f $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp %_config:: unconfig @$(MKCONFIG) -A $(@:_config=) @@ -1120,6 +1125,10 @@ clobber: clean @[ ! -d $(obj)nand_spl ] || find $(obj)nand_spl -name "*" -type l -print | xargs rm -f @[ ! -d $(obj)onenand_ipl ] || find $(obj)onenand_ipl -name "*" -type l -print | xargs rm -f @[ ! -d $(obj)mmc_spl ] || find $(obj)mmc_spl -name "*" -type l -print | xargs rm -f + @rm -f $(obj)include/config.h $(obj)include/config.mk \ + $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \ + $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep + ifeq ($(OBJTREE),$(SRCTREE)) mrproper \ @@ -2408,6 +2408,12 @@ Configuration Settings: Enables allocating and saving a kernel copy of the bd_info in space between "bootm_low" and "bootm_low" + BOOTMAPSZ. +- CONFIG_SYS_FLASH_PRESENCE + If defined is checked and if zero then board_init_r + skips calling flash_init. Used in cases where board + has optional NOR flash (and when not present does not + setup chip select to allow access). + - CONFIG_SYS_MAX_FLASH_BANKS: Max number of Flash memory banks diff --git a/arch/arm/cpu/armv7/omap-common/timer.c b/arch/arm/cpu/armv7/omap-common/timer.c index 9beebb1e745..c4f245492fe 100644 --- a/arch/arm/cpu/armv7/omap-common/timer.c +++ b/arch/arm/cpu/armv7/omap-common/timer.c @@ -33,18 +33,21 @@ */ #include <common.h> +#include <errno.h> #include <asm/io.h> DECLARE_GLOBAL_DATA_PTR; static struct gptimer *timer_base = (struct gptimer *)CONFIG_SYS_TIMERBASE; + /* * Nothing really to do with interrupts, just starts up a counter. */ #define TIMER_CLOCK (V_SCLK / (2 << CONFIG_SYS_PTV)) -#define TIMER_LOAD_VAL 0xffffffff +#define TIMER_OVERFLOW_VAL 0xffffffff +#define TIMER_LOAD_VAL 0 int timer_init(void) { @@ -86,7 +89,7 @@ void __udelay(unsigned long usec) while (tmo > 0) { now = readl(&timer_base->tcrr); if (last > now) /* count up timer overflow */ - tmo -= TIMER_LOAD_VAL - last + now; + tmo -= TIMER_OVERFLOW_VAL - last + now; else tmo -= now - last; last = now; @@ -132,3 +135,50 @@ ulong get_tbclk(void) { return CONFIG_SYS_HZ; } + +int init_gpt_timer(u32 timer, u32 value, u32 range) +{ + struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + struct gptimer *timer_base; + u32 reg; + + switch(timer) { + case 10: + writel(readl(&prcm_base->clksel_core) | (1 << 6), &prcm_base->clksel_core); + writel(readl(&prcm_base->fclken1_core) | (1 << 11), &prcm_base->fclken1_core); + writel(readl(&prcm_base->iclken1_core) | (1 << 11), &prcm_base->iclken1_core); + timer_base = (struct gptimer *)OMAP34XX_GPT10; + break; + default: + return -ENODEV; + } + + /* dm_timer_set_load */ + reg = readl(&timer_base->tclr); + reg |= TCLR_AR; + writel(reg, &timer_base->tclr); + writel(0xffff0000, &timer_base->tldr); + writel(0, &timer_base->ttgr); + + /* dm_set_pwm */ + reg = readl(&timer_base->tclr); + reg &= ~(TCLR_GPO_CFG | TCLR_SCPWM | TCLR_PT | (0x03 << 10)); + reg |= TCLR_PT; + reg |= (0x2 << 10); /* OVERFLOW_AND_COMPARE */ + writel(reg, &timer_base->tclr); + + /* dm_set_match */ + reg = readl(&timer_base->tclr); + reg |= TCLR_CE; + writel(reg, &timer_base->tclr); + writel(0xffff0000 | (((value * 0xff) / range) << 8), &timer_base->tmar); + + /* dm_timer_start */ + reg = readl(&timer_base->tclr); + if (!(reg & TCLR_ST)) { + reg |= TCLR_ST; + writel(reg | TCLR_ST, &timer_base->tclr); + } + + return 0; +} diff --git a/arch/arm/cpu/armv7/omap3/Makefile b/arch/arm/cpu/armv7/omap3/Makefile index 7164d505b9c..1a540b545ab 100644 --- a/arch/arm/cpu/armv7/omap3/Makefile +++ b/arch/arm/cpu/armv7/omap3/Makefile @@ -37,7 +37,7 @@ COBJS += sys_info.o COBJS-$(CONFIG_EMIF4) += emif4.o COBJS-$(CONFIG_SDRC) += sdrc.o -SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS) $(COBJS-y) $(SOBJS)) all: $(obj).depend $(LIB) diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index 6c2a132b63b..18133d876d0 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -37,6 +37,7 @@ #include <asm/arch/sys_proto.h> #include <asm/arch/mem.h> #include <asm/cache.h> +#include <nand.h> extern omap3_sysinfo sysinfo; @@ -249,12 +250,48 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { + struct mtd_info *mtd; + struct nand_chip *nand; + + /* the following commands operat on the current device */ + if (nand_curr_device > 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("\nno NAND devices available\n"); + return 1; + } + mtd = &nand_info[nand_curr_device]; + nand = mtd->priv; + + if (argc == 1) { + switch (nand->ecc.mode) { + case NAND_ECC_SOFT: + printf("Software ECC\n"); + break; + case NAND_ECC_HW: + printf("Hardware ECC\n"); + break; + case NAND_ECC_CHIP: + printf("Internal to NAND Hardware ECC\n"); + break; + case NAND_ECC_SOFT_BCH: + printf("Software BCH ECC\n"); + break; + default: + printf("Unknown ECC method %d!\n", nand->ecc.mode); + return -1; + } + return 0; + } if (argc != 2) goto usage; - if (strncmp(argv[1], "hw", 2) == 0) - omap_nand_switch_ecc(1); - else if (strncmp(argv[1], "sw", 2) == 0) - omap_nand_switch_ecc(0); + if (strcmp(argv[1], "hw") == 0) + omap_nand_switch_ecc(OMAP_ECC_HW); + else if (strcmp(argv[1], "sw") == 0) + omap_nand_switch_ecc(OMAP_ECC_SOFT); + else if (strcmp(argv[1], "chip") == 0) + omap_nand_switch_ecc(OMAP_ECC_CHIP); + else if (strcmp(argv[1], "bch") == 0) + omap_nand_switch_ecc(OMAP_ECC_SOFT_BCH); else goto usage; @@ -268,7 +305,7 @@ usage: U_BOOT_CMD( nandecc, 2, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "[hw/sw/chip] - Switch between NAND hardware (hw), software (sw),\n in-chip (chip) ecc algorithm, or BCH (bch) ecc algorithm" ); #endif /* CONFIG_NAND_OMAP_GPMC */ diff --git a/arch/arm/cpu/armv7/omap3/sdrc.c b/arch/arm/cpu/armv7/omap3/sdrc.c index 2a7970b4d00..f592388e869 100644 --- a/arch/arm/cpu/armv7/omap3/sdrc.c +++ b/arch/arm/cpu/armv7/omap3/sdrc.c @@ -215,3 +215,44 @@ void mem_init(void) /* only init up first bank here */ do_sdrc_init(CS0, EARLY_INIT); } + +#ifdef CONFIG_CMD_SDRC_CONFIG +static void dump_sdrc_config(int cs) +{ + struct sdrc_actim *sdrc_actim_base; + + if(cs) + sdrc_actim_base = (struct sdrc_actim *)SDRC_ACTIM_CTRL1_BASE; + else + sdrc_actim_base = (struct sdrc_actim *)SDRC_ACTIM_CTRL0_BASE; + + printf("cs%d: mcfg %08x mr %08x rfr_ctrl %08x emr2 %08x\n", + cs, readl(&sdrc_base->cs[cs].mcfg), + readl(&sdrc_base->cs[cs].mr), + readl(&sdrc_base->cs[cs].rfr_ctrl), + readl(&sdrc_base->cs[cs].emr2)); + printf("cs%d: ctrla %08x ctrlb %08x\n", + cs, + readl(&sdrc_actim_base->ctrla), + readl(&sdrc_actim_base->ctrlb)); +} + +int do_dump_sdrc_config(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + printf("sysconfig %08x sharing %08x dlla_ctrl %08x", + readl(&sdrc_base->sysconfig), + readl(&sdrc_base->sharing), + readl(&sdrc_base->dlla_ctrl)); + printf(" cs_cfg %08x\n", + readl(&sdrc_base->cs_cfg)); + + dump_sdrc_config(0); + dump_sdrc_config(1); + return 0; +} + +U_BOOT_CMD(sdrc_config, 1, 1, do_dump_sdrc_config, + "sdrc_config - dump SDRC registers", + "" +); +#endif diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S index d91ae12cb99..35e1031e7ee 100644 --- a/arch/arm/cpu/armv7/start.S +++ b/arch/arm/cpu/armv7/start.S @@ -97,6 +97,16 @@ _bss_end_ofs: _end_ofs: .word _end - _start +#ifdef CONFIG_GDB_SECTION_STARTS +.globl _data_start_ofs +_data_start_ofs: + .word __data_start - _start + +.globl _rodata_start_ofs +_rodata_start_ofs: + .word __rodata_start - _start +#endif + #ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START diff --git a/arch/arm/cpu/armv7/u-boot.lds b/arch/arm/cpu/armv7/u-boot.lds index dbae54d4f8a..31abfbcb3c3 100644 --- a/arch/arm/cpu/armv7/u-boot.lds +++ b/arch/arm/cpu/armv7/u-boot.lds @@ -39,10 +39,14 @@ SECTIONS } . = ALIGN(4); - .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + .rodata : { + __rodata_start = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } . = ALIGN(4); .data : { + __data_start = .; *(.data) } diff --git a/arch/arm/include/asm/arch-omap3/cpu.h b/arch/arm/include/asm/arch-omap3/cpu.h index e944de7192c..def013768c2 100644 --- a/arch/arm/include/asm/arch-omap3/cpu.h +++ b/arch/arm/include/asm/arch-omap3/cpu.h @@ -454,9 +454,13 @@ struct prm { #define RESETDONE (0x1 << 0) -#define TCLR_ST (0x1 << 0) -#define TCLR_AR (0x1 << 1) +#define TCLR_GPO_CFG (0x1 << 14) +#define TCLR_PT (0x1 << 12) +#define TCLR_SCPWM (0x1 << 7) +#define TCLR_CE (0x1 << 6) #define TCLR_PRE (0x1 << 5) +#define TCLR_AR (0x1 << 1) +#define TCLR_ST (0x1 << 0) /* SMX-APE */ #define PM_RT_APE_BASE_ADDR_ARM (SMX_APE_BASE + 0x10000) diff --git a/arch/arm/include/asm/arch-omap3/dss.h b/arch/arm/include/asm/arch-omap3/dss.h new file mode 100644 index 00000000000..4a3db1abb4b --- /dev/null +++ b/arch/arm/include/asm/arch-omap3/dss.h @@ -0,0 +1,213 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Syed Mohammed Khasim <khasim <at> ti.com> + * + * Referred to Linux Kernel DSS driver files for OMAP3 by + * Tomi Valkeinen from drivers/video/omap2/dss/ + * + * 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's version 2 and any + * later version the License. + * + * 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 DSS_H +#define DSS_H + +/* + * DSS Base Registers + */ +#define OMAP3_DSS_BASE 0x48050040 +#define OMAP3_DISPC_BASE 0x48050440 +#define OMAP3_VENC_BASE 0x48050C00 + +/* DSS Registers */ +struct dss_regs { + u32 control; /* 0x40 */ + u32 sdi_control; /* 0x44 */ + u32 pll_control; /* 0x48 */ +}; + +/* DISPC Registers */ +struct dispc_regs { + u32 control; /* 0x40 */ + u32 config; /* 0x44 */ + u32 reserve_2; /* 0x48 */ + u32 default_color0; /* 0x4C */ + u32 default_color1; /* 0x50 */ + u32 trans_color0; /* 0x54 */ + u32 trans_color1; /* 0x58 */ + u32 line_status; /* 0x5C */ + u32 line_number; /* 0x60 */ + u32 timing_h; /* 0x64 */ + u32 timing_v; /* 0x68 */ + u32 pol_freq; /* 0x6C */ + u32 divisor; /* 0x70 */ + u32 global_alpha; /* 0x74 */ + u32 size_dig; /* 0x78 */ + u32 size_lcd; /* 0x7C */ + u32 gfx_ba0; /* 0x80 */ + u32 gfx_ba1; /* 0x84 */ + u32 gfx_position; /* 0x88 */ + u32 gfx_size; /* 0x8C */ + u32 padding1; /* 0x90 */ + u32 padding2; /* 0x94 */ + u32 padding3; /* 0x98 */ + u32 padding4; /* 0x9C */ + u32 gfx_attributes; /* 0xA0 */ + u32 gfx_fifo_threashold; /* 0xA4 */ + u32 gfx_fifo_size_status; /* 0xA8 */ + u32 gfx_row_inc; /* 0xAC */ + u32 gfx_pixel_inc; /* 0xB0 */ + u32 gfx_window_skip; /* 0xB4 */ + u32 gfx_table_ba; /* 0xB8 */ +}; + +/* VENC Registers */ +struct venc_regs { + u32 rev_id; /* 0x00 */ + u32 status; /* 0x04 */ + u32 f_control; /* 0x08 */ + u32 reserve_1; /* 0x0C */ + u32 vidout_ctrl; /* 0x10 */ + u32 sync_ctrl; /* 0x14 */ + u32 reserve_2; /* 0x18 */ + u32 llen; /* 0x1C */ + u32 flens; /* 0x20 */ + u32 hfltr_ctrl; /* 0x24 */ + u32 cc_carr_wss_carr; /* 0x28 */ + u32 c_phase; /* 0x2C */ + u32 gain_u; /* 0x30 */ + u32 gain_v; /* 0x34 */ + u32 gain_y; /* 0x38 */ + u32 black_level; /* 0x3C */ + u32 blank_level; /* 0x40 */ + u32 x_color; /* 0x44 */ + u32 m_control; /* 0x48 */ + u32 bstamp_wss_data; /* 0x4C */ + u32 s_carr; /* 0x50 */ + u32 line21; /* 0x54 */ + u32 ln_sel; /* 0x58 */ + u32 l21__wc_ctl; /* 0x5C */ + u32 htrigger_vtrigger; /* 0x60 */ + u32 savid__eavid; /* 0x64 */ + u32 flen__fal; /* 0x68 */ + u32 lal__phase_reset; /* 0x6C */ + u32 hs_int_start_stop_x; /* 0x70 */ + u32 hs_ext_start_stop_x; /* 0x74 */ + u32 vs_int_start_x; /* 0x78 */ + u32 vs_int_stop_x__vs_int_start_y; /* 0x7C */ + u32 vs_int_stop_y__vs_ext_start_x; /* 0x80 */ + u32 vs_ext_stop_x__vs_ext_start_y; /* 0x84 */ + u32 vs_ext_stop_y; /* 0x88 */ + u32 reserve_3; /* 0x8C */ + u32 avid_start_stop_x; /* 0x90 */ + u32 avid_start_stop_y; /* 0x94 */ + u32 reserve_4; /* 0x98 */ + u32 reserve_5; /* 0x9C */ + u32 fid_int_start_x__fid_int_start_y; /* 0xA0 */ + u32 fid_int_offset_y__fid_ext_start_x; /* 0xA4 */ + u32 fid_ext_start_y__fid_ext_offset_y; /* 0xA8 */ + u32 reserve_6; /* 0xAC */ + u32 tvdetgp_int_start_stop_x; /* 0xB0 */ + u32 tvdetgp_int_start_stop_y; /* 0xB4 */ + u32 gen_ctrl; /* 0xB8 */ + u32 reserve_7; /* 0xBC */ + u32 reserve_8; /* 0xC0 */ + u32 output_control; /* 0xC4 */ + u32 dac_b__dac_c; /* 0xC8 */ + u32 height_width; /* 0xCC */ +}; + +enum omap_panel_config { + OMAP_DSS_LCD_IVS = 1<<0, + OMAP_DSS_LCD_IHS = 1<<1, + OMAP_DSS_LCD_IPC = 1<<2, + OMAP_DSS_LCD_IEO = 1<<3, + OMAP_DSS_LCD_RF = 1<<4, + OMAP_DSS_LCD_ONOFF = 1<<5, + + OMAP_DSS_LCD_TFT = 1<<20, +}; + +/* Few Register Offsets */ +#define FRAME_MODE_SHIFT 1 +#define TFTSTN_SHIFT 3 +#define DATALINES_SHIFT 8 + +/* Enabling Display controller */ +#define LCD_ENABLE 1 +#define DIG_ENABLE (1 << 1) +#define GO_LCD (1 << 5) +#define GO_DIG (1 << 6) +#define GP_OUT0 (1 << 15) +#define GP_OUT1 (1 << 16) + +#define DISPC_ENABLE (LCD_ENABLE | \ + DIG_ENABLE | \ + GO_LCD | \ + GO_DIG | \ + GP_OUT0| \ + GP_OUT1) + +/* Configure VENC DSS Params */ +#define VENC_CLK_ENABLE (1 << 3) +#define DAC_DEMEN (1 << 4) +#define DAC_POWERDN (1 << 5) +#define VENC_OUT_SEL (1 << 6) +#define DIG_LPP_SHIFT 16 +#define VENC_DSS_CONFIG (VENC_CLK_ENABLE | \ + DAC_DEMEN | \ + DAC_POWERDN | \ + VENC_OUT_SEL) +/* + * Panel Configuration + */ +struct panel_config { + u32 timing_h; + u32 timing_v; + u32 pol_freq; +#if 1 + u32 pixel_clock; +#else + u32 divisor; +#endif + u32 lcd_size; + u32 panel_type; + u32 data_lines; + u32 load_mode; + u32 panel_color; +}; + +/* + * Generic DSS Functions + */ +void omap3_dss_venc_config(const struct venc_regs *venc_cfg, + u32 height, u32 width); +void omap3_dss_panel_config(const struct panel_config *panel_cfg); +void omap3_dss_enable(void); + +#define CONFIG_DSS_DUMP +#ifdef CONFIG_DSS_DUMP +extern void omap3_dss_dump(void); +#endif + +extern int omap3_dss_calc_divisor(int is_tft, unsigned int req_pck, + unsigned int *dispc_divisor, + unsigned int *result_fck_div); + +#endif /* DSS_H */ diff --git a/arch/arm/include/asm/arch-omap3/mmc_host_def.h b/arch/arm/include/asm/arch-omap3/mmc_host_def.h index ba1c2ffc06b..5f0342354cc 100644 --- a/arch/arm/include/asm/arch-omap3/mmc_host_def.h +++ b/arch/arm/include/asm/arch-omap3/mmc_host_def.h @@ -188,6 +188,7 @@ typedef struct { unsigned int mode; unsigned int size; unsigned int RCA; + unsigned int max_freq; /* max frequency of MMC device (if !0) */ } mmc_card_data; #define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK) #define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE) diff --git a/arch/arm/include/asm/arch-omap3/omap3.h b/arch/arm/include/asm/arch-omap3/omap3.h index cc2b5415c12..8c91b9aac9f 100644 --- a/arch/arm/include/asm/arch-omap3/omap3.h +++ b/arch/arm/include/asm/arch-omap3/omap3.h @@ -69,6 +69,24 @@ struct control_prog_io { #define OMAP34XX_UART2 (OMAP34XX_L4_IO_BASE + 0x6c000) #define OMAP34XX_UART3 (OMAP34XX_L4_PER + 0x20000) +/* OTG */ +#define OMAP34XX_OTG_BASE (OMAP34XX_L4_IO_BASE + 0xab000) +#define OTG_SYSCONFIG (OMAP34XX_OTG_BASE + 0x404) +#define OTG_SYSCONFIG_MIDLEMODE_SHIFT 12 +#define OTG_SYSCONFIG_MIDLEMODE_MASK (0x3 << OTG_SYSCONFIG_MIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_MIDLEMODE_FORCE_STDBY (0x0 << OTG_SYSCONFIG_MIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_MIDLEMODE_NO_STDBY (0x1 << OTG_SYSCONFIG_MIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_MIDLEMODE_SMART_STDBY (0x2 << OTG_SYSCONFIG_MIDLEMODE_SHIFT) + +#define OTG_SYSCONFIG_SIDLEMODE_SHIFT 3 +#define OTG_SYSCONFIG_SIDLEMODE_MASK (0x3 << OTG_SYSCONFIG_SIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_SIDLEMODE_FORCE_IDLE (0x0 << OTG_SYSCONFIG_SIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_SIDLEMODE_NO_IDLE (0x1 << OTG_SYSCONFIG_SIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_SIDLEMODE_SMART_IDLE (0x2 << OTG_SYSCONFIG_SIDLEMODE_SHIFT) +#define OTG_SYSCONFIG_ENABLEWKUP (1 << 0x2) +#define OTG_SYSCONFIG_SOFTRESET (1 << 0x1) +#define OTG_SYSCONFIG_AUTOIDLE (1 << 0x0) + /* General Purpose Timers */ #define OMAP34XX_GPT1 0x48318000 #define OMAP34XX_GPT2 0x49032000 @@ -159,6 +177,8 @@ struct gpio { #define SRAM_VECT_CODE (SRAM_OFFSET0 | SRAM_OFFSET1 | \ SRAM_OFFSET2) +#define SRAM_BASE (SRAM_OFFSET0 | SRAM_OFFSET1) + #define LOW_LEVEL_SRAM_STACK 0x4020FFFC #define DEBUG_LED1 149 /* gpio */ @@ -195,6 +215,8 @@ struct gpio { #define CPU_3XX_ID_SHIFT 28 +#define TYPE_READTYPE 0x20000000 /* Readtype, 1 == sync in GPMC */ + #define WIDTH_8BIT 0x0000 #define WIDTH_16BIT 0x1000 /* bit pos for 16 bit in gpmc */ diff --git a/arch/arm/include/asm/arch-omap3/omap_gpmc.h b/arch/arm/include/asm/arch-omap3/omap_gpmc.h index bd22bce837f..c86448baa81 100644 --- a/arch/arm/include/asm/arch-omap3/omap_gpmc.h +++ b/arch/arm/include/asm/arch-omap3/omap_gpmc.h @@ -58,6 +58,25 @@ } #endif +/* Micron MT29F4G16ABBDA internal-to-NAND ECC layout */ +#define GPMC_NAND_CHIP_ECC_LAYOUT {\ + .eccbytes = 32,\ + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15, \ + 24, 25, 26, 27, 28, 19, 30, 31, \ + 40, 41, 42, 43, 44, 45, 46, 47, \ + 56, 57, 58, 59, 60, 61, 62, 63}, \ + .oobfree = {\ + {.offset = 4,\ + .length = 4 },\ + {.offset = 20,\ + .length = 4 },\ + {.offset = 36,\ + .length = 4 },\ + {.offset = 52,\ + .length = 4 },\ + } \ +}; + /* Small Page x8 NAND device Layout */ #ifdef GPMC_NAND_ECC_SP_x8_LAYOUT #define GPMC_NAND_HW_ECC_LAYOUT {\ diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h index 4a28ba1c4ae..cb45504c228 100644 --- a/arch/arm/include/asm/arch-omap3/sys_proto.h +++ b/arch/arm/include/asm/arch-omap3/sys_proto.h @@ -63,7 +63,16 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); +enum omap_nand_ecc_mode { + OMAP_ECC_SOFT = 1, + OMAP_ECC_HW, + OMAP_ECC_CHIP, + OMAP_ECC_SOFT_BCH, +}; +extern void omap_nand_switch_ecc(enum omap_nand_ecc_mode mode); +extern enum omap_nand_ecc_mode omap_nand_current_ecc_method(void); +int omap_nand_chip_has_ecc(void); + void power_init_r(void); void dieid_num_r(void); diff --git a/arch/arm/include/asm/arch-omap3/timer.h b/arch/arm/include/asm/arch-omap3/timer.h new file mode 100644 index 00000000000..c3dd72ce084 --- /dev/null +++ b/arch/arm/include/asm/arch-omap3/timer.h @@ -0,0 +1,25 @@ +/* + * (C) Copyright 2011 + * Logic Product Development, Inc. + * Peter Barada <peter.barada@logicpd.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 + */ + +extern int init_gpt_timer(u32 timer, u32 value, u32 range); diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index ef9959e6eef..3fa72af0ff9 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -64,6 +64,17 @@ typedef struct global_data { #ifdef CONFIG_IXP425 unsigned long timestamp; #endif +#ifdef CONFIG_GDB_SECTION_STARTS + /* Section start information. Used with GDB command: + * add-symbol-file u-boot $relocaddr \ + * -s .data $data_start \ + * -s .bss $bss_start \ + * -s .rodata $rodata_start + */ + unsigned long bss_start; + unsigned long data_start; + unsigned long rodata_start; +#endif unsigned long relocaddr; /* Start address of U-Boot in RAM */ phys_size_t ram_size; /* RAM size */ unsigned long mon_len; /* monitor len */ diff --git a/arch/arm/include/asm/u-boot-arm.h b/arch/arm/include/asm/u-boot-arm.h index 3904027016e..49a5aa50b4c 100644 --- a/arch/arm/include/asm/u-boot-arm.h +++ b/arch/arm/include/asm/u-boot-arm.h @@ -33,6 +33,10 @@ extern ulong _bss_start_ofs; /* BSS start relative to _start */ extern ulong _bss_end_ofs; /* BSS end relative to _start */ extern ulong _end_ofs; /* end of image relative to _start */ +#ifdef CONFIG_GDB_SECTION_STARTS +extern ulong _data_start_ofs; /* .data start relative to _start */ +extern ulong _rodata_start_ofs; /* .rodata start relative to _start */ +#endif extern ulong IRQ_STACK_START; /* top of IRQ stack */ extern ulong FIQ_STACK_START; /* top of FIQ stack */ extern ulong _TEXT_BASE; /* code start */ diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c index 169dfebcf15..2e223199471 100644 --- a/arch/arm/lib/board.c +++ b/arch/arm/lib/board.c @@ -47,6 +47,7 @@ #include <net.h> #include <serial.h> #include <nand.h> +#include <asm/arch/sys_proto.h> #include <onenand_uboot.h> #include <mmc.h> @@ -413,6 +414,11 @@ void board_init_f (ulong bootflag) gd->relocaddr = addr; gd->start_addr_sp = addr_sp; gd->reloc_off = addr - _TEXT_BASE; +#ifdef CONFIG_GDB_SECTION_STARTS + gd->bss_start = addr + _bss_start_ofs; + gd->data_start = addr + _data_start_ofs; + gd->rodata_start = addr + _rodata_start_ofs; +#endif debug ("relocation Offset is: %08lx\n", gd->reloc_off); memcpy (id, (void *)gd, sizeof (gd_t)); @@ -471,6 +477,10 @@ void board_init_r (gd_t *id, ulong dest_addr) mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN); #if !defined(CONFIG_SYS_NO_FLASH) +#ifdef CONFIG_SYS_FLASH_PRESENCE + if (!CONFIG_SYS_FLASH_PRESENCE) + goto skip_flash; +#endif puts ("Flash: "); if ((flash_size = flash_init ()) > 0) { @@ -495,6 +505,10 @@ void board_init_r (gd_t *id, ulong dest_addr) puts (failed); hang (); } +#ifdef CONFIG_SYS_FLASH_PRESENCE +skip_flash: + ; +#endif #endif #if defined(CONFIG_CMD_NAND) @@ -506,6 +520,10 @@ void board_init_r (gd_t *id, ulong dest_addr) onenand_init(); #endif +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_setup_default_ecc_method(); /* Set the default ECC method */ +#endif + #ifdef CONFIG_GENERIC_MMC puts("MMC: "); mmc_initialize(bd); diff --git a/board/ti/logic/Makefile b/board/ti/logic/Makefile new file mode 100644 index 00000000000..67c7fd3f926 --- /dev/null +++ b/board/ti/logic/Makefile @@ -0,0 +1,50 @@ +# +# (C) Copyright 2000, 2001, 2002 +# Wolfgang Denk, DENX Software Engineering, wd@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 +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(BOARD).o + +PROD_ID_OBJS := prod-id/query.o prod-id/startup.o prod-id/crc-15.o \ + prod-id/extract.o prod-id/type.o prod-id/size.o + +COBJS := logic.o logic-data.o logic-at88.o logic-at24.o logic-i2c.o logic-gpio.o logic-display.o logic-product-id.o $(PROD_ID_OBJS) + +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +clean: + rm -f $(OBJS) + +distclean: clean + rm -f $(LIB) core *.bak $(obj).depend + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend diff --git a/board/ti/logic/config.mk b/board/ti/logic/config.mk new file mode 100644 index 00000000000..897b252b07d --- /dev/null +++ b/board/ti/logic/config.mk @@ -0,0 +1,33 @@ +# +# (C) Copyright 2006 - 2008 +# Texas Instruments, <www.ti.com> +# +# EVM uses OMAP3 (ARM-CortexA8) cpu +# see http://www.ti.com/ for more information on Texas Instruments +# +# 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 +# +# Physical Address: +# 8000'0000 (bank0) +# A000/0000 (bank1) +# Linux-Kernel is expected to be at 8000'8000, entry 8000'8000 +# (mem base + reserved) + +# For use with external or internal boots. +CONFIG_SYS_TEXT_BASE = 0x80400000 diff --git a/board/ti/logic/logic-at24.c b/board/ti/logic/logic-at24.c new file mode 100644 index 00000000000..e1f03db2ce1 --- /dev/null +++ b/board/ti/logic/logic-at24.c @@ -0,0 +1,149 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <command.h> +#include <asm/arch/cpu.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/clocks.h> +#include <asm/arch/mem.h> +#include <i2c.h> +#include <asm/mach-types.h> +#include "logic-i2c.h" + +#define AT24_CHIP_ID 0xA0 +#define AT24_ACCESS_WRITE 0 +#define AT24_ACCESS_READ 1 + +#define AT24_DEVICE_ADDRESS(rw) (AT24_CHIP_ID | (rw & 1)) +#define AT24_SIZE_MASK 0x7FF /* 2048 byte AT24C16 */ +int at24_wakeup(void) +{ + int i; + gpio_i2c_init(100000); + + /* Need 9 clocks to wake up AT24 chips */ + gpio_i2c_config_pin(GPIO_I2C_SCLK, GPIO_I2C_OUTPUT); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + for (i=0; i<10; ++i) { + udelay(100); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + udelay(100); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + } + + /* We can't identify AT24 chips... */ + + return 0; + +} + +int at24_shutdown(void) +{ + /* Restore GPIO_OE registers back to reset state (All input) */ + gpio_i2c_config_pin(GPIO_I2C_SDATA, GPIO_I2C_INPUT); + gpio_i2c_config_pin(GPIO_I2C_SCLK, GPIO_I2C_INPUT); + /* Restore pins back to their intended use */ + gpio_i2c_restore_pins(); +} + +/* Send the offset to the AT24 chip */ +static int at24_send_offset(u32 offset) +{ + int sleeps = 100; + int timeout = 1000; + int err, i; + u32 byteoffset, sizemask, sentoffset; + + do { + while (gpio_i2c_busy() && timeout--) + udelay(100); + sleeps--; + + gpio_i2c_tx_start(); + + err = gpio_i2c_tx_byte(AT24_DEVICE_ADDRESS(AT24_ACCESS_WRITE)); + + /* If the eeprom is busy, reset the bus an start over */ + if (err) + gpio_i2c_tx_stop(); + + if (err && !sleeps) { + /* NACK, return error */ + return err; + } + } while (err); + + /* Write the destination offset to the AT24 */ + sizemask = AT24_SIZE_MASK; + sentoffset = offset & sizemask; + + for (i=0; i<4; ++i) { + byteoffset = (sentoffset & 0xFF000000) >> 24; + if (sizemask & 0xFF000000) { + err = gpio_i2c_tx_byte(byteoffset); + if (err) + return err; + } + sentoffset <<= 8; + sizemask <<= 8; + } + return 0; +} + +/* Read 'size' bytes from the AT88 at offset 'offset'; return 0 if good */ +int at24_read(u32 offset, u8 *buf, u32 size) +{ + int rx_mode; + int err, idx; + + err = at24_send_offset(offset); + if (err) + return err; + gpio_i2c_tx_stop(); + + gpio_i2c_tx_start(); + err = gpio_i2c_tx_byte(AT24_DEVICE_ADDRESS(AT24_ACCESS_READ)); + if (err) + return err; + + for (idx = 0; idx < size; ++idx) { + if (size == 1) + rx_mode = RX_MODE_ONE_BYTE; + else if (idx == (size - 1)) + rx_mode = RX_MODE_LAST_BYTE; + else if (idx == (size - 2)) + rx_mode = RX_MODE_NEXT_TO_LAST_BYTE; + else if (idx == 0) + rx_mode = RX_MODE_FIRST_BYTE; + else + rx_mode = RX_MODE_MIDDLE_BYTE; + + err = gpio_i2c_rx_byte(&buf[idx], rx_mode); + if (err) + printf("%s:%d err idx %d\n", __FUNCTION__, __LINE__, idx); + } + + gpio_i2c_tx_stop(); + return err; +} diff --git a/board/ti/logic/logic-at24.h b/board/ti/logic/logic-at24.h new file mode 100644 index 00000000000..43c8b1bc972 --- /dev/null +++ b/board/ti/logic/logic-at24.h @@ -0,0 +1,24 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 + */ + +extern int at24_read(u32 offset, u8 *buf, u32 size); +extern int at24_wakeup(void); +extern int at24_shutdown(void); diff --git a/board/ti/logic/logic-at88.c b/board/ti/logic/logic-at88.c new file mode 100644 index 00000000000..21299cadc80 --- /dev/null +++ b/board/ti/logic/logic-at88.c @@ -0,0 +1,117 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <command.h> +#include <asm/arch/cpu.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/clocks.h> +#include <asm/arch/mem.h> +#include <i2c.h> +#include <asm/mach-types.h> +#include "product_id.h" +#include "logic-gpio.h" +#include "logic-i2c.h" +#include "logic-at88.h" +#include "logic-data.h" + +int at88_send_packet(uint8_t *data, int len, uint8_t *rxbuf, int rxlen) +{ + int timeout = 1000; + int retry; + int rx_mode; + int tick, err, idx; + + if (DEBUG_PRODUCTION_DATA) { + char buf[3 * len + 2]; + int i, offset; + for (offset = 0, i=0; i<len; ++i) { + if (!i) + offset = sprintf(buf, "%02x", data[i]); + else + offset += sprintf(&buf[offset], " %02x", data[i]); + } + printf("%s: %s\n", __FUNCTION__, buf); + } + + /* Wait for bus */ + while (gpio_i2c_busy() && timeout--) + udelay(100); + + if (!timeout) + printf("%s:%d i2c_busy never return zero!\n", __FUNCTION__, __LINE__); + + retry = 0; + do { + tick = 0; + do { + gpio_i2c_tx_stop(); + gpio_i2c_tx_start(); + + /* send cmd */ + err = gpio_i2c_tx_byte(data[0]); + tick++; + } while (err && tick < 100); + + if (tick > 3) + printf("I2C ACK polling tick %d!\n", tick); + + for (idx = 1; idx<len; ++idx) { + err = gpio_i2c_tx_byte(data[idx]); + if (err) { + printf("%s:%d NACK idx %d\n", __FUNCTION__, __LINE__, idx); + } + } + } while (err && (retry++ < 5)); + + if (err) + return err; + + /* Are we expecting a response? */ + if (rxbuf) { + for (idx = 0; idx < rxlen; ++idx) { + if (rxlen == 1) + rx_mode = RX_MODE_ONE_BYTE; + else if (idx == (rxlen - 1)) + rx_mode = RX_MODE_LAST_BYTE; + else if (idx == (rxlen - 2)) + rx_mode = RX_MODE_NEXT_TO_LAST_BYTE; + else if (idx == 0) + rx_mode = RX_MODE_FIRST_BYTE; + else + rx_mode = RX_MODE_MIDDLE_BYTE; + + err = gpio_i2c_rx_byte(&rxbuf[idx], rx_mode); + if (DEBUG_PRODUCTION_DATA) { + if (err) + printf("%s:%d err idx %d\n", __FUNCTION__, __LINE__, idx); + } + } + } + + gpio_i2c_tx_stop(); + return err; +} + + + diff --git a/board/ti/logic/logic-at88.h b/board/ti/logic/logic-at88.h new file mode 100644 index 00000000000..6f66f8a0c8d --- /dev/null +++ b/board/ti/logic/logic-at88.h @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 + */ + + +extern int at88_send_packet(uint8_t *data, int len, uint8_t *rxbuf, int rxlen); diff --git a/board/ti/logic/logic-data.c b/board/ti/logic/logic-data.c new file mode 100644 index 00000000000..ba4633bf891 --- /dev/null +++ b/board/ti/logic/logic-data.c @@ -0,0 +1,643 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <command.h> +#include <asm/arch/cpu.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/clocks.h> +#include <asm/arch/mem.h> +#include <i2c.h> +#include <asm/mach-types.h> +#include "product_id.h" +#include "logic-gpio.h" +#include "logic-i2c.h" +#include "logic-at88.h" +#include "logic-product-id.h" +#include "logic-data.h" + +static int header_version = -1; + + +/* + * Identify the device + */ +struct device_param { + char *name; + unsigned char reset[8]; /* ATR for part */ + unsigned int zones; /* number of zones */ + unsigned int zonesize; /* bytes per zone */ +}; + +static const struct device_param answers[] = { + { + .name = "AT88SC0104C", + .reset = {0x3B, 0xB2, 0x11, 0x00, 0x10, 0x80, 0x00, 0x01}, + .zones = 4, + .zonesize = 32 + }, + { + .name = "AT88SC0204C", + .reset = {0x3B, 0xB2, 0x11, 0x00, 0x10, 0x80, 0x00, 0x02}, + .zones = 4, + .zonesize = 64 + }, + { + .name = "AT88SC0404C", + .reset = {0x3B, 0xB2, 0x11, 0x00, 0x10, 0x80, 0x00, 0x04}, + .zones = 4, + .zonesize = 128 + }, + { + .name = "AT88SC0808C", + .reset = {0x3B, 0xB2, 0x11, 0x00, 0x10, 0x80, 0x00, 0x08}, + .zones = 8, + .zonesize = 128 + }, + { + .name = "AT88SC1616C", + .reset = {0x3B, 0xB2, 0x11, 0x00, 0x10, 0x80, 0x00, 0x16}, + .zones = 16, + .zonesize = 128 + }, + { + .name = "AT88SC3216C", + .reset = {0x3B, 0xB3, 0x11, 0x00, 0x00, 0x00, 0x00, 0x32}, + .zones = 16, + .zonesize = 256 + }, + { + .name = "AT88SC6416C", + .reset = {0x3B, 0xB3, 0x11, 0x00, 0x00, 0x00, 0x00, 0x64}, + .zones = 16, + .zonesize = 512 + }, + { + .name = "AT88SC12816C", + .reset = {0x3B, 0xB3, 0x11, 0x00, 0x00, 0x00, 0x01, 0x28}, + .zones = 16, + .zonesize = 1024 + }, + { + .name = "AT88SC25616C", + .reset = {0x3B, 0xB3, 0x11, 0x00, 0x00, 0x00, 0x02, 0x56}, + .zones = 16, + .zonesize = 2048 + }, +}; + +static const struct device_param *devptr; /* pointer to ID'd device */ + +#define CMD_SYSTEM_READ 0xB6 + +static int +identify_device(void) +{ + const struct device_param *p; + unsigned char cmd[] = { CMD_SYSTEM_READ, 0x00, 0x00, 0x00 }; + unsigned char buf[8]; + int err; + int i,j; + + err = at88_send_packet(cmd, sizeof(cmd), buf, sizeof(buf)); + if (err) + return err; + + if (DEBUG_PRODUCTION_DATA) + printf("%s: ident %02x %02x %02x %02x %02x %02x %02x %02x\n", __FUNCTION__, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + + for (p=answers,i=0; i<sizeof(answers)/sizeof(answers[0]); ++i,++p) { + for (j=0; j<8 && (p->reset[j] == buf[j]); ++j) + ; + if (j==8) { + devptr = p; + + if (DEBUG_PRODUCTION_DATA) + printf("%s: device %s zones %u zonesize %u\n", __FUNCTION__, + devptr->name, devptr->zones, devptr->zonesize); + + return 0; + } + } + + if (DEBUG_PRODUCTION_DATA) + printf("%s: Huh? couldn't ID productID device\n", __FUNCTION__); + + return -1; +} + +#define CMD_SYSTEM_WRITE 0xB4 + +static int +set_user_zone(int zone) +{ + unsigned char cmd[] = { CMD_SYSTEM_WRITE, 0x03, 0x00, 0x00 }; + + if (DEBUG_PRODUCTION_DATA) + printf("%s: zone %d\n", __FUNCTION__, zone); + cmd[2] = zone; + return at88_send_packet(cmd, sizeof(cmd), NULL, 0); +} + +#define CMD_READ_USER_ZONE 0xB2 + +static int +read_user_zone(unsigned int startzone, unsigned int offset,unsigned char *buf, int len) +{ + unsigned char cmd[] = { CMD_READ_USER_ZONE, 0x00, 0x00, 0x00 }; + int ret; + unsigned int endzone; + unsigned int nbytes, zone_offset; + + if (DEBUG_PRODUCTION_DATA) + printf("%s: offset %u len %d\n", __FUNCTION__, offset, len); + + /* If zone is non-zero, then we use zone/offset addressing, not + offset from start of chip */ + + /* abort if we'll go past the end of the device */ + if (startzone) { + if (offset > devptr->zonesize) { + printf("%s: offset %u > zonesize %u\n", __FUNCTION__, offset, devptr->zonesize); + return -1; + } + if (startzone > devptr->zones) { + printf("%s: startzone %u > numzones %u\n", __FUNCTION__, startzone, devptr->zones); + return -1; + } + } else { + startzone = offset / devptr->zonesize; + } + endzone = (offset + (len - 1)) / devptr->zonesize; + if (endzone > devptr->zones) { + printf("%s: endzone %u > numzones %u\n", __FUNCTION__, endzone, devptr->zones); + return -1; + } + + do { + /* Set the zone */ + if (set_user_zone(startzone)) + return -1; + + zone_offset = offset % devptr->zonesize; + nbytes = devptr->zonesize - zone_offset; + if (nbytes > len) + nbytes = len; + + cmd[2] = zone_offset; + cmd[3] = nbytes; + ret = at88_send_packet(cmd, sizeof(cmd), buf, nbytes); + if (DEBUG_PRODUCTION_DATA_BUF) { + char obuf[128]; + int i,j,offset; + for (i = 0, offset=0; i<len; i+=16) { + for (j = 0; j<16 && i+j<len; ++j) + if (!j) + offset = sprintf(obuf, "%02x", buf[i+j]); + else + offset += sprintf(&obuf[offset], " %02x", buf[i+j]); + printf("%s\n", obuf); + } + } + + buf += nbytes; + len -= nbytes; + offset += nbytes; + startzone++; + } while (len); + return ret; +} + +#define XMK_STR(x) #x +#define MK_STR(x) XMK_STR(x) +int production_data_valid; + +static struct product_id_data product_id_data; + +static int valid_mac_address(unsigned char mac[3]) +{ + if (mac[0] == 0xff && mac[1] == 0xff && mac[2] == 0xff) + return 0; + if (mac[0] == 0x00 && mac[1] == 0x00 && mac[2] == 0x00) + return 0; + return !0; +} + +static int valid_full_mac_address(unsigned char mac[6]) +{ + return (valid_mac_address(&mac[0]) && valid_mac_address(&mac[3])); +} + +int extract_mac_address(struct product_id_data *p, int position, unsigned char mac[6]) +{ + unsigned char *m = NULL; + if (!production_data_valid) + return -1; + + if (DEBUG_PRODUCTION_DATA) + printf("%s: position %d\n", __FUNCTION__, position); + switch(position) { + case 0: + if (header_version >= 2) { + if (valid_full_mac_address(p->d.u_zone0.pz_0r2.full_mac)) { + memcpy(mac, p->d.u_zone0.pz_0r2.full_mac, 6); + goto out; + } + } + m = p->d.zone2.pz_2r0.mac0; + break; + case 1: + m = p->d.zone2.pz_2r0.mac1; + break; + case 2: + m = p->d.zone2.pz_2r0.mac2; + break; + case 3: + m = p->d.zone2.pz_2r0.mac3; + break; + default: + return -1; + } + if (valid_mac_address(m)) { + mac[0] = 0x00; + mac[1] = 0x08; + mac[2] = 0xee; + mac[3] = m[0]; + mac[4] = m[1]; + mac[5] = m[2]; + } else { + return -1; + } + +out: + if (DEBUG_PRODUCTION_DATA) + printf("%s:%d valid %d position %d %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, __LINE__, + production_data_valid, position, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return 0; +} + +/* + * Extract/set an ethernet address. + * Which is the address in the environment, position is which MAC address + * in the product ID data + */ +void board_get_nth_enetaddr (unsigned char *enetaddr, int which, int position) +{ + unsigned char mac[6]; + char buf[32]; + char *s = NULL, *e; + int i; + char ethbuf[18]; + int ret; + + /* We only handle the first two interfaces (LAN/WiFi)... */ + if (which >= 2) + return; + + memset(mac, 0, sizeof(mac)); + ret = extract_mac_address(&product_id_data, position, mac); + if (DEBUG_PRODUCTION_DATA) + printf("%s: ret %d valid %d which %d position %d %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, + ret, production_data_valid, which, position, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + memset(enetaddr, '\0', 6); + if (!production_data_valid || + !valid_full_mac_address(mac)) { + if (DEBUG_PRODUCTION_DATA) + printf("%s: no valid address\n", __FUNCTION__); + s = getenv("ethaddr"); + +#ifdef CONFIG_ETHADDR + if (!s) + s = MK_STR(CONFIG_ETHADDR); +#endif + + /* If no ethaddr found in productID or environment, then punt*/ + if (!s) + return; + + for (i = 0; i < 6; ++i) { + enetaddr[i] = s ? simple_strtoul (s, &e, 16) : 0; + if (s) + s = (*e) ? e + 1 : e; + } + goto set_it; + } + + memcpy(enetaddr, mac, 6); + +set_it: + if (which == 0) { + sprintf(ethbuf, "%02x:%02x:%02x:%02x:%02x:%02x", enetaddr[0], enetaddr[1], enetaddr[2], enetaddr[3], enetaddr[4], enetaddr[5]); + sprintf(buf, "ethaddr"); + } else { + sprintf(ethbuf, "%02x:%02x:%02x:%02x:%02x:%02x", enetaddr[0], enetaddr[1], enetaddr[2], enetaddr[3], enetaddr[4], enetaddr[5]); + sprintf(buf, "eth%daddr", which); + } + if (DEBUG_PRODUCTION_DATA) + printf("setenv '%s' '%s'\n", __FUNCTION__, ethbuf); + + /* If already set, then don't change it */ + s = getenv(buf); + if (s) { + if (strcmp(s, ethbuf) != 0) + printf("Overriding %s productID value %s with environment value\n", buf, ethbuf); + return; + } + setenv(buf, ethbuf); +} + +static int extract_product_id_part_number(struct product_id_data *p, char *buf, int buflen) +{ + int size; + + if (!production_data_valid) + return -1; + + buf[0] = '\0'; + if (header_version == LOGIC_HEADER_VERSION_0) { + size = sizeof(p->d.u_zone0.pz_0r0.part_number); + if (buflen < sizeof(p->d.u_zone0.pz_0r0.part_number)) + size = buflen; + strncpy(buf, p->d.u_zone0.pz_0r0.part_number, sizeof(p->d.u_zone0.pz_0r0.part_number)); + buf[sizeof(p->d.u_zone0.pz_0r0.part_number)] = '\0'; + return 0; + } + + if (header_version == LOGIC_HEADER_VERSION_1) { + size = sizeof(p->d.u_zone0.pz_0r1.part_number); + if (buflen < sizeof(p->d.u_zone0.pz_0r1.part_number)) + size = buflen; + strncpy(buf, p->d.u_zone0.pz_0r1.part_number, sizeof(p->d.u_zone0.pz_0r1.part_number)); + buf[sizeof(p->d.u_zone0.pz_0r1.part_number)] = '\0'; + return 0; + } + + if (p->d.u_zone0.pz_0r0.header_version == LOGIC_HEADER_VERSION_2 + || p->d.u_zone0.pz_0r0.header_version == LOGIC_HEADER_VERSION_3) { + size = sizeof(p->d.u_zone0.pz_0r2.part_number); + if (buflen < sizeof(p->d.u_zone0.pz_0r2.part_number)) + size = buflen; + strncpy(buf, p->d.u_zone0.pz_0r2.part_number, sizeof(p->d.u_zone0.pz_0r2.part_number)); + buf[sizeof(p->d.u_zone0.pz_0r2.part_number)] = '\0'; + return 0; + } + + return -1; +} + + +static int extract_header_version(struct product_id_data *p, int *header_version) +{ + if (p->d.u_zone0.pz_0r0.header_version == LOGIC_HEADER_VERSION_0) { + *header_version = p->d.u_zone0.pz_0r0.header_version; + return 0; + } + + if (p->d.u_zone0.pz_0r1.header_version == LOGIC_HEADER_VERSION_1) { + *header_version = p->d.u_zone0.pz_0r1.header_version; + return 0; + } + + if (p->d.u_zone0.pz_0r2.header_version == LOGIC_HEADER_VERSION_2 + || p->d.u_zone0.pz_0r2.header_version == LOGIC_HEADER_VERSION_3) { + *header_version = p->d.u_zone0.pz_0r2.header_version; + return 0; + } + + *header_version = -1; + return -1; + +} + +static int extract_serial_number(struct product_id_data *p, char *buf, int buflen) +{ + buf[0] = '\0'; + + if (!production_data_valid) + return -1; + + if (header_version == LOGIC_HEADER_VERSION_0) { + sprintf(buf, "%02d%02d%c%05d", p->d.u_zone0.pz_0r0.sn_week, + p->d.u_zone0.pz_0r0.sn_year, p->d.u_zone0.pz_0r0.sn_site, + p->d.u_zone0.pz_0r0.sn_cnt); + return 0; + } + if (header_version == LOGIC_HEADER_VERSION_1) { + sprintf(buf, "%02d%02d%c%05d", p->d.u_zone0.pz_0r1.sn_week, + p->d.u_zone0.pz_0r1.sn_year, p->d.u_zone0.pz_0r1.sn_site, + p->d.u_zone0.pz_0r1.sn_cnt); + return 0; + } + if (header_version == LOGIC_HEADER_VERSION_2 + || header_version == LOGIC_HEADER_VERSION_3) { + sprintf(buf, "%02d%02d%c%05d", p->d.u_zone0.pz_0r2.sn_week, + p->d.u_zone0.pz_0r2.sn_year, p->d.u_zone0.pz_0r2.sn_site, + p->d.u_zone0.pz_0r2.sn_cnt); + return 0; + } + return -1; +} + +static void extract_model_number_revision(struct product_id_data *p, char *buf, int buflen) +{ + int i; + + strncpy(buf, product_id_data.d.zone1.model_number, buflen); + buf[buflen-1] = '\0'; + if (header_version < LOGIC_HEADER_VERSION_2) { + i = strlen(buf); + if (i + 3 < buflen) { + buf[i] = '-'; + buf[i+1] = product_id_data.d.zone1.model_revision; + buf[i+2] = '\0'; + } + } +} + +int _fetch_production_data(void) +{ + int err = 0; + int checksum; + int i; + + production_data_valid = 0; + + /* Make sure voltage is to productID chip! */ + gpio_i2c_init(50000); + + /* The productID chip wants at least 5 clocks to wake it up... */ + gpio_i2c_config_pin(GPIO_I2C_SCLK, GPIO_I2C_OUTPUT); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + for (i=0; i<10; ++i) { + udelay(100); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + udelay(100); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + } + + printf("Read production data: "); + + if (identify_device()) { + printf("failed to identify device!\n"); + err = -1; + goto out; + } + + if (read_user_zone(0, 0, (unsigned char *)&product_id_data.d.u_zone0, sizeof(product_id_data.d.u_zone0))) { + printf("failed!\n"); + err = -1; + goto out; + } + + /* If the header doesn't match, we can't map any of the data */ + if (extract_header_version(&product_id_data, &header_version)) { + printf("failed - invalid header version %d!\n", header_version); + err = -2; + goto out; + } + + if (read_user_zone(0, 32, (unsigned char *)&product_id_data.d.zone1, sizeof(product_id_data.d.zone1))) { + printf("failed reading zone1 data!\n"); + err = -3; + goto out; + } + + if (read_user_zone(0, 64, (unsigned char *)&product_id_data.d.zone2, sizeof(product_id_data.d.zone2))) { + printf("failed reading zone2 data!\n"); + err = -4; + goto out; + } + + printf("done\n"); + + production_data_valid = 1; + /* Correct endianess issues */ + product_id_data.d.zone2.pz_2r0.processor_type = le16_to_cpu(product_id_data.d.zone2.pz_2r0.processor_type); + + if (header_version < 2) + product_id_data.d.zone2.pz_2r0.feature_bits = le32_to_cpu(product_id_data.d.zone2.pz_2r0.feature_bits); + + product_id_data.d.zone2.pz_2r0.platform_bits = le32_to_cpu(product_id_data.d.zone2.pz_2r0.platform_bits); + + /* WiFi config data starts at begining of zone 2. Don't bother + reading it if we know it can't fit in the productID chip */ + if (2 + sizeof(product_id_data.d.wifi_config_data.data) / devptr->zonesize < devptr->zones) { + if (read_user_zone(2, 0, (unsigned char *)&product_id_data.d.wifi_config_data.data, sizeof(product_id_data.d.wifi_config_data.data))) { + printf("failed reading wifi_config data!\n"); + } else + product_id_data.d.wifi_config_data.valid = 1; + } + +out: + + /* Restore GPIO_OE registers back to reset state (All input) */ + gpio_i2c_config_pin(GPIO_I2C_SDATA, GPIO_I2C_INPUT); + gpio_i2c_config_pin(GPIO_I2C_SCLK, GPIO_I2C_INPUT); + + /* Restore pins back to their intended use */ + gpio_i2c_restore_pins(); + + /* Calculate a checksum for the data and copy the + * production data to the start of SRAM */ + checksum = calculate_checksum(&product_id_data.d, + sizeof(product_id_data.d)); + product_id_data.checksum = checksum; + + *(struct product_id_data *)(SRAM_BASE) = product_id_data; + + return err; +} + +void _dump_production_data(void) +{ + /* DECLARE_GLOBAL_DATA_PTR; */ + char buf[36]; + unsigned char mac[6]; + int ret; + int i; + + if (!production_data_valid) + return; + + /* Print out the name, model number, and set MAC addresses */ + extract_product_id_part_number(&product_id_data, buf, sizeof(buf)); + printf("Part Number : %s\n", buf); + + extract_model_number_revision(&product_id_data, buf, sizeof(buf)); + if (strlen(buf)) + printf("Model Name : %s\n", buf); + + extract_serial_number(&product_id_data, buf, sizeof(buf)); + printf("Serial Number: %s\n", buf); + + ret = extract_mac_address(&product_id_data, 0, mac); + if (!ret) { + printf("LAN ethaddr : %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } + + for (i=1; i<4; ++i) { + if (!extract_mac_address(&product_id_data, i, mac)) + printf("LAN[%d] = %02x:%02x:%02x:%02x:%02x:%02x\n", + i, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } +} + +#ifdef CONFIG_OMAP3_LOGIC_USE_NEW_PRODUCT_ID +static int has_new_product_id_data; +#endif + +/* Stub functions that call into current AT88 product ID code. + * Need to add tests for *new* product ID data before looking for older + * data device/format. */ +int fetch_production_data(void) +{ +#ifdef CONFIG_OMAP3_LOGIC_USE_NEW_PRODUCT_ID + + /* This makes first pass through the EEPROM so we have to initialize + * software i2c before reading and clean up after we are done */ + + if (logic_has_new_product_id()) { + has_new_product_id_data = 1; + return 0; + } +#endif + return _fetch_production_data(); +} + +void dump_production_data(void) +{ +#ifdef CONFIG_OMAP3_LOGIC_USE_NEW_PRODUCT_ID + + if (has_new_product_id_data) { + logic_dump_serialization_info(); + return; + } +#endif + _dump_production_data(); + +} diff --git a/board/ti/logic/logic-data.h b/board/ti/logic/logic-data.h new file mode 100644 index 00000000000..b296d2c4d0f --- /dev/null +++ b/board/ti/logic/logic-data.h @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 + */ + +#define DEBUG_PRODUCTION_DATA 0 +#define DEBUG_PRODUCTION_DATA_BUF 0 diff --git a/board/ti/logic/logic-display.c b/board/ti/logic/logic-display.c new file mode 100644 index 00000000000..3c0d17c188d --- /dev/null +++ b/board/ti/logic/logic-display.c @@ -0,0 +1,889 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <asm/io.h> +#include <asm/arch/gpio.h> +#include <asm/arch/mux.h> +#include <asm/arch/dss.h> +#include <asm/arch/timer.h> +#include <twl4030.h> +#include <lcd.h> +#include "splash-332x57.h" +#include "logic-proto.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* LCD-required members */ +int lcd_line_length; /* initialized in lcd.c */ +int lcd_color_fg; +int lcd_color_bg; +void *lcd_base; /* initialized in lcd.c */ +void *lcd_console_address; /* where is this initialized? */ +short console_col = 0; +short console_row = 0; + +vidinfo_t panel_info; /* Filled in by find_screen */ + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ + /* Empty */ +} + +void lcd_initcolregs(void) +{ + /* Empty */ +} + +struct omap_video_timings { + /* Unit: pixels */ + u16 x_res; + /* Unit: pixels */ + u16 y_res; + /* Unit: KHz */ + u32 pixel_clock; + /* Unit: pixel clocks */ + u16 hsw; /* Horizontal synchronization pulse width */ + /* Unit: pixel clocks */ + u16 hfp; /* Horizontal front porch */ + /* Unit: pixel clocks */ + u16 hbp; /* Horizontal back porch */ + /* Unit: line clocks */ + u16 vsw; /* Vertical synchronization pulse width */ + /* Unit: line clocks */ + u16 vfp; /* Vertical front porch */ + /* Unit: line clocks */ + u16 vbp; /* Vertical back porch */ +}; + +struct logic_panel { + char *name; + int config; + int acb; + char data_lines; + struct omap_video_timings timing; +}; + + +static struct logic_panel default_panel; + +static struct omap_custom_lcd_fields { + char *field; + char *format; + void *ptr; + int len; +} omap_custom_lcd_fields[] = { + { "xres", "%u", &default_panel.timing.x_res , 2}, + { "yres", "%u", &default_panel.timing.y_res , 2}, + { "left margin", "%u", &default_panel.timing.hbp, 2 }, + { "right margin", "%u", &default_panel.timing.hfp, 2 }, + { "top margin", "%u", &default_panel.timing.vbp, 2 }, + { "bottom margin", "%u", &default_panel.timing.vfp, 2 }, + { "hsync length", "%u", &default_panel.timing.hsw, 2 }, + { "vsync length", "%u", &default_panel.timing.vsw, 2 }, + { "pixclock", "%u", &default_panel.timing.pixel_clock, 4 }, + { "config", "%u", &default_panel.config, 4 }, + { "data_lines", "%u", &default_panel.data_lines, 1 }, +}; + +ulong calc_fbsize(void) { + ulong size; + if (!default_panel.timing.x_res || !default_panel.timing.y_res) + return 0; + + size = default_panel.timing.x_res * default_panel.timing.y_res; + size *= (default_panel.data_lines / 8); + + return size; +} + + +struct logic_panel logic_panels[] = { + { + .name = "15", + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS + | OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_RF | OMAP_DSS_LCD_IEO + | OMAP_DSS_LCD_IHS, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 480 x 272, LQ043T1DG01 */ + .x_res = 480, + .y_res = 272, + .pixel_clock = 9000, + .hfp = 3, + .hsw = 42, + .hbp = 2, + .vfp = 4, + .vsw = 11, + .vbp = 3, + }, + }, + { + .name = "3", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 320 x 240, LQ036Q1DA01 */ + .x_res = 320, + .y_res = 240, + .pixel_clock = 24500, + .hfp = 20, + .hsw = 20, + .hbp = 20, + .vfp = 3, + .vsw = 3, + .vbp = 4, + }, + }, + { + .name = "7", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 640 x 480, LQ10D368 */ + .x_res = 640, + .y_res = 480, + .pixel_clock = 27000, + .hfp = 24, + .hsw = 48, + .hbp = 135, + .vfp = 34, + .vsw = 1, + .vbp = 34, + }, + }, + { + .name = "5", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 640 x 240, LQ036Q1DA01 */ + .x_res = 640, + .y_res = 480, + .pixel_clock = 27000, + .hfp = 24, + .hsw = 48, + .hbp = 135, + .vfp = 34, + .vsw = 1, + .vbp = 34, + }, + }, + { + .name = "2", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 800 x 600, LQ121S1DG31 */ + .x_res = 800, + .y_res = 600, + .pixel_clock = 42000, + .hfp = 120, + .hsw = 5, + .hbp = 88-4-2, + .vfp = 100, + .vsw = 4, + .vbp = 21-1, + }, + }, + { + .name = "vga", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 640 x 480, VGA on DVI */ + .x_res = 640, + .y_res = 480, + .pixel_clock = 24685, + .hfp = 16, + .hsw = 96, + .hbp = 48, + .vfp = 10, + .vsw = 2, + .vbp = 33, + }, + }, + { + .name = "svga", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 800 x 600, SVGA on DVI */ + .x_res = 800, + .y_res = 600, + .pixel_clock = 42000, + .hfp = 120, + .hsw = 5, + .hbp = 88-4-2, + .vfp = 100, + .vsw = 4, + .vbp = 21-1, + }, + }, + { + .name = "xga", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 1024 x 769, XGA on DVI */ + .x_res = 1024, + .y_res = 768, + .pixel_clock = 61714, + .hfp = 24, + .hsw = 41, + .hbp = 160, + .vfp = 3, + .vsw = 6, + .vbp = 29, + }, + }, + { + .name = "720p", + .config = OMAP_DSS_LCD_TFT, + .acb = 0x28, + .data_lines = 16, + .timing = { + /* 1280 x 720, 720P on DVI */ + .x_res = 1280, + .y_res = 720, + .pixel_clock = 72000, + .hfp = 110, + .hsw = 40, + .hbp = 220, + .vfp = 5, + .vsw = 5, + .vbp = 20, + }, + }, +}; + + + +struct logic_panel *logic_find_panel(void) +{ + char *p, *q, *r; + struct omap_custom_lcd_fields *f; + char *panel; + char panel_name[128]; + unsigned int val; + int err = 0, i; + int last, data_lines; + int found = 0; + + panel = getenv("display"); + if (!panel) { + printf("No 'display' variable found in environment; suppress splashimage\n"); + return NULL; + } + + if (strchr(panel, ':')) { + default_panel = logic_panels[0]; + default_panel.name = "custom"; + default_panel.data_lines = 16; + strcpy(panel_name, panel); + found = 1; + last = 0; + p = panel_name; + for (i=0, f=omap_custom_lcd_fields; !last && i<ARRAY_SIZE(omap_custom_lcd_fields); ++i, ++f) { + q = strchr(p, ':'); + if (q) + *q = '\0'; + else + last = 1; + + val = simple_strtoul(p, &r, 0); + + if (q && (r != q)) { + printf("Custom display field '%s' value of '%s' invalid\n", f->field, p); + err = 1; + break; + } + switch(f->len) { + case 1: { + u8 *ptr = f->ptr; + *ptr = val; + } + break; + case 2: { + u16 *ptr = f->ptr; + *ptr = val; + } + break; + default: + case 4: { + u32 *ptr = f->ptr; + *ptr = val; + } + break; + } + p = q+1; + } + strcpy(panel_name, default_panel.name); + if (calc_fbsize() > board_lcd_setmem(0)) { + printf("Custom screen definition too large!\n"); + found = 0; + } + } else { + /* Copy panel name and null-terminate it */ + strncpy(panel_name, panel, sizeof(panel_name)); + panel_name[sizeof(panel_name)-1] = '\0'; + + /* Search for trailing "-dvi" or "-hdmi", if found + * set data_lines and strip off trailing specifier */ + data_lines = 16; + if ((p = strrchr(panel_name, '-')) != NULL) { + if (!strcmp(p+1, "dvi")) { + data_lines = 16; + *p='\0'; + } else if (!strcmp(p+1, "hdmi")) { + data_lines = 24; + *p='\0'; + } + } + + for (i=0; i<ARRAY_SIZE(logic_panels); ++i) + if (!strcmp(panel_name, logic_panels[i].name)) { + default_panel = logic_panels[i]; + default_panel.data_lines = data_lines; + found = 1; + break; + } + } + + if (found) { + printf("Found '%s' display panel\n", panel_name); + panel_info.vl_col = default_panel.timing.x_res; + panel_info.vl_row = default_panel.timing.y_res; + if (default_panel.data_lines == 16) + panel_info.vl_bpix = LCD_COLOR16; + else + panel_info.vl_bpix = LCD_COLOR24; + lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + return &default_panel; + } + printf("display='%s' does not describe a valid screen!\n", panel); + return NULL; +} + +/* Return size of largest framebuffer (so system can reserve memory on start) */ +ulong board_lcd_setmem(ulong addr) +{ + return 1600*1200*4; /* sxga at 24bpp(w 8bits of alpha) */ +} + +void touchup_display_env(void) +{ + // enable the splash screen + char splash_bmp_gz_str[12]; + + sprintf(splash_bmp_gz_str, "0x%08X", (unsigned int)splash_bmp_gz); + setenv("splashimage", splash_bmp_gz_str); + setenv("splashpos", "m,m"); +} + +void lcd_setup_pinmux(int data_lines) +{ + u32 arch_number; + + arch_number = gd->bd->bi_arch_number; + + /* Setup common pins */ + MUX_VAL(CP(DSS_PCLK), (IDIS | PTD | DIS | M0)); /*DSS_PCLK*/ + MUX_VAL(CP(DSS_HSYNC), (IDIS | PTD | DIS | M0)); /*DSS_HSYNC*/ + MUX_VAL(CP(DSS_VSYNC), (IDIS | PTD | DIS | M0)); /*DSS_VSYNC*/ + MUX_VAL(CP(DSS_ACBIAS), (IDIS | PTD | DIS | M0)); /*DSS_ACBIAS*/ + MUX_VAL(CP(DSS_DATA6), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA7), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA8), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA9), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA10), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA11), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA12), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA13), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA14), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA15), (IDIS | PTD | DIS | M0)); /*DSS_DATA6*/ + + /* omap35x use DSS_DATA0:15 + * dm37x SOM uses DSS_DATA0:15 + * dm37x Torpedo uses DSS_DATA18:23 as DSS_DATA0:5 */ + + if (arch_number == MACH_TYPE_DM3730_TORPEDO) { + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | DIS | M3)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | DIS | M3)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | DIS | M3)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | DIS | M3)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | DIS | M3)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | DIS | M3)); /*DSS_DATA5*/ + } else { + MUX_VAL(CP(DSS_DATA0), (IDIS | PTD | DIS | M0)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA1), (IDIS | PTD | DIS | M0)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA2), (IDIS | PTD | DIS | M0)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA3), (IDIS | PTD | DIS | M0)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA4), (IDIS | PTD | DIS | M0)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA5), (IDIS | PTD | DIS | M0)); /*DSS_DATA5*/ + } + + /* No need to mux the top 8 pins if not using them */ + if (data_lines <= 16) + return; + + MUX_VAL(CP(DSS_DATA16), (IDIS | PTD | DIS | M0)); /*DSS_DATA16*/ + MUX_VAL(CP(DSS_DATA17), (IDIS | PTD | DIS | M0)); /*DSS_DATA17*/ + + if (arch_number == MACH_TYPE_DM3730_TORPEDO) { + MUX_VAL(CP(SYS_BOOT0), (IDIS | PTD | DIS | M3)); /*DSS_DATA18*/ + MUX_VAL(CP(SYS_BOOT1), (IDIS | PTD | DIS | M3)); /*DSS_DATA19*/ + MUX_VAL(CP(SYS_BOOT3), (IDIS | PTD | DIS | M3)); /*DSS_DATA20*/ + MUX_VAL(CP(SYS_BOOT4), (IDIS | PTD | DIS | M3)); /*DSS_DATA21*/ + MUX_VAL(CP(SYS_BOOT5), (IDIS | PTD | DIS | M3)); /*DSS_DATA22*/ + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTD | DIS | M3)); /*DSS_DATA23*/ + } else { + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | DIS | M0)); /*DSS_DATA18*/ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | DIS | M0)); /*DSS_DATA19*/ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | DIS | M0)); /*DSS_DATA20*/ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | DIS | M0)); /*DSS_DATA21*/ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | DIS | M0)); /*DSS_DATA22*/ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | DIS | M0)); /*DSS_DATA23*/ + } +} + +void lcd_ctrl_init(void *lcdbase) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + struct logic_panel *panel; + struct panel_config dss_panel; + + memset(&panel_info, 0, sizeof(panel_info)); + panel = logic_find_panel(); + if (!panel) + return; + + /* Convert from timings into panel_config structure */ + dss_panel.panel_color = 0x0; /* black */ + dss_panel.load_mode = 0x2; /* Frame Mode */ + dss_panel.panel_type = 1; /* Active TFT */ + + dss_panel.timing_h = panel->timing.hsw - 1; + dss_panel.timing_h |= ((panel->timing.hfp - 1) << 8); + dss_panel.timing_h |= ((panel->timing.hbp - 1) << 20); + dss_panel.timing_v = panel->timing.vsw - 1; + dss_panel.timing_v |= ((panel->timing.vfp - 1) << 8); + dss_panel.timing_v |= ((panel->timing.vbp - 1) << 20); + dss_panel.pol_freq = panel->acb; + dss_panel.pol_freq |= ((panel->config & 0x3f) << 12); + dss_panel.lcd_size = panel->timing.x_res - 1; + dss_panel.lcd_size |= (panel->timing.y_res - 1) << 16; + if (panel->data_lines == 16) + dss_panel.data_lines = 1; + else if (panel->data_lines == 24) + dss_panel.data_lines = 3; + else { + printf("%s: Invalid data_lines!\n", __FUNCTION__); + memset(&panel_info, 0, sizeof(panel_info)); + return; + } + + dss_panel.pixel_clock = panel->timing.pixel_clock; + + lcd_setup_pinmux(panel->data_lines); + + /* configure DSS for single graphics layer */ + omap3_dss_panel_config(&dss_panel); + + writel((ulong)lcdbase, &dispc->gfx_ba0); /* frame buffer address */ + writel((ulong)lcdbase, &dispc->gfx_ba1); /* frame buffer address */ + writel(dss_panel.lcd_size, &dispc->gfx_size); /* size - same as LCD */ + if (panel->data_lines == 16) + writel((1<<0)|(6<<1), &dispc->gfx_attributes); /* 6=RGB16,8=RGB24 */ + else + writel(0x91, &dispc->gfx_attributes); /* 6=RGB16,8=RGB24 */ + + + if (panel->data_lines == 16) { + lcd_color_fg = 0xffff; + lcd_color_bg = 0x0000; + } else { + lcd_color_fg = 0x00ffffff; + lcd_color_bg = 0x00000000; + } +} + +void lcd_enable(void) +{ + int mSec; + u32 arch_number; + int gpio_backlight_pwr; + int gpio_panel_pwr; + + arch_number = gd->bd->bi_arch_number; + if (arch_number == MACH_TYPE_DM3730_TORPEDO + || arch_number == MACH_TYPE_OMAP3_TORPEDO) { + gpio_backlight_pwr = 154; + gpio_panel_pwr = 155; + MUX_VAL(CP(MCBSP4_DX), (IDIS | PTD | EN | M4)); /*GPIO_154*/ + + } else { + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTU | DIS | M4)); /*GPIO_8 */ + gpio_backlight_pwr = 8; + gpio_panel_pwr = 155; + } + MUX_VAL(CP(MCBSP4_FSX), (IDIS | PTD | EN | M4)); /*GPIO_155*/ + + /* Kill LCD_BACKLIGHT_PWR */ + if (!omap_request_gpio(gpio_backlight_pwr)) { + omap_set_gpio_direction(gpio_backlight_pwr, 0); + omap_set_gpio_dataout(gpio_backlight_pwr, 0); + } + + lcd_is_enabled = 0; /* keep console messages on the serial port */ + + /* Start clocks */ + omap3_dss_enable(); + + /* Delay 300mS to allow 4.3" panel to initialize */ + for (mSec=0; mSec<300; ++mSec) + udelay(1000); + + /* + * panel_enable = 155 + * backlight 154 (torpedo); 8 (som) + */ + + /* turn on LCD_PANEL_PWR */ + if (!omap_request_gpio(gpio_panel_pwr)) { + omap_set_gpio_direction(gpio_panel_pwr, 0); + omap_set_gpio_dataout(gpio_panel_pwr, 1); + } else + printf("%s:%d fail!\n", __FUNCTION__, __LINE__); + + + /* Torpedo-20 boards uses GPIO_56 as their backlight SOM + * use GPIO.6 on TWL4030 */ + + if (arch_number == MACH_TYPE_DM3730_TORPEDO + || arch_number == MACH_TYPE_OMAP3_TORPEDO) { + + MUX_VAL(CP(GPMC_NCS5), (IEN | PTD | EN | M3)); /*GPT10 backlight */ + init_gpt_timer(10, 70, 100); + } else { + twl4030_set_pwm0(70, 100); /* 70% backlight brighntess */ + } + + /* Sleep 300mS to allow panel to stabilize */ + for (mSec=0; mSec < 300; ++mSec) + udelay(1000); + + /* turn on LCD_BACKLIGHT_PWR SOM LV */ + if (!omap_request_gpio(gpio_backlight_pwr)) { + omap_set_gpio_direction(gpio_backlight_pwr, 0); + omap_set_gpio_dataout(gpio_backlight_pwr, 1); + } else + printf("%s:%d fail!\n", __FUNCTION__, __LINE__); + +} + +int do_backlight(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + u32 arch_number; + ulong level; + + level = simple_strtoul(argv[1], NULL, 10); + + arch_number = gd->bd->bi_arch_number; + if (arch_number == MACH_TYPE_DM3730_TORPEDO + || arch_number == MACH_TYPE_OMAP3_TORPEDO) { + + /* Adjust Torpedo GPT10 timer (used for LCD_PWM0) */ + init_gpt_timer(10, level, 100); + } else { + /* Adjust SOM LV TWL4030 PWM0 (used for LCD_PWM0) */ + twl4030_set_pwm0(level, 100); + } + + return 0; +} + +U_BOOT_CMD(backlight, 2, 1, do_backlight, + "backlight - change backlight level", + "<level>" +); + +int do_dump_pwm0(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + twl4030_dump_pwm0(); + return 0; +} + +U_BOOT_CMD(dump_pwm0, 1, 1, do_dump_pwm0, + " - dump TWL PWM registers", + "" +); + +struct fb_bitfield { + u8 length; + u8 offset; +}; +static struct fb_var_screeninfo { + int bits_per_pixel; + u32 xres, yres; + struct fb_bitfield red, green, blue; + u32 line_length; /* bytes/line */ +} fb_var; + +void *fb_ptr; +unsigned int fb_stride; +unsigned int fb_size; +int fb_max_x, fb_max_y; + +typedef struct { + unsigned int x,y; +} point_t; + +typedef unsigned int color_t; + +void +draw_pixel_32(point_t *p, color_t c) +{ + unsigned int offset; + + if (p->x >= fb_max_x || p->y >= fb_max_y) + printf("%s: point [%u:%u] out of range\n", __FUNCTION__, p->x, p->y); + + offset = p->y * fb_stride + p->x*sizeof(int); + *((unsigned int *)((unsigned char *)fb_ptr + offset)) = c; +} + +void +draw_pixel_16(point_t *p, color_t c) +{ + unsigned int offset; + + if (p->x >= fb_max_x || p->y >= fb_max_y) + printf("%s: point [%u:%u] out of range\n", __FUNCTION__, p->x, p->y); + + offset = p->y * fb_stride + p->x*sizeof(short); + *((unsigned short *)((unsigned char *)fb_ptr + offset)) = c; +} + +void +draw_pixel_8(point_t *p, color_t c) +{ + unsigned int offset; + + if (p->x >= fb_max_x || p->y >= fb_max_y) + printf("%s: point [%u:%u] out of range\n", __FUNCTION__, p->x, p->y); + + offset = p->y * fb_stride + p->x*sizeof(char); + *((unsigned char *)((unsigned char *)fb_ptr + offset)) = c; +} + +void (*draw_pixel)(point_t *p, color_t c); + +void draw_rect(point_t *p1, point_t *p2, color_t c, int fill) +{ + point_t p; + + if (fill) { + for (p.y = p1->y; p.y <= p2->y; ++p.y) + for (p.x = p1->x; p.x <= p2->x; ++p.x) + (*draw_pixel)(&p, c); + } else { + for (p.x = p1->x; p.x <= p2->x; ++p.x) { + p.y = p1->y; + (*draw_pixel)(&p, c); + p.y = p2->y; + (*draw_pixel)(&p, c); + } + for (p.y = p1->y; p.y <= p2->y; ++p.y) { + p.x = p1->x; + (*draw_pixel)(&p, c); + p.x = p2->x; + (*draw_pixel)(&p, c); + } + } +} + +void draw_test_frame(int x, int y, int margin, color_t c) +{ + point_t start, end; + + start.x = margin; + end.x = x - margin - 1; + start.y = margin; + end.y = y - margin - 1; + + draw_rect(&start, &end, c, 0); +} + +void +clear_video_frame(void) +{ + memset(fb_ptr, 0, fb_size); +} + +/* Draw a ramp, [l,r] to [l+w,r+h], in color *color */ +void draw_ramp (point_t *start, point_t *end, struct fb_bitfield *fb_bits, struct fb_var_screeninfo *fb_var) +{ + point_t ul, br; + int i, width_round_up; + int colors; + + ul = *start; + br = *end; + + colors = 1<<fb_bits->length; + width_round_up = (end->x % colors) ? 1 : 0; + + for (i=0; i<colors; ++i) { + draw_rect(&ul, &br, i<<fb_bits->offset, 1); + ul.x = br.x; + br.x += fb_var->xres / colors + ((i % 2) * width_round_up); + if (br.x >= fb_var->xres) + br.x = fb_var->xres-1; + } +} + +void scribble_frame_buffer(void) +{ + unsigned int x,y; + color_t color_white, color_black, color_blue, color_red; + unsigned int colorbitwidth, width_round_up; + point_t start, end; + + if (fb_var.bits_per_pixel == 8) + draw_pixel = draw_pixel_8; + else if (fb_var.bits_per_pixel == 16) + draw_pixel = draw_pixel_16; + else + draw_pixel = draw_pixel_32; + + printf("%s:%d draw_pixel %p\n", __FUNCTION__, __LINE__, draw_pixel); + + // white is all bits on + color_white = (((1<<fb_var.red.length)-1) << fb_var.red.offset) + | (((1<<fb_var.green.length)-1) << fb_var.green.offset) + | (((1<<fb_var.blue.length)-1) << fb_var.blue.offset); + + // black is all bits off + color_black = 0; + + color_blue = (((1 << fb_var.blue.length) - 1) << fb_var.blue.offset); + + color_red = (((1 << fb_var.red.length) - 1) << fb_var.red.offset); + + + + x = fb_var.xres; + y = fb_var.yres; + colorbitwidth = 32; + + start.x = 0; + width_round_up = (x % colorbitwidth) ? 1 : 0; + end.x = x / colorbitwidth + width_round_up; + + end.y = y/4; + start.y = 0; + draw_ramp(&start, &end, &fb_var.red, &fb_var); + start.y = end.y; + end.y += y/4; + draw_ramp(&start, &end, &fb_var.green, &fb_var); + start.y = end.y; + end.y += y/4; + draw_ramp(&start, &end, &fb_var.blue, &fb_var); + /* draw stipple, stop test when error is encountered */ + for (start.y = 3 * (y / 4); start.y < y; start.y++) { + for (start.x = 0; start.x < (x / 3); start.x++) { + draw_rect(&start, &start, + (start.x ^ start.y) & 1 ? color_white : color_black, + 0); + } + } + + /* draw vert-lines, stop test when error is encountered */ + start.y = 3 * (y / 4); + end.y = y-1; + for (start.x = x / 3; start.x < 2 * (x / 3); start.x++) { + end.x = start.x; + draw_rect(&start, &end, + start.x & 1 ? color_white : color_black, 0); + } + + /* draw horiz-lines, stop test when error is encountered */ + start.x = 2 * (x / 3); + end.x = x-1; + for (start.y = 3 * (y / 4); start.y < y; start.y++) { + end.y = start.y; + draw_rect(&start, &end, + start.y & 1 ? color_white : color_black, 0); + } + + + // Draw some frames + draw_test_frame(x, y, 0, color_white); + draw_test_frame(x, y, 1, color_red); + draw_test_frame(x, y, 2, color_blue); +} + +int do_draw_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + if (panel_info.vl_col && panel_info.vl_row) { + fb_var.xres = panel_info.vl_col; + fb_var.yres = panel_info.vl_row; + if (panel_info.vl_bpix == LCD_COLOR16) { + fb_var.bits_per_pixel = 16; + fb_var.red.offset = 11; + fb_var.red.length = 5; + fb_var.blue.offset = 5; + fb_var.blue.length = 6; + fb_var.green.offset = 0; + fb_var.green.length = 5; + } else if (panel_info.vl_bpix == LCD_COLOR24) { + fb_var.bits_per_pixel = 32; + fb_var.red.offset = 16; + fb_var.red.length = 8; + fb_var.blue.offset = 8; + fb_var.blue.length = 8; + fb_var.green.offset = 0; + fb_var.green.length = 8; + } + fb_var.line_length = fb_var.xres * (fb_var.bits_per_pixel / 8); + fb_ptr = (void *)gd->fb_base; + fb_stride = fb_var.line_length; + fb_size = fb_stride * fb_var.yres; + fb_max_x = fb_var.xres; + fb_max_y = fb_var.yres; + scribble_frame_buffer(); + } + return 0; +} + +U_BOOT_CMD(draw_test, 1, 1, do_draw_test, + " - Draw ramps/stipples/boarders on LCD", + "" +); diff --git a/board/ti/logic/logic-gpio.c b/board/ti/logic/logic-gpio.c new file mode 100644 index 00000000000..1b631839788 --- /dev/null +++ b/board/ti/logic/logic-gpio.c @@ -0,0 +1,161 @@ +/* + * (C) Copyright 2008 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <asm/arch/cpu.h> +#include <asm/io.h> +// #include <asm/arch/bits.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +// #include <asm/arch/sys_info.h> +#include <asm/arch/clocks.h> +#include <asm/arch/mem.h> +#include <i2c.h> +#include <asm/mach-types.h> +#include "logic-gpio.h" + +#define NUM_OF_BITS_IN_REG 32 +#define MAX_GPIO_PINS 192 +#define GPIO_PIN 1 +#define GPIO_MAX_MODULES 6 + +/* GPIO base address */ +#define GPIO1_MODULE_BA 0x48310000 +#define GPIO2_MODULE_BA 0x49050000 +#define GPIO3_MODULE_BA 0x49052000 +#define GPIO4_MODULE_BA 0x49054000 +#define GPIO5_MODULE_BA 0x49056000 +#define GPIO6_MODULE_BA 0x49058000 + +#define GPIO_DATAIN ((uint32_t)0x038) +#define GPIO_DATAOUT ((uint32_t)0x03C) +#define GPIO_OE ((uint32_t)0x034) + +/* int and long both fit to 32 bits */ +typedef volatile uint32_t* PREG_U32; +typedef volatile int32_t* PREG_S32; + +#define in_regl(offSet) (*(PREG_U32)(offSet)) +#define out_regl(offSet, value) (*(PREG_U32)(offSet) = (uint32_t)(value)) + +static uint32_t g_gpio_module_base_address[GPIO_MAX_MODULES] + = {GPIO1_MODULE_BA, GPIO2_MODULE_BA, GPIO3_MODULE_BA, + GPIO4_MODULE_BA, GPIO5_MODULE_BA, GPIO6_MODULE_BA}; + +uint32_t check_gpio_pin_num(uint32_t pin_num) +{ + return (pin_num > MAX_GPIO_PINS); +} + +uint32_t get_module_pin_mask(uint32_t pin_num, uint32_t *module_num, uint32_t *offset, uint32_t *pinmask) +{ + uint32_t snum, ret_val; + + *module_num = pin_num / NUM_OF_BITS_IN_REG + 1; + snum = (*module_num-1)*NUM_OF_BITS_IN_REG; + *offset = pin_num - snum; + ret_val = check_gpio_pin_num(pin_num); + if (ret_val) + return ret_val; + + *pinmask = GPIO_PIN<<*offset; + return 0; +} + + +void gpio_write_output_pin(int module_num, uint32_t pin_mask, uint32_t data) +{ + uint32_t temp, gpio_data_out_reg; + + gpio_data_out_reg = (g_gpio_module_base_address[module_num-1]+GPIO_DATAOUT); + + temp = in_regl(gpio_data_out_reg); + temp = temp & ~pin_mask; + + out_regl(gpio_data_out_reg, (temp | (data & pin_mask))); +} + +void gpio_read_input_pin(uint32_t module_num, uint32_t pin_mask, uint32_t *data) +{ + uint32_t gpio_data_in_reg, temp; + + gpio_data_in_reg = (g_gpio_module_base_address[module_num-1]+GPIO_DATAIN); + + temp = in_regl(gpio_data_in_reg); + *data = temp & pin_mask; +} + +uint32_t pin_get_gpio_input(uint32_t pin_num) +{ + uint32_t module_num, pinmask, offset, data; + + get_module_pin_mask(pin_num, &module_num, &offset, &pinmask); + + gpio_read_input_pin(module_num, (1<<offset), &data); + data >>= offset; + + // printf("%s:%d pin %d data %d\n", __FUNCTION__, __LINE__, pin_num, data); + + return data; +} + + +uint32_t pin_set_gpio_dataout(uint32_t pin_num, uint32_t set) +{ + uint32_t module_num, pinmask, offset, ret_val; + + // printf("%s:%d pin %d set %d\n", __FUNCTION__, __LINE__, pin_num, set); + + ret_val = get_module_pin_mask(pin_num, &module_num, &offset, &pinmask); + + if (set) + gpio_write_output_pin(module_num, (1<<offset), (1<<offset)); + else + gpio_write_output_pin(module_num, (1<<offset), (0<<offset)); + + return ret_val; +} + +uint32_t set_gpio_in_out(uint32_t module_num, uint32_t pin_mask, uint32_t io_mask) +{ + uint32_t temp_oe, gpio_pin_output_en_reg; + + gpio_pin_output_en_reg = (g_gpio_module_base_address[module_num-1]+GPIO_OE); + + temp_oe = in_regl(gpio_pin_output_en_reg); + temp_oe &= ~pin_mask; + temp_oe |= io_mask; + + out_regl(gpio_pin_output_en_reg, temp_oe); + + return 0; +} + +uint32_t pin_init_gpio(uint32_t pin_num, uint32_t in_out) +{ + uint32_t module_num, pinmask, offset, ret_val; + + ret_val = get_module_pin_mask(pin_num, &module_num, &offset, &pinmask); + + set_gpio_in_out(module_num, pinmask, in_out<<offset); + + return ret_val; +} diff --git a/board/ti/logic/logic-gpio.h b/board/ti/logic/logic-gpio.h new file mode 100644 index 00000000000..fca023f93fb --- /dev/null +++ b/board/ti/logic/logic-gpio.h @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 + */ + +// GPIO +extern unsigned int pin_get_gpio_input(unsigned int pin); +extern unsigned int pin_set_gpio_dataout(unsigned int pin, unsigned int set); +extern unsigned int pin_init_gpio(unsigned int pin_num, unsigned int in_out); + +// Turn on VAUX1 voltage for Product ID +extern void init_vaux1_voltage(void); diff --git a/board/ti/logic/logic-i2c.c b/board/ti/logic/logic-i2c.c new file mode 100644 index 00000000000..0ff463afcfc --- /dev/null +++ b/board/ti/logic/logic-i2c.c @@ -0,0 +1,242 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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> +#include <command.h> +#include <asm/arch/cpu.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/clocks.h> +#include <asm/arch/mem.h> +#include <i2c.h> +#include <asm/mach-types.h> +#include "logic-i2c.h" +#include "product_id.h" +#include "logic-gpio.h" +#include "logic-data.h" + +static enum { + GPIO_I2C_UNINIT, + GPIO_I2C_STOPPED, + GPIO_I2C_STARTED, +} gpio_i2c_bus_state; + +static int gpio_i2c_clock_high_width, gpio_i2c_clock_low_width; +static int gpio_i2c_coarse_delay; + +/* + * IEN - Input Enable + * IDIS - Input Disable + * PTD - Pull type Down + * PTU - Pull type Up + * DIS - Pull type selection is inactive + * EN - Pull type selection is active + * M0 - Mode 0 + * The commented string gives the final mux configuration for that pin + */ + +/* Put SCLK/SDA pins connected to the product ID into GPIO mode */ +static void gpio_i2c_config_pins(void) +{ + MUX_VAL(CP(I2C3_SCL), (IEN | PTU | EN | M4)); /*I2C3_SCL*/ + MUX_VAL(CP(I2C3_SDA), (IEN | PTU | EN | M4)); /*I2C3_SDA*/ +} + +/* Restore SCLK/SDA pins connected to the product ID back to I2C mode */ + +void gpio_i2c_restore_pins(void) +{ + MUX_VAL(CP(I2C3_SCL), (IEN | PTU | EN | M0)); /*I2C3_SCL*/ + MUX_VAL(CP(I2C3_SDA), (IEN | PTU | EN | M0)); /*I2C3_SDA*/ +} + +#define GPIO_I2C_GPIO_SCLK 184 +#define GPIO_I2C_GPIO_SDATA 185 + +void gpio_i2c_config_pin(GPIO_I2C_PIN pin, GPIO_I2C_DIRECTION dir) +{ + if (dir == GPIO_I2C_INPUT) { + if (pin == GPIO_I2C_SCLK) + pin_init_gpio(GPIO_I2C_GPIO_SCLK, 1); + else + pin_init_gpio(GPIO_I2C_GPIO_SDATA, 1); + } else if (dir == GPIO_I2C_OUTPUT) { + if (pin == GPIO_I2C_SCLK) + pin_init_gpio(GPIO_I2C_GPIO_SCLK, 0); + else + pin_init_gpio(GPIO_I2C_GPIO_SDATA, 0); + } +} + +static int gpio_i2c_read_pin(GPIO_I2C_PIN pin) +{ + if (pin == GPIO_I2C_SCLK) + return pin_get_gpio_input(GPIO_I2C_GPIO_SCLK); + else + return pin_get_gpio_input(GPIO_I2C_GPIO_SDATA); + return 0; +} + +void gpio_i2c_set_pin_level(GPIO_I2C_PIN pin, int level) +{ + uint8_t pin_level; + + if (pin == GPIO_I2C_SCLK) { + pin_level = pin_get_gpio_input(GPIO_I2C_GPIO_SCLK); + if (((level == 1) && (pin_level == 0)) || + ((level == 0) && (pin_level == 1))) + pin_set_gpio_dataout(GPIO_I2C_GPIO_SCLK, level); + } else if (pin == GPIO_I2C_SDATA) { + if (level == 0) { + gpio_i2c_config_pin(pin, GPIO_I2C_OUTPUT); + pin_set_gpio_dataout(GPIO_I2C_GPIO_SDATA, 0); + } else { + gpio_i2c_config_pin(pin, GPIO_I2C_INPUT); + } + } +} + + +void gpio_i2c_init(int bps) +{ + gpio_i2c_bus_state = GPIO_I2C_UNINIT; + + /* Config SCLK, SDATA pins */ + gpio_i2c_config_pin(GPIO_I2C_SCLK, GPIO_I2C_OUTPUT); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + + gpio_i2c_config_pin(GPIO_I2C_SDATA, GPIO_I2C_INPUT); + + gpio_i2c_config_pins(); + + /* Assume 1:1 clock duty cycle */ + gpio_i2c_clock_high_width = gpio_i2c_clock_low_width + = 1000000 / bps / 2; + + gpio_i2c_coarse_delay = gpio_i2c_clock_high_width; +} + +int gpio_i2c_busy(void) +{ + return (gpio_i2c_bus_state == GPIO_I2C_STARTED); +} + +void gpio_i2c_tx_stop(void) +{ + if (gpio_i2c_bus_state == GPIO_I2C_STARTED) { + udelay(gpio_i2c_coarse_delay); + + /* Pull SDATA low */ + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 0); + udelay(gpio_i2c_coarse_delay); + + /* Push SCLK high */ + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_coarse_delay); + + /* Now drive SDATA high - thats a STOP. */ + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 1); + udelay(gpio_i2c_coarse_delay); + gpio_i2c_bus_state = GPIO_I2C_STOPPED; + } +} + +void gpio_i2c_tx_start(void) +{ + if (gpio_i2c_bus_state == GPIO_I2C_UNINIT + || gpio_i2c_bus_state == GPIO_I2C_STOPPED) { + udelay(gpio_i2c_coarse_delay); + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 1); + udelay(gpio_i2c_coarse_delay); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_coarse_delay); + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 0); + udelay(gpio_i2c_coarse_delay); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + udelay(gpio_i2c_coarse_delay); + gpio_i2c_bus_state = GPIO_I2C_STARTED; + } +} + +/* Return !0 if NACK */ +int gpio_i2c_tx_byte(uint8_t data) +{ + uint8_t clock, tx_bit_mask=0x80, nack; + + if (gpio_i2c_bus_state != GPIO_I2C_STARTED) + printf("%s: Unexpected I2C bus state!\n", __FUNCTION__); + + udelay(gpio_i2c_coarse_delay); + + for (clock=0; clock <= 7; ++clock) { + if (data & tx_bit_mask) + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 1); + else + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 0); + udelay(gpio_i2c_clock_low_width); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_clock_high_width); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + tx_bit_mask >>= 1; + } + gpio_i2c_config_pin(GPIO_I2C_SDATA, GPIO_I2C_INPUT); + udelay(gpio_i2c_clock_low_width); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_clock_high_width); + nack = gpio_i2c_read_pin(GPIO_I2C_SDATA); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + return (nack != 0); +} + +int gpio_i2c_rx_byte(uint8_t *data, int rx_mode) +{ + uint8_t clock, data_bit; + + *data = 0; + + gpio_i2c_config_pin(GPIO_I2C_SDATA, GPIO_I2C_INPUT); + + udelay(gpio_i2c_coarse_delay); + + for (clock=0; clock<=8; ++clock) { + if (clock < 8) { + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_clock_high_width); + data_bit = gpio_i2c_read_pin(GPIO_I2C_SDATA); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + *data = (*data << 1) | data_bit; + } else { + if ((rx_mode == RX_MODE_LAST_BYTE) || (rx_mode == RX_MODE_ONE_BYTE)) + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 1); + else + gpio_i2c_set_pin_level(GPIO_I2C_SDATA, 0); + + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 1); + udelay(gpio_i2c_clock_high_width); + gpio_i2c_set_pin_level(GPIO_I2C_SCLK, 0); + } + udelay(gpio_i2c_clock_low_width); + } + + return 0; +} + diff --git a/board/ti/logic/logic-i2c.h b/board/ti/logic/logic-i2c.h new file mode 100644 index 00000000000..bd1f01aea01 --- /dev/null +++ b/board/ti/logic/logic-i2c.h @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2008-2011 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 + */ + +typedef enum { + GPIO_I2C_INPUT, + GPIO_I2C_OUTPUT, +} GPIO_I2C_DIRECTION; + +typedef enum { + RX_MODE_FIRST_BYTE, + RX_MODE_MIDDLE_BYTE, + RX_MODE_NEXT_TO_LAST_BYTE, + RX_MODE_LAST_BYTE, + RX_MODE_ONE_BYTE +} I2C_RX_MODE; + +typedef enum { + GPIO_I2C_SDATA, + GPIO_I2C_SCLK, +} GPIO_I2C_PIN; + +extern int gpio_i2c_busy(void); +extern void gpio_i2c_tx_stop(void); +extern void gpio_i2c_tx_start(void); +extern int gpio_i2c_tx_byte(uint8_t data); +extern int gpio_i2c_rx_byte(uint8_t *data, int rx_mode); +extern void gpio_i2c_init(int bps); +extern void gpio_i2c_config_pin(GPIO_I2C_PIN pin, GPIO_I2C_DIRECTION dir); +extern void gpio_i2c_set_pin_level(GPIO_I2C_PIN pin, int level); +extern void gpio_i2c_restore_pins(void); diff --git a/board/ti/logic/logic-id-data.h b/board/ti/logic/logic-id-data.h new file mode 100644 index 00000000000..73a7ddaa369 --- /dev/null +++ b/board/ti/logic/logic-id-data.h @@ -0,0 +1,156 @@ +const unsigned char id_data_buf[] /* __attribute__ ((section ("id_data"))) */ = { +/* ID data header: */ +/* offset 0 */ 0x4c, 0x70, 0x49, 0x64, /* Magic key "LpId" */ +/* offset 4 */ 0x04, /* (0x04) one-byte version of id format */ +/* offset 5 */ 0x00, /* (0x00) unused byte */ +/* offset 6 */ 0x94, 0x02, /* (660) LE two-byte length of data following header */ + +/* ID checksums: */ +/* offset 8 */ 0xf1, 0x4c, /* (0x4cf1) LE CRC-15 of data header */ +/* offset 10 */ 0x0f, 0x29, /* (0x290f) LE CRC-15 of following 660 ID data bytes */ + +/* ID data: */ +/* offset 12 */ 0xa0|0x12, 0x29, /* (658) dict; size */ +/* offset 14 */ 0xc0|0x00, /* (0) key 'serialization_group' */ +/* offset 15 */ 0xa0|0x19, 0x1e, /* (489) dict; size */ +/* offset 17 */ 0xc0|0x01, /* (1) key 'serial_number' */ +/* offset 18 */ 0x40|0x0a, /* (10) string; len */ +/* offset 19 */ 0x31, 0x31, 0x31, 0x31, 0x4d, 0x30, 0x31, 0x38, +/* offset 27 */ 0x32, 0x35, +/* offset 29 */ 0xc0|0x02, /* (2) key 'lan_ethaddr1' */ +/* offset 30 */ 0x60|0x06, /* (6) string; len */ +/* offset 31 */ 0x00, 0x08, 0xee, 0x04, 0xb7, 0xd2, +/* offset 37 */ 0xc0|0x06, /* (6) key 'nvs' */ +/* offset 38 */ 0x60|0x12, 0x1d, /* (466) string; len */ +/* offset 40 */ 0x01, 0x6d, 0x54, 0x00, 0x00, 0xef, 0xbe, 0x01, +/* offset 48 */ 0x71, 0x54, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, +/* offset 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* offset 64 */ 0x01, 0x99, 0x01, 0xfa, 0xd6, 0xd0, 0xca, 0xc2, +/* offset 72 */ 0xc9, 0xd6, 0xda, 0x05, 0x00, 0xf8, 0xfc, 0x0c, +/* offset 80 */ 0x04, 0x00, 0xfb, 0x0c, 0x04, 0x00, 0xfc, 0x0a, +/* offset 88 */ 0x03, 0xff, 0xfa, 0x0b, 0x02, 0xfe, 0xfb, 0x10, +/* offset 96 */ 0x04, 0xff, 0xfa, 0x12, 0x04, 0x00, 0xfa, 0x12, +/* offset 104 */ 0x05, 0x00, 0xfb, 0xea, 0xfc, 0x09, 0x09, 0x0c, +/* offset 112 */ 0x0c, 0x0e, 0x11, 0x13, 0x17, 0x1a, 0x1d, 0x21, +/* offset 120 */ 0x26, 0x2c, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3d, +/* offset 128 */ 0x00, 0x41, 0x00, 0x46, 0x00, 0x4c, 0x00, 0x4f, +/* offset 136 */ 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x40, +/* offset 144 */ 0x00, 0x38, 0x00, 0xe9, 0xfc, 0x07, 0x08, 0x09, +/* offset 152 */ 0x0b, 0x0a, 0x0e, 0x10, 0x13, 0x16, 0x19, 0x1d, +/* offset 160 */ 0x1f, 0x23, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x31, +/* offset 168 */ 0x00, 0x37, 0x00, 0x3d, 0x00, 0x44, 0x00, 0x4e, +/* offset 176 */ 0x00, 0x55, 0x00, 0x5f, 0x00, 0x67, 0x00, 0x6c, +/* offset 184 */ 0x00, 0x6d, 0x00, 0xeb, 0xfc, 0x07, 0x09, 0x0a, +/* offset 192 */ 0x0b, 0x0a, 0x0f, 0x11, 0x14, 0x18, 0x1a, 0x1d, +/* offset 200 */ 0x21, 0x25, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x33, +/* offset 208 */ 0x00, 0x39, 0x00, 0x3f, 0x00, 0x46, 0x00, 0x4d, +/* offset 216 */ 0x00, 0x59, 0x00, 0x5d, 0x00, 0x64, 0x00, 0x6a, +/* offset 224 */ 0x00, 0x69, 0x00, 0xee, 0xfc, 0x08, 0x09, 0x0a, +/* offset 232 */ 0x0e, 0x10, 0x12, 0x15, 0x18, 0x1d, 0x20, 0x23, +/* offset 240 */ 0x26, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x3a, 0x00, +/* offset 248 */ 0x3d, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x57, 0x00, +/* offset 256 */ 0x5e, 0x00, 0x6c, 0x00, 0x70, 0x00, 0x72, 0x00, +/* offset 264 */ 0x6c, 0x00, 0xef, 0xfc, 0x08, 0x09, 0x0b, 0x0a, +/* offset 272 */ 0x0e, 0x10, 0x14, 0x15, 0x18, 0x1e, 0x20, 0x23, +/* offset 280 */ 0x2a, 0x00, 0x2b, 0x00, 0x32, 0x00, 0x3c, 0x00, +/* offset 288 */ 0x3e, 0x00, 0x48, 0x00, 0x51, 0x00, 0x5b, 0x00, +/* offset 296 */ 0x63, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, +/* offset 304 */ 0x72, 0x00, 0xf2, 0xfc, 0x08, 0x0a, 0x0b, 0x0a, +/* offset 312 */ 0x10, 0x11, 0x14, 0x17, 0x1a, 0x1e, 0x21, 0x25, +/* offset 320 */ 0x29, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x3b, 0x00, +/* offset 328 */ 0x3f, 0x00, 0x49, 0x00, 0x4f, 0x00, 0x5a, 0x00, +/* offset 336 */ 0x63, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, +/* offset 344 */ 0x77, 0x00, 0xf1, 0xfc, 0x08, 0x0a, 0x0c, 0x0c, +/* offset 352 */ 0x0f, 0x11, 0x14, 0x16, 0x1c, 0x1c, 0x21, 0x25, +/* offset 360 */ 0x29, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x3b, 0x00, +/* offset 368 */ 0x41, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x5e, 0x00, +/* offset 376 */ 0x69, 0x00, 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, +/* offset 384 */ 0x84, 0x00, 0xef, 0xfc, 0x07, 0x0b, 0x0a, 0x0e, +/* offset 392 */ 0x10, 0x13, 0x16, 0x19, 0x1d, 0x1f, 0x23, 0x29, +/* offset 400 */ 0x00, 0x2d, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x40, +/* offset 408 */ 0x00, 0x4a, 0x00, 0x54, 0x00, 0x62, 0x00, 0x6e, +/* offset 416 */ 0x00, 0x76, 0x00, 0x81, 0x00, 0x81, 0x00, 0x8c, +/* offset 424 */ 0x00, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x00, +/* offset 432 */ 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x0a, 0xfe, +/* offset 440 */ 0x00, 0x01, 0x01, 0xff, 0x00, 0x01, 0xff, 0xff, +/* offset 448 */ 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0xfe, 0x00, +/* offset 456 */ 0x00, 0x01, 0x09, 0x04, 0x00, 0xfb, 0xf6, 0x04, +/* offset 464 */ 0x02, 0x00, 0xfe, 0xfd, 0xfc, 0x03, 0x02, 0x00, +/* offset 472 */ 0xfd, 0xfb, 0x02, 0x13, 0x00, 0x00, 0x00, 0x00, +/* offset 480 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* offset 488 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* offset 496 */ 0xaa, 0x03, 0x00, 0x00, 0x01, 0x02, 0xff, 0xff, +/* offset 504 */ 0x00, 0x00, +/* offset 506 */ 0xc0|0x07, /* (7) key 'model_group' */ +/* offset 507 */ 0xa0|0x15, 0x02, /* (37) dict; size */ +/* offset 509 */ 0xc0|0x08, /* (8) key 'model_name' */ +/* offset 510 */ 0x40|0x17, 0x01, /* (23) string; len */ +/* offset 512 */ 0x53, 0x4f, 0x4d, 0x44, 0x4d, 0x33, 0x37, 0x33, +/* offset 520 */ 0x30, 0x2d, 0x31, 0x30, 0x2d, 0x32, 0x37, 0x38, +/* offset 528 */ 0x32, 0x49, 0x46, 0x43, 0x52, 0x2d, 0x41, +/* offset 535 */ 0xc0|0x09, /* (9) key 'part_number' */ +/* offset 536 */ 0x20|0x16, 0xde, 0xf0, 0x03, /* (0xf85e6) +num */ +/* offset 540 */ 0xc0|0x0a, /* (10) key 'version_code' */ +/* offset 541 */ 0x20|0x1e, 0x01, /* (0x1e) +num */ +/* offset 543 */ 0xc0|0x0b, /* (11) key 'hardware_platform' */ +/* offset 544 */ 0x40|0x01, /* (1) string; len */ +/* offset 545 */ 0x74, +/* offset 546 */ 0xc0|0x0c, /* (12) key 'cpu0_group' */ +/* offset 547 */ 0xa0|0x0f, /* (15) dict; size */ +/* offset 548 */ 0xc0|0x0d, /* (13) key 'type' */ +/* offset 549 */ 0x40|0x02, /* (2) string; len */ +/* offset 550 */ 0x64, 0x6d, +/* offset 552 */ 0xc0|0x0e, /* (14) key 'number' */ +/* offset 553 */ 0x20|0x12, 0xe9, 0x01, /* (0xe92) +num */ +/* offset 556 */ 0xc0|0x0f, /* (15) key 'speed_mhz' */ +/* offset 557 */ 0x20|0x18, 0x3e, /* (0x3e8) +num */ +/* offset 559 */ 0xc0|0x10, 0x01, /* (16) key 'temp_class' */ +/* offset 561 */ 0x40|0x01, /* (1) string; len */ +/* offset 562 */ 0x63, +/* offset 563 */ 0xc0|0x11, 0x01, /* (17) key 'cpu0_bus_group' */ +/* offset 565 */ 0xa0|0x19, 0x06, /* (105) dict; size */ +/* offset 567 */ 0xc0|0x12, 0x01, /* (18) key 'dram_bus_group' */ +/* offset 569 */ 0xa0|0x16, 0x03, /* (54) dict; size */ +/* offset 571 */ 0xc0|0x13, 0x01, /* (19) key 'sysconfig_reg' */ +/* offset 573 */ 0x20|0x12, 0x01, /* (0x12) +num */ +/* offset 575 */ 0xc0|0x14, 0x01, /* (20) key 'sharing_reg' */ +/* offset 577 */ 0x20|0x10, 0x10, /* (0x100) +num */ +/* offset 579 */ 0xc0|0x1d, 0x01, /* (29) key 'power_reg' */ +/* offset 581 */ 0x20|0x09, /* (0x9) +num */ +/* offset 582 */ 0xc0|0x16, 0x01, /* (22) key 'cs_cfg_reg' */ +/* offset 584 */ 0x20|0x02, /* (0x2) +num */ +/* offset 585 */ 0xc0|0x1f, 0x01, /* (31) key 'cs0_group' */ +/* offset 587 */ 0xa0|0x14, 0x02, /* (36) dict; size */ +/* offset 589 */ 0xc0|0x17, 0x01, /* (23) key 'mcfg_reg' */ +/* offset 591 */ 0x20|0x19, 0x81, 0x90, 0xd6, 0x01, /* (0x3588019) +num */ +/* offset 596 */ 0xc0|0x18, 0x01, /* (24) key 'mr_reg' */ +/* offset 598 */ 0x20|0x12, 0x03, /* (0x32) +num */ +/* offset 600 */ 0xc0|0x19, 0x01, /* (25) key 'rfr_ctrl_reg' */ +/* offset 602 */ 0x20|0x11, 0xf0, 0x92, 0x01, /* (0x49701) +num */ +/* offset 606 */ 0xc0|0x1a, 0x01, /* (26) key 'emr2_reg' */ +/* offset 608 */ 0x20|0x10, 0x02, /* (0x20) +num */ +/* offset 610 */ 0xc0|0x1b, 0x01, /* (27) key 'actim_ctrla_reg' */ +/* offset 612 */ 0x20|0x17, 0xf0, 0xc8, 0xc9, 0x41, /* (0x83264707) +num */ +/* offset 617 */ 0xc0|0x1c, 0x01, /* (28) key 'actim_ctrlb_reg' */ +/* offset 619 */ 0x20|0x13, 0xe2, 0x42, /* (0x21623) +num */ +/* offset 622 */ 0xc0|0x15, 0x01, /* (21) key 'dlla_ctrl_reg' */ +/* offset 624 */ 0x20|0x08, /* (0x8) +num */ +/* offset 625 */ 0xc0|0x1e, 0x01, /* (30) key 'local_bus_group' */ +/* offset 627 */ 0xa0|0x1b, 0x02, /* (43) dict; size */ +/* offset 629 */ 0xc0|0x1f, 0x01, /* (31) key 'cs0_group' */ +/* offset 631 */ 0xa0|0x17, 0x02, /* (39) dict; size */ +/* offset 633 */ 0xc0|0x16, 0x02, /* (38) key 'config1_reg' */ +/* offset 635 */ 0x20|0x10, 0x80, 0x03, /* (0x1800) +num */ +/* offset 638 */ 0xc0|0x17, 0x02, /* (39) key 'config2_reg' */ +/* offset 640 */ 0x20|0x10, 0x90, 0xa1, 0x02, /* (0x90900) +num */ +/* offset 644 */ 0xc0|0x18, 0x02, /* (40) key 'config3_reg' */ +/* offset 646 */ 0x20|0x10, 0x90, 0xa1, 0x02, /* (0x90900) +num */ +/* offset 650 */ 0xc0|0x19, 0x02, /* (41) key 'config4_reg' */ +/* offset 652 */ 0x20|0x10, 0xd0, 0x80, 0x80, 0x03, /* (0x6000500) +num */ +/* offset 657 */ 0xc0|0x1a, 0x02, /* (42) key 'config5_reg' */ +/* offset 659 */ 0x20|0x1a, 0x80, 0xe1, 0x01, /* (0x7080a) +num */ +/* offset 663 */ 0xc0|0x1b, 0x02, /* (43) key 'config6_reg' */ +/* offset 665 */ 0x20|0x1f, 0x2c, /* (0x2cf) +num */ +/* offset 667 */ 0xc0|0x1c, 0x02, /* (44) key 'config7_reg' */ +/* offset 669 */ 0x20|0x1f, 0xf5, 0x01, /* (0xf5f) +num */ +}; diff --git a/board/ti/logic/logic-product-id.c b/board/ti/logic/logic-product-id.c new file mode 100644 index 00000000000..4f427f94b63 --- /dev/null +++ b/board/ti/logic/logic-product-id.c @@ -0,0 +1,285 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.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 + */ + +/* Code to extract x-loader perntinent data from product ID chip */ + +#include <common.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/mem.h> + +// #include "dm3730logic-ddr.h" +// #include "dm3730logic-product-id.h" + +#include "prod-id/interface.h" +#include "prod-id/id-errno.h" +#include "logic-i2c.h" +#include "logic-id-data.h" +#include "logic-at24.h" +#include "logic-gpio.h" + +struct id_data id_data; + +/* Fetch a byte of data from the ID data on the i2c bus */ +unsigned char id_fetch_byte(unsigned char *mem_ptr, int offset, int *oor) +{ + unsigned char val; + + /* If data is off end of known size then complain */ + if (id_data.root_size && (offset >= (id_data.root_offset + id_data.root_size))) { + id_printf("Attempt to read past end of buffer (offset %u >= size %u)\n", offset, sizeof(id_data_buf)); + *oor = -ID_ERANGE; + return 0; /* Force upper layer to recover */ + } + + *oor = ID_EOK; + + if (mem_ptr) { + val = mem_ptr[offset]; + return val; + } else { + if (at24_read(offset, &val, 1) == 0) { + return val; + } + } + + *oor = ID_ENODEV; + return 0; +} + +int id_printf(const char *fmt, ...) +{ + va_list args; + char printbuffer[256]; + + va_start (args, fmt); + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + vsprintf (printbuffer, fmt, args); + va_end (args); + /* Print the string */ + serial_puts (printbuffer); + + return 0; +} + +void id_error(const char *fmt, ...) +{ + va_list args; + char printbuffer[256]; + + va_start (args, fmt); + /* For this to work, printbuffer must be larger than + * anything we ever want to print. + */ + vsprintf (printbuffer, fmt, args); + va_end (args); + /* Print the string */ + serial_puts (printbuffer); + +} + +static int found_id_data; +/* Initialize the product ID data and return 0 if found */ +static int product_id_init(void) +{ + int ret; + + memset(&id_data, 0, sizeof(id_data)); + + /* id_data.mem_ptr is an address top copy the ID data from the AT24 + * chip into during startup(since startup codehas to CRC whole data + * area). Once its read there we can access the copy instead of + * going back to the AT24 to read the data. */ + id_data.mem_ptr = (void *)SRAM_BASE; + + ret=at24_wakeup(); + if(ret) { + printf("wakeup_err=%d\n", ret); + } + + ret = id_startup(&id_data); + + + at24_shutdown(); + + if (ret != ID_EOK) { + return -1; + } + + return 0; +} + +int logic_has_new_product_id(void) +{ + if (!found_id_data) { + if (!product_id_init()) { + found_id_data = 1; + } + } + return found_id_data; +} + + +int logic_dump_serialization_info(void) +{ + int ret; + struct id_cookie cookie; + int part_number; + u8 model_name[32]; + u32 model_name_size; + u8 serial_number[10]; + u32 serial_number_size; + + if (!found_id_data) { + return -1; + } + + ret = id_init_cookie(&id_data, &cookie); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* find /serialization_group from root */ + ret = id_find_dict(&cookie, ID_KEY_serialization_group, IDENUM_DICT); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* Find serial_number */ + serial_number_size = sizeof(serial_number); + ret = id_find_string(&cookie, ID_KEY_serial_number, serial_number, &serial_number_size); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* Reinitialise cookie back to the root */ + ret = id_init_cookie(&id_data, &cookie); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* find /model_group from root */ + ret = id_find_dict(&cookie, ID_KEY_model_group, IDENUM_DICT); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* Find part number */ + ret = id_find_number(&cookie, ID_KEY_part_number, &part_number); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + /* Find model name */ + model_name_size = sizeof(model_name); + ret = id_find_string(&cookie, ID_KEY_model_name, model_name, &model_name_size); + if (ret != ID_EOK) { + printf("%s:%d ret %d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + + printf("Part Number : %u\n", part_number); + printf("Model Name : %.*s\n", model_name_size, model_name); + printf("Serial Number: %.*s\n", serial_number_size, serial_number); + return 0; +} + +/* Extract GPMC timings for particular CS register */ +id_keys_t gpmc_ncs_keys[] = { + ID_KEY_cs0_group, + ID_KEY_cs1_group, + ID_KEY_cs2_group, + ID_KEY_cs3_group, + ID_KEY_cs4_group, + ID_KEY_cs5_group, + ID_KEY_cs6_group, +}; + +id_keys_t gpmc_config_reg_keys[] = { + ID_KEY_config1_reg, + ID_KEY_config2_reg, + ID_KEY_config3_reg, + ID_KEY_config4_reg, + ID_KEY_config5_reg, + ID_KEY_config6_reg, + ID_KEY_config7_reg, +}; + +int logic_extract_gpmc_timing(int cs, int *config_regs) +{ + int ret; + struct id_cookie cookie; + // int gpmc_config_values[ARRAY_SIZE(gpmc_config_reg_keys)]; + + if (!found_id_data) + return -1; + + ret = id_init_cookie(&id_data, &cookie); + if (ret != ID_EOK) { + return ret; + } + + /* find /cpu0_bus_group from root */ + ret = id_find_dict(&cookie, ID_KEY_cpu0_bus_group, IDENUM_DICT); + if (ret != ID_EOK) { + return ret; + } + + /* find /local_bus_group from /cpu0_bus_group */ + ret = id_find_dict(&cookie, ID_KEY_local_bus_group, IDENUM_DICT); + if (ret != ID_EOK) { + return ret; + } + + /* Now look for the particular chip select group */ + ret = id_find_dict(&cookie, gpmc_ncs_keys[cs], IDENUM_DICT); + if (ret != ID_EOK) { + return ret; + } + + /* We have the group, now extract all the config registers */ + ret = id_find_numbers(&cookie, gpmc_config_reg_keys, ARRAY_SIZE(gpmc_config_reg_keys), config_regs); + + return ret; +} + +int do_dump_id_data(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) +{ + printf("id_data: mem_ptr %p root_offset %u root_size %u\n", + id_data.mem_ptr, id_data.root_offset, id_data.root_size); + return 1; +} + +U_BOOT_CMD( + dump_id_data, 1, 1, do_dump_id_data, + "dump_id_data - dump product ID data", + "dump product ID data in human-readable form" +); diff --git a/board/ti/logic/logic-product-id.h b/board/ti/logic/logic-product-id.h new file mode 100644 index 00000000000..b4af92d606c --- /dev/null +++ b/board/ti/logic/logic-product-id.h @@ -0,0 +1,27 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.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 + */ + +extern int logic_has_new_product_id(void); +extern void logic_dump_new_product_data(void); +extern void logic_dump_serialization_info(void); diff --git a/board/ti/logic/logic-proto.h b/board/ti/logic/logic-proto.h new file mode 100644 index 00000000000..2e67f76b978 --- /dev/null +++ b/board/ti/logic/logic-proto.h @@ -0,0 +1,26 @@ +/* + * (C) Copyright 2010 + * Logic Product Development <peter.barada@logicpd.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 + */ + +/* Function to touchup environment variables for display */ +extern void touchup_display_env(void); + diff --git a/board/ti/logic/logic.c b/board/ti/logic/logic.c new file mode 100644 index 00000000000..94562bdd40c --- /dev/null +++ b/board/ti/logic/logic.c @@ -0,0 +1,1300 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * + * Author : + * Peter Barada <peter.barada@logicpd.com> + * + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff <r-woodruff2@ti.com> + * Syed Mohammed Khasim <khasim@ti.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 + */ +#include <common.h> +#include <netdev.h> +#include <flash.h> +#include <nand.h> +#include <i2c.h> +#include <twl4030.h> +#include <asm/io.h> +#include <asm/arch/mem.h> +#include <asm/arch/mux.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/gpio.h> +#include <asm/mach-types.h> +#include "logic.h" +#include "product_id.h" +#include "logic-proto.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define MUX_LOGIC_HSUSB0_D5_GPIO_MUX() \ + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M4)) /*GPIO_189*/ + +#define MUX_LOGIC_HSUSB0_D5_DATA5() \ + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA5*/ + +/* + * Routine: logic_identify + * Description: Detect if we are running on a Logic or Torpedo. + * This can be done by GPIO_189. If its low after driving it high, + * then its a SOM LV, else Torpedo. + */ +unsigned int logic_identify(void) +{ + unsigned int val = 0; + u32 cpu_family = get_cpu_family(); + int i; + + MUX_LOGIC_HSUSB0_D5_GPIO_MUX(); + + if (!omap_request_gpio(189)) { + + omap_set_gpio_direction(189, 0); + omap_set_gpio_dataout(189, 1); + + /* Let it soak for a bit */ + for (i=0; i<0x100; ++i) + asm("nop"); + + omap_set_gpio_direction(189, 1); + val = omap_get_gpio_datain(189); + omap_free_gpio(189); + + printf("Board:"); + if (cpu_family == CPU_OMAP36XX) { + printf(" DM37xx"); + if (val) { + printf(" Torpedo\n"); + val = MACH_TYPE_DM3730_TORPEDO; + } else { + printf(" SOM LV\n"); + val = MACH_TYPE_DM3730_SOM_LV; + } + } else { + printf(" OMAP35xx"); + if (val) { + printf(" Torpedo\n"); + val = MACH_TYPE_OMAP3_TORPEDO; + } else { + printf(" SOM LV\n"); + val = MACH_TYPE_OMAP3530_LV_SOM; + } + } + } + + MUX_LOGIC_HSUSB0_D5_DATA5(); + + return val; +} + +/* + * Set the default NAND ECC method used for the environment + */ +static int omap3logic_nand_default = -1; +void nand_setup_default_ecc_method(void) +{ + if (omap_nand_chip_has_ecc()) + omap3logic_nand_default = OMAP_ECC_CHIP; + else + omap3logic_nand_default = OMAP_ECC_HW; + + omap_nand_switch_ecc(omap3logic_nand_default); +} + +/* + * Switch NAND ECC method to that used for the environment, + * returning the current ECC method through *method + */ +void nand_switch_ecc_default(int *method) +{ + enum omap_nand_ecc_mode curr_mode; + + curr_mode = omap_nand_current_ecc_method(); + *method = (int) curr_mode; + + if (curr_mode != omap3logic_nand_default) + omap_nand_switch_ecc(omap3logic_nand_default); +} + +/* + * Switch ECC method + */ +void nand_switch_ecc_method(int method) +{ + enum omap_nand_ecc_mode curr_mode, new_mode; + + curr_mode = omap_nand_current_ecc_method(); + new_mode = (enum omap_nand_ecc_mode)method; + if (curr_mode != new_mode) + omap_nand_switch_ecc(new_mode); +} + +/* + * Touchup the environment, specificaly to setenv "defaultecc" + */ +void touchup_env(void) +{ + /* Set the defaultecc environment variable to the "natural" + * ECC method supported by the NAND chip */ + if (omap3logic_nand_default == OMAP_ECC_CHIP) + setenv("defaultecc", "chip"); + else if (omap3logic_nand_default == OMAP_ECC_HW) + setenv("defaultecc", "hw"); + else + printf("%s: bad NAND ECC default %d!\n", __FUNCTION__, omap3logic_nand_default); + + /* touchup the display environment variable(s) */ + touchup_display_env(); +} + +/* + * If the user tries to 'setenv foo', check if 'foo' is a "reserved" name. + * certain system variables should not be changed as they are board-specific + * variables + */ +int setenv_reserved_name(const char *name) +{ + if (!strcmp(name, "defaultecc")) + return 1; + return 0; +} + +#ifdef CONFIG_USB_OMAP3 +/* + * MUSB port on OMAP3EVM Rev >= E requires extvbus programming. + */ +u8 omap3_evm_need_extvbus(void) +{ + u8 retval = 0; + + retval = 1; + + return retval; +} +#endif + +static void setup_nand_settings(void); +static void setup_isp176x_settings(void); +static void setup_compact_flash_settings(void); +static void fix_flash_sync(void); + +/* + * Routine: board_init + * Description: Early hardware init. + */ +int board_init(void) +{ + gpmc_init(); /* in SRAM or SDRAM, finish GPMC */ + + /* board id for Linux */ + gd->bd->bi_arch_number = logic_identify(); + + /* boot param addr */ + gd->bd->bi_boot_params = (OMAP34XX_SDRC_CS0 + 0x100); + + /* Update NAND settings */ + setup_nand_settings(); + + /* Setup ISP176x settings */ + if (gd->bd->bi_arch_number == MACH_TYPE_DM3730_TORPEDO + || gd->bd->bi_arch_number == MACH_TYPE_OMAP3_TORPEDO) + setup_isp176x_settings(); + + /* Setup ComactFlash GPMC settings */ + if (gd->bd->bi_arch_number == MACH_TYPE_DM3730_SOM_LV + || gd->bd->bi_arch_number == MACH_TYPE_OMAP3530_LV_SOM) + setup_compact_flash_settings(); + + /* Probe for NOR and if found put into sync mode */ + fix_flash_sync(); + + return 0; +} + +int board_late_init(void) +{ + unsigned char enetaddr[6]; + + dump_production_data(); + + /* Fetch the ethaddr of the LAN */ + board_get_nth_enetaddr(enetaddr, 0, 0); +#ifdef CONFIG_HAS_ETH1 + /* Fetch the ethaddr of the WiFi */ + board_get_nth_enetaddr(enetaddr, 1, 1); +#endif + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + nand_unlock(&nand_info[0], 0x0, nand_info[0].size); +#endif + +#ifdef CONFIG_ENABLE_TWL4030_CHARGING + /* Enable charging on Torpedo unless $disablecharging == yes */ + if (gd->bd->bi_arch_number == MACH_TYPE_OMAP3_TORPEDO) { + char *str; + str = getenv("disablecharging"); + if (!str || strcmp(str, "yes") != 0) { + printf("Torpedo: Enabling battery charging\n"); + twl4030_enable_charging(); + } + } +#endif + +#ifdef CONFIG_CMD_CACHE + dcache_enable(); + printf ("Data (writethrough) Cache is %s\n", + dcache_status() ? "ON" : "OFF"); +#endif + return 0; +} + +/* Turn on VAUX1 voltage to 3.0 volts to drive level shifters and + * power 3.0v parts (tsc2004 and Product ID chip) */ +#define I2C_TRITON2 0x4b /* Address of Triton power group */ + +void init_vaux1_voltage(void) +{ +#ifdef CONFIG_DRIVER_OMAP34XX_I2C + unsigned char data; + unsigned short msg; + + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /* Select the output voltage */ + data = 0x04; + i2c_write(I2C_TRITON2, 0x72, 1, &data, 1); + /* Select the Processor resource group */ + data = 0x20; + i2c_write(I2C_TRITON2, 0x72, 1, &data, 1); + /* Enable I2C access to the Power bus */ + data = 0x02; + i2c_write(I2C_TRITON2, 0x4a, 1, &data, 1); + /* Send message MSB */ + msg = (1<<13) | (1<<4) | (0xd<<0); /* group(process_grp1):resource(vaux1):res_active; */ + data = msg >> 8; + i2c_write(I2C_TRITON2, 0x4b, 1, &data, 1); + /* Send message LSB */ + data = msg & 0xff; + i2c_write(I2C_TRITON2, 0x4c, 1, &data, 1); +#endif +} + +/* + * Check _SYSCONFIG registers and fixup bootrom code leaving them in + * non forced-idle/smart-stdby mode + */ + +static void check_sysconfig_regs(void) +{ + unsigned int temp, temp2; + + /* Since DM3730Logic boards have bootorder of 0x2f, the bootrom + * attempts to boot via USB and leaves OTG_SYSCONFIG in non-idle */ + temp = *(unsigned int *)OTG_SYSCONFIG; + temp2 = OTG_SYSCONFIG_MIDLEMODE_SMART_STDBY + | OTG_SYSCONFIG_SIDLEMODE_FORCE_IDLE + | OTG_SYSCONFIG_AUTOIDLE; + if (temp != temp2) { + printf("OTG_SYSCONFIG: %08x - needs to be %08x\n", temp, temp2); + *(unsigned int *)OTG_SYSCONFIG = temp2; + } +} + + + +/* + * Routine: misc_init_r + * Description: Init ethernet (done here so udelay works) + */ +int misc_init_r(void) +{ + +#ifdef CONFIG_DRIVER_OMAP34XX_I2C + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + + /* Turn on vaux1 to make sure voltage is to the product ID chip. + * Extract production data from ID chip, used to selectively + * initialize portions of the system */ + init_vaux1_voltage(); + fetch_production_data(); + +#if defined(CONFIG_CMD_NET) + setup_net_chip(); +#endif + + dieid_num_r(); + + check_sysconfig_regs(); + + return 0; +} + + +/* GPMC CS1 settings for Logic SOM LV/Torpedo LAN92xx Ethernet chip */ +#define LOGIC_NET_GPMC_CONFIG1 0x00001000 +#define LOGIC_NET_GPMC_CONFIG2 0x00080801 +#define LOGIC_NET_GPMC_CONFIG3 0x00000000 +#define LOGIC_NET_GPMC_CONFIG4 0x08010801 +#define LOGIC_NET_GPMC_CONFIG5 0x00080a0a +#define LOGIC_NET_GPMC_CONFIG6 0x03000280 +#define LOGIC_NET_GPMC_CONFIG7 0x00000848 + +/* + * Routine: setup_net_chip + * Description: Setting up the configuration GPMC registers specific to the + * Ethernet hardware. + */ +static void setup_net_chip(void) +{ + struct ctrl *ctrl_base = (struct ctrl *)OMAP34XX_CTRL_BASE; + + /* Configure GPMC registers */ + writel(LOGIC_NET_GPMC_CONFIG1, &gpmc_cfg->cs[1].config1); + writel(LOGIC_NET_GPMC_CONFIG2, &gpmc_cfg->cs[1].config2); + writel(LOGIC_NET_GPMC_CONFIG3, &gpmc_cfg->cs[1].config3); + writel(LOGIC_NET_GPMC_CONFIG4, &gpmc_cfg->cs[1].config4); + writel(LOGIC_NET_GPMC_CONFIG5, &gpmc_cfg->cs[1].config5); + writel(LOGIC_NET_GPMC_CONFIG6, &gpmc_cfg->cs[1].config6); + writel(LOGIC_NET_GPMC_CONFIG7, &gpmc_cfg->cs[1].config7); + + /* Enable off mode for NWE in PADCONF_GPMC_NWE register */ + writew(readw(&ctrl_base ->gpmc_nwe) | 0x0E00, &ctrl_base->gpmc_nwe); + /* Enable off mode for NOE in PADCONF_GPMC_NADV_ALE register */ + writew(readw(&ctrl_base->gpmc_noe) | 0x0E00, &ctrl_base->gpmc_noe); + /* Enable off mode for ALE in PADCONF_GPMC_NADV_ALE register */ + writew(readw(&ctrl_base->gpmc_nadv_ale) | 0x0E00, + &ctrl_base->gpmc_nadv_ale); + +} + +/* GPMC CS0 settings for Logic SOM LV/Torpedo NAND settings */ +#define LOGIC_NAND_GPMC_CONFIG1 0x00001800 +#define LOGIC_NAND_GPMC_CONFIG2 0x00090900 +#define LOGIC_NAND_GPMC_CONFIG3 0x00090900 +#define LOGIC_NAND_GPMC_CONFIG4 0x06000500 +#define LOGIC_NAND_GPMC_CONFIG5 0x0007080A +#define LOGIC_NAND_GPMC_CONFIG6 0x000002CF +#define LOGIC_NAND_GPMC_CONFIG7 0x00000C70 + +static void setup_nand_settings(void) +{ + /* struct ctrl *ctrl_base = (struct ctrl *)OMAP34XX_CTRL_BASE; */ + + /* Configure GPMC registers */ + writel(0x00000000, &gpmc_cfg->cs[0].config7); + sdelay(1000); + writel(LOGIC_NAND_GPMC_CONFIG1, &gpmc_cfg->cs[0].config1); + writel(LOGIC_NAND_GPMC_CONFIG2, &gpmc_cfg->cs[0].config2); + writel(LOGIC_NAND_GPMC_CONFIG3, &gpmc_cfg->cs[0].config3); + writel(LOGIC_NAND_GPMC_CONFIG4, &gpmc_cfg->cs[0].config4); + writel(LOGIC_NAND_GPMC_CONFIG5, &gpmc_cfg->cs[0].config5); + writel(LOGIC_NAND_GPMC_CONFIG6, &gpmc_cfg->cs[0].config6); + writel(LOGIC_NAND_GPMC_CONFIG7, &gpmc_cfg->cs[0].config7); + sdelay(2000); +} + +#define LOGIC_CF_GPMC_CONFIG1 0x00001210 +#define LOGIC_CF_GPMC_CONFIG2 0x00131000 +#define LOGIC_CF_GPMC_CONFIG3 0x001f1f01 +#define LOGIC_CF_GPMC_CONFIG4 0x10030e03 +#define LOGIC_CF_GPMC_CONFIG5 0x010f1411 +#define LOGIC_CF_GPMC_CONFIG6 0x80030600 +#define LOGIC_CF_GPMC_CONFIG7 0x00000f58 + +static void setup_compact_flash_settings(void) +{ + /* Configure GPMC registers */ + writel(0x00000000, &gpmc_cfg->cs[3].config7); + sdelay(1000); + writel(LOGIC_CF_GPMC_CONFIG1, &gpmc_cfg->cs[3].config1); + writel(LOGIC_CF_GPMC_CONFIG2, &gpmc_cfg->cs[3].config2); + writel(LOGIC_CF_GPMC_CONFIG3, &gpmc_cfg->cs[3].config3); + writel(LOGIC_CF_GPMC_CONFIG4, &gpmc_cfg->cs[3].config4); + writel(LOGIC_CF_GPMC_CONFIG5, &gpmc_cfg->cs[3].config5); + writel(LOGIC_CF_GPMC_CONFIG6, &gpmc_cfg->cs[3].config6); + writel(LOGIC_CF_GPMC_CONFIG7, &gpmc_cfg->cs[3].config7); + sdelay(2000); +} + +/* GPMC CS6 settings for Logic SOM LV/Torpedo ISP176x settings */ +#define LOGIC_ISP176X_GPMC_CONFIG1 0x00001000 +#define LOGIC_ISP176X_GPMC_CONFIG2 0x00090900 +#define LOGIC_ISP176X_GPMC_CONFIG3 0x00000000 +#define LOGIC_ISP176X_GPMC_CONFIG4 0x05000900 +#define LOGIC_ISP176X_GPMC_CONFIG5 0x0007090c +#define LOGIC_ISP176X_GPMC_CONFIG6 0x04010200 +#define LOGIC_ISP176X_GPMC_CONFIG7 0x00000f5c + +static void setup_isp176x_settings(void) +{ + /* struct ctrl *ctrl_base = (struct ctrl *)OMAP34XX_CTRL_BASE; */ + + /* Configure GPMC registers */ + writel(0x00000000, &gpmc_cfg->cs[6].config7); + sdelay(1000); + writel(LOGIC_ISP176X_GPMC_CONFIG1, &gpmc_cfg->cs[6].config1); + writel(LOGIC_ISP176X_GPMC_CONFIG2, &gpmc_cfg->cs[6].config2); + writel(LOGIC_ISP176X_GPMC_CONFIG3, &gpmc_cfg->cs[6].config3); + writel(LOGIC_ISP176X_GPMC_CONFIG4, &gpmc_cfg->cs[6].config4); + writel(LOGIC_ISP176X_GPMC_CONFIG5, &gpmc_cfg->cs[6].config5); + writel(LOGIC_ISP176X_GPMC_CONFIG6, &gpmc_cfg->cs[6].config6); + writel(LOGIC_ISP176X_GPMC_CONFIG7, &gpmc_cfg->cs[6].config7); + sdelay(2000); +} + +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG1 0x00001210 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG2 0x00101001 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG3 0x00020201 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG4 0x0f031003 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG5 0x000f1111 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG6 0x0f030080 +#define LOGIC_STNOR_ASYNC_GPMC_CONFIG7 0x00000c50 + +#define LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG1 0x6A411213 +#define LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG2 0x000C1503 +#define LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG3 0x00050503 +#define LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG4 0x0B051506 + +#define LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG1 0x68411213 +#define LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG2 0x000C1502 +#define LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG3 0x00040402 +#define LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG4 0x0B051505 + +#define LOGIC_STNOR_SYNC_GPMC_CONFIG5 0x020E0C15 +#define LOGIC_STNOR_SYNC_GPMC_CONFIG6 0x0B0603C3 +#define LOGIC_STNOR_SYNC_GPMC_CONFIG7 0x00000c50 + + +#define LOGIC_FLASH_BASE 0x10000000 + +/* These are bit definitions for the RCR register of the NOR flash */ +/* 28FxxxP30 device. This register sets the bus configration for reads. */ +/* settings, located on address pins A[15:0]. */ +#define FLASH_28FxxxP30_RCR_RM 0x8000 +#define FLASH_28FxxxP30_RCR_R 0x4000 +#define FLASH_28FxxxP30_RCR_LC(x) ((x & 0x7) << 11) +#define FLASH_28FxxxP30_RCR_WP 0x0400 +#define FLASH_28FxxxP30_RCR_DH 0x0200 +#define FLASH_28FxxxP30_RCR_WD 0x0100 +#define FLASH_28FxxxP30_RCR_BS 0x0080 +#define FLASH_28FxxxP30_RCR_CE 0x0040 +#define FLASH_28FxxxP30_RCR_BW 0x0008 +#define FLASH_28FxxxP30_RCR_BL(x) ((x & 0x7) << 0) +#define FLASH_28FxxxP30_BL_4 0x1 +#define FLASH_28FxxxP30_BL_8 0x2 +#define FLASH_28FxxxP30_BL_16 0x3 +#define FLASH_28FxxxP30_BL_CONT 0x7 + +/* + * Routine: fix_flash_sync + * Description: Setting up the configuration GPMC registers specific to the + * NOR flash (and place in sync mode if not done). + */ +int omap3logic_flash_exists; +static void fix_flash_sync(void) +{ + int arch_number; + u16 rcrval; + + /* Check the arch_number - Torpedo doesn't have NOR flash */ + arch_number = gd->bd->bi_arch_number; + if (!(arch_number == MACH_TYPE_DM3730_SOM_LV + || arch_number == MACH_TYPE_OMAP3530_LV_SOM)) + return; + + /* Check CS2 config, if its not in sync, or not valid, then configure it */ + if ( !(readl(&gpmc_cfg->cs[2].config1) & TYPE_READTYPE) || + !(readl(&gpmc_cfg->cs[2].config7) & 0x00000040) ) { + + /* Invalidate, in case it is set valid */ + writel(0x00000000, &gpmc_cfg->cs[2].config7); + + /* clear WAIT1 polarity */ + writel(readl(&gpmc_cfg->config) & ~0x200, &gpmc_cfg->config); + + /* clear GPMC_TIMEOUT */ + writel(0x0, &gpmc_cfg->timeout_control); + + /* Configure GPMC registers for async */ + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG1, &gpmc_cfg->cs[2].config1); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG2, &gpmc_cfg->cs[2].config2); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG3, &gpmc_cfg->cs[2].config3); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG4, &gpmc_cfg->cs[2].config4); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG5, &gpmc_cfg->cs[2].config5); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG6, &gpmc_cfg->cs[2].config6); + writel(LOGIC_STNOR_ASYNC_GPMC_CONFIG7, &gpmc_cfg->cs[2].config7); + + /* Test if this NOR flash is connected */ + *(volatile u16 *)LOGIC_FLASH_BASE = 0x0070; // Read status reg + if (*(volatile u16 *)LOGIC_FLASH_BASE != 0x0080) + { + writel(0x00000000, &gpmc_cfg->cs[2].config7); /* Invalidate chip select */ + puts("NOR: no flash device detected\n"); + return; // fail - no NOR flash detected + } + *(volatile u16 *)LOGIC_FLASH_BASE = 0x0050; // Restore to read mode + + puts("NOR: initialize in sync mode\n"); + + /* 1st NOR cycle, send read config register setup 0x60 */ + *(volatile u16 *)LOGIC_FLASH_BASE = 0x0060; + + /* 2nd NOR cycle, send 0x03 to latch in read + * configuration register setttings, located on A[15:0] */ + rcrval = FLASH_28FxxxP30_RCR_LC(4) | FLASH_28FxxxP30_RCR_WP | + FLASH_28FxxxP30_RCR_BS | FLASH_28FxxxP30_RCR_CE | + FLASH_28FxxxP30_RCR_BW | FLASH_28FxxxP30_RCR_BL(FLASH_28FxxxP30_BL_4); + *(volatile u16 *)(LOGIC_FLASH_BASE | (rcrval << 1)) = 0x0003; + + /* Give a chance for accesses to finish... */ + sdelay(2000); + + /* Third, set GPMC for sync. */ + if (arch_number == MACH_TYPE_DM3730_SOM_LV) { + /* Use DM3730 SOM LV NOR timings */ + writel(LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG1, &gpmc_cfg->cs[2].config1); + writel(LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG2, &gpmc_cfg->cs[2].config2); + writel(LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG3, &gpmc_cfg->cs[2].config3); + writel(LOGIC_STNOR_DM37x_SYNC_GPMC_CONFIG4, &gpmc_cfg->cs[2].config4); + } + if (arch_number == MACH_TYPE_OMAP3530_LV_SOM) { + /* Use DM3730 SOM LV NOR timings */ + writel(LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG1, &gpmc_cfg->cs[2].config1); + writel(LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG2, &gpmc_cfg->cs[2].config2); + writel(LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG3, &gpmc_cfg->cs[2].config3); + writel(LOGIC_STNOR_OMAP35x_SYNC_GPMC_CONFIG4, &gpmc_cfg->cs[2].config4); + } + writel(LOGIC_STNOR_SYNC_GPMC_CONFIG5, &gpmc_cfg->cs[2].config5); + writel(LOGIC_STNOR_SYNC_GPMC_CONFIG6, &gpmc_cfg->cs[2].config6); + writel(LOGIC_STNOR_SYNC_GPMC_CONFIG7, &gpmc_cfg->cs[2].config7); + /* And lastly, set the WAIT1 polarity high */ + writel(readl(&gpmc_cfg->config) | 0x200, &gpmc_cfg->config); + } else + puts ("NOR: Already initialized in sync mode\n"); + + omap3logic_flash_exists = 1; +} + +int board_eth_init(bd_t *bis) +{ + int rc = 0; +#ifdef CONFIG_SMC911X + rc = smc911x_initialize(0, CONFIG_SMC911X_BASE); +#endif + return rc; +} + +#ifdef CONFIG_CMD_GPMC_CONFIG +int do_dump_gpmc(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]) +{ + gpmc_cfg = (struct gpmc *)GPMC_BASE; + int i; + printf("GPMC_SYSCONFIG: %08x\n", gpmc_cfg->sysconfig); + printf("GPMC_CONFIG: %08x\n", gpmc_cfg->config); + for (i=0; i<8; ++i) { + struct gpmc_cs *p = &gpmc_cfg->cs[i]; + if (p->config7 & (1<<6)) { + printf("GPMC%d: %08x %08x %08x %08x\n", i, + p->config1, p->config2, p->config3, p->config4); + printf(" %08x %08x %03x\n", + p->config5, p->config6, p->config7); + } + } + return 1; +} + +U_BOOT_CMD( + gpmc_config, 1, 1, do_dump_gpmc, + "gpmc_config - dump GPMC settings", + "dump valid GPMC configuration" +); +#endif + +#ifdef CONFIG_CMD_MUX_CONFIG +int do_dump_mux_config(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + int i,j; + u16 val; + struct { + u16 start; + u16 stop; + } mux_offsets[] = { + { + CONTROL_PADCONF_SDRC_D0, /* 0x0030 */ + CONTROL_PADCONF_SDRC_CKE1, /* 0x0264 */ + }, + { + CONTROL_PADCONF_SYS_32K, /* 0x0A04 */ + CONTROL_PADCONF_D2D_SWAKEUP, /* 0x0A4C */ + }, + { + CONTROL_PADCONF_ETK_CLK_ES2, /* 0x05D8 */ + CONTROL_PADCONF_ETK_D15_ES2, /* 0x05FA */ + }, + }; + + for (i=0; i<ARRAY_SIZE(mux_offsets); ++i) { + for (j=mux_offsets[i].start; j<mux_offsets[i].stop; j+= sizeof(u16)) { + val = readw(OMAP34XX_CTRL_BASE + j); + if ((val & M7) != M7) { + printf("%04x: %04x (", + j, val); + if (val & IEN) + printf("IEN "); + else + printf("IDIS"); + if (val & PTU) + printf(" | PTU"); + else + printf(" | PTD"); + if (val & EN) + printf(" | EN "); + else + printf(" | DIS"); + printf(" | M%d)\n", (val & M7)); + } + } + } + return 0; +} + +U_BOOT_CMD(mux_config, 1, 1, do_dump_mux_config, + "mux_config - dump active mux registers", + "dump active mux configuration" +); +#endif + + + +/* + * IEN - Input Enable + * IDIS - Input Disable + * PTD - Pull type Down + * PTU - Pull type Up + * DIS - Pull type selection is inactive + * EN - Pull type selection is active + * M0 - Mode 0 + * The commented string gives the final mux configuration for that pin + */ + +/* + * Routine: set_muxconf_regs + * Description: Setting up the configuration Mux registers specific to the + * hardware. Many pins need to be moved from protect to primary + * mode. + */ +#define SAFE_MODE_PINS_1 +#define SAFE_MODE_PINS_2 +#define SAFE_MODE_PINS_3 +#define SAFE_MODE_PINS_4 +#undef SAFE_MODE_PINS_5 +#define SAFE_MODE_PINS_5A +#define SAFE_MODE_PINS_6 +void set_muxconf_regs(void) +{ + /*SDRC*/ + MUX_VAL(CP(SDRC_D0), (IEN | PTD | DIS | M0)); /*SDRC_D0*/ + MUX_VAL(CP(SDRC_D1), (IEN | PTD | DIS | M0)); /*SDRC_D1*/ + MUX_VAL(CP(SDRC_D2), (IEN | PTD | DIS | M0)); /*SDRC_D2*/ + MUX_VAL(CP(SDRC_D3), (IEN | PTD | DIS | M0)); /*SDRC_D3*/ + MUX_VAL(CP(SDRC_D4), (IEN | PTD | DIS | M0)); /*SDRC_D4*/ + MUX_VAL(CP(SDRC_D5), (IEN | PTD | DIS | M0)); /*SDRC_D5*/ + MUX_VAL(CP(SDRC_D6), (IEN | PTD | DIS | M0)); /*SDRC_D6*/ + MUX_VAL(CP(SDRC_D7), (IEN | PTD | DIS | M0)); /*SDRC_D7*/ + MUX_VAL(CP(SDRC_D8), (IEN | PTD | DIS | M0)); /*SDRC_D8*/ + MUX_VAL(CP(SDRC_D9), (IEN | PTD | DIS | M0)); /*SDRC_D9*/ + MUX_VAL(CP(SDRC_D10), (IEN | PTD | DIS | M0)); /*SDRC_D10*/ + MUX_VAL(CP(SDRC_D11), (IEN | PTD | DIS | M0)); /*SDRC_D11*/ + MUX_VAL(CP(SDRC_D12), (IEN | PTD | DIS | M0)); /*SDRC_D12*/ + MUX_VAL(CP(SDRC_D13), (IEN | PTD | DIS | M0)); /*SDRC_D13*/ + MUX_VAL(CP(SDRC_D14), (IEN | PTD | DIS | M0)); /*SDRC_D14*/ + MUX_VAL(CP(SDRC_D15), (IEN | PTD | DIS | M0)); /*SDRC_D15*/ + MUX_VAL(CP(SDRC_D16), (IEN | PTD | DIS | M0)); /*SDRC_D16*/ + MUX_VAL(CP(SDRC_D17), (IEN | PTD | DIS | M0)); /*SDRC_D17*/ + MUX_VAL(CP(SDRC_D18), (IEN | PTD | DIS | M0)); /*SDRC_D18*/ + MUX_VAL(CP(SDRC_D19), (IEN | PTD | DIS | M0)); /*SDRC_D19*/ + MUX_VAL(CP(SDRC_D20), (IEN | PTD | DIS | M0)); /*SDRC_D20*/ + MUX_VAL(CP(SDRC_D21), (IEN | PTD | DIS | M0)); /*SDRC_D21*/ + MUX_VAL(CP(SDRC_D22), (IEN | PTD | DIS | M0)); /*SDRC_D22*/ + MUX_VAL(CP(SDRC_D23), (IEN | PTD | DIS | M0)); /*SDRC_D23*/ + MUX_VAL(CP(SDRC_D24), (IEN | PTD | DIS | M0)); /*SDRC_D24*/ + MUX_VAL(CP(SDRC_D25), (IEN | PTD | DIS | M0)); /*SDRC_D25*/ + MUX_VAL(CP(SDRC_D26), (IEN | PTD | DIS | M0)); /*SDRC_D26*/ + MUX_VAL(CP(SDRC_D27), (IEN | PTD | DIS | M0)); /*SDRC_D27*/ + MUX_VAL(CP(SDRC_D28), (IEN | PTD | DIS | M0)); /*SDRC_D28*/ + MUX_VAL(CP(SDRC_D29), (IEN | PTD | DIS | M0)); /*SDRC_D29*/ + MUX_VAL(CP(SDRC_D30), (IEN | PTD | DIS | M0)); /*SDRC_D30*/ + MUX_VAL(CP(SDRC_D31), (IEN | PTD | DIS | M0)); /*SDRC_D31*/ + MUX_VAL(CP(SDRC_CLK), (IEN | PTD | DIS | M0)); /*SDRC_CLK*/ + MUX_VAL(CP(SDRC_DQS0), (IEN | PTD | DIS | M0)); /*SDRC_DQS0*/ + MUX_VAL(CP(SDRC_DQS1), (IEN | PTD | DIS | M0)); /*SDRC_DQS1*/ + MUX_VAL(CP(SDRC_DQS2), (IEN | PTD | DIS | M0)); /*SDRC_DQS2*/ + MUX_VAL(CP(SDRC_DQS3), (IEN | PTD | DIS | M0)); /*SDRC_DQS3*/ + /*GPMC*/ + MUX_VAL(CP(GPMC_A1), (IDIS | PTU | EN | M0)); /*GPMC_A1*/ + MUX_VAL(CP(GPMC_A2), (IDIS | PTU | EN | M0)); /*GPMC_A2*/ + MUX_VAL(CP(GPMC_A3), (IDIS | PTU | EN | M0)); /*GPMC_A3*/ + MUX_VAL(CP(GPMC_A4), (IDIS | PTU | EN | M0)); /*GPMC_A4*/ + MUX_VAL(CP(GPMC_A5), (IDIS | PTU | EN | M0)); /*GPMC_A5*/ + MUX_VAL(CP(GPMC_A6), (IDIS | PTU | EN | M0)); /*GPMC_A6*/ + MUX_VAL(CP(GPMC_A7), (IDIS | PTU | EN | M0)); /*GPMC_A7*/ + MUX_VAL(CP(GPMC_A8), (IDIS | PTU | EN | M0)); /*GPMC_A8*/ + MUX_VAL(CP(GPMC_A9), (IDIS | PTU | EN | M0)); /*GPMC_A9*/ + MUX_VAL(CP(GPMC_A10), (IDIS | PTU | EN | M0)); /*GPMC_A10*/ + MUX_VAL(CP(GPMC_D0), (IEN | PTU | EN | M0)); /*GPMC_D0*/ + MUX_VAL(CP(GPMC_D1), (IEN | PTU | EN | M0)); /*GPMC_D1*/ + MUX_VAL(CP(GPMC_D2), (IEN | PTU | EN | M0)); /*GPMC_D2*/ + MUX_VAL(CP(GPMC_D3), (IEN | PTU | EN | M0)); /*GPMC_D3*/ + MUX_VAL(CP(GPMC_D4), (IEN | PTU | EN | M0)); /*GPMC_D4*/ + MUX_VAL(CP(GPMC_D5), (IEN | PTU | EN | M0)); /*GPMC_D5*/ + MUX_VAL(CP(GPMC_D6), (IEN | PTU | EN | M0)); /*GPMC_D6*/ + MUX_VAL(CP(GPMC_D7), (IEN | PTU | EN | M0)); /*GPMC_D7*/ + MUX_VAL(CP(GPMC_D8), (IEN | PTU | EN | M0)); /*GPMC_D8*/ + MUX_VAL(CP(GPMC_D9), (IEN | PTU | EN | M0)); /*GPMC_D9*/ + MUX_VAL(CP(GPMC_D10), (IEN | PTU | EN | M0)); /*GPMC_D10*/ + MUX_VAL(CP(GPMC_D11), (IEN | PTU | EN | M0)); /*GPMC_D11*/ + MUX_VAL(CP(GPMC_D12), (IEN | PTU | EN | M0)); /*GPMC_D12*/ + MUX_VAL(CP(GPMC_D13), (IEN | PTU | EN | M0)); /*GPMC_D13*/ + MUX_VAL(CP(GPMC_D14), (IEN | PTU | EN | M0)); /*GPMC_D14*/ + MUX_VAL(CP(GPMC_D15), (IEN | PTU | EN | M0)); /*GPMC_D15*/ + MUX_VAL(CP(GPMC_NCS0), (IDIS | PTU | EN | M0)); /*GPMC_nCS0*/ + MUX_VAL(CP(GPMC_NCS1), (IDIS | PTU | EN | M0)); /*GPMC_nCS1*/ + MUX_VAL(CP(GPMC_NCS2), (IDIS | PTU | EN | M0)); /*GPMC_nCS2*/ + MUX_VAL(CP(GPMC_NCS3), (IDIS | PTD | DIS | M0)); /*GPMC_nCS3*/ + MUX_VAL(CP(GPMC_NCS4), (IEN | PTU | EN | M0)); /*GPMC_nCS4*/ +#if 1 + /* Display GPIO */ + MUX_VAL(CP(GPMC_NCS5), (IDIS | PTU | DIS | M4)); /*GPIO_65 backlight */ +#else + MUX_VAL(CP(GPMC_NCS5), (IDIS | PTU | EN | M0)); /*GPMC_nCS5*/ +#endif + MUX_VAL(CP(GPMC_NCS6), (IEN | PTD | DIS | M0)); /*GPMC_nCS6*/ + MUX_VAL(CP(GPMC_NCS7), (IDIS | PTD | DIS | M1)); /*GPMC_IO_DIR*/ + MUX_VAL(CP(GPMC_CLK), (IEN | PTD | DIS | M0)); /*GPMC_CLK*/ + MUX_VAL(CP(GPMC_NADV_ALE), (IDIS | PTD | DIS | M0)); /*GPMC_nADV_ALE*/ + MUX_VAL(CP(GPMC_NOE), (IDIS | PTD | DIS | M0)); /*GPMC_nOE*/ + MUX_VAL(CP(GPMC_NWE), (IDIS | PTD | DIS | M0)); /*GPMC_nWE*/ + MUX_VAL(CP(GPMC_NBE0_CLE), (IDIS | PTU | EN | M0)); /*GPMC_nBE0_CLE*/ + MUX_VAL(CP(GPMC_NBE1), (IEN | PTU | EN | M0)); /*GPMC_nBE1*/ + MUX_VAL(CP(GPMC_NWP), (IEN | PTD | DIS | M0)); /*GPMC_nWP*/ + MUX_VAL(CP(GPMC_WAIT0), (IEN | PTU | EN | M0)); /*GPMC_WAIT0*/ + MUX_VAL(CP(GPMC_WAIT1), (IEN | PTU | EN | M0)); /*GPMC_WAIT1*/ +#ifdef SAFE_MODE_PINS_1 + MUX_VAL(CP(GPMC_WAIT2), (IEN | PTD | EN | M7)); /*safe mode */ +#else + MUX_VAL(CP(GPMC_WAIT2), (IEN | PTU | EN | M4)); /*GPIO_64*/ + /* - ETH_nRESET*/ +#endif + MUX_VAL(CP(GPMC_WAIT3), (IEN | PTU | EN | M0)); /*GPMC_WAIT3*/ + /*DSS*/ +#ifdef SAFE_MODE_PINS_2 + MUX_VAL(CP(DSS_PCLK), (IEN | PTD | EN | M7)); /*DSS_PCLK*/ + MUX_VAL(CP(DSS_HSYNC), (IEN | PTD | EN | M7)); /*DSS_HSYNC*/ + MUX_VAL(CP(DSS_VSYNC), (IEN | PTD | EN | M7)); /*DSS_VSYNC*/ + MUX_VAL(CP(DSS_ACBIAS), (IEN | PTD | EN | M7)); /*DSS_ACBIAS*/ + MUX_VAL(CP(DSS_DATA0), (IEN | PTD | EN | M7)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA1), (IEN | PTD | EN | M7)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA2), (IEN | PTD | EN | M7)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA3), (IEN | PTD | EN | M7)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA4), (IEN | PTD | EN | M7)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA5), (IEN | PTD | EN | M7)); /*DSS_DATA5*/ + MUX_VAL(CP(DSS_DATA6), (IEN | PTD | EN | M7)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA7), (IEN | PTD | EN | M7)); /*DSS_DATA7*/ + MUX_VAL(CP(DSS_DATA8), (IEN | PTD | EN | M7)); /*DSS_DATA8*/ + MUX_VAL(CP(DSS_DATA9), (IEN | PTD | EN | M7)); /*DSS_DATA9*/ + MUX_VAL(CP(DSS_DATA10), (IEN | PTD | EN | M7)); /*DSS_DATA10*/ + MUX_VAL(CP(DSS_DATA11), (IEN | PTD | EN | M7)); /*DSS_DATA11*/ + MUX_VAL(CP(DSS_DATA12), (IEN | PTD | EN | M7)); /*DSS_DATA12*/ + MUX_VAL(CP(DSS_DATA13), (IEN | PTD | EN | M7)); /*DSS_DATA13*/ + MUX_VAL(CP(DSS_DATA14), (IEN | PTD | EN | M7)); /*DSS_DATA14*/ + MUX_VAL(CP(DSS_DATA15), (IEN | PTD | EN | M7)); /*DSS_DATA15*/ + MUX_VAL(CP(DSS_DATA16), (IEN | PTD | EN | M7)); /*DSS_DATA16*/ + MUX_VAL(CP(DSS_DATA17), (IEN | PTD | EN | M7)); /*DSS_DATA17*/ + MUX_VAL(CP(DSS_DATA18), (IEN | PTD | EN | M7)); /*DSS_DATA18*/ + MUX_VAL(CP(DSS_DATA19), (IEN | PTD | EN | M7)); /*DSS_DATA19*/ + MUX_VAL(CP(DSS_DATA20), (IEN | PTD | EN | M7)); /*DSS_DATA20*/ + MUX_VAL(CP(DSS_DATA21), (IEN | PTD | EN | M7)); /*DSS_DATA21*/ + MUX_VAL(CP(DSS_DATA22), (IEN | PTD | EN | M7)); /*DSS_DATA22*/ + MUX_VAL(CP(DSS_DATA23), (IEN | PTD | EN | M7)); /*DSS_DATA23*/ + /*CAMERA*/ + MUX_VAL(CP(CAM_HS), (IEN | PTD | EN | M7)); /*CAM_HS */ + MUX_VAL(CP(CAM_VS), (IEN | PTD | EN | M7)); /*CAM_VS */ + MUX_VAL(CP(CAM_XCLKA), (IEN | PTD | EN | M7)); /*CAM_XCLKA*/ + MUX_VAL(CP(CAM_PCLK), (IEN | PTD | EN | M7)); /*CAM_PCLK*/ + MUX_VAL(CP(CAM_FLD), (IEN | PTD | EN | M7)); /*GPIO_98*/ + /* - CAM_RESET*/ + MUX_VAL(CP(CAM_D0), (IEN | PTD | EN | M7)); /*CAM_D0*/ + MUX_VAL(CP(CAM_D1), (IEN | PTD | EN | M7)); /*CAM_D1*/ + MUX_VAL(CP(CAM_D2), (IEN | PTD | EN | M7)); /*CAM_D2*/ + MUX_VAL(CP(CAM_D3), (IEN | PTD | EN | M7)); /*CAM_D3*/ + MUX_VAL(CP(CAM_D4), (IEN | PTD | EN | M7)); /*CAM_D4*/ + MUX_VAL(CP(CAM_D5), (IEN | PTD | EN | M7)); /*CAM_D5*/ + MUX_VAL(CP(CAM_D6), (IEN | PTD | EN | M7)); /*CAM_D6*/ + MUX_VAL(CP(CAM_D7), (IEN | PTD | EN | M7)); /*CAM_D7*/ + MUX_VAL(CP(CAM_D8), (IEN | PTD | EN | M7)); /*CAM_D8*/ + MUX_VAL(CP(CAM_D9), (IEN | PTD | EN | M7)); /*CAM_D9*/ + MUX_VAL(CP(CAM_D10), (IEN | PTD | EN | M7)); /*CAM_D10*/ + MUX_VAL(CP(CAM_D11), (IEN | PTD | EN | M7)); /*CAM_D11*/ + MUX_VAL(CP(CAM_XCLKB), (IEN | PTD | EN | M7)); /*CAM_XCLKB*/ + MUX_VAL(CP(CAM_WEN), (IEN | PTD | EN | M7)); /*GPIO_167*/ + MUX_VAL(CP(CAM_STROBE), (IEN | PTD | EN | M7)); /*CAM_STROBE*/ + MUX_VAL(CP(CSI2_DX0), (IEN | PTD | EN | M7)); /*CSI2_DX0*/ + MUX_VAL(CP(CSI2_DY0), (IEN | PTD | EN | M7)); /*CSI2_DY0*/ + MUX_VAL(CP(CSI2_DX1), (IEN | PTD | EN | M7)); /*CSI2_DX1*/ + MUX_VAL(CP(CSI2_DY1), (IEN | PTD | EN | M7)); /*CSI2_DY1*/ + /*Audio Interface */ + MUX_VAL(CP(MCBSP2_FSX), (IEN | PTD | EN | M7)); /*McBSP2_FSX*/ + MUX_VAL(CP(MCBSP2_CLKX), (IEN | PTD | EN | M7)); /*McBSP2_CLKX*/ + MUX_VAL(CP(MCBSP2_DR), (IEN | PTD | EN | M7)); /*McBSP2_DR*/ + MUX_VAL(CP(MCBSP2_DX), (IEN | PTD | EN | M7)); /*McBSP2_DX*/ +#else + /*CAMERA*/ + MUX_VAL(CP(CAM_HS), (IEN | PTD | EN | M0)); /*CAM_HS */ + MUX_VAL(CP(CAM_VS), (IEN | PTD | EN | M0)); /*CAM_VS */ + MUX_VAL(CP(CAM_XCLKA), (IDIS | PTD | EN | M0)); /*CAM_XCLKA*/ + MUX_VAL(CP(CAM_PCLK), (IEN | PTD | EN | M0)); /*CAM_PCLK*/ + MUX_VAL(CP(CAM_FLD), (IDIS | PTD | EN | M4)); /*GPIO_98*/ + /* - CAM_RESET*/ + MUX_VAL(CP(CAM_D0), (IEN | PTD | EN | M0)); /*CAM_D0*/ + MUX_VAL(CP(CAM_D1), (IEN | PTD | EN | M0)); /*CAM_D1*/ + MUX_VAL(CP(CAM_D2), (IEN | PTD | EN | M0)); /*CAM_D2*/ + MUX_VAL(CP(CAM_D3), (IEN | PTD | EN | M0)); /*CAM_D3*/ + MUX_VAL(CP(CAM_D4), (IEN | PTD | EN | M0)); /*CAM_D4*/ + MUX_VAL(CP(CAM_D5), (IEN | PTD | EN | M0)); /*CAM_D5*/ + MUX_VAL(CP(CAM_D6), (IEN | PTD | EN | M0)); /*CAM_D6*/ + MUX_VAL(CP(CAM_D7), (IEN | PTD | EN | M0)); /*CAM_D7*/ + MUX_VAL(CP(CAM_D8), (IEN | PTD | EN | M0)); /*CAM_D8*/ + MUX_VAL(CP(CAM_D9), (IEN | PTD | EN | M0)); /*CAM_D9*/ + MUX_VAL(CP(CAM_D10), (IEN | PTD | EN | M0)); /*CAM_D10*/ + MUX_VAL(CP(CAM_D11), (IEN | PTD | EN | M0)); /*CAM_D11*/ + MUX_VAL(CP(CAM_XCLKB), (IDIS | PTD | EN | M0)); /*CAM_XCLKB*/ + MUX_VAL(CP(CAM_WEN), (IEN | PTD | EN | M4)); /*GPIO_167*/ + MUX_VAL(CP(CAM_STROBE), (IDIS | PTD | EN | M0)); /*CAM_STROBE*/ + MUX_VAL(CP(CSI2_DX0), (IEN | PTD | EN | M0)); /*CSI2_DX0*/ + MUX_VAL(CP(CSI2_DY0), (IEN | PTD | EN | M0)); /*CSI2_DY0*/ + MUX_VAL(CP(CSI2_DX1), (IEN | PTD | EN | M0)); /*CSI2_DX1*/ + MUX_VAL(CP(CSI2_DY1), (IEN | PTD | EN | M0)); /*CSI2_DY1*/ + /*Audio Interface */ + MUX_VAL(CP(MCBSP2_FSX), (IEN | PTD | EN | M0)); /*McBSP2_FSX*/ + MUX_VAL(CP(MCBSP2_CLKX), (IEN | PTD | EN | M0)); /*McBSP2_CLKX*/ + MUX_VAL(CP(MCBSP2_DR), (IEN | PTD | EN | M0)); /*McBSP2_DR*/ + MUX_VAL(CP(MCBSP2_DX), (IDIS | PTD | EN | M0)); /*McBSP2_DX*/ +#endif + +#if 0 /* Setup in lcd_setup_pinmux() */ + MUX_VAL(CP(DSS_PCLK), (IDIS | PTD | EN | M0)); /*DSS_PCLK*/ + MUX_VAL(CP(DSS_HSYNC), (IDIS | PTD | EN | M0)); /*DSS_HSYNC*/ + MUX_VAL(CP(DSS_VSYNC), (IDIS | PTD | EN | M0)); /*DSS_VSYNC*/ + MUX_VAL(CP(DSS_ACBIAS), (IDIS | PTD | EN | M0)); /*DSS_ACBIAS*/ +#if 1 + /*DSS - with DATA18-23 muxed as DATA0-5 */ + MUX_VAL(CP(DSS_PCLK), (IDIS | PTD | EN | M0)); /*DSS_PCLK*/ + MUX_VAL(CP(DSS_HSYNC), (IDIS | PTD | EN | M0)); /*DSS_HSYNC*/ + MUX_VAL(CP(DSS_VSYNC), (IDIS | PTD | EN | M0)); /*DSS_VSYNC*/ + MUX_VAL(CP(DSS_ACBIAS), (IDIS | PTD | EN | M0)); /*DSS_ACBIAS*/ +#if 1 + /* SOM used DATA0-5 for output */ + MUX_VAL(CP(DSS_DATA0), (IDIS | PTD | EN | M0)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA1), (IDIS | PTD | EN | M0)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA2), (IDIS | PTD | EN | M0)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA3), (IDIS | PTD | EN | M0)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA4), (IDIS | PTD | EN | M0)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA5), (IDIS | PTD | EN | M0)); /*DSS_DATA5*/ +#else + /* Torpedo doesn't used DATA0-5 for output */ + MUX_VAL(CP(DSS_DATA0), (IDIS | PTD | EN | M7)); /*SAFE*/ + MUX_VAL(CP(DSS_DATA1), (IDIS | PTD | EN | M7)); /*SAFE*/ + MUX_VAL(CP(DSS_DATA2), (IDIS | PTD | EN | M7)); /*SAFE*/ + MUX_VAL(CP(DSS_DATA3), (IDIS | PTD | EN | M7)); /*SAFE*/ + MUX_VAL(CP(DSS_DATA4), (IDIS | PTD | EN | M7)); /*SAFE*/ + MUX_VAL(CP(DSS_DATA5), (IDIS | PTD | EN | M7)); /*SAFE*/ +#endif + MUX_VAL(CP(DSS_DATA6), (IDIS | PTD | EN | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA7), (IDIS | PTD | EN | M0)); /*DSS_DATA7*/ + MUX_VAL(CP(DSS_DATA8), (IDIS | PTD | EN | M0)); /*DSS_DATA8*/ + MUX_VAL(CP(DSS_DATA9), (IDIS | PTD | EN | M0)); /*DSS_DATA9*/ + MUX_VAL(CP(DSS_DATA10), (IDIS | PTD | EN | M0)); /*DSS_DATA10*/ + MUX_VAL(CP(DSS_DATA11), (IDIS | PTD | EN | M0)); /*DSS_DATA11*/ + MUX_VAL(CP(DSS_DATA12), (IDIS | PTD | EN | M0)); /*DSS_DATA12*/ + MUX_VAL(CP(DSS_DATA13), (IDIS | PTD | EN | M0)); /*DSS_DATA13*/ + MUX_VAL(CP(DSS_DATA14), (IDIS | PTD | EN | M0)); /*DSS_DATA14*/ + MUX_VAL(CP(DSS_DATA15), (IDIS | PTD | EN | M0)); /*DSS_DATA15*/ + MUX_VAL(CP(DSS_DATA16), (IDIS | PTD | EN | M0)); /*DSS_DATA16*/ + MUX_VAL(CP(DSS_DATA17), (IDIS | PTD | EN | M0)); /*DSS_DATA17*/ +#if 1 + /* SOM uses DATA18-23 as they are*/ + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | EN | M0)); /*DSS_DATA18*/ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | EN | M0)); /*DSS_DATA19*/ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | EN | M0)); /*DSS_DATA20*/ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | EN | M0)); /*DSS_DATA21*/ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | EN | M0)); /*DSS_DATA22*/ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | EN | M0)); /*DSS_DATA23*/ +#else + /* Torpedo uses DATA18-23 as DATA0-5 */ + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | EN | M3)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | EN | M3)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | EN | M3)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | EN | M3)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | EN | M3)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | EN | M3)); /*DSS_DATA5*/ +#endif +#else + MUX_VAL(CP(DSS_DATA0), (IDIS | PTD | EN | M0)); /*DSS_DATA0*/ + MUX_VAL(CP(DSS_DATA1), (IDIS | PTD | EN | M0)); /*DSS_DATA1*/ + MUX_VAL(CP(DSS_DATA2), (IDIS | PTD | EN | M0)); /*DSS_DATA2*/ + MUX_VAL(CP(DSS_DATA3), (IDIS | PTD | EN | M0)); /*DSS_DATA3*/ + MUX_VAL(CP(DSS_DATA4), (IDIS | PTD | EN | M0)); /*DSS_DATA4*/ + MUX_VAL(CP(DSS_DATA5), (IDIS | PTD | EN | M0)); /*DSS_DATA5*/ + MUX_VAL(CP(DSS_DATA6), (IDIS | PTD | EN | M0)); /*DSS_DATA6*/ + MUX_VAL(CP(DSS_DATA7), (IDIS | PTD | EN | M0)); /*DSS_DATA7*/ + MUX_VAL(CP(DSS_DATA8), (IDIS | PTD | EN | M0)); /*DSS_DATA8*/ + MUX_VAL(CP(DSS_DATA9), (IDIS | PTD | EN | M0)); /*DSS_DATA9*/ + MUX_VAL(CP(DSS_DATA10), (IDIS | PTD | EN | M0)); /*DSS_DATA10*/ + MUX_VAL(CP(DSS_DATA11), (IDIS | PTD | EN | M0)); /*DSS_DATA11*/ + MUX_VAL(CP(DSS_DATA12), (IDIS | PTD | EN | M0)); /*DSS_DATA12*/ + MUX_VAL(CP(DSS_DATA13), (IDIS | PTD | EN | M0)); /*DSS_DATA13*/ + MUX_VAL(CP(DSS_DATA14), (IDIS | PTD | EN | M0)); /*DSS_DATA14*/ + MUX_VAL(CP(DSS_DATA15), (IDIS | PTD | EN | M0)); /*DSS_DATA15*/ + MUX_VAL(CP(DSS_DATA16), (IDIS | PTD | EN | M0)); /*DSS_DATA16*/ + MUX_VAL(CP(DSS_DATA17), (IDIS | PTD | EN | M0)); /*DSS_DATA17*/ + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | EN | M0)); /*DSS_DATA18*/ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | EN | M0)); /*DSS_DATA19*/ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | EN | M0)); /*DSS_DATA20*/ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | EN | M0)); /*DSS_DATA21*/ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | EN | M0)); /*DSS_DATA22*/ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | EN | M0)); /*DSS_DATA23*/ +#endif +#endif + + /*Expansion card */ + MUX_VAL(CP(MMC1_CLK), (IDIS | PTU | EN | M0)); /*MMC1_CLK*/ + MUX_VAL(CP(MMC1_CMD), (IEN | PTU | EN | M0)); /*MMC1_CMD*/ + MUX_VAL(CP(MMC1_DAT0), (IEN | PTU | EN | M0)); /*MMC1_DAT0*/ + MUX_VAL(CP(MMC1_DAT1), (IEN | PTU | EN | M0)); /*MMC1_DAT1*/ + MUX_VAL(CP(MMC1_DAT2), (IEN | PTU | EN | M0)); /*MMC1_DAT2*/ + MUX_VAL(CP(MMC1_DAT3), (IEN | PTU | EN | M0)); /*MMC1_DAT3*/ +#ifdef SAFE_MODE_PINS_3 + MUX_VAL(CP(MMC1_DAT4), (IEN | PTD | EN | M7)); /*MMC1_DAT4*/ + MUX_VAL(CP(MMC1_DAT5), (IEN | PTD | EN | M7)); /*MMC1_DAT5*/ + MUX_VAL(CP(MMC1_DAT6), (IEN | PTD | EN | M7)); /*MMC1_DAT6*/ + MUX_VAL(CP(MMC1_DAT7), (IEN | PTD | EN | M7)); /*MMC1_DAT7*/ + /*Wireless LAN */ + MUX_VAL(CP(MMC2_CLK), (IEN | PTD | EN | M7)); /*MMC2_CLK*/ + MUX_VAL(CP(MMC2_CMD), (IEN | PTD | EN | M7)); /*MMC2_CMD*/ + MUX_VAL(CP(MMC2_DAT0), (IEN | PTD | EN | M7)); /*MMC2_DAT0*/ + MUX_VAL(CP(MMC2_DAT1), (IEN | PTD | EN | M7)); /*MMC2_DAT1*/ + MUX_VAL(CP(MMC2_DAT2), (IEN | PTD | EN | M7)); /*MMC2_DAT2*/ + MUX_VAL(CP(MMC2_DAT3), (IEN | PTD | EN | M7)); /*MMC2_DAT3*/ + MUX_VAL(CP(MMC2_DAT4), (IEN | PTD | EN | M7)); /*MMC2_DAT4*/ + MUX_VAL(CP(MMC2_DAT5), (IEN | PTD | EN | M7)); /*MMC2_DAT5*/ + MUX_VAL(CP(MMC2_DAT6), (IEN | PTD | EN | M7)); /*MMC2_DAT6 */ + MUX_VAL(CP(MMC2_DAT7), (IEN | PTD | EN | M7)); /*MMC2_DAT7*/ + /*Bluetooth*/ + MUX_VAL(CP(MCBSP3_DX), (IEN | PTD | EN | M7)); /*McBSP3_DX*/ + MUX_VAL(CP(MCBSP3_DR), (IEN | PTD | EN | M7)); /*McBSP3_DR*/ + MUX_VAL(CP(MCBSP3_CLKX), (IEN | PTD | EN | M7)); /*McBSP3_CLKX */ + MUX_VAL(CP(MCBSP3_FSX), (IEN | PTD | EN | M7)); /*McBSP3_FSX*/ + MUX_VAL(CP(UART2_CTS), (IEN | PTD | EN | M7)); /*UART2_CTS*/ + MUX_VAL(CP(UART2_RTS), (IEN | PTD | EN | M7)); /*UART2_RTS*/ + MUX_VAL(CP(UART2_TX), (IEN | PTD | EN | M7)); /*UART2_TX*/ + MUX_VAL(CP(UART2_RX), (IEN | PTD | EN | M7)); /*UART2_RX*/ +#else + MUX_VAL(CP(MMC1_DAT4), (IEN | PTU | EN | M0)); /*MMC1_DAT4*/ + MUX_VAL(CP(MMC1_DAT5), (IEN | PTU | EN | M0)); /*MMC1_DAT5*/ + MUX_VAL(CP(MMC1_DAT6), (IEN | PTU | EN | M0)); /*MMC1_DAT6*/ + MUX_VAL(CP(MMC1_DAT7), (IEN | PTU | EN | M0)); /*MMC1_DAT7*/ + /*Wireless LAN */ + MUX_VAL(CP(MMC2_CLK), (IEN | PTD | DIS | M0)); /*MMC2_CLK*/ + MUX_VAL(CP(MMC2_CMD), (IEN | PTU | EN | M0)); /*MMC2_CMD*/ + MUX_VAL(CP(MMC2_DAT0), (IEN | PTU | EN | M0)); /*MMC2_DAT0*/ + MUX_VAL(CP(MMC2_DAT1), (IEN | PTU | EN | M0)); /*MMC2_DAT1*/ + MUX_VAL(CP(MMC2_DAT2), (IEN | PTU | EN | M0)); /*MMC2_DAT2*/ + MUX_VAL(CP(MMC2_DAT3), (IEN | PTU | EN | M0)); /*MMC2_DAT3*/ + MUX_VAL(CP(MMC2_DAT4), (IDIS | PTD | DIS | M0)); /*MMC2_DAT4*/ + MUX_VAL(CP(MMC2_DAT5), (IDIS | PTD | DIS | M0)); /*MMC2_DAT5*/ + MUX_VAL(CP(MMC2_DAT6), (IDIS | PTD | DIS | M0)); /*MMC2_DAT6 */ + MUX_VAL(CP(MMC2_DAT7), (IEN | PTU | EN | M0)); /*MMC2_DAT7*/ + /*Bluetooth*/ + MUX_VAL(CP(MCBSP3_DX), (IDIS | PTD | DIS | M0)); /*McBSP3_DX*/ + MUX_VAL(CP(MCBSP3_DR), (IEN | PTD | DIS | M0)); /*McBSP3_DR*/ + MUX_VAL(CP(MCBSP3_CLKX), (IEN | PTD | DIS | M0)); /*McBSP3_CLKX */ + MUX_VAL(CP(MCBSP3_FSX), (IEN | PTD | DIS | M0)); /*McBSP3_FSX*/ + MUX_VAL(CP(UART2_CTS), (IEN | PTU | EN | M0)); /*UART2_CTS*/ + MUX_VAL(CP(UART2_RTS), (IDIS | PTD | DIS | M0)); /*UART2_RTS*/ + MUX_VAL(CP(UART2_TX), (IDIS | PTD | DIS | M0)); /*UART2_TX*/ + MUX_VAL(CP(UART2_RX), (IEN | PTD | DIS | M0)); /*UART2_RX*/ +#endif + /*Modem Interface */ + MUX_VAL(CP(UART1_TX), (IDIS | PTD | DIS | M0)); /*UART1_TX*/ + MUX_VAL(CP(UART1_RTS), (IDIS | PTD | DIS | M0)); /*UART1_RTS*/ + MUX_VAL(CP(UART1_CTS), (IEN | PTU | DIS | M0)); /*UART1_CTS*/ + MUX_VAL(CP(UART1_RX), (IEN | PTD | DIS | M0)); /*UART1_RX*/ +#ifdef SAFE_MODE_PINS_4 + MUX_VAL(CP(MCBSP4_CLKX), (IEN | PTD | EN | M7)); /*GPIO_152*/ + /* - LCD_INI*/ + MUX_VAL(CP(MCBSP4_DR), (IEN | PTD | EN | M7)); /*GPIO_153*/ + /* - LCD_ENVDD */ +#if 1 +#if 1 + /* SOM doesn't use GPIO_154 for backlight pwr */ + MUX_VAL(CP(MCBSP4_DX), (IDIS | PTD | EN | M7)); /*GPIO_154*/ +#else + MUX_VAL(CP(MCBSP4_DX), (IDIS | PTD | EN | M4)); /*GPIO_154*/ +#endif + MUX_VAL(CP(MCBSP4_FSX), (IDIS | PTD | EN | M4)); /*GPIO_155*/ +#else + MUX_VAL(CP(MCBSP4_DX), (IEN | PTD | EN | M7)); /*GPIO_154*/ + /* - LCD_QVGA/nVGA */ + MUX_VAL(CP(MCBSP4_FSX), (IEN | PTD | EN | M7)); /*GPIO_155*/ + /* - LCD_RESB */ +#endif + + MUX_VAL(CP(MCBSP1_CLKR), (IEN | PTD | EN | M7)); /*MCBSP1_CLKR */ + MUX_VAL(CP(MCBSP1_FSR), (IEN | PTD | EN | M7)); /*MCBSP1_FSR*/ + MUX_VAL(CP(MCBSP1_DX), (IEN | PTD | EN | M7)); /*MCBSP1_DX*/ + MUX_VAL(CP(MCBSP1_DR), (IEN | PTD | EN | M7)); /*MCBSP1_DR*/ + MUX_VAL(CP(MCBSP_CLKS), (IEN | PTD | EN | M7)); /*MCBSP_CLKS */ + MUX_VAL(CP(MCBSP1_FSX), (IEN | PTD | EN | M7)); /*MCBSP1_FSX*/ + MUX_VAL(CP(MCBSP1_CLKX), (IEN | PTD | EN | M7)); /*MCBSP1_CLKX */ + /*Serial Interface*/ + MUX_VAL(CP(UART3_CTS_RCTX), (IEN | PTD | EN | M7)); /*UART3_CTS_*/ + /* RCTX*/ + MUX_VAL(CP(UART3_RTS_SD), (IEN | PTD | EN | M7)); /*UART3_RTS_SD */ + MUX_VAL(CP(UART3_RX_IRRX), (IEN | PTD | EN | M7)); /*UART3_RX_IRRX*/ + MUX_VAL(CP(UART3_TX_IRTX), (IEN | PTD | EN | M7)); /*UART3_TX_IRTX*/ + MUX_VAL(CP(HSUSB0_CLK), (IEN | PTD | EN | M7)); /*HSUSB0_CLK*/ + MUX_VAL(CP(HSUSB0_STP), (IEN | PTD | EN | M7)); /*HSUSB0_STP*/ + MUX_VAL(CP(HSUSB0_DIR), (IEN | PTD | EN | M7)); /*HSUSB0_DIR*/ + MUX_VAL(CP(HSUSB0_NXT), (IEN | PTD | EN | M7)); /*HSUSB0_NXT*/ + MUX_VAL(CP(HSUSB0_DATA0), (IEN | PTD | EN | M7)); /*HSUSB0_DATA0*/ + MUX_VAL(CP(HSUSB0_DATA1), (IEN | PTD | EN | M7)); /*HSUSB0_DATA1*/ + MUX_VAL(CP(HSUSB0_DATA2), (IEN | PTD | EN | M7)); /*HSUSB0_DATA2*/ + MUX_VAL(CP(HSUSB0_DATA3), (IEN | PTD | EN | M7)); /*HSUSB0_DATA3*/ + MUX_VAL(CP(HSUSB0_DATA4), (IEN | PTD | EN | M7)); /*HSUSB0_DATA4*/ + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | EN | M7)); /*HSUSB0_DATA5*/ + MUX_VAL(CP(HSUSB0_DATA6), (IEN | PTD | EN | M7)); /*HSUSB0_DATA6*/ + MUX_VAL(CP(HSUSB0_DATA7), (IEN | PTD | EN | M7)); /*HSUSB0_DATA7*/ +#else + MUX_VAL(CP(MCBSP4_CLKX), (IDIS | PTD | DIS | M4)); /*GPIO_152*/ + /* - LCD_INI*/ + MUX_VAL(CP(MCBSP4_DR), (IDIS | PTD | DIS | M4)); /*GPIO_153*/ + /* - LCD_ENVDD */ + MUX_VAL(CP(MCBSP4_DX), (IDIS | PTD | DIS | M4)); /*GPIO_154*/ + /* - LCD_QVGA/nVGA */ + MUX_VAL(CP(MCBSP4_FSX), (IDIS | PTD | DIS | M4)); /*GPIO_155*/ + /* - LCD_RESB */ + MUX_VAL(CP(MCBSP1_CLKR), (IEN | PTD | DIS | M0)); /*MCBSP1_CLKR */ + MUX_VAL(CP(MCBSP1_FSR), (IDIS | PTU | EN | M0)); /*MCBSP1_FSR*/ + MUX_VAL(CP(MCBSP1_DX), (IDIS | PTD | DIS | M0)); /*MCBSP1_DX*/ + MUX_VAL(CP(MCBSP1_DR), (IEN | PTD | DIS | M0)); /*MCBSP1_DR*/ + MUX_VAL(CP(MCBSP_CLKS), (IEN | PTU | DIS | M0)); /*MCBSP_CLKS */ + MUX_VAL(CP(MCBSP1_FSX), (IEN | PTD | DIS | M0)); /*MCBSP1_FSX*/ + MUX_VAL(CP(MCBSP1_CLKX), (IEN | PTD | DIS | M0)); /*MCBSP1_CLKX */ + /*Serial Interface*/ + MUX_VAL(CP(UART3_CTS_RCTX), (IEN | PTD | EN | M0)); /*UART3_CTS_*/ + /* RCTX*/ + MUX_VAL(CP(UART3_RTS_SD), (IDIS | PTD | DIS | M0)); /*UART3_RTS_SD */ + MUX_VAL(CP(UART3_RX_IRRX), (IEN | PTD | DIS | M0)); /*UART3_RX_IRRX*/ + MUX_VAL(CP(UART3_TX_IRTX), (IDIS | PTD | DIS | M0)); /*UART3_TX_IRTX*/ + MUX_VAL(CP(HSUSB0_CLK), (IEN | PTD | DIS | M0)); /*HSUSB0_CLK*/ + MUX_VAL(CP(HSUSB0_STP), (IDIS | PTU | EN | M0)); /*HSUSB0_STP*/ + MUX_VAL(CP(HSUSB0_DIR), (IEN | PTD | DIS | M0)); /*HSUSB0_DIR*/ + MUX_VAL(CP(HSUSB0_NXT), (IEN | PTD | DIS | M0)); /*HSUSB0_NXT*/ + MUX_VAL(CP(HSUSB0_DATA0), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA0*/ + MUX_VAL(CP(HSUSB0_DATA1), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA1*/ + MUX_VAL(CP(HSUSB0_DATA2), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA2*/ + MUX_VAL(CP(HSUSB0_DATA3), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA3*/ + MUX_VAL(CP(HSUSB0_DATA4), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA4*/ + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA5*/ + MUX_VAL(CP(HSUSB0_DATA6), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA6*/ + MUX_VAL(CP(HSUSB0_DATA7), (IEN | PTD | DIS | M0)); /*HSUSB0_DATA7*/ +#endif + MUX_VAL(CP(I2C1_SCL), (IEN | PTU | EN | M0)); /*I2C1_SCL*/ + MUX_VAL(CP(I2C1_SDA), (IEN | PTU | EN | M0)); /*I2C1_SDA*/ +#ifdef SAFE_MODE_PINS_5 + MUX_VAL(CP(I2C2_SCL), (IEN | PTD | EN | M7)); /*I2C2_SCL*/ + MUX_VAL(CP(I2C2_SDA), (IEN | PTD | EN | M7)); /*I2C2_SDA*/ + MUX_VAL(CP(I2C3_SCL), (IEN | PTD | EN | M7)); /*I2C3_SCL*/ + MUX_VAL(CP(I2C3_SDA), (IEN | PTD | EN | M7)); /*I2C3_SDA*/ + MUX_VAL(CP(I2C4_SCL), (IEN | PTD | EN | M7)); /*I2C4_SCL*/ + MUX_VAL(CP(I2C4_SDA), (IEN | PTD | EN | M7)); /*I2C4_SDA*/ +#else + MUX_VAL(CP(I2C2_SCL), (IEN | PTU | EN | M0)); /*I2C2_SCL*/ + MUX_VAL(CP(I2C2_SDA), (IEN | PTU | EN | M0)); /*I2C2_SDA*/ + MUX_VAL(CP(I2C3_SCL), (IEN | PTU | EN | M0)); /*I2C3_SCL*/ + MUX_VAL(CP(I2C3_SDA), (IEN | PTU | EN | M0)); /*I2C3_SDA*/ + MUX_VAL(CP(I2C4_SCL), (IEN | PTU | EN | M0)); /*I2C4_SCL*/ + MUX_VAL(CP(I2C4_SDA), (IEN | PTU | EN | M0)); /*I2C4_SDA*/ +#endif + MUX_VAL(CP(HDQ_SIO), (IEN | PTU | EN | M0)); /*HDQ_SIO*/ +#ifdef SAFE_MODE_PINS_5A + MUX_VAL(CP(MCSPI1_CLK), (IEN | PTD | EN | M7)); /*McSPI1_CLK*/ + MUX_VAL(CP(MCSPI1_SIMO), (IEN | PTD | EN | M7)); /*McSPI1_SIMO */ + MUX_VAL(CP(MCSPI1_SOMI), (IEN | PTD | EN | M7)); /*McSPI1_SOMI */ + MUX_VAL(CP(MCSPI1_CS0), (IEN | PTD | EN | M7)); /*McSPI1_CS0*/ + MUX_VAL(CP(MCSPI1_CS1), (IEN | PTD | EN | M7)); /*GPIO_175*/ + /* TS_PEN_IRQ */ + MUX_VAL(CP(MCSPI1_CS2), (IEN | PTD | EN | M7)); /*GPIO_176*/ + /* - LAN_INTR*/ + MUX_VAL(CP(MCSPI1_CS3), (IEN | PTD | EN | M7)); /*McSPI1_CS3*/ + MUX_VAL(CP(MCSPI2_CLK), (IEN | PTD | EN | M7)); /*McSPI2_CLK*/ + MUX_VAL(CP(MCSPI2_SIMO), (IEN | PTD | EN | M7)); /*McSPI2_SIMO*/ + MUX_VAL(CP(MCSPI2_SOMI), (IEN | PTD | EN | M7)); /*McSPI2_SOMI*/ + MUX_VAL(CP(MCSPI2_CS0), (IEN | PTD | EN | M7)); /*McSPI2_CS0*/ + MUX_VAL(CP(MCSPI2_CS1), (IEN | PTD | EN | M7)); /*McSPI2_CS1*/ +#else + MUX_VAL(CP(MCSPI1_CLK), (IEN | PTD | DIS | M0)); /*McSPI1_CLK*/ + MUX_VAL(CP(MCSPI1_SIMO), (IEN | PTD | DIS | M0)); /*McSPI1_SIMO */ + MUX_VAL(CP(MCSPI1_SOMI), (IEN | PTD | DIS | M0)); /*McSPI1_SOMI */ + MUX_VAL(CP(MCSPI1_CS0), (IEN | PTD | EN | M0)); /*McSPI1_CS0*/ + MUX_VAL(CP(MCSPI1_CS1), (IEN | PTD | EN | M4)); /*GPIO_175*/ + /* TS_PEN_IRQ */ + MUX_VAL(CP(MCSPI1_CS2), (IEN | PTU | DIS | M4)); /*GPIO_176*/ + /* - LAN_INTR*/ + MUX_VAL(CP(MCSPI1_CS3), (IEN | PTD | EN | M0)); /*McSPI1_CS3*/ + MUX_VAL(CP(MCSPI2_CLK), (IEN | PTD | DIS | M0)); /*McSPI2_CLK*/ + MUX_VAL(CP(MCSPI2_SIMO), (IEN | PTD | DIS | M0)); /*McSPI2_SIMO*/ + MUX_VAL(CP(MCSPI2_SOMI), (IEN | PTD | DIS | M0)); /*McSPI2_SOMI*/ + MUX_VAL(CP(MCSPI2_CS0), (IEN | PTD | EN | M0)); /*McSPI2_CS0*/ + MUX_VAL(CP(MCSPI2_CS1), (IEN | PTD | EN | M0)); /*McSPI2_CS1*/ +#endif + /*Control and debug */ + MUX_VAL(CP(SYS_32K), (IEN | PTD | DIS | M0)); /*SYS_32K*/ + MUX_VAL(CP(SYS_CLKREQ), (IEN | PTD | DIS | M0)); /*SYS_CLKREQ*/ + MUX_VAL(CP(SYS_NIRQ), (IEN | PTU | EN | M0)); /*SYS_nIRQ*/ +#ifdef SAFE_MODE_PINS_6 + MUX_VAL(CP(SYS_BOOT0), (IEN | PTD | EN | M7)); /*GPIO_2*/ + /* - PEN_IRQ */ + MUX_VAL(CP(SYS_BOOT1), (IEN | PTD | EN | M7)); /*GPIO_3 */ + MUX_VAL(CP(SYS_BOOT2), (IEN | PTD | EN | M7)); /*GPIO_4*/ + MUX_VAL(CP(SYS_BOOT3), (IEN | PTD | EN | M7)); /*GPIO_5*/ + MUX_VAL(CP(SYS_BOOT4), (IEN | PTD | EN | M7)); /*GPIO_6*/ + MUX_VAL(CP(SYS_BOOT5), (IEN | PTD | EN | M7)); /*GPIO_7*/ +#if 1 + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTU | DIS | M4)); /*SOM BACKLIGHT PWR*/ +#else + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTD | EN | M7)); /*GPIO_8*/ +#endif +#else + MUX_VAL(CP(SYS_BOOT0), (IEN | PTD | DIS | M4)); /*GPIO_2*/ + /* - PEN_IRQ */ + MUX_VAL(CP(SYS_BOOT1), (IEN | PTD | DIS | M4)); /*GPIO_3 */ + MUX_VAL(CP(SYS_BOOT2), (IEN | PTD | DIS | M4)); /*GPIO_4*/ + MUX_VAL(CP(SYS_BOOT3), (IEN | PTD | DIS | M4)); /*GPIO_5*/ + MUX_VAL(CP(SYS_BOOT4), (IEN | PTD | DIS | M4)); /*GPIO_6*/ + MUX_VAL(CP(SYS_BOOT5), (IEN | PTD | DIS | M4)); /*GPIO_7*/ + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTD | DIS | M4)); /*GPIO_8*/ +#endif /* - VIO_1V8*/ + MUX_VAL(CP(SYS_OFF_MODE), (IEN | PTD | DIS | M0)); /*SYS_OFF_MODE*/ + MUX_VAL(CP(SYS_CLKOUT1), (IEN | PTD | DIS | M0)); /*SYS_CLKOUT1*/ + MUX_VAL(CP(SYS_CLKOUT2), (IEN | PTU | EN | M0)); /*SYS_CLKOUT2*/ + MUX_VAL(CP(JTAG_nTRST), (IEN | PTD | DIS | M0)); /*JTAG_nTRST*/ + MUX_VAL(CP(JTAG_TCK), (IEN | PTD | DIS | M0)); /*JTAG_TCK*/ + MUX_VAL(CP(JTAG_TMS), (IEN | PTD | DIS | M0)); /*JTAG_TMS*/ + MUX_VAL(CP(JTAG_TDI), (IEN | PTD | DIS | M0)); /*JTAG_TDI*/ + MUX_VAL(CP(JTAG_EMU0), (IEN | PTD | DIS | M0)); /*JTAG_EMU0*/ + MUX_VAL(CP(JTAG_EMU1), (IEN | PTD | DIS | M0)); /*JTAG_EMU1*/ + MUX_VAL(CP(ETK_CLK_ES2), (IDIS | PTU | EN | M0)); /*ETK_CLK*/ + MUX_VAL(CP(ETK_CTL_ES2), (IDIS | PTD | DIS | M0)); /*ETK_CTL*/ + MUX_VAL(CP(ETK_D0_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D0*/ + MUX_VAL(CP(ETK_D1_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D1*/ + MUX_VAL(CP(ETK_D2_ES2 ), (IEN | PTD | EN | M0)); /*ETK_D2*/ + MUX_VAL(CP(ETK_D3_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D3*/ + MUX_VAL(CP(ETK_D4_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D4*/ + MUX_VAL(CP(ETK_D5_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D5*/ + MUX_VAL(CP(ETK_D6_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D6*/ + MUX_VAL(CP(ETK_D7_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D7*/ + MUX_VAL(CP(ETK_D8_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D8*/ + MUX_VAL(CP(ETK_D9_ES2 ), (IEN | PTD | DIS | M0)); /*ETK_D9*/ + MUX_VAL(CP(ETK_D10_ES2), (IEN | PTD | DIS | M0)); /*ETK_D10*/ + MUX_VAL(CP(ETK_D11_ES2), (IEN | PTD | DIS | M0)); /*ETK_D11*/ + MUX_VAL(CP(ETK_D12_ES2), (IEN | PTD | DIS | M0)); /*ETK_D12*/ + MUX_VAL(CP(ETK_D13_ES2), (IEN | PTD | DIS | M0)); /*ETK_D13*/ + MUX_VAL(CP(ETK_D14_ES2), (IEN | PTD | DIS | M0)); /*ETK_D14*/ + MUX_VAL(CP(ETK_D15_ES2), (IEN | PTD | DIS | M0)); /*ETK_D15*/ + /*Die to Die */ + MUX_VAL(CP(D2D_MCAD1), (IEN | PTD | EN | M0)); /*d2d_mcad1*/ + MUX_VAL(CP(D2D_MCAD2), (IEN | PTD | EN | M0)); /*d2d_mcad2*/ + MUX_VAL(CP(D2D_MCAD3), (IEN | PTD | EN | M0)); /*d2d_mcad3*/ + MUX_VAL(CP(D2D_MCAD4), (IEN | PTD | EN | M0)); /*d2d_mcad4*/ + MUX_VAL(CP(D2D_MCAD5), (IEN | PTD | EN | M0)); /*d2d_mcad5*/ + MUX_VAL(CP(D2D_MCAD6), (IEN | PTD | EN | M0)); /*d2d_mcad6*/ + MUX_VAL(CP(D2D_MCAD7), (IEN | PTD | EN | M0)); /*d2d_mcad7*/ + MUX_VAL(CP(D2D_MCAD8), (IEN | PTD | EN | M0)); /*d2d_mcad8*/ + MUX_VAL(CP(D2D_MCAD9), (IEN | PTD | EN | M0)); /*d2d_mcad9*/ + MUX_VAL(CP(D2D_MCAD10), (IEN | PTD | EN | M0)); /*d2d_mcad10*/ + MUX_VAL(CP(D2D_MCAD11), (IEN | PTD | EN | M0)); /*d2d_mcad11*/ + MUX_VAL(CP(D2D_MCAD12), (IEN | PTD | EN | M0)); /*d2d_mcad12*/ + MUX_VAL(CP(D2D_MCAD13), (IEN | PTD | EN | M0)); /*d2d_mcad13*/ + MUX_VAL(CP(D2D_MCAD14), (IEN | PTD | EN | M0)); /*d2d_mcad14*/ + MUX_VAL(CP(D2D_MCAD15), (IEN | PTD | EN | M0)); /*d2d_mcad15*/ + MUX_VAL(CP(D2D_MCAD16), (IEN | PTD | EN | M0)); /*d2d_mcad16*/ + MUX_VAL(CP(D2D_MCAD17), (IEN | PTD | EN | M0)); /*d2d_mcad17*/ + MUX_VAL(CP(D2D_MCAD18), (IEN | PTD | EN | M0)); /*d2d_mcad18*/ + MUX_VAL(CP(D2D_MCAD19), (IEN | PTD | EN | M0)); /*d2d_mcad19*/ + MUX_VAL(CP(D2D_MCAD20), (IEN | PTD | EN | M0)); /*d2d_mcad20*/ + MUX_VAL(CP(D2D_MCAD21), (IEN | PTD | EN | M0)); /*d2d_mcad21*/ + MUX_VAL(CP(D2D_MCAD22), (IEN | PTD | EN | M0)); /*d2d_mcad22*/ + MUX_VAL(CP(D2D_MCAD23), (IEN | PTD | EN | M0)); /*d2d_mcad23*/ + MUX_VAL(CP(D2D_MCAD24), (IEN | PTD | EN | M0)); /*d2d_mcad24*/ + MUX_VAL(CP(D2D_MCAD25), (IEN | PTD | EN | M0)); /*d2d_mcad25*/ + MUX_VAL(CP(D2D_MCAD26), (IEN | PTD | EN | M0)); /*d2d_mcad26*/ + MUX_VAL(CP(D2D_MCAD27), (IEN | PTD | EN | M0)); /*d2d_mcad27*/ + MUX_VAL(CP(D2D_MCAD28), (IEN | PTD | EN | M0)); /*d2d_mcad28*/ + MUX_VAL(CP(D2D_MCAD29), (IEN | PTD | EN | M0)); /*d2d_mcad29*/ + MUX_VAL(CP(D2D_MCAD30), (IEN | PTD | EN | M0)); /*d2d_mcad30*/ + MUX_VAL(CP(D2D_MCAD31), (IEN | PTD | EN | M0)); /*d2d_mcad31*/ + MUX_VAL(CP(D2D_MCAD32), (IEN | PTD | EN | M0)); /*d2d_mcad32*/ + MUX_VAL(CP(D2D_MCAD33), (IEN | PTD | EN | M0)); /*d2d_mcad33*/ + MUX_VAL(CP(D2D_MCAD34), (IEN | PTD | EN | M0)); /*d2d_mcad34*/ + MUX_VAL(CP(D2D_MCAD35), (IEN | PTD | EN | M0)); /*d2d_mcad35*/ + MUX_VAL(CP(D2D_MCAD36), (IEN | PTD | EN | M0)); /*d2d_mcad36*/ + MUX_VAL(CP(D2D_CLK26MI), (IEN | PTD | DIS | M0)); /*d2d_clk26mi*/ + MUX_VAL(CP(D2D_NRESPWRON), (IEN | PTD | EN | M0)); /*d2d_nrespwron*/ + MUX_VAL(CP(D2D_NRESWARM), (IEN | PTU | EN | M0)); /*d2d_nreswarm */ + MUX_VAL(CP(D2D_ARM9NIRQ), (IEN | PTD | DIS | M0)); /*d2d_arm9nirq */ + MUX_VAL(CP(D2D_UMA2P6FIQ), (IEN | PTD | DIS | M0)); /*d2d_uma2p6fiq*/ + MUX_VAL(CP(D2D_SPINT), (IEN | PTD | EN | M0)); /*d2d_spint*/ + MUX_VAL(CP(D2D_FRINT), (IEN | PTD | EN | M0)); /*d2d_frint*/ + MUX_VAL(CP(D2D_DMAREQ0), (IEN | PTD | DIS | M0)); /*d2d_dmareq0*/ + MUX_VAL(CP(D2D_DMAREQ1), (IEN | PTD | DIS | M0)); /*d2d_dmareq1*/ + MUX_VAL(CP(D2D_DMAREQ2), (IEN | PTD | DIS | M0)); /*d2d_dmareq2*/ + MUX_VAL(CP(D2D_DMAREQ3), (IEN | PTD | DIS | M0)); /*d2d_dmareq3*/ + MUX_VAL(CP(D2D_N3GTRST), (IEN | PTD | DIS | M0)); /*d2d_n3gtrst*/ + MUX_VAL(CP(D2D_N3GTDI), (IEN | PTD | DIS | M0)); /*d2d_n3gtdi*/ + MUX_VAL(CP(D2D_N3GTDO), (IEN | PTD | DIS | M0)); /*d2d_n3gtdo*/ + MUX_VAL(CP(D2D_N3GTMS), (IEN | PTD | DIS | M0)); /*d2d_n3gtms*/ + MUX_VAL(CP(D2D_N3GTCK), (IEN | PTD | DIS | M0)); /*d2d_n3gtck*/ + MUX_VAL(CP(D2D_N3GRTCK), (IEN | PTD | DIS | M0)); /*d2d_n3grtck*/ + MUX_VAL(CP(D2D_MSTDBY), (IEN | PTU | EN | M0)); /*d2d_mstdby*/ + MUX_VAL(CP(D2D_SWAKEUP), (IEN | PTD | EN | M0)); /*d2d_swakeup*/ + MUX_VAL(CP(D2D_IDLEREQ), (IEN | PTD | DIS | M0)); /*d2d_idlereq*/ + MUX_VAL(CP(D2D_IDLEACK), (IEN | PTU | EN | M0)); /*d2d_idleack*/ + MUX_VAL(CP(D2D_MWRITE), (IEN | PTD | DIS | M0)); /*d2d_mwrite*/ + MUX_VAL(CP(D2D_SWRITE), (IEN | PTD | DIS | M0)); /*d2d_swrite*/ + MUX_VAL(CP(D2D_MREAD), (IEN | PTD | DIS | M0)); /*d2d_mread*/ + MUX_VAL(CP(D2D_SREAD), (IEN | PTD | DIS | M0)); /*d2d_sread*/ + MUX_VAL(CP(D2D_MBUSFLAG), (IEN | PTD | DIS | M0)); /*d2d_mbusflag*/ + MUX_VAL(CP(D2D_SBUSFLAG), (IEN | PTD | DIS | M0)); /*d2d_sbusflag*/ + MUX_VAL(CP(SDRC_CKE0), (IDIS | PTU | EN | M0)); /*sdrc_cke0*/ + MUX_VAL(CP(SDRC_CKE1), (IDIS | PTD | DIS | M7)); /*sdrc_cke1*/ +} diff --git a/board/ti/logic/logic.h b/board/ti/logic/logic.h new file mode 100644 index 00000000000..a880281e0e2 --- /dev/null +++ b/board/ti/logic/logic.h @@ -0,0 +1,412 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * + * Author: + * Peter Barada <peter.barada@logicpd.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 _EVM_H_ +#define _EVM_H_ + +const omap3_sysinfo sysinfo = { + DDR_DISCRETE, + "Logic DM37x/OMAP35x reference board", +#if defined(CONFIG_ENV_IS_IN_ONENAND) + "OneNAND", +#else + "NAND", +#endif +}; + +/* + * OMAP35x EVM revision + * Run time detection of EVM revision is done by reading Ethernet + * PHY ID - + * GEN_1 = 0x01150000 + * GEN_2 = 0x92200000 + */ +enum { + OMAP3EVM_BOARD_GEN_1 = 0, /* EVM Rev between A - D */ + OMAP3EVM_BOARD_GEN_2, /* EVM Rev >= Rev E */ +}; + +u32 get_omap3_evm_rev(void); + +#if defined(CONFIG_CMD_NET) +static void setup_net_chip(void); +#endif + +/* + * IEN - Input Enable + * IDIS - Input Disable + * PTD - Pull type Down + * PTU - Pull type Up + * DIS - Pull type selection is inactive + * EN - Pull type selection is active + * M0 - Mode 0 + * The commented string gives the final mux configuration for that pin + */ +#define MUX_EVM() \ + /*SDRC*/\ + MUX_VAL(CP(SDRC_D0), (IEN | PTD | DIS | M0)) /*SDRC_D0*/\ + MUX_VAL(CP(SDRC_D1), (IEN | PTD | DIS | M0)) /*SDRC_D1*/\ + MUX_VAL(CP(SDRC_D2), (IEN | PTD | DIS | M0)) /*SDRC_D2*/\ + MUX_VAL(CP(SDRC_D3), (IEN | PTD | DIS | M0)) /*SDRC_D3*/\ + MUX_VAL(CP(SDRC_D4), (IEN | PTD | DIS | M0)) /*SDRC_D4*/\ + MUX_VAL(CP(SDRC_D5), (IEN | PTD | DIS | M0)) /*SDRC_D5*/\ + MUX_VAL(CP(SDRC_D6), (IEN | PTD | DIS | M0)) /*SDRC_D6*/\ + MUX_VAL(CP(SDRC_D7), (IEN | PTD | DIS | M0)) /*SDRC_D7*/\ + MUX_VAL(CP(SDRC_D8), (IEN | PTD | DIS | M0)) /*SDRC_D8*/\ + MUX_VAL(CP(SDRC_D9), (IEN | PTD | DIS | M0)) /*SDRC_D9*/\ + MUX_VAL(CP(SDRC_D10), (IEN | PTD | DIS | M0)) /*SDRC_D10*/\ + MUX_VAL(CP(SDRC_D11), (IEN | PTD | DIS | M0)) /*SDRC_D11*/\ + MUX_VAL(CP(SDRC_D12), (IEN | PTD | DIS | M0)) /*SDRC_D12*/\ + MUX_VAL(CP(SDRC_D13), (IEN | PTD | DIS | M0)) /*SDRC_D13*/\ + MUX_VAL(CP(SDRC_D14), (IEN | PTD | DIS | M0)) /*SDRC_D14*/\ + MUX_VAL(CP(SDRC_D15), (IEN | PTD | DIS | M0)) /*SDRC_D15*/\ + MUX_VAL(CP(SDRC_D16), (IEN | PTD | DIS | M0)) /*SDRC_D16*/\ + MUX_VAL(CP(SDRC_D17), (IEN | PTD | DIS | M0)) /*SDRC_D17*/\ + MUX_VAL(CP(SDRC_D18), (IEN | PTD | DIS | M0)) /*SDRC_D18*/\ + MUX_VAL(CP(SDRC_D19), (IEN | PTD | DIS | M0)) /*SDRC_D19*/\ + MUX_VAL(CP(SDRC_D20), (IEN | PTD | DIS | M0)) /*SDRC_D20*/\ + MUX_VAL(CP(SDRC_D21), (IEN | PTD | DIS | M0)) /*SDRC_D21*/\ + MUX_VAL(CP(SDRC_D22), (IEN | PTD | DIS | M0)) /*SDRC_D22*/\ + MUX_VAL(CP(SDRC_D23), (IEN | PTD | DIS | M0)) /*SDRC_D23*/\ + MUX_VAL(CP(SDRC_D24), (IEN | PTD | DIS | M0)) /*SDRC_D24*/\ + MUX_VAL(CP(SDRC_D25), (IEN | PTD | DIS | M0)) /*SDRC_D25*/\ + MUX_VAL(CP(SDRC_D26), (IEN | PTD | DIS | M0)) /*SDRC_D26*/\ + MUX_VAL(CP(SDRC_D27), (IEN | PTD | DIS | M0)) /*SDRC_D27*/\ + MUX_VAL(CP(SDRC_D28), (IEN | PTD | DIS | M0)) /*SDRC_D28*/\ + MUX_VAL(CP(SDRC_D29), (IEN | PTD | DIS | M0)) /*SDRC_D29*/\ + MUX_VAL(CP(SDRC_D30), (IEN | PTD | DIS | M0)) /*SDRC_D30*/\ + MUX_VAL(CP(SDRC_D31), (IEN | PTD | DIS | M0)) /*SDRC_D31*/\ + MUX_VAL(CP(SDRC_CLK), (IEN | PTD | DIS | M0)) /*SDRC_CLK*/\ + MUX_VAL(CP(SDRC_DQS0), (IEN | PTD | DIS | M0)) /*SDRC_DQS0*/\ + MUX_VAL(CP(SDRC_DQS1), (IEN | PTD | DIS | M0)) /*SDRC_DQS1*/\ + MUX_VAL(CP(SDRC_DQS2), (IEN | PTD | DIS | M0)) /*SDRC_DQS2*/\ + MUX_VAL(CP(SDRC_DQS3), (IEN | PTD | DIS | M0)) /*SDRC_DQS3*/\ + /*GPMC*/\ + MUX_VAL(CP(GPMC_A1), (IDIS | PTU | EN | M0)) /*GPMC_A1*/\ + MUX_VAL(CP(GPMC_A2), (IDIS | PTU | EN | M0)) /*GPMC_A2*/\ + MUX_VAL(CP(GPMC_A3), (IDIS | PTU | EN | M0)) /*GPMC_A3*/\ + MUX_VAL(CP(GPMC_A4), (IDIS | PTU | EN | M0)) /*GPMC_A4*/\ + MUX_VAL(CP(GPMC_A5), (IDIS | PTU | EN | M0)) /*GPMC_A5*/\ + MUX_VAL(CP(GPMC_A6), (IDIS | PTU | EN | M0)) /*GPMC_A6*/\ + MUX_VAL(CP(GPMC_A7), (IDIS | PTU | EN | M0)) /*GPMC_A7*/\ + MUX_VAL(CP(GPMC_A8), (IDIS | PTU | EN | M0)) /*GPMC_A8*/\ + MUX_VAL(CP(GPMC_A9), (IDIS | PTU | EN | M0)) /*GPMC_A9*/\ + MUX_VAL(CP(GPMC_A10), (IDIS | PTU | EN | M0)) /*GPMC_A10*/\ + MUX_VAL(CP(GPMC_D0), (IEN | PTU | EN | M0)) /*GPMC_D0*/\ + MUX_VAL(CP(GPMC_D1), (IEN | PTU | EN | M0)) /*GPMC_D1*/\ + MUX_VAL(CP(GPMC_D2), (IEN | PTU | EN | M0)) /*GPMC_D2*/\ + MUX_VAL(CP(GPMC_D3), (IEN | PTU | EN | M0)) /*GPMC_D3*/\ + MUX_VAL(CP(GPMC_D4), (IEN | PTU | EN | M0)) /*GPMC_D4*/\ + MUX_VAL(CP(GPMC_D5), (IEN | PTU | EN | M0)) /*GPMC_D5*/\ + MUX_VAL(CP(GPMC_D6), (IEN | PTU | EN | M0)) /*GPMC_D6*/\ + MUX_VAL(CP(GPMC_D7), (IEN | PTU | EN | M0)) /*GPMC_D7*/\ + MUX_VAL(CP(GPMC_D8), (IEN | PTU | EN | M0)) /*GPMC_D8*/\ + MUX_VAL(CP(GPMC_D9), (IEN | PTU | EN | M0)) /*GPMC_D9*/\ + MUX_VAL(CP(GPMC_D10), (IEN | PTU | EN | M0)) /*GPMC_D10*/\ + MUX_VAL(CP(GPMC_D11), (IEN | PTU | EN | M0)) /*GPMC_D11*/\ + MUX_VAL(CP(GPMC_D12), (IEN | PTU | EN | M0)) /*GPMC_D12*/\ + MUX_VAL(CP(GPMC_D13), (IEN | PTU | EN | M0)) /*GPMC_D13*/\ + MUX_VAL(CP(GPMC_D14), (IEN | PTU | EN | M0)) /*GPMC_D14*/\ + MUX_VAL(CP(GPMC_D15), (IEN | PTU | EN | M0)) /*GPMC_D15*/\ + MUX_VAL(CP(GPMC_NCS0), (IDIS | PTU | EN | M0)) /*GPMC_nCS0*/\ + MUX_VAL(CP(GPMC_NCS1), (IDIS | PTU | EN | M0)) /*GPMC_nCS1*/\ + MUX_VAL(CP(GPMC_NCS2), (IDIS | PTU | EN | M0)) /*GPMC_nCS2*/\ + MUX_VAL(CP(GPMC_NCS3), (IDIS | PTU | EN | M0)) /*GPMC_nCS3*/\ + MUX_VAL(CP(GPMC_NCS4), (IEN | PTU | EN | M0)) /*GPMC_nCS4*/\ + MUX_VAL(CP(GPMC_NCS5), (IDIS | PTU | EN | M0)) /*GPMC_nCS5*/\ + MUX_VAL(CP(GPMC_NCS6), (IEN | PTD | DIS | M0)) /*GPMC_nCS6*/\ + MUX_VAL(CP(GPMC_NCS7), (IEN | PTU | EN | M0)) /*GPMC_nCS7*/\ + MUX_VAL(CP(GPMC_CLK), (IDIS | PTU | EN | M0)) /*GPMC_CLK*/\ + MUX_VAL(CP(GPMC_NADV_ALE), (IDIS | PTD | DIS | M0)) /*GPMC_nADV_ALE*/\ + MUX_VAL(CP(GPMC_NOE), (IDIS | PTD | DIS | M0)) /*GPMC_nOE*/\ + MUX_VAL(CP(GPMC_NWE), (IDIS | PTD | DIS | M0)) /*GPMC_nWE*/\ + MUX_VAL(CP(GPMC_NBE0_CLE), (IDIS | PTU | EN | M0)) /*GPMC_nBE0_CLE*/\ + MUX_VAL(CP(GPMC_NBE1), (IEN | PTU | EN | M0)) /*GPMC_nBE1*/\ + MUX_VAL(CP(GPMC_NWP), (IEN | PTD | DIS | M0)) /*GPMC_nWP*/\ + MUX_VAL(CP(GPMC_WAIT0), (IEN | PTU | EN | M0)) /*GPMC_WAIT0*/\ + MUX_VAL(CP(GPMC_WAIT1), (IEN | PTU | EN | M0)) /*GPMC_WAIT1*/\ + MUX_VAL(CP(GPMC_WAIT2), (IEN | PTU | EN | M4)) /*GPIO_64*/\ + /* - ETH_nRESET*/\ + MUX_VAL(CP(GPMC_WAIT3), (IEN | PTU | EN | M0)) /*GPMC_WAIT3*/\ + /*DSS*/\ + MUX_VAL(CP(DSS_PCLK), (IDIS | PTD | DIS | M0)) /*DSS_PCLK*/\ + MUX_VAL(CP(DSS_HSYNC), (IDIS | PTD | DIS | M0)) /*DSS_HSYNC*/\ + MUX_VAL(CP(DSS_VSYNC), (IDIS | PTD | DIS | M0)) /*DSS_VSYNC*/\ + MUX_VAL(CP(DSS_ACBIAS), (IDIS | PTD | DIS | M0)) /*DSS_ACBIAS*/\ + MUX_VAL(CP(DSS_DATA0), (IDIS | PTD | DIS | M0)) /*DSS_DATA0*/\ + MUX_VAL(CP(DSS_DATA1), (IDIS | PTD | DIS | M0)) /*DSS_DATA1*/\ + MUX_VAL(CP(DSS_DATA2), (IDIS | PTD | DIS | M0)) /*DSS_DATA2*/\ + MUX_VAL(CP(DSS_DATA3), (IDIS | PTD | DIS | M0)) /*DSS_DATA3*/\ + MUX_VAL(CP(DSS_DATA4), (IDIS | PTD | DIS | M0)) /*DSS_DATA4*/\ + MUX_VAL(CP(DSS_DATA5), (IDIS | PTD | DIS | M0)) /*DSS_DATA5*/\ + MUX_VAL(CP(DSS_DATA6), (IDIS | PTD | DIS | M0)) /*DSS_DATA6*/\ + MUX_VAL(CP(DSS_DATA7), (IDIS | PTD | DIS | M0)) /*DSS_DATA7*/\ + MUX_VAL(CP(DSS_DATA8), (IDIS | PTD | DIS | M0)) /*DSS_DATA8*/\ + MUX_VAL(CP(DSS_DATA9), (IDIS | PTD | DIS | M0)) /*DSS_DATA9*/\ + MUX_VAL(CP(DSS_DATA10), (IDIS | PTD | DIS | M0)) /*DSS_DATA10*/\ + MUX_VAL(CP(DSS_DATA11), (IDIS | PTD | DIS | M0)) /*DSS_DATA11*/\ + MUX_VAL(CP(DSS_DATA12), (IDIS | PTD | DIS | M0)) /*DSS_DATA12*/\ + MUX_VAL(CP(DSS_DATA13), (IDIS | PTD | DIS | M0)) /*DSS_DATA13*/\ + MUX_VAL(CP(DSS_DATA14), (IDIS | PTD | DIS | M0)) /*DSS_DATA14*/\ + MUX_VAL(CP(DSS_DATA15), (IDIS | PTD | DIS | M0)) /*DSS_DATA15*/\ + MUX_VAL(CP(DSS_DATA16), (IDIS | PTD | DIS | M0)) /*DSS_DATA16*/\ + MUX_VAL(CP(DSS_DATA17), (IDIS | PTD | DIS | M0)) /*DSS_DATA17*/\ + MUX_VAL(CP(DSS_DATA18), (IDIS | PTD | DIS | M0)) /*DSS_DATA18*/\ + MUX_VAL(CP(DSS_DATA19), (IDIS | PTD | DIS | M0)) /*DSS_DATA19*/\ + MUX_VAL(CP(DSS_DATA20), (IDIS | PTD | DIS | M0)) /*DSS_DATA20*/\ + MUX_VAL(CP(DSS_DATA21), (IDIS | PTD | DIS | M0)) /*DSS_DATA21*/\ + MUX_VAL(CP(DSS_DATA22), (IDIS | PTD | DIS | M0)) /*DSS_DATA22*/\ + MUX_VAL(CP(DSS_DATA23), (IDIS | PTD | DIS | M0)) /*DSS_DATA23*/\ + /*CAMERA*/\ + MUX_VAL(CP(CAM_HS), (IEN | PTU | EN | M0)) /*CAM_HS */\ + MUX_VAL(CP(CAM_VS), (IEN | PTU | EN | M0)) /*CAM_VS */\ + MUX_VAL(CP(CAM_XCLKA), (IDIS | PTD | DIS | M0)) /*CAM_XCLKA*/\ + MUX_VAL(CP(CAM_PCLK), (IEN | PTU | EN | M0)) /*CAM_PCLK*/\ + MUX_VAL(CP(CAM_FLD), (IDIS | PTD | DIS | M4)) /*GPIO_98*/\ + /* - CAM_RESET*/\ + MUX_VAL(CP(CAM_D0), (IEN | PTD | DIS | M0)) /*CAM_D0*/\ + MUX_VAL(CP(CAM_D1), (IEN | PTD | DIS | M0)) /*CAM_D1*/\ + MUX_VAL(CP(CAM_D2), (IEN | PTD | DIS | M0)) /*CAM_D2*/\ + MUX_VAL(CP(CAM_D3), (IEN | PTD | DIS | M0)) /*CAM_D3*/\ + MUX_VAL(CP(CAM_D4), (IEN | PTD | DIS | M0)) /*CAM_D4*/\ + MUX_VAL(CP(CAM_D5), (IEN | PTD | DIS | M0)) /*CAM_D5*/\ + MUX_VAL(CP(CAM_D6), (IEN | PTD | DIS | M0)) /*CAM_D6*/\ + MUX_VAL(CP(CAM_D7), (IEN | PTD | DIS | M0)) /*CAM_D7*/\ + MUX_VAL(CP(CAM_D8), (IEN | PTD | DIS | M0)) /*CAM_D8*/\ + MUX_VAL(CP(CAM_D9), (IEN | PTD | DIS | M0)) /*CAM_D9*/\ + MUX_VAL(CP(CAM_D10), (IEN | PTD | DIS | M0)) /*CAM_D10*/\ + MUX_VAL(CP(CAM_D11), (IEN | PTD | DIS | M0)) /*CAM_D11*/\ + MUX_VAL(CP(CAM_XCLKB), (IDIS | PTD | DIS | M0)) /*CAM_XCLKB*/\ + MUX_VAL(CP(CAM_WEN), (IEN | PTD | DIS | M4)) /*GPIO_167*/\ + MUX_VAL(CP(CAM_STROBE), (IDIS | PTD | DIS | M0)) /*CAM_STROBE*/\ + MUX_VAL(CP(CSI2_DX0), (IEN | PTD | DIS | M0)) /*CSI2_DX0*/\ + MUX_VAL(CP(CSI2_DY0), (IEN | PTD | DIS | M0)) /*CSI2_DY0*/\ + MUX_VAL(CP(CSI2_DX1), (IEN | PTD | DIS | M0)) /*CSI2_DX1*/\ + MUX_VAL(CP(CSI2_DY1), (IEN | PTD | DIS | M0)) /*CSI2_DY1*/\ + /*Audio Interface */\ + MUX_VAL(CP(MCBSP2_FSX), (IEN | PTD | DIS | M0)) /*McBSP2_FSX*/\ + MUX_VAL(CP(MCBSP2_CLKX), (IEN | PTD | DIS | M0)) /*McBSP2_CLKX*/\ + MUX_VAL(CP(MCBSP2_DR), (IEN | PTD | DIS | M0)) /*McBSP2_DR*/\ + MUX_VAL(CP(MCBSP2_DX), (IDIS | PTD | DIS | M0)) /*McBSP2_DX*/\ + /*Expansion card */\ + MUX_VAL(CP(MMC1_CLK), (IDIS | PTU | EN | M0)) /*MMC1_CLK*/\ + MUX_VAL(CP(MMC1_CMD), (IEN | PTU | EN | M0)) /*MMC1_CMD*/\ + MUX_VAL(CP(MMC1_DAT0), (IEN | PTU | EN | M0)) /*MMC1_DAT0*/\ + MUX_VAL(CP(MMC1_DAT1), (IEN | PTU | EN | M0)) /*MMC1_DAT1*/\ + MUX_VAL(CP(MMC1_DAT2), (IEN | PTU | EN | M0)) /*MMC1_DAT2*/\ + MUX_VAL(CP(MMC1_DAT3), (IEN | PTU | EN | M0)) /*MMC1_DAT3*/\ + MUX_VAL(CP(MMC1_DAT4), (IEN | PTU | EN | M0)) /*MMC1_DAT4*/\ + MUX_VAL(CP(MMC1_DAT5), (IEN | PTU | EN | M0)) /*MMC1_DAT5*/\ + MUX_VAL(CP(MMC1_DAT6), (IEN | PTU | EN | M0)) /*MMC1_DAT6*/\ + MUX_VAL(CP(MMC1_DAT7), (IEN | PTU | EN | M0)) /*MMC1_DAT7*/\ + /*Wireless LAN */\ + MUX_VAL(CP(MMC2_CLK), (IEN | PTD | DIS | M0)) /*MMC2_CLK*/\ + MUX_VAL(CP(MMC2_CMD), (IEN | PTU | EN | M0)) /*MMC2_CMD*/\ + MUX_VAL(CP(MMC2_DAT0), (IEN | PTU | EN | M0)) /*MMC2_DAT0*/\ + MUX_VAL(CP(MMC2_DAT1), (IEN | PTU | EN | M0)) /*MMC2_DAT1*/\ + MUX_VAL(CP(MMC2_DAT2), (IEN | PTU | EN | M0)) /*MMC2_DAT2*/\ + MUX_VAL(CP(MMC2_DAT3), (IEN | PTU | EN | M0)) /*MMC2_DAT3*/\ + MUX_VAL(CP(MMC2_DAT4), (IDIS | PTD | DIS | M0)) /*MMC2_DAT4*/\ + MUX_VAL(CP(MMC2_DAT5), (IDIS | PTD | DIS | M0)) /*MMC2_DAT5*/\ + MUX_VAL(CP(MMC2_DAT6), (IDIS | PTD | DIS | M0)) /*MMC2_DAT6 */\ + MUX_VAL(CP(MMC2_DAT7), (IEN | PTU | EN | M0)) /*MMC2_DAT7*/\ + /*Bluetooth*/\ + MUX_VAL(CP(MCBSP3_DX), (IDIS | PTD | DIS | M0)) /*McBSP3_DX*/\ + MUX_VAL(CP(MCBSP3_DR), (IEN | PTD | DIS | M0)) /*McBSP3_DR*/\ + MUX_VAL(CP(MCBSP3_CLKX), (IEN | PTD | DIS | M0)) /*McBSP3_CLKX */\ + MUX_VAL(CP(MCBSP3_FSX), (IEN | PTD | DIS | M0)) /*McBSP3_FSX*/\ + MUX_VAL(CP(UART2_CTS), (IEN | PTU | EN | M0)) /*UART2_CTS*/\ + MUX_VAL(CP(UART2_RTS), (IDIS | PTD | DIS | M0)) /*UART2_RTS*/\ + MUX_VAL(CP(UART2_TX), (IDIS | PTD | DIS | M0)) /*UART2_TX*/\ + MUX_VAL(CP(UART2_RX), (IEN | PTD | DIS | M0)) /*UART2_RX*/\ + /*Modem Interface */\ + MUX_VAL(CP(UART1_TX), (IDIS | PTD | DIS | M0)) /*UART1_TX*/\ + MUX_VAL(CP(UART1_RTS), (IDIS | PTD | DIS | M0)) /*UART1_RTS*/\ + MUX_VAL(CP(UART1_CTS), (IEN | PTU | DIS | M0)) /*UART1_CTS*/\ + MUX_VAL(CP(UART1_RX), (IEN | PTD | DIS | M0)) /*UART1_RX*/\ + MUX_VAL(CP(MCBSP4_CLKX), (IDIS | PTD | DIS | M4)) /*GPIO_152*/\ + /* - LCD_INI*/\ + MUX_VAL(CP(MCBSP4_DR), (IDIS | PTD | DIS | M4)) /*GPIO_153*/\ + /* - LCD_ENVDD */\ + MUX_VAL(CP(MCBSP4_DX), (IDIS | PTD | DIS | M4)) /*GPIO_154*/\ + /* - LCD_QVGA/nVGA */\ + MUX_VAL(CP(MCBSP4_FSX), (IDIS | PTD | DIS | M4)) /*GPIO_155*/\ + /* - LCD_RESB */\ + MUX_VAL(CP(MCBSP1_CLKR), (IEN | PTD | DIS | M0)) /*MCBSP1_CLKR */\ + MUX_VAL(CP(MCBSP1_FSR), (IDIS | PTU | EN | M0)) /*MCBSP1_FSR*/\ + MUX_VAL(CP(MCBSP1_DX), (IDIS | PTD | DIS | M0)) /*MCBSP1_DX*/\ + MUX_VAL(CP(MCBSP1_DR), (IEN | PTD | DIS | M0)) /*MCBSP1_DR*/\ + MUX_VAL(CP(MCBSP_CLKS), (IEN | PTU | DIS | M0)) /*MCBSP_CLKS */\ + MUX_VAL(CP(MCBSP1_FSX), (IEN | PTD | DIS | M0)) /*MCBSP1_FSX*/\ + MUX_VAL(CP(MCBSP1_CLKX), (IEN | PTD | DIS | M0)) /*MCBSP1_CLKX */\ + /*Serial Interface*/\ + MUX_VAL(CP(UART3_CTS_RCTX), (IEN | PTD | EN | M0)) /*UART3_CTS_*/\ + /* RCTX*/\ + MUX_VAL(CP(UART3_RTS_SD), (IDIS | PTD | DIS | M0)) /*UART3_RTS_SD */\ + MUX_VAL(CP(UART3_RX_IRRX), (IEN | PTD | DIS | M0)) /*UART3_RX_IRRX*/\ + MUX_VAL(CP(UART3_TX_IRTX), (IDIS | PTD | DIS | M0)) /*UART3_TX_IRTX*/\ + MUX_VAL(CP(HSUSB0_CLK), (IEN | PTD | DIS | M0)) /*HSUSB0_CLK*/\ + MUX_VAL(CP(HSUSB0_STP), (IDIS | PTU | EN | M0)) /*HSUSB0_STP*/\ + MUX_VAL(CP(HSUSB0_DIR), (IEN | PTD | DIS | M0)) /*HSUSB0_DIR*/\ + MUX_VAL(CP(HSUSB0_NXT), (IEN | PTD | DIS | M0)) /*HSUSB0_NXT*/\ + MUX_VAL(CP(HSUSB0_DATA0), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA0*/\ + MUX_VAL(CP(HSUSB0_DATA1), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA1*/\ + MUX_VAL(CP(HSUSB0_DATA2), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA2*/\ + MUX_VAL(CP(HSUSB0_DATA3), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA3*/\ + MUX_VAL(CP(HSUSB0_DATA4), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA4*/\ + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA5*/\ + MUX_VAL(CP(HSUSB0_DATA6), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA6*/\ + MUX_VAL(CP(HSUSB0_DATA7), (IEN | PTD | DIS | M0)) /*HSUSB0_DATA7*/\ + MUX_VAL(CP(I2C1_SCL), (IEN | PTU | EN | M0)) /*I2C1_SCL*/\ + MUX_VAL(CP(I2C1_SDA), (IEN | PTU | EN | M0)) /*I2C1_SDA*/\ + MUX_VAL(CP(I2C2_SCL), (IEN | PTU | EN | M0)) /*I2C2_SCL*/\ + MUX_VAL(CP(I2C2_SDA), (IEN | PTU | EN | M0)) /*I2C2_SDA*/\ + MUX_VAL(CP(I2C3_SCL), (IEN | PTU | EN | M0)) /*I2C3_SCL*/\ + MUX_VAL(CP(I2C3_SDA), (IEN | PTU | EN | M0)) /*I2C3_SDA*/\ + MUX_VAL(CP(I2C4_SCL), (IEN | PTU | EN | M0)) /*I2C4_SCL*/\ + MUX_VAL(CP(I2C4_SDA), (IEN | PTU | EN | M0)) /*I2C4_SDA*/\ + MUX_VAL(CP(HDQ_SIO), (IEN | PTU | EN | M0)) /*HDQ_SIO*/\ + MUX_VAL(CP(MCSPI1_CLK), (IEN | PTD | DIS | M0)) /*McSPI1_CLK*/\ + MUX_VAL(CP(MCSPI1_SIMO), (IEN | PTD | DIS | M0)) /*McSPI1_SIMO */\ + MUX_VAL(CP(MCSPI1_SOMI), (IEN | PTD | DIS | M0)) /*McSPI1_SOMI */\ + MUX_VAL(CP(MCSPI1_CS0), (IEN | PTD | EN | M0)) /*McSPI1_CS0*/\ + MUX_VAL(CP(MCSPI1_CS1), (IEN | PTD | EN | M4)) /*GPIO_175*/\ + /* TS_PEN_IRQ */\ + MUX_VAL(CP(MCSPI1_CS2), (IEN | PTU | DIS | M4)) /*GPIO_176*/\ + /* - LAN_INTR*/\ + MUX_VAL(CP(MCSPI1_CS3), (IEN | PTD | EN | M0)) /*McSPI1_CS3*/\ + MUX_VAL(CP(MCSPI2_CLK), (IEN | PTD | DIS | M0)) /*McSPI2_CLK*/\ + MUX_VAL(CP(MCSPI2_SIMO), (IEN | PTD | DIS | M0)) /*McSPI2_SIMO*/\ + MUX_VAL(CP(MCSPI2_SOMI), (IEN | PTD | DIS | M0)) /*McSPI2_SOMI*/\ + MUX_VAL(CP(MCSPI2_CS0), (IEN | PTD | EN | M0)) /*McSPI2_CS0*/\ + MUX_VAL(CP(MCSPI2_CS1), (IEN | PTD | EN | M0)) /*McSPI2_CS1*/\ + /*Control and debug */\ + MUX_VAL(CP(SYS_32K), (IEN | PTD | DIS | M0)) /*SYS_32K*/\ + MUX_VAL(CP(SYS_CLKREQ), (IEN | PTD | DIS | M0)) /*SYS_CLKREQ*/\ + MUX_VAL(CP(SYS_NIRQ), (IEN | PTU | EN | M0)) /*SYS_nIRQ*/\ + MUX_VAL(CP(SYS_BOOT0), (IEN | PTD | DIS | M4)) /*GPIO_2*/\ + /* - PEN_IRQ */\ + MUX_VAL(CP(SYS_BOOT1), (IEN | PTD | DIS | M4)) /*GPIO_3 */\ + MUX_VAL(CP(SYS_BOOT2), (IEN | PTD | DIS | M4)) /*GPIO_4*/\ + MUX_VAL(CP(SYS_BOOT3), (IEN | PTD | DIS | M4)) /*GPIO_5*/\ + MUX_VAL(CP(SYS_BOOT4), (IEN | PTD | DIS | M4)) /*GPIO_6*/\ + MUX_VAL(CP(SYS_BOOT5), (IEN | PTD | DIS | M4)) /*GPIO_7*/\ + MUX_VAL(CP(SYS_BOOT6), (IDIS | PTD | DIS | M4)) /*GPIO_8*/\ + /* - VIO_1V8*/\ + MUX_VAL(CP(SYS_OFF_MODE), (IEN | PTD | DIS | M0)) /*SYS_OFF_MODE*/\ + MUX_VAL(CP(SYS_CLKOUT1), (IEN | PTD | DIS | M0)) /*SYS_CLKOUT1*/\ + MUX_VAL(CP(SYS_CLKOUT2), (IEN | PTU | EN | M0)) /*SYS_CLKOUT2*/\ + MUX_VAL(CP(JTAG_nTRST), (IEN | PTD | DIS | M0)) /*JTAG_nTRST*/\ + MUX_VAL(CP(JTAG_TCK), (IEN | PTD | DIS | M0)) /*JTAG_TCK*/\ + MUX_VAL(CP(JTAG_TMS), (IEN | PTD | DIS | M0)) /*JTAG_TMS*/\ + MUX_VAL(CP(JTAG_TDI), (IEN | PTD | DIS | M0)) /*JTAG_TDI*/\ + MUX_VAL(CP(JTAG_EMU0), (IEN | PTD | DIS | M0)) /*JTAG_EMU0*/\ + MUX_VAL(CP(JTAG_EMU1), (IEN | PTD | DIS | M0)) /*JTAG_EMU1*/\ + MUX_VAL(CP(ETK_CLK_ES2), (IDIS | PTU | EN | M0)) /*ETK_CLK*/\ + MUX_VAL(CP(ETK_CTL_ES2), (IDIS | PTD | DIS | M0)) /*ETK_CTL*/\ + MUX_VAL(CP(ETK_D0_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D0*/\ + MUX_VAL(CP(ETK_D1_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D1*/\ + MUX_VAL(CP(ETK_D2_ES2 ), (IEN | PTD | EN | M0)) /*ETK_D2*/\ + MUX_VAL(CP(ETK_D3_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D3*/\ + MUX_VAL(CP(ETK_D4_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D4*/\ + MUX_VAL(CP(ETK_D5_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D5*/\ + MUX_VAL(CP(ETK_D6_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D6*/\ + MUX_VAL(CP(ETK_D7_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D7*/\ + MUX_VAL(CP(ETK_D8_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D8*/\ + MUX_VAL(CP(ETK_D9_ES2 ), (IEN | PTD | DIS | M0)) /*ETK_D9*/\ + MUX_VAL(CP(ETK_D10_ES2), (IEN | PTD | DIS | M0)) /*ETK_D10*/\ + MUX_VAL(CP(ETK_D11_ES2), (IEN | PTD | DIS | M0)) /*ETK_D11*/\ + MUX_VAL(CP(ETK_D12_ES2), (IEN | PTD | DIS | M0)) /*ETK_D12*/\ + MUX_VAL(CP(ETK_D13_ES2), (IEN | PTD | DIS | M0)) /*ETK_D13*/\ + MUX_VAL(CP(ETK_D14_ES2), (IEN | PTD | DIS | M0)) /*ETK_D14*/\ + MUX_VAL(CP(ETK_D15_ES2), (IEN | PTD | DIS | M0)) /*ETK_D15*/\ + /*Die to Die */\ + MUX_VAL(CP(D2D_MCAD1), (IEN | PTD | EN | M0)) /*d2d_mcad1*/\ + MUX_VAL(CP(D2D_MCAD2), (IEN | PTD | EN | M0)) /*d2d_mcad2*/\ + MUX_VAL(CP(D2D_MCAD3), (IEN | PTD | EN | M0)) /*d2d_mcad3*/\ + MUX_VAL(CP(D2D_MCAD4), (IEN | PTD | EN | M0)) /*d2d_mcad4*/\ + MUX_VAL(CP(D2D_MCAD5), (IEN | PTD | EN | M0)) /*d2d_mcad5*/\ + MUX_VAL(CP(D2D_MCAD6), (IEN | PTD | EN | M0)) /*d2d_mcad6*/\ + MUX_VAL(CP(D2D_MCAD7), (IEN | PTD | EN | M0)) /*d2d_mcad7*/\ + MUX_VAL(CP(D2D_MCAD8), (IEN | PTD | EN | M0)) /*d2d_mcad8*/\ + MUX_VAL(CP(D2D_MCAD9), (IEN | PTD | EN | M0)) /*d2d_mcad9*/\ + MUX_VAL(CP(D2D_MCAD10), (IEN | PTD | EN | M0)) /*d2d_mcad10*/\ + MUX_VAL(CP(D2D_MCAD11), (IEN | PTD | EN | M0)) /*d2d_mcad11*/\ + MUX_VAL(CP(D2D_MCAD12), (IEN | PTD | EN | M0)) /*d2d_mcad12*/\ + MUX_VAL(CP(D2D_MCAD13), (IEN | PTD | EN | M0)) /*d2d_mcad13*/\ + MUX_VAL(CP(D2D_MCAD14), (IEN | PTD | EN | M0)) /*d2d_mcad14*/\ + MUX_VAL(CP(D2D_MCAD15), (IEN | PTD | EN | M0)) /*d2d_mcad15*/\ + MUX_VAL(CP(D2D_MCAD16), (IEN | PTD | EN | M0)) /*d2d_mcad16*/\ + MUX_VAL(CP(D2D_MCAD17), (IEN | PTD | EN | M0)) /*d2d_mcad17*/\ + MUX_VAL(CP(D2D_MCAD18), (IEN | PTD | EN | M0)) /*d2d_mcad18*/\ + MUX_VAL(CP(D2D_MCAD19), (IEN | PTD | EN | M0)) /*d2d_mcad19*/\ + MUX_VAL(CP(D2D_MCAD20), (IEN | PTD | EN | M0)) /*d2d_mcad20*/\ + MUX_VAL(CP(D2D_MCAD21), (IEN | PTD | EN | M0)) /*d2d_mcad21*/\ + MUX_VAL(CP(D2D_MCAD22), (IEN | PTD | EN | M0)) /*d2d_mcad22*/\ + MUX_VAL(CP(D2D_MCAD23), (IEN | PTD | EN | M0)) /*d2d_mcad23*/\ + MUX_VAL(CP(D2D_MCAD24), (IEN | PTD | EN | M0)) /*d2d_mcad24*/\ + MUX_VAL(CP(D2D_MCAD25), (IEN | PTD | EN | M0)) /*d2d_mcad25*/\ + MUX_VAL(CP(D2D_MCAD26), (IEN | PTD | EN | M0)) /*d2d_mcad26*/\ + MUX_VAL(CP(D2D_MCAD27), (IEN | PTD | EN | M0)) /*d2d_mcad27*/\ + MUX_VAL(CP(D2D_MCAD28), (IEN | PTD | EN | M0)) /*d2d_mcad28*/\ + MUX_VAL(CP(D2D_MCAD29), (IEN | PTD | EN | M0)) /*d2d_mcad29*/\ + MUX_VAL(CP(D2D_MCAD30), (IEN | PTD | EN | M0)) /*d2d_mcad30*/\ + MUX_VAL(CP(D2D_MCAD31), (IEN | PTD | EN | M0)) /*d2d_mcad31*/\ + MUX_VAL(CP(D2D_MCAD32), (IEN | PTD | EN | M0)) /*d2d_mcad32*/\ + MUX_VAL(CP(D2D_MCAD33), (IEN | PTD | EN | M0)) /*d2d_mcad33*/\ + MUX_VAL(CP(D2D_MCAD34), (IEN | PTD | EN | M0)) /*d2d_mcad34*/\ + MUX_VAL(CP(D2D_MCAD35), (IEN | PTD | EN | M0)) /*d2d_mcad35*/\ + MUX_VAL(CP(D2D_MCAD36), (IEN | PTD | EN | M0)) /*d2d_mcad36*/\ + MUX_VAL(CP(D2D_CLK26MI), (IEN | PTD | DIS | M0)) /*d2d_clk26mi*/\ + MUX_VAL(CP(D2D_NRESPWRON), (IEN | PTD | EN | M0)) /*d2d_nrespwron*/\ + MUX_VAL(CP(D2D_NRESWARM), (IEN | PTU | EN | M0)) /*d2d_nreswarm */\ + MUX_VAL(CP(D2D_ARM9NIRQ), (IEN | PTD | DIS | M0)) /*d2d_arm9nirq */\ + MUX_VAL(CP(D2D_UMA2P6FIQ), (IEN | PTD | DIS | M0)) /*d2d_uma2p6fiq*/\ + MUX_VAL(CP(D2D_SPINT), (IEN | PTD | EN | M0)) /*d2d_spint*/\ + MUX_VAL(CP(D2D_FRINT), (IEN | PTD | EN | M0)) /*d2d_frint*/\ + MUX_VAL(CP(D2D_DMAREQ0), (IEN | PTD | DIS | M0)) /*d2d_dmareq0*/\ + MUX_VAL(CP(D2D_DMAREQ1), (IEN | PTD | DIS | M0)) /*d2d_dmareq1*/\ + MUX_VAL(CP(D2D_DMAREQ2), (IEN | PTD | DIS | M0)) /*d2d_dmareq2*/\ + MUX_VAL(CP(D2D_DMAREQ3), (IEN | PTD | DIS | M0)) /*d2d_dmareq3*/\ + MUX_VAL(CP(D2D_N3GTRST), (IEN | PTD | DIS | M0)) /*d2d_n3gtrst*/\ + MUX_VAL(CP(D2D_N3GTDI), (IEN | PTD | DIS | M0)) /*d2d_n3gtdi*/\ + MUX_VAL(CP(D2D_N3GTDO), (IEN | PTD | DIS | M0)) /*d2d_n3gtdo*/\ + MUX_VAL(CP(D2D_N3GTMS), (IEN | PTD | DIS | M0)) /*d2d_n3gtms*/\ + MUX_VAL(CP(D2D_N3GTCK), (IEN | PTD | DIS | M0)) /*d2d_n3gtck*/\ + MUX_VAL(CP(D2D_N3GRTCK), (IEN | PTD | DIS | M0)) /*d2d_n3grtck*/\ + MUX_VAL(CP(D2D_MSTDBY), (IEN | PTU | EN | M0)) /*d2d_mstdby*/\ + MUX_VAL(CP(D2D_SWAKEUP), (IEN | PTD | EN | M0)) /*d2d_swakeup*/\ + MUX_VAL(CP(D2D_IDLEREQ), (IEN | PTD | DIS | M0)) /*d2d_idlereq*/\ + MUX_VAL(CP(D2D_IDLEACK), (IEN | PTU | EN | M0)) /*d2d_idleack*/\ + MUX_VAL(CP(D2D_MWRITE), (IEN | PTD | DIS | M0)) /*d2d_mwrite*/\ + MUX_VAL(CP(D2D_SWRITE), (IEN | PTD | DIS | M0)) /*d2d_swrite*/\ + MUX_VAL(CP(D2D_MREAD), (IEN | PTD | DIS | M0)) /*d2d_mread*/\ + MUX_VAL(CP(D2D_SREAD), (IEN | PTD | DIS | M0)) /*d2d_sread*/\ + MUX_VAL(CP(D2D_MBUSFLAG), (IEN | PTD | DIS | M0)) /*d2d_mbusflag*/\ + MUX_VAL(CP(D2D_SBUSFLAG), (IEN | PTD | DIS | M0)) /*d2d_sbusflag*/\ + MUX_VAL(CP(SDRC_CKE0), (IDIS | PTU | EN | M0)) /*sdrc_cke0*/\ + MUX_VAL(CP(SDRC_CKE1), (IDIS | PTD | DIS | M7)) /*sdrc_cke1*/\ + +#endif diff --git a/board/ti/logic/prod-id/crc-15.c b/board/ti/logic/prod-id/crc-15.c new file mode 100644 index 00000000000..344fdaad9ea --- /dev/null +++ b/board/ti/logic/prod-id/crc-15.c @@ -0,0 +1,31 @@ +#include "crc-15.h" + +/* + * Calculate a CRC-15 of a data buffer passed in + */ + +void crc_15_step(unsigned short *crc, unsigned char byte) +{ + int i; + unsigned short crcnext; + + for (i=0; i<7; ++i) { + crcnext = (byte & 1) ^ (*crc>>14); + *crc = (*crc << 1) & 0x7fff; + if (crcnext) + *crc ^= 0x4599; + byte >>= 1; + } +} + +unsigned short crc_15(void *buf, int len) +{ + unsigned char *p = buf; + unsigned short xsum = 0; + int i; + + for (i=0; i<len; ++i) { + crc_15_step(&xsum, p[i]); + } + return xsum; +} diff --git a/board/ti/logic/prod-id/crc-15.h b/board/ti/logic/prod-id/crc-15.h new file mode 100644 index 00000000000..15a891e3872 --- /dev/null +++ b/board/ti/logic/prod-id/crc-15.h @@ -0,0 +1,3 @@ +extern unsigned short crc_15(void *byte, int len); + +extern void crc_15_step(unsigned short *crc, unsigned char byte); diff --git a/board/ti/logic/prod-id/debug.h b/board/ti/logic/prod-id/debug.h new file mode 100644 index 00000000000..38dc9c3b96d --- /dev/null +++ b/board/ti/logic/prod-id/debug.h @@ -0,0 +1,12 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTF(fmt, args...) printf(fmt, ## args) +#else +#define DPRINTF(fmt, ...) +#endif + +#endif diff --git a/board/ti/logic/prod-id/dict.c b/board/ti/logic/prod-id/dict.c new file mode 100644 index 00000000000..dde08790d4b --- /dev/null +++ b/board/ti/logic/prod-id/dict.c @@ -0,0 +1,47 @@ +#include "interface.h" +#include "internals.h" +#include "id-errno.h" + +int id_dict_size(struct id_data *data, struct id_cookie *cookie) +{ + idenum_t type; + int err; + int count = 0; + unsigned int size, keyval; + struct id_cookie d_cookie; + + d_cookie = *cookie; + + /* It has to be a dictionary */ + err = id_whatis(&d_cookie, &type); + if (type != IDENUM_DICT) + return -ID_EINVAL; + + size = extract_unsigned_pnum(&d_cookie, 5, &err); + if (err != ID_EOK) + return err; + + d_cookie.size = size; + d_cookie.start_offset = d_cookie.offset; + while (d_cookie.offset < d_cookie.start_offset+d_cookie.size) { + /* It has to be a key */ + err = id_whatis(&d_cookie, &type); + if (type != IDENUM_KEY) + return -ID_EINVAL; + keyval = extract_unsigned_pnum(&d_cookie, 5, &err); + if (err != ID_EOK) + return err; + + /* Get the size of the object */ + size = id_extract_size(&d_cookie, &err); + if (err != ID_EOK) + return err; + + /* Move the offset forward by the object size */ + d_cookie.offset += size; + + /* Increment the count */ + count++; + } + return count; +} diff --git a/board/ti/logic/prod-id/extract.c b/board/ti/logic/prod-id/extract.c new file mode 100644 index 00000000000..09c7bedd7ff --- /dev/null +++ b/board/ti/logic/prod-id/extract.c @@ -0,0 +1,70 @@ +#include "interface.h" +#include "internals.h" +#include "id-errno.h" +#include "debug.h" + +unsigned int extract_unsigned_pnum(struct id_cookie *cookie, int start_bit, int *err) +{ + unsigned int value=0; + unsigned int bit_offset=0; + unsigned char bits; + unsigned char ch; + int oor; + + *err = ID_EOK; + for (;;) { + ch = id_fetch_byte(cookie->mem_ptr, cookie->offset++, &oor); + if (oor != ID_EOK) { + *err = oor; + id_error("extract runs oor"); + return 0; + } + if (ch & (1<<(start_bit-1))) { + /* more to go, accumulate bits */ + bits = ch & ((1<<(start_bit - 1)) - 1); + value |= (bits << bit_offset); + bit_offset += start_bit-1; + start_bit = 8; + } else { + /* last byte of number */ + bits = ch & ((1<<(start_bit - 1)) - 1); + value |= (bits << bit_offset); + break; + } + } + return value; +} + +int extract_signed_pnum(struct id_cookie *cookie, int start_bit, int *err) +{ + int value=0; + unsigned int bit_offset=0; + unsigned char bits; + unsigned char ch; + int oor; + + *err = ID_EOK; + for (;;) { + ch = id_fetch_byte(cookie->mem_ptr, cookie->offset++, &oor); + if (oor != ID_EOK) { + *err = oor; + id_error("extract runs oor"); + return 0; + } + if (ch & (1<<(start_bit-1))) { + /* more to go, accumulate bits */ + bits = ch & ((1<<(start_bit - 1)) - 1); + value |= (bits << bit_offset); + bit_offset += start_bit-1; + start_bit = 8; + } else { + /* last byte of number */ + bits = ch & ((1<<(start_bit - 2)) - 1); + value |= (bits << bit_offset); + if (ch & (1<<(start_bit - 2))) + value = -value; + break; + } + } + return value; +} diff --git a/board/ti/logic/prod-id/id-errno.h b/board/ti/logic/prod-id/id-errno.h new file mode 100644 index 00000000000..a9a85e98aa4 --- /dev/null +++ b/board/ti/logic/prod-id/id-errno.h @@ -0,0 +1,10 @@ +#define ID_EOK 0 /* Okay */ +#define ID_ENOENT 2 /* No such key */ +#define ID_ENOMEM 12 /* Out of memory */ +#define ID_EACCES 13 /* Permission denied */ +#define ID_ENODEV 19 /* No such device */ +#define ID_EINVAL 22 /* Invalid arcument */ +#define ID_EDOM 33 /* argument out of domain of func */ +#define ID_ERANGE 34 /* Out of range */ +#define ID_EL2NSYNC 45 /* Level 2 not synchronized */ +#define ID_ENOMEDIUM 123 /* No medium found */ diff --git a/board/ti/logic/prod-id/interface.h b/board/ti/logic/prod-id/interface.h new file mode 100644 index 00000000000..2646df24ed2 --- /dev/null +++ b/board/ti/logic/prod-id/interface.h @@ -0,0 +1,105 @@ +/* + * Header file that interfaces to environment to access data + */ + +/* Create the enum list of keys, not strings! */ +#undef ID_KEY_STRINGS +#define ID_KEY_ENUMS +#include "keys.h" + + +typedef enum { + /* Number */ + IDENUM_NEG_NUM = 0, + IDENUM_POS_NUM, + + /* String/Hex String */ + IDENUM_STR, + IDENUM_HEXSTR, + + /* Array */ + IDENUM_ARRAY, + + /* Dictionary */ + IDENUM_DICT, + + /* Key */ + IDENUM_KEY, + + /* Any string */ + IDENUM_ANY_STRING, + + /* Any number */ + IDENUM_ANY_NUMBER, + +} idenum_t; + +/* structure of builtin keys */ +struct id_key { + unsigned char *ptr; + unsigned int size; +}; + + +/* + * return a byte from the ID data at offset 'offset' and set *oor to zero + * if offset is in range of the device. If offset is out of range then + * set *oor to non-zero. If mem_ptr is non-NULL use it as a pointer to + * a cached copy of the ID data */ +extern unsigned char id_fetch_byte(unsigned char *mem_ptr, int offset, int *oor); + +struct id_data { + /* mem_ptr is a pointer to read the initial ID data into, then if not + * null used to read a cached copy of the ID data from */ + unsigned char *mem_ptr; + unsigned int root_size; + unsigned int root_offset; +}; + +/* Function to do the intial startup (i.e. figure out how much data, offset of + * key table, etc */ +extern int id_startup(struct id_data *data); +/* + * Functions provided back to callers for use in accessing data + */ + +/* ID data "cookie" used to access data; ultimately this will be opaque + * to the callers as they don't need to know whats in it, just pass it around + */ +struct id_cookie { + unsigned char *mem_ptr; /* cache pointer to ID data */ + unsigned int start_offset; /* start offset from beginning of data */ + unsigned int size; /* size of data in bytes */ + unsigned int offset; /* current read offset */ +}; + +/* Initialize the cookie to cover the whole root dictionary */ +extern int id_init_cookie(struct id_data *data, struct id_cookie *cookie); + +/* What is the read pointer cookie is pointing at */ +extern int id_whatis(struct id_cookie *cookie, idenum_t *type); + +/* Given a string, return the key code (or -1 if not found) */ +extern int id_data_get_key(char *key_name); + + +/* ID error routine to handle malformed data */ +extern void id_error(const char *fmt, ...); + +/* Ouptut routine */ +extern int id_printf(const char *format, ...); + + +/* User interface functions */ +extern int id_dict_size(struct id_data *data, struct id_cookie *cookie); +extern int id_array_size(struct id_data *data, struct id_cookie *cookie); + +extern int id_dict_find_key(struct id_cookie *cookie, id_keys_t key); +extern int id_find_dict(struct id_cookie *cookie, id_keys_t key, idenum_t type); +extern int id_find_string(struct id_cookie *cookie, id_keys_t key, unsigned char *str_ptr, unsigned int *str_size); +extern int id_find_number(struct id_cookie *cookie, id_keys_t key, int *num); +extern int id_find_numbers(struct id_cookie *cookie, id_keys_t *key, int key_size, int *nums); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(var) sizeof(var)/sizeof((var)[0]) +#endif diff --git a/board/ti/logic/prod-id/internals.h b/board/ti/logic/prod-id/internals.h new file mode 100644 index 00000000000..95e9654b0eb --- /dev/null +++ b/board/ti/logic/prod-id/internals.h @@ -0,0 +1,19 @@ +/* + * Extract an unsigned packed number, first byte is in 'pack_bits' + * of first byte, starting at offset 'offset' */ +extern unsigned int extract_unsigned_pnum(struct id_cookie *cookie, int pack_bits, int *err); +extern int extract_signed_pnum(struct id_cookie *cookie, int pack_bits, int *err); + + + +// extern struct id_data id_data; + +extern int id_read_keys(struct id_data *data); +extern int id_verify_builtin_keys(struct id_data *data); + +#define ID_MAX_KEY_SIZE 32 + +extern int id_get_key(struct id_data *data, int keyval, struct id_key *key); +extern int id_iterate_dict(struct id_data *data, struct id_cookie *cookie); + +extern int id_extract_size(struct id_cookie *cookie, int *err); diff --git a/board/ti/logic/prod-id/keys.h b/board/ti/logic/prod-id/keys.h new file mode 100644 index 00000000000..fcd58817728 --- /dev/null +++ b/board/ti/logic/prod-id/keys.h @@ -0,0 +1,95 @@ + +#undef ID_KEY_START +#undef ID_KEY_ENTRY +#undef ID_KEY_END + +#if defined(ID_KEY_STRINGS) +/* This is the usage to build the keys for the compiler; we define + * an array of strings whose index is the value */ +#define ID_KEY_START static char *id_keys[] = { +#define ID_KEY_ENTRY(XX) #XX , +#define ID_KEY_END }; +#elif defined(ID_KEY_ENUMS) +/* This is the usage by people using the library to access the data */ +#define ID_KEY_START typedef enum { +#define ID_KEY_ENTRY(XX) ID_KEY_ ## XX, +#define ID_KEY_END } id_keys_t; +#else +#error "Need either ID_KEY_INTERFACE or ID_KEY_COMPILER defined!" +#endif + + +/* There are some implied conventions here: */ +/* - names of keys that contain other keys (dictionaries) end in "_group" */ +/* - names of keys that provide a register setting end in "_reg" */ +/* - any keys that specify a unit of measure, include units in the name (ie. _mhz, _degf, _bytes) */ + +ID_KEY_START + +/* Manufacturing unique data for each SOM */ +ID_KEY_ENTRY(serialization_group) +ID_KEY_ENTRY(serial_number) +ID_KEY_ENTRY(wifi_ethaddr1) +ID_KEY_ENTRY(wifi_ethaddr2) +ID_KEY_ENTRY(wifi_ethaddr3) +ID_KEY_ENTRY(wifi_ethaddr4) +ID_KEY_ENTRY(nvs) + +/* BOM Model number infromation */ +ID_KEY_ENTRY(model_group) +ID_KEY_ENTRY(model_name) +ID_KEY_ENTRY(part_number) +ID_KEY_ENTRY(version_code) +ID_KEY_ENTRY(hardware_platform) + +/* CPU specific information */ +ID_KEY_ENTRY(cpu0_group) +ID_KEY_ENTRY(type) +ID_KEY_ENTRY(number) +ID_KEY_ENTRY(speed_mhz) +ID_KEY_ENTRY(temp_class) + +/* CPU bus information */ +ID_KEY_ENTRY(cpu0_bus_group) + +/* DRAM bus information */ +ID_KEY_ENTRY(dram_bus_group) +ID_KEY_ENTRY(sysconfig_reg) +ID_KEY_ENTRY(sharing_reg) +ID_KEY_ENTRY(dlla_ctrl_reg) +ID_KEY_ENTRY(cs_cfg_reg) +// ID_KEY_ENTRY(cs0_group) Used in the dram_bus_group, but key defined below after local_bus_group +// ID_KEY_ENTRY(cs1_group) Used in the dram_bus_group, but key defined below after local_bus_group +ID_KEY_ENTRY(mcfg_reg) +ID_KEY_ENTRY(mr_reg) +ID_KEY_ENTRY(rfr_ctrl_reg) +ID_KEY_ENTRY(emr2_reg) +ID_KEY_ENTRY(actim_ctrla_reg) +ID_KEY_ENTRY(actim_ctrlb_reg) +ID_KEY_ENTRY(power_reg) + +/* GPMC keys */ +ID_KEY_ENTRY(local_bus_group) +ID_KEY_ENTRY(cs0_group) +ID_KEY_ENTRY(cs1_group) +ID_KEY_ENTRY(cs2_group) +ID_KEY_ENTRY(cs3_group) +ID_KEY_ENTRY(cs4_group) +ID_KEY_ENTRY(cs5_group) +ID_KEY_ENTRY(cs6_group) +ID_KEY_ENTRY(config1_reg) +ID_KEY_ENTRY(config2_reg) +ID_KEY_ENTRY(config3_reg) +ID_KEY_ENTRY(config4_reg) +ID_KEY_ENTRY(config5_reg) +ID_KEY_ENTRY(config6_reg) +ID_KEY_ENTRY(config7_reg) + +/* Manufacturing unique data for each SOM */ +ID_KEY_ENTRY(lan_ethaddr1) +ID_KEY_ENTRY(lan_ethaddr2) +ID_KEY_ENTRY(lan_ethaddr3) +ID_KEY_ENTRY(lan_ethaddr4) + +/* End of keys */ +ID_KEY_END diff --git a/board/ti/logic/prod-id/query.c b/board/ti/logic/prod-id/query.c new file mode 100644 index 00000000000..14290392206 --- /dev/null +++ b/board/ti/logic/prod-id/query.c @@ -0,0 +1,184 @@ +#include "interface.h" +#include "internals.h" +#include "id-errno.h" + +static int id_extract_key(struct id_cookie *cookie, id_keys_t *key) +{ + int err; + id_keys_t keyval; + + keyval = (id_keys_t)extract_unsigned_pnum(cookie, 5, &err); + if (err != ID_EOK) + return err; + *key = keyval; + return ID_EOK; +} + +/* in dictionary that cookie points to find key "key"; if found + * update cookie to associated "key" entry and return ID_EOK; + * else return -ID_ENOENT */ +int id_dict_find_key(struct id_cookie *cookie, id_keys_t key) +{ + int err; + unsigned int size; + id_keys_t d_key; + idenum_t type; + struct id_cookie d_cookie = *cookie; + struct id_cookie t_cookie; + + err = id_whatis(cookie, &type); + if (err != ID_EOK) + return err; + + /* Header has to be a dictionary */ + if (type != IDENUM_DICT) + return -ID_EINVAL; + + /* Extract size of dictionary */ + size = extract_unsigned_pnum(&d_cookie, 5, &err); + if (err != ID_EOK) + return err; + + d_cookie.size = size; + d_cookie.start_offset = d_cookie.offset; + + /* cookie->offset is now at first key */ + while (d_cookie.offset < d_cookie.start_offset + d_cookie.size) { + /* Extract the key and move the cookie over key */ + err = id_extract_key(&d_cookie, &d_key); + if (err != ID_EOK) + return err; + t_cookie = d_cookie; + /* move forward over the value */ + size = id_extract_size(&d_cookie, &err); + if (err != ID_EOK) + return err; + if (key == d_key) { + d_cookie.size = size; + d_cookie.start_offset = t_cookie.offset; + d_cookie.offset = t_cookie.offset; + *cookie = d_cookie; + return ID_EOK; + } + d_cookie.offset += size; + } + return -ID_ENOENT; +} + +/* Are these two types a match? */ +static int id_match_type(idenum_t type_a, idenum_t type_b) +{ + idenum_t tmp; + + if (type_a == type_b) + return 1; + + /* Oder the types (so the "*ANY*" types are in type_b) */ + if ((int)type_a > (int)type_b) { + tmp = type_a; + type_a = type_b; + type_b = tmp; + } + if (type_b == IDENUM_ANY_STRING && (type_a == IDENUM_STR || type_a == IDENUM_HEXSTR)) + return 1; + + if (type_b == IDENUM_ANY_NUMBER && (type_a == IDENUM_NEG_NUM || type_a == IDENUM_POS_NUM)) + return 1; + + return 0; +} + +/* Find in dictionary (that cookie points to) key "key" that is type "type" */ +int id_find_dict(struct id_cookie *cookie, id_keys_t key, idenum_t type) +{ + int err; + struct id_cookie d_cookie = *cookie; + idenum_t l_type; + + err = id_dict_find_key(&d_cookie, key); + if (err != ID_EOK) + return err; + err = id_whatis(&d_cookie, &l_type); + if (err != ID_EOK) + return err; + if (!id_match_type(l_type, type)) + return -ID_EINVAL; + *cookie = d_cookie; + return ID_EOK; +} + +/* in dictionary pointed at by cookie, find the key "key"; verify its a + * string and copy its value */ +int id_find_string(struct id_cookie *cookie, id_keys_t key, unsigned char *str_ptr, unsigned int *str_size) +{ + int err, i; + unsigned char byte; + unsigned int size; + struct id_cookie d_cookie = *cookie; + + err = id_find_dict(&d_cookie, key, IDENUM_ANY_STRING); + + if (err != ID_EOK) + return err; + /* Extract the string size */ + size = extract_unsigned_pnum(&d_cookie, 5, &err); + if (err != ID_EOK) + return err; + + if (size > *str_size) + return -ID_ERANGE; + + for(i=0; i<size; ++i) { + byte = id_fetch_byte(d_cookie.mem_ptr, d_cookie.offset++, &err); + if (err) + return err; + str_ptr[i] = byte; + } + *str_size = size; + + return ID_EOK; +} + +/* in dictionary pointed at by cookie, find the key "key"; verify its a + * number (either pos/neg) and return its value through *num */ +int id_find_number(struct id_cookie *cookie, id_keys_t key, int *num) +{ + int err; + int l_num; + idenum_t l_type; + struct id_cookie d_cookie = *cookie; + + err = id_find_dict(&d_cookie, key, IDENUM_ANY_NUMBER); + + if (err != ID_EOK) + return err; + err = id_whatis(&d_cookie, &l_type); + if (err != ID_EOK) + return err; + /* Extract the number size */ + l_num = extract_unsigned_pnum(&d_cookie, 5, &err); + if (err != ID_EOK) + return err; + + if (l_type == IDENUM_NEG_NUM) + l_num = -l_num; + + *num = l_num; + return ID_EOK; +} + +/* in dictionary pointed at by cookie, find the list of keys; verify they are + * numbers (either pos/neg) and return their value through *nums */ +int id_find_numbers(struct id_cookie *cookie, id_keys_t *keys, int key_size, int *nums) +{ + int i, err; + struct id_cookie d_cookie; + + for (i=0;i<key_size; ++i) { + d_cookie = *cookie; + err = id_find_number(&d_cookie, keys[i], &nums[i]); + if (err != ID_EOK) + return err; + } + return ID_EOK; +} diff --git a/board/ti/logic/prod-id/size.c b/board/ti/logic/prod-id/size.c new file mode 100644 index 00000000000..c4aec4c6fea --- /dev/null +++ b/board/ti/logic/prod-id/size.c @@ -0,0 +1,49 @@ +#include "interface.h" +#include "internals.h" +#include "id-errno.h" + +int id_extract_size(struct id_cookie *cookie, int *err) +{ + idenum_t type; + struct id_cookie s_cookie; + int size; + + s_cookie = *cookie; + + *err = id_whatis(&s_cookie, &type); + if (*err != ID_EOK) + return *err; + + switch(type) { + case IDENUM_DICT: + size = extract_unsigned_pnum(&s_cookie, 5, err); + size += (s_cookie.offset - cookie->offset); + break; + case IDENUM_ARRAY: + size = extract_unsigned_pnum(&s_cookie, 5, err); + size += (s_cookie.offset - cookie->offset); + break; + case IDENUM_STR: + case IDENUM_HEXSTR: + size = extract_unsigned_pnum(&s_cookie, 5, err); + size += (s_cookie.offset - cookie->offset); + break; + case IDENUM_POS_NUM: + case IDENUM_NEG_NUM: + extract_signed_pnum(&s_cookie, 5, err); + size = (s_cookie.offset - cookie->offset); + break; + case IDENUM_KEY: + extract_unsigned_pnum(&s_cookie, 5, err); + size = (s_cookie.offset - cookie->offset); + break; + default: + *err = -ID_EDOM; + size = 0; + break; + } + if (*err != ID_EOK) + return *err; + + return size; +} diff --git a/board/ti/logic/prod-id/startup.c b/board/ti/logic/prod-id/startup.c new file mode 100644 index 00000000000..73dd74069d8 --- /dev/null +++ b/board/ti/logic/prod-id/startup.c @@ -0,0 +1,185 @@ +#include <linux/stddef.h> +#include <linux/string.h> +#include "interface.h" +#include "internals.h" +#include "id-errno.h" +#include "crc-15.h" + +/* struct id_data id_data; */ + +struct __attribute__ ((packed)) id_header { + unsigned char signature[4]; + unsigned char id_fmt_ver; + unsigned char unused0; + unsigned short data_length; +} ; + +struct __attribute__ ((packed)) id_checksums { + unsigned short header; + unsigned short data; +} ; + +int id_startup(struct id_data *data) +{ + int i, err; + struct id_cookie cookie; + unsigned char byte, *p; + char *header_tag= "LpId"; + unsigned short xsum; + struct id_header hdr; + struct id_checksums xsums; + unsigned char *mem_ptr = data->mem_ptr; + + /* Clear out data->mem_ptr since we want all the fetches to come + * from the AT24 chip. Once we've validated the CRCs, restore + * data->mem_ptr to allow id_fetch_byte to read from data->mem_ptr + * instead of the AT24 chip. Should speed up accesses dramatically */ + data->mem_ptr = NULL; + + memset(&cookie, 0, sizeof(cookie)); + /* Data starts with the header, should be 'LpId' */ + for (i=0; i<4; ++i) { + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + hdr.signature[i] = byte; + cookie.offset++; + if (err != ID_EOK) { + id_printf("%s[%u]\n", __FILE__, __LINE__); + goto err_ret; + } + if (hdr.signature[i] != header_tag[i]) { + id_printf("%s[%u]\n", __FILE__, __LINE__); + err = ID_ENODEV; + goto err_ret; + } + } + + /* First LE 8-bit value is ID format version */ + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + hdr.id_fmt_ver = byte; + cookie.offset++; + + /* Second LE 8-bit value is currently not used */ + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + hdr.unused0 = byte; + cookie.offset++; + + /* Next LE 16-bit value is length of data */ + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + hdr.data_length = byte; + cookie.offset++; + + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + hdr.data_length |= byte << 8; + cookie.offset++; + + /* Next LE 16-bit value is xsum of header */ + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + xsums.header = byte; + cookie.offset++; + + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + xsums.header |= byte << 8; + cookie.offset++; + + /* Checksum the header */ + xsum = 0; + p = (unsigned char *)&hdr; + for (i = 0; i < sizeof(hdr); ++i) + crc_15_step(&xsum, p[i]); + + if (xsum != xsums.header) { + id_printf("%s[%u] xsum: 0x%04x, xsums.header: 0x%04x\n", + __FILE__, __LINE__, xsum, xsums.header); + err = -ID_EL2NSYNC; + goto err_ret; + } + + /* Next LE 16-bit value is xsum of data */ + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + xsums.data = byte; + cookie.offset++; + + byte = id_fetch_byte(NULL, cookie.offset, &err); + if (mem_ptr) + mem_ptr[cookie.offset] = byte; + xsums.data |= byte << 8; + cookie.offset++; + + /* Checksum the data (next id_len bytes), must match xsums.data */ + xsum = 0; + for (i = 0; i < hdr.data_length; ++i) { + byte = id_fetch_byte(NULL, cookie.offset + i, &err); + if (mem_ptr) + mem_ptr[cookie.offset + i] = byte; + if (err != ID_EOK) { + id_printf("%s[%u]\n", __FILE__, __LINE__); + goto err_ret; + } + crc_15_step(&xsum, byte); + } + if (xsum != xsums.data) { + id_printf("%s[%u] xsum: 0x%04x, xsums.data: 0x%04x\n", + __FILE__, __LINE__, xsum, xsums.data); + err = -ID_EL2NSYNC; + goto err_ret; + } + + /* offset is now at the first byte of the root dictionary which + contains its span */ + data->root_offset = cookie.offset; + data->root_size = extract_unsigned_pnum(&cookie, 5, &err); + if (err != ID_EOK) { + id_printf("%s[%u]\n", __FILE__, __LINE__); + goto err_ret; + } + + data->root_size += cookie.offset - data->root_offset; + +#if 0 + id_printf("Data format version: %u\n", hdr.id_fmt_ver); +#endif + + /* Restore data->mem_ptr to allow id_fetch_byte to read + * from the cached data instead of the AT24 chip */ + data->mem_ptr = mem_ptr; + + return ID_EOK; + +err_ret: + + /* Error return - make sure signature in SRAM is invalid */ + if (mem_ptr) + mem_ptr[0] = 0; + + return err; +} + +/* + * Reset the cookie to cover the whole root dictionary + */ +int id_init_cookie(struct id_data *data, struct id_cookie *cookie) +{ + if (!cookie) + return -ID_EINVAL; + cookie->mem_ptr = data->mem_ptr; + cookie->start_offset = data->root_offset; + cookie->size = data->root_size; + cookie->offset = cookie->start_offset; + return ID_EOK; +} diff --git a/board/ti/logic/prod-id/type.c b/board/ti/logic/prod-id/type.c new file mode 100644 index 00000000000..57eda7f7343 --- /dev/null +++ b/board/ti/logic/prod-id/type.c @@ -0,0 +1,20 @@ +#include "interface.h" +#include "internals.h" +#include "id-errno.h" + +int id_whatis(struct id_cookie *cookie, idenum_t *type) +{ + unsigned char byte; + int oor; + if (!cookie) + return -ID_EINVAL; + + byte = id_fetch_byte(cookie->mem_ptr, cookie->offset, &oor); + if (oor != ID_EOK) + return -ID_ERANGE; + + byte >>= 5; + *type = (idenum_t)byte; + + return ID_EOK; +} diff --git a/board/ti/logic/product_id.h b/board/ti/logic/product_id.h new file mode 100644 index 00000000000..b8a1600656e --- /dev/null +++ b/board/ti/logic/product_id.h @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2008, 2009, 2010 + * Logic Product Development, <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.com> + * + * 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 __PRODUCTID_H__ +#define __PRODUCTID_H__ + +#define LOGIC_HEADER_VERSION_0 0 +#define LOGIC_HEADER_VERSION_1 1 +#define LOGIC_HEADER_VERSION_2 2 +#define LOGIC_HEADER_VERSION_3 3 + +struct product_zone_0_rev_0 { + unsigned char header_version; // offset 0 + char part_number[11]; // offset 1 + char revision; // offset 12 + char sn_week; // offset 13 + char sn_year; // offset 14 + char sn_site; // offset 15 + int sn_cnt; // offset 16 + char maturity; // offset 20 +}; + +struct product_zone_0_rev_1 { + unsigned char header_version; // offset 0 + char part_number[11]; // offset 1 + char reserved; // revision removed after version 0 + char sn_week; // offset 13 + char sn_year; // offset 14 + char sn_site; // offset 15 + int sn_cnt; // offset 16 + char maturity; // offset 20 + char wifi_trim; // offset 21 +}; + + +struct product_zone_0_rev_2 { + unsigned char header_version; // offset 0 + char part_number[11]; // offset 1 + char revision; // offset 12 + char sn_week; // offset 13 + char sn_year; // offset 14 + char sn_site; // offset 15 + int sn_cnt; // offset 16 + char maturity; // offset 20 + char wifi_trim; // offset 21 + unsigned char full_mac[6]; // offset 22 +}; + +struct product_zone_2_rev_0 { + unsigned char mac0[3]; // offset 0 + unsigned char mac1[3]; // offset 3 + unsigned char mac2[3]; // offset 6 + unsigned char mac3[3]; // offset 9 + unsigned char nor0_size; // offset 12 + unsigned char nor1_size; // offset 13 + unsigned char nand0_size; // offset 14 + unsigned char nand1_size; // offset 15 + unsigned char sdram0_size; // offset 16 + unsigned char sdram1_size; // offset 17 + short processor_type; // offset 18 + int feature_bits; // offset 20 + int platform_bits; // offset 24 +}; + +struct product_zone_2_rev_2 { + unsigned char mac0[3]; // offset 0 + unsigned char mac1[3]; // offset 3 + unsigned char mac2[3]; // offset 6 + unsigned char mac3[3]; // offset 9 + unsigned char nor0_size; // offset 12 + unsigned char nor1_size; // offset 13 + unsigned char nand0_size; // offset 14 + unsigned char nand1_size; // offset 15 + unsigned char sdram0_size; // offset 16 + unsigned char sdram1_size; // offset 17 + short processor_type; // offset 18 + char reserved[4]; // offset 20 + int platform_bits; // offset 24 +}; + +struct product_zone_2_rev_3 { + unsigned char mac0[3]; // offset 0 + unsigned char mac1[3]; // offset 3 + unsigned char mac2[3]; // offset 6 + unsigned char mac3[3]; // offset 9 + unsigned char nor0_size; // offset 12 + unsigned char nor1_size; // offset 13 + unsigned char nand0_size; // offset 14 + unsigned char nand1_size; // offset 15 + unsigned char sdram0_size; // offset 16 + unsigned char sdram1_size; // offset 17 + short processor_type; // offset 18 + char reserved[4]; // offset 20 + int platform_bits; // offset 24 + int hardware_revision; // offset 28 +}; + + +struct product_id_data { + struct { + union { + struct product_zone_0_rev_0 pz_0r0; + struct product_zone_0_rev_1 pz_0r1; + struct product_zone_0_rev_2 pz_0r2; + } u_zone0; + + struct { + char model_revision; + char model_number[31]; + } zone1; + union { + struct product_zone_2_rev_0 pz_2r0; + struct product_zone_2_rev_2 pz_2r2; + struct product_zone_2_rev_3 pz_2r3; + } zone2; + struct { + unsigned char data[468]; + unsigned int valid; + } wifi_config_data; + } d; + unsigned int checksum; +}; + +// Only calculate across the data to checksum, compare to stored +// value(outside of checksummed range) +static inline int calculate_checksum(void *p, int len) +{ + unsigned char *buf = p; + unsigned int xsum = 0; + int i; + + for (i=0; i<len; ++i) + xsum = ((xsum << 3) || (xsum >> 29)) ^ buf[i]; + + return xsum; +} + +extern int fetch_production_data(void); +extern void dump_production_data(void); +extern void board_get_nth_enetaddr (unsigned char *enetaddr, int which, int position); + +#endif diff --git a/board/ti/logic/splash-332x57.h b/board/ti/logic/splash-332x57.h new file mode 100644 index 00000000000..d5b2e63d617 --- /dev/null +++ b/board/ti/logic/splash-332x57.h @@ -0,0 +1,367 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.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 + */ + +/* + * Splash Screen Image + * + * To update this image, perform the following steps to convert your + * image to RGB565, compress with gzip, and format as C code. + * 1) Open your image in GIMP + * 2) Select File -> Save as... + * 3) Enter splash.bmp and select Save + * 4) On the "Save as BMP" dialog, open the "Advanced Options" section + * 5) Select "R5 G6 B5" under the "16 bits" section and select Save + * 6) At a prompt, run the following commands: + * gzip splash.bmp + * xxd -i splash.bmp.gz > splash.h + * 7) Replace the code below with the contents of splash.h + * + * Note that this image originally came from a 800x600 logo image, cropped + * in gimp using a selection box originating at [234,212], and 332x57 large. + */ +unsigned char splash_bmp_gz[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x95, 0xeb, 0x22, 0x4f, 0x00, 0x03, 0x73, 0x70, + 0x6c, 0x61, 0x73, 0x68, 0x32, 0x2e, 0x62, 0x6d, 0x70, 0x00, 0xed, 0x5d, + 0x4f, 0x48, 0xe3, 0x5e, 0x1e, 0xef, 0x8f, 0x2d, 0x6c, 0x60, 0x3d, 0x84, + 0x1d, 0x61, 0x02, 0xca, 0x6e, 0x02, 0x0a, 0x06, 0x1c, 0x68, 0x61, 0x0e, + 0x16, 0xbc, 0xb4, 0xd0, 0x05, 0xcb, 0x7a, 0xb0, 0xfc, 0x84, 0x51, 0xc6, + 0x83, 0x53, 0x1c, 0x90, 0xe2, 0x41, 0x8a, 0x07, 0x29, 0x5e, 0x4a, 0xf1, + 0x20, 0x65, 0x0e, 0x6e, 0x11, 0x46, 0xda, 0x39, 0x38, 0xe8, 0x80, 0x4b, + 0x3d, 0x08, 0xf5, 0x20, 0xd8, 0x8b, 0xd0, 0x1e, 0x84, 0x0a, 0x23, 0x74, + 0x96, 0x11, 0xda, 0x05, 0x85, 0x08, 0x2e, 0xe4, 0xe0, 0x21, 0x0b, 0x1e, + 0xdc, 0x3e, 0xf3, 0x8b, 0x7d, 0x2f, 0x7f, 0x5e, 0xde, 0x4b, 0xaa, 0xee, + 0xfc, 0x26, 0x9f, 0x87, 0xce, 0x80, 0x4d, 0xf2, 0x5e, 0xf2, 0xc9, 0xf7, + 0x7d, 0xff, 0x37, 0x32, 0xfe, 0x97, 0xbc, 0xef, 0x1e, 0x7f, 0x6b, 0xff, + 0x8c, 0xb4, 0x7f, 0xfe, 0xfe, 0x8b, 0xcf, 0x17, 0x6a, 0xff, 0xfb, 0x8b, + 0x8f, 0xf5, 0xfd, 0xa1, 0xfd, 0xef, 0xbf, 0xfe, 0xe1, 0xf3, 0xfd, 0xf9, + 0x4f, 0xea, 0xcf, 0x03, 0xfe, 0xeb, 0xf3, 0xfd, 0xfb, 0x8f, 0x3e, 0xdf, + 0x5f, 0x7d, 0xdd, 0xc3, 0x2c, 0x33, 0x20, 0x48, 0xa1, 0xb1, 0x98, 0x3a, + 0x86, 0x22, 0x3b, 0x41, 0xbf, 0x10, 0x66, 0xba, 0x78, 0x01, 0x0f, 0x1e, + 0xa8, 0x10, 0x66, 0x6a, 0xa1, 0xd3, 0xf4, 0x64, 0xe9, 0x63, 0x7d, 0x45, + 0xae, 0x28, 0xda, 0xe8, 0x97, 0xde, 0x54, 0x5f, 0x14, 0xe5, 0x34, 0x13, + 0x39, 0xe6, 0x9e, 0x7b, 0x86, 0x1e, 0x7e, 0x36, 0x84, 0x99, 0x9e, 0x84, + 0x72, 0xf4, 0x49, 0xb9, 0xb3, 0x44, 0x45, 0x39, 0x6c, 0xbc, 0x28, 0x8e, + 0xc5, 0x3c, 0xd9, 0xe9, 0xe1, 0xa9, 0xb0, 0x16, 0x1f, 0xa9, 0x5a, 0x33, + 0x12, 0xc5, 0x72, 0x7d, 0x2f, 0x35, 0x20, 0x3c, 0xf7, 0x8c, 0x3d, 0xfc, + 0xde, 0x11, 0x66, 0x16, 0x73, 0xa4, 0x9c, 0xd4, 0xd0, 0xd7, 0x5a, 0xcc, + 0x79, 0xdc, 0xf4, 0xf0, 0xb8, 0xa0, 0x91, 0x96, 0x1d, 0x8c, 0x4a, 0x72, + 0xda, 0xd3, 0x38, 0x3d, 0x3c, 0x26, 0x66, 0x99, 0xf1, 0xe4, 0x79, 0x93, + 0x9e, 0x9b, 0x9b, 0x8d, 0xeb, 0xe9, 0xe7, 0x9e, 0xbb, 0x87, 0xdf, 0x37, + 0x8e, 0xb9, 0x89, 0xdc, 0x8a, 0x4c, 0xcf, 0xcd, 0xc9, 0x52, 0x54, 0x7c, + 0xee, 0xb9, 0x77, 0x10, 0x66, 0x2e, 0xf9, 0x5a, 0x68, 0x2d, 0x7e, 0x32, + 0x3d, 0x9e, 0x3c, 0x99, 0x9e, 0x8f, 0xd5, 0x42, 0x17, 0x9c, 0x7b, 0x4b, + 0x8d, 0x6f, 0xdf, 0x9d, 0x9d, 0xe0, 0x7c, 0xec, 0x64, 0xfa, 0x55, 0xf2, + 0x55, 0x72, 0x37, 0xbe, 0x16, 0x67, 0x22, 0x51, 0x31, 0xc3, 0xf2, 0x5d, + 0x98, 0xb1, 0x39, 0xc2, 0x8c, 0x5f, 0x60, 0x22, 0x6b, 0xf1, 0xeb, 0xf6, + 0x3a, 0xae, 0xef, 0xd7, 0xf1, 0x33, 0xef, 0x4d, 0x5f, 0x42, 0x93, 0x25, + 0x7a, 0x66, 0xf6, 0x4b, 0x27, 0x2e, 0xa4, 0xe6, 0x6e, 0x3c, 0x90, 0x95, + 0xd3, 0x76, 0x83, 0x6d, 0x7f, 0x66, 0xd0, 0x86, 0xff, 0x52, 0x88, 0xcd, + 0x2a, 0x47, 0xe7, 0xcd, 0x22, 0xf4, 0x76, 0x15, 0xe5, 0xf3, 0xa6, 0x72, + 0x14, 0xc8, 0x32, 0x11, 0xde, 0xe1, 0xfc, 0xe6, 0xc4, 0xbd, 0xd4, 0x41, + 0xf9, 0xb0, 0xb1, 0x22, 0xb7, 0xa0, 0x35, 0x57, 0x94, 0x51, 0x69, 0xb9, + 0xbe, 0x51, 0x96, 0xd3, 0x6b, 0xf1, 0x2d, 0xd6, 0xfa, 0xe8, 0x01, 0x41, + 0x9d, 0x3b, 0xf8, 0x7d, 0x9a, 0xbe, 0x20, 0xe0, 0x56, 0x98, 0x19, 0x8b, + 0x4d, 0xe4, 0x46, 0xaa, 0xb7, 0xcd, 0x0a, 0xe4, 0x1d, 0x59, 0x91, 0x0f, + 0x1b, 0x4b, 0xa5, 0xf1, 0xa4, 0x9f, 0x50, 0xab, 0x07, 0xfe, 0x95, 0x40, + 0x36, 0x90, 0x1d, 0x8b, 0x91, 0x7d, 0x5e, 0xc3, 0x31, 0xa7, 0xcd, 0x57, + 0x3f, 0x3e, 0xa4, 0x86, 0x13, 0x6b, 0xf1, 0xa8, 0xf8, 0x5c, 0xfe, 0x18, + 0x67, 0xda, 0xe6, 0x7a, 0x3e, 0x83, 0x79, 0x3a, 0x38, 0x2c, 0x11, 0xbf, + 0x09, 0x6b, 0x71, 0xab, 0x73, 0x08, 0xcc, 0x6e, 0x5c, 0x39, 0x6a, 0x62, + 0xbc, 0x5c, 0xad, 0x3b, 0xae, 0x3a, 0x9c, 0x78, 0x47, 0x39, 0xc7, 0xab, + 0xf0, 0xaf, 0xdb, 0x45, 0xdb, 0x3d, 0xe4, 0xb6, 0x79, 0x56, 0x10, 0x2d, + 0xfc, 0x67, 0xf3, 0xb1, 0xce, 0xe7, 0x2a, 0x4a, 0x2d, 0x84, 0xbf, 0x5e, + 0x86, 0x1d, 0x4e, 0x4c, 0xd5, 0x71, 0xd7, 0x1a, 0x95, 0x26, 0x72, 0x24, + 0x92, 0x33, 0xc3, 0x2e, 0xdf, 0x9f, 0x67, 0x3f, 0x4f, 0xb7, 0xe2, 0x5a, + 0x08, 0xbf, 0x56, 0xf0, 0x7e, 0xdc, 0x14, 0x76, 0xe3, 0xcf, 0x21, 0xbd, + 0x33, 0xec, 0x87, 0xd4, 0x2d, 0xb5, 0xb6, 0x79, 0x50, 0x26, 0x91, 0x06, + 0x46, 0xfc, 0xba, 0x4d, 0x7a, 0x05, 0xab, 0x77, 0xff, 0x4b, 0x68, 0xa3, + 0x4c, 0x76, 0x06, 0xae, 0x4a, 0x2e, 0x3f, 0x8e, 0xb9, 0xfd, 0x7c, 0x05, + 0xc3, 0x74, 0x14, 0xad, 0xbb, 0x97, 0x95, 0xeb, 0x69, 0x23, 0x37, 0xc7, + 0x62, 0xad, 0x87, 0xcf, 0x14, 0xe5, 0x2f, 0x58, 0x5e, 0x8e, 0xc5, 0x38, + 0x22, 0x89, 0xb0, 0xd9, 0x98, 0xb7, 0x5d, 0x45, 0x86, 0x55, 0xa5, 0xcb, + 0x62, 0x8e, 0x74, 0xbd, 0x2a, 0x76, 0x82, 0x64, 0x6b, 0x3e, 0x6c, 0x04, + 0xb2, 0xa4, 0xb2, 0xbb, 0x9b, 0xf0, 0x0b, 0xeb, 0x79, 0x7b, 0x49, 0x81, + 0x62, 0xaa, 0xee, 0x44, 0xd3, 0x24, 0xe5, 0x65, 0xcb, 0x82, 0x97, 0xc3, + 0x89, 0x19, 0x8a, 0x79, 0xb6, 0xee, 0xd8, 0x2c, 0xc9, 0x4e, 0x24, 0x85, + 0x0e, 0x1b, 0x74, 0xab, 0x07, 0x30, 0x4a, 0x74, 0x98, 0x97, 0x2b, 0x18, + 0x5e, 0x0a, 0x8c, 0x9c, 0xc6, 0xc9, 0x7b, 0x14, 0x15, 0xe5, 0x43, 0x0a, + 0x3f, 0x7f, 0xb7, 0xbc, 0x5c, 0x2a, 0xad, 0xe7, 0xf7, 0x1f, 0xc6, 0x7a, + 0xfe, 0xa6, 0x70, 0x50, 0x1e, 0xa9, 0xf6, 0x4b, 0xf0, 0x2c, 0x6e, 0x9b, + 0x3d, 0x09, 0xba, 0xb3, 0x77, 0x07, 0xb5, 0xd0, 0x01, 0xa1, 0x1c, 0xd2, + 0x70, 0xd8, 0xa0, 0x67, 0x26, 0xb9, 0xbc, 0x34, 0x93, 0x12, 0xa7, 0x69, + 0xba, 0x19, 0x02, 0x9c, 0x15, 0x04, 0x1b, 0x66, 0xae, 0xc5, 0x69, 0xb8, + 0xde, 0x81, 0x51, 0xcf, 0x26, 0xe3, 0xa5, 0xc0, 0xec, 0xe7, 0x69, 0xaf, + 0xb5, 0x90, 0xc4, 0xad, 0xc0, 0x2d, 0x2f, 0x77, 0x82, 0xc6, 0xbf, 0x85, + 0x99, 0x41, 0x71, 0x28, 0x32, 0x91, 0xfb, 0x08, 0x69, 0x1a, 0x8b, 0xb9, + 0xd9, 0x67, 0xd0, 0x37, 0x05, 0xe6, 0x64, 0x7a, 0x19, 0xab, 0xef, 0xe8, + 0xb1, 0xd9, 0xa0, 0xdd, 0xcd, 0x61, 0x5e, 0xb6, 0xee, 0x3a, 0x71, 0x79, + 0xe3, 0x30, 0xca, 0xcb, 0x0f, 0x29, 0xe3, 0x0c, 0xde, 0x4a, 0x8d, 0x72, + 0x20, 0xfb, 0x2a, 0x39, 0x9c, 0x18, 0x4e, 0xec, 0xa5, 0x5e, 0x6f, 0x6f, + 0x9a, 0xc8, 0xbd, 0x9b, 0x02, 0x4e, 0x66, 0x0e, 0x45, 0x8c, 0x7e, 0x89, + 0x8a, 0xc2, 0x55, 0xd7, 0xf3, 0xe3, 0xf7, 0x67, 0x1d, 0x4e, 0x9c, 0xa6, + 0x7b, 0xb7, 0xb9, 0xaa, 0xf1, 0x53, 0xce, 0x78, 0xc9, 0xfb, 0xd6, 0x4d, + 0x58, 0x79, 0xdb, 0x5c, 0x2a, 0xc9, 0x69, 0x70, 0xb5, 0xf1, 0xe4, 0x7a, + 0xde, 0x4c, 0xeb, 0x64, 0x22, 0xd6, 0x6b, 0x70, 0xcb, 0x4b, 0x09, 0xa3, + 0x71, 0xbc, 0x63, 0x81, 0x36, 0xaf, 0xcd, 0x62, 0xb2, 0x44, 0xab, 0xb5, + 0x5b, 0x21, 0xc3, 0x2e, 0x24, 0xc9, 0xad, 0x94, 0x2d, 0x56, 0x4e, 0xbf, + 0x95, 0x8c, 0x77, 0xc5, 0x0a, 0xca, 0x11, 0x9d, 0x05, 0x04, 0xf3, 0xf2, + 0xa6, 0x50, 0x0b, 0x99, 0x0f, 0xa9, 0xfd, 0xa3, 0x3f, 0xef, 0x58, 0x4c, + 0xaf, 0x0b, 0x01, 0x5f, 0xff, 0x9c, 0x4e, 0x62, 0x7f, 0xe6, 0xd6, 0xa0, + 0xbb, 0xa8, 0x81, 0xcd, 0x5a, 0xcd, 0x67, 0x40, 0xd0, 0xeb, 0xd6, 0x15, + 0x65, 0x3f, 0x5f, 0x0b, 0xe9, 0x65, 0xac, 0xc0, 0xac, 0x06, 0xf7, 0x52, + 0x28, 0xeb, 0x9d, 0xf1, 0xf2, 0x55, 0x52, 0x3f, 0xbb, 0xef, 0x8d, 0x85, + 0x24, 0x1a, 0x4f, 0xcb, 0xb0, 0x27, 0xd3, 0xa8, 0x66, 0x71, 0x53, 0xc0, + 0x49, 0x00, 0xb7, 0xbc, 0xbc, 0x0a, 0xe3, 0x3f, 0x17, 0x66, 0x3e, 0xa4, + 0xb4, 0x6c, 0x0a, 0x5a, 0xdb, 0xca, 0x0a, 0xc0, 0x46, 0xe4, 0xaa, 0x76, + 0xb6, 0x21, 0x8c, 0xa8, 0x78, 0x56, 0x20, 0xb7, 0x01, 0xce, 0x0a, 0x34, + 0xb3, 0x81, 0x79, 0x79, 0x9a, 0x26, 0x3f, 0xee, 0x98, 0xd3, 0x6b, 0x80, + 0x4b, 0x25, 0xab, 0xd8, 0x28, 0x7c, 0x17, 0x35, 0xae, 0x99, 0x6b, 0xab, + 0xbc, 0xef, 0xb5, 0x4e, 0xaf, 0x58, 0xae, 0x0f, 0x61, 0xe4, 0xd2, 0xa0, + 0x08, 0x4b, 0x4d, 0x27, 0xbc, 0x9c, 0x13, 0xf5, 0x3a, 0xc3, 0x7e, 0xde, + 0xdc, 0xda, 0x3d, 0xe6, 0x34, 0xdf, 0x45, 0x5f, 0xcb, 0xce, 0x33, 0xf7, + 0xd8, 0xbc, 0x04, 0x60, 0x22, 0x5a, 0x2c, 0xa6, 0x3b, 0xd1, 0x95, 0xb3, + 0x82, 0x7a, 0x97, 0x86, 0xa9, 0xb4, 0x56, 0x26, 0xd2, 0x20, 0xd4, 0x36, + 0x5b, 0x26, 0xcf, 0xc7, 0x1a, 0x30, 0x2f, 0xad, 0xa5, 0x98, 0x11, 0x6c, + 0x16, 0xbd, 0xea, 0x7a, 0x1e, 0xaf, 0xe9, 0xcc, 0xc7, 0xd0, 0xe7, 0x3f, + 0x52, 0x35, 0xdb, 0x7f, 0xd6, 0xe2, 0xad, 0x3b, 0xf4, 0x53, 0x97, 0x3c, + 0xee, 0xac, 0x17, 0xdc, 0x28, 0xb4, 0x97, 0x38, 0xe1, 0xa5, 0xfe, 0x3d, + 0xd8, 0xc3, 0x58, 0x34, 0xb3, 0x0c, 0xf0, 0x2f, 0x6f, 0x94, 0xed, 0x3c, + 0xb9, 0x4f, 0xc3, 0x4b, 0x60, 0x81, 0xa8, 0x3b, 0xe9, 0x6d, 0xd3, 0x99, + 0x2f, 0x06, 0xc6, 0x16, 0xdb, 0xd9, 0x7b, 0x64, 0x0a, 0xf9, 0x04, 0x64, + 0x49, 0x4f, 0x82, 0x4c, 0xdb, 0xec, 0x6b, 0x91, 0xfb, 0x10, 0x9c, 0xf1, + 0xf2, 0x92, 0x47, 0x6d, 0xc3, 0xc9, 0x92, 0x9d, 0x2d, 0xe3, 0xf3, 0x9d, + 0x4c, 0xa3, 0x32, 0xdf, 0xf8, 0x96, 0x0b, 0x0c, 0xea, 0xa9, 0xd9, 0x6c, + 0xd8, 0xad, 0xe3, 0x92, 0x7f, 0xeb, 0x8a, 0x97, 0x52, 0x08, 0x9d, 0xd3, + 0x84, 0x0d, 0x8f, 0xb6, 0x58, 0x33, 0x6f, 0x94, 0x11, 0x4f, 0xc3, 0x4b, + 0x9f, 0xef, 0x7a, 0xba, 0x45, 0xf9, 0xe4, 0xac, 0xaf, 0x0c, 0xef, 0x3c, + 0x34, 0x3b, 0x27, 0xc0, 0x67, 0xee, 0x94, 0x48, 0xdb, 0x24, 0xd7, 0x39, + 0x9c, 0xf1, 0x72, 0x01, 0xd1, 0xc9, 0x6e, 0x9b, 0x64, 0xef, 0x01, 0x9a, + 0x39, 0xf5, 0xb2, 0xa2, 0xff, 0x3b, 0x13, 0x81, 0x7d, 0x35, 0x15, 0x05, + 0xb7, 0x83, 0xab, 0x70, 0xcb, 0x4b, 0xd4, 0x0e, 0xe7, 0xaa, 0xdd, 0x8a, + 0xa6, 0x3c, 0x15, 0x2f, 0x7d, 0x3e, 0x35, 0x42, 0x78, 0xde, 0xfc, 0xec, + 0x52, 0x62, 0xfe, 0xe7, 0x1d, 0xca, 0x20, 0xbc, 0xbf, 0xc1, 0x0c, 0x83, + 0x6d, 0x6d, 0xd3, 0xce, 0xb7, 0x59, 0x94, 0x57, 0x4d, 0x3c, 0x0d, 0x66, + 0x70, 0xc2, 0x4b, 0xde, 0x87, 0xca, 0x35, 0xd2, 0x35, 0x1c, 0x73, 0xb0, + 0x4d, 0x53, 0x51, 0xf4, 0xde, 0x10, 0x94, 0xb7, 0x24, 0x7a, 0xb2, 0x3b, + 0x5e, 0x66, 0x58, 0x78, 0x3e, 0x4d, 0xc5, 0xde, 0x5f, 0x4e, 0x8a, 0xa7, + 0xe3, 0xe5, 0x55, 0x58, 0x3d, 0xc2, 0x4d, 0x2c, 0x1a, 0x60, 0x42, 0x97, + 0x6d, 0xf9, 0x49, 0xc1, 0xf9, 0x1b, 0xac, 0x67, 0x63, 0x17, 0x63, 0x21, + 0xbd, 0x23, 0x4e, 0x78, 0xb9, 0x8a, 0xc8, 0xfc, 0xf3, 0x26, 0xb9, 0x07, + 0x00, 0xd5, 0x4a, 0x51, 0x5d, 0xee, 0x1d, 0x0b, 0x6b, 0x29, 0x9f, 0x6c, + 0xa3, 0x86, 0x00, 0xee, 0x78, 0x09, 0xff, 0x15, 0xc8, 0x6f, 0x7b, 0x5d, + 0x84, 0x14, 0x4f, 0xc7, 0x4b, 0x81, 0x51, 0xbd, 0x1d, 0xaf, 0xb7, 0x79, + 0xca, 0x39, 0xa2, 0x30, 0x66, 0x66, 0xbc, 0xa9, 0x3a, 0xf3, 0x8d, 0xfe, + 0x13, 0xeb, 0xdb, 0xec, 0x97, 0xc8, 0x62, 0xa8, 0x4e, 0x78, 0x79, 0x32, + 0x0d, 0x5f, 0xc9, 0x4e, 0x27, 0x83, 0xb1, 0x1a, 0x84, 0x25, 0xfd, 0x52, + 0x89, 0x87, 0xfe, 0x16, 0x45, 0x2c, 0x63, 0xe5, 0x88, 0x64, 0x4f, 0xbd, + 0xe0, 0xdc, 0xf0, 0x52, 0x46, 0xa2, 0x02, 0xaf, 0xa8, 0x77, 0x2e, 0x6b, + 0x3c, 0x1d, 0x2f, 0xb5, 0x77, 0x9d, 0x46, 0x3a, 0x18, 0x31, 0xcb, 0x98, + 0x65, 0x65, 0xd0, 0xef, 0xe5, 0x2a, 0xb6, 0xd8, 0xbd, 0x54, 0xbf, 0xa5, + 0xb6, 0xb9, 0x6b, 0x99, 0x67, 0x01, 0xc3, 0x09, 0x2f, 0xd1, 0xe7, 0x69, + 0x9d, 0xcf, 0x61, 0x44, 0x86, 0x85, 0x3d, 0xd4, 0x9b, 0x0d, 0xf8, 0x5e, + 0xae, 0xc5, 0xe1, 0xb3, 0x06, 0x30, 0x73, 0xe1, 0x1f, 0xfe, 0xe7, 0x17, + 0xdc, 0xd8, 0xe3, 0xbd, 0xd0, 0xda, 0x2b, 0x0a, 0xa9, 0xe6, 0x43, 0x82, + 0xa7, 0xe4, 0xe5, 0x50, 0x04, 0x1c, 0xf1, 0xc9, 0xd5, 0xfc, 0x51, 0x0d, + 0x4b, 0xc3, 0x54, 0xdd, 0xb9, 0xcf, 0x1e, 0x44, 0xd2, 0xcd, 0xf3, 0x36, + 0x6f, 0x88, 0xfc, 0x98, 0x4e, 0x78, 0x79, 0x53, 0xe8, 0x1c, 0xf3, 0x56, + 0xa2, 0x8b, 0x7d, 0xc2, 0x7e, 0x99, 0xbe, 0x16, 0xec, 0x05, 0xea, 0x49, + 0xc0, 0xb3, 0xb7, 0xd2, 0x97, 0xa2, 0xe2, 0x46, 0x59, 0x39, 0x52, 0xc7, + 0xb7, 0x23, 0xae, 0x0a, 0x5b, 0x4a, 0x74, 0xbc, 0x9c, 0x65, 0x5e, 0x56, + 0x3a, 0xc7, 0x6e, 0x36, 0x70, 0xf9, 0x72, 0xb4, 0xd0, 0x78, 0x49, 0xb3, + 0x97, 0x00, 0x38, 0xe1, 0xe5, 0xa0, 0xa8, 0xee, 0x41, 0x64, 0x72, 0xc8, + 0x1c, 0x7e, 0xc1, 0x8c, 0x43, 0x2d, 0xaa, 0x79, 0x18, 0x51, 0x0b, 0x99, + 0x65, 0xab, 0x8d, 0x10, 0x59, 0x97, 0xf4, 0xbc, 0xe4, 0x7d, 0xb0, 0x6e, + 0x4b, 0xfb, 0x3c, 0x61, 0x0d, 0x73, 0x45, 0x86, 0x2d, 0x9f, 0x3d, 0x28, + 0xaa, 0x59, 0xb1, 0xd4, 0xba, 0x6b, 0x21, 0xeb, 0x08, 0x03, 0x1d, 0x2f, + 0xb7, 0x10, 0x7d, 0xf6, 0xa0, 0xdc, 0x3d, 0xed, 0xb2, 0xc3, 0xcb, 0x9b, + 0x42, 0x54, 0x9c, 0x7b, 0x18, 0xab, 0x41, 0xbf, 0x90, 0x61, 0x71, 0xd7, + 0x71, 0xc2, 0xcb, 0x8b, 0xdf, 0x64, 0xdd, 0xb8, 0x0b, 0x3d, 0x24, 0x2a, + 0x9a, 0xdf, 0x51, 0x3a, 0x4f, 0xa6, 0x19, 0x76, 0xe3, 0xfa, 0x18, 0xee, + 0xa8, 0x44, 0x52, 0x9b, 0xe6, 0x96, 0x97, 0xcb, 0x75, 0x3a, 0xbd, 0x06, + 0x8e, 0xa9, 0xa3, 0xb6, 0x0d, 0xca, 0xd8, 0xf7, 0x16, 0xbb, 0xd2, 0x97, + 0x90, 0x75, 0x56, 0x3f, 0x1d, 0x2f, 0x33, 0x08, 0x2f, 0x27, 0x4b, 0x34, + 0xab, 0xb0, 0x83, 0xc6, 0xcb, 0x8a, 0x32, 0x23, 0x77, 0xc6, 0x8a, 0xdc, + 0x2f, 0x9d, 0x37, 0xa7, 0xea, 0x67, 0x85, 0x5d, 0x8b, 0x6c, 0x66, 0x27, + 0xbc, 0xd4, 0xd6, 0x41, 0xeb, 0x73, 0x84, 0x61, 0xc5, 0xcb, 0x6e, 0xdc, + 0x15, 0x10, 0x77, 0x87, 0xb5, 0x04, 0x32, 0x8b, 0xf6, 0xff, 0x93, 0x97, + 0x33, 0xb2, 0x3e, 0xce, 0xae, 0xe1, 0xc7, 0xe2, 0xa5, 0x35, 0x96, 0xeb, + 0x66, 0xf1, 0x3e, 0x27, 0xbc, 0xdc, 0x7a, 0x44, 0x5e, 0x72, 0x55, 0xe7, + 0xe7, 0x84, 0x71, 0xc9, 0x07, 0xb2, 0x9a, 0x25, 0x60, 0xbd, 0x17, 0xc2, + 0x70, 0xc2, 0x4b, 0x58, 0x6b, 0xf8, 0x58, 0xa7, 0xdb, 0xc7, 0x61, 0x9b, + 0x09, 0xdd, 0xc7, 0x61, 0x5f, 0x7d, 0x51, 0xb6, 0xca, 0xa8, 0xc1, 0xed, + 0xe3, 0xc6, 0x08, 0x12, 0x9e, 0x97, 0xf0, 0x0e, 0xb3, 0x51, 0xe6, 0x69, + 0x96, 0x61, 0x03, 0x8d, 0x97, 0x45, 0xf9, 0xb6, 0xd9, 0xd7, 0xd2, 0xc6, + 0x6d, 0xb3, 0x5f, 0x82, 0xf5, 0xe1, 0xde, 0x6d, 0xbd, 0x5d, 0xe1, 0x84, + 0x97, 0x97, 0x7c, 0xdf, 0xfd, 0x22, 0xe9, 0xe2, 0xda, 0x28, 0x1e, 0x9b, + 0x97, 0xe0, 0xb9, 0x69, 0x32, 0xb3, 0xa2, 0x90, 0xac, 0xce, 0x89, 0xdd, + 0x03, 0xe7, 0x85, 0x8d, 0x4a, 0x74, 0x79, 0xd3, 0x2f, 0x8a, 0x9d, 0x63, + 0x51, 0xbb, 0x07, 0xf5, 0x3e, 0x59, 0x69, 0xf1, 0xef, 0x83, 0x53, 0xf5, + 0xc3, 0x86, 0xf9, 0x30, 0xfa, 0xc5, 0x71, 0xbc, 0x14, 0x18, 0x38, 0xbf, + 0xd5, 0x8d, 0xed, 0x69, 0x84, 0xc6, 0xcb, 0xb3, 0x82, 0x5f, 0x18, 0x80, + 0x46, 0x54, 0x94, 0x42, 0x3d, 0x89, 0xa5, 0x92, 0xc6, 0x4e, 0x7d, 0xec, + 0xc0, 0x09, 0x2f, 0xa3, 0xa2, 0x7a, 0x0c, 0x8d, 0x5f, 0xc4, 0x78, 0x0e, + 0x73, 0x5e, 0x1e, 0x94, 0x9d, 0x9f, 0xb3, 0x83, 0x39, 0xb1, 0x77, 0xbb, + 0xf3, 0x3e, 0x3e, 0x96, 0xbc, 0x44, 0x2d, 0x14, 0xab, 0x3c, 0x76, 0x73, + 0xa0, 0x7e, 0xb2, 0x8f, 0x88, 0x0e, 0x80, 0x46, 0x21, 0xad, 0x34, 0x6e, + 0xbe, 0xfd, 0xcc, 0xad, 0x86, 0xd1, 0xa2, 0xc0, 0xfb, 0x89, 0xce, 0x20, + 0xbf, 0xc2, 0x8a, 0xdc, 0xcd, 0xba, 0x52, 0x3b, 0x7b, 0x5c, 0x8c, 0x69, + 0xd9, 0x58, 0xa8, 0x8c, 0x77, 0xc2, 0xcb, 0xb1, 0xfb, 0x1a, 0xa6, 0xa2, + 0xa5, 0xe6, 0x43, 0x02, 0x2b, 0x5e, 0xd2, 0xfa, 0x13, 0x8c, 0x30, 0xe6, + 0x69, 0x92, 0xc5, 0x22, 0x9d, 0xf0, 0x12, 0xcd, 0xfa, 0xc1, 0x79, 0x1a, + 0xf5, 0x78, 0x8f, 0xf8, 0xd5, 0x37, 0x10, 0x1b, 0xd8, 0x2f, 0xc0, 0xbe, + 0x58, 0x32, 0xbf, 0xba, 0x1d, 0xf0, 0xbc, 0x44, 0xa3, 0xfc, 0x6e, 0xf6, + 0x41, 0x3d, 0xec, 0xfd, 0x97, 0x51, 0x51, 0x5d, 0x2d, 0x1a, 0x93, 0x77, + 0xc2, 0x4b, 0x35, 0x82, 0x78, 0xd8, 0x70, 0x23, 0xef, 0xa3, 0x22, 0xfc, + 0x44, 0x3b, 0x70, 0x23, 0x83, 0xd5, 0xaa, 0x50, 0x63, 0x3d, 0xcc, 0x79, + 0x93, 0x24, 0xe2, 0xe3, 0x84, 0x97, 0x97, 0x3c, 0xec, 0xcd, 0xa6, 0xb9, + 0x23, 0xa8, 0x47, 0x1e, 0xd5, 0xd4, 0xc3, 0x0c, 0x9c, 0x3f, 0x6c, 0x5f, + 0xbd, 0x48, 0x02, 0x3c, 0x2f, 0x51, 0x5d, 0xf5, 0xdb, 0xd1, 0xd3, 0xc6, + 0x21, 0xd5, 0xb7, 0xa2, 0x89, 0x64, 0x09, 0xd0, 0xf3, 0x32, 0xcc, 0xa8, + 0x5a, 0x32, 0x5d, 0xd6, 0xad, 0x1e, 0x68, 0xbe, 0xa0, 0x86, 0x5b, 0x22, + 0x06, 0x59, 0x81, 0x89, 0x7c, 0x33, 0xe4, 0x83, 0x03, 0x90, 0xe9, 0x06, + 0xce, 0xf2, 0x89, 0xd0, 0xe8, 0x3c, 0x69, 0xbc, 0xea, 0x82, 0x43, 0xf3, + 0x24, 0xf4, 0x7a, 0x06, 0x5a, 0x2d, 0xd4, 0xbb, 0x4d, 0x3a, 0x1b, 0x6b, + 0xe0, 0x79, 0x19, 0x66, 0x60, 0x8b, 0x9c, 0x4e, 0x23, 0xc1, 0x83, 0x84, + 0x97, 0x5a, 0xdc, 0x15, 0xd6, 0xa4, 0xe9, 0x79, 0xa9, 0xed, 0x5d, 0xee, + 0x24, 0x5b, 0x86, 0x35, 0xab, 0xf3, 0x73, 0x9e, 0x3d, 0x17, 0x15, 0x6f, + 0x0a, 0x56, 0x75, 0x7c, 0x64, 0x67, 0x75, 0xc6, 0xcb, 0x5d, 0x24, 0x66, + 0xd8, 0x2f, 0xd9, 0x67, 0xca, 0x02, 0xa0, 0x59, 0x65, 0x53, 0x75, 0xbd, + 0x7c, 0x7a, 0x8f, 0xe4, 0x83, 0x54, 0x14, 0x77, 0xf7, 0x1a, 0xc0, 0x2e, + 0xcf, 0x0d, 0x95, 0xdf, 0xb4, 0x3e, 0x2f, 0x6b, 0x90, 0xf0, 0x72, 0x8b, + 0x55, 0x2b, 0xc8, 0xe0, 0xda, 0x4a, 0x5a, 0x5e, 0x86, 0x7f, 0xcb, 0x58, + 0xdd, 0x74, 0xb5, 0x8b, 0x03, 0x1b, 0xd0, 0x98, 0x75, 0x8e, 0x5a, 0xa5, + 0xe4, 0xc8, 0xb4, 0x35, 0x4a, 0x33, 0xe9, 0xab, 0xa2, 0x65, 0x5a, 0x53, + 0x67, 0x84, 0x33, 0x5e, 0x66, 0xd8, 0x8f, 0x88, 0x17, 0xff, 0xa0, 0x6c, + 0xaf, 0x0d, 0x8e, 0x27, 0xd1, 0x37, 0xc8, 0x4c, 0x9f, 0x43, 0xb3, 0xc7, + 0x6f, 0x9b, 0x56, 0xde, 0x75, 0x52, 0xd8, 0xf1, 0x32, 0x2a, 0xa2, 0x3a, + 0xb9, 0xbb, 0xdd, 0xb0, 0x03, 0x12, 0x5e, 0x6a, 0xfe, 0xd3, 0x3d, 0x17, + 0xbc, 0xd4, 0x76, 0x18, 0x5c, 0x96, 0x3d, 0x19, 0x60, 0x1b, 0x50, 0x85, + 0x93, 0x2a, 0xe0, 0xb0, 0x6d, 0xa5, 0xe4, 0xcb, 0x0a, 0x99, 0xdd, 0xe0, + 0xb4, 0x8e, 0x62, 0x38, 0x81, 0x5e, 0x6f, 0xb2, 0x84, 0xf7, 0x63, 0xbe, + 0xd2, 0xb1, 0xd2, 0x5c, 0x36, 0xed, 0x04, 0xd1, 0x3a, 0xa0, 0xcd, 0x06, + 0xde, 0x76, 0x0b, 0x33, 0x81, 0x2c, 0xce, 0x9f, 0x6c, 0x5f, 0x47, 0xa1, + 0xef, 0xf2, 0x38, 0x91, 0xb3, 0xbe, 0x6f, 0xb3, 0x4c, 0xef, 0x36, 0x59, + 0xfd, 0x3b, 0x09, 0x2f, 0x07, 0x7f, 0x7b, 0x27, 0x60, 0x8b, 0x9c, 0x8e, + 0x97, 0xd7, 0xd3, 0xea, 0x3d, 0xed, 0x86, 0x9c, 0xd7, 0x57, 0x5c, 0x7f, + 0xcd, 0xf3, 0xd4, 0xe7, 0xb8, 0x0a, 0xdb, 0x57, 0x96, 0x93, 0x5a, 0x97, + 0x4e, 0x79, 0x19, 0x66, 0xf4, 0x5a, 0xed, 0x48, 0xd5, 0xca, 0x17, 0xfe, + 0x99, 0x5b, 0xcc, 0xe9, 0xb5, 0x0d, 0xab, 0xbc, 0x8c, 0x80, 0xae, 0x6e, + 0x08, 0x57, 0xe3, 0xb5, 0x13, 0x04, 0x1e, 0xfe, 0x16, 0x46, 0x5a, 0xd8, + 0xf3, 0xf2, 0xb3, 0x49, 0xfd, 0x9c, 0xb9, 0xc7, 0xc5, 0x2f, 0xa8, 0x5a, + 0xf5, 0x52, 0xc9, 0xde, 0x63, 0x4b, 0xc2, 0x4b, 0x2d, 0x4f, 0x05, 0xbe, + 0x6b, 0x34, 0xbc, 0xec, 0x49, 0xa8, 0xef, 0xb0, 0x55, 0x0d, 0x1f, 0x1d, + 0xe6, 0x63, 0xf0, 0x13, 0x3a, 0x28, 0xd3, 0x32, 0x7d, 0x40, 0x20, 0xe9, + 0x8f, 0x82, 0x66, 0x90, 0xe1, 0xe0, 0x94, 0x97, 0x60, 0x0f, 0xd4, 0x6b, + 0x11, 0x4d, 0xe5, 0x45, 0x91, 0x89, 0xc0, 0x57, 0x0e, 0x33, 0x51, 0x71, + 0x2f, 0x65, 0xec, 0xa1, 0x68, 0xbd, 0x5f, 0x66, 0x58, 0x63, 0x37, 0x96, + 0x83, 0xb2, 0xbe, 0x23, 0xcf, 0x31, 0x27, 0xc6, 0x6e, 0xa0, 0x1a, 0x51, + 0xab, 0xfe, 0x17, 0x24, 0x75, 0xba, 0xc6, 0x7a, 0xe3, 0x15, 0x79, 0x31, + 0xb7, 0x13, 0x9c, 0x45, 0x7c, 0x58, 0x70, 0x8c, 0xf7, 0xb6, 0x69, 0xa7, + 0xf9, 0xda, 0xf3, 0xf2, 0x98, 0xfb, 0xde, 0x30, 0x3e, 0x29, 0x52, 0x5e, + 0x5e, 0x70, 0x9d, 0xd8, 0x06, 0xfa, 0x56, 0x82, 0x8e, 0x7a, 0x02, 0xc3, + 0xfb, 0xc0, 0xef, 0x0c, 0x3b, 0xcb, 0x84, 0x19, 0xb2, 0x9a, 0xb4, 0x01, + 0x28, 0x63, 0xf0, 0xa0, 0x4c, 0x17, 0xc3, 0xcb, 0xb0, 0x7b, 0xa9, 0xbe, + 0xce, 0x8d, 0xc6, 0x80, 0xbc, 0x72, 0xd3, 0x39, 0x2f, 0xc1, 0x3b, 0x66, + 0xac, 0xe7, 0x68, 0x2a, 0xcb, 0xf5, 0xc9, 0x12, 0xd8, 0x5d, 0xe5, 0xf4, + 0x4d, 0x81, 0xab, 0x9a, 0x69, 0xc0, 0x07, 0x65, 0x9c, 0x96, 0x1e, 0x15, + 0xcd, 0x7a, 0x81, 0x1e, 0x36, 0x36, 0xca, 0x8b, 0xb9, 0xd3, 0xf4, 0x69, + 0x7a, 0x31, 0xb7, 0x51, 0xd6, 0xcb, 0xb8, 0x8a, 0x62, 0xbe, 0x62, 0xb2, + 0x7e, 0x1b, 0xe3, 0x86, 0x0a, 0x72, 0xe0, 0xfd, 0x1d, 0xa9, 0x82, 0x5d, + 0xfb, 0x34, 0x1d, 0xc8, 0x6e, 0x94, 0xf5, 0x33, 0x6a, 0xd9, 0x64, 0xef, + 0xd8, 0xf9, 0xd5, 0xa3, 0xa2, 0x96, 0x63, 0x87, 0x6a, 0x21, 0x24, 0x7d, + 0x0d, 0x06, 0x84, 0xbd, 0x54, 0x67, 0xfd, 0x13, 0x39, 0x1e, 0xf9, 0xab, + 0x14, 0x9a, 0xaa, 0xcf, 0x89, 0x97, 0xfc, 0x72, 0x7d, 0x35, 0xf8, 0x35, + 0xbf, 0x98, 0x63, 0x22, 0x64, 0x7e, 0x99, 0x4e, 0xf4, 0xeb, 0x6b, 0x9e, + 0xc6, 0x86, 0x02, 0x3d, 0xd3, 0xf0, 0xbd, 0xc6, 0x3a, 0x58, 0x22, 0xa8, + 0x4e, 0xd4, 0xe0, 0x86, 0x97, 0xc0, 0x4b, 0x41, 0xdf, 0xb3, 0xd3, 0xfe, + 0x6d, 0xdc, 0x09, 0x76, 0xa7, 0x3b, 0x11, 0x79, 0x7f, 0xa2, 0xf1, 0x24, + 0x79, 0x7d, 0x3e, 0xc0, 0x61, 0x03, 0x6f, 0x55, 0x5a, 0xf3, 0x32, 0xc3, + 0xd6, 0x42, 0xec, 0x43, 0x0e, 0x03, 0x57, 0x45, 0xf7, 0x35, 0x8d, 0x97, + 0x7a, 0xdf, 0x2d, 0xdf, 0xd6, 0x6d, 0xfd, 0x42, 0x2d, 0x34, 0x9c, 0x58, + 0x2a, 0x75, 0x2c, 0xb5, 0x15, 0xd9, 0xe8, 0xa1, 0x1b, 0x8a, 0xcc, 0xc8, + 0x20, 0x9f, 0x6e, 0x46, 0xee, 0x49, 0xac, 0xc8, 0xb7, 0xcd, 0xf1, 0xe4, + 0x72, 0x1d, 0x37, 0xd3, 0x0e, 0x80, 0x86, 0x09, 0xce, 0xc8, 0x93, 0x7d, + 0xfc, 0x1e, 0x35, 0xe2, 0x9e, 0x69, 0x60, 0x97, 0xa1, 0xe9, 0xbd, 0xee, + 0x8e, 0x97, 0xc0, 0x7b, 0x4a, 0xd7, 0xa9, 0xe6, 0xa6, 0x40, 0xb2, 0x47, + 0x68, 0xba, 0x1c, 0x39, 0x36, 0xca, 0x66, 0x71, 0x44, 0x52, 0x5e, 0x82, + 0xe8, 0x3c, 0xd9, 0x4e, 0x04, 0x30, 0x52, 0xb5, 0xbb, 0xc3, 0x1a, 0x2f, + 0xbf, 0x37, 0x5e, 0x6f, 0x4f, 0x96, 0x3a, 0xe3, 0xdb, 0xd1, 0x72, 0xbd, + 0x63, 0xd9, 0x8d, 0x54, 0xf5, 0x9a, 0xaa, 0xc6, 0x4b, 0xe5, 0xa8, 0x73, + 0xdc, 0xaf, 0xdb, 0x20, 0x13, 0xfa, 0x8d, 0xae, 0x23, 0x27, 0xf8, 0x8c, + 0x59, 0xbd, 0x28, 0x13, 0xe9, 0x97, 0xde, 0x07, 0x41, 0x2e, 0xc7, 0x64, + 0xe9, 0x7b, 0x63, 0xb3, 0x31, 0x59, 0x52, 0x8e, 0xec, 0xef, 0x37, 0xc0, + 0x9c, 0xb8, 0x54, 0xc2, 0x77, 0xbc, 0x43, 0x01, 0x32, 0xd2, 0xc9, 0xdf, + 0xe6, 0x26, 0xa5, 0xd7, 0x0f, 0xae, 0x37, 0x72, 0xe6, 0x47, 0x3d, 0xe6, + 0xac, 0x32, 0xe6, 0xf5, 0x38, 0x6c, 0x90, 0xfb, 0x1e, 0x04, 0x66, 0x21, + 0x49, 0xca, 0x94, 0xf3, 0xb6, 0x54, 0x30, 0xdf, 0x21, 0x68, 0xfa, 0x0c, + 0x46, 0x91, 0xec, 0x02, 0xeb, 0xfb, 0xfb, 0x35, 0x6f, 0xff, 0x66, 0xd9, + 0xe7, 0xb9, 0xad, 0xc8, 0xeb, 0x79, 0x63, 0x6d, 0x2d, 0x59, 0x9f, 0xc1, + 0x8a, 0xa2, 0x1c, 0xf5, 0x24, 0xcc, 0xfd, 0x02, 0x4c, 0x64, 0x54, 0x9a, + 0x13, 0xd5, 0xba, 0xa7, 0x85, 0xa4, 0x9c, 0xae, 0x28, 0x53, 0x84, 0xf2, + 0x92, 0x06, 0xb3, 0x0c, 0x9a, 0x53, 0x69, 0x0f, 0x5a, 0x4f, 0x16, 0xcc, + 0x4b, 0x9a, 0x48, 0x37, 0x8a, 0x2f, 0xa1, 0xb3, 0x82, 0x75, 0xa5, 0x11, + 0xc0, 0x72, 0xfd, 0x94, 0xfa, 0x7b, 0x0a, 0x06, 0x84, 0x40, 0x16, 0xbf, + 0xa3, 0xb7, 0xee, 0xcf, 0x6b, 0xad, 0xd9, 0xc3, 0x7d, 0x59, 0x8b, 0xb2, + 0x7d, 0x64, 0x73, 0x28, 0x32, 0x59, 0xb2, 0x7e, 0xc7, 0x5a, 0x6d, 0x1d, + 0x84, 0xcc, 0xf6, 0x45, 0x73, 0xe8, 0xd0, 0x73, 0x8c, 0x4a, 0x23, 0xd5, + 0x40, 0xd6, 0xdc, 0xff, 0x55, 0x0b, 0xb5, 0x30, 0xab, 0x05, 0x5d, 0x59, + 0x0f, 0xca, 0x72, 0x5a, 0x32, 0x74, 0x6b, 0xd2, 0xc0, 0x66, 0xa7, 0xea, + 0x15, 0x65, 0xa4, 0xba, 0x9f, 0x1f, 0x95, 0x2a, 0x0a, 0xc8, 0x90, 0x07, + 0xd2, 0x80, 0x64, 0xc6, 0xe4, 0xe0, 0x7d, 0xbb, 0xd4, 0xbd, 0x83, 0xe9, + 0xf3, 0x3f, 0x76, 0x82, 0xa0, 0x5f, 0x39, 0x18, 0xbb, 0x71, 0x77, 0x19, + 0x35, 0x7e, 0xe1, 0x7a, 0xfa, 0xac, 0x00, 0x3a, 0x40, 0xaf, 0xc8, 0x4d, + 0x45, 0x1d, 0x33, 0xed, 0x3b, 0x09, 0xfa, 0x58, 0x8f, 0xc5, 0x9c, 0x7a, + 0xd8, 0x32, 0xec, 0x7c, 0x6c, 0x31, 0xf7, 0xb2, 0xf2, 0xbd, 0x31, 0xf3, + 0x70, 0x56, 0xa0, 0x37, 0x4d, 0xdd, 0x77, 0xb1, 0x1e, 0x8a, 0xe0, 0x6b, + 0x49, 0x8f, 0xb9, 0xb5, 0xb8, 0x36, 0xe6, 0x09, 0xe7, 0x30, 0x27, 0x8e, + 0x27, 0x7b, 0xb7, 0xdf, 0x54, 0xfb, 0x5a, 0x45, 0x59, 0xbb, 0xde, 0x79, + 0x93, 0xab, 0xd2, 0x74, 0xe3, 0x16, 0x18, 0xd0, 0x9d, 0xdd, 0x6c, 0x0c, + 0x45, 0x06, 0x31, 0xbd, 0xa8, 0xc1, 0x6a, 0xcd, 0x8f, 0x03, 0x47, 0xae, + 0x06, 0xed, 0x24, 0x35, 0x13, 0xb9, 0x9e, 0x06, 0x5d, 0xe1, 0x87, 0x22, + 0x62, 0x0c, 0xec, 0xf2, 0x02, 0x33, 0x16, 0xb3, 0xef, 0x0e, 0x41, 0x03, + 0xd5, 0x37, 0x47, 0x87, 0x45, 0x9d, 0x6d, 0xf6, 0x1c, 0x00, 0x1d, 0xf3, + 0x57, 0x83, 0x57, 0x61, 0x29, 0x04, 0xc6, 0xfb, 0x60, 0x77, 0x3a, 0x2e, + 0xf3, 0xbe, 0x0b, 0x6e, 0x35, 0xa8, 0x9e, 0xf3, 0x2a, 0xbc, 0x1a, 0x1c, + 0x10, 0xba, 0x99, 0x2d, 0x69, 0x06, 0xf0, 0x3d, 0x9d, 0x3b, 0xf7, 0xeb, + 0x00, 0xd7, 0x03, 0x5e, 0x97, 0xc7, 0xbd, 0xde, 0x8f, 0x00, 0xbf, 0x40, + 0xff, 0xdd, 0x14, 0x2d, 0x57, 0x79, 0xf4, 0x1e, 0x3c, 0xe0, 0x11, 0x66, + 0xc6, 0x29, 0x35, 0x4a, 0x80, 0x15, 0xf9, 0x79, 0x7a, 0x1a, 0x7b, 0xf8, + 0x39, 0x30, 0x16, 0x73, 0xf2, 0x6d, 0x14, 0x53, 0x75, 0x9c, 0x1f, 0xd6, + 0x83, 0x07, 0x37, 0x58, 0x0d, 0xbe, 0xde, 0xa6, 0xf3, 0xee, 0xaa, 0xd8, + 0x37, 0xf1, 0x37, 0x78, 0xf0, 0xd0, 0x0d, 0x5c, 0x70, 0x81, 0x2c, 0xed, + 0xf7, 0x4f, 0x00, 0x2c, 0xd7, 0xdd, 0xe7, 0x28, 0x7a, 0xf0, 0x60, 0x06, + 0xf0, 0xed, 0x90, 0x4e, 0xe2, 0x6e, 0x33, 0xb2, 0x9c, 0xf6, 0x24, 0xa5, + 0x87, 0xc7, 0x41, 0x98, 0xa1, 0xf7, 0x07, 0x01, 0x4e, 0xee, 0xe7, 0xbd, + 0x6f, 0x7a, 0xf6, 0xf0, 0x78, 0xe0, 0x7d, 0x43, 0x11, 0x3a, 0x5b, 0x67, + 0x54, 0x5a, 0xcc, 0x75, 0xb3, 0xf7, 0x98, 0x07, 0x0f, 0xe6, 0x08, 0x13, + 0xee, 0xe4, 0x20, 0xc2, 0xf4, 0x21, 0xe5, 0xac, 0xea, 0xc2, 0x83, 0x07, + 0x27, 0x38, 0xe6, 0x40, 0x1f, 0x06, 0xf3, 0x7a, 0x9d, 0xd6, 0xdd, 0x5b, + 0x09, 0xc4, 0x45, 0xaf, 0xc2, 0xcf, 0xf5, 0x6d, 0xaa, 0x1e, 0x7e, 0x6e, + 0x5c, 0xf2, 0x63, 0xb1, 0xd3, 0xf4, 0x8b, 0xe2, 0x41, 0x59, 0x1d, 0xe0, + 0x7b, 0x02, 0x17, 0x92, 0x4c, 0xc4, 0x2f, 0xf0, 0xcf, 0x3d, 0x35, 0x0f, + 0x3f, 0x35, 0x78, 0xdf, 0x67, 0x2e, 0x2a, 0xae, 0x06, 0x77, 0xee, 0x87, + 0xda, 0x31, 0xf1, 0xb9, 0xe7, 0xe4, 0xe1, 0xc7, 0xc6, 0xff, 0x00, 0x2f, + 0x51, 0xc1, 0x57, 0x1e, 0x94, 0x00, 0x00 +}; +unsigned int splash_bmp_gz_len = 3859; diff --git a/board/ti/logic/splash-480x272.h b/board/ti/logic/splash-480x272.h new file mode 100644 index 00000000000..0d249e7936e --- /dev/null +++ b/board/ti/logic/splash-480x272.h @@ -0,0 +1,408 @@ +/* + * (C) Copyright 2011 + * Logic Product Development <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.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 + */ + +/* + * Splash Screen Image + * + * To update this image, perform the following steps to convert your + * image to RGB565, compress with gzip, and format as C code. + * 1) Open your image in GIMP + * 2) Select File -> Save as... + * 3) Enter splash.bmp and select Save + * 4) On the "Save as BMP" dialog, open the "Advanced Options" section + * 5) Select "R5 G6 B5" under the "16 bits" section and select Save + * 6) At a prompt, run the following commands: + * gzip splash.bmp + * xxd -i splash.bmp.gz > splash.h + * 7) Replace the code below with the contents of splash.h + */ +unsigned char splash_bmp_gz[] = { + 0x1f, 0x8b, 0x08, 0x08, 0xbe, 0x32, 0x9b, 0x4e, 0x00, 0x03, 0x73, 0x70, + 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x62, 0x6d, 0x70, 0x00, 0xed, 0xdd, 0x4f, + 0x48, 0x1b, 0x6b, 0xbf, 0xc0, 0xf1, 0xbc, 0x5c, 0xe1, 0x0e, 0xdc, 0x2e, + 0xc2, 0x6d, 0xa1, 0x81, 0x16, 0xde, 0x04, 0x2c, 0x18, 0xb0, 0x60, 0xa0, + 0x0b, 0x03, 0x6e, 0x12, 0xc8, 0x0b, 0x06, 0x5c, 0x34, 0x1c, 0xe1, 0x28, + 0xed, 0xc2, 0x23, 0x2d, 0x14, 0xe9, 0x42, 0xc4, 0x85, 0x04, 0x37, 0x41, + 0x5c, 0x88, 0x74, 0x21, 0x41, 0xa8, 0xc4, 0xb3, 0x68, 0xd1, 0x82, 0x97, + 0xb8, 0x28, 0xc4, 0x85, 0x60, 0x36, 0x42, 0x5c, 0x14, 0x22, 0x1c, 0x21, + 0x07, 0x5a, 0x48, 0x2e, 0xb4, 0x90, 0x42, 0x2f, 0x64, 0xe1, 0x22, 0x17, + 0xbc, 0xd0, 0x9b, 0x31, 0x67, 0x9a, 0x99, 0xc9, 0xfc, 0x79, 0x9e, 0x49, + 0xd4, 0xbe, 0x2f, 0xdf, 0xcf, 0xe0, 0xe1, 0x80, 0xf9, 0xf3, 0x8c, 0x9d, + 0xdf, 0x3c, 0xff, 0x7e, 0xcf, 0x33, 0xf1, 0x89, 0x7f, 0xfc, 0xdf, 0xbf, + 0xf9, 0x54, 0xff, 0x68, 0xfd, 0x8c, 0xb6, 0x7e, 0xfe, 0xfb, 0x6f, 0x3e, + 0x9f, 0xbf, 0xf5, 0xf3, 0x37, 0x9f, 0xdf, 0x77, 0xf9, 0x9b, 0xd6, 0xef, + 0xff, 0xf3, 0x3f, 0x7c, 0x97, 0x3f, 0x3f, 0xfc, 0x6f, 0xeb, 0x75, 0xff, + 0xee, 0xf3, 0xfd, 0xdd, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7a, 0x66, + 0x94, 0xc1, 0x50, 0x3d, 0x3a, 0x9e, 0x6c, 0x1f, 0x43, 0xf1, 0xdd, 0xc8, + 0x40, 0x28, 0xa6, 0xdc, 0x74, 0xa9, 0x00, 0x38, 0x8b, 0x29, 0x27, 0xd1, + 0xd3, 0xf4, 0x64, 0xfe, 0x75, 0x79, 0xb9, 0x51, 0x6c, 0x6a, 0xc7, 0xfd, + 0xfa, 0xaf, 0xa5, 0xdb, 0xdb, 0x8d, 0xb4, 0x12, 0x3f, 0x0e, 0xdc, 0x74, + 0x09, 0x01, 0x58, 0x89, 0x29, 0xb7, 0x66, 0x9b, 0x47, 0xbf, 0x37, 0xbf, + 0xdb, 0x2a, 0x36, 0x0f, 0x2b, 0xb7, 0xb7, 0xc7, 0x93, 0xd4, 0xc5, 0xc0, + 0xcf, 0x65, 0x2d, 0x35, 0x5a, 0xb2, 0x8f, 0x5c, 0xa3, 0xa5, 0xf2, 0xfe, + 0xc2, 0x60, 0xe8, 0xa6, 0x4b, 0x0c, 0x40, 0x15, 0x53, 0xe6, 0xd7, 0x45, + 0x63, 0x57, 0x73, 0xaf, 0x36, 0xbf, 0x4e, 0x0c, 0x03, 0x3f, 0x03, 0x99, + 0xda, 0xb7, 0x63, 0xac, 0xde, 0x48, 0xd3, 0x23, 0x06, 0x6e, 0xde, 0x8c, + 0x32, 0x31, 0xf7, 0xa9, 0x2a, 0x1f, 0xc3, 0x5b, 0x95, 0x6f, 0xd3, 0x37, + 0x5d, 0x76, 0x00, 0x3e, 0xdf, 0x71, 0xe0, 0xf1, 0xfa, 0x72, 0x43, 0x3e, + 0x86, 0x27, 0xf3, 0x89, 0xf0, 0x4d, 0x97, 0xbd, 0x23, 0xa6, 0x7c, 0x09, + 0x9e, 0x44, 0xd7, 0x52, 0x1f, 0xa6, 0x27, 0xe6, 0x3e, 0x4c, 0xbf, 0x48, + 0x9e, 0x44, 0x3f, 0x07, 0x7a, 0x1f, 0x71, 0x0b, 0xb6, 0xfe, 0x3a, 0xbb, + 0x91, 0x17, 0xc9, 0x0f, 0xd3, 0x0f, 0xe7, 0x1e, 0xce, 0xed, 0xa5, 0xd6, + 0x52, 0x4a, 0x3c, 0x11, 0xce, 0xf8, 0x83, 0x7d, 0x28, 0xb1, 0xb5, 0x98, + 0x32, 0x10, 0x52, 0xe2, 0x6b, 0xa9, 0x6f, 0xad, 0xf3, 0xf8, 0x76, 0x79, + 0x1e, 0xb4, 0x75, 0xe0, 0xe6, 0x5d, 0x74, 0x32, 0x2f, 0x1f, 0xc1, 0xf7, + 0xeb, 0x1f, 0x7a, 0xa8, 0x85, 0xf7, 0x52, 0x23, 0x2b, 0x8d, 0xb4, 0xdb, + 0xe1, 0x6f, 0xbd, 0xe6, 0x81, 0xcb, 0x7d, 0xa2, 0x1e, 0xf5, 0xaf, 0x34, + 0x8f, 0x3e, 0x55, 0xb7, 0x75, 0x77, 0xa1, 0xed, 0xc6, 0xa7, 0x6a, 0xf3, + 0x68, 0x64, 0x45, 0x89, 0x07, 0x3d, 0x96, 0xef, 0x59, 0x78, 0x7f, 0xe1, + 0xa0, 0x70, 0x58, 0x59, 0x6e, 0xd4, 0x74, 0xe7, 0x5c, 0x6c, 0x8e, 0xd5, + 0x97, 0xca, 0x9b, 0x85, 0x46, 0x7a, 0x2d, 0xf5, 0xc6, 0x6f, 0xff, 0xee, + 0xc1, 0x50, 0xbb, 0xec, 0xea, 0x7f, 0x4f, 0xd3, 0x9f, 0x05, 0x62, 0x30, + 0xa6, 0x8c, 0x27, 0x1f, 0xaf, 0x8f, 0x96, 0x2e, 0xaa, 0x45, 0xdd, 0x6c, + 0xc0, 0x72, 0xe3, 0xb0, 0xb2, 0x98, 0x9f, 0x98, 0x1b, 0x10, 0x1c, 0x75, + 0x50, 0xe7, 0x13, 0x46, 0x56, 0x46, 0x56, 0xc6, 0x93, 0x62, 0xaf, 0xd7, + 0x1c, 0x07, 0xb4, 0xf2, 0x9a, 0x8f, 0x57, 0x0b, 0xc3, 0xb3, 0x6b, 0xa9, + 0x44, 0x98, 0xf9, 0x87, 0x9f, 0x9b, 0xb7, 0xde, 0xf0, 0x46, 0x36, 0xe3, + 0x70, 0x15, 0x3b, 0x59, 0x14, 0xbe, 0x63, 0xac, 0xa5, 0xec, 0x3e, 0x23, + 0xa4, 0xec, 0xa5, 0x9a, 0x47, 0x55, 0x87, 0xd9, 0xaf, 0xda, 0xf7, 0x40, + 0x69, 0x78, 0xf6, 0x37, 0xc9, 0x32, 0x7e, 0x8d, 0xfd, 0xb2, 0xb3, 0xed, + 0xda, 0x26, 0xb9, 0xa8, 0x9e, 0xe5, 0xc2, 0x36, 0xf3, 0x6a, 0x2f, 0x92, + 0x9d, 0xd7, 0x15, 0x9b, 0x27, 0x51, 0xe7, 0xef, 0xcb, 0xf8, 0x87, 0x67, + 0xa7, 0xca, 0x4e, 0xdf, 0x35, 0x56, 0x7f, 0xbc, 0x2e, 0x52, 0x13, 0x67, + 0xfc, 0x4b, 0x97, 0x9f, 0xf3, 0x3e, 0x2b, 0x77, 0xc6, 0x27, 0x51, 0xe7, + 0x73, 0x55, 0xef, 0x23, 0xe7, 0xb9, 0xbd, 0x14, 0xad, 0x81, 0x9f, 0x57, + 0xc6, 0xff, 0x6a, 0xe1, 0x42, 0xba, 0x37, 0x7c, 0x50, 0x10, 0xa9, 0x5d, + 0xba, 0xfd, 0xb2, 0x23, 0xfa, 0x0d, 0x76, 0x75, 0xc9, 0xbb, 0xe8, 0x66, + 0x41, 0xec, 0x13, 0x02, 0x25, 0xf1, 0xfa, 0xe8, 0x38, 0xf0, 0x3e, 0x5b, + 0x74, 0xb8, 0x23, 0x18, 0xd5, 0xbe, 0xdf, 0x2d, 0x7e, 0x9b, 0xee, 0x8e, + 0xe1, 0xf1, 0x64, 0xed, 0xc7, 0x6b, 0xb6, 0x1b, 0xef, 0x1c, 0xe3, 0x77, + 0x3c, 0x19, 0x10, 0xba, 0x73, 0x6e, 0x55, 0x5e, 0xb8, 0x9e, 0x45, 0xc6, + 0xdf, 0xbe, 0x0b, 0xcf, 0xaf, 0x8b, 0x9e, 0x6f, 0xdb, 0x6e, 0x44, 0xec, + 0x9c, 0x0f, 0x2b, 0x23, 0x2b, 0xa2, 0x6d, 0x01, 0x5c, 0xbf, 0x81, 0xd0, + 0x46, 0xd6, 0xbd, 0xe6, 0x31, 0x9a, 0x2a, 0x7b, 0xe9, 0x09, 0x8b, 0xc6, + 0x6f, 0xcd, 0x26, 0x7e, 0x87, 0x67, 0x9f, 0x4a, 0x94, 0xb3, 0xf6, 0xdd, + 0xbf, 0x22, 0xd2, 0x02, 0xac, 0x47, 0x0f, 0x2b, 0x72, 0x67, 0xaf, 0xea, + 0x6e, 0x21, 0xe8, 0xe3, 0x77, 0xd9, 0x21, 0x7e, 0x43, 0x4a, 0x23, 0xed, + 0xd4, 0x7e, 0x30, 0x2a, 0x36, 0x5f, 0x2d, 0x38, 0x97, 0xbf, 0xd7, 0xf8, + 0x5d, 0xcc, 0x6f, 0x64, 0xdf, 0xff, 0x38, 0x36, 0xb2, 0xe7, 0xb9, 0x83, + 0xc2, 0x68, 0xe9, 0x7e, 0x5d, 0x5f, 0x8a, 0x8b, 0xea, 0xad, 0x59, 0xb9, + 0x4f, 0xc7, 0x75, 0x3a, 0x89, 0x1e, 0x08, 0xd6, 0x6b, 0x9a, 0xc3, 0x8a, + 0x7c, 0x04, 0x8b, 0xd7, 0xbf, 0x56, 0xb5, 0xce, 0x69, 0x5a, 0xae, 0x84, + 0xaa, 0xb3, 0x5c, 0xc8, 0x25, 0x82, 0xd7, 0x52, 0x32, 0xf7, 0x84, 0x8e, + 0xee, 0x71, 0x00, 0xb1, 0xf8, 0x0d, 0x29, 0xef, 0xb3, 0xb2, 0xdf, 0xf5, + 0x72, 0xce, 0xe9, 0x0c, 0x7a, 0x8d, 0xdf, 0xdd, 0x48, 0xf7, 0xef, 0x62, + 0xca, 0x83, 0xf0, 0x50, 0xfc, 0xf1, 0xfa, 0x6b, 0x5d, 0x0b, 0x7f, 0x7e, + 0x7d, 0x86, 0xfe, 0xf0, 0x4f, 0x2b, 0xa4, 0x7c, 0x98, 0x5e, 0x72, 0xec, + 0x8f, 0x99, 0x6d, 0x55, 0x64, 0x5b, 0xd1, 0xfa, 0xf8, 0xad, 0x7d, 0xef, + 0xe4, 0x5d, 0x77, 0x1f, 0xdd, 0xf5, 0xef, 0xab, 0x85, 0xee, 0x12, 0x3c, + 0xa9, 0x57, 0x0a, 0x23, 0x2b, 0x0f, 0xe7, 0x86, 0x67, 0x87, 0x67, 0xf7, + 0x17, 0x1e, 0xed, 0x6c, 0x59, 0xd4, 0xa3, 0xe7, 0x39, 0xa7, 0x3a, 0x78, + 0x28, 0xde, 0x3d, 0x0e, 0x5f, 0x6c, 0x06, 0x4a, 0x1b, 0xd9, 0x89, 0xcb, + 0x4f, 0x1d, 0x9e, 0x3d, 0x4d, 0xdf, 0xd9, 0x09, 0x94, 0xba, 0x5f, 0xe5, + 0x2d, 0x7e, 0x83, 0xbe, 0x0d, 0x8b, 0xe8, 0xbd, 0xa8, 0x2e, 0xe6, 0x1b, + 0x69, 0xf5, 0xdb, 0x26, 0xe6, 0x36, 0xb2, 0x56, 0xbd, 0x62, 0x25, 0x6e, + 0x7f, 0x0e, 0xbd, 0xc6, 0x6f, 0xdd, 0xa1, 0xa5, 0xff, 0x9b, 0x5f, 0x1d, + 0x6d, 0xd0, 0x4a, 0x31, 0x99, 0x97, 0x1d, 0x55, 0x40, 0x6f, 0x32, 0xfe, + 0x97, 0x73, 0xe2, 0xa3, 0x4d, 0x6f, 0xfc, 0x8d, 0xf4, 0x93, 0x7a, 0xf7, + 0xd5, 0x63, 0xa7, 0x79, 0x24, 0x37, 0x92, 0xa5, 0x8f, 0xdf, 0xf3, 0xdc, + 0x49, 0xd4, 0xfa, 0xa8, 0xb7, 0x7e, 0xcc, 0x9f, 0x3b, 0x9e, 0x34, 0xf7, + 0xd5, 0xd4, 0x9c, 0x92, 0x67, 0xa6, 0x16, 0xc0, 0xdb, 0xc0, 0x9a, 0xee, + 0x6a, 0xd3, 0xf8, 0x57, 0xec, 0xca, 0x33, 0x18, 0x32, 0xf7, 0xfd, 0x8b, + 0xcd, 0xf7, 0xd9, 0x93, 0xa8, 0xb9, 0xce, 0x0e, 0x29, 0xab, 0x91, 0xfd, + 0x05, 0xe3, 0xdd, 0xc1, 0x5b, 0xfc, 0x3e, 0x9c, 0x33, 0x97, 0xee, 0x63, + 0xe5, 0xe5, 0x9c, 0x31, 0xbf, 0x2d, 0xe3, 0xff, 0x30, 0x6d, 0x6c, 0xd1, + 0x9f, 0xe7, 0x9c, 0xee, 0x94, 0xbd, 0xc6, 0xef, 0xd7, 0x98, 0xf3, 0xeb, + 0x62, 0xca, 0xab, 0x05, 0x2d, 0x5b, 0x5e, 0x76, 0x8c, 0x0c, 0xbd, 0x51, + 0xc7, 0x44, 0x03, 0x25, 0xb7, 0xb1, 0x50, 0xbd, 0x44, 0xf8, 0x2c, 0x27, + 0x3e, 0x96, 0x73, 0x96, 0x93, 0x29, 0x8d, 0x3e, 0x7e, 0x4f, 0xd3, 0xe2, + 0xef, 0x3b, 0x0e, 0x98, 0x7b, 0xa8, 0x8b, 0x79, 0xbb, 0x9c, 0x4e, 0xfd, + 0xd5, 0xa6, 0xc5, 0xa4, 0x75, 0x6f, 0x3a, 0xe8, 0x7b, 0x64, 0x6a, 0xcf, + 0x2f, 0x95, 0x87, 0x1c, 0xea, 0xb9, 0x07, 0x61, 0x7d, 0x2d, 0xec, 0x25, + 0x7e, 0x9f, 0x85, 0xcd, 0x6d, 0xf5, 0xf7, 0x59, 0xeb, 0xd1, 0xdd, 0xe3, + 0x80, 0x36, 0x56, 0x7f, 0xaf, 0xe6, 0x36, 0x63, 0x77, 0xd5, 0xf1, 0xab, + 0x52, 0xe2, 0x5a, 0xce, 0x0f, 0x59, 0x3c, 0xd7, 0xe9, 0x2c, 0xd7, 0xbe, + 0x9a, 0x86, 0xa5, 0x46, 0x1f, 0x94, 0x78, 0x45, 0xb0, 0x37, 0x5c, 0xb3, + 0xb8, 0x8e, 0xed, 0xe9, 0xe3, 0xd7, 0xbe, 0x56, 0xec, 0xe6, 0x5f, 0x31, + 0x7e, 0xeb, 0x46, 0xd6, 0xb9, 0x27, 0xf6, 0x22, 0x69, 0x8c, 0x93, 0xd1, + 0x92, 0x55, 0xbb, 0x6f, 0x2d, 0x55, 0xfb, 0x6e, 0x7c, 0xd5, 0x97, 0xa0, + 0xd3, 0xa7, 0x7e, 0x0e, 0x8c, 0xe9, 0xda, 0x26, 0x5e, 0xe2, 0xd7, 0x7c, + 0xbf, 0xd8, 0x77, 0x18, 0x99, 0x9a, 0x51, 0xd4, 0xf9, 0xf9, 0xcd, 0x82, + 0xdb, 0x4c, 0xf8, 0xf5, 0xc4, 0xaf, 0x3a, 0x42, 0xd2, 0x6e, 0x99, 0x5d, + 0x54, 0xbd, 0xcd, 0x3d, 0x40, 0xde, 0x1b, 0x7f, 0xa7, 0xcd, 0xd7, 0x90, + 0xa8, 0xef, 0xd4, 0xba, 0xe9, 0xd6, 0xac, 0x58, 0x6f, 0xf8, 0x5e, 0x4d, + 0x7c, 0x6e, 0xc1, 0x5b, 0xfc, 0x7e, 0x09, 0x1a, 0xc7, 0x42, 0x27, 0xf3, + 0x6e, 0x63, 0x52, 0x3e, 0xdf, 0x87, 0x69, 0x63, 0x1b, 0xa2, 0xbb, 0xd6, + 0x08, 0x29, 0xc6, 0x19, 0x9c, 0xad, 0x8a, 0xdb, 0x79, 0x7c, 0x09, 0x3e, + 0xe9, 0x29, 0x7e, 0xeb, 0x51, 0x63, 0x99, 0x1e, 0xbb, 0xc4, 0xdb, 0x1b, + 0xbf, 0xd5, 0x2c, 0x55, 0xb7, 0xeb, 0x89, 0x5f, 0x9f, 0xef, 0xdb, 0x74, + 0x4d, 0xf2, 0x5f, 0x0e, 0xbd, 0xd9, 0x8d, 0xe8, 0x5b, 0x7c, 0x32, 0x2d, + 0x56, 0xd5, 0xdb, 0xc0, 0xa9, 0x50, 0x6f, 0x58, 0xbc, 0x4f, 0xe4, 0x2d, + 0x7e, 0x5f, 0x1a, 0xfa, 0x8c, 0x17, 0x55, 0xb1, 0xfb, 0x85, 0x71, 0xa5, + 0xd5, 0xdd, 0xa2, 0xf9, 0xf7, 0x4a, 0x5c, 0x3f, 0x87, 0x53, 0x6c, 0x3a, + 0xb5, 0x9c, 0xdb, 0x7a, 0x8d, 0x5f, 0xe3, 0xb8, 0x73, 0xa0, 0xd4, 0xaf, + 0xec, 0xa6, 0xeb, 0x8a, 0x5f, 0x9f, 0xaf, 0x9d, 0xb1, 0xf7, 0xa9, 0xfa, + 0x96, 0x1a, 0xf8, 0x5a, 0xfc, 0xcf, 0x6f, 0xc6, 0x48, 0x73, 0x9e, 0x87, + 0xb0, 0xf2, 0xa0, 0xd5, 0x1b, 0x76, 0x9b, 0x1b, 0xde, 0x6e, 0xac, 0x5a, + 0xcc, 0x40, 0x58, 0xf1, 0x12, 0xbf, 0x41, 0x9f, 0xb1, 0x9e, 0x14, 0x3d, + 0x87, 0xe3, 0x80, 0x7e, 0x6c, 0xaa, 0xd8, 0x34, 0xcf, 0x92, 0x18, 0xe3, + 0x5b, 0xa4, 0x1f, 0xdf, 0x5b, 0xfc, 0x66, 0xfc, 0xfa, 0xf2, 0x54, 0x9b, + 0xee, 0x79, 0x19, 0xa2, 0xae, 0x2f, 0x7e, 0xbf, 0xc6, 0xda, 0xef, 0xe8, + 0x25, 0x87, 0x16, 0xe2, 0x1e, 0x9b, 0x56, 0xfb, 0xfe, 0xde, 0x74, 0x9a, + 0x87, 0xb0, 0xf3, 0x35, 0xe6, 0x96, 0xf3, 0x24, 0x7a, 0xe5, 0x78, 0x89, + 0xdf, 0x55, 0x43, 0x1b, 0xe2, 0x53, 0x55, 0x7c, 0xc4, 0xdb, 0xd8, 0x6b, + 0x36, 0xf6, 0x35, 0x7f, 0xf3, 0xeb, 0x7b, 0x07, 0xbf, 0xbb, 0x66, 0x3b, + 0xaa, 0x7a, 0x8b, 0x5f, 0xfd, 0x6f, 0xd5, 0xf6, 0x80, 0x7b, 0x1f, 0x40, + 0xd4, 0xf5, 0xc5, 0x6f, 0x48, 0x69, 0x8f, 0xee, 0x3f, 0xda, 0x09, 0x4a, + 0x96, 0x11, 0x5e, 0x74, 0xaf, 0x50, 0xf8, 0xb5, 0xe4, 0x6d, 0x0e, 0xfe, + 0xbf, 0x1c, 0xe7, 0x86, 0xef, 0xd7, 0xc5, 0x72, 0x64, 0xbd, 0xc4, 0xef, + 0x87, 0x69, 0xfd, 0x37, 0xb9, 0xf5, 0x19, 0xf5, 0x56, 0x23, 0xfa, 0x96, + 0xc3, 0x62, 0x3e, 0xa8, 0xfb, 0x5d, 0xc2, 0x30, 0x12, 0xdc, 0x3c, 0x12, + 0x69, 0xcb, 0x7e, 0x0e, 0xf4, 0x12, 0xbf, 0x0d, 0x43, 0xf6, 0xc9, 0x43, + 0xe9, 0x96, 0x90, 0xbd, 0xeb, 0x8b, 0x5f, 0xed, 0x9e, 0x28, 0x73, 0x17, + 0x85, 0x57, 0x33, 0x8a, 0xd5, 0xea, 0x04, 0xf9, 0x36, 0x74, 0xdb, 0x1b, + 0xff, 0xfe, 0xc2, 0x7d, 0xdb, 0xde, 0xf0, 0x9e, 0xed, 0x7a, 0x03, 0x3d, + 0x2f, 0xf1, 0x6b, 0xbc, 0xee, 0xed, 0xd7, 0x35, 0x74, 0xcb, 0xf8, 0xf5, + 0x99, 0x10, 0x5b, 0x15, 0xfd, 0x35, 0xb7, 0x96, 0xd2, 0x7f, 0xea, 0x88, + 0x43, 0x59, 0x82, 0x3f, 0xfe, 0x6f, 0x20, 0xd4, 0xcb, 0xf8, 0xf3, 0x1d, + 0xdd, 0xb9, 0x17, 0x9b, 0xa2, 0x3d, 0x0e, 0x11, 0xd7, 0x19, 0xbf, 0x43, + 0x71, 0xf5, 0x1d, 0xbf, 0xf7, 0xb5, 0xfc, 0xb0, 0x66, 0xec, 0x01, 0x6a, + 0xa6, 0xca, 0xde, 0x73, 0x68, 0xd4, 0x4c, 0x69, 0xeb, 0x75, 0xc3, 0xe7, + 0x42, 0xf3, 0xc0, 0x5e, 0xe2, 0xf7, 0x3c, 0xd7, 0x79, 0xcf, 0x93, 0xba, + 0x5c, 0xce, 0xa6, 0x7e, 0xbe, 0xe6, 0x5e, 0x4d, 0x3f, 0x3b, 0x74, 0x6b, + 0x56, 0x5f, 0x7a, 0xbb, 0xfe, 0x5c, 0x22, 0xbc, 0x59, 0x68, 0x1e, 0xb5, + 0x8f, 0x3f, 0x8f, 0x02, 0x25, 0xfd, 0x88, 0x97, 0x5c, 0xfc, 0xce, 0x28, + 0x77, 0x8b, 0x9d, 0xf7, 0x6e, 0x55, 0x9c, 0xd6, 0x21, 0xca, 0xd2, 0xe2, + 0x57, 0xa6, 0x6d, 0xa2, 0xf2, 0x12, 0xbf, 0x0f, 0xc2, 0xed, 0x36, 0x8d, + 0xd8, 0xfd, 0x1a, 0xbd, 0x18, 0x08, 0x59, 0xc5, 0x5a, 0x4d, 0xea, 0xdf, + 0xab, 0xdb, 0x49, 0xd4, 0x6a, 0x15, 0xe0, 0xa8, 0xd0, 0x68, 0xaa, 0x7c, + 0xfc, 0x06, 0x7d, 0xfa, 0xbe, 0xb7, 0xec, 0x75, 0xaf, 0xef, 0x01, 0x2f, + 0x37, 0xf4, 0x23, 0x58, 0xfb, 0xba, 0x6c, 0xcc, 0xa2, 0xed, 0xa8, 0xc0, + 0x49, 0xd4, 0x3e, 0x93, 0x45, 0x2e, 0x7e, 0xdf, 0x18, 0xfa, 0xdb, 0x07, + 0x85, 0xfe, 0xf5, 0x7e, 0x3b, 0xf1, 0x7b, 0x9e, 0x4b, 0x84, 0x9f, 0xfd, + 0x38, 0x56, 0x23, 0x03, 0xa1, 0x8c, 0xdf, 0xe9, 0x7b, 0xbc, 0xc4, 0xef, + 0xe7, 0xbf, 0xea, 0x84, 0x89, 0x3e, 0xb6, 0xff, 0x61, 0x2d, 0x11, 0xb6, + 0xbe, 0xf2, 0xe4, 0x66, 0x82, 0xad, 0xec, 0xa5, 0xcc, 0x39, 0xba, 0x63, + 0x75, 0x91, 0x3d, 0xee, 0x7a, 0x8d, 0xdf, 0xa5, 0xb2, 0x5c, 0xbf, 0x4b, + 0x9f, 0x33, 0x6d, 0x1c, 0xa3, 0x32, 0x46, 0xf6, 0x73, 0x9b, 0xd6, 0xe0, + 0xbb, 0xa8, 0xfd, 0x2e, 0x25, 0x72, 0xf1, 0x9b, 0x31, 0xc4, 0xef, 0x64, + 0x5e, 0xe6, 0x2c, 0xdc, 0x68, 0xf1, 0x5b, 0x6c, 0x3e, 0x6d, 0x74, 0x8e, + 0xe5, 0xc6, 0xfd, 0xfa, 0xa7, 0xea, 0x54, 0xf9, 0x2c, 0xb7, 0x67, 0xb3, + 0xeb, 0x80, 0x97, 0xf8, 0xd5, 0xce, 0x43, 0x76, 0x2e, 0x12, 0xf2, 0xec, + 0xe2, 0xb7, 0x1f, 0x57, 0x8f, 0x9a, 0x57, 0xad, 0x6f, 0x9d, 0x8b, 0x8d, + 0xe0, 0xfe, 0x9c, 0xf1, 0xfb, 0xb4, 0x61, 0xce, 0xa3, 0xd6, 0xfc, 0x73, + 0xc5, 0xaf, 0xbd, 0xa5, 0xb2, 0x55, 0xfe, 0x9d, 0x97, 0xf8, 0x7d, 0x43, + 0xfc, 0x5e, 0x1b, 0xbb, 0xf8, 0x0d, 0x94, 0xfa, 0xf3, 0xf9, 0x5f, 0x82, + 0x23, 0x2b, 0xda, 0x88, 0x8e, 0x7d, 0x1b, 0x54, 0xcf, 0x4b, 0xfc, 0xea, + 0x5b, 0xeb, 0xaf, 0xcb, 0x72, 0xed, 0x67, 0xfd, 0xd8, 0x97, 0xb1, 0xfd, + 0xac, 0xcf, 0x09, 0xd9, 0x6e, 0xd8, 0xad, 0xc0, 0x71, 0x6a, 0x3f, 0x77, + 0x67, 0x74, 0x39, 0xc7, 0xaf, 0xbe, 0xc5, 0xb2, 0x59, 0x08, 0xca, 0x9c, + 0x86, 0x0b, 0x2d, 0x7e, 0xb7, 0x1b, 0x17, 0xd5, 0x7b, 0x35, 0xed, 0xb8, + 0xa8, 0xde, 0xaf, 0xeb, 0xfb, 0xeb, 0x77, 0x76, 0xcc, 0xe3, 0x1e, 0x5e, + 0xe2, 0xf7, 0x4b, 0xf0, 0xde, 0xe5, 0x49, 0xca, 0xe5, 0xe3, 0xc2, 0x8b, + 0xab, 0x8e, 0x5f, 0xf5, 0xfa, 0xd6, 0xea, 0xe0, 0x62, 0x53, 0xe4, 0x2a, + 0xf0, 0x32, 0x7e, 0xa5, 0x5f, 0x6f, 0x37, 0x56, 0x97, 0xdb, 0x07, 0xe2, + 0xf6, 0x76, 0xe7, 0xbd, 0xc6, 0xf1, 0x2b, 0xe3, 0xac, 0x94, 0xdd, 0x68, + 0xcc, 0xf3, 0xc8, 0x54, 0xf9, 0xb0, 0x62, 0x7d, 0x74, 0xe7, 0x5f, 0x38, + 0xc5, 0x6f, 0x48, 0xd1, 0xaf, 0xaf, 0xee, 0x65, 0x0c, 0xb1, 0x9b, 0x16, + 0xbf, 0x67, 0xb9, 0x81, 0xd0, 0xa0, 0xee, 0x48, 0x84, 0xeb, 0xd1, 0x5b, + 0xb3, 0x8b, 0x79, 0x2d, 0x8a, 0xcd, 0x39, 0x2a, 0x5e, 0xe2, 0x37, 0x11, + 0x6e, 0xbf, 0x47, 0x66, 0x1e, 0x00, 0xde, 0xd8, 0xc5, 0xef, 0x41, 0xa1, + 0x1f, 0x9f, 0xfe, 0x2c, 0x7c, 0x67, 0xa7, 0x73, 0x7f, 0xbf, 0xaa, 0xfa, + 0xd7, 0x38, 0xd2, 0x64, 0xb7, 0x2f, 0x87, 0x35, 0xe3, 0xfc, 0xd9, 0x6b, + 0x43, 0xdb, 0xdb, 0x98, 0x3d, 0x69, 0x37, 0x22, 0x10, 0x6c, 0xc5, 0x86, + 0xdd, 0xd1, 0x3d, 0x32, 0xe4, 0x3c, 0x7f, 0x74, 0xa6, 0x1b, 0x47, 0x5f, + 0x6e, 0xf4, 0x73, 0x1f, 0x4f, 0xb7, 0xf1, 0xe7, 0x70, 0x52, 0x5b, 0xbd, + 0x65, 0x6c, 0x33, 0x78, 0x89, 0xdf, 0xf1, 0xcb, 0x3d, 0xbe, 0xb6, 0x6d, + 0x7b, 0x1c, 0xe8, 0x1f, 0xbb, 0xf8, 0x95, 0x9d, 0x67, 0xe8, 0xd6, 0xbd, + 0x4e, 0x58, 0x2c, 0x87, 0xd2, 0x4b, 0xfc, 0x1a, 0x57, 0x09, 0x39, 0xcd, + 0xd4, 0x9a, 0x3d, 0x37, 0xe4, 0x6f, 0x6c, 0x1a, 0xc6, 0x7c, 0x07, 0x42, + 0xfa, 0xb9, 0x6c, 0xb1, 0xfc, 0x0d, 0x37, 0xce, 0xf1, 0x6b, 0xcc, 0xe2, + 0xee, 0x67, 0xfb, 0xd3, 0x7d, 0xfe, 0x37, 0x11, 0x6e, 0x9f, 0xad, 0x31, + 0xe7, 0xda, 0x4b, 0xfc, 0xb6, 0x33, 0xfa, 0x0e, 0x2b, 0xac, 0xe3, 0xbf, + 0x7a, 0x89, 0xb0, 0xfe, 0xca, 0xef, 0xe8, 0xad, 0xed, 0xa3, 0xee, 0x56, + 0xda, 0xbd, 0x5f, 0xd4, 0xa7, 0xaa, 0x48, 0x06, 0x96, 0x97, 0xf8, 0xfd, + 0x12, 0xd4, 0x67, 0x4d, 0xc8, 0x5c, 0x39, 0xc6, 0xcc, 0x0f, 0xe3, 0x88, + 0x4b, 0x4c, 0xd1, 0xaf, 0xf3, 0x77, 0xdf, 0x2d, 0x52, 0x84, 0x73, 0xfc, + 0x1a, 0xfb, 0xd2, 0x7f, 0x1e, 0x5d, 0x6f, 0xfe, 0x64, 0xfb, 0xee, 0x51, + 0x35, 0x64, 0x81, 0xcb, 0xc7, 0x6f, 0x4c, 0x69, 0xf7, 0xe2, 0xe5, 0x56, + 0x7d, 0xc3, 0x1b, 0xe3, 0x7a, 0x55, 0xcd, 0x85, 0x50, 0xa4, 0xd9, 0x51, + 0xe2, 0x7f, 0x76, 0xed, 0x6f, 0xa1, 0x12, 0x6b, 0x93, 0x7b, 0x5b, 0x7f, + 0x64, 0xcc, 0xbe, 0x16, 0xcd, 0x1f, 0xfb, 0x1c, 0x30, 0xae, 0x17, 0x30, + 0xb7, 0xef, 0x8d, 0xbb, 0x69, 0xdd, 0xd9, 0x11, 0x2d, 0x8d, 0x3d, 0xe7, + 0xf8, 0x8d, 0x29, 0xfa, 0x11, 0x68, 0xb9, 0x9e, 0x80, 0x33, 0x91, 0xf8, + 0xd5, 0xf2, 0x45, 0xf5, 0x3d, 0x7d, 0xf9, 0xf8, 0xd5, 0xda, 0x42, 0xf4, + 0x7e, 0xaf, 0x43, 0xc6, 0x6f, 0xb5, 0xaf, 0xa2, 0xf7, 0xd5, 0x9b, 0x89, + 0xf0, 0x79, 0xce, 0x6e, 0xdf, 0x44, 0xb1, 0x4f, 0xf5, 0x16, 0xbf, 0x7b, + 0x86, 0x5c, 0xc7, 0xfb, 0x75, 0xf7, 0x15, 0xed, 0x2a, 0xe3, 0x6a, 0xbd, + 0xa9, 0xb2, 0xb9, 0xbe, 0x7b, 0x6e, 0x58, 0x17, 0x51, 0x6c, 0xf6, 0x7e, + 0x4d, 0xba, 0xad, 0x1f, 0x34, 0xb6, 0x07, 0x64, 0xe7, 0xc2, 0xec, 0x89, + 0xc4, 0xef, 0x1b, 0x7f, 0x7b, 0x27, 0x3a, 0xfd, 0x5e, 0x96, 0xb2, 0xf1, + 0x1b, 0xfb, 0x6b, 0xc5, 0xf4, 0x16, 0xad, 0xe7, 0x6b, 0x11, 0x52, 0xba, + 0x77, 0xd1, 0x30, 0x8e, 0xc2, 0x8a, 0xcb, 0xb4, 0x7a, 0xbc, 0x56, 0xb5, + 0x79, 0x5b, 0xcd, 0x72, 0x0f, 0xc3, 0x6e, 0xde, 0xe2, 0x37, 0xe3, 0x7f, + 0x6d, 0xc8, 0x16, 0x39, 0x28, 0xb8, 0xf7, 0x56, 0x27, 0xe6, 0x8c, 0x77, + 0x1a, 0xab, 0xfe, 0xa6, 0x71, 0x37, 0x8c, 0x8b, 0xaa, 0x5d, 0x16, 0x87, + 0x28, 0xb7, 0xf8, 0x4d, 0x84, 0x8d, 0x63, 0x06, 0xfd, 0x6a, 0x85, 0x8a, + 0xc4, 0xaf, 0x36, 0xff, 0xbc, 0xdf, 0x43, 0xfc, 0x6a, 0x2d, 0x16, 0xa7, + 0x5d, 0x43, 0xd0, 0x4f, 0xfa, 0x31, 0xcf, 0x36, 0x2f, 0xbb, 0xf8, 0xc6, + 0x5c, 0x77, 0xa6, 0xbc, 0x5b, 0x14, 0x1b, 0xff, 0xf1, 0xba, 0x7f, 0xce, + 0xf0, 0xac, 0xf1, 0xfb, 0x26, 0xf3, 0xce, 0xf3, 0xc0, 0x0f, 0x4d, 0xd1, + 0x6b, 0x5d, 0xd7, 0xed, 0x46, 0x8c, 0xfb, 0x64, 0x6d, 0x55, 0x9c, 0xc7, + 0xe0, 0x62, 0xca, 0xc8, 0x8a, 0x53, 0xde, 0x82, 0xfb, 0xfe, 0x39, 0xe6, + 0xa7, 0xb7, 0x3e, 0x5e, 0xb7, 0xff, 0xbb, 0xcd, 0x28, 0x77, 0x76, 0xc4, + 0xf6, 0xaf, 0x16, 0x89, 0xdf, 0x07, 0x7f, 0xdd, 0x3b, 0xf4, 0x23, 0xd0, + 0x72, 0xf1, 0xfb, 0x6d, 0xba, 0xfd, 0x37, 0xed, 0x5f, 0xbb, 0x01, 0x6e, + 0xcc, 0x3b, 0x26, 0xff, 0x91, 0x0d, 0x4a, 0x7f, 0xc6, 0xd7, 0x98, 0xfb, + 0xce, 0xd0, 0xa2, 0xa3, 0xa9, 0x5e, 0xe3, 0x37, 0xa6, 0x98, 0x7b, 0xdd, + 0xa3, 0x25, 0xbb, 0x9c, 0x8b, 0xb7, 0x81, 0xf9, 0x75, 0x73, 0x2b, 0xdf, + 0x6e, 0x7d, 0xc2, 0x88, 0x69, 0x5f, 0x2d, 0xa7, 0xbd, 0xe2, 0x76, 0x23, + 0x6a, 0x26, 0x49, 0xcd, 0xa1, 0xf6, 0x71, 0x8f, 0xdf, 0xb7, 0x16, 0xfb, + 0xf0, 0x59, 0xcf, 0xc4, 0x0c, 0x84, 0xda, 0xbd, 0xfe, 0xc5, 0xbc, 0xfb, + 0x8c, 0xb7, 0x48, 0xfc, 0x6a, 0xeb, 0x35, 0xf4, 0x7f, 0x35, 0x99, 0xf8, + 0xbd, 0x35, 0xdb, 0xbe, 0xd7, 0xd9, 0xed, 0x05, 0x88, 0xab, 0xf0, 0x22, + 0xa9, 0xbf, 0x92, 0x0f, 0x0a, 0xb2, 0x77, 0xce, 0xc1, 0x90, 0xc8, 0x73, + 0x45, 0x8c, 0x2b, 0xf3, 0x9c, 0x78, 0x8d, 0x5f, 0xb5, 0xed, 0x69, 0x6e, + 0xbd, 0x57, 0x9b, 0xb7, 0xb7, 0x95, 0xb8, 0xfe, 0x9b, 0x63, 0x4a, 0x22, + 0xbc, 0xbf, 0xd0, 0xfd, 0x6c, 0x54, 0xfb, 0x76, 0x6a, 0xc6, 0xdf, 0xfd, + 0x14, 0x93, 0x83, 0x82, 0xf9, 0x89, 0x3f, 0xc7, 0x81, 0x70, 0xf2, 0x5c, + 0xb7, 0x27, 0xa7, 0xdd, 0xf3, 0x10, 0x44, 0xf6, 0x8f, 0xed, 0xde, 0x07, + 0x77, 0xb9, 0x31, 0xbf, 0xbe, 0x1b, 0x99, 0x31, 0xcc, 0x6d, 0xe9, 0x73, + 0x53, 0x2f, 0xaa, 0x6e, 0x3d, 0x73, 0xf7, 0xf8, 0x3d, 0x0e, 0x7c, 0xac, + 0x74, 0xff, 0x4b, 0x89, 0xc6, 0xef, 0xe7, 0x40, 0x27, 0x87, 0xc6, 0x78, + 0xf7, 0x52, 0x9f, 0x00, 0x19, 0x52, 0x82, 0x3e, 0xf5, 0xbf, 0x19, 0xff, + 0x8c, 0x12, 0x53, 0xd8, 0xdb, 0xae, 0x9f, 0x06, 0x75, 0x2b, 0x56, 0x0f, + 0x0a, 0x72, 0xb9, 0x87, 0x19, 0xff, 0xfe, 0xc2, 0xbd, 0xce, 0x05, 0xe9, + 0x40, 0x7c, 0x47, 0x51, 0xef, 0xf1, 0xab, 0xde, 0x8b, 0xba, 0xf7, 0xf1, + 0xa9, 0x36, 0x97, 0xca, 0x93, 0x79, 0xb5, 0x55, 0xdb, 0x48, 0x9f, 0xe7, + 0x02, 0x25, 0xab, 0x1e, 0xfa, 0x41, 0xc1, 0x69, 0xb4, 0x25, 0x11, 0xb6, + 0x7a, 0x16, 0xf2, 0x61, 0x65, 0xb3, 0x30, 0xbf, 0x7e, 0x9a, 0x3e, 0x4d, + 0xcf, 0xaf, 0x6f, 0x16, 0xcc, 0x75, 0x66, 0xb1, 0x69, 0x7d, 0xc6, 0x62, + 0xcf, 0x5f, 0x98, 0xe8, 0xda, 0x01, 0x5a, 0x9d, 0x3d, 0x1f, 0x2d, 0xa9, + 0xad, 0xe5, 0xd3, 0xf4, 0xc8, 0xca, 0x66, 0xc1, 0x5c, 0xa2, 0x9a, 0xcb, + 0x6a, 0x1f, 0xb7, 0xfc, 0x8d, 0x44, 0x58, 0x5b, 0xbb, 0x68, 0x6c, 0xfd, + 0x8b, 0xec, 0xdf, 0x3e, 0x18, 0xda, 0x5f, 0xe8, 0x9c, 0xff, 0xe3, 0xf5, + 0xa0, 0xe1, 0xb7, 0xf5, 0xe8, 0x54, 0xf9, 0x59, 0xf8, 0x4b, 0x70, 0xa9, + 0xbc, 0x1a, 0xf9, 0x23, 0x3b, 0xbf, 0xae, 0xc4, 0x45, 0xc6, 0x26, 0x20, + 0xaa, 0x93, 0xb5, 0xf7, 0x47, 0x56, 0x66, 0xcc, 0x50, 0x7d, 0xc6, 0x9f, + 0xf3, 0xb3, 0xf1, 0x3a, 0x16, 0x05, 0x76, 0x83, 0xd4, 0xf4, 0x12, 0xbf, + 0xea, 0xec, 0x85, 0xfc, 0x33, 0x8b, 0xdd, 0xef, 0x5a, 0xbb, 0x91, 0xfe, + 0x3c, 0xfd, 0x48, 0xfc, 0xf9, 0x47, 0x13, 0x73, 0xe2, 0xfb, 0x6b, 0xab, + 0x0e, 0x2b, 0xce, 0xa3, 0x83, 0xf6, 0xf1, 0x9b, 0xf1, 0x9f, 0x44, 0xfd, + 0x3f, 0x72, 0xd4, 0x03, 0x25, 0x63, 0x3b, 0x49, 0x8b, 0x5f, 0xf3, 0xdc, + 0x77, 0xb0, 0xd5, 0xf7, 0x1e, 0x08, 0x9d, 0x44, 0x87, 0x67, 0x17, 0xf3, + 0x9d, 0x11, 0xb7, 0xe5, 0x46, 0xf7, 0xcc, 0xdd, 0x50, 0xfc, 0x69, 0x43, + 0x5d, 0xa7, 0xf8, 0xb4, 0x71, 0x6b, 0x76, 0xb9, 0x71, 0x51, 0x9d, 0x98, + 0x5b, 0x2a, 0x13, 0xbf, 0xfd, 0xa4, 0xf6, 0x80, 0xd5, 0xbf, 0x7c, 0x50, + 0xe2, 0x3d, 0x27, 0xc2, 0xcf, 0xf8, 0x53, 0x5b, 0x77, 0x22, 0xeb, 0x06, + 0x35, 0xbd, 0xc5, 0xaf, 0x3a, 0xfb, 0x2c, 0xf7, 0x84, 0x97, 0xf3, 0x9c, + 0x48, 0x9b, 0x43, 0xeb, 0x6b, 0x8a, 0xdb, 0x2c, 0x58, 0xe5, 0x3f, 0x8a, + 0xc6, 0xaf, 0x9a, 0x7d, 0x2d, 0xd6, 0xb2, 0x51, 0x8d, 0x96, 0xdc, 0xfe, + 0xc2, 0x5a, 0xfc, 0x7e, 0xac, 0x3c, 0xda, 0x99, 0xcc, 0x77, 0x8e, 0x3f, + 0x8f, 0x96, 0xca, 0x9d, 0x11, 0xba, 0xd1, 0x92, 0xb9, 0x27, 0xad, 0xc5, + 0x6f, 0xf3, 0xa8, 0xf3, 0xbe, 0x5f, 0x76, 0xd4, 0x1d, 0x0b, 0x7e, 0x35, + 0x3d, 0x91, 0x58, 0x7d, 0x8d, 0xd5, 0xfe, 0x9c, 0x4a, 0xfc, 0x7e, 0xfd, + 0x79, 0x44, 0x5d, 0xd3, 0x30, 0x99, 0xff, 0x58, 0xd9, 0xaa, 0x4c, 0xe6, + 0xfb, 0x93, 0xc9, 0x06, 0xcd, 0xb3, 0xf0, 0x62, 0xde, 0xf9, 0x49, 0x96, + 0x46, 0xea, 0x0e, 0x1b, 0xe2, 0xb5, 0x43, 0x55, 0x72, 0xd6, 0x54, 0xbf, + 0x1f, 0x97, 0xb7, 0x79, 0xe8, 0xe3, 0x80, 0xdd, 0x0e, 0x20, 0x66, 0x87, + 0x15, 0xf1, 0xb1, 0xf6, 0x90, 0xf2, 0x72, 0x4e, 0x34, 0xa2, 0x3e, 0xb5, + 0x6a, 0x19, 0xeb, 0x16, 0x87, 0xcc, 0xf3, 0x43, 0x13, 0x86, 0xec, 0x71, + 0xfb, 0xbf, 0xef, 0x1f, 0x59, 0xf7, 0x3b, 0x90, 0xfb, 0xfa, 0xc1, 0xe5, + 0xc6, 0x46, 0xb6, 0x7b, 0xcf, 0x57, 0xb1, 0xe7, 0x87, 0x16, 0x9b, 0xcd, + 0xa3, 0x5b, 0xb3, 0xd6, 0x51, 0xa9, 0xc4, 0xc7, 0xea, 0xcf, 0xc2, 0xed, + 0x7d, 0xc1, 0x5e, 0xce, 0x35, 0xd2, 0xc5, 0xe6, 0x14, 0xf5, 0xef, 0x8d, + 0x99, 0x51, 0x8c, 0x6b, 0x7a, 0xdd, 0xc9, 0xce, 0x04, 0xea, 0xe3, 0x57, + 0x26, 0x93, 0xd9, 0xe8, 0x5d, 0xf4, 0x2c, 0x67, 0xbf, 0x13, 0x97, 0x6a, + 0xa9, 0x7c, 0x9a, 0x96, 0xcd, 0x32, 0x1b, 0x0c, 0x8d, 0xac, 0x38, 0xb7, + 0xa4, 0x6b, 0x97, 0x9f, 0x6b, 0x3f, 0x42, 0xa3, 0x7f, 0x7e, 0xf7, 0x76, + 0xc3, 0x3d, 0x23, 0x73, 0x28, 0x3e, 0x99, 0xb7, 0xbf, 0x17, 0xd5, 0x5a, + 0x6d, 0x7f, 0xb1, 0xb1, 0x5e, 0xe3, 0xda, 0x44, 0xe3, 0x67, 0x8c, 0xd5, + 0x47, 0x4b, 0x23, 0x2b, 0xd6, 0xf3, 0x62, 0x27, 0xd1, 0x9a, 0xc3, 0xd9, + 0xaa, 0x4f, 0xef, 0x3e, 0x28, 0x34, 0xd2, 0xf5, 0xae, 0xa7, 0x41, 0x69, + 0xfc, 0x2b, 0x53, 0xe5, 0x62, 0x73, 0xb4, 0xf4, 0x3e, 0x3b, 0x56, 0x2f, + 0x36, 0xd5, 0x1d, 0x3f, 0xd4, 0xbb, 0x66, 0x3f, 0x77, 0x16, 0x81, 0xa8, + 0xa0, 0x6f, 0x2f, 0xe5, 0x76, 0x17, 0x37, 0x93, 0x5f, 0x07, 0xb1, 0x1b, + 0xd9, 0x4b, 0xad, 0x5d, 0x1e, 0x7b, 0xa9, 0xde, 0x56, 0xe0, 0x0c, 0x84, + 0xbe, 0x4d, 0x9f, 0xe5, 0x46, 0x5b, 0xed, 0xbc, 0xe5, 0x46, 0xb5, 0xd9, + 0x3e, 0x9e, 0xb6, 0xae, 0xb8, 0xe6, 0xd1, 0xc8, 0xca, 0x78, 0xd2, 0xeb, + 0x0c, 0x65, 0xc6, 0xff, 0x22, 0x39, 0xbf, 0x7e, 0xb7, 0xf8, 0xb1, 0xf2, + 0xf4, 0xc7, 0xa7, 0xaa, 0xfd, 0xba, 0xa9, 0xf2, 0x66, 0xeb, 0x4a, 0x1e, + 0x8a, 0x3b, 0xef, 0xdd, 0x79, 0x1c, 0x58, 0x4b, 0x69, 0xc7, 0x0b, 0xc1, + 0x32, 0x3c, 0x0b, 0x4f, 0xcc, 0xdd, 0xd9, 0xf9, 0xb5, 0x74, 0xaf, 0xb6, + 0xdd, 0xd0, 0xbe, 0xef, 0x53, 0x35, 0xd0, 0x8a, 0x39, 0x25, 0x1e, 0x14, + 0x2c, 0x75, 0x48, 0x51, 0xe2, 0x9d, 0x6f, 0xd6, 0x1f, 0x43, 0xf1, 0x07, + 0x61, 0xfb, 0xfa, 0x50, 0x3d, 0x5b, 0xeb, 0xf7, 0xa9, 0xef, 0x5c, 0x8d, + 0xb8, 0xd5, 0xfc, 0x4a, 0xfc, 0xdb, 0xf4, 0x5a, 0xea, 0xdb, 0xf4, 0x50, + 0x3c, 0x9c, 0x54, 0x5b, 0xd7, 0x21, 0x65, 0x3c, 0xe9, 0xbe, 0x0b, 0x3e, + 0xfa, 0xaf, 0x3d, 0xb7, 0x29, 0x67, 0xde, 0x34, 0x16, 0x79, 0x13, 0x62, + 0xca, 0x40, 0x68, 0x35, 0xf2, 0x35, 0x56, 0x8f, 0xaa, 0xc7, 0xf3, 0x48, + 0x2f, 0x99, 0xdd, 0x1d, 0x41, 0xdf, 0xe7, 0xc0, 0x6a, 0xa4, 0xfd, 0x99, + 0x5f, 0x63, 0xab, 0x91, 0xc1, 0xd0, 0x55, 0xe7, 0x0b, 0xce, 0x28, 0x83, + 0xa1, 0xdd, 0xcb, 0xf3, 0x50, 0xbf, 0x4f, 0x9d, 0x8d, 0xb9, 0xda, 0xef, + 0xc3, 0xbf, 0x8e, 0x81, 0xd0, 0xe3, 0x75, 0xd9, 0x71, 0xdd, 0x1a, 0xfb, + 0xa7, 0x00, 0x37, 0x2e, 0xa6, 0x4c, 0x48, 0xf6, 0x78, 0x55, 0xcb, 0x0d, + 0x2f, 0x59, 0x98, 0x00, 0xfa, 0x69, 0x3c, 0x29, 0xdb, 0xe3, 0x55, 0x4d, + 0x95, 0x9d, 0xe6, 0xfb, 0x01, 0x5c, 0xbd, 0xd5, 0xc8, 0xa3, 0x1d, 0xb9, + 0x2c, 0x82, 0xb6, 0xf7, 0x16, 0xf3, 0x10, 0x00, 0xae, 0xcf, 0xe7, 0xc0, + 0xc8, 0x8a, 0xdb, 0x33, 0x05, 0xad, 0x2c, 0x95, 0x59, 0xb7, 0x0d, 0xdc, + 0xa4, 0x99, 0x56, 0x8f, 0xd7, 0x4b, 0xbe, 0xe0, 0xd3, 0x46, 0x23, 0x4d, + 0xcd, 0x0b, 0xdc, 0xa4, 0x98, 0x22, 0x3f, 0x4f, 0xa4, 0xc6, 0xee, 0xfb, + 0xac, 0x4c, 0x96, 0x24, 0x80, 0xab, 0x10, 0xf4, 0x0d, 0xc5, 0xe5, 0xc6, + 0xac, 0xc6, 0xea, 0xf3, 0xeb, 0x3c, 0x6b, 0x0e, 0xf8, 0x59, 0xc4, 0x04, + 0x5b, 0xd0, 0x6a, 0x66, 0xdc, 0xab, 0x05, 0x6f, 0xbb, 0xed, 0x00, 0xb8, + 0x3a, 0xc7, 0x01, 0x75, 0x5f, 0x7e, 0xeb, 0xfd, 0xac, 0x6a, 0xdf, 0x9f, + 0xd4, 0xd5, 0xbc, 0xd7, 0xaf, 0x31, 0x32, 0xd1, 0x81, 0x9f, 0xd7, 0x97, + 0xe0, 0x78, 0xf2, 0x34, 0x7d, 0x7b, 0xfb, 0xa0, 0xd0, 0x3e, 0x16, 0xf3, + 0x1b, 0xd9, 0x97, 0x73, 0x4a, 0x7c, 0x20, 0x14, 0xbc, 0xe9, 0xa2, 0x01, + 0x70, 0x15, 0xf4, 0xbd, 0x0d, 0x24, 0xc2, 0xab, 0x91, 0xdd, 0xcb, 0xa3, + 0xfd, 0xc4, 0xd8, 0x9b, 0x2e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x57, 0xe9, 0xff, 0x01, 0x8a, 0xc0, 0x18, 0xe0, 0x46, 0xfc, + 0x03, 0x00 +}; +unsigned int splash_bmp_gz_len = 4382; diff --git a/boards.cfg b/boards.cfg index dfefc3fd6a6..f9b09e7426a 100644 --- a/boards.cfg +++ b/boards.cfg @@ -153,6 +153,7 @@ omap3_zoom1 arm armv7 zoom1 logicpd omap3_zoom2 arm armv7 zoom2 logicpd omap3 omap3_beagle arm armv7 beagle ti omap3 omap3_evm arm armv7 evm ti omap3 +omap3logic arm armv7 logic ti omap3 omap3_sdp3430 arm armv7 sdp3430 ti omap3 devkit8000 arm armv7 devkit8000 timll omap3 omap4_panda arm armv7 panda ti omap4 diff --git a/common/Makefile b/common/Makefile index 224b7cc7127..f3ad4bdd63b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -160,6 +160,7 @@ COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o +COBJS-$(CONFIG_YAFFS2_NEW) += cmd_yaffs2.o # others COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o diff --git a/common/cmd_bdinfo.c b/common/cmd_bdinfo.c index 75924f8aac6..a5a2930c40b 100644 --- a/common/cmd_bdinfo.c +++ b/common/cmd_bdinfo.c @@ -350,6 +350,11 @@ int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #endif print_num("relocaddr", gd->relocaddr); print_num("reloc off", gd->reloc_off); +#ifdef CONFIG_GDB_SECTION_STARTS + print_num("data_start", gd->data_start); + print_num("rodata_start", gd->rodata_start); + print_num("bss_start", gd->bss_start); +#endif print_num("irq_sp", gd->irq_sp); /* irq stack pointer */ print_num("sp start ", gd->start_addr_sp); print_num("FB base ", gd->fb_base); @@ -462,3 +467,18 @@ U_BOOT_CMD( "print Board Info structure", "" ); + +#ifdef CONFIG_GDB_SECTION_STARTS +int do_gdb_debug(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("add-symbol-file u-boot %#lx -s .data %#lx \\\n\t-s .rodata %#lx -s .bss %#lx\n", + gd->relocaddr, gd->data_start, gd->rodata_start, gd->bss_start); + return 0; +} + +U_BOOT_CMD( + gdb_debug, 1, 1, do_gdb_debug, + "print gdb 'add-symbol-file' command", + "" +); +#endif diff --git a/common/cmd_cache.c b/common/cmd_cache.c index 5cdd8341f28..5ece6572849 100644 --- a/common/cmd_cache.c +++ b/common/cmd_cache.c @@ -59,6 +59,15 @@ int do_dcache ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) break; case 1: dcache_enable (); break; + default: +#ifdef CONFIG_ARM + if (!strcmp(argv[1], "flush")) { + /* Flush all of memory */ + flush_cache(0, ~0); + return 0; + } +#endif + return cmd_usage(cmdtp); } /* FALL TROUGH */ case 1: /* get status */ @@ -93,6 +102,11 @@ U_BOOT_CMD( U_BOOT_CMD( dcache, 2, 1, do_dcache, "enable or disable data cache", +#ifdef CONFIG_ARM + "[on, off, flush]\n" + " - enable, disable, or flush data (writethrough) cache" +#else "[on, off]\n" " - enable or disable data (writethrough) cache" +#endif ); diff --git a/common/cmd_flash.c b/common/cmd_flash.c index bd49b796c64..1b954619e98 100644 --- a/common/cmd_flash.c +++ b/common/cmd_flash.c @@ -33,12 +33,7 @@ #if defined(CONFIG_CMD_MTDPARTS) #include <jffs2/jffs2.h> - -/* partition handling routines */ -int mtdparts_init(void); -int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); -int find_dev_and_part(const char *id, struct mtd_device **dev, - u8 *part_num, struct part_info **part); +#include "mtd_parts.h" #endif #ifndef CONFIG_SYS_NO_FLASH @@ -359,9 +354,9 @@ int do_flerase (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #if defined(CONFIG_CMD_MTDPARTS) /* erase <part-id> - erase partition */ - if ((argc == 2) && (mtd_id_parse(argv[1], NULL, &dev_type, &dev_num) == 0)) { + if ((argc == 2) && (mtd_id_parse(argv[1], NULL, &dev_type, &dev_num, 0) == 0)) { mtdparts_init(); - if (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0) { + if (find_dev_and_part(argv[1], &dev, &pnum, &part, 0) == 0) { if (dev->id->type == MTD_DEV_TYPE_NOR) { bank = dev->id->num; info = &flash_info[bank]; @@ -555,9 +550,9 @@ int do_protect (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #if defined(CONFIG_CMD_MTDPARTS) /* protect on/off <part-id> */ - if ((argc == 3) && (mtd_id_parse(argv[2], NULL, &dev_type, &dev_num) == 0)) { + if ((argc == 3) && (mtd_id_parse(argv[2], NULL, &dev_type, &dev_num, 0) == 0)) { mtdparts_init(); - if (find_dev_and_part(argv[2], &dev, &pnum, &part) == 0) { + if (find_dev_and_part(argv[2], &dev, &pnum, &part, 0) == 0) { if (dev->id->type == MTD_DEV_TYPE_NOR) { bank = dev->id->num; info = &flash_info[bank]; diff --git a/common/cmd_jffs2.c b/common/cmd_jffs2.c index 27296ddd7d6..ecb70908298 100644 --- a/common/cmd_jffs2.c +++ b/common/cmd_jffs2.c @@ -106,6 +106,8 @@ #include <onenand_uboot.h> #endif +#include "mtd_parts.h" + /* enable/disable debugging messages */ #define DEBUG_JFFS #undef DEBUG_JFFS @@ -205,6 +207,7 @@ static int mtd_device_validate(u8 type, u8 num, u32 *size) return 1; } +#if 0 /** * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>, * return device type and number. @@ -244,6 +247,7 @@ static int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *d *ret_id = p; return 0; } +#endif /* * 'Static' version of command line mtdparts_init() routine. Single partition on @@ -378,7 +382,7 @@ int mtdparts_init(void) dev_name = "nor0"; #endif - if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num) != 0) || + if ((mtd_id_parse(dev_name, NULL, &id->type, &id->num, 0) != 0) || (mtd_device_validate(id->type, id->num, &size) != 0)) { printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num); free(current_mtd_dev); diff --git a/common/cmd_mem.c b/common/cmd_mem.c index a5576aaab00..b6804dd87e7 100644 --- a/common/cmd_mem.c +++ b/common/cmd_mem.c @@ -1264,3 +1264,50 @@ U_BOOT_CMD( "[.b, .w, .l] address value delay(ms)" ); #endif /* CONFIG_MX_CYCLIC */ + +#ifdef CONFIG_CMD_HEXDUMP +#include <linux/ctype.h> + +int do_hd ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *src; + int i, j, len; + if (argc < 3) { + cmd_usage(cmdtp); + return 1; + } + + src = (char *)simple_strtoul(argv[1], NULL, 16); + len = simple_strtoul(argv[2], NULL, 16); + + while (len > 0) { + printf("%p ", src); + j = (len > 16) ? 16 : len; + for (i=0; i<16; ++i) { + if (i < j) + printf("%02x ", src[i]); + else + printf(" "); + if (i == 7) + printf(" "); + } + printf(" |"); + for (i=0; i<j; ++i) { + if (isascii(src[i]) && isprint(src[i])) + printf("%c", src[i]); + else + printf("."); + } + printf("|\n"); + src += j; + len -= j; + } + return 0; +} + +U_BOOT_CMD( + hd, 3, 1, do_hd, + "hexdump a memory region", + "srcaddr length" +); +#endif diff --git a/common/cmd_misc.c b/common/cmd_misc.c index 061b1bbad01..c4d266c3e80 100644 --- a/common/cmd_misc.c +++ b/common/cmd_misc.c @@ -53,3 +53,46 @@ U_BOOT_CMD( "N\n" " - delay execution for N seconds (N is _decimal_ !!!)" ); + +#ifdef CONFIG_CMD_TIME +int do_time (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong start; + ulong delta; + cmd_tbl_t *cmdtp2; + int ret; + unsigned int secs, msecs; + + if (argc < 2) { + cmd_usage(cmdtp); + return 1; + } + + cmdtp2 = find_cmd(argv[1]); + if (!cmdtp2) { + printf ("Unknown command '%s' - try help\n", argv[1]); + return 1; + } + + start = get_timer(0); + + /* Execute command */ + ret = (cmdtp2->cmd)(cmdtp2, flag, argc-1, argv+1); + + delta = get_timer(start); + + secs = (delta * 1000) / CONFIG_SYS_HZ; + msecs = secs % 1000; + secs /= 1000; + + printf("'%s' took %u.%03u seconds\n", argv[1], secs, msecs); + return ret; +} + +U_BOOT_CMD( + time , CONFIG_SYS_MAXARGS, 1, do_time, + "time execution of command", + "command to time\n" + " - time execution of command in seconds" +); +#endif diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 5481c885d3e..d0639f6cd21 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -106,6 +106,8 @@ #include <onenand_uboot.h> #endif +#include "mtd_parts.h" + /* special size referring to all the remaining space in a partition */ #define SIZE_REMAINING 0xFFFFFFFF @@ -1037,9 +1039,10 @@ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_ * @param ret_id output pointer to next char after parse completes (output) * @param dev_type parsed device type (output) * @param dev_num parsed device number (output) - * @return 0 on success, 1 otherwise + * @param quiet non-zero to suppress messages + * @return 0 on success, -ERRNO otherwise */ -int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) +int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num, int quiet) { const char *p = id; @@ -1054,13 +1057,15 @@ int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num) *dev_type = MTD_DEV_TYPE_ONENAND; p += 7; } else { - printf("incorrect device type in %s\n", id); - return 1; + if (!quiet) + printf("incorrect device type in %s\n", id); + return -ENODEV; } if (!isdigit(*p)) { - printf("incorrect device number in %s\n", id); - return 1; + if (!quiet) + printf("incorrect device number in %s\n", id); + return -ERANGE; } *dev_num = simple_strtoul(p, (char **)&p, 0); @@ -1340,14 +1345,16 @@ static void list_partitions(void) * @param dev pointer to the requested device (output) * @param part_num verified partition number (output) * @param part pointer to requested partition (output) + * @param quiet non-zero to suppress messages * @return 0 on success, 1 otherwise */ int find_dev_and_part(const char *id, struct mtd_device **dev, - u8 *part_num, struct part_info **part) + u8 *part_num, struct part_info **part, int quiet) { struct list_head *dentry, *pentry; u8 type, dnum, pnum; const char *p; + int ret; debug("--- find_dev_and_part ---\nid = %s\n", id); @@ -1367,26 +1374,31 @@ int find_dev_and_part(const char *id, struct mtd_device **dev, *part = NULL; *part_num = 0; - if (mtd_id_parse(p, &p, &type, &dnum) != 0) - return 1; + ret = mtd_id_parse(p, &p, &type, &dnum, quiet); + if (ret != 0) + return ret; if ((*p++ != ',') || (*p == '\0')) { - printf("no partition number specified\n"); + if (!quiet) + printf("no partition number specified\n"); return 1; } pnum = simple_strtoul(p, (char **)&p, 0); if (*p != '\0') { - printf("unexpected trailing character '%c'\n", *p); + if (!quiet) + printf("unexpected trailing character '%c'\n", *p); return 1; } if ((*dev = device_find(type, dnum)) == NULL) { - printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); + if (!quiet) + printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum); return 1; } if ((*part = mtd_part_info(*dev, pnum)) == NULL) { - printf("no such partition\n"); + if (!quiet) + printf("no such partition\n"); *dev = NULL; return 1; } @@ -1408,7 +1420,7 @@ static int delete_partition(const char *id) struct mtd_device *dev; struct part_info *part; - if (find_dev_and_part(id, &dev, &pnum, &part) == 0) { + if (find_dev_and_part(id, &dev, &pnum, &part, 0) == 0) { debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08x@0x%08x\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum, @@ -1615,7 +1627,7 @@ static int parse_mtdids(const char *const ids) ret = 1; /* parse 'nor'|'nand'|'onenand'<dev-num> */ - if (mtd_id_parse(p, &p, &type, &num) != 0) + if (mtd_id_parse(p, &p, &type, &num, 0) != 0) break; if (*p != '=') { @@ -1813,7 +1825,7 @@ int mtdparts_init(void) debug("--- getting current partition: %s\n", tmp_ep); - if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) { + if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p, 0) == 0) { current_mtd_dev = cdev; current_mtd_partnum = pnum; current_save(); @@ -1896,7 +1908,7 @@ int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 1; } - if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0) + if (find_dev_and_part(argv[1], &dev, &pnum, &part, 0) != 0) return 1; current_mtd_dev = dev; @@ -1963,7 +1975,7 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) struct mtdids *id; struct part_info *p; - if (mtd_id_parse(argv[2], NULL, &type, &num) != 0) + if (mtd_id_parse(argv[2], NULL, &type, &num, 0) != 0) return 1; if ((id = id_find(type, num)) == NULL) { @@ -2090,3 +2102,46 @@ U_BOOT_CMD( "<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)" ); /***************************************************/ + +int mtd_get_part_priv(const char *partname, int *idx, struct mtd_device **dev, loff_t *off, loff_t *size, void **cookie, void **priv, int quiet) +{ + int ret; + u8 pnum; + struct mtd_device *mtd_dev; + struct part_info *part; + + ret = mtdparts_init(); + if (ret) + return ret; + + ret = find_dev_and_part(partname, &mtd_dev, &pnum, &part, quiet); + if (ret) + return ret; + + if (mtd_dev->id->type != MTD_DEV_TYPE_NAND) { + if (!quiet) + puts("not a NAND device\n"); + return -1; + } + + *off = part->offset; + *dev = mtd_dev; + *size = part->size; + *idx = mtd_dev->id->num; + + *cookie = part; + *priv = part->jffs2_priv; + + ret = nand_set_dev(*idx); + if (ret) + return ret; + + return 0; +} + +void mtd_set_part_priv(void *cookie, void *priv) +{ + struct part_info *part = cookie; + + part->jffs2_priv = priv; +} diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 44c4d1f89ce..9b7a0a73e3a 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -29,12 +29,7 @@ #include <nand.h> #if defined(CONFIG_CMD_MTDPARTS) - -/* partition handling routines */ -int mtdparts_init(void); -int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); -int find_dev_and_part(const char *id, struct mtd_device **dev, - u8 *part_num, struct part_info **part); +#include "mtd_parts.h" #endif static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) @@ -98,7 +93,7 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) /* ------------------------------------------------------------------------- */ -static int set_dev(int dev) +int nand_set_dev(int dev) { if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { @@ -148,7 +143,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) if (ret) return ret; - ret = find_dev_and_part(partname, &dev, &pnum, &part); + ret = find_dev_and_part(partname, &dev, &pnum, &part, 0); if (ret) return ret; @@ -161,7 +156,7 @@ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size) *size = part->size; *idx = dev->id->num; - ret = set_dev(*idx); + ret = nand_set_dev(*idx); if (ret) return ret; @@ -283,7 +278,7 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) return 1; } - set_dev(0); + nand_set_dev(0); if (!strcmp(cmd, "get")) { ret = get_nand_env_oob(nand, &nand_env_oob_offset); @@ -423,7 +418,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) } dev = (int)simple_strtoul(argv[2], NULL, 10); - set_dev(dev); + nand_set_dev(dev); return 0; } @@ -637,6 +632,17 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) return 1; } +#ifdef CONFIG_MTD_DEBUG + if (strcmp(cmd, "debug") == 0) { + if (argc == 3) { + ulong val = simple_strtoul(argv[2], NULL, 16); + mtd_debug_verbose = val; + } else + printf("%d\n", mtd_debug_verbose); + return 1; + } +#endif + #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK if (strcmp(cmd, "lock") == 0) { int tight = 0; @@ -718,6 +724,10 @@ U_BOOT_CMD( "nand env.oob set off|partition - set enviromnent offset\n" "nand env.oob get - get environment offset" #endif +#ifdef CONFIG_MTD_DEBUG + "\n" + "nand debug [level] - display or set the MTD debug level" +#endif ); static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, @@ -827,7 +837,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) if (argc >= 2) { char *p = (argc == 2) ? argv[1] : argv[2]; if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && - (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { + (find_dev_and_part(p, &dev, &pnum, &part, 0) == 0)) { if (dev->id->type != MTD_DEV_TYPE_NAND) { puts("Not a NAND device\n"); return 1; diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index b2c88babc41..91728f7109e 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -381,6 +381,13 @@ int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (argc < 2) return cmd_usage(cmdtp); +#ifdef CONFIG_CHECK_SETENV + if (setenv_reserved_name(argv[1])) { + printf("## Error: can not set variable \"%s\"\n", argv[1]); + return 1; + } +#endif + return _do_env_set(flag, argc, argv); } @@ -584,6 +591,11 @@ static int do_env_default(cmd_tbl_t *cmdtp, int flag, int argc, char * const arg return cmd_usage(cmdtp); set_default_env("## Resetting to default environment\n"); +#if defined(CONFIG_TOUCHUP_ENV) + /* On a restored environment, need to initialise board + * specific variables */ + touchup_env(); +#endif return 0; } diff --git a/common/cmd_yaffs2.c b/common/cmd_yaffs2.c index 7c01ea2a251..9fd037afb2f 100644 --- a/common/cmd_yaffs2.c +++ b/common/cmd_yaffs2.c @@ -11,11 +11,10 @@ extern void cmd_yaffs_mount(char *mp); extern void cmd_yaffs_umount(char *mp); -extern void cmd_yaffs_read_file(char *fn); -extern void cmd_yaffs_write_file(char *fn,char bval,int sizeOfFile); extern void cmd_yaffs_ls(const char *mountpt, int longlist); +extern int cmd_yaffs_df(const char *path, loff_t *size); extern void cmd_yaffs_mwrite_file(char *fn, char *addr, int size); -extern void cmd_yaffs_mread_file(char *fn, char *addr); +extern void cmd_yaffs_mread_file(char *fn, char *addr, long *size); extern void cmd_yaffs_mkdir(const char *dir); extern void cmd_yaffs_rmdir(const char *dir); extern void cmd_yaffs_rm(const char *path); @@ -26,69 +25,81 @@ extern int yaffs_DumpDevStruct(const char *path); int do_ymount (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *mtpoint = argv[1]; - cmd_yaffs_mount(mtpoint); + if (argc < 2) + return cmd_usage(cmdtp); - return(0); -} - -int do_yumount (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - char *mtpoint = argv[1]; - cmd_yaffs_umount(mtpoint); + cmd_yaffs_mount(argv[1]); return(0); } -int do_yls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_yumount (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *dirname = argv[argc-1]; + if (argc < 2) + return cmd_usage(cmdtp); - cmd_yaffs_ls(dirname, (argc>2)?1:0); + cmd_yaffs_umount(argv[1]); return(0); } -int do_yrd (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_yls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *filename = argv[1]; - printf ("Reading file %s ", filename); - - cmd_yaffs_read_file(filename); + if (argc < 2) + return cmd_usage(cmdtp); + cmd_yaffs_ls(argv[argc-1], (argc>2)?1:0); - printf ("done\n"); return(0); } -int do_ywr (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_ydf (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *filename = argv[1]; - ulong value = simple_strtoul(argv[2], NULL, 16); - ulong numValues = simple_strtoul(argv[3], NULL, 16); + loff_t space; + int ret; - printf ("Writing value (%x) %x times to %s... ", value, numValues, filename); + if (argc < 2) + return cmd_usage(cmdtp); - cmd_yaffs_write_file(filename,value,numValues); + ret = cmd_yaffs_df(argv[1], &space); + if (!ret) + printf("Free Space: %lluKiB\n", space / 1024); - printf ("done\n"); - return(0); + return !!ret; } + int do_yrdm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *filename = argv[1]; - ulong addr = simple_strtoul(argv[2], NULL, 16); + long size; + char buf[12]; + char *filename; + ulong addr; + + if (argc < 3) + return cmd_usage(cmdtp); + + filename = argv[2]; + addr = simple_strtoul(argv[1], NULL, 16); - cmd_yaffs_mread_file(filename, (char *)addr); + cmd_yaffs_mread_file(filename, (char *)addr, &size); + sprintf(buf, "%lX", size); + setenv("filesize", buf); return(0); } int do_ywrm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *filename = argv[1]; - ulong addr = simple_strtoul(argv[2], NULL, 16); - ulong size = simple_strtoul(argv[3], NULL, 16); + char *filename; + ulong addr; + ulong size; + + if (argc < 4) + return cmd_usage(cmdtp); + + filename = argv[1]; + addr = simple_strtoul(argv[2], NULL, 16); + size = simple_strtoul(argv[3], NULL, 16); cmd_yaffs_mwrite_file(filename, (char *)addr, size); @@ -97,35 +108,44 @@ int do_ywrm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int do_ymkdir (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *dirname = argv[1]; - - cmd_yaffs_mkdir(dirname); + if (argc < 2) + return cmd_usage(cmdtp); + + cmd_yaffs_mkdir(argv[1]); return(0); } int do_yrmdir (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *dirname = argv[1]; + if (argc < 2) + return cmd_usage(cmdtp); - cmd_yaffs_rmdir(dirname); + cmd_yaffs_rmdir(argv[1]); return(0); } int do_yrm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *path = argv[1]; + if (argc < 2) + return cmd_usage(cmdtp); - cmd_yaffs_rm(path); + cmd_yaffs_rm(argv[1]); return(0); } int do_ymv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *oldPath = argv[1]; - char *newPath = argv[2]; + char *oldPath; + char *newPath; + + if (argc < 3) + return cmd_usage(cmdtp); + + oldPath = argv[1]; + newPath = argv[2]; cmd_yaffs_mv(newPath, oldPath); @@ -134,7 +154,12 @@ int do_ymv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int do_ydump (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char *dirname = argv[1]; + char *dirname; + + if (argc < 2) + return cmd_usage(cmdtp); + + dirname = argv[1]; if (yaffs_DumpDevStruct(dirname) != 0) printf("yaffs_DumpDevStruct returning error when dumping path: , %s\n", dirname); return 0; @@ -142,62 +167,56 @@ int do_ydump (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD( ymount, 3, 0, do_ymount, - "mount yaffs", - "" + "YAFFS mount", + "<partition>" ); U_BOOT_CMD( yumount, 3, 0, do_yumount, - "unmount yaffs", - "" + "YAFFS unmount", + "<partition>" ); U_BOOT_CMD( yls, 4, 0, do_yls, - "yaffs ls", - "[-l] name" -); - -U_BOOT_CMD( - yrd, 2, 0, do_yrd, - "read file from yaffs", - "filename" + "YAFFS ls", + "[-l] <name>" ); U_BOOT_CMD( - ywr, 4, 0, do_ywr, - "write file to yaffs", - "filename value num_vlues" + ydf, 2, 0, do_ydf, + "YAFFS disk free", + "[-l] <dirname>" ); U_BOOT_CMD( yrdm, 3, 0, do_yrdm, - "read file to memory from yaffs", - "filename offset" + "YAFFS read file to memory", + "<addr> <filename>" ); U_BOOT_CMD( ywrm, 4, 0, do_ywrm, - "write file from memory to yaffs", - "filename offset size" + "YAFFS write file from memory", + "<filename> <addr> <size>" ); U_BOOT_CMD( ymkdir, 2, 0, do_ymkdir, "YAFFS mkdir", - "dirname" + "<dirname>" ); U_BOOT_CMD( yrmdir, 2, 0, do_yrmdir, "YAFFS rmdir", - "dirname" + "<dirname>" ); U_BOOT_CMD( yrm, 2, 0, do_yrm, "YAFFS rm", - "path" + "<path>" ); U_BOOT_CMD( diff --git a/common/env_common.c b/common/env_common.c index c3e6388ac0b..04a947e92ba 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -196,6 +196,14 @@ void set_default_env(const char *s) gd->flags |= GD_FLG_ENV_READY; } +int env_check_valid(const char *buf) +{ + env_t *ep = (env_t *)buf; + uint32_t crc; + + memcpy(&crc, &ep->crc, sizeof(crc)); + return crc32(0, ep->data, ENV_SIZE) == crc; +} /* * Check if CRC is valid and (if yes) import the environment. * Note that "buf" may or may not be aligned. @@ -205,11 +213,7 @@ int env_import(const char *buf, int check) env_t *ep = (env_t *)buf; if (check) { - uint32_t crc; - - memcpy(&crc, &ep->crc, sizeof(crc)); - - if (crc32(0, ep->data, ENV_SIZE) != crc) { + if (!env_check_valid(buf)) { set_default_env("!bad CRC"); return 0; } @@ -244,6 +248,9 @@ void env_relocate (void) } else { env_relocate_spec (); } +#ifdef CONFIG_TOUCHUP_ENV + touchup_env(); +#endif } #ifdef CONFIG_AUTO_COMPLETE diff --git a/common/env_nand.c b/common/env_nand.c index 14446a6a579..facb4b3016f 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -153,30 +153,64 @@ int env_init(void) * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ -int writeenv(size_t offset, u_char *buf) +static int writeenv(size_t offset, u_char *buf) { size_t end = offset + CONFIG_ENV_RANGE; size_t amount_saved = 0; size_t blocksize, len; - + int ret, ncopies = 0; u_char *char_ptr; +#if defined(CONFIG_NAND_MULTIPLE_ECC) + int current_ecc_method; +#endif + +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_default(¤t_ecc_method); +#endif blocksize = nand_info[0].erasesize; len = min(blocksize, CONFIG_ENV_SIZE); - while (amount_saved < CONFIG_ENV_SIZE && offset < end) { + while (offset < end) { if (nand_block_isbad(&nand_info[0], offset)) { offset += blocksize; } else { char_ptr = &buf[amount_saved]; - if (nand_write(&nand_info[0], offset, &len, - char_ptr)) - return 1; - offset += blocksize; - amount_saved += len; + /* Data written can not straddle a block in case + one of the two blocks goes bad */ + if ((offset & ~(blocksize - 1)) != ((offset + len - 1) & ~(blocksize - 1))) + offset = (offset + blocksize - 1) & ~(blocksize-1); + + if (offset >= end) + break; + + ret = nand_write(&nand_info[0], offset, &len, + char_ptr); + + if (ret) { + /* Need to move offset forward by len, + * then to the start of the next block, + * reset amount_amount saved to start + * at the beginning of the environment */ + offset += len; + offset = (offset + blocksize - 1) & ~(blocksize - 1); + amount_saved = 0; + } else { + amount_saved += len; + if (amount_saved >= CONFIG_ENV_SIZE) { + ncopies++; + amount_saved = 0; + } + } + offset += len; } } - if (amount_saved != CONFIG_ENV_SIZE) + +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_method(current_ecc_method); +#endif + + if (!ncopies) return 1; return 0; @@ -211,8 +245,9 @@ int saveenv(void) if(gd->env_valid == 1) { puts("Erasing redundant NAND...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; - if (nand_erase_opts(&nand_info[0], &nand_erase_options)) + if (nand_erase_opts(&nand_info[0], &nand_erase_options)) { return 1; + } puts("Writing to redundant NAND... "); ret = writeenv(CONFIG_ENV_OFFSET_REDUND, @@ -220,8 +255,9 @@ int saveenv(void) } else { puts("Erasing NAND...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET; - if (nand_erase_opts(&nand_info[0], &nand_erase_options)) + if (nand_erase_opts(&nand_info[0], &nand_erase_options)) { return 1; + } puts("Writing to NAND... "); ret = writeenv(CONFIG_ENV_OFFSET, @@ -263,8 +299,9 @@ int saveenv(void) env_new.crc = crc32(0, env_new.data, ENV_SIZE); puts("Erasing Nand...\n"); - if (nand_erase_opts(&nand_info[0], &nand_erase_options)) + if (nand_erase_opts(&nand_info[0], &nand_erase_options)) { return 1; + } puts("Writing to Nand... "); if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) { @@ -273,40 +310,123 @@ int saveenv(void) } puts("done\n"); + return ret; } #endif /* CONFIG_ENV_OFFSET_REDUND */ #endif /* CMD_SAVEENV */ -int readenv(size_t offset, u_char * buf) +#ifdef CONFIG_ENV_OFFSET_REDUND +static int readenv(size_t offset, u_char * buf) { size_t end = offset + CONFIG_ENV_RANGE; size_t amount_loaded = 0; size_t blocksize, len; - u_char *char_ptr; +#if defined(CONFIG_NAND_MULTIPLE_ECC) + int current_ecc_method; +#endif blocksize = nand_info[0].erasesize; if (!blocksize) return 1; len = min(blocksize, CONFIG_ENV_SIZE); +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_default(¤t_ecc_method); +#endif + while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { if (nand_block_isbad(&nand_info[0], offset)) { offset += blocksize; } else { char_ptr = &buf[amount_loaded]; - if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr)) + if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr)) { +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_method(current_ecc_method); +#endif + return 1; + } offset += blocksize; amount_loaded += len; } } +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_method(current_ecc_method); +#endif if (amount_loaded != CONFIG_ENV_SIZE) return 1; return 0; } +#else /* ! CONFIG_ENV_OFFSET_REDUND */ +/* Read the environment, checking along the way. return 0 if valid + * environment or no environment is found, negative on error */ +static int readenv(size_t offset, u_char * buf) +{ + size_t end = offset + CONFIG_ENV_RANGE; + size_t amount_loaded = 0; + size_t blocksize, len; + u_char *char_ptr; + int ret; +#if defined(CONFIG_NAND_MULTIPLE_ECC) + int current_ecc_method; +#endif + + blocksize = nand_info[0].erasesize; + if (!blocksize) + return -1; + len = min(blocksize, CONFIG_ENV_SIZE); + +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_default(¤t_ecc_method); +#endif + + while (offset < end) { + if (nand_block_isbad(&nand_info[0], offset)) { + offset += blocksize; + } else { + char_ptr = &buf[amount_loaded]; + /* Data read can not straddle a block in case + one of the two blocks goes bad */ + if ((offset & ~(blocksize - 1)) != ((offset + len - 1) & ~(blocksize - 1))) + offset = (offset + blocksize - 1) & ~(blocksize-1); + if (offset >= end) + break; + + ret = nand_read(&nand_info[0], offset, &len, char_ptr); + + if (ret) { + /* Need to move offset forward by len, + * then to the start of the next block, + * reset amount_amount saved to start + * at the beginning of the environment */ + offset += len; + offset = (offset + blocksize - 1) & ~(blocksize - 1); + amount_loaded = 0; + } else { + amount_loaded += len; + if (amount_loaded >= CONFIG_ENV_SIZE) { + if (env_check_valid(buf)) + goto found; + amount_loaded = 0; + } + } + offset += len; + } + } + +found: +#if defined(CONFIG_NAND_MULTIPLE_ECC) + nand_switch_ecc_method(current_ecc_method); +#endif + if (amount_loaded != CONFIG_ENV_SIZE) + return 0; + + return 1; +} +#endif #ifdef CONFIG_ENV_OFFSET_OOB int get_nand_env_oob(nand_info_t *nand, unsigned long *result) @@ -346,6 +466,7 @@ void env_relocate_spec(void) #if !defined(ENV_IS_EMBEDDED) int crc1_ok = 0, crc2_ok = 0; env_t *ep, *tmp_env1, *tmp_env2; + int ret; tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE); @@ -358,11 +479,11 @@ void env_relocate_spec(void) return; } - if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1)) - puts("No Valid Environment Area found\n"); + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1); + printf("%sValid Environment Area found\n", ret ? "No ":""); - if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2)) - puts("No Valid Redundant Environment Area found\n"); + ret = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2); + printf("%sValid Redundant Environment Area found\n", ret ? "No ":""); crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); @@ -433,7 +554,7 @@ void env_relocate_spec (void) #endif ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); - if (ret) { + if (ret < 0) { set_default_env("!readenv() failed"); return; } diff --git a/common/lcd.c b/common/lcd.c index 0555ab4fb72..85a946828e5 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -35,12 +35,16 @@ #include <stdarg.h> #include <linux/types.h> #include <stdio_dev.h> +#include <div64.h> +#include <malloc.h> #if defined(CONFIG_POST) #include <post.h> #endif #include <lcd.h> #include <watchdog.h> +int console_color_black, console_color_white; + #if defined CONFIG_PXA250 || defined CONFIG_PXA27X || defined CONFIG_CPU_MONAHANS #include <asm/byteorder.h> #endif @@ -63,20 +67,22 @@ /************************************************************************/ #ifdef CONFIG_LCD_LOGO # include <bmp_logo.h> /* Get logo data, width and height */ -# if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16) -# error Default Color Map overlaps with Logo Color Map -# endif #endif DECLARE_GLOBAL_DATA_PTR; ulong lcd_setmem (ulong addr); -static void lcd_drawchars (ushort x, ushort y, uchar *str, int count); +static void lcd_drawchars_16 (ushort x, ushort y, uchar *str, int count); +static void lcd_drawchars_24 (ushort x, ushort y, uchar *str, int count); +static void lcd_drawchars_x (ushort x, ushort y, uchar *str, int count); + +static void (*lcd_drawchars) (ushort x, ushort y, uchar *str, int count); static inline void lcd_puts_xy (ushort x, ushort y, uchar *s); static inline void lcd_putc_xy (ushort x, ushort y, uchar c); static int lcd_init (void *lcdbase); +static int lcd_init_colors (void); static int lcd_clear (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]); static void *lcd_logo (void); @@ -139,12 +145,9 @@ static inline void console_newline (void) /*----------------------------------------------------------------------*/ -void lcd_putc (const char c) +void _lcd_putc (const char c) { - if (!lcd_is_enabled) { - serial_putc(c); - return; - } + int i; switch (c) { case '\r': console_col = 0; @@ -165,6 +168,13 @@ void lcd_putc (const char c) case '\b': console_back(); return; + case '\f': /* Clear to EOL */ + for (i=console_col; i < CONSOLE_COLS; ++i) + lcd_putc_xy (i * VIDEO_FONT_WIDTH, + console_row * VIDEO_FONT_HEIGHT, + ' '); + return; + default: lcd_putc_xy (console_col * VIDEO_FONT_WIDTH, console_row * VIDEO_FONT_HEIGHT, c); @@ -176,6 +186,15 @@ void lcd_putc (const char c) /* NOTREACHED */ } +void lcd_putc (const char c) +{ + if (!lcd_is_enabled) { + serial_putc(c); + return; + } + _lcd_putc(c); +} + /*----------------------------------------------------------------------*/ void lcd_puts (const char *s) @@ -204,11 +223,152 @@ void lcd_printf(const char *fmt, ...) lcd_puts(buf); } +#define NUM_ANCHORS 5 +struct anchors { + int row, col; +} anchors[NUM_ANCHORS]; + +void output_lcd_string(char *p) +{ + char c; + int row,col; + int tmp; + char *s, *r; + int idx; + + while ((c = *p++) != '\0') { + if (c == '/' && *p) { + switch(*p) { + case 'g': + /* Set cursor to anchor idx+'A' */ + if (p[1]) { + idx = p[1]-'A'; + if (idx >= 0 && idx < NUM_ANCHORS) { + console_row = anchors[idx].row; + console_col = anchors[idx].col; + } + p++; + } else { + _lcd_putc('/'); + _lcd_putc(*p); + } + break; + case 'a': + /* Set anchor idx+'A' to cursor */ + if (p[1]) { + idx = p[1]-'A'; + if (idx >= 0 && idx < NUM_ANCHORS) { + anchors[idx].row = console_row; + anchors[idx].col = console_col; + } + p++; + } else { + _lcd_putc('/'); + _lcd_putc(*p); + } + break; + case 'A': + /* Goto the lcd_anchor in environment */ + s = getenv("lcd_anchor"); + if (s) { + row = simple_strtoul(s, &r, 0); + if (r && *r==',') { + col = simple_strtoul(r+1, &r, 0); + if (r && !*r) { + if (row >= CONSOLE_ROWS) + row = CONSOLE_ROWS - 1; + if (row >= CONSOLE_COLS) + row = CONSOLE_COLS - 1; + console_row = row; + console_col = col; + } + } + } + break; + case 'p': + /* Cursor position, pos+'A' */ + if (p[1] && p[2]) { + console_row = p[1]-'A'; + console_col = p[2]-'A'; + p+=2; + } else { + _lcd_putc('/'); + _lcd_putc(*p); + } + break; + case 'i': + /* Invert video */ + tmp = lcd_color_fg; + lcd_color_fg = lcd_color_bg; + lcd_color_bg = tmp; + break; + case 'b': + /* Back up the display */ + _lcd_putc('\b'); + break; + case 'r': + /* Carriage return */ + _lcd_putc('\r'); + break; + case 'n': + /* Line feed */ + _lcd_putc('\n'); + break; + case 'k': + /* Clear to end of line */ + _lcd_putc('\f'); + break; + default: + _lcd_putc('/'); + _lcd_putc(*p); + break; + } + p++; + } else { + _lcd_putc(c); + } + } + +#ifdef CONFIG_ARM + /* Flush the cache to make sure display tracks content of memory */ + flush_cache(0, ~0); +#endif +} + +int do_echo_lcd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int i; + + for (i = 1; i < argc; i++) { + if (i > 1) + _lcd_putc(' '); + output_lcd_string(argv[i]); + } + + return 0; +} + +U_BOOT_CMD( + echo_lcd, CONFIG_SYS_MAXARGS, 1, do_echo_lcd, + "echo args to LCD", + "[args..]\n" + " - echo args to LCD, following escape sequences:\n" +" /pRC - goto row 'R' ('A'+row), column 'C' ('A'+col)\n" +" /aN - save the current position as anchor 'N' ('A'+n)\n" +" /gN - set cursor position to anchor point 'N' ('A'+n)\n" +" /i - invert video colors\n" +" /b - backspace\n" +" /r - carriage return\n" +" /n - carriage return + linefeed\n" +" /k - clear to end of line\n" +" /A - goto lcd_anchor value in environment (R,C in decimal)\n" +); + /************************************************************************/ /* ** Low-Level Graphics Routines */ /************************************************************************/ -static void lcd_drawchars (ushort x, ushort y, uchar *str, int count) +static void lcd_drawchars_unknown (ushort x, ushort y, uchar *str, int count) { uchar *dest; ushort off, row; @@ -261,6 +421,64 @@ static void lcd_drawchars (ushort x, ushort y, uchar *str, int count) } } +static void lcd_drawchars_16 (ushort x, ushort y, uchar *str, int count) +{ + uchar *dest; + ushort off, row; + + dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_COLOR16) / 8); + off = x * (1 << LCD_COLOR16) % 8; + + for (row=0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) { + uchar *s = str; + int i; + ushort *d = (ushort *)dest; + + for (i=0; i<count; ++i) { + uchar c, bits; + + c = *s++; + bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; + + for (c=0; c<8; ++c) { + *d++ = (bits & 0x80) ? + lcd_color_fg : lcd_color_bg; + bits <<= 1; + } + } + } +} + +static void lcd_drawchars_24 (ushort x, ushort y, uchar *str, int count) +{ + uchar *dest; + ushort off, row; + + dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_COLOR24) / 8); + off = x * (1 << LCD_COLOR24) % 8; + + for (row=0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) { + uchar *s = str; + int i; + uint *d = (uint *)dest; + + for (i=0; i<count; ++i) { + uchar c, bits; + + c = *s++; + bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row]; + + for (c=0; c<8; ++c) { + *d++ = (bits & 0x80) ? + lcd_color_fg : lcd_color_bg; + bits <<= 1; + } + } + } +} + + + /*----------------------------------------------------------------------*/ static inline void lcd_puts_xy (ushort x, ushort y, uchar *s) @@ -331,10 +549,33 @@ int drv_lcd_init (void) lcd_base = (void *)(gd->fb_base); + debug("%s: lcd_base %p\n", __FUNCTION__, lcd_base); + lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + debug("%s: vl_col %u vl_bpix %u lcd_line_length %u\n", __FUNCTION__, + panel_info.vl_col, panel_info.vl_bpix, lcd_line_length); + lcd_init (lcd_base); /* LCD initialization */ + /* lcd_init may setup panel_info structure */ + lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; + + debug("%s: vl_col %u vl_bpix %u lcd_line_length %u\n", __FUNCTION__, + panel_info.vl_col, panel_info.vl_bpix, lcd_line_length); + + switch(panel_info.vl_bpix) { + case LCD_COLOR16: + lcd_drawchars = lcd_drawchars_16; + break; + case LCD_COLOR24: + lcd_drawchars = lcd_drawchars_24; + break; + default: + lcd_drawchars = lcd_drawchars_unknown; + break; + } + /* Device initialization */ memset (&lcddev, 0, sizeof (lcddev)); @@ -352,35 +593,21 @@ int drv_lcd_init (void) /*----------------------------------------------------------------------*/ static int lcd_clear (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { -#if LCD_BPP == LCD_MONOCHROME - /* Setting the palette */ - lcd_initcolregs(); + lcd_init_colors(); -#elif LCD_BPP == LCD_COLOR8 - /* Setting the palette */ - lcd_setcolreg (CONSOLE_COLOR_BLACK, 0, 0, 0); - lcd_setcolreg (CONSOLE_COLOR_RED, 0xFF, 0, 0); - lcd_setcolreg (CONSOLE_COLOR_GREEN, 0, 0xFF, 0); - lcd_setcolreg (CONSOLE_COLOR_YELLOW, 0xFF, 0xFF, 0); - lcd_setcolreg (CONSOLE_COLOR_BLUE, 0, 0, 0xFF); - lcd_setcolreg (CONSOLE_COLOR_MAGENTA, 0xFF, 0, 0xFF); - lcd_setcolreg (CONSOLE_COLOR_CYAN, 0, 0xFF, 0xFF); - lcd_setcolreg (CONSOLE_COLOR_GREY, 0xAA, 0xAA, 0xAA); - lcd_setcolreg (CONSOLE_COLOR_WHITE, 0xFF, 0xFF, 0xFF); -#endif - -#ifndef CONFIG_SYS_WHITE_ON_BLACK - lcd_setfgcolor (CONSOLE_COLOR_BLACK); - lcd_setbgcolor (CONSOLE_COLOR_WHITE); +#ifdef CONFIG_SYS_WHITE_ON_BLACK + lcd_setfgcolor (console_color_white); + lcd_setbgcolor (console_color_black); #else - lcd_setfgcolor (CONSOLE_COLOR_WHITE); - lcd_setbgcolor (CONSOLE_COLOR_BLACK); + lcd_setfgcolor (console_color_black); + lcd_setbgcolor (console_color_white); #endif /* CONFIG_SYS_WHITE_ON_BLACK */ #ifdef LCD_TEST_PATTERN test_pattern(); #else - /* set framebuffer to background color */ + /* set framebuffer to background color (only works if depth is 8 + * or background is black) */ memset ((char *)lcd_base, COLOR_MASK(lcd_getbgcolor()), lcd_line_length*panel_info.vl_row); @@ -401,6 +628,45 @@ U_BOOT_CMD( "" ); +static int lcd_init_colors(void) +{ + if (panel_info.vl_bpix == LCD_MONOCHROME) { + /* Setting the palette */ + lcd_initcolregs(); + console_color_black = 0; + console_color_white = 1; + } else if (panel_info.vl_bpix == LCD_COLOR8) { + /* Setting the palette */ + lcd_setcolreg (0, 0, 0, 0); /* black */ + lcd_setcolreg (1, 0xFF, 0, 0); /* red */ + lcd_setcolreg (2, 0, 0xFF, 0); /* green */ + lcd_setcolreg (3, 0xFF, 0xFF, 0); /* yellow */ + lcd_setcolreg (4, 0, 0, 0xFF); /* blue */ + lcd_setcolreg (5, 0xFF, 0, 0xFF); /* magenta */ + lcd_setcolreg (6, 0, 0xFF, 0xFF); /* cyan */ + lcd_setcolreg (14, 0xAA, 0xAA, 0xAA); /* grey */ + lcd_setcolreg (15, 0xFF, 0xFF, 0xFF); /* white */ + console_color_black = 0; + console_color_white = 15; + } else if (panel_info.vl_bpix == LCD_COLOR16) { + console_color_white = 0xffff; + console_color_black = 0x0000; + } else if (panel_info.vl_bpix == LCD_COLOR24) { + console_color_white = 0x00ffffff; + console_color_black = 0x00000000; + } + +#ifdef CONFIG_LCD_LOGO + if (panel_info.vl_bpix != LCD_COLOR16) { + if (console_color_white >= BMP_LOGO_OFFSET) { + printf("Default Color Map overlaps with Logo Color Map!\n"); + return -1; + } + } +#endif + return 0; +} + /*----------------------------------------------------------------------*/ static int lcd_init (void *lcdbase) @@ -409,6 +675,15 @@ static int lcd_init (void *lcdbase) debug ("[LCD] Initializing LCD frambuffer at %p\n", lcdbase); lcd_ctrl_init (lcdbase); + + + /* If no panel setup then return an error */ + if (!panel_info.vl_row || !panel_info.vl_col) + return -1; + + if (lcd_init_colors() < 0) + return -1; + lcd_is_enabled = 1; lcd_clear (NULL, 1, 1, NULL); /* dummy args */ lcd_enable (); @@ -435,6 +710,15 @@ static int lcd_init (void *lcdbase) * * Note that this is running from ROM, so no write access to global data. */ +#ifdef CONFIG_BOARD_LCD_SETMEM +ulong lcd_setmem (ulong addr) +{ + ulong size; + size = board_lcd_setmem(addr); + addr -= size; + return addr; +} +#else ulong lcd_setmem (ulong addr) { ulong size; @@ -455,6 +739,7 @@ ulong lcd_setmem (ulong addr) return (addr); } +#endif /*----------------------------------------------------------------------*/ @@ -608,7 +893,7 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) ushort *cmap_base = NULL; ushort i, j; uchar *fb; - bmp_image_t *bmp=(bmp_image_t *)bmp_image; + bmp_image_t *bmp; uchar *bmap; ushort padded_line; unsigned long width, height, byte_width; @@ -622,6 +907,10 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) volatile cpm8xx_t *cp = &(immr->im_cpm); #endif + if (!bmp_image) + return 1; + bmp = (bmp_image_t *)bmp_image; + if (!((bmp->header.signature[0]=='B') && (bmp->header.signature[1]=='M'))) { printf ("Error: no valid bmp image at %lx\n", bmp_image); @@ -636,14 +925,14 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) bpix = NBITS(panel_info.vl_bpix); - if ((bpix != 1) && (bpix != 8) && (bpix != 16)) { + if ((bpix != 1) && (bpix != 8) && (bpix != 16) && (bpix != 32)) { printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", bpix, bmp_bpix); return 1; } /* We support displaying 8bpp BMPs on 16bpp LCDs */ - if (bpix != bmp_bpix && (bmp_bpix != 8 || bpix != 16)) { + if (bpix != bmp_bpix && (bmp_bpix != 8 || bpix != 16) && (bmp_bpix != 16 || bpix != 32)) { printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", bpix, le16_to_cpu(bmp->header.bit_count)); @@ -714,14 +1003,15 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) #ifdef CONFIG_SPLASH_SCREEN_ALIGN if (x == BMP_ALIGN_CENTER) - x = max(0, (pwidth - width) / 2); + x = max(0, ((int)pwidth - (int)width) / 2); else if (x < 0) - x = max(0, pwidth - width + x + 1); + x = max(0, (int)pwidth - (int)width + x + 1); if (y == BMP_ALIGN_CENTER) - y = max(0, (panel_info.vl_row - height) / 2); + y = max(0, ((int)panel_info.vl_row - (int)height) / 2); else if (y < 0) - y = max(0, panel_info.vl_row - height + y + 1); + y = max(0, (int)panel_info.vl_row - (int)height + y + 1); + #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ if ((x + width)>pwidth) @@ -762,22 +1052,42 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) #if defined(CONFIG_BMP_16BPP) case 16: - for (i = 0; i < height; ++i) { - WATCHDOG_RESET(); - for (j = 0; j < width; j++) { + if (bpix == 32) { + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + uint32_t color; + + /* Convert bmp from 16bpp->32bpp */ + /* Red */ + color = (bmap[1] << 8) | bmap[0]; + fb[2] = (color >> 8) & 0xf8; + fb[1] = (color >> 3) & 0xfc; + fb[0] = (color << 3) & 0xf8; + fb += 4; + bmap+=2; + } + bmap += (padded_line - width) * 2; + fb -= (width * 4 + lcd_line_length); + } + } else { + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { #if defined(CONFIG_ATMEL_LCD_BGR555) - *(fb++) = ((bmap[0] & 0x1f) << 2) | - (bmap[1] & 0x03); - *(fb++) = (bmap[0] & 0xe0) | - ((bmap[1] & 0x7c) >> 2); - bmap += 2; + *(fb++) = ((bmap[0] & 0x1f) << 2) | + (bmap[1] & 0x03); + *(fb++) = (bmap[0] & 0xe0) | + ((bmap[1] & 0x7c) >> 2); + bmap += 2; #else - *(fb++) = *(bmap++); - *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); #endif + } + bmap += (padded_line - width) * 2; + fb -= (width * 2 + lcd_line_length); } - bmap += (padded_line - width) * 2; - fb -= (width * 2 + lcd_line_length); } break; #endif /* CONFIG_BMP_16BPP */ @@ -786,6 +1096,10 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) break; }; +#ifdef CONFIG_ARM + /* Flush the cache to make sure display tracks content of memory */ + flush_cache(0, ~0); +#endif return (0); } #endif @@ -796,6 +1110,7 @@ static void *lcd_logo (void) char *s; ulong addr; static int do_splash = 1; + int ret; if (do_splash && (s = getenv("splashimage")) != NULL) { int x = 0, y = 0; @@ -828,7 +1143,12 @@ static void *lcd_logo (void) } #endif - if (lcd_display_bitmap (addr, x, y) == 0) { + ret = lcd_display_bitmap (addr, x, y); +#ifdef CONFIG_VIDEO_BMP_GZIP + if ((unsigned long)bmp != addr) + free((void *)addr); +#endif + if (ret == 0) { return ((void *)lcd_base); } } @@ -853,3 +1173,79 @@ static void *lcd_logo (void) /************************************************************************/ /************************************************************************/ + +#ifdef CONFIG_LCD_PERCENT +#define PERCENT_BUF_SIZE 128 +static struct { + int size; + int total; + ulong now, when; + int percent; + char string[PERCENT_BUF_SIZE]; +} percent_data; + +void lcd_percent_init(int size) +{ + percent_data.size = 0; + percent_data.percent = -1; + percent_data.total = size; + percent_data.when = get_timer(0); +} + +void lcd_percent_update(int size) +{ + int percent; + char buf[PERCENT_BUF_SIZE]; + char *src, *dst; + + if (percent_data.string[0]) { + unsigned long long n = size * 100ULL; + do_div(n, percent_data.total); + percent = (int)n; + percent_data.now = get_timer(0); + if (percent != percent_data.percent) { + if (percent == 100 + || !percent_data.size + || ((percent_data.now - percent_data.when) > (CONFIG_SYS_HZ/4))) { + percent_data.percent = percent; + /* copy string into buf, replace '/P' with percent value */ + dst = buf; + src = percent_data.string; + while (*src && (dst < &buf[PERCENT_BUF_SIZE-10])) { + if (src[0] == '/' && src[1] == 'P') { + dst += sprintf(dst, "%d", percent); + src+=2; + } else + *dst++ = *src++; + } + *dst = '\0'; + output_lcd_string(buf); + percent_data.when = percent_data.now; + } + } + percent_data.size = size; + } +} + +static int do_lcd_percent (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc > 1) + strncpy(percent_data.string, argv[1], sizeof(percent_data.string) - 1); + else + percent_data.string[0] = '\0'; + return 0; +} + +U_BOOT_CMD( + lcd_percent, 2, 1, do_lcd_percent, + "setup percentage output on LCD", + " - string to print when percent changes (/P is replaced with percent)" +); +#else +void lcd_percent_init(int total_size) +{ +} +void lcd_percent_update(int size) +{ +} +#endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a5fa2b5d851..fc6eb59244b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -31,6 +31,8 @@ COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o COBJS-$(CONFIG_PCA953X) += pca953x.o COBJS-$(CONFIG_S5P) += s5p_gpio.o +COBJS-$(CONFIG_TWL4030_GPIO) += twl4030-gpio.o +COBJS-$(CONFIG_TWL4030_PWM) += twl4030-pwm.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c new file mode 100644 index 00000000000..57c12fcfb95 --- /dev/null +++ b/drivers/gpio/twl4030-gpio.c @@ -0,0 +1,105 @@ +#include <common.h> +#include <twl4030.h> + +#define BIT(x) (1 << (x)) + +/* GPIO_CTRL Fields */ +#define MASK_GPIO_CTRL_GPIO_ON BIT(2) + +/* store usage of each GPIO. - each bit represents one GPIO */ +static unsigned int gpio_usage_count; + +static inline int gpio_twl4030_write(u8 address, u8 data) +{ + return twl4030_i2c_write_u8(TWL4030_CHIP_GPIO, data, address); +} + +static inline int gpio_twl4030_read(u8 address) +{ + u8 data; + int ret = 0; + + ret = twl4030_i2c_read_u8(TWL4030_CHIP_GPIO, &data, address); + return (ret < 0) ? ret : data; +} + +int twl4030_set_gpio_direction(unsigned int gpio, unsigned int is_input) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 reg = 0; + u8 base = REG_GPIODATADIR1 + d_bnk; + int ret = 0; + + ret = gpio_twl4030_read(base); + if (ret >= 0) { + if (is_input) + reg = ret & ~d_msk; + else + reg = ret | d_msk; + + ret = gpio_twl4030_write(base, reg); + } + + return ret; +} + +int twl4030_set_gpio_dataout(unsigned int gpio, unsigned int enable) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 base = 0; + + if (enable) + base = REG_SETGPIODATAOUT1 + d_bnk; + else + base = REG_CLEARGPIODATAOUT1 + d_bnk; + + return gpio_twl4030_write(base, d_msk); +} + +int twl4030_get_gpio_datain(unsigned int gpio) +{ + u8 d_bnk = gpio >> 3; + u8 d_off = gpio & 0x7; + u8 base = 0; + int ret = 0; + + if (unlikely((gpio >= TWL4030_GPIO_MAX) + || !(gpio_usage_count & BIT(gpio)))) + return -1; + + base = REG_GPIODATAIN1 + d_bnk; + ret = gpio_twl4030_read(base); + if (ret > 0) + ret = (ret >> d_off) & 0x1; + + return ret; +} + +int twl4030_request_gpio(unsigned int gpio) +{ + int status = 0; + + /* on first use, turn GPIO module "on" */ + if (!gpio_usage_count) { + // struct twl4030_gpio_platform_data *pdata; + u8 value = MASK_GPIO_CTRL_GPIO_ON; + + status = gpio_twl4030_write(REG_GPIO_CTRL, value); + } + + if (!status) + gpio_usage_count |= (0x1 << gpio); + + return status; +} + +void twl4030_free_gpio(unsigned int gpio) +{ + gpio_usage_count &= ~BIT(gpio); + + /* on last use, switch off GPIO module */ + if (!gpio_usage_count) + gpio_twl4030_write(REG_GPIO_CTRL, 0x0); +} diff --git a/drivers/gpio/twl4030-pwm.c b/drivers/gpio/twl4030-pwm.c new file mode 100644 index 00000000000..dfbb7f5b596 --- /dev/null +++ b/drivers/gpio/twl4030-pwm.c @@ -0,0 +1,126 @@ +#include <common.h> +#include <twl4030.h> + +/* INTRB register offsets (TWL4030_CHIP_INTBR) */ +#define TWL_INTBR_PMBR1 0x92 +#define TWL_INTBR_GPBR1 0x91 + +/*PWM0 register offsets (TWL4030_CHIP_PWM0) */ +#define TWL_LED_PWMON 0xf8 +#define TWL_LED_PWMOFF 0xf9 + +#if 0 +#define PWM_PRINT(fmt, args...) printf(fmt, ## args) +#else +#define PWM_PRINT(fmt, args...) +#endif + +int twl4030_set_pwm0(int level, int max_brightness) +{ + u8 mux_pwm, enb_pwm; + unsigned char c; + int status; + + PWM_PRINT("%s: level %d\n", __FUNCTION__, level); + if (level > max_brightness) + return -1; + + twl4030_i2c_read_u8(TWL4030_CHIP_INTBR, &mux_pwm, TWL_INTBR_PMBR1); + twl4030_i2c_read_u8(TWL4030_CHIP_INTBR, &enb_pwm, TWL_INTBR_GPBR1); + + PWM_PRINT("%s: enb_pwm %02x mux_pwm %02x\n", __FUNCTION__, enb_pwm, mux_pwm); + + if (level == 0) { + /* disable pwm0 output and clock */ + enb_pwm &= ~0x05; + /* change pwm0 pin to gpio pin */ + mux_pwm &= ~0x0c; + + PWM_PRINT("%s: disable enb_pwm %02x mux_pwm %02x\n", __FUNCTION__, enb_pwm, mux_pwm); + + status = twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, + enb_pwm, TWL_INTBR_GPBR1); + if (status) { + printf("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -2; + } + status = twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, + mux_pwm, TWL_INTBR_PMBR1); + if (status) { + printf("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -3; + } +#if 0 + PWM_PRINT("%s: turn off GPIO_%d as backlight!\n", __FUNCTION__, omap3logic_dss_lcd_data.lcd_gpio_backlight); + /* Turn off the backlight! */ + gpio_set_value(omap3logic_dss_lcd_data.lcd_gpio_backlight, 0); +#endif + return 0; + } + + if (((enb_pwm & 0x5) != 0x5) || ((mux_pwm & 0x0c) != 0x4)) { + /* change gpio pin to pwm0 pin */ + mux_pwm = (mux_pwm & ~0xc) | 0x04; + /* enable pwm0 output and clock*/ + enb_pwm = (enb_pwm & ~0x5) | 0x05; + + PWM_PRINT("%s: enable enb_pwm %02x mux_pwm %02x\n", __FUNCTION__, enb_pwm, mux_pwm); + status = twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, + mux_pwm, TWL_INTBR_PMBR1); + if (status) { + printf("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -4; + } + status = twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, + enb_pwm, TWL_INTBR_GPBR1); + if (status) { + printf("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -5; + } +#if 0 + PWM_PRINT("%s: turn on GPIO_%d as backlight!\n", __FUNCTION__, omap3logic_dss_lcd_data.lcd_gpio_backlight); + /* Turn on the backlight! */ + gpio_set_value(omap3logic_dss_lcd_data.lcd_gpio_backlight, 1); +#endif + } + + /* 255 -> 1, 1 -> 126 */ + c = (max_brightness * 126 + (1 - 126) * level) / (max_brightness - 1); + + PWM_PRINT("%s: c %d (%d%% on)\n", __FUNCTION__, c, (((max_brightness+1)-c) * 100)/(max_brightness+1)); + status = twl4030_i2c_write_u8(TWL4030_CHIP_PWM0, 0x7F, TWL_LED_PWMOFF); + if (status) { + PWM_PRINT("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -6; + } + status = twl4030_i2c_write_u8(TWL4030_CHIP_PWM0, c, TWL_LED_PWMON); + if (status) { + PWM_PRINT("%s:%d status %d\n", __FUNCTION__, __LINE__, status); + return -7; + } + return 0; +} + +void twl4030_dump_pwm0(void) +{ + int result; + u8 mux_pwm, enb_pwm; + u8 off_period, on_period; + + result = twl4030_i2c_read_u8(TWL4030_CHIP_INTBR, &mux_pwm, + TWL_INTBR_PMBR1); + result |= twl4030_i2c_read_u8(TWL4030_CHIP_INTBR, &enb_pwm, + TWL_INTBR_GPBR1); + + result |= twl4030_i2c_read_u8(TWL4030_CHIP_PWM0, &off_period, + TWL_LED_PWMOFF); + result |=twl4030_i2c_read_u8(TWL4030_CHIP_PWM0, &on_period, + TWL_LED_PWMON); + + if (result) { + printf("%s: failed to read TWL regitsters; result %d\n", __FUNCTION__, result); + return; + } + printf("%s: mux_pwm %02x enb_pwm %02x off_period %02x on_period %02x\n", + __FUNCTION__, mux_pwm, enb_pwm, off_period, on_period); +} diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 71251d8007f..ec02a5d33e8 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -318,6 +318,10 @@ int i2c_probe (uchar chip) return res; } + // Pre-enable the peripheral. Otherwise it randomly would not transmit. + // When it doesn't tramsit the probe command reports spurious addresses. + writew(I2C_CON_EN, &i2c_base->con); + /* wait until bus not busy */ wait_for_bb (); diff --git a/drivers/mmc/omap3_mmc.c b/drivers/mmc/omap3_mmc.c index 15d41e55bd1..83d27918872 100644 --- a/drivers/mmc/omap3_mmc.c +++ b/drivers/mmc/omap3_mmc.c @@ -33,6 +33,36 @@ #include "omap3_mmc.h" +#undef DEBUG_MMC + +#ifdef DEBUG_MMC +#define MMC_PRINTF(fmt, args...) printf(fmt, ## args) +#else +#define MMC_PRINTF(fmt, args...) +#endif + +#define USE_NEW_TRANSPEED_ENCODING + +#ifdef USE_NEW_TRANSPEED_ENCODING +/* TRAN_SPEED Bit encoding: + * bits 2:0 transfer rate unit + * 0=100kbit/s, 1=1Mbit/s, 2=10Mbit/s, 3=100Mbit/s, 4... 7=reserved + * 6:3 time value + * 0=reserved, 1=1.0, 2=1.2, 3=1.3, 4=1.5, 5=2.0, 6=2.5, 7=3.0, + * 8=3.5, 9=4.0, A=4.5, B=5.0, C=5.5, D=6.0, E=7.0, F=8.0 + */ + +/* trans_rate is 1/10 of actual while trans_value is 10x actual. + * multiplication of both together gives speed, or negative if + * trans_rate is reserved */ +int trans_rate[8] = { + 10000, 100000, 1000000, 10000000, -1, -1 -1 -1 +}; +int trans_value[16] = { + -1, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 +}; +#else + static const unsigned short mmc_transspeed_val[15][4] = { {CLKD(10, 1), CLKD(10, 10), CLKD(10, 100), CLKD(10, 1000)}, {CLKD(12, 1), CLKD(12, 10), CLKD(12, 100), CLKD(12, 1000)}, @@ -50,6 +80,7 @@ static const unsigned short mmc_transspeed_val[15][4] = { {CLKD(70, 1), CLKD(70, 10), CLKD(70, 100), CLKD(70, 1000)}, {CLKD(80, 1), CLKD(80, 10), CLKD(80, 100), CLKD(80, 1000)} }; +#endif static mmc_card_data cur_card_data; static block_dev_desc_t mmc_blk_dev; @@ -475,12 +506,13 @@ static unsigned long mmc_bread(int dev_num, unsigned long blknr, return i; } -static unsigned char configure_mmc(mmc_card_data *mmc_card_cur) +/* static */ unsigned char configure_mmc(mmc_card_data *mmc_card_cur) { unsigned char ret_val; unsigned int argument; - unsigned int trans_clk, trans_fact, trans_unit, retries = 2; - unsigned char trans_speed; + unsigned int trans_clk, retries = 2; + unsigned int trans_fact, trans_unit; + int trans_speed; mmc_resp_t mmc_resp; ret_val = mmc_init_setup(); @@ -501,12 +533,40 @@ static unsigned char configure_mmc(mmc_card_data *mmc_card_cur) if (mmc_card_cur->card_type == MMC_CARD) mmc_card_cur->version = mmc_resp.Card_CSD.spec_vers; + MMC_PRINTF("%s: %08x %08x %08x %08x\n", __FUNCTION__, mmc_resp.resp[0], + mmc_resp.resp[1], mmc_resp.resp[2], mmc_resp.resp[3]); + trans_speed = mmc_resp.Card_CSD.tran_speed; + MMC_PRINTF("%s: tran_speed %#x\n", __FUNCTION__, trans_speed); + ret_val = mmc_send_cmd(MMC_CMD4, MMC_DSR_DEFAULT << 16, mmc_resp.resp); if (ret_val != 1) return ret_val; + if (mmc_card_cur->max_freq && trans_speed > mmc_card_cur->max_freq) { + trans_speed = mmc_card_cur->max_freq; + } + +#ifdef USE_NEW_TRANSPEED_ENCODING + trans_unit = trans_speed & MMC_CSD_TRAN_SPEED_UNIT_MASK; + trans_fact = trans_speed & MMC_CSD_TRAN_SPEED_FACTOR_MASK; + trans_unit >>= 0; + trans_fact >>= 3; + + MMC_PRINTF("%s: trans_unit %u trans_fact %u\n", __FUNCTION__, trans_unit, trans_fact); + trans_speed = trans_rate[trans_unit] * trans_value[trans_fact]; + MMC_PRINTF("%s: trans_speed %d\n", __FUNCTION__, trans_speed); + if (trans_speed < 0) + return 0; + MMC_PRINTF("%s: MMC_CLOCK %u\n", __FUNCTION__, (MMC_CLOCK_REFERENCE * 1000000)); + trans_clk = (MMC_CLOCK_REFERENCE * 1000000) / trans_speed; + MMC_PRINTF("%s: trans_clk %u\n", __FUNCTION__, trans_clk); + if (trans_speed * trans_clk < (MMC_CLOCK_REFERENCE * 1000000)) + trans_clk++; + if (trans_clk > 1023) + trans_clk = 1023; +#else trans_unit = trans_speed & MMC_CSD_TRAN_SPEED_UNIT_MASK; trans_fact = trans_speed & MMC_CSD_TRAN_SPEED_FACTOR_MASK; @@ -521,6 +581,8 @@ static unsigned char configure_mmc(mmc_card_data *mmc_card_cur) trans_fact >>= 3; trans_clk = mmc_transspeed_val[trans_fact - 1][trans_unit] * 2; +#endif + MMC_PRINTF("%s:%d trans_clk %#x\n", __FUNCTION__, __LINE__, trans_clk); ret_val = mmc_clock_config(CLK_MISC, trans_clk); if (ret_val != 1) @@ -547,9 +609,23 @@ static unsigned char configure_mmc(mmc_card_data *mmc_card_cur) int mmc_legacy_init(int dev) { + char *p, *q, buf[32]; + if (mmc_set_dev(dev) != 0) return 1; + sprintf(buf, "mmc%d_max_freq_mhz", dev); + p = getenv(buf); + if (p) { + cur_card_data.max_freq = (unsigned int)simple_strtoull(p, &q, 0); + if (q && *q) + cur_card_data.max_freq = 0; + } else + cur_card_data.max_freq = 0; + + if (cur_card_data.max_freq) + printf("MMC%d: max frequency limited to %uMHz\n", dev, cur_card_data.max_freq); + if (configure_mmc(&cur_card_data) != 1) return 1; diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5a5ecdfe3c6..f01f157c155 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmtd.o +COBJS-$(CONFIG_MTD_DEBUG) += mtd_debug.o COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o diff --git a/drivers/mtd/mtd_debug.c b/drivers/mtd/mtd_debug.c new file mode 100644 index 00000000000..31913f05659 --- /dev/null +++ b/drivers/mtd/mtd_debug.c @@ -0,0 +1,18 @@ +/* + * MTD verbose debug + * + * (C) 2011 Peter Barada <peter.barada@logicpd.com> + * + * This code is GPL + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/compat.h> +#include <ubi_uboot.h> + +#ifdef CONFIG_MTD_DEBUG +#ifndef CONFIG_MTD_DEBUG_VERBOSE +#define CONFIG_MTD_DEBUG_VERBOSE -1 +#endif +int mtd_debug_verbose = CONFIG_MTD_DEBUG_VERBOSE; +#endif diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8b598f6bfef..975d152e794 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_MTD_NAND_ECC_BCH) += nand_bch.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 52f8575aac6..1092a797321 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -43,6 +43,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> @@ -136,7 +137,11 @@ static void nand_release_device (struct mtd_info *mtd) static uint8_t nand_read_byte(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return readb(chip->IO_ADDR_R); + uint8_t byte; + + byte = readb(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %02x\n", byte); + return byte; } /** @@ -149,7 +154,11 @@ static uint8_t nand_read_byte(struct mtd_info *mtd) static uint8_t nand_read_byte16(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); + uint8_t byte; + + byte = (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %02x\n", byte); + return byte; } /** @@ -162,7 +171,11 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd) static u16 nand_read_word(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - return readw(chip->IO_ADDR_R); + u16 word; + + word = readw(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %04x\n", word); + return word; } /** @@ -201,6 +214,8 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) int i; struct nand_chip *chip = mtd->priv; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND W %d bytes\n", len); + for (i = 0; i < len; i++) writeb(buf[i], chip->IO_ADDR_W); } @@ -218,6 +233,8 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) int i; struct nand_chip *chip = mtd->priv; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %d bytes\n", len); + for (i = 0; i < len; i++) buf[i] = readb(chip->IO_ADDR_R); } @@ -236,8 +253,10 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) - if (buf[i] != readb(chip->IO_ADDR_R)) + if (buf[i] != readb(chip->IO_ADDR_R)) { + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND verify fail at offset %d\n", i); return -EFAULT; + } return 0; } @@ -256,6 +275,8 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) u16 *p = (u16 *) buf; len >>= 1; + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND W %d words\n", len); + for (i = 0; i < len; i++) writew(p[i], chip->IO_ADDR_W); @@ -276,8 +297,20 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) u16 *p = (u16 *) buf; len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND R %d words\n", len); + + for (i = 0; i < len; i+=8) { + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + *p++ = readw(chip->IO_ADDR_R); + } + for (; i < len; ++i) + *p++ = readw(chip->IO_ADDR_R); } /** @@ -399,11 +432,88 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; - /* Check the WP bit */ + + /* Check the WP bit. To do so requires resetting the device to + force the status back to its reset value (so WP becomes whether + the WP pin is set). */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Now check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } +/* + * nand_get_features: - Read the features of the NAND chip + * @param mtd nand mtd instance + * @param faddr nand feature address + * @param features return 4-byte array of features + * + * @return 0 on success, -1 in case of errors + */ +int nand_get_features(struct mtd_info *mtd, uint8_t faddr, uint8_t *features) +{ + struct nand_chip *chip = mtd->priv; + int i; + + chip->select_chip(mtd, 0); + + /* Send the status command */ + chip->cmd_ctrl(mtd, NAND_CMD_GET_FEATURES, NAND_CTRL_CHANGE | NAND_CTRL_CLE); + + /* Send the feature address */ + chip->cmd_ctrl(mtd, faddr, NAND_CTRL_CHANGE | NAND_CTRL_ALE); + /* Switch to data access */ + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE | NAND_NCE); + + ndelay(100); + + for (i=0; i<4; ++i) + features[i] = chip->read_byte(mtd); + + MTDDEBUG(MTD_DEBUG_LEVEL4, "%s: %02x %02x %02x %02x\n", __FUNCTION__, features[0], + features[1], features[2], features[3]); + + return 0; +} + +/** + * nand_set_features - [GENERIC] set features array + * @mtd: MTD device structure + * @faddr: feature address + * @params: 4-byte array of parameters to write + */ +int nand_set_features(struct mtd_info *mtd, uint8_t faddr, uint8_t *features) +{ + struct nand_chip *chip; + + chip = mtd->priv; + + chip->select_chip(mtd, 0); + + /* Send the status command */ + chip->cmd_ctrl(mtd, NAND_CMD_SET_FEATURES, NAND_CTRL_CHANGE | NAND_CTRL_CLE); + /* Send the feature address */ + chip->cmd_ctrl(mtd, faddr, NAND_CTRL_CHANGE | NAND_CTRL_ALE); + /* Switch to data access */ + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_CTRL_CHANGE | NAND_NCE); + + ndelay(100); + if (chip->options & NAND_BUSWIDTH_16) { + uint16_t ftrs16[4]; + int i; + for (i=0; i<4; ++i) + ftrs16[i] = features[i]; + chip->write_buf(mtd, (uint8_t *)ftrs16, sizeof(ftrs16)); + } else + chip->write_buf(mtd, features, 4); + + udelay(2); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: faddr %02x [%02x %02x %02x %02x]\n", __FUNCTION__, faddr, features[0], features[1], features[2], features[3]); + + return 0; +} + /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure @@ -751,7 +861,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) * * Not for syndrome calculating ecc controllers, which use a special oob layout */ -static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { chip->read_buf(mtd, buf, mtd->writesize); @@ -808,7 +918,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c * @buf: buffer to store read data * @page: page number to read */ -static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { int i, eccsize = chip->ecc.size; @@ -850,7 +960,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @readlen: data length * @bufpoi: buffer to store read data */ -static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +/* static */ int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -1170,6 +1280,26 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, sndcmd = 0; } + /* If in chipe ECC mode, need to read the status + to see if an ECC error occurred. */ + if (chip->ecc.mode == NAND_ECC_CHIP) { + int status; + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + status = chip->read_byte(mtd); + chip->cmd_ctrl(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + if (status & 0x1) + mtd->ecc_stats.failed++; + else if (status & 0x10) + mtd->ecc_stats.corrected++; + } + /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, @@ -1306,7 +1436,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @page: page number to read * @sndcmd: flag whether to issue read command or not */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if (sndcmd) { @@ -1362,7 +1492,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status = 0; @@ -1586,7 +1716,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, * * Not for syndrome calculating ecc controllers, which use a special oob layout */ -static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { chip->write_buf(mtd, buf, mtd->writesize); @@ -1637,7 +1767,7 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip * @chip: nand chip info structure * @buf: data buffer */ -static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, +/* static */ void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { int i, eccsize = chip->ecc.size; @@ -1996,7 +2126,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); + "Attempt to write past end of page(%d+%d>%d)\n", ops->ooboffs, ops->ooblen, len); return -EINVAL; } @@ -2585,6 +2715,9 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, return ERR_PTR(-ENODEV); } + chip->maf_id = tmp_manf; + chip->dev_id = tmp_id; + if (!type) type = nand_flash_ids; @@ -2763,7 +2896,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one */ - if (!chip->ecc.layout) { + if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: chip->ecc.layout = &nand_oob_8; @@ -2862,6 +2995,71 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; chip->ecc.bytes = 3; + if (chip->has_chip_ecc) { + /* Put chip into no-ECC mode */ + uint8_t params[4] = {0x00, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } + break; + + case NAND_ECC_SOFT_BCH: + printk(KERN_INFO "NAND ECC: SOFT_BCH\n"); + if (!mtd_nand_has_bch()) { + printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n"); + BUG(); + } + chip->ecc.calculate = nand_bch_calculate_ecc; + chip->ecc.correct = nand_bch_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. + * Otherwise, default to 4 bits for large page devices + */ + if (!chip->ecc.size && (mtd->oobsize >= 64)) { + chip->ecc.size = 512; + chip->ecc.bytes = 7; + } + chip->ecc.priv = nand_bch_init(mtd, + chip->ecc.size, + chip->ecc.bytes, + &chip->ecc.layout); + if (!chip->ecc.priv) { + printk(KERN_WARNING "BCH ECC initialization failed!\n"); + BUG(); + } + + if (chip->has_chip_ecc) { + /* Put chip into no-ECC mode */ + uint8_t params[4] = {0x00, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } + break; + + case NAND_ECC_CHIP: + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 0; + + if (chip->has_chip_ecc) { + /* Put chip into ECC mode */ + uint8_t params[4] = {0x08, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } break; case NAND_ECC_NONE: @@ -2875,6 +3073,11 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; + if (chip->has_chip_ecc) { + /* Put chip into ECC mode */ + uint8_t params[4] = {0x08, 0x00, 0x00, 0x00}; + nand_set_features(mtd, 0x90, params); + } break; default: @@ -2893,6 +3096,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: oobavail %d\n", __FUNCTION__, mtd->oobavail); /* * Set the number of read / write steps for one page depending on ECC @@ -2994,6 +3198,9 @@ void nand_release(struct mtd_info *mtd) del_mtd_partitions(mtd); #endif + if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + /* Free bad block table memory */ kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS)) diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c new file mode 100644 index 00000000000..632dc7ae7c1 --- /dev/null +++ b/drivers/mtd/nand/nand_bch.c @@ -0,0 +1,287 @@ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * This file 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 or (at your option) any + * later version. + * + * This file 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 file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#if 1 +#if 1 +#include <config.h> +#include <common.h> +#include <linux/mtd/compat.h> +#include <malloc.h> +#endif +#include <linux/types.h> +// #include <linux/kernel.h> +// #include <linux/module.h> +// #include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> +#else +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_bch.h> +#include <linux/bch.h> +#endif + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + +#ifdef DEBUG_BCH + printf("%s:%d buf %p code %p\n", __FUNCTION__, __LINE__, buf, code); + for (i=0; i<16; ++i) + printf("%s%02x", !i?"dat: ":" ", buf[i]); + printf("\n"); +#endif + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + +#ifdef DEBUG_BCH + printf("%s:%d code:", __FUNCTION__, __LINE__); + for (i=0; i<7; ++i) + printf(" %02x", code[i]); + printf("\n"); +#endif + + return 0; +} +// EXPORT_SYMBOL(nand_bch_calculate_ecc); + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd->priv; + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + +#ifdef DEBUG_BCH + printf("%s:%d buf %p read_ecc %p calc_ecc %p\n", __FUNCTION__, __LINE__, buf, read_ecc, calc_ecc); + for (i=0; i<16; ++i) + printf("%s%02x", !i?"dat: ":" ", buf[i]); + printf("\n"); + for (i=0; i<7; ++i) + printf("%s%02x", !i?"read_ecc: ":" ", read_ecc[i]); + printf("\n"); + for (i=0; i<7; ++i) + printf("%s%02x", !i?"calc_ecc: ":" ", calc_ecc[i]); + printf("\n"); +#endif + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n", + __func__, errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -1; + } + return count; +} +// EXPORT_SYMBOL(nand_bch_correct_data); + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * @eccsize: ecc block size in bytes + * @eccbytes: ecc length in bytes + * @ecclayout: output default layout + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, + struct nand_ecclayout **ecclayout) +{ + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!*ecclayout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + *ecclayout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} +// EXPORT_SYMBOL(nand_bch_init); + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} +// EXPORT_SYMBOL(nand_bch_free); + +// MODULE_LICENSE("GPL"); +// MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); +// MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 5a6f7aec888..82e661d39c9 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -120,6 +120,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) priv_nand->bbt = NULL; } + lcd_percent_init(erase_length); + for (erased_length = 0; erased_length < erase_length; erase.addr += meminfo->erasesize) { @@ -138,6 +140,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (!opts->spread) erased_length++; + lcd_percent_update(erased_length); + continue; } else if (ret < 0) { @@ -157,6 +161,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) continue; } + /* format for JFFS2 ? */ if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { chip->ops.ooblen = 8; @@ -197,10 +202,13 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) erase.addr); } } + lcd_percent_update(erased_length); } + lcd_percent_update(erased_length); if (!opts->quiet) printf("\n"); + if (nand_block_bad_old) { struct nand_chip *priv_nand = meminfo->priv; @@ -337,8 +345,11 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) int status; int page; struct nand_chip *chip = mtd->priv; + +#if 0 printf ("nand_unlock: start: %08x, length: %d!\n", (int)start, (int)length); +#endif /* select the NAND device */ chipnr = (int)(start >> chip->chip_shift); @@ -455,7 +466,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, u_char *buffer, int withoob) { int rval = 0, blocksize; - size_t left_to_write = *length; + size_t left_to_write = *length, total_to_write; u_char *p_buffer = buffer; int need_skip; @@ -499,7 +510,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return -EINVAL; } - if (!need_skip) { + if (!need_skip && !withoob) { rval = nand_write (nand, offset, length, buffer); if (rval == 0) return 0; @@ -510,6 +521,9 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return rval; } + total_to_write = left_to_write; + lcd_percent_init(total_to_write); + while (left_to_write > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t write_size; @@ -548,7 +562,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, ops.oobbuf = ops.datbuf + pagesize; rval = nand->write_oob(nand, offset, &ops); - if (!rval) + if (rval) break; offset += pagesize; @@ -571,8 +585,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, } left_to_write -= write_size; + lcd_percent_update(total_to_write - left_to_write); } + lcd_percent_update(total_to_write); + return 0; } @@ -594,7 +611,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, u_char *buffer) { int rval; - size_t left_to_read = *length; + size_t left_to_read = *length, total_to_read; u_char *p_buffer = buffer; int need_skip; @@ -622,6 +639,9 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, return rval; } + total_to_read = left_to_read; + lcd_percent_init(total_to_read); + while (left_to_read > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t read_length; @@ -651,7 +671,11 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, left_to_read -= read_length; offset += read_length; p_buffer += read_length; + + lcd_percent_update(total_to_read - left_to_read); } + lcd_percent_update(total_to_read); + return 0; } diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 99b9cef17c2..d5ee9bc5836 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -26,11 +26,15 @@ #include <asm/errno.h> #include <asm/arch/mem.h> #include <asm/arch/omap_gpmc.h> +#include <asm/arch/sys_proto.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> #include <nand.h> static uint8_t cs; static struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static struct nand_ecclayout chip_nand_oob = GPMC_NAND_CHIP_ECC_LAYOUT; /* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -57,8 +61,17 @@ static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, break; } - if (cmd != NAND_CMD_NONE) + if (cmd != NAND_CMD_NONE) { +#ifdef CONFIG_MTD_DEBUG + if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_cmd) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND C: %02x\n", cmd & 0xff); + else if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_adr) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND A: %02x\n", cmd & 0xff); + else if (this->IO_ADDR_W == &gpmc_cfg->cs[cs].nand_dat) + MTDDEBUG(MTD_DEBUG_LEVEL4, "NAND D: %02x\n", cmd & 0xff); +#endif writeb(cmd, this->IO_ADDR_W); + } } /* @@ -156,6 +169,25 @@ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, return 0; } +static int omap_correct_chip_hwecc(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip; + + chip = mtd->priv; + + printf("%s: ecc_status %02x\n", __func__, chip->ecc_status); + /* We stored the read status in info->ecc_status in the read. + If bit 0 is set, then there was an uncorrectable ECC error. + If bit 3 is set, then there was a correctable error (up to + four bits of correction). */ + if (chip->ecc_status & 0x01) + return -1; + if (chip->ecc_status & 0x08) + return 4; + return 0; +} + /* * omap_calculate_ecc - Generate non-inverted ECC bytes. * @@ -192,6 +224,14 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, return 0; } +static int omap_calculate_chip_hwecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:\n", __func__); + return 0; +} + + /* * omap_enable_ecc - This function enables the hardware ecc functionality * @mtd: MTD device structure @@ -224,14 +264,308 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } } +static void omap_enable_chip_hwecc(struct mtd_info *mtd, int mode) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:\n", __func__); +} + +/* + * omap_nand_chip_has_ecc - return true if chip has internal ECC + */ +int omap_nand_chip_has_ecc(void) +{ + struct nand_chip *chip; + struct mtd_info *mtd; + int i; + uint8_t ident[5]; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("Error: Can't switch ecc, no devices available\n"); + return 0; + } + + mtd = &nand_info[nand_curr_device]; + chip = mtd->priv; + +#if 1 + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Wait for the chip to get the ID ready */ + ndelay(100); + + for (i=0; i<2; ++i) + ident[i] = chip->read_byte(mtd); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d %02x %02x\n", __FUNCTION__, __LINE__, ident[0], ident[1]); + if (ident[0] == NAND_MFR_MICRON) { + for (i=2; i<5; ++i) + ident[i] = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d %02x %02x %02x\n", __FUNCTION__, __LINE__, ident[2], ident[3], ident[4]); + if (ident[4] & 0x3) + chip->has_chip_ecc = 1; + } + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: has_chip_ecc %d\n", __FUNCTION__, chip->has_chip_ecc); +#else + if (nand->maf_id == NAND_MFR_MICRON) { + switch(nand->dev_id) { + case 0x2c: + case 0xdc: + case 0xcc: + case 0xac: + case 0xbc: + case 0xa3: + case 0xb3: + case 0xd3: + case 0xc3: + nand->has_chip_ecc = 1; + return 1; + default: + break; + } + } +#endif + return chip->has_chip_ecc; +} + + +static void micron_set_chip_ecc(struct mtd_info *mtd, int enable) +{ + uint8_t params[4]; + + MTDDEBUG(MTD_DEBUG_LEVEL3,"%s:%d enable %d\n", __FUNCTION__, __LINE__, enable); + + memset(params, 0x00, sizeof(params)); + if (enable) + params[0] = 0x08; + nand_set_features(mtd, 0x90, params); + +#if 1 + nand_get_features(mtd, 0x90, params); + MTDDEBUG(MTD_DEBUG_LEVEL4,"%s: %02x %02x %02x %02x\n", __FUNCTION__, params[0], params[1], params[2], params[3]); +#endif +} + +/** + * nand_read_oob_chipecc - read; get status to seeif chip ECC error + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int omap_read_oob_chipecc(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + struct nand_chip *nand; + + nand = mtd->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: page = %d, len = %i\n", + __func__, page, mtd->oobsize); + + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + + /* Send the status command */ + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + chip->ecc_status = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: ecc_status %02x\n", __func__, chip->ecc_status); + if (chip->ecc_status & (0x8|0x1)) { + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d page %d ecc_status %02x\n", __FUNCTION__, __LINE__, page, chip->ecc_status); + if (chip->ecc_status & 0x1) + mtd->ecc_stats.failed++; + else if (chip->ecc_status & 0x80) + mtd->ecc_stats.corrected += 4; + } + + /* Send the read prefix */ + omap_nand_hwcontrol(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/** + * omap_nand_command_lp - Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void omap_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + omap_nand_hwcontrol(mtd, command & 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; + omap_nand_hwcontrol(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + omap_nand_hwcontrol(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + omap_nand_hwcontrol(mtd, page_addr, ctrl); + omap_nand_hwcontrol(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + omap_nand_hwcontrol(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + omap_nand_hwcontrol(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + + case NAND_CMD_READ0: + + /* Send the read start */ + omap_nand_hwcontrol(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + goto ready_exit; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + nand_wait_ready(mtd); + +ready_exit: + /* If the chip has internal ECC, then we need to read the status + to determin if there's an ECC error - capture it for handling by + omap_nand_correct_chip_hwecc() later */ + if (command == NAND_CMD_READ0) { + if (chip->has_chip_ecc) { + + /* Send the status command */ + omap_nand_hwcontrol(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + chip->ecc_status = chip->read_byte(mtd); + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: ecc_status %02x\n", __func__, chip->ecc_status); +#if 0 + if (chip->ecc_status & (0x8|0x1)) + printk("%s:%d page %d column %d ecc_status %02x\n", __FUNCTION__, __LINE__, page_addr, column, chip->ecc_status); +#endif + + /* Send the read prefix */ + omap_nand_hwcontrol(mtd, NAND_CMD_READ0, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + /* Switch to data access */ + omap_nand_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + } + } + +} + +static enum omap_nand_ecc_mode current_ecc_method; +enum omap_nand_ecc_mode omap_nand_current_ecc_method(void) +{ + return current_ecc_method; +} + /* * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. * The default is to come up on s/w ecc * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc, 2 - chip ecc * */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(enum omap_nand_ecc_mode mode) { struct nand_chip *nand; struct mtd_info *mtd; @@ -257,8 +591,50 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = NULL; nand->ecc.calculate = NULL; + /* If currently in BCH then free the priv pointer */ + if (nand->ecc.mode == NAND_ECC_SOFT_BCH && nand->ecc.priv) { + nand_bch_free((struct nand_bch_control *)nand->ecc.priv); + nand->ecc.priv = NULL; + } + /* Setup the ecc configurations again */ - if (hardware) { + if (mode == OMAP_ECC_SOFT_BCH) { + nand->ecc.mode = NAND_ECC_SOFT_BCH; + nand->ecc.calculate = nand_bch_calculate_ecc; + nand->ecc.correct = nand_bch_correct_data; + nand->ecc.read_page = nand_read_page_swecc; + nand->ecc.read_subpage = nand_read_subpage; + nand->ecc.write_page = nand_write_page_swecc; + nand->ecc.read_page_raw = nand_read_page_raw; + nand->ecc.write_page_raw = nand_write_page_raw; + nand->ecc.read_oob = nand_read_oob_std; + nand->ecc.write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.bytes values to + * select how many bits are correctable; see nand_bch_init() + * for details. + * Otherwise, default to 4 bits for large page devices + */ +#if 1 + /* Since switching, clear out previous ECC setup and let + * BCH figure it out */ + nand->ecc.size = 0; + nand->ecc.bytes = 0; + nand->ecc.layout = NULL; +#endif + if (!nand->ecc.size && (mtd->oobsize >= 64)) { + nand->ecc.size = 512; + nand->ecc.bytes = 7; + } + nand->ecc.priv = nand_bch_init(mtd, + nand->ecc.size, + nand->ecc.bytes, + &nand->ecc.layout); + if (!nand->ecc.priv) { + printk(KERN_WARNING "BCH ECC initialization failed!\n"); + BUG(); + } + } else if (mode == OMAP_ECC_HW) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_nand_oob; nand->ecc.size = 512; @@ -267,13 +643,41 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = omap_correct_data; nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); - printf("HW ECC selected\n"); - } else { + printf("NAND: HW ECC selected\n"); + if (nand->has_chip_ecc) + micron_set_chip_ecc(mtd, 0); + } else if (mode == OMAP_ECC_SOFT) { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ nand->ecc.layout = NULL; - printf("SW ECC selected\n"); - } + printf("NAND: SW ECC selected\n"); + if (nand->has_chip_ecc) + micron_set_chip_ecc(mtd, 0); + } else if (mode == OMAP_ECC_CHIP) { + if (!nand->has_chip_ecc) { + printf("NAND: Chip does not have internal ECC!\n"); + return; + } + nand->ecc.bytes = 0; + nand->ecc.size = 2048; + nand->ecc.calculate = omap_calculate_chip_hwecc; + nand->ecc.hwctl = omap_enable_chip_hwecc; + nand->ecc.correct = omap_correct_chip_hwecc; + nand->ecc.read_oob = omap_read_oob_chipecc; + nand->ecc.mode = NAND_ECC_CHIP; /* internal to chip */ + nand->ecc.layout = &chip_nand_oob; + if (nand->options & NAND_BUSWIDTH_16) + nand->cmdfunc = omap_nand_command_lp; + else + printf("%s: Huh? not 16-bit wide\n", __FUNCTION__); + micron_set_chip_ecc(mtd, 1); + printf("NAND: Internal to NAND ECC selected\n"); + } else { + printf("NAND: unknown ECC mode %d\n", mode); + return; + } + + current_ecc_method = mode; /* Update NAND handling after ECC mode switch */ nand_scan_tail(mtd); @@ -336,6 +740,11 @@ int board_nand_init(struct nand_chip *nand) if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000) nand->options |= NAND_BUSWIDTH_16; +#ifdef CONFIG_MTD_SKIP_BBTSCAN + /* Skip the bad block scan */ + nand->options |= NAND_SKIP_BBTSCAN; +#endif + nand->chip_delay = 100; /* Default ECC mode */ nand->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/power/twl4030.c b/drivers/power/twl4030.c index 5a7323a7150..cf79161aaf3 100644 --- a/drivers/power/twl4030.c +++ b/drivers/power/twl4030.c @@ -103,3 +103,520 @@ void twl4030_power_mmc_init(void) TWL4030_PM_RECEIVER_VMMC1_DEV_GRP, TWL4030_PM_RECEIVER_DEV_GRP_P1); } + +void twl4030_power_off(void) +{ + u8 val = 0; + if (twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, &val, + TWL4030_PM_MASTER_P1_SW_EVENTS)) { + printf("Error:TWL4030: failed to read the power register\n"); + } else { + val |= TWL4030_PM_MASTER_SW_EVENTS_DEVOFF; + if (twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, val, + TWL4030_PM_MASTER_P1_SW_EVENTS)) { + printf("Error:TWL4030: failed to write the power register\n"); + printf("Could not power off\n"); + } + } +} + +int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + printf("Power down.\n"); + twl4030_power_off(); + return 0; +} + +U_BOOT_CMD(poweroff, 1, 1, do_poweroff, + "Power down board", + "" +); + +#ifdef CONFIG_TWL4030_CHARGING +int twl4030_enable_charging(void) +{ + u8 val = 0; + /* write 0x57 to 0x4a, 0x85 (BCIMFKEY)*/ + /* Enabel access to BCIMFEN1 register access */ + if (twl4030_i2c_write_u8(TWL4030_CHIP_MAIN_CHARGE, 0x57, + TWL4030_MAIN_CHARGE_BCIMFKEY)) { + printf("Error:TWL4030: failed to write BCIMFKEY\n"); + return 1; + } + + /* read 0x4a, 0x86 */ + if (twl4030_i2c_read_u8(TWL4030_CHIP_MAIN_CHARGE, &val, + TWL4030_MAIN_CHARGE_BCIMFEN1)) { + printf("Error:TWL4030: failed to read BCIMFEN1\n"); + return 1; + } + + /* or in 0x80 */ + /* write to 0x4a, 0x86 (TWL_BCIMFEN1) */ + val |= TWL4030_MAIN_CHARGE_BCIMFEN1_VBATOV1EN; + if (twl4030_i2c_write_u8(TWL4030_CHIP_MAIN_CHARGE, val, + TWL4030_MAIN_CHARGE_BCIMFEN1)) { + printf("Error:TWL4030: failed to write BCIMFEN1\n"); + return 1; + } + + /* write 0xd2 to 0x4a, 0x85 (BCIMFKEY) */ + val = TWL4030_MAIN_CHARGE_BCIMFKEY_MFKEY5; + if (twl4030_i2c_write_u8(TWL4030_CHIP_MAIN_CHARGE, + val, + TWL4030_MAIN_CHARGE_BCIMFKEY)) { + printf("Error:TWL4030: failed to write BCIMFKEY\n"); + return 1; + } + + /* clear low four bits in 0x4a, 0x8a (BCIMFTH1) */ + if (twl4030_i2c_read_u8(TWL4030_CHIP_MAIN_CHARGE, &val, + TWL4030_MAIN_CHARGE_BCIMFTH1)) { + printf("Error:TWL4030: failed to read BCIMFTH1\n"); + return 1; + } + val = (val & ~TWL4030_MAIN_CHARGE_BCIMFTH1_VBATOV1TH_MASK) | + TWL4030_MAIN_CHARGE_BCIMFTH1_VBATOV1TH_2636_mV; + /* clear low four bits in 0x4a, 0x8a (BCIMFTH1) */ + if (twl4030_i2c_write_u8(TWL4030_CHIP_MAIN_CHARGE, val, + TWL4030_MAIN_CHARGE_BCIMFTH1)) { + printf("Error:TWL4030: failed to write BCIMFTH1\n"); + return 1; + } + + /* read 0x4a, 0x86; 0x4a, 0x8a (???) */ + + /* Turn on AC charging */ + /* write 0x90 to 0x49, 0x91 (MADC_HFCLK_EN, DEFAULTMADC_CLK_EN) */ + val = TWL4030_INTBR_GPBR1_MADC_HFCLK_EN; + val |= TWL4030_INTBR_GPBR1_DEFAULT_MADC_CLK_EN; + if (twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, val, + TWL4030_INTBR_GPBR1)) { + printf("Error:TWL4030: failed to write BCIMFTH1\n"); + return 1; + } + return 0; +} + + +struct { + u8 idx; + u8 reg_base; + char *str; + u16 mul; +} adc_regs[] = { + { + 0, + 0x37, + "(GP analog input/Bat type)", + 1466 + }, + { + 1, + 0x39, + "(GP analog input/Bat temp)", + 1446 + }, + { + 2, + 0x3b, + "(GP analog input)\t", + 2444, + }, + { + 3, + 0x3d, + "(GP analog input)\t", + 2444, + }, + { + 4, + 0x3f, + "(GP analog input)\t", + 2444, + }, + { + 5, + 0x41, + "(GP analog input)\t", + 2444, + }, + { + 6, + 0x43, + "(GP analog input)\t", + 2444, + }, + { + 7, + 0x45, + "(GP analog input)\t", + 2444, + }, + { + 8, + 0x47, + "(VBUS voltage)\t", + 6843, + }, + { + 9, + 0x49, + "(Charger backup bat volt)", + 4399, + }, + { + 11, + 0x4d, + "(Battery charger voltage)", + 9775, + }, + { + 12, + 0x4f, + "(Main battery voltage)", + 5865, + }, + { + 15, + 0x55, + "(VRUSB supply/spkr pol)", + 3225, + }, +}; + +struct +{ + u8 val; + char *str; + u8 pr_info; +} charge_state[] = { + { + 0x00, + "No charging device", + 0, + }, + { + 0x01, + "Off mode", + 0, + }, + { + 0x02, + "Standby mode", + 0, + }, + { + 0x03, + "Open bat or USB not debounced", + 0, + }, + { + 0x21, + "Constant voltage AC", + 1, + }, + { + 0x22, + "Quick charge AC 1", + 1, + }, + { + 0x23, + "Quick charge AC 2", + 1, + }, + { + 0x24, + "Quick charge AC 3", + 1, + }, + { + 0x25, + "Quick charge AC 4", + 1, + }, + { + 0x26, + "Quick charge AC 5", + 1, + }, + { + 0x27, + "Quick charge AC 6", + 1, + }, + { + 0x28, + "Charge stop AC 1", + 1, + }, + { + 0x29, + "Charge stop AC 1", + 1, + }, + { + 0x2a, + "Charge stop AC 1", + 1, + }, + { + 0x2b, + "Charge AC comp 1", + 1, + }, + { + 0x2c, + "Charge AC comp 2", + 1, + }, + { + 0x2d, + "Charge AC comp 3", + 1, + }, + { + 0x2e, + "Charge AC comp 4", + 1, + }, + { + 0x2f, + "AC adapter overvoltage", + 0, + }, + { + 0x12, + "Quick charge USB 1", + 1, + }, + { + 0x13, + "Quick charge USB 2", + 1, + }, + { + 0x14, + "Quick charge USB 3", + 1, + }, + { + 0x15, + "Quick charge USB 4", + 1, + }, + { + 0x16, + "Quick charge USB 5", + 1, + }, + { + 0x17, + "Quick charge USB 6", + 1, + }, + { + 0x18, + "Charge stop USB 1", + 1, + }, + { + 0x19, + "Charge stop USB 2", + 1, + }, + { + 0x1a, + "Charge stop USB 3", + 1, + }, + { + 0x1b, + "Charge USB comp 1", + 1, + }, + { + 0x1c, + "Charge USB comp 2", + 1, + }, + { + 0x1d, + "Charge USB comp 3", + 1, + }, + { + 0x1e, + "Charge USB comp 4", + 1, + }, + { + 0x1f, + "USB adapter overvoltage", + 0, + }, +}; + +int read_madc_msblsb(u8 reg) +{ + u8 val; + int temp; + + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &val, reg+1); + temp = ((int)(val & 0x3)) << 8; + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &val, reg); + return temp | val; +} + +#define VOLT_STEP_SIZE 588 +#define VOLT_PSR_R 100 + +int pm_battery_voltage(void) +{ + int volt = read_madc_msblsb(0x78); + return (volt * VOLT_STEP_SIZE) / VOLT_PSR_R; +} + +#define CURR_SLOPE 1194 + +int pm_battery_current(void) +{ + int curr = read_madc_msblsb(0x7c); + u8 val; + + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &val, 0x98); + if (val & 0x20) + return (curr * 1000) / (2 * CURR_SLOPE); + else + return (curr * 1000) / (1 * CURR_SLOPE); +} + +static int batt_table[] = { +/* 0 C*/ +30800, 29500, 28300, 27100, +26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, +17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, +11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, +8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, +5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, +4040, 3910, 3790, 3670, 3550 +}; + +#define TEMP_STEP_SIZE 147 +#define TEMP_PSR_R 100 + +int pm_battery_temp(void) +{ + u8 val; + int temp, curr, volt, res, ret; + + ret = read_madc_msblsb(0x7a); + + volt = (ret * TEMP_STEP_SIZE) / TEMP_PSR_R; + + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &val, 0x98); + curr = ((val & 0x07) + 1) * 10; + res = volt * 1000 / curr; + for (temp = 58; temp >= 0; temp--) { + int actual = batt_table[temp]; + if ((actual - res) >= 0) + break; + } + + if (temp < 3) { + if (temp == 2) + temp = -1; + else if (temp == 1) + temp = -2; + else + temp = -3; + } + return temp + 1; +} + +void print_charge_info(void) +{ + printf("\n"); + + printf("Charger main battery voltage\t\t %4u mV\n", pm_battery_voltage()); + + printf("Charger current\t\t\t\t %4u mA\n", pm_battery_current()); + + printf("\n"); + + printf("Battery temperature (if using Logic battery)\t%2u deg C\n", pm_battery_temp()); + +} + +int twl4030_info(void) +{ + u8 val; + int i; + + twl4030_i2c_write_u8(TWL4030_CHIP_INTBR, 0x90, TWL4030_INTBR_GPBR1); + + twl4030_i2c_write_u8(TWL4030_CHIP_MADC, 0x01, 0x00); + + twl4030_i2c_write_u8(TWL4030_CHIP_MADC, 0xff, 0x06); + + twl4030_i2c_write_u8(TWL4030_CHIP_MADC, 0xff, 0x07); + + twl4030_i2c_write_u8(TWL4030_CHIP_MADC, 0x02, 0x97); + + twl4030_i2c_write_u8(TWL4030_CHIP_MADC, 0x20, 0x12); + + udelay(200000); + + for (i=0; i<sizeof(adc_regs)/sizeof(adc_regs[0]); ++i) { + u8 lsb, msb; + u32 adc, v; + + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &lsb, adc_regs[i].reg_base); + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &msb, adc_regs[i].reg_base+1); + adc = lsb / 64 + msb * 4; + v = (adc * adc_regs[i].mul) / 1000; + printf("ADC%d %s\tvalue 0x%03x = %4u mV\n", adc_regs[i].idx, adc_regs[i].str, adc, v); + } + + twl4030_i2c_read_u8(TWL4030_CHIP_MADC, &val, 0x76); + + for (i=0; i<sizeof(charge_state)/sizeof(charge_state[0]);++i) { + if (val == charge_state[i].val) { + printf("Charge state \t\t\tvalue 0x%02x = %s\n", val, charge_state[i].str); + if (charge_state[i].pr_info) { + print_charge_info(); + } + break; + } + } + if (i >= sizeof(charge_state)/sizeof(charge_state[0])) { + printf("Charge state \t\t\tvalue 0x%02x = %s\n", val, "Unknown charge state"); + } + return 0; +} + + +int adc_cmd(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + char *cmd; + + if (argc < 2) + goto usage; + + cmd = argv[1]; + if (strcmp(cmd, "enable") == 0) + return twl4030_enable_charging(); + if (strcmp(cmd, "info") == 0) + return twl4030_info(); +usage: + return cmd_usage(cmdtp); +} + +U_BOOT_CMD( + madc, 2, 1, adc_cmd, + "MADC subsytem", + "madc enable - enable charging\n" + "madc info - print information on ADC registers/charging state" +); +#endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 086dc053834..691d55602e9 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -41,6 +41,8 @@ COBJS-$(CONFIG_SED156X) += sed156x.o COBJS-$(CONFIG_VIDEO_SM501) += sm501.o COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o +COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o + COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/video/omap3_dss.c b/drivers/video/omap3_dss.c new file mode 100644 index 00000000000..6b33250d3a5 --- /dev/null +++ b/drivers/video/omap3_dss.c @@ -0,0 +1,343 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Syed Mohammed Khasim <khasim <at> ti.com> + * + * Referred to Linux Kernel DSS driver files for OMAP3 by + * Tomi Valkeinen from drivers/video/omap2/dss/ + * + * 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's version 2 and any + * later version the License. + * + * 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> +#include <asm/io.h> +#include <asm/arch/dss.h> +#include <asm/arch/sys_proto.h> + +#ifdef DEBUG +#define DSS_DBG_CLK(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define DSS_DBG_CLK(fmt, ...) +#endif + +/* + * Configure VENC for a given Mode (NTSC / PAL) + */ +void omap3_dss_venc_config(const struct venc_regs *venc_cfg, + u32 height, u32 width) +{ + struct venc_regs *venc = (struct venc_regs *) OMAP3_VENC_BASE; + struct dss_regs *dss = (struct dss_regs *) OMAP3_DSS_BASE; + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + + writel(venc_cfg->status, &venc->status); + writel(venc_cfg->f_control, &venc->f_control); + writel(venc_cfg->vidout_ctrl, &venc->vidout_ctrl); + writel(venc_cfg->sync_ctrl, &venc->sync_ctrl); + writel(venc_cfg->llen, &venc->llen); + writel(venc_cfg->flens, &venc->flens); + writel(venc_cfg->hfltr_ctrl, &venc->hfltr_ctrl); + writel(venc_cfg->cc_carr_wss_carr, &venc->cc_carr_wss_carr); + writel(venc_cfg->c_phase, &venc->c_phase); + writel(venc_cfg->gain_u, &venc->gain_u); + writel(venc_cfg->gain_v, &venc->gain_v); + writel(venc_cfg->gain_y, &venc->gain_y); + writel(venc_cfg->black_level, &venc->black_level); + writel(venc_cfg->blank_level, &venc->blank_level); + writel(venc_cfg->x_color, &venc->x_color); + writel(venc_cfg->m_control, &venc->m_control); + writel(venc_cfg->bstamp_wss_data, &venc->bstamp_wss_data); + writel(venc_cfg->s_carr, &venc->s_carr); + writel(venc_cfg->line21, &venc->line21); + writel(venc_cfg->ln_sel, &venc->ln_sel); + writel(venc_cfg->l21__wc_ctl, &venc->l21__wc_ctl); + writel(venc_cfg->htrigger_vtrigger, &venc->htrigger_vtrigger); + writel(venc_cfg->savid__eavid, &venc->savid__eavid); + writel(venc_cfg->flen__fal, &venc->flen__fal); + writel(venc_cfg->lal__phase_reset, &venc->lal__phase_reset); + writel(venc_cfg->hs_int_start_stop_x, + &venc->hs_int_start_stop_x); + writel(venc_cfg->hs_ext_start_stop_x, + &venc->hs_ext_start_stop_x); + writel(venc_cfg->vs_int_start_x, + &venc->vs_int_start_x); + writel(venc_cfg->vs_int_stop_x__vs_int_start_y, + &venc->vs_int_stop_x__vs_int_start_y); + writel(venc_cfg->vs_int_stop_y__vs_ext_start_x, + &venc->vs_int_stop_y__vs_ext_start_x); + writel(venc_cfg->vs_ext_stop_x__vs_ext_start_y, + &venc->vs_ext_stop_x__vs_ext_start_y); + writel(venc_cfg->vs_ext_stop_y, + &venc->vs_ext_stop_y); + writel(venc_cfg->avid_start_stop_x, + &venc->avid_start_stop_x); + writel(venc_cfg->avid_start_stop_y, + &venc->avid_start_stop_y); + writel(venc_cfg->fid_int_start_x__fid_int_start_y, + &venc->fid_int_start_x__fid_int_start_y); + writel(venc_cfg->fid_int_offset_y__fid_ext_start_x, + &venc->fid_int_offset_y__fid_ext_start_x); + writel(venc_cfg->fid_ext_start_y__fid_ext_offset_y, + &venc->fid_ext_start_y__fid_ext_offset_y); + writel(venc_cfg->tvdetgp_int_start_stop_x, + &venc->tvdetgp_int_start_stop_x); + writel(venc_cfg->tvdetgp_int_start_stop_y, + &venc->tvdetgp_int_start_stop_y); + writel(venc_cfg->gen_ctrl, &venc->gen_ctrl); + writel(venc_cfg->output_control, &venc->output_control); + writel(venc_cfg->dac_b__dac_c, &venc->dac_b__dac_c); + + /* Configure DSS for VENC Settings */ + writel(VENC_DSS_CONFIG, &dss->control); + + /* Configure height and width for Digital out */ + writel(((height << DIG_LPP_SHIFT) | width), &dispc->size_dig); +} + +/* + * Configure Panel Specific Parameters + */ +void omap3_dss_panel_config(const struct panel_config *panel_cfg) +{ + struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + int ret; + u32 divisor, cm_clksel_dss; + u32 fck_div; + + /* Calculate timing of DISPC_DIVISOR; LCD in 16:23, PCD in 0:7 */ + ret = omap3_dss_calc_divisor(panel_cfg->panel_type == 1, + panel_cfg->pixel_clock * 1000, &divisor, &fck_div); + DSS_DBG_CLK("%s: Need to program CM_CLKSEL_DSS:clksel_dss1 to %u !\n", __FUNCTION__, fck_div); + cm_clksel_dss = readl(&prcm_base->clksel_dss); + DSS_DBG_CLK("%s: &cm_clksel_dss %p val %#x\n", __FUNCTION__, &prcm_base->clksel_dss, cm_clksel_dss); + cm_clksel_dss &= ~0x3f; /* clear CLKSELDSS1 */ + cm_clksel_dss |= fck_div; /* or in new clksel_dss1 */ + writel(cm_clksel_dss, &prcm_base->clksel_dss); + DSS_DBG_CLK("%s: cm_clksel_dss %#x\n", __FUNCTION__, cm_clksel_dss); + + writel(panel_cfg->timing_h, &dispc->timing_h); + writel(panel_cfg->timing_v, &dispc->timing_v); + writel(panel_cfg->pol_freq, &dispc->pol_freq); +#if 1 + writel(divisor, &dispc->divisor); +#else + writel(panel_cfg->divisor, &dispc->divisor); +#endif + writel(panel_cfg->lcd_size, &dispc->size_lcd); + writel((panel_cfg->load_mode << FRAME_MODE_SHIFT), &dispc->config); + writel(((panel_cfg->panel_type << TFTSTN_SHIFT) | + (panel_cfg->data_lines << DATALINES_SHIFT)), &dispc->control); + writel(panel_cfg->panel_color, &dispc->default_color0); +} + +struct dss_clock_info { + /* rates that we get with dividers below */ + unsigned long fck; + + /* dividers */ + u16 fck_div; +}; + +struct dispc_clock_info { + /* rates that we get with dividers below */ + unsigned long lck; + unsigned long pck; + + /* dividers */ + u16 lck_div; + u16 pck_div; +}; + +static inline int abs(int x) +{ + if (x < 0) + return -x; + return x; +} + +/* with fck as input clock rate, find dispc dividers that produce req_pck */ +void dispc_find_clk_divs(int is_tft, unsigned long req_pck, unsigned long fck, + struct dispc_clock_info *cinfo) +{ + u16 pcd_min = is_tft ? 2 : 3; + unsigned long best_pck; + u16 best_ld, cur_ld; + u16 best_pd, cur_pd; + + best_pck = 0; + best_ld = 0; + best_pd = 0; + + for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { + unsigned long lck = fck / cur_ld; + + for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { + unsigned long pck = lck / cur_pd; + long old_delta, new_delta; + + old_delta = abs(best_pck - req_pck); + new_delta = abs(pck - req_pck); + + if (best_pck == 0 || new_delta < old_delta) { + DSS_DBG_CLK("%s: cur_ld %u cur_pd %u old_delta %ld new_delta %ld\n", __FUNCTION__, cur_ld, cur_pd, old_delta, new_delta); + + best_pck = pck; + best_ld = cur_ld; + best_pd = cur_pd; + + DSS_DBG_CLK("%s: best_ld %u best_pd %u\n", __FUNCTION__, best_ld, best_pd); + + if (pck == req_pck) + goto found; + } + + if (pck < req_pck) + break; + } + + if (lck / pcd_min < req_pck) + break; + } + +found: + cinfo->lck_div = best_ld; + cinfo->pck_div = best_pd; + cinfo->lck = fck / cinfo->lck_div; + cinfo->pck = cinfo->lck / cinfo->pck_div; + DSS_DBG_CLK("%s: %d best_ld %u best_pd %u pck %lu\n", __FUNCTION__, __LINE__, best_ld, best_pd, cinfo->pck); +} + +int omap3_dss_calc_divisor(int is_tft, unsigned int req_pck, + unsigned int *dispc_divisor, + unsigned int *result_fck_div) +{ + unsigned long prate; + u16 fck_div, fck_div_max, fck_min_div = 1, fck_div_factor; + int min_fck_per_pck; + unsigned long fck, max_dss_fck = 173000000; /* max DSS VP_CLK */ + u32 cpu_family = get_cpu_family(); + struct dispc_clock_info cur_dispc; + struct dss_clock_info best_dss; + struct dispc_clock_info best_dispc; + + prate = 864000000; /* Fclk of DSS (864Mhz) */ + + memset(&best_dss, 0, sizeof(best_dss)); + memset(&best_dispc, 0, sizeof(best_dispc)); + + min_fck_per_pck = 1; + +#ifdef CONFIG_OMAP44XX + fck_div_max = 32; + fck_div_factor = 2; +#else + fck_div_max = 16; + fck_div_factor = 1; +#endif + + for (fck_div = fck_div_max; fck_div >= fck_min_div; --fck_div) { + DSS_DBG_CLK("%s:%d fck_div %d\n", __FUNCTION__, __LINE__, fck_div); + fck = prate / fck_div * fck_div_factor; + + if (fck > max_dss_fck) { + DSS_DBG_CLK("%s:%d fck %lu > max_dss_fck %lu\n", __FUNCTION__, __LINE__, fck, max_dss_fck); + continue; + } + + if (min_fck_per_pck && + fck < req_pck * min_fck_per_pck) { + DSS_DBG_CLK("%s:%d\n", __FUNCTION__, __LINE__); + continue; + } + + dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); + + DSS_DBG_CLK("%s:%d cur.pck %u < best_pck %u?\n", __FUNCTION__, __LINE__, + abs(cur_dispc.pck - req_pck), + abs(best_dispc.pck - req_pck)); + + if (abs(cur_dispc.pck - req_pck) < + abs(best_dispc.pck - req_pck)) { + + DSS_DBG_CLK("%s:%d yes fck %lu fck_div %u\n", __FUNCTION__, __LINE__, fck, fck_div); + best_dss.fck = fck; + best_dss.fck_div = fck_div; + + best_dispc = cur_dispc; + + if (cur_dispc.pck == req_pck) + break; + } + } + + /* Setup divisor */ + *dispc_divisor = (cur_dispc.lck_div << 16) | cur_dispc.pck_div; + DSS_DBG_CLK("%s: fck_div %u best_dss.fck_div %u\n", __FUNCTION__, fck_div, best_dss.fck_div); + *result_fck_div = best_dss.fck_div; + return 0; +} + +/* + * Enable LCD and DIGITAL OUT in DSS + */ +void omap3_dss_enable(void) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + u32 l = 0; + + l = readl(&dispc->control); + l |= DISPC_ENABLE; + writel(l, &dispc->control); +} + +#ifdef CONFIG_DSS_DUMP + +#define DUMP_IT(offset) printf("%p = %08x " #offset "\n", &dispc->offset, readl(&dispc->offset)) + +void omap3_dss_dump(void) +{ + struct dispc_regs *dispc = (struct dispc_regs *) OMAP3_DISPC_BASE; + struct prcm *prcm_base = (struct prcm *)PRCM_BASE; + + DUMP_IT(control); + DUMP_IT(config); + DUMP_IT(timing_h); + DUMP_IT(timing_v); + DUMP_IT(pol_freq); + DUMP_IT(divisor); + DUMP_IT(size_lcd); + DUMP_IT(gfx_attributes); + DUMP_IT(default_color0); + printf("%p = %08x cd_clksel_dss\n", &prcm_base->clksel_dss, readl(&prcm_base->clksel_dss)); +} + +static int do_dss_dump (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + omap3_dss_dump(); + return (0); +} + +U_BOOT_CMD( + dss_dump, 1, 1, do_dss_dump, + "dump DSS registers", + "" +); + +#endif diff --git a/fs/Makefile b/fs/Makefile index 22aad126bc2..f32931a7071 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -29,6 +29,7 @@ subdirs-$(CONFIG_CMD_FDOS) += fdos subdirs-$(CONFIG_CMD_JFFS2) += jffs2 subdirs-$(CONFIG_CMD_REISER) += reiserfs subdirs-$(CONFIG_YAFFS2) += yaffs2 +subdirs-$(CONFIG_YAFFS2_NEW) += yaffs2-new subdirs-$(CONFIG_CMD_UBIFS) += ubifs SUBDIRS := $(subdirs-y) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index c450bf69244..5b312993c34 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -277,7 +277,7 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry) */ static int get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, - unsigned long size) + unsigned long size, __u8 *start_buffer) { __u32 idx = 0; __u32 startsect; @@ -291,24 +291,52 @@ get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); - if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) { - debug("Error reading data\n"); - return -1; - } - if (size % FS_BLOCK_SIZE) { - __u8 tmpbuf[FS_BLOCK_SIZE]; + if (start_buffer) { + while (size >= FS_BLOCK_SIZE) { + if (disk_read(startsect, 1, buffer) < 0) { + debug("Error reading data\n"); + return -1; + } + buffer += FS_BLOCK_SIZE; + startsect++; + size -= FS_BLOCK_SIZE; + lcd_percent_update(buffer - start_buffer); + } + if (size % FS_BLOCK_SIZE) { + __u8 tmpbuf[FS_BLOCK_SIZE]; - idx = size / FS_BLOCK_SIZE; - if (disk_read(startsect + idx, 1, tmpbuf) < 0) { + idx = size / FS_BLOCK_SIZE; + if (disk_read(startsect, 1, tmpbuf) < 0) { + debug("Error reading data\n"); + return -1; + } + memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); + + buffer += size % FS_BLOCK_SIZE; + lcd_percent_update(buffer - start_buffer); + return 0; + } + } else { + if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) { debug("Error reading data\n"); return -1; } - buffer += idx * FS_BLOCK_SIZE; + lcd_percent_update(buffer + (size & ~FS_BLOCK_SIZE) - start_buffer); + if (size % FS_BLOCK_SIZE) { + __u8 tmpbuf[FS_BLOCK_SIZE]; - memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); - return 0; - } + idx = size / FS_BLOCK_SIZE; + if (disk_read(startsect + idx, 1, tmpbuf) < 0) { + debug("Error reading data\n"); + return -1; + } + buffer += idx * FS_BLOCK_SIZE; + memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); + lcd_percent_update(buffer + size - start_buffer); + return 0; + } + } return 0; } @@ -326,7 +354,9 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, __u32 curclust = START(dentptr); __u32 endclust, newclust; unsigned long actsize; + __u8 *start_buffer = buffer; + lcd_percent_init(filesize); debug("Filesize: %ld bytes\n", filesize); if (maxsize > 0 && filesize > maxsize) @@ -348,6 +378,7 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, debug("Invalid FAT entry\n"); return gotsize; } + lcd_percent_update(actsize); endclust = newclust; actsize += bytesperclust; } @@ -356,7 +387,7 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, actsize -= bytesperclust; /* get remaining clusters */ - if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + if (get_cluster(mydata, curclust, buffer, (int)actsize, start_buffer) != 0) { printf("Error reading cluster\n"); return -1; } @@ -366,20 +397,23 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, filesize -= actsize; buffer += actsize; actsize = filesize; - if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { + lcd_percent_update(gotsize); + if (get_cluster(mydata, endclust, buffer, (int)actsize, start_buffer) != 0) { printf("Error reading cluster\n"); return -1; } gotsize += actsize; + lcd_percent_update(gotsize); return gotsize; getit: - if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { + if (get_cluster(mydata, curclust, buffer, (int)actsize, start_buffer) != 0) { printf("Error reading cluster\n"); return -1; } gotsize += (int)actsize; filesize -= actsize; buffer += actsize; + lcd_percent_update(gotsize); curclust = get_fatent(mydata, endclust); if (CHECK_CLUST(curclust, mydata->fatsize)) { @@ -473,7 +507,7 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, } if (get_cluster(mydata, curclust, get_vfatname_block, - mydata->clust_size * SECTOR_SIZE) != 0) { + mydata->clust_size * SECTOR_SIZE, NULL) != 0) { debug("Error: reading directory block\n"); return -1; } @@ -555,7 +589,7 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, int i; if (get_cluster(mydata, curclust, get_dentfromdir_block, - mydata->clust_size * SECTOR_SIZE) != 0) { + mydata->clust_size * SECTOR_SIZE, NULL) != 0) { debug("Error: reading directory block\n"); return NULL; } diff --git a/fs/yaffs2-new/Makefile b/fs/yaffs2-new/Makefile new file mode 100644 index 00000000000..2ed148c0267 --- /dev/null +++ b/fs/yaffs2-new/Makefile @@ -0,0 +1,63 @@ +# Makefile for YAFFS direct test +# +# +# YAFFS: Yet another Flash File System. A NAND-flash specific file system. +# +# Copyright (C) 2003 Aleph One Ltd. +# +# +# Created by Charles Manning <charles@aleph1.co.uk> +# +# 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. +# +# NB Warning this Makefile does not include header dependencies. +# +# $Id: Makefile,v 1.15 2007/07/18 19:40:38 charles Exp $ + +#EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC +include $(TOPDIR)/config.mk + +LIB = $(obj)libyaffs2-new.o + +COBJS-$(CONFIG_YAFFS2_NEW) := \ + yaffs_uboot.o yaffs_guts.o yaffs_summary.o yaffs_yaffs2.o \ + yaffs_allocator.o yaffs_nand.o yaffs_verify.o yaffs_nameval.o \ + yaffs_checkptrw.o yaffs_tagscompat.o yaffs_bitmap.o yaffs_attribs.o \ + yaffs_packedtags2.o yaffs_qsort.o yaffs_yaffs1.o yaffs_ecc.o \ + yaffs_mtdif.o yaffs_mtdif2_single.o \ + yaffsfs.o yaffsfs_errno.o + +#COBJS-$(CONFIG_YAFFS2) := \ +# yaffscfg.o yaffs_ecc.o yaffsfs.o yaffs_guts.o yaffs_packedtags1.o \ +# yaffs_tagscompat.o yaffs_packedtags2.o yaffs_tagsvalidity.o \ +# yaffs_nand.o yaffs_checkptrw.o yaffs_qsort.o yaffs_mtdif.o \ +# yaffs_mtdif2.o + +SRCS := $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) + +# -DCONFIG_YAFFS_NO_YAFFS1 +CFLAGS += -DCONFIG_YAFFS_DIRECT -DCONFIG_YAFFS_SHORT_NAMES_IN_RAM -DCONFIG_YAFFS_YAFFS2 -DNO_Y_INLINE -DLINUX_VERSION_CODE=0x20622 + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +.PHONY: clean distclean +clean: + rm -f $(OBJS) + +distclean: clean + rm -f $(LIB) core *.bak .depend + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/fs/yaffs2-new/devextras.h b/fs/yaffs2-new/devextras.h new file mode 100644 index 00000000000..b0147b85a43 --- /dev/null +++ b/fs/yaffs2-new/devextras.h @@ -0,0 +1,113 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This file is just holds extra declarations used during development. + * Most of these are from kernel includes placed here so we can use them in + * applications. + * + */ + +#ifndef __EXTRAS_H__ +#define __EXTRAS_H__ + +#if defined WIN32 +#define __inline__ __inline +#define new newHack +#endif + +/* XXX U-BOOT XXX */ +#if 1 /* !(defined __KERNEL__) || (defined WIN32) */ + +/* User space defines */ + +/* XXX U-BOOT XXX */ +#if 0 +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned __u32; +#endif + +#include <asm/types.h> +#include <linux/list.h> + +/* + * File types + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#ifndef WIN32 +/* XXX U-BOOT XXX */ +#if 0 +#include <sys/stat.h> +#else +#include "common.h" +#endif +#endif + +/* + * Attribute flags. These should be or-ed together to figure out what + * has been changed! + */ +#define ATTR_MODE 1 +#define ATTR_UID 2 +#define ATTR_GID 4 +#define ATTR_SIZE 8 +#define ATTR_ATIME 16 +#define ATTR_MTIME 32 +#define ATTR_CTIME 64 +#define ATTR_ATIME_SET 128 +#define ATTR_MTIME_SET 256 +#define ATTR_FORCE 512 /* Not a change, but a change it */ +#define ATTR_ATTR_FLAG 1024 + +struct iattr { + unsigned int ia_valid; + unsigned ia_mode; + unsigned ia_uid; + unsigned ia_gid; + unsigned ia_size; + unsigned ia_atime; + unsigned ia_mtime; + unsigned ia_ctime; + unsigned int ia_attr_flags; +}; + +#define KERN_DEBUG + +#else + +#ifndef WIN32 +#include <linux/types.h> +#include <linux/list.h> +#include <linux/fs.h> +#include <linux/stat.h> +#endif + +#endif + +#if defined WIN32 +#undef new +#endif + +#endif diff --git a/fs/yaffs2-new/yaffs_allocator.c b/fs/yaffs2-new/yaffs_allocator.c new file mode 100644 index 00000000000..c8f2861c53e --- /dev/null +++ b/fs/yaffs2-new/yaffs_allocator.c @@ -0,0 +1,357 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_allocator.h" +#include "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yportenv.h" + +/* + * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks + * of approx 100 objects that are themn allocated singly. + * This is basically a simplified slab allocator. + * + * We don't use the Linux slab allocator because slab does not allow + * us to dump all the objects in one hit when we do a umount and tear + * down all the tnodes and objects. slab requires that we first free + * the individual objects. + * + * Once yaffs has been mainlined I shall try to motivate for a change + * to slab to provide the extra features we need here. + */ + +struct yaffs_tnode_list { + struct yaffs_tnode_list *next; + struct yaffs_tnode *tnodes; +}; + +struct yaffs_obj_list { + struct yaffs_obj_list *next; + struct yaffs_obj *objects; +}; + +struct yaffs_allocator { + int n_tnodes_created; + struct yaffs_tnode *free_tnodes; + int n_free_tnodes; + struct yaffs_tnode_list *alloc_tnode_list; + + int n_obj_created; + struct list_head free_objs; + int n_free_objects; + + struct yaffs_obj_list *allocated_obj_list; +}; + +static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + struct yaffs_tnode_list *tmp; + + if (!allocator) { + BUG(); + return; + } + + while (allocator->alloc_tnode_list) { + tmp = allocator->alloc_tnode_list->next; + + kfree(allocator->alloc_tnode_list->tnodes); + kfree(allocator->alloc_tnode_list); + allocator->alloc_tnode_list = tmp; + } + + allocator->free_tnodes = NULL; + allocator->n_free_tnodes = 0; + allocator->n_tnodes_created = 0; +} + +static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + allocator->alloc_tnode_list = NULL; + allocator->free_tnodes = NULL; + allocator->n_free_tnodes = 0; + allocator->n_tnodes_created = 0; +} + +static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + int i; + struct yaffs_tnode *new_tnodes; + u8 *mem; + struct yaffs_tnode *curr; + struct yaffs_tnode *next; + struct yaffs_tnode_list *tnl; + + if (!allocator) { + BUG(); + return YAFFS_FAIL; + } + + if (n_tnodes < 1) + return YAFFS_OK; + + /* make these things */ + new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); + mem = (u8 *) new_tnodes; + + if (!new_tnodes) { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs: Could not allocate Tnodes"); + return YAFFS_FAIL; + } + + /* New hookup for wide tnodes */ + for (i = 0; i < n_tnodes - 1; i++) { + curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; + next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; + curr->internal[0] = next; + } + + curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; + curr->internal[0] = allocator->free_tnodes; + allocator->free_tnodes = (struct yaffs_tnode *)mem; + + allocator->n_free_tnodes += n_tnodes; + allocator->n_tnodes_created += n_tnodes; + + /* Now add this bunch of tnodes to a list for freeing up. + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ + tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); + if (!tnl) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Could not add tnodes to management list"); + return YAFFS_FAIL; + } else { + tnl->tnodes = new_tnodes; + tnl->next = allocator->alloc_tnode_list; + allocator->alloc_tnode_list = tnl; + } + + yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); + + return YAFFS_OK; +} + +struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = + (struct yaffs_allocator *)dev->allocator; + struct yaffs_tnode *tn = NULL; + + if (!allocator) { + BUG(); + return NULL; + } + + /* If there are none left make more */ + if (!allocator->free_tnodes) + yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); + + if (allocator->free_tnodes) { + tn = allocator->free_tnodes; + allocator->free_tnodes = allocator->free_tnodes->internal[0]; + allocator->n_free_tnodes--; + } + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + if (tn) { + tn->internal[0] = allocator->free_tnodes; + allocator->free_tnodes = tn; + allocator->n_free_tnodes++; + } + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +/*--------------- yaffs_obj alloaction ------------------------ + * + * Free yaffs_objs are stored in a list using obj->siblings. + * The blocks of allocated objects are stored in a linked list. + */ + +static void yaffs_init_raw_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + allocator->allocated_obj_list = NULL; + INIT_LIST_HEAD(&allocator->free_objs); + allocator->n_free_objects = 0; +} + +static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator = dev->allocator; + struct yaffs_obj_list *tmp; + + if (!allocator) { + BUG(); + return; + } + + while (allocator->allocated_obj_list) { + tmp = allocator->allocated_obj_list->next; + kfree(allocator->allocated_obj_list->objects); + kfree(allocator->allocated_obj_list); + allocator->allocated_obj_list = tmp; + } + + INIT_LIST_HEAD(&allocator->free_objs); + allocator->n_free_objects = 0; + allocator->n_obj_created = 0; +} + +static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) +{ + struct yaffs_allocator *allocator = dev->allocator; + int i; + struct yaffs_obj *new_objs; + struct yaffs_obj_list *list; + + if (!allocator) { + BUG(); + return YAFFS_FAIL; + } + + if (n_obj < 1) + return YAFFS_OK; + + /* make these things */ + new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); + list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); + + if (!new_objs || !list) { + kfree(new_objs); + new_objs = NULL; + kfree(list); + list = NULL; + yaffs_trace(YAFFS_TRACE_ALLOCATE, + "Could not allocate more objects"); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ + for (i = 0; i < n_obj; i++) + list_add(&new_objs[i].siblings, &allocator->free_objs); + + allocator->n_free_objects += n_obj; + allocator->n_obj_created += n_obj; + + /* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = new_objs; + list->next = allocator->allocated_obj_list; + allocator->allocated_obj_list = list; + + return YAFFS_OK; +} + +struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj = NULL; + struct list_head *lh; + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return obj; + } + + /* If there are none left make more */ + if (list_empty(&allocator->free_objs)) + yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); + + if (!list_empty(&allocator->free_objs)) { + lh = allocator->free_objs.next; + obj = list_entry(lh, struct yaffs_obj, siblings); + list_del_init(lh); + allocator->n_free_objects--; + } + + return obj; +} + +void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) +{ + + struct yaffs_allocator *allocator = dev->allocator; + + if (!allocator) { + BUG(); + return; + } + + /* Link into the free list. */ + list_add(&obj->siblings, &allocator->free_objs); + allocator->n_free_objects++; +} + +void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) +{ + + if (!dev->allocator) { + BUG(); + return; + } + + yaffs_deinit_raw_tnodes(dev); + yaffs_deinit_raw_objs(dev); + kfree(dev->allocator); + dev->allocator = NULL; +} + +void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) +{ + struct yaffs_allocator *allocator; + + if (dev->allocator) { + BUG(); + return; + } + + allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); + if (allocator) { + dev->allocator = allocator; + yaffs_init_raw_tnodes(dev); + yaffs_init_raw_objs(dev); + } +} + diff --git a/fs/yaffs2-new/yaffs_allocator.h b/fs/yaffs2-new/yaffs_allocator.h new file mode 100644 index 00000000000..a8cc3226421 --- /dev/null +++ b/fs/yaffs2-new/yaffs_allocator.h @@ -0,0 +1,30 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_ALLOCATOR_H__ +#define __YAFFS_ALLOCATOR_H__ + +#include "yaffs_guts.h" + +void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); +void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); + +struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); +void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); + +struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); +void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); + +#endif diff --git a/fs/yaffs2-new/yaffs_attribs.c b/fs/yaffs2-new/yaffs_attribs.c new file mode 100644 index 00000000000..fe914e558b6 --- /dev/null +++ b/fs/yaffs2-new/yaffs_attribs.c @@ -0,0 +1,124 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_guts.h" +#include "yaffs_attribs.h" + +void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) +{ + obj->yst_uid = oh->yst_uid; + obj->yst_gid = oh->yst_gid; + obj->yst_atime = oh->yst_atime; + obj->yst_mtime = oh->yst_mtime; + obj->yst_ctime = oh->yst_ctime; + obj->yst_rdev = oh->yst_rdev; +} + +void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) +{ + oh->yst_uid = obj->yst_uid; + oh->yst_gid = obj->yst_gid; + oh->yst_atime = obj->yst_atime; + oh->yst_mtime = obj->yst_mtime; + oh->yst_ctime = obj->yst_ctime; + oh->yst_rdev = obj->yst_rdev; + +} + +void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) +{ + obj->yst_mtime = Y_CURRENT_TIME; + if (do_a) + obj->yst_atime = obj->yst_mtime; + if (do_c) + obj->yst_ctime = obj->yst_mtime; +} + +void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) +{ + yaffs_load_current_time(obj, 1, 1); + obj->yst_rdev = rdev; + obj->yst_uid = uid; + obj->yst_gid = gid; +} + +loff_t yaffs_get_file_size(struct yaffs_obj *obj) +{ + YCHAR *alias = NULL; + obj = yaffs_get_equivalent_obj(obj); + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return obj->variant.file_variant.file_size; + case YAFFS_OBJECT_TYPE_SYMLINK: + alias = obj->variant.symlink_variant.alias; + if (!alias) + return 0; + return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); + default: + return 0; + } +} + +int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + + if (valid & ATTR_MODE) + obj->yst_mode = attr->ia_mode; + if (valid & ATTR_UID) + obj->yst_uid = attr->ia_uid; + if (valid & ATTR_GID) + obj->yst_gid = attr->ia_gid; + + if (valid & ATTR_ATIME) + obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); + if (valid & ATTR_CTIME) + obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); + if (valid & ATTR_MTIME) + obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); + + if (valid & ATTR_SIZE) + yaffs_resize_file(obj, attr->ia_size); + + yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); + + return YAFFS_OK; + +} + +int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) +{ + unsigned int valid = 0; + + attr->ia_mode = obj->yst_mode; + valid |= ATTR_MODE; + attr->ia_uid = obj->yst_uid; + valid |= ATTR_UID; + attr->ia_gid = obj->yst_gid; + valid |= ATTR_GID; + + Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; + valid |= ATTR_ATIME; + Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; + valid |= ATTR_CTIME; + Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; + valid |= ATTR_MTIME; + + attr->ia_size = yaffs_get_file_size(obj); + valid |= ATTR_SIZE; + + attr->ia_valid = valid; + + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_attribs.h b/fs/yaffs2-new/yaffs_attribs.h new file mode 100644 index 00000000000..5b21b085b73 --- /dev/null +++ b/fs/yaffs2-new/yaffs_attribs.h @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_ATTRIBS_H__ +#define __YAFFS_ATTRIBS_H__ + +#include "yaffs_guts.h" + +void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); +void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); +void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); +void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); +int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); +int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); + +#endif diff --git a/fs/yaffs2-new/yaffs_bitmap.c b/fs/yaffs2-new/yaffs_bitmap.c new file mode 100644 index 00000000000..4440e930d6b --- /dev/null +++ b/fs/yaffs2-new/yaffs_bitmap.c @@ -0,0 +1,97 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_bitmap.h" +#include "yaffs_trace.h" +/* + * Chunk bitmap manipulations + */ + +static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "BlockBits block %d is not valid", + blk); + BUG(); + } + return dev->chunk_bits + + (dev->chunk_bit_stride * (blk - dev->internal_start_block)); +} + +void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block || + chunk < 0 || chunk >= dev->param.chunks_per_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Chunk Id (%d:%d) invalid", + blk, chunk); + BUG(); + } +} + +void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + memset(blk_bits, 0, dev->chunk_bit_stride); +} + +void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); +} + +void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + blk_bits[chunk / 8] |= (1 << (chunk & 7)); +} + +int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + + yaffs_verify_chunk_bit_id(dev, blk, chunk); + return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; +} + +int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + int i; + + for (i = 0; i < dev->chunk_bit_stride; i++) { + if (*blk_bits) + return 1; + blk_bits++; + } + return 0; +} + +int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) +{ + u8 *blk_bits = yaffs_block_bits(dev, blk); + int i; + int n = 0; + + for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) + n += hweight8(*blk_bits); + + return n; +} diff --git a/fs/yaffs2-new/yaffs_bitmap.h b/fs/yaffs2-new/yaffs_bitmap.h new file mode 100644 index 00000000000..e26b37d89ae --- /dev/null +++ b/fs/yaffs2-new/yaffs_bitmap.h @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * Chunk bitmap manipulations + */ + +#ifndef __YAFFS_BITMAP_H__ +#define __YAFFS_BITMAP_H__ + +#include "yaffs_guts.h" + +void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); +void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); +void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); +int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); +int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); + +#endif diff --git a/fs/yaffs2-new/yaffs_checkptrw.c b/fs/yaffs2-new/yaffs_checkptrw.c new file mode 100644 index 00000000000..997a618aee8 --- /dev/null +++ b/fs/yaffs2-new/yaffs_checkptrw.c @@ -0,0 +1,408 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_checkptrw.h" +#include "yaffs_getblockinfo.h" + +static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) +{ + int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpt blocks_avail = %d", blocks_avail); + + return (blocks_avail <= 0) ? 0 : 1; +} + +static int yaffs_checkpt_erase(struct yaffs_dev *dev) +{ + int i; + + if (!dev->param.erase_fn) + return 0; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checking blocks %d to %d", + dev->internal_start_block, dev->internal_end_block); + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); + if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "erasing checkpt block %d", i); + + dev->n_erasures++; + + if (dev->param. + erase_fn(dev, + i - dev->block_offset /* realign */)) { + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + dev->n_free_chunks += + dev->param.chunks_per_block; + } else { + dev->param.bad_block_fn(dev, i); + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + } + } + } + + dev->blocks_in_checkpt = 0; + + return 1; +} + +static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) +{ + int i; + int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "allocating checkpt block: erased %d reserved %d avail %d next %d ", + dev->n_erased_blocks, dev->param.n_reserved_blocks, + blocks_avail, dev->checkpt_next_block); + + if (dev->checkpt_next_block >= 0 && + dev->checkpt_next_block <= dev->internal_end_block && + blocks_avail > 0) { + + for (i = dev->checkpt_next_block; i <= dev->internal_end_block; + i++) { + struct yaffs_block_info *bi = + yaffs_get_block_info(dev, i); + if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + dev->checkpt_next_block = i + 1; + dev->checkpt_cur_block = i; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "allocating checkpt block %d", i); + return; + } + } + } + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); + + dev->checkpt_next_block = -1; + dev->checkpt_cur_block = -1; +} + +static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) +{ + int i; + struct yaffs_ext_tags tags; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "find next checkpt block: start: blocks %d next %d", + dev->blocks_in_checkpt, dev->checkpt_next_block); + + if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) + for (i = dev->checkpt_next_block; i <= dev->internal_end_block; + i++) { + int chunk = i * dev->param.chunks_per_block; + int realigned_chunk = chunk - dev->chunk_offset; + + dev->param.read_chunk_tags_fn(dev, realigned_chunk, + NULL, &tags); + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "find next checkpt block: search: block %d oid %d seq %d eccr %d", + i, tags.obj_id, tags.seq_number, + tags.ecc_result); + + if (tags.seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) { + /* Right kind of block */ + dev->checkpt_next_block = tags.obj_id; + dev->checkpt_cur_block = i; + dev->checkpt_block_list[dev-> + blocks_in_checkpt] = i; + dev->blocks_in_checkpt++; + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "found checkpt block %d", i); + return; + } + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); + + dev->checkpt_next_block = -1; + dev->checkpt_cur_block = -1; +} + +int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) +{ + int i; + + dev->checkpt_open_write = writing; + + /* Got the functions we need? */ + if (!dev->param.write_chunk_tags_fn || + !dev->param.read_chunk_tags_fn || + !dev->param.erase_fn || !dev->param.bad_block_fn) + return 0; + + if (writing && !yaffs2_checkpt_space_ok(dev)) + return 0; + + if (!dev->checkpt_buffer) + dev->checkpt_buffer = + kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + if (!dev->checkpt_buffer) + return 0; + + dev->checkpt_page_seq = 0; + dev->checkpt_byte_count = 0; + dev->checkpt_sum = 0; + dev->checkpt_xor = 0; + dev->checkpt_cur_block = -1; + dev->checkpt_cur_chunk = -1; + dev->checkpt_next_block = dev->internal_start_block; + + /* Erase all the blocks in the checkpoint area */ + if (writing) { + memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); + dev->checkpt_byte_offs = 0; + return yaffs_checkpt_erase(dev); + } + + /* Set to a value that will kick off a read */ + dev->checkpt_byte_offs = dev->data_bytes_per_chunk; + /* A checkpoint block list of 1 checkpoint block per 16 block is + * (hopefully) going to be way more than we need */ + dev->blocks_in_checkpt = 0; + dev->checkpt_max_blocks = + (dev->internal_end_block - dev->internal_start_block) / 16 + 2; + dev->checkpt_block_list = + kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); + + if (!dev->checkpt_block_list) + return 0; + + for (i = 0; i < dev->checkpt_max_blocks; i++) + dev->checkpt_block_list[i] = -1; + + return 1; +} + +int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) +{ + u32 composite_sum; + + composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); + *sum = composite_sum; + return 1; +} + +static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) +{ + int chunk; + int realigned_chunk; + struct yaffs_ext_tags tags; + + if (dev->checkpt_cur_block < 0) { + yaffs2_checkpt_find_erased_block(dev); + dev->checkpt_cur_chunk = 0; + } + + if (dev->checkpt_cur_block < 0) + return 0; + + tags.is_deleted = 0; + tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ + tags.chunk_id = dev->checkpt_page_seq + 1; + tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; + tags.n_bytes = dev->data_bytes_per_chunk; + if (dev->checkpt_cur_chunk == 0) { + /* First chunk we write for the block? Set block state to + checkpoint */ + struct yaffs_block_info *bi = + yaffs_get_block_info(dev, dev->checkpt_cur_block); + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + dev->blocks_in_checkpt++; + } + + chunk = + dev->checkpt_cur_block * dev->param.chunks_per_block + + dev->checkpt_cur_chunk; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", + chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, + tags.obj_id, tags.chunk_id); + + realigned_chunk = chunk - dev->chunk_offset; + + dev->n_page_writes++; + + dev->param.write_chunk_tags_fn(dev, realigned_chunk, + dev->checkpt_buffer, &tags); + dev->checkpt_byte_offs = 0; + dev->checkpt_page_seq++; + dev->checkpt_cur_chunk++; + if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { + dev->checkpt_cur_chunk = 0; + dev->checkpt_cur_block = -1; + } + memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); + + return 1; +} + +int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) +{ + int i = 0; + int ok = 1; + u8 *data_bytes = (u8 *) data; + + if (!dev->checkpt_buffer) + return 0; + + if (!dev->checkpt_open_write) + return -1; + + while (i < n_bytes && ok) { + dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; + dev->checkpt_sum += *data_bytes; + dev->checkpt_xor ^= *data_bytes; + + dev->checkpt_byte_offs++; + i++; + data_bytes++; + dev->checkpt_byte_count++; + + if (dev->checkpt_byte_offs < 0 || + dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) + ok = yaffs2_checkpt_flush_buffer(dev); + } + + return i; +} + +int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) +{ + int i = 0; + int ok = 1; + struct yaffs_ext_tags tags; + int chunk; + int realigned_chunk; + u8 *data_bytes = (u8 *) data; + + if (!dev->checkpt_buffer) + return 0; + + if (dev->checkpt_open_write) + return -1; + + while (i < n_bytes && ok) { + + if (dev->checkpt_byte_offs < 0 || + dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { + + if (dev->checkpt_cur_block < 0) { + yaffs2_checkpt_find_block(dev); + dev->checkpt_cur_chunk = 0; + } + + if (dev->checkpt_cur_block < 0) { + ok = 0; + break; + } + + chunk = dev->checkpt_cur_block * + dev->param.chunks_per_block + + dev->checkpt_cur_chunk; + + realigned_chunk = chunk - dev->chunk_offset; + dev->n_page_reads++; + + /* read in the next chunk */ + dev->param.read_chunk_tags_fn(dev, + realigned_chunk, + dev->checkpt_buffer, + &tags); + + if (tags.chunk_id != (dev->checkpt_page_seq + 1) || + tags.ecc_result > YAFFS_ECC_RESULT_FIXED || + tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { + ok = 0; + break; + } + + dev->checkpt_byte_offs = 0; + dev->checkpt_page_seq++; + dev->checkpt_cur_chunk++; + + if (dev->checkpt_cur_chunk >= + dev->param.chunks_per_block) + dev->checkpt_cur_block = -1; + } + + *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; + dev->checkpt_sum += *data_bytes; + dev->checkpt_xor ^= *data_bytes; + dev->checkpt_byte_offs++; + i++; + data_bytes++; + dev->checkpt_byte_count++; + } + + return i; +} + +int yaffs_checkpt_close(struct yaffs_dev *dev) +{ + int i; + + if (dev->checkpt_open_write) { + if (dev->checkpt_byte_offs != 0) + yaffs2_checkpt_flush_buffer(dev); + } else if (dev->checkpt_block_list) { + for (i = 0; + i < dev->blocks_in_checkpt && + dev->checkpt_block_list[i] >= 0; i++) { + int blk = dev->checkpt_block_list[i]; + struct yaffs_block_info *bi = NULL; + + if (dev->internal_start_block <= blk && + blk <= dev->internal_end_block) + bi = yaffs_get_block_info(dev, blk); + if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + } + kfree(dev->checkpt_block_list); + dev->checkpt_block_list = NULL; + } + + dev->n_free_chunks -= + dev->blocks_in_checkpt * dev->param.chunks_per_block; + dev->n_erased_blocks -= dev->blocks_in_checkpt; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", + dev->checkpt_byte_count); + + if (dev->checkpt_buffer) { + /* free the buffer */ + kfree(dev->checkpt_buffer); + dev->checkpt_buffer = NULL; + return 1; + } else { + return 0; + } +} + +int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) +{ + /* Erase the checkpoint data */ + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "checkpoint invalidate of %d blocks", + dev->blocks_in_checkpt); + + return yaffs_checkpt_erase(dev); +} diff --git a/fs/yaffs2-new/yaffs_checkptrw.h b/fs/yaffs2-new/yaffs_checkptrw.h new file mode 100644 index 00000000000..cdbaba7153e --- /dev/null +++ b/fs/yaffs2-new/yaffs_checkptrw.h @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CHECKPTRW_H__ +#define __YAFFS_CHECKPTRW_H__ + +#include "yaffs_guts.h" + +int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); + +int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); + +int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); + +int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); + +int yaffs_checkpt_close(struct yaffs_dev *dev); + +int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2-new/yaffs_direct.h b/fs/yaffs2-new/yaffs_direct.h new file mode 100644 index 00000000000..28008a608d4 --- /dev/null +++ b/fs/yaffs2-new/yaffs_direct.h @@ -0,0 +1,8 @@ + +struct yaffs_direct_context { + struct yaffs_dev *dev; + u8 *spare_buffer; +}; + +#define yaffs_dev_to_lc(dev) ((struct yaffs_direct_context *)((dev)->os_context)) +#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) diff --git a/fs/yaffs2-new/yaffs_ecc.c b/fs/yaffs2-new/yaffs_ecc.c new file mode 100644 index 00000000000..9294107c150 --- /dev/null +++ b/fs/yaffs2-new/yaffs_ecc.c @@ -0,0 +1,281 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +/* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two + * such ECC blocks are used on a 512-byte NAND page. + * + */ + +#include "yportenv.h" + +#include "yaffs_ecc.h" + +/* Table generated by gen-ecc.c + * Using a table means we do not have to calculate p1..p4 and p1'..p4' + * for each byte of data. These are instead provided in a table in bits7..2. + * Bit 0 of each entry indicates whether the entry has an odd or even parity, + * and therefore this bytes influence on the line parity. + */ + +static const unsigned char column_parity_table[] = { + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, +}; + + +/* Calculate the ECC for a 256-byte block of data */ +void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) +{ + unsigned int i; + unsigned char col_parity = 0; + unsigned char line_parity = 0; + unsigned char line_parity_prime = 0; + unsigned char t; + unsigned char b; + + for (i = 0; i < 256; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + } + + ecc[2] = (~col_parity) | 0x03; + + t = 0; + if (line_parity & 0x80) + t |= 0x80; + if (line_parity_prime & 0x80) + t |= 0x40; + if (line_parity & 0x40) + t |= 0x20; + if (line_parity_prime & 0x40) + t |= 0x10; + if (line_parity & 0x20) + t |= 0x08; + if (line_parity_prime & 0x20) + t |= 0x04; + if (line_parity & 0x10) + t |= 0x02; + if (line_parity_prime & 0x10) + t |= 0x01; + ecc[1] = ~t; + + t = 0; + if (line_parity & 0x08) + t |= 0x80; + if (line_parity_prime & 0x08) + t |= 0x40; + if (line_parity & 0x04) + t |= 0x20; + if (line_parity_prime & 0x04) + t |= 0x10; + if (line_parity & 0x02) + t |= 0x08; + if (line_parity_prime & 0x02) + t |= 0x04; + if (line_parity & 0x01) + t |= 0x02; + if (line_parity_prime & 0x01) + t |= 0x01; + ecc[0] = ~t; + +} + +/* Correct the ECC on a 256 byte block of data */ + +int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc) +{ + unsigned char d0, d1, d2; /* deltas */ + + d0 = read_ecc[0] ^ test_ecc[0]; + d1 = read_ecc[1] ^ test_ecc[1]; + d2 = read_ecc[2] ^ test_ecc[2]; + + if ((d0 | d1 | d2) == 0) + return 0; /* no error */ + + if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && + ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && + ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { + /* Single bit (recoverable) error in data */ + + unsigned byte; + unsigned bit; + + bit = byte = 0; + + if (d1 & 0x80) + byte |= 0x80; + if (d1 & 0x20) + byte |= 0x40; + if (d1 & 0x08) + byte |= 0x20; + if (d1 & 0x02) + byte |= 0x10; + if (d0 & 0x80) + byte |= 0x08; + if (d0 & 0x20) + byte |= 0x04; + if (d0 & 0x08) + byte |= 0x02; + if (d0 & 0x02) + byte |= 0x01; + + if (d2 & 0x80) + bit |= 0x04; + if (d2 & 0x20) + bit |= 0x02; + if (d2 & 0x08) + bit |= 0x01; + + data[byte] ^= (1 << bit); + + return 1; /* Corrected the error */ + } + + if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { + /* Reccoverable error in ecc */ + + read_ecc[0] = test_ecc[0]; + read_ecc[1] = test_ecc[1]; + read_ecc[2] = test_ecc[2]; + + return 1; /* Corrected the error */ + } + + /* Unrecoverable error */ + + return -1; + +} + +/* + * ECCxxxOther does ECC calcs on arbitrary n bytes of data + */ +void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *ecc_other) +{ + unsigned int i; + unsigned char col_parity = 0; + unsigned line_parity = 0; + unsigned line_parity_prime = 0; + unsigned char b; + + for (i = 0; i < n_bytes; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { + /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + ecc_other->col_parity = (col_parity >> 2) & 0x3f; + ecc_other->line_parity = line_parity; + ecc_other->line_parity_prime = line_parity_prime; +} + +int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *read_ecc, + const struct yaffs_ecc_other *test_ecc) +{ + unsigned char delta_col; /* column parity delta */ + unsigned delta_line; /* line parity delta */ + unsigned delta_line_prime; /* line parity delta */ + unsigned bit; + + delta_col = read_ecc->col_parity ^ test_ecc->col_parity; + delta_line = read_ecc->line_parity ^ test_ecc->line_parity; + delta_line_prime = + read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; + + if ((delta_col | delta_line | delta_line_prime) == 0) + return 0; /* no error */ + + if (delta_line == ~delta_line_prime && + (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { + /* Single bit (recoverable) error in data */ + + bit = 0; + + if (delta_col & 0x20) + bit |= 0x04; + if (delta_col & 0x08) + bit |= 0x02; + if (delta_col & 0x02) + bit |= 0x01; + + if (delta_line >= n_bytes) + return -1; + + data[delta_line] ^= (1 << bit); + + return 1; /* corrected */ + } + + if ((hweight32(delta_line) + + hweight32(delta_line_prime) + + hweight8(delta_col)) == 1) { + /* Reccoverable error in ecc */ + + *read_ecc = *test_ecc; + return 1; /* corrected */ + } + + /* Unrecoverable error */ + + return -1; +} diff --git a/fs/yaffs2-new/yaffs_ecc.h b/fs/yaffs2-new/yaffs_ecc.h new file mode 100644 index 00000000000..17d47bd80f3 --- /dev/null +++ b/fs/yaffs2-new/yaffs_ecc.h @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. + * Thus, two such ECC blocks are used on a 512-byte NAND page. + * + */ + +#ifndef __YAFFS_ECC_H__ +#define __YAFFS_ECC_H__ + +struct yaffs_ecc_other { + unsigned char col_parity; + unsigned line_parity; + unsigned line_parity_prime; +}; + +void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); +int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc); + +void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *ecc); +int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, + struct yaffs_ecc_other *read_ecc, + const struct yaffs_ecc_other *test_ecc); +#endif diff --git a/fs/yaffs2-new/yaffs_getblockinfo.h b/fs/yaffs2-new/yaffs_getblockinfo.h new file mode 100644 index 00000000000..8fd0802bddd --- /dev/null +++ b/fs/yaffs2-new/yaffs_getblockinfo.h @@ -0,0 +1,35 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GETBLOCKINFO_H__ +#define __YAFFS_GETBLOCKINFO_H__ + +#include "yaffs_guts.h" +#include "yaffs_trace.h" + +/* Function to manipulate block info */ +static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev + *dev, int blk) +{ + if (blk < dev->internal_start_block || blk > dev->internal_end_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs: get_block_info block %d is not valid", + blk); + BUG(); + } + return &dev->block_info[blk - dev->internal_start_block]; +} + +#endif diff --git a/fs/yaffs2-new/yaffs_guts.c b/fs/yaffs2-new/yaffs_guts.c new file mode 100644 index 00000000000..ec95812ba63 --- /dev/null +++ b/fs/yaffs2-new/yaffs_guts.c @@ -0,0 +1,4991 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 */ +#include <common.h> +#include "linux/stat.h" + + +#include "yportenv.h" +#include "yaffs_trace.h" + +#include "yaffs_guts.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_tagscompat.h" +#include "yaffs_nand.h" +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h" +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" +#include "yaffs_attribs.h" +#include "yaffs_summary.h" + +/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ +#define YAFFS_GC_GOOD_ENOUGH 2 +#define YAFFS_GC_PASSIVE_THRESHOLD 4 + +#include "yaffs_ecc.h" + +/* Forward declarations */ + +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, + const u8 *buffer, int n_bytes, int use_reserve); + + + +/* Function to calculate chunk and offset */ + +static inline void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, + int *chunk_out, u32 *offset_out) +{ + int chunk; + u32 offset; + + chunk = (u32) (addr >> dev->chunk_shift); + + if (dev->chunk_div == 1) { + /* easy power of 2 case */ + offset = (u32) (addr & dev->chunk_mask); + } else { + /* Non power-of-2 case */ + + loff_t chunk_base; + + chunk /= dev->chunk_div; + + chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; + offset = (u32) (addr - chunk_base); + } + + *chunk_out = chunk; + *offset_out = offset; +} + +/* Function to return the number of shifts for a power of 2 greater than or + * equal to the given number + * Note we don't try to cater for all possible numbers and this does not have to + * be hellishly efficient. + */ + +static inline u32 calc_shifts_ceiling(u32 x) +{ + int extra_bits; + int shifts; + + shifts = extra_bits = 0; + + while (x > 1) { + if (x & 1) + extra_bits++; + x >>= 1; + shifts++; + } + + if (extra_bits) + shifts++; + + return shifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 + */ + +static inline u32 calc_shifts(u32 x) +{ + u32 shifts; + + shifts = 0; + + if (!x) + return 0; + + while (!(x & 1)) { + x >>= 1; + shifts++; + } + + return shifts; +} + +/* + * Temporary buffer manipulations. + */ + +static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) +{ + int i; + u8 *buf = (u8 *) 1; + + memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); + + for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { + dev->temp_buffer[i].in_use = 0; + buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + dev->temp_buffer[i].buffer = buf; + } + + return buf ? YAFFS_OK : YAFFS_FAIL; +} + +u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) +{ + int i; + + dev->temp_in_use++; + if (dev->temp_in_use > dev->max_temp) + dev->max_temp = dev->temp_in_use; + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->temp_buffer[i].in_use == 0) { + dev->temp_buffer[i].in_use = 1; + return dev->temp_buffer[i].buffer; + } + } + + yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); + /* + * If we got here then we have to allocate an unmanaged one + * This is not good. + */ + + dev->unmanaged_buffer_allocs++; + return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); + +} + +void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) +{ + int i; + + dev->temp_in_use--; + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->temp_buffer[i].buffer == buffer) { + dev->temp_buffer[i].in_use = 0; + return; + } + } + + if (buffer) { + /* assume it is an unmanaged one. */ + yaffs_trace(YAFFS_TRACE_BUFFERS, "Releasing unmanaged temp buffer"); + kfree(buffer); + dev->unmanaged_buffer_deallocs++; + } + +} + +/* + * Determine if we have a managed buffer. + */ +int yaffs_is_managed_tmp_buffer(struct yaffs_dev *dev, const u8 *buffer) +{ + int i; + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->temp_buffer[i].buffer == buffer) + return 1; + } + + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].data == buffer) + return 1; + } + + if (buffer == dev->checkpt_buffer) + return 1; + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: unmaged buffer detected."); + return 0; +} + +/* + * Functions for robustisizing TODO + * + */ + +static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, + const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + data = data; + tags = tags; +} + +static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, + const struct yaffs_ext_tags *tags) +{ + dev = dev; + nand_chunk = nand_chunk; + tags = tags; +} + +void yaffs_handle_chunk_error(struct yaffs_dev *dev, + struct yaffs_block_info *bi) +{ + if (!bi->gc_prioritise) { + bi->gc_prioritise = 1; + dev->has_pending_prioritised_gc = 1; + bi->chunk_error_strikes++; + + if (bi->chunk_error_strikes > 3) { + bi->needs_retiring = 1; /* Too many stikes, so retire */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Block struck out"); + + } + } +} + +static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, + int erased_ok) +{ + int flash_block = nand_chunk / dev->param.chunks_per_block; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); + + yaffs_handle_chunk_error(dev, bi); + + if (erased_ok) { + /* Was an actual write failure, + * so mark the block for retirement.*/ + bi->needs_retiring = 1; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Block %d needs retiring", flash_block); + } + + /* Delete the chunk */ + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + yaffs_skip_rest_of_block(dev); +} + +/* + * Verification code + */ + +/* + * Simple hash function. Needs to have a reasonable spread + */ + +static inline int yaffs_hash_fn(int n) +{ + n = abs(n); + return n % YAFFS_NOBJECT_BUCKETS; +} + +/* + * Access functions to useful fake objects. + * Note that root might have a presence in NAND if permissions are set. + */ + +struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) +{ + return dev->root_dir; +} + +struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) +{ + return dev->lost_n_found; +} + +/* + * Erased NAND checking functions + */ + +int yaffs_check_ff(u8 *buffer, int n_bytes) +{ + /* Horrible, slow implementation */ + while (n_bytes--) { + if (*buffer != 0xff) + return 0; + buffer++; + } + return 1; +} + +static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) +{ + int retval = YAFFS_OK; + u8 *data = yaffs_get_temp_buffer(dev); + struct yaffs_ext_tags tags; + int result; + + result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); + + if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) + retval = YAFFS_FAIL; + + if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || + tags.chunk_used) { + yaffs_trace(YAFFS_TRACE_NANDACCESS, + "Chunk %d not erased", nand_chunk); + retval = YAFFS_FAIL; + } + + yaffs_release_temp_buffer(dev, data); + + return retval; + +} + +static int yaffs_verify_chunk_written(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, + struct yaffs_ext_tags *tags) +{ + int retval = YAFFS_OK; + struct yaffs_ext_tags temp_tags; + u8 *buffer = yaffs_get_temp_buffer(dev); + int result; + + result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); + if (memcmp(buffer, data, dev->data_bytes_per_chunk) || + temp_tags.obj_id != tags->obj_id || + temp_tags.chunk_id != tags->chunk_id || + temp_tags.n_bytes != tags->n_bytes) + retval = YAFFS_FAIL; + + yaffs_release_temp_buffer(dev, buffer); + + return retval; +} + + +int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) +{ + int reserved_chunks; + int reserved_blocks = dev->param.n_reserved_blocks; + int checkpt_blocks; + + checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); + + reserved_chunks = + (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; + + return (dev->n_free_chunks > (reserved_chunks + n_chunks)); +} + +static int yaffs_find_alloc_block(struct yaffs_dev *dev) +{ + int i; + struct yaffs_block_info *bi; + + if (dev->n_erased_blocks < 1) { + /* Hoosterman we've got a problem. + * Can't get space to gc + */ + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: no more erased blocks"); + + return -1; + } + + /* Find an empty block. */ + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + dev->alloc_block_finder++; + if (dev->alloc_block_finder < dev->internal_start_block + || dev->alloc_block_finder > dev->internal_end_block) { + dev->alloc_block_finder = dev->internal_start_block; + } + + bi = yaffs_get_block_info(dev, dev->alloc_block_finder); + + if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->seq_number++; + bi->seq_number = dev->seq_number; + dev->n_erased_blocks--; + yaffs_trace(YAFFS_TRACE_ALLOCATE, + "Allocated block %d, seq %d, %d left" , + dev->alloc_block_finder, dev->seq_number, + dev->n_erased_blocks); + return dev->alloc_block_finder; + } + } + + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs tragedy: no more erased blocks, but there should have been %d", + dev->n_erased_blocks); + + return -1; +} + +static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, + struct yaffs_block_info **block_ptr) +{ + int ret_val; + struct yaffs_block_info *bi; + + if (dev->alloc_block < 0) { + /* Get next block to allocate off */ + dev->alloc_block = yaffs_find_alloc_block(dev); + dev->alloc_page = 0; + } + + if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { + /* No space unless we're allowed to use the reserve. */ + return -1; + } + + if (dev->n_erased_blocks < dev->param.n_reserved_blocks + && dev->alloc_page == 0) + yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); + + /* Next page please.... */ + if (dev->alloc_block >= 0) { + bi = yaffs_get_block_info(dev, dev->alloc_block); + + ret_val = (dev->alloc_block * dev->param.chunks_per_block) + + dev->alloc_page; + bi->pages_in_use++; + yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); + + dev->alloc_page++; + + dev->n_free_chunks--; + + /* If the block is full set the state to full */ + if (dev->alloc_page >= dev->param.chunks_per_block) { + bi->block_state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + + if (block_ptr) + *block_ptr = bi; + + return ret_val; + } + + yaffs_trace(YAFFS_TRACE_ERROR, + "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); + + return -1; +} + +static int yaffs_get_erased_chunks(struct yaffs_dev *dev) +{ + int n; + + n = dev->n_erased_blocks * dev->param.chunks_per_block; + + if (dev->alloc_block > 0) + n += (dev->param.chunks_per_block - dev->alloc_page); + + return n; + +} + +/* + * yaffs_skip_rest_of_block() skips over the rest of the allocation block + * if we don't want to write to it. + */ +void yaffs_skip_rest_of_block(struct yaffs_dev *dev) +{ + struct yaffs_block_info *bi; + + if (dev->alloc_block > 0) { + bi = yaffs_get_block_info(dev, dev->alloc_block); + if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + bi->block_state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + } +} + +static int yaffs_write_new_chunk(struct yaffs_dev *dev, + const u8 *data, + struct yaffs_ext_tags *tags, int use_reserver) +{ + int attempts = 0; + int write_ok = 0; + int chunk; + + yaffs2_checkpt_invalidate(dev); + + do { + struct yaffs_block_info *bi = 0; + int erased_ok = 0; + + chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * Check the first page we try to write in a block. + * If the check passes then we don't need to check any + * more. If the check fails, we check again... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * chunk due to power loss. This checking policy should + * catch that case with very few checks and thus save a + * lot of checks that are most likely not needed. + * + * Mods to the above + * If an erase check fails or the write fails we skip the + * rest of the block. + */ + + /* let's give it a try */ + attempts++; + + if (dev->param.always_check_erased) + bi->skip_erased_check = 0; + + if (!bi->skip_erased_check) { + erased_ok = yaffs_check_chunk_erased(dev, chunk); + if (erased_ok != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs chunk %d was not erased", + chunk); + + /* If not erased, delete this one, + * skip rest of block and + * try another chunk */ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + yaffs_skip_rest_of_block(dev); + continue; + } + } + + write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); + + if (!bi->skip_erased_check) + write_ok = + yaffs_verify_chunk_written(dev, chunk, data, tags); + + if (write_ok != YAFFS_OK) { + /* Clean up aborted write, skip to next block and + * try another chunk */ + yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); + continue; + } + + bi->skip_erased_check = 1; + + /* Copy the data into the robustification buffer */ + yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); + + } while (write_ok != YAFFS_OK && + (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); + + if (!write_ok) + chunk = -1; + + if (attempts > 1) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs write required %d attempts", + attempts); + dev->n_retried_writes += (attempts - 1); + } + + return chunk; +} + +/* + * Block retiring for handling a broken block. + */ + +static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); + + yaffs2_checkpt_invalidate(dev); + + yaffs2_clear_oldest_dirty_seq(dev, bi); + + if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { + if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Failed to mark bad and erase block %d", + flash_block); + } else { + struct yaffs_ext_tags tags; + int chunk_id = + flash_block * dev->param.chunks_per_block; + + u8 *buffer = yaffs_get_temp_buffer(dev); + + memset(buffer, 0xff, dev->data_bytes_per_chunk); + memset(&tags, 0, sizeof(tags)); + tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; + if (dev->param.write_chunk_tags_fn(dev, chunk_id - + dev->chunk_offset, + buffer, + &tags) != YAFFS_OK) + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Failed to write bad block marker to block %d", + flash_block); + + yaffs_release_temp_buffer(dev, buffer); + } + } + + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + bi->gc_prioritise = 0; + bi->needs_retiring = 0; + + dev->n_retired_blocks++; +} + +/*---------------- Name handling functions ------------*/ + +static u16 yaffs_calc_name_sum(const YCHAR *name) +{ + u16 sum = 0; + u16 i = 1; + + if (!name) + return 0; + + while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { + + /* 0x1f mask is case insensitive */ + sum += ((*name) & 0x1f) * i; + i++; + name++; + } + return sum; +} + +void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) +{ + memset(obj->short_name, 0, sizeof(obj->short_name)); + if (name && + strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= + YAFFS_SHORT_NAME_LENGTH) + strcpy(obj->short_name, name); + else + obj->short_name[0] = _Y('\0'); + obj->sum = yaffs_calc_name_sum(name); +} + +void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, + const struct yaffs_obj_hdr *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; + memset(tmp_name, 0, sizeof(tmp_name)); + yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, + YAFFS_MAX_NAME_LENGTH + 1); + yaffs_set_obj_name(obj, tmp_name); +#else + yaffs_set_obj_name(obj, oh->name); +#endif +} + +/*-------------------- TNODES ------------------- + + * List of spare tnodes + * The list is hooked together using the first pointer + * in the tnode. + */ + +struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) +{ + struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); + + if (tn) { + memset(tn, 0, dev->tnode_size); + dev->n_tnodes++; + } + + dev->checkpoint_blocks_required = 0; /* force recalculation */ + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) +{ + yaffs_free_raw_tnode(dev, tn); + dev->n_tnodes--; + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) +{ + yaffs_deinit_raw_tnodes_and_objs(dev); + dev->n_obj = 0; + dev->n_tnodes = 0; +} + +void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos, unsigned val) +{ + u32 *map = (u32 *) tn; + u32 bit_in_map; + u32 bit_in_word; + u32 word_in_map; + u32 mask; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + val >>= dev->chunk_grp_bits; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map / 32; + bit_in_word = bit_in_map & (32 - 1); + + mask = dev->tnode_mask << bit_in_word; + + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val << bit_in_word)); + + if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + mask = + dev->tnode_mask >> bit_in_word; + map[word_in_map] &= ~mask; + map[word_in_map] |= (mask & (val >> bit_in_word)); + } +} + +u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos) +{ + u32 *map = (u32 *) tn; + u32 bit_in_map; + u32 bit_in_word; + u32 word_in_map; + u32 val; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + + bit_in_map = pos * dev->tnode_width; + word_in_map = bit_in_map / 32; + bit_in_word = bit_in_map & (32 - 1); + + val = map[word_in_map] >> bit_in_word; + + if (dev->tnode_width > (32 - bit_in_word)) { + bit_in_word = (32 - bit_in_word); + word_in_map++; + val |= (map[word_in_map] << bit_in_word); + } + + val &= dev->tnode_mask; + val <<= dev->chunk_grp_bits; + + return val; +} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ + * The look up tree is represented by the top tnode and the number of top_level + * in the tree. 0 means only the level 0 tnode is in the tree. + */ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ +struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id) +{ + struct yaffs_tnode *tn = file_struct->top; + u32 i; + int required_depth; + int level = file_struct->top_level; + + dev = dev; + + /* Check sane level and chunk Id */ + if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) + return NULL; + + if (chunk_id > YAFFS_MAX_CHUNK_ID) + return NULL; + + /* First check we're tall enough (ie enough top_level) */ + + i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (i) { + i >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + if (required_depth > file_struct->top_level) + return NULL; /* Not tall enough, so we can't find it */ + + /* Traverse down to level 0 */ + while (level > 0 && tn) { + tn = tn->internal[(chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (level - 1) * + YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK]; + level--; + } + + return tn; +} + +/* add_find_tnode_0 finds the level 0 tnode if it exists, + * otherwise first expands the tree. + * This happens in two steps: + * 1. If the tree isn't tall enough, then make it taller. + * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. + * + * Used when modifying the tree. + * + * If the tn argument is NULL, then a fresh tnode will be added otherwise the + * specified tn will be plugged into the ttree. + */ + +struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id, + struct yaffs_tnode *passed_tn) +{ + int required_depth; + int i; + int l; + struct yaffs_tnode *tn; + u32 x; + + /* Check sane level and page Id */ + if (file_struct->top_level < 0 || + file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) + return NULL; + + if (chunk_id > YAFFS_MAX_CHUNK_ID) + return NULL; + + /* First check we're tall enough (ie enough top_level) */ + + x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (x) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + if (required_depth > file_struct->top_level) { + /* Not tall enough, gotta make the tree taller */ + for (i = file_struct->top_level; i < required_depth; i++) { + + tn = yaffs_get_tnode(dev); + + if (tn) { + tn->internal[0] = file_struct->top; + file_struct->top = tn; + file_struct->top_level++; + } else { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs: no more tnodes"); + return NULL; + } + } + } + + /* Traverse down to level 0, adding anything we need */ + + l = file_struct->top_level; + tn = file_struct->top; + + if (l > 0) { + while (l > 0 && tn) { + x = (chunk_id >> + (YAFFS_TNODES_LEVEL0_BITS + + (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK; + + if ((l > 1) && !tn->internal[x]) { + /* Add missing non-level-zero tnode */ + tn->internal[x] = yaffs_get_tnode(dev); + if (!tn->internal[x]) + return NULL; + } else if (l == 1) { + /* Looking from level 1 at level 0 */ + if (passed_tn) { + /* If we already have one, release it */ + if (tn->internal[x]) + yaffs_free_tnode(dev, + tn->internal[x]); + tn->internal[x] = passed_tn; + + } else if (!tn->internal[x]) { + /* Don't have one, none passed in */ + tn->internal[x] = yaffs_get_tnode(dev); + if (!tn->internal[x]) + return NULL; + } + } + + tn = tn->internal[x]; + l--; + } + } else { + /* top is level 0 */ + if (passed_tn) { + memcpy(tn, passed_tn, + (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); + yaffs_free_tnode(dev, passed_tn); + } + } + + return tn; +} + +static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, + int chunk_obj) +{ + return (tags->chunk_id == chunk_obj && + tags->obj_id == obj_id && + !tags->is_deleted) ? 1 : 0; + +} + +static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, + struct yaffs_ext_tags *tags, int obj_id, + int inode_chunk) +{ + int j; + + for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { + if (yaffs_check_chunk_bit + (dev, the_chunk / dev->param.chunks_per_block, + the_chunk % dev->param.chunks_per_block)) { + + if (dev->chunk_grp_size == 1) + return the_chunk; + else { + yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, + tags); + if (yaffs_tags_match(tags, + obj_id, inode_chunk)) { + /* found it; */ + return the_chunk; + } + } + } + the_chunk++; + } + return -1; +} + +static int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + struct yaffs_ext_tags *tags) +{ + /*Get the Tnode, then get the level 0 offset chunk offset */ + struct yaffs_tnode *tn; + int the_chunk = -1; + struct yaffs_ext_tags local_tags; + int ret_val = -1; + struct yaffs_dev *dev = in->my_dev; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); + + if (!tn) + return ret_val; + + the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); + + ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, + inode_chunk); + return ret_val; +} + +static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, + struct yaffs_ext_tags *tags) +{ + /* Get the Tnode, then get the level 0 offset chunk offset */ + struct yaffs_tnode *tn; + int the_chunk = -1; + struct yaffs_ext_tags local_tags; + struct yaffs_dev *dev = in->my_dev; + int ret_val = -1; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &local_tags; + } + + tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); + + if (!tn) + return ret_val; + + the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); + + ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, + inode_chunk); + + /* Delete the entry in the filestructure (if found) */ + if (ret_val != -1) + yaffs_load_tnode_0(dev, tn, inode_chunk, 0); + + return ret_val; +} + +int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + int nand_chunk, int in_scan) +{ + /* NB in_scan is zero unless scanning. + * For forward scanning, in_scan is > 0; + * for backward scanning in_scan is < 0 + * + * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. + */ + + struct yaffs_tnode *tn; + struct yaffs_dev *dev = in->my_dev; + int existing_cunk; + struct yaffs_ext_tags existing_tags; + struct yaffs_ext_tags new_tags; + unsigned existing_serial, new_serial; + + if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { + /* Just ignore an attempt at putting a chunk into a non-file + * during scanning. + * If it is not during Scanning then something went wrong! + */ + if (!in_scan) { + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy:attempt to put data chunk into a non-file" + ); + BUG(); + } + + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + return YAFFS_OK; + } + + tn = yaffs_add_find_tnode_0(dev, + &in->variant.file_variant, + inode_chunk, NULL); + if (!tn) + return YAFFS_FAIL; + + if (!nand_chunk) + /* Dummy insert, bail now */ + return YAFFS_OK; + + existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); + + if (in_scan != 0) { + /* If we're scanning then we need to test for duplicates + * NB This does not need to be efficient since it should only + * happen when the power fails during a write, then only one + * chunk should ever be affected. + * + * Correction for YAFFS2: This could happen quite a lot and we + * need to think about efficiency! TODO + * Update: For backward scanning we don't need to re-read tags + * so this is quite cheap. + */ + + if (existing_cunk > 0) { + /* NB Right now existing chunk will not be real + * chunk_id if the chunk group size > 1 + * thus we have to do a FindChunkInFile to get the + * real chunk id. + * + * We have a duplicate now we need to decide which + * one to use: + * + * Backwards scanning YAFFS2: The old one is what + * we use, dump the new one. + * YAFFS1: Get both sets of tags and compare serial + * numbers. + */ + + if (in_scan > 0) { + /* Only do this for forward scanning */ + yaffs_rd_chunk_tags_nand(dev, + nand_chunk, + NULL, &new_tags); + + /* Do a proper find */ + existing_cunk = + yaffs_find_chunk_in_file(in, inode_chunk, + &existing_tags); + } + + if (existing_cunk <= 0) { + /*Hoosterman - how did this happen? */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: existing chunk < 0 in scan" + ); + + } + + /* NB The deleted flags should be false, otherwise + * the chunks will not be loaded during a scan + */ + + if (in_scan > 0) { + new_serial = new_tags.serial_number; + existing_serial = existing_tags.serial_number; + } + + if ((in_scan > 0) && + (existing_cunk <= 0 || + ((existing_serial + 1) & 3) == new_serial)) { + /* Forward scanning. + * Use new + * Delete the old one and drop through to + * update the tnode + */ + yaffs_chunk_del(dev, existing_cunk, 1, + __LINE__); + } else { + /* Backward scanning or we want to use the + * existing one + * Delete the new one and return early so that + * the tnode isn't changed + */ + yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); + return YAFFS_OK; + } + } + + } + + if (existing_cunk == 0) + in->n_data_chunks++; + + yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); + + return YAFFS_OK; +} + +static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) +{ + struct yaffs_block_info *the_block; + unsigned block_no; + + yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); + + block_no = chunk / dev->param.chunks_per_block; + the_block = yaffs_get_block_info(dev, block_no); + if (the_block) { + the_block->soft_del_pages++; + dev->n_free_chunks++; + yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all + * the chunks in the file. + * All soft deleting does is increment the block's softdelete count and pulls + * the chunk out of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks + * are soft deleted. + */ + +static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, + u32 level, int chunk_offset) +{ + int i; + int the_chunk; + int all_done = 1; + struct yaffs_dev *dev = in->my_dev; + + if (!tn) + return 1; + + if (level > 0) { + for (i = YAFFS_NTNODES_INTERNAL - 1; + all_done && i >= 0; + i--) { + if (tn->internal[i]) { + all_done = + yaffs_soft_del_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + + i); + if (all_done) { + yaffs_free_tnode(dev, + tn->internal[i]); + tn->internal[i] = NULL; + } else { + /* Can this happen? */ + } + } + } + return (all_done) ? 1 : 0; + } + + /* level 0 */ + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { + the_chunk = yaffs_get_group_base(dev, tn, i); + if (the_chunk) { + yaffs_soft_del_chunk(dev, the_chunk); + yaffs_load_tnode_0(dev, tn, i, 0); + } + } + return 1; +} + +static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev = obj->my_dev; + struct yaffs_obj *parent; + + yaffs_verify_obj_in_dir(obj); + parent = obj->parent; + + yaffs_verify_dir(parent); + + if (dev && dev->param.remove_obj_fn) + dev->param.remove_obj_fn(obj); + + list_del_init(&obj->siblings); + obj->parent = NULL; + + yaffs_verify_dir(parent); +} + +void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) +{ + if (!directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: Trying to add an object to a null pointer directory" + ); + BUG(); + return; + } + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: Trying to add an object to a non-directory" + ); + BUG(); + } + + if (obj->siblings.prev == NULL) { + /* Not initialised */ + BUG(); + } + + yaffs_verify_dir(directory); + + yaffs_remove_obj_from_dir(obj); + + /* Now add it */ + list_add(&obj->siblings, &directory->variant.dir_variant.children); + obj->parent = directory; + + if (directory == obj->my_dev->unlinked_dir + || directory == obj->my_dev->del_dir) { + obj->unlinked = 1; + obj->my_dev->n_unlinked_files++; + obj->rename_allowed = 0; + } + + yaffs_verify_dir(directory); + yaffs_verify_obj_in_dir(obj); +} + +static int yaffs_change_obj_name(struct yaffs_obj *obj, + struct yaffs_obj *new_dir, + const YCHAR *new_name, int force, int shadows) +{ + int unlink_op; + int del_op; + struct yaffs_obj *existing_target; + + if (new_dir == NULL) + new_dir = obj->parent; /* use the old directory */ + + if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_change_obj_name: new_dir is not a directory" + ); + BUG(); + } + + unlink_op = (new_dir == obj->my_dev->unlinked_dir); + del_op = (new_dir == obj->my_dev->del_dir); + + existing_target = yaffs_find_by_name(new_dir, new_name); + + /* If the object is a file going into the unlinked directory, + * then it is OK to just stuff it in since duplicate names are OK. + * else only proceed if the new name does not exist and we're putting + * it into a directory. + */ + if (!(unlink_op || del_op || force || + shadows > 0 || !existing_target) || + new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + return YAFFS_FAIL; + + yaffs_set_obj_name(obj, new_name); + obj->dirty = 1; + yaffs_add_obj_to_dir(new_dir, obj); + + if (unlink_op) + obj->unlinked = 1; + + /* If it is a deletion then we mark it as a shrink for gc */ + if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +/*------------------------ Short Operations Cache ------------------------------ + * In many situations where there is no high level buffering a lot of + * reads might be short sequential reads, and a lot of writes may be short + * sequential writes. eg. scanning/writing a jpeg file. + * In these cases, a short read/write cache can provide a huge perfomance + * benefit with dumb-as-a-rock code. + * In Linux, the page cache provides read buffering and the short op cache + * provides write buffering. + * + * There are a small number (~10) of cache chunks per device so that we don't + * need a very intelligent search. + */ + +static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev = obj->my_dev; + int i; + struct yaffs_cache *cache; + int n_caches = obj->my_dev->param.n_caches; + + for (i = 0; i < n_caches; i++) { + cache = &dev->cache[i]; + if (cache->object == obj && cache->dirty) + return 1; + } + + return 0; +} + +static void yaffs_flush_file_cache(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev = obj->my_dev; + int lowest = -99; /* Stop compiler whining. */ + int i; + struct yaffs_cache *cache; + int chunk_written = 0; + int n_caches = obj->my_dev->param.n_caches; + + if (n_caches < 1) + return; + do { + cache = NULL; + + /* Find the lowest dirty chunk for this object */ + for (i = 0; i < n_caches; i++) { + if (dev->cache[i].object == obj && + dev->cache[i].dirty) { + if (!cache || + dev->cache[i].chunk_id < lowest) { + cache = &dev->cache[i]; + lowest = cache->chunk_id; + } + } + } + + if (cache && !cache->locked) { + /* Write it out and free it up */ + chunk_written = + yaffs_wr_data_obj(cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes, 1); + cache->dirty = 0; + cache->object = NULL; + } + } while (cache && chunk_written > 0); + + if (cache) + /* Hoosterman, disk full while writing cache out. */ + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: no space during cache write"); +} + +/*yaffs_flush_whole_cache(dev) + * + * + */ + +void yaffs_flush_whole_cache(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + int n_caches = dev->param.n_caches; + int i; + + /* Find a dirty object in the cache and flush it... + * until there are no further dirty objects. + */ + do { + obj = NULL; + for (i = 0; i < n_caches && !obj; i++) { + if (dev->cache[i].object && dev->cache[i].dirty) + obj = dev->cache[i].object; + } + if (obj) + yaffs_flush_file_cache(obj); + } while (obj); + +} + +/* Grab us a cache chunk for use. + * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) +{ + int i; + + if (dev->param.n_caches > 0) { + for (i = 0; i < dev->param.n_caches; i++) { + if (!dev->cache[i].object) + return &dev->cache[i]; + } + } + return NULL; +} + +static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) +{ + struct yaffs_cache *cache; + struct yaffs_obj *the_obj; + int usage; + int i; + int pushout; + + if (dev->param.n_caches < 1) + return NULL; + + /* Try find a non-dirty one... */ + + cache = yaffs_grab_chunk_worker(dev); + + if (!cache) { + /* They were all dirty, find the LRU object and flush + * its cache, then find again. + * NB what's here is not very accurate, + * we actually flush the object with the LRU chunk. + */ + + /* With locking we can't assume we can use entry zero, + * Set the_obj to a valid pointer for Coverity. */ + the_obj = dev->cache[0].object; + usage = -1; + cache = NULL; + pushout = -1; + + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object && + !dev->cache[i].locked && + (dev->cache[i].last_use < usage || + !cache)) { + usage = dev->cache[i].last_use; + the_obj = dev->cache[i].object; + cache = &dev->cache[i]; + pushout = i; + } + } + + if (!cache || cache->dirty) { + /* Flush and try again */ + yaffs_flush_file_cache(the_obj); + cache = yaffs_grab_chunk_worker(dev); + } + } + return cache; +} + +/* Find a cached chunk */ +static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, + int chunk_id) +{ + struct yaffs_dev *dev = obj->my_dev; + int i; + + if (dev->param.n_caches < 1) + return NULL; + + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object == obj && + dev->cache[i].chunk_id == chunk_id) { + dev->cache_hits++; + + return &dev->cache[i]; + } + } + return NULL; +} + +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, + int is_write) +{ + int i; + + if (dev->param.n_caches < 1) + return; + + if (dev->cache_last_use < 0 || + dev->cache_last_use > 100000000) { + /* Reset the cache usages */ + for (i = 1; i < dev->param.n_caches; i++) + dev->cache[i].last_use = 0; + + dev->cache_last_use = 0; + } + dev->cache_last_use++; + cache->last_use = dev->cache_last_use; + + if (is_write) + cache->dirty = 1; +} + +/* Invalidate a single cache page. + * Do this when a whole page gets written, + * ie the short cache for this page is no longer valid. + */ +static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) +{ + struct yaffs_cache *cache; + + if (object->my_dev->param.n_caches > 0) { + cache = yaffs_find_chunk_cache(object, chunk_id); + + if (cache) + cache->object = NULL; + } +} + +/* Invalidate all the cache pages associated with this object + * Do this whenever ther file is deleted or resized. + */ +static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) +{ + int i; + struct yaffs_dev *dev = in->my_dev; + + if (dev->param.n_caches > 0) { + /* Invalidate it. */ + for (i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].object == in) + dev->cache[i].object = NULL; + } + } +} + +static void yaffs_unhash_obj(struct yaffs_obj *obj) +{ + int bucket; + struct yaffs_dev *dev = obj->my_dev; + + /* If it is still linked into the bucket list, free from the list */ + if (!list_empty(&obj->hash_link)) { + list_del_init(&obj->hash_link); + bucket = yaffs_hash_fn(obj->obj_id); + dev->obj_bucket[bucket].count--; + } +} + +/* FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_free_obj(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + + if (!obj) { + BUG(); + return; + } + dev = obj->my_dev; + yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", + obj, obj->my_inode); + if (obj->parent) + BUG(); + if (!list_empty(&obj->siblings)) + BUG(); + + if (obj->my_inode) { + /* We're still hooked up to a cached inode. + * Don't delete now, but mark for later deletion + */ + obj->defered_free = 1; + return; + } + + yaffs_unhash_obj(obj); + + yaffs_free_raw_obj(dev, obj); + dev->n_obj--; + dev->checkpoint_blocks_required = 0; /* force recalculation */ +} + +void yaffs_handle_defered_free(struct yaffs_obj *obj) +{ + if (obj->defered_free) + yaffs_free_obj(obj); +} + +static int yaffs_generic_obj_del(struct yaffs_obj *in) +{ + /* Iinvalidate the file's data in the cache, without flushing. */ + yaffs_invalidate_whole_cache(in); + + if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { + /* Move to unlinked directory so we have a deletion record */ + yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, + 0); + } + + yaffs_remove_obj_from_dir(in); + yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); + in->hdr_chunk = 0; + + yaffs_free_obj(in); + return YAFFS_OK; + +} + +static void yaffs_soft_del_file(struct yaffs_obj *obj) +{ + if (!obj->deleted || + obj->variant_type != YAFFS_OBJECT_TYPE_FILE || + obj->soft_del) + return; + + if (obj->n_data_chunks <= 0) { + /* Empty file with no duplicate object headers, + * just delete it immediately */ + yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); + obj->variant.file_variant.top = NULL; + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: Deleting empty file %d", + obj->obj_id); + yaffs_generic_obj_del(obj); + } else { + yaffs_soft_del_worker(obj, + obj->variant.file_variant.top, + obj->variant. + file_variant.top_level, 0); + obj->soft_del = 1; + } +} + +/* Pruning removes any part of the file structure tree that is beyond the + * bounds of the file (ie that does not point to chunks). + * + * A file should only get pruned when its size is reduced. + * + * Before pruning, the chunks must be pulled from the tree and the + * level 0 tnode entries must be zeroed out. + * Could also use this for file deletion, but that's probably better handled + * by a special case. + * + * This function is recursive. For levels > 0 the function is called again on + * any sub-tree. For level == 0 we just check if the sub-tree has data. + * If there is no data in a subtree then it is pruned. + */ + +static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, + struct yaffs_tnode *tn, u32 level, + int del0) +{ + int i; + int has_data; + + if (!tn) + return tn; + + has_data = 0; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) { + tn->internal[i] = + yaffs_prune_worker(dev, + tn->internal[i], + level - 1, + (i == 0) ? del0 : 1); + } + + if (tn->internal[i]) + has_data++; + } + } else { + int tnode_size_u32 = dev->tnode_size / sizeof(u32); + u32 *map = (u32 *) tn; + + for (i = 0; !has_data && i < tnode_size_u32; i++) { + if (map[i]) + has_data++; + } + } + + if (has_data == 0 && del0) { + /* Free and return NULL */ + yaffs_free_tnode(dev, tn); + tn = NULL; + } + return tn; +} + +static int yaffs_prune_tree(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct) +{ + int i; + int has_data; + int done = 0; + struct yaffs_tnode *tn; + + if (file_struct->top_level < 1) + return YAFFS_OK; + + file_struct->top = + yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); + + /* Now we have a tree with all the non-zero branches NULL but + * the height is the same as it was. + * Let's see if we can trim internal tnodes to shorten the tree. + * We can do this if only the 0th element in the tnode is in use + * (ie all the non-zero are NULL) + */ + + while (file_struct->top_level && !done) { + tn = file_struct->top; + + has_data = 0; + for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) + has_data++; + } + + if (!has_data) { + file_struct->top = tn->internal[0]; + file_struct->top_level--; + yaffs_free_tnode(dev, tn); + } else { + done = 1; + } + } + + return YAFFS_OK; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* alloc_empty_obj gets us a clean Object.*/ +static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); + + if (!obj) + return obj; + + dev->n_obj++; + + /* Now sweeten it up... */ + + memset(obj, 0, sizeof(struct yaffs_obj)); + obj->being_created = 1; + + obj->my_dev = dev; + obj->hdr_chunk = 0; + obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; + INIT_LIST_HEAD(&(obj->hard_links)); + INIT_LIST_HEAD(&(obj->hash_link)); + INIT_LIST_HEAD(&obj->siblings); + + /* Now make the directory sane */ + if (dev->root_dir) { + obj->parent = dev->root_dir; + list_add(&(obj->siblings), + &dev->root_dir->variant.dir_variant.children); + } + + /* Add it to the lost and found directory. + * NB Can't put root or lost-n-found in lost-n-found so + * check if lost-n-found exists first + */ + if (dev->lost_n_found) + yaffs_add_obj_to_dir(dev->lost_n_found, obj); + + obj->being_created = 0; + + dev->checkpoint_blocks_required = 0; /* force recalculation */ + + return obj; +} + +static int yaffs_find_nice_bucket(struct yaffs_dev *dev) +{ + int i; + int l = 999; + int lowest = 999999; + + /* Search for the shortest list or one that + * isn't too long. + */ + + for (i = 0; i < 10 && lowest > 4; i++) { + dev->bucket_finder++; + dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; + if (dev->obj_bucket[dev->bucket_finder].count < lowest) { + lowest = dev->obj_bucket[dev->bucket_finder].count; + l = dev->bucket_finder; + } + } + + return l; +} + +static int yaffs_new_obj_id(struct yaffs_dev *dev) +{ + int bucket = yaffs_find_nice_bucket(dev); + int found = 0; + struct list_head *i; + u32 n = (u32) bucket; + + /* Now find an object value that has not already been taken + * by scanning the list. + */ + + while (!found) { + found = 1; + n += YAFFS_NOBJECT_BUCKETS; + if (1 || dev->obj_bucket[bucket].count > 0) { + list_for_each(i, &dev->obj_bucket[bucket].list) { + /* If there is already one in the list */ + if (i && list_entry(i, struct yaffs_obj, + hash_link)->obj_id == n) { + found = 0; + } + } + } + } + return n; +} + +static void yaffs_hash_obj(struct yaffs_obj *in) +{ + int bucket = yaffs_hash_fn(in->obj_id); + struct yaffs_dev *dev = in->my_dev; + + list_add(&in->hash_link, &dev->obj_bucket[bucket].list); + dev->obj_bucket[bucket].count++; +} + +struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) +{ + int bucket = yaffs_hash_fn(number); + struct list_head *i; + struct yaffs_obj *in; + + list_for_each(i, &dev->obj_bucket[bucket].list) { + /* Look if it is in the list */ + in = list_entry(i, struct yaffs_obj, hash_link); + if (in->obj_id == number) { + /* Don't show if it is defered free */ + if (in->defered_free) + return NULL; + return in; + } + } + + return NULL; +} + +struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, + enum yaffs_obj_type type) +{ + struct yaffs_obj *the_obj = NULL; + struct yaffs_tnode *tn = NULL; + + if (number < 0) + number = yaffs_new_obj_id(dev); + + if (type == YAFFS_OBJECT_TYPE_FILE) { + tn = yaffs_get_tnode(dev); + if (!tn) + return NULL; + } + + the_obj = yaffs_alloc_empty_obj(dev); + if (!the_obj) { + if (tn) + yaffs_free_tnode(dev, tn); + return NULL; + } + + the_obj->fake = 0; + the_obj->rename_allowed = 1; + the_obj->unlink_allowed = 1; + the_obj->obj_id = number; + yaffs_hash_obj(the_obj); + the_obj->variant_type = type; + yaffs_load_current_time(the_obj, 1, 1); + + switch (type) { + case YAFFS_OBJECT_TYPE_FILE: + the_obj->variant.file_variant.file_size = 0; + the_obj->variant.file_variant.scanned_size = 0; + the_obj->variant.file_variant.shrink_size = ~0; /* max */ + the_obj->variant.file_variant.top_level = 0; + the_obj->variant.file_variant.top = tn; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); + INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + /* No action required */ + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* todo this should not happen */ + break; + } + return the_obj; +} + +static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, + int number, u32 mode) +{ + + struct yaffs_obj *obj = + yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); + + if (!obj) + return NULL; + + obj->fake = 1; /* it is fake so it might not use NAND */ + obj->rename_allowed = 0; + obj->unlink_allowed = 0; + obj->deleted = 0; + obj->unlinked = 0; + obj->yst_mode = mode; + obj->my_dev = dev; + obj->hdr_chunk = 0; /* Not a valid chunk. */ + return obj; + +} + + +static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) +{ + int i; + + dev->n_obj = 0; + dev->n_tnodes = 0; + yaffs_init_raw_tnodes_and_objs(dev); + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + INIT_LIST_HEAD(&dev->obj_bucket[i].list); + dev->obj_bucket[i].count = 0; + } +} + +struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, + int number, + enum yaffs_obj_type type) +{ + struct yaffs_obj *the_obj = NULL; + + if (number > 0) + the_obj = yaffs_find_by_number(dev, number); + + if (!the_obj) + the_obj = yaffs_new_obj(dev, number, type); + + return the_obj; + +} + +YCHAR *yaffs_clone_str(const YCHAR *str) +{ + YCHAR *new_str = NULL; + int len; + + if (!str) + str = _Y(""); + + len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); + new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); + if (new_str) { + strncpy(new_str, str, len); + new_str[len] = 0; + } + return new_str; + +} +/* + *yaffs_update_parent() handles fixing a directories mtime and ctime when a new + * link (ie. name) is created or deleted in the directory. + * + * ie. + * create dir/a : update dir's mtime/ctime + * rm dir/a: update dir's mtime/ctime + * modify dir/a: don't update dir's mtimme/ctime + * + * This can be handled immediately or defered. Defering helps reduce the number + * of updates when many files in a directory are changed within a brief period. + * + * If the directory updating is defered then yaffs_update_dirty_dirs must be + * called periodically. + */ + +static void yaffs_update_parent(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + + if (!obj) + return; + dev = obj->my_dev; + obj->dirty = 1; + yaffs_load_current_time(obj, 0, 1); + if (dev->param.defered_dir_update) { + struct list_head *link = &obj->variant.dir_variant.dirty; + + if (list_empty(link)) { + list_add(link, &dev->dirty_dirs); + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "Added object %d to dirty directories", + obj->obj_id); + } + + } else { + yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); + } +} + +void yaffs_update_dirty_dirs(struct yaffs_dev *dev) +{ + struct list_head *link; + struct yaffs_obj *obj; + struct yaffs_dir_var *d_s; + union yaffs_obj_var *o_v; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); + + while (!list_empty(&dev->dirty_dirs)) { + link = dev->dirty_dirs.next; + list_del_init(link); + + d_s = list_entry(link, struct yaffs_dir_var, dirty); + o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); + obj = list_entry(o_v, struct yaffs_obj, variant); + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", + obj->obj_id); + + if (obj->dirty) + yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); + } +} + +/* + * Mknod (create) a new object. + * equiv_obj only has meaning for a hard link; + * alias_str only has meaning for a symlink. + * rdev only has meaning for devices (a subset of special objects) + */ + +static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, + struct yaffs_obj *parent, + const YCHAR *name, + u32 mode, + u32 uid, + u32 gid, + struct yaffs_obj *equiv_obj, + const YCHAR *alias_str, u32 rdev) +{ + struct yaffs_obj *in; + YCHAR *str = NULL; + struct yaffs_dev *dev = parent->my_dev; + + /* Check if the entry exists. + * If it does then fail the call since we don't want a dup. */ + if (yaffs_find_by_name(parent, name)) + return NULL; + + if (type == YAFFS_OBJECT_TYPE_SYMLINK) { + str = yaffs_clone_str(alias_str); + if (!str) + return NULL; + } + + in = yaffs_new_obj(dev, -1, type); + + if (!in) { + kfree(str); + return NULL; + } + + in->hdr_chunk = 0; + in->valid = 1; + in->variant_type = type; + + in->yst_mode = mode; + + yaffs_attribs_init(in, gid, uid, rdev); + + in->n_data_chunks = 0; + + yaffs_set_obj_name(in, name); + in->dirty = 1; + + yaffs_add_obj_to_dir(parent, in); + + in->my_dev = parent->my_dev; + + switch (type) { + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symlink_variant.alias = str; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardlink_variant.equiv_obj = equiv_obj; + in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; + list_add(&in->hard_links, &equiv_obj->hard_links); + break; + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_DIRECTORY: + case YAFFS_OBJECT_TYPE_SPECIAL: + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* do nothing */ + break; + } + + if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { + /* Could not create the object header, fail */ + yaffs_del_obj(in); + in = NULL; + } + + if (in) + yaffs_update_parent(parent); + + return in; +} + +struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, + uid, gid, NULL, NULL, 0); +} + +struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, + u32 mode, u32 uid, u32 gid) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, + mode, uid, gid, NULL, NULL, 0); +} + +struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, u32 rdev) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, + uid, gid, NULL, NULL, rdev); +} + +struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, const YCHAR *alias) +{ + return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, + uid, gid, NULL, alias, 0); +} + +/* yaffs_link_obj returns the object id of the equivalent object.*/ +struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, + struct yaffs_obj *equiv_obj) +{ + /* Get the real object in case we were fed a hard link obj */ + equiv_obj = yaffs_get_equivalent_obj(equiv_obj); + + if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, + parent, name, 0, 0, 0, + equiv_obj, NULL, 0)) + return equiv_obj; + + return NULL; + +} + + + +/*---------------------- Block Management and Page Allocation -------------*/ + +static void yaffs_deinit_blocks(struct yaffs_dev *dev) +{ + if (dev->block_info_alt && dev->block_info) + vfree(dev->block_info); + else + kfree(dev->block_info); + + dev->block_info_alt = 0; + + dev->block_info = NULL; + + if (dev->chunk_bits_alt && dev->chunk_bits) + vfree(dev->chunk_bits); + else + kfree(dev->chunk_bits); + dev->chunk_bits_alt = 0; + dev->chunk_bits = NULL; +} + +static int yaffs_init_blocks(struct yaffs_dev *dev) +{ + int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + + dev->block_info = NULL; + dev->chunk_bits = NULL; + dev->alloc_block = -1; /* force it to get a new one */ + + /* If the first allocation strategy fails, thry the alternate one */ + dev->block_info = + kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); + if (!dev->block_info) { + dev->block_info = + vmalloc(n_blocks * sizeof(struct yaffs_block_info)); + dev->block_info_alt = 1; + } else { + dev->block_info_alt = 0; + } + + if (!dev->block_info) + goto alloc_error; + + /* Set up dynamic blockinfo stuff. Round up bytes. */ + dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; + dev->chunk_bits = + kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); + if (!dev->chunk_bits) { + dev->chunk_bits = + vmalloc(dev->chunk_bit_stride * n_blocks); + dev->chunk_bits_alt = 1; + } else { + dev->chunk_bits_alt = 0; + } + if (!dev->chunk_bits) + goto alloc_error; + + + memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); + memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); + return YAFFS_OK; + +alloc_error: + yaffs_deinit_blocks(dev); + return YAFFS_FAIL; +} + + +void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); + int erased_ok = 0; + int i; + + /* If the block is still healthy erase it and mark as clean. + * If the block has had a data failure, then retire it. + */ + + yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, + "yaffs_block_became_dirty block %d state %d %s", + block_no, bi->block_state, + (bi->needs_retiring) ? "needs retiring" : ""); + + yaffs2_clear_oldest_dirty_seq(dev, bi); + + bi->block_state = YAFFS_BLOCK_STATE_DIRTY; + + /* If this is the block being garbage collected then stop gc'ing */ + if (block_no == dev->gc_block) + dev->gc_block = 0; + + /* If this block is currently the best candidate for gc + * then drop as a candidate */ + if (block_no == dev->gc_dirtiest) { + dev->gc_dirtiest = 0; + dev->gc_pages_in_use = 0; + } + + if (!bi->needs_retiring) { + yaffs2_checkpt_invalidate(dev); + erased_ok = yaffs_erase_block(dev, block_no); + if (!erased_ok) { + dev->n_erase_failures++; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Erasure failed %d", block_no); + } + } + + /* Verify erasure if needed */ + if (erased_ok && + ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || + !yaffs_skip_verification(dev))) { + for (i = 0; i < dev->param.chunks_per_block; i++) { + if (!yaffs_check_chunk_erased(dev, + block_no * dev->param.chunks_per_block + i)) { + yaffs_trace(YAFFS_TRACE_ERROR, + ">>Block %d erasure supposedly OK, but chunk %d not erased", + block_no, i); + } + } + } + + if (!erased_ok) { + /* We lost a block of free space */ + dev->n_free_chunks -= dev->param.chunks_per_block; + yaffs_retire_block(dev, block_no); + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>> Block %d retired", block_no); + return; + } + + /* Clean it up... */ + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + bi->seq_number = 0; + dev->n_erased_blocks++; + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + bi->has_shrink_hdr = 0; + bi->skip_erased_check = 1; /* Clean, so no need to check */ + bi->gc_prioritise = 0; + bi->has_summary=0; + + yaffs_clear_chunk_bits(dev, block_no); + + yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); +} + +static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, + int old_chunk, u8 *buffer) +{ + int new_chunk; + int mark_flash = 1; + struct yaffs_ext_tags tags; + struct yaffs_obj *object; + int matching_chunk; + int ret_val = YAFFS_OK; + + memset(&tags, 0, sizeof(tags)); + yaffs_rd_chunk_tags_nand(dev, old_chunk, + buffer, &tags); + object = yaffs_find_by_number(dev, tags.obj_id); + + yaffs_trace(YAFFS_TRACE_GC_DETAIL, + "Collecting chunk in block %d, %d %d %d ", + dev->gc_chunk, tags.obj_id, + tags.chunk_id, tags.n_bytes); + + if (object && !yaffs_skip_verification(dev)) { + if (tags.chunk_id == 0) + matching_chunk = + object->hdr_chunk; + else if (object->soft_del) + /* Defeat the test */ + matching_chunk = old_chunk; + else + matching_chunk = + yaffs_find_chunk_in_file + (object, tags.chunk_id, + NULL); + + if (old_chunk != matching_chunk) + yaffs_trace(YAFFS_TRACE_ERROR, + "gc: page in gc mismatch: %d %d %d %d", + old_chunk, + matching_chunk, + tags.obj_id, + tags.chunk_id); + } + + if (!object) { + yaffs_trace(YAFFS_TRACE_ERROR, + "page %d in gc has no object: %d %d %d ", + old_chunk, + tags.obj_id, tags.chunk_id, + tags.n_bytes); + } + + if (object && + object->deleted && + object->soft_del && tags.chunk_id != 0) { + /* Data chunk in a soft deleted file, + * throw it away. + * It's a soft deleted data chunk, + * No need to copy this, just forget + * about it and fix up the object. + */ + + /* Free chunks already includes + * softdeleted chunks, how ever this + * chunk is going to soon be really + * deleted which will increment free + * chunks. We have to decrement free + * chunks so this works out properly. + */ + dev->n_free_chunks--; + bi->soft_del_pages--; + + object->n_data_chunks--; + if (object->n_data_chunks <= 0) { + /* remeber to clean up obj */ + dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; + dev->n_clean_ups++; + } + mark_flash = 0; + } else if (object) { + /* It's either a data chunk in a live + * file or an ObjectHeader, so we're + * interested in it. + * NB Need to keep the ObjectHeaders of + * deleted files until the whole file + * has been deleted off + */ + tags.serial_number++; + dev->n_gc_copies++; + + if (tags.chunk_id == 0) { + /* It is an object Id, + * We need to nuke the + * shrinkheader flags since its + * work is done. + * Also need to clean up + * shadowing. + */ + struct yaffs_obj_hdr *oh; + oh = (struct yaffs_obj_hdr *) buffer; + + oh->is_shrink = 0; + tags.extra_is_shrink = 0; + oh->shadows_obj = 0; + oh->inband_shadowed_obj_id = 0; + tags.extra_shadows = 0; + + /* Update file size */ + if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { + oh->file_size = + object->variant.file_variant.file_size; + tags.extra_length = oh->file_size; + } + + yaffs_verify_oh(object, oh, &tags, 1); + new_chunk = + yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); + } else { + new_chunk = + yaffs_write_new_chunk(dev, buffer, &tags, 1); + } + + if (new_chunk < 0) { + ret_val = YAFFS_FAIL; + } else { + + /* Now fix up the Tnodes etc. */ + + if (tags.chunk_id == 0) { + /* It's a header */ + object->hdr_chunk = new_chunk; + object->serial = tags.serial_number; + } else { + /* It's a data chunk */ + yaffs_put_chunk_in_file(object, tags.chunk_id, + new_chunk, 0); + } + } + } + if (ret_val == YAFFS_OK) + yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); + return ret_val; +} + +static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) +{ + int old_chunk; + int ret_val = YAFFS_OK; + int i; + int is_checkpt_block; + int max_copies; + int chunks_before = yaffs_get_erased_chunks(dev); + int chunks_after; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); + + is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); + + yaffs_trace(YAFFS_TRACE_TRACING, + "Collecting block %d, in use %d, shrink %d, whole_block %d", + block, bi->pages_in_use, bi->has_shrink_hdr, + whole_block); + + /*yaffs_verify_free_chunks(dev); */ + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL) + bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; + + bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ + + dev->gc_disable = 1; + + yaffs_summary_gc(dev, block); + + if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { + yaffs_trace(YAFFS_TRACE_TRACING, + "Collecting block %d that has no chunks in use", + block); + yaffs_block_became_dirty(dev, block); + } else { + + u8 *buffer = yaffs_get_temp_buffer(dev); + + yaffs_verify_blk(dev, bi, block); + + max_copies = (whole_block) ? dev->param.chunks_per_block : 5; + old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; + + for (/* init already done */ ; + ret_val == YAFFS_OK && + dev->gc_chunk < dev->param.chunks_per_block && + (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && + max_copies > 0; + dev->gc_chunk++, old_chunk++) { + if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { + /* Page is in use and might need to be copied */ + max_copies--; + ret_val = yaffs_gc_process_chunk(dev, bi, + old_chunk, buffer); + } + } + yaffs_release_temp_buffer(dev, buffer); + } + + yaffs_verify_collected_blk(dev, bi, block); + + if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { + /* + * The gc did not complete. Set block state back to FULL + * because checkpointing does not restore gc. + */ + bi->block_state = YAFFS_BLOCK_STATE_FULL; + } else { + /* The gc completed. */ + /* Do any required cleanups */ + for (i = 0; i < dev->n_clean_ups; i++) { + /* Time to delete the file too */ + struct yaffs_obj *object = + yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); + if (object) { + yaffs_free_tnode(dev, + object->variant.file_variant.top); + object->variant.file_variant.top = NULL; + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: About to finally delete object %d", + object->obj_id); + yaffs_generic_obj_del(object); + object->my_dev->n_deleted_files--; + } + + } + chunks_after = yaffs_get_erased_chunks(dev); + if (chunks_before >= chunks_after) + yaffs_trace(YAFFS_TRACE_GC, + "gc did not increase free chunks before %d after %d", + chunks_before, chunks_after); + dev->gc_block = 0; + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + + dev->gc_disable = 0; + + return ret_val; +} + +/* + * find_gc_block() selects the dirtiest block (or close enough) + * for garbage collection. + */ + +static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, + int aggressive, int background) +{ + int i; + int iterations; + unsigned selected = 0; + int prioritised = 0; + int prioritised_exist = 0; + struct yaffs_block_info *bi; + int threshold; + + /* First let's see if we need to grab a prioritised block */ + if (dev->has_pending_prioritised_gc && !aggressive) { + dev->gc_dirtiest = 0; + bi = dev->block_info; + for (i = dev->internal_start_block; + i <= dev->internal_end_block && !selected; i++) { + + if (bi->gc_prioritise) { + prioritised_exist = 1; + if (bi->block_state == YAFFS_BLOCK_STATE_FULL && + yaffs_block_ok_for_gc(dev, bi)) { + selected = i; + prioritised = 1; + } + } + bi++; + } + + /* + * If there is a prioritised block and none was selected then + * this happened because there is at least one old dirty block + * gumming up the works. Let's gc the oldest dirty block. + */ + + if (prioritised_exist && + !selected && dev->oldest_dirty_block > 0) + selected = dev->oldest_dirty_block; + + if (!prioritised_exist) /* None found, so we can clear this */ + dev->has_pending_prioritised_gc = 0; + } + + /* If we're doing aggressive GC then we are happy to take a less-dirty + * block, and search harder. + * else (leasurely gc), then we only bother to do this if the + * block has only a few pages in use. + */ + + if (!selected) { + int pages_used; + int n_blocks = + dev->internal_end_block - dev->internal_start_block + 1; + if (aggressive) { + threshold = dev->param.chunks_per_block; + iterations = n_blocks; + } else { + int max_threshold; + + if (background) + max_threshold = dev->param.chunks_per_block / 2; + else + max_threshold = dev->param.chunks_per_block / 8; + + if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) + max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; + + threshold = background ? (dev->gc_not_done + 2) * 2 : 0; + if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) + threshold = YAFFS_GC_PASSIVE_THRESHOLD; + if (threshold > max_threshold) + threshold = max_threshold; + + iterations = n_blocks / 16 + 1; + if (iterations > 100) + iterations = 100; + } + + for (i = 0; + i < iterations && + (dev->gc_dirtiest < 1 || + dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); + i++) { + dev->gc_block_finder++; + if (dev->gc_block_finder < dev->internal_start_block || + dev->gc_block_finder > dev->internal_end_block) + dev->gc_block_finder = + dev->internal_start_block; + + bi = yaffs_get_block_info(dev, dev->gc_block_finder); + + pages_used = bi->pages_in_use - bi->soft_del_pages; + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL && + pages_used < dev->param.chunks_per_block && + (dev->gc_dirtiest < 1 || + pages_used < dev->gc_pages_in_use) && + yaffs_block_ok_for_gc(dev, bi)) { + dev->gc_dirtiest = dev->gc_block_finder; + dev->gc_pages_in_use = pages_used; + } + } + + if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) + selected = dev->gc_dirtiest; + } + + /* + * If nothing has been selected for a while, try the oldest dirty + * because that's gumming up the works. + */ + + if (!selected && dev->param.is_yaffs2 && + dev->gc_not_done >= (background ? 10 : 20)) { + yaffs2_find_oldest_dirty_seq(dev); + if (dev->oldest_dirty_block > 0) { + selected = dev->oldest_dirty_block; + dev->gc_dirtiest = selected; + dev->oldest_dirty_gc_count++; + bi = yaffs_get_block_info(dev, selected); + dev->gc_pages_in_use = + bi->pages_in_use - bi->soft_del_pages; + } else { + dev->gc_not_done = 0; + } + } + + if (selected) { + yaffs_trace(YAFFS_TRACE_GC, + "GC Selected block %d with %d free, prioritised:%d", + selected, + dev->param.chunks_per_block - dev->gc_pages_in_use, + prioritised); + + dev->n_gc_blocks++; + if (background) + dev->bg_gcs++; + + dev->gc_dirtiest = 0; + dev->gc_pages_in_use = 0; + dev->gc_not_done = 0; + if (dev->refresh_skip > 0) + dev->refresh_skip--; + } else { + dev->gc_not_done++; + yaffs_trace(YAFFS_TRACE_GC, + "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", + dev->gc_block_finder, dev->gc_not_done, threshold, + dev->gc_dirtiest, dev->gc_pages_in_use, + dev->oldest_dirty_block, background ? " bg" : ""); + } + + return selected; +} + +/* New garbage collector + * If we're very low on erased blocks then we do aggressive garbage collection + * otherwise we do "leasurely" garbage collection. + * Aggressive gc looks further (whole array) and will accept less dirty blocks. + * Passive gc only inspects smaller areas and only accepts more dirty blocks. + * + * The idea is to help clear out space in a more spread-out manner. + * Dunno if it really does anything useful. + */ +static int yaffs_check_gc(struct yaffs_dev *dev, int background) +{ + int aggressive = 0; + int gc_ok = YAFFS_OK; + int max_tries = 0; + int min_erased; + int erased_chunks; + int checkpt_block_adjust; + + if (dev->param.gc_control && (dev->param.gc_control(dev) & 1) == 0) + return YAFFS_OK; + + if (dev->gc_disable) + /* Bail out so we don't get recursive gc */ + return YAFFS_OK; + + /* This loop should pass the first time. + * Only loops here if the collection does not increase space. + */ + + do { + max_tries++; + + checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); + + min_erased = + dev->param.n_reserved_blocks + checkpt_block_adjust + 1; + erased_chunks = + dev->n_erased_blocks * dev->param.chunks_per_block; + + /* If we need a block soon then do aggressive gc. */ + if (dev->n_erased_blocks < min_erased) + aggressive = 1; + else { + if (!background + && erased_chunks > (dev->n_free_chunks / 4)) + break; + + if (dev->gc_skip > 20) + dev->gc_skip = 20; + if (erased_chunks < dev->n_free_chunks / 2 || + dev->gc_skip < 1 || background) + aggressive = 0; + else { + dev->gc_skip--; + break; + } + } + + dev->gc_skip = 5; + + /* If we don't already have a block being gc'd then see if we + * should start another */ + + if (dev->gc_block < 1 && !aggressive) { + dev->gc_block = yaffs2_find_refresh_block(dev); + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + if (dev->gc_block < 1) { + dev->gc_block = + yaffs_find_gc_block(dev, aggressive, background); + dev->gc_chunk = 0; + dev->n_clean_ups = 0; + } + + if (dev->gc_block > 0) { + dev->all_gcs++; + if (!aggressive) + dev->passive_gc_count++; + + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: GC n_erased_blocks %d aggressive %d", + dev->n_erased_blocks, aggressive); + + gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); + } + + if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && + dev->gc_block > 0) { + yaffs_trace(YAFFS_TRACE_GC, + "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", + dev->n_erased_blocks, max_tries, + dev->gc_block); + } + } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && + (dev->gc_block > 0) && (max_tries < 2)); + + return aggressive ? gc_ok : YAFFS_OK; +} + +/* + * yaffs_bg_gc() + * Garbage collects. Intended to be called from a background thread. + * Returns non-zero if at least half the free chunks are erased. + */ +int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) +{ + int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; + + yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); + + yaffs_check_gc(dev, 1); + return erased_chunks > dev->n_free_chunks / 2; +} + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) +{ + int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); + + if (nand_chunk >= 0) + return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, + buffer, NULL); + else { + yaffs_trace(YAFFS_TRACE_NANDACCESS, + "Chunk %d not found zero instead", + nand_chunk); + /* get sane (zero) data if you read a hole */ + memset(buffer, 0, in->my_dev->data_bytes_per_chunk); + return 0; + } + +} + +void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, + int lyn) +{ + int block; + int page; + struct yaffs_ext_tags tags; + struct yaffs_block_info *bi; + + if (chunk_id <= 0) + return; + + dev->n_deletions++; + block = chunk_id / dev->param.chunks_per_block; + page = chunk_id % dev->param.chunks_per_block; + + if (!yaffs_check_chunk_bit(dev, block, page)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Deleting invalid chunk %d", chunk_id); + + bi = yaffs_get_block_info(dev, block); + + yaffs2_update_oldest_dirty_seq(dev, block, bi); + + yaffs_trace(YAFFS_TRACE_DELETION, + "line %d delete of chunk %d", + lyn, chunk_id); + + if (!dev->param.is_yaffs2 && mark_flash && + bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { + + memset(&tags, 0, sizeof(tags)); + tags.is_deleted = 1; + yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); + yaffs_handle_chunk_update(dev, chunk_id, &tags); + } else { + dev->n_unmarked_deletions++; + } + + /* Pull out of the management area. + * If the whole block became dirty, this will kick off an erasure. + */ + if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || + bi->block_state == YAFFS_BLOCK_STATE_FULL || + bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { + dev->n_free_chunks++; + yaffs_clear_chunk_bit(dev, block, page); + bi->pages_in_use--; + + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && + bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { + yaffs_block_became_dirty(dev, block); + } + } +} + +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, + const u8 *buffer, int n_bytes, int use_reserve) +{ + /* Find old chunk Need to do this to get serial number + * Write new one and patch into tree. + * Invalidate old tags. + */ + + int prev_chunk_id; + struct yaffs_ext_tags prev_tags; + int new_chunk_id; + struct yaffs_ext_tags new_tags; + struct yaffs_dev *dev = in->my_dev; + + yaffs_check_gc(dev, 0); + + /* Get the previous chunk at this location in the file if it exists. + * If it does not exist then put a zero into the tree. This creates + * the tnode now, rather than later when it is harder to clean up. + */ + prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); + if (prev_chunk_id < 1 && + !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) + return 0; + + /* Set up new tags */ + memset(&new_tags, 0, sizeof(new_tags)); + + new_tags.chunk_id = inode_chunk; + new_tags.obj_id = in->obj_id; + new_tags.serial_number = + (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; + new_tags.n_bytes = n_bytes; + + if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Writing %d bytes to chunk!!!!!!!!!", + n_bytes); + BUG(); + } + + new_chunk_id = + yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); + + if (new_chunk_id > 0) { + yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); + + if (prev_chunk_id > 0) + yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); + + yaffs_verify_file_sane(in); + } + return new_chunk_id; + +} + + + +static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, + const YCHAR *name, const void *value, int size, + int flags) +{ + struct yaffs_xattr_mod xmod; + int result; + + xmod.set = set; + xmod.name = name; + xmod.data = value; + xmod.size = size; + xmod.flags = flags; + xmod.result = -ENOSPC; + + result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); + + if (result > 0) + return xmod.result; + else + return -ENOSPC; +} + +static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, + struct yaffs_xattr_mod *xmod) +{ + int retval = 0; + int x_offs = sizeof(struct yaffs_obj_hdr); + struct yaffs_dev *dev = obj->my_dev; + int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); + char *x_buffer = buffer + x_offs; + + if (xmod->set) + retval = + nval_set(x_buffer, x_size, xmod->name, xmod->data, + xmod->size, xmod->flags); + else + retval = nval_del(x_buffer, x_size, xmod->name); + + obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->xattr_known = 1; + xmod->result = retval; + + return retval; +} + +static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, + void *value, int size) +{ + char *buffer = NULL; + int result; + struct yaffs_ext_tags tags; + struct yaffs_dev *dev = obj->my_dev; + int x_offs = sizeof(struct yaffs_obj_hdr); + int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); + char *x_buffer; + int retval = 0; + + if (obj->hdr_chunk < 1) + return -ENODATA; + + /* If we know that the object has no xattribs then don't do all the + * reading and parsing. + */ + if (obj->xattr_known && !obj->has_xattr) { + if (name) + return -ENODATA; + else + return 0; + } + + buffer = (char *)yaffs_get_temp_buffer(dev); + if (!buffer) + return -ENOMEM; + + result = + yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); + + if (result != YAFFS_OK) + retval = -ENOENT; + else { + x_buffer = buffer + x_offs; + + if (!obj->xattr_known) { + obj->has_xattr = nval_hasvalues(x_buffer, x_size); + obj->xattr_known = 1; + } + + if (name) + retval = nval_get(x_buffer, x_size, name, value, size); + else + retval = nval_list(x_buffer, x_size, value, size); + } + yaffs_release_temp_buffer(dev, (u8 *) buffer); + return retval; +} + +int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, + const void *value, int size, int flags) +{ + return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); +} + +int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) +{ + return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); +} + +int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, + int size) +{ + return yaffs_do_xattrib_fetch(obj, name, value, size); +} + +int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) +{ + return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); +} + +static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) +{ + u8 *buf; + struct yaffs_obj_hdr *oh; + struct yaffs_dev *dev; + struct yaffs_ext_tags tags; + int result; + int alloc_failed = 0; + + if (!in || !in->lazy_loaded || in->hdr_chunk < 1) + return; + + dev = in->my_dev; + in->lazy_loaded = 0; + buf = yaffs_get_temp_buffer(dev); + + result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); + oh = (struct yaffs_obj_hdr *)buf; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + yaffs_set_obj_name_from_oh(in, oh); + + if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { + in->variant.symlink_variant.alias = + yaffs_clone_str(oh->alias); + if (!in->variant.symlink_variant.alias) + alloc_failed = 1; /* Not returned */ + } + yaffs_release_temp_buffer(dev, buf); +} + +static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, + const YCHAR *oh_name, int buff_size) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + if (dev->param.auto_unicode) { + if (*oh_name) { + /* It is an ASCII name, do an ASCII to + * unicode conversion */ + const char *ascii_oh_name = (const char *)oh_name; + int n = buff_size - 1; + while (n > 0 && *ascii_oh_name) { + *name = *ascii_oh_name; + name++; + ascii_oh_name++; + n--; + } + } else { + strncpy(name, oh_name + 1, buff_size - 1); + } + } else { +#else + { +#endif + strncpy(name, oh_name, buff_size - 1); + } +} + +static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, + const YCHAR *name) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE + + int is_ascii; + YCHAR *w; + + if (dev->param.auto_unicode) { + + is_ascii = 1; + w = name; + + /* Figure out if the name will fit in ascii character set */ + while (is_ascii && *w) { + if ((*w) & 0xff00) + is_ascii = 0; + w++; + } + + if (is_ascii) { + /* It is an ASCII name, so convert unicode to ascii */ + char *ascii_oh_name = (char *)oh_name; + int n = YAFFS_MAX_NAME_LENGTH - 1; + while (n > 0 && *name) { + *ascii_oh_name = *name; + name++; + ascii_oh_name++; + n--; + } + } else { + /* Unicode name, so save starting at the second YCHAR */ + *oh_name = 0; + strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); + } + } else { +#else + { +#endif + strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); + } +} + +/* UpdateObjectHeader updates the header on NAND for an object. + * If name is not NULL, then that new name is used. + */ +int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, + int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) +{ + + struct yaffs_block_info *bi; + struct yaffs_dev *dev = in->my_dev; + int prev_chunk_id; + int ret_val = 0; + int result = 0; + int new_chunk_id; + struct yaffs_ext_tags new_tags; + struct yaffs_ext_tags old_tags; + const YCHAR *alias = NULL; + u8 *buffer = NULL; + YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; + struct yaffs_obj_hdr *oh = NULL; + + strcpy(old_name, _Y("silly old name")); + + if (in->fake && in != dev->root_dir && !force && !xmod) + return ret_val; + + yaffs_check_gc(dev, 0); + yaffs_check_obj_details_loaded(in); + + buffer = yaffs_get_temp_buffer(in->my_dev); + oh = (struct yaffs_obj_hdr *)buffer; + + prev_chunk_id = in->hdr_chunk; + + if (prev_chunk_id > 0) { + result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, + buffer, &old_tags); + + yaffs_verify_oh(in, oh, &old_tags, 0); + memcpy(old_name, oh->name, sizeof(oh->name)); + memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); + } else { + memset(buffer, 0xff, dev->data_bytes_per_chunk); + } + + oh->type = in->variant_type; + oh->yst_mode = in->yst_mode; + oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; + + yaffs_load_attribs_oh(oh, in); + + if (in->parent) + oh->parent_obj_id = in->parent->obj_id; + else + oh->parent_obj_id = 0; + + if (name && *name) { + memset(oh->name, 0, sizeof(oh->name)); + yaffs_load_oh_from_name(dev, oh->name, name); + } else if (prev_chunk_id > 0) { + memcpy(oh->name, old_name, sizeof(oh->name)); + } else { + memset(oh->name, 0, sizeof(oh->name)); + } + + oh->is_shrink = is_shrink; + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Should not happen */ + break; + case YAFFS_OBJECT_TYPE_FILE: + oh->file_size = + (oh->parent_obj_id == YAFFS_OBJECTID_DELETED || + oh->parent_obj_id == YAFFS_OBJECTID_UNLINKED) ? + 0 : in->variant.file_variant.file_size; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + oh->equiv_id = in->variant.hardlink_variant.equiv_id; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + alias = in->variant.symlink_variant.alias; + if (!alias) + alias = _Y("no alias"); + strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); + oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; + break; + } + + /* process any xattrib modifications */ + if (xmod) + yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); + + /* Tags */ + memset(&new_tags, 0, sizeof(new_tags)); + in->serial++; + new_tags.chunk_id = 0; + new_tags.obj_id = in->obj_id; + new_tags.serial_number = in->serial; + + /* Add extra info for file header */ + new_tags.extra_available = 1; + new_tags.extra_parent_id = oh->parent_obj_id; + new_tags.extra_length = oh->file_size; + new_tags.extra_is_shrink = oh->is_shrink; + new_tags.extra_equiv_id = oh->equiv_id; + new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; + new_tags.extra_obj_type = in->variant_type; + yaffs_verify_oh(in, oh, &new_tags, 1); + + /* Create new chunk in NAND */ + new_chunk_id = + yaffs_write_new_chunk(dev, buffer, &new_tags, + (prev_chunk_id > 0) ? 1 : 0); + + if (buffer) + yaffs_release_temp_buffer(dev, buffer); + + if (new_chunk_id < 0) + return new_chunk_id; + + in->hdr_chunk = new_chunk_id; + + if (prev_chunk_id > 0) + yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); + + if (!yaffs_obj_cache_dirty(in)) + in->dirty = 0; + + /* If this was a shrink, then mark the block + * that the chunk lives on */ + if (is_shrink) { + bi = yaffs_get_block_info(in->my_dev, + new_chunk_id / + in->my_dev->param.chunks_per_block); + bi->has_shrink_hdr = 1; + } + + + return new_chunk_id; +} + +/*--------------------- File read/write ------------------------ + * Read and write have very similar structures. + * In general the read/write has three parts to it + * An incomplete chunk to start with (if the read/write is not chunk-aligned) + * Some complete chunks + * An incomplete chunk to end off with + * + * Curve-balls: the first chunk might also be the last chunk. + */ + +int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) +{ + int chunk; + u32 start; + int n_copy; + int n = n_bytes; + int n_done = 0; + struct yaffs_cache *cache; + struct yaffs_dev *dev; + + dev = in->my_dev; + + while (n > 0) { + yaffs_addr_to_chunk(dev, offset, &chunk, &start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + if ((start + n) < dev->data_bytes_per_chunk) + n_copy = n; + else + n_copy = dev->data_bytes_per_chunk - start; + + cache = yaffs_find_chunk_cache(in, chunk); + + /* If the chunk is already in the cache or it is less than + * a whole chunk or we're using inband tags then use the cache + * (if there is caching) else bypass the cache. + */ + if (cache || n_copy != dev->data_bytes_per_chunk || + dev->param.inband_tags) { + if (dev->param.n_caches > 0) { + + /* If we can't find the data in the cache, + * then load it up. */ + + if (!cache) { + cache = + yaffs_grab_chunk_cache(in->my_dev); + cache->object = in; + cache->chunk_id = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_rd_data_obj(in, chunk, + cache->data); + cache->n_bytes = 0; + } + + yaffs_use_cache(dev, cache, 0); + + cache->locked = 1; + + memcpy(buffer, &cache->data[start], n_copy); + + cache->locked = 0; + } else { + /* Read into the local buffer then copy.. */ + + u8 *local_buffer = + yaffs_get_temp_buffer(dev); + yaffs_rd_data_obj(in, chunk, local_buffer); + + memcpy(buffer, &local_buffer[start], n_copy); + + yaffs_release_temp_buffer(dev, local_buffer); + } + } else { + /* A full chunk. Read directly into the buffer. */ + yaffs_rd_data_obj(in, chunk, buffer); + } + n -= n_copy; + offset += n_copy; + buffer += n_copy; + n_done += n_copy; + } + return n_done; +} + +int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_trhrough) +{ + + int chunk; + u32 start; + int n_copy; + int n = n_bytes; + int n_done = 0; + int n_writeback; + int start_write = offset; + int chunk_written = 0; + u32 n_bytes_read; + u32 chunk_start; + struct yaffs_dev *dev; + + dev = in->my_dev; + + while (n > 0 && chunk_written >= 0) { + yaffs_addr_to_chunk(dev, offset, &chunk, &start); + + if (chunk * dev->data_bytes_per_chunk + start != offset || + start >= dev->data_bytes_per_chunk) { + yaffs_trace(YAFFS_TRACE_ERROR, + "AddrToChunk of offset %d gives chunk %d start %d", + (int)offset, chunk, start); + } + chunk++; /* File pos to chunk in file offset */ + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + + if ((start + n) < dev->data_bytes_per_chunk) { + n_copy = n; + + /* Now calculate how many bytes to write back.... + * If we're overwriting and not writing to then end of + * file then we need to write back as much as was there + * before. + */ + + chunk_start = ((chunk - 1) * dev->data_bytes_per_chunk); + + if (chunk_start > in->variant.file_variant.file_size) + n_bytes_read = 0; /* Past end of file */ + else + n_bytes_read = + in->variant.file_variant.file_size - + chunk_start; + + if (n_bytes_read > dev->data_bytes_per_chunk) + n_bytes_read = dev->data_bytes_per_chunk; + + n_writeback = + (n_bytes_read > + (start + n)) ? n_bytes_read : (start + n); + + if (n_writeback < 0 || + n_writeback > dev->data_bytes_per_chunk) + BUG(); + + } else { + n_copy = dev->data_bytes_per_chunk - start; + n_writeback = dev->data_bytes_per_chunk; + } + + if (n_copy != dev->data_bytes_per_chunk || + dev->param.inband_tags) { + /* An incomplete start or end chunk (or maybe both + * start and end chunk), or we're using inband tags, + * so we want to use the cache buffers. + */ + if (dev->param.n_caches > 0) { + struct yaffs_cache *cache; + + /* If we can't find the data in the cache, then + * load the cache */ + cache = yaffs_find_chunk_cache(in, chunk); + + if (!cache && + yaffs_check_alloc_available(dev, 1)) { + cache = yaffs_grab_chunk_cache(dev); + cache->object = in; + cache->chunk_id = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_rd_data_obj(in, chunk, + cache->data); + } else if (cache && + !cache->dirty && + !yaffs_check_alloc_available(dev, + 1)) { + /* Drop the cache if it was a read cache + * item and no space check has been made + * for it. + */ + cache = NULL; + } + + if (cache) { + yaffs_use_cache(dev, cache, 1); + cache->locked = 1; + + memcpy(&cache->data[start], buffer, + n_copy); + + cache->locked = 0; + cache->n_bytes = n_writeback; + + if (write_trhrough) { + chunk_written = + yaffs_wr_data_obj + (cache->object, + cache->chunk_id, + cache->data, + cache->n_bytes, 1); + cache->dirty = 0; + } + } else { + chunk_written = -1; /* fail write */ + } + } else { + /* An incomplete start or end chunk (or maybe + * both start and end chunk). Read into the + * local buffer then copy over and write back. + */ + + u8 *local_buffer = yaffs_get_temp_buffer(dev); + + yaffs_rd_data_obj(in, chunk, local_buffer); + memcpy(&local_buffer[start], buffer, n_copy); + + chunk_written = + yaffs_wr_data_obj(in, chunk, + local_buffer, + n_writeback, 0); + + yaffs_release_temp_buffer(dev, local_buffer); + } + } else { + /* A full chunk. Write directly from the buffer. */ + + chunk_written = + yaffs_wr_data_obj(in, chunk, buffer, + dev->data_bytes_per_chunk, 0); + + /* Since we've overwritten the cached data, + * we better invalidate it. */ + yaffs_invalidate_chunk_cache(in, chunk); + } + + if (chunk_written >= 0) { + n -= n_copy; + offset += n_copy; + buffer += n_copy; + n_done += n_copy; + } + } + + /* Update file object */ + + if ((start_write + n_done) > in->variant.file_variant.file_size) + in->variant.file_variant.file_size = (start_write + n_done); + + in->dirty = 1; + return n_done; +} + +int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_trhrough) +{ + yaffs2_handle_hole(in, offset); + return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_trhrough); +} + +/* ---------------------- File resizing stuff ------------------ */ + +static void yaffs_prune_chunks(struct yaffs_obj *in, int new_size) +{ + + struct yaffs_dev *dev = in->my_dev; + int old_size = in->variant.file_variant.file_size; + int i; + int chunk_id; + int last_del = 1 + (old_size - 1) / dev->data_bytes_per_chunk; + int start_del = 1 + (new_size + dev->data_bytes_per_chunk - 1) / + dev->data_bytes_per_chunk; + + + /* Delete backwards so that we don't end up with holes if + * power is lost part-way through the operation. + */ + for (i = last_del; i >= start_del; i--) { + /* NB this could be optimised somewhat, + * eg. could retrieve the tags and write them without + * using yaffs_chunk_del + */ + + chunk_id = yaffs_find_del_file_chunk(in, i, NULL); + + if (chunk_id < 1) + continue; + + if (chunk_id < + (dev->internal_start_block * dev->param.chunks_per_block) || + chunk_id >= + ((dev->internal_end_block + 1) * + dev->param.chunks_per_block)) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Found daft chunk_id %d for %d", + chunk_id, i); + } else { + in->n_data_chunks--; + yaffs_chunk_del(dev, chunk_id, 1, __LINE__); + } + } +} + +void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) +{ + int new_full; + u32 new_partial; + struct yaffs_dev *dev = obj->my_dev; + + yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); + + yaffs_prune_chunks(obj, new_size); + + if (new_partial != 0) { + int last_chunk = 1 + new_full; + u8 *local_buffer = yaffs_get_temp_buffer(dev); + + /* Rewrite the last chunk with its new size and zero pad */ + yaffs_rd_data_obj(obj, last_chunk, local_buffer); + memset(local_buffer + new_partial, 0, + dev->data_bytes_per_chunk - new_partial); + + yaffs_wr_data_obj(obj, last_chunk, local_buffer, + new_partial, 1); + + yaffs_release_temp_buffer(dev, local_buffer); + } + + obj->variant.file_variant.file_size = new_size; + + yaffs_prune_tree(dev, &obj->variant.file_variant); +} + +int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) +{ + struct yaffs_dev *dev = in->my_dev; + int old_size = in->variant.file_variant.file_size; + + yaffs_flush_file_cache(in); + yaffs_invalidate_whole_cache(in); + + yaffs_check_gc(dev, 0); + + if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) + return YAFFS_FAIL; + + if (new_size == old_size) + return YAFFS_OK; + + if (new_size > old_size) { + yaffs2_handle_hole(in, new_size); + in->variant.file_variant.file_size = new_size; + } else { + /* new_size < old_size */ + yaffs_resize_file_down(in, new_size); + } + + /* Write a new object header to reflect the resize. + * show we've shrunk the file, if need be + * Do this only if the file is not in the deleted directories + * and is not shadowed. + */ + if (in->parent && + !in->is_shadowed && + in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && + in->parent->obj_id != YAFFS_OBJECTID_DELETED) + yaffs_update_oh(in, NULL, 0, 0, 0, NULL); + + return YAFFS_OK; +} + +int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) +{ + if (!in->dirty) + return YAFFS_OK; + + yaffs_flush_file_cache(in); + + if (data_sync) + return YAFFS_OK; + + if (update_time) + yaffs_load_current_time(in, 0, 0); + + return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? + YAFFS_OK : YAFFS_FAIL; +} + + +/* yaffs_del_file deletes the whole file data + * and the inode associated with the file. + * It does not delete the links associated with the file. + */ +static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) +{ + int ret_val; + int del_now = 0; + struct yaffs_dev *dev = in->my_dev; + + if (!in->my_inode) + del_now = 1; + + if (del_now) { + ret_val = + yaffs_change_obj_name(in, in->my_dev->del_dir, + _Y("deleted"), 0, 0); + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: immediate deletion of file %d", + in->obj_id); + in->deleted = 1; + in->my_dev->n_deleted_files++; + if (dev->param.disable_soft_del || dev->param.is_yaffs2) + yaffs_resize_file(in, 0); + yaffs_soft_del_file(in); + } else { + ret_val = + yaffs_change_obj_name(in, in->my_dev->unlinked_dir, + _Y("unlinked"), 0, 0); + } + return ret_val; +} + +int yaffs_del_file(struct yaffs_obj *in) +{ + int ret_val = YAFFS_OK; + int deleted; /* Need to cache value on stack if in is freed */ + struct yaffs_dev *dev = in->my_dev; + + if (dev->param.disable_soft_del || dev->param.is_yaffs2) + yaffs_resize_file(in, 0); + + if (in->n_data_chunks > 0) { + /* Use soft deletion if there is data in the file. + * That won't be the case if it has been resized to zero. + */ + if (!in->unlinked) + ret_val = yaffs_unlink_file_if_needed(in); + + deleted = in->deleted; + + if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { + in->deleted = 1; + deleted = 1; + in->my_dev->n_deleted_files++; + yaffs_soft_del_file(in); + } + return deleted ? YAFFS_OK : YAFFS_FAIL; + } else { + /* The file has no data chunks so we toss it immediately */ + yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); + in->variant.file_variant.top = NULL; + yaffs_generic_obj_del(in); + + return YAFFS_OK; + } +} + +int yaffs_is_non_empty_dir(struct yaffs_obj *obj) +{ + return (obj && + obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && + !(list_empty(&obj->variant.dir_variant.children)); +} + +static int yaffs_del_dir(struct yaffs_obj *obj) +{ + /* First check that the directory is empty. */ + if (yaffs_is_non_empty_dir(obj)) + return YAFFS_FAIL; + + return yaffs_generic_obj_del(obj); +} + +static int yaffs_del_symlink(struct yaffs_obj *in) +{ + kfree(in->variant.symlink_variant.alias); + in->variant.symlink_variant.alias = NULL; + + return yaffs_generic_obj_del(in); +} + +static int yaffs_del_link(struct yaffs_obj *in) +{ + /* remove this hardlink from the list associated with the equivalent + * object + */ + list_del_init(&in->hard_links); + return yaffs_generic_obj_del(in); +} + +int yaffs_del_obj(struct yaffs_obj *obj) +{ + int ret_val = -1; + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + ret_val = yaffs_del_file(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + if (!list_empty(&obj->variant.dir_variant.dirty)) { + yaffs_trace(YAFFS_TRACE_BACKGROUND, + "Remove object %d from dirty directories", + obj->obj_id); + list_del_init(&obj->variant.dir_variant.dirty); + } + return yaffs_del_dir(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + ret_val = yaffs_del_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + ret_val = yaffs_del_link(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + ret_val = yaffs_generic_obj_del(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + ret_val = 0; + break; /* should not happen. */ + } + return ret_val; +} + +static int yaffs_unlink_worker(struct yaffs_obj *obj) +{ + int del_now = 0; + + if (!obj) + return YAFFS_FAIL; + + if (!obj->my_inode) + del_now = 1; + + yaffs_update_parent(obj->parent); + + if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { + return yaffs_del_link(obj); + } else if (!list_empty(&obj->hard_links)) { + /* Curve ball: We're unlinking an object that has a hardlink. + * + * This problem arises because we are not strictly following + * The Linux link/inode model. + * + * We can't really delete the object. + * Instead, we do the following: + * - Select a hardlink. + * - Unhook it from the hard links + * - Move it from its parent directory so that the rename works. + * - Rename the object to the hardlink's name. + * - Delete the hardlink + */ + + struct yaffs_obj *hl; + struct yaffs_obj *parent; + int ret_val; + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + hl = list_entry(obj->hard_links.next, struct yaffs_obj, + hard_links); + + yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); + parent = hl->parent; + + list_del_init(&hl->hard_links); + + yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); + + ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); + + if (ret_val == YAFFS_OK) + ret_val = yaffs_generic_obj_del(hl); + + return ret_val; + + } else if (del_now) { + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return yaffs_del_file(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + list_del_init(&obj->variant.dir_variant.dirty); + return yaffs_del_dir(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_del_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + return yaffs_generic_obj_del(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + return YAFFS_FAIL; + } + } else if (yaffs_is_non_empty_dir(obj)) { + return YAFFS_FAIL; + } else { + return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, + _Y("unlinked"), 0, 0); + } +} + +static int yaffs_unlink_obj(struct yaffs_obj *obj) +{ + if (obj && obj->unlink_allowed) + return yaffs_unlink_worker(obj); + + return YAFFS_FAIL; +} + +int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) +{ + struct yaffs_obj *obj; + + obj = yaffs_find_by_name(dir, name); + return yaffs_unlink_obj(obj); +} + +/* Note: + * If old_name is NULL then we take old_dir as the object to be renamed. + */ +int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, + struct yaffs_obj *new_dir, const YCHAR *new_name) +{ + struct yaffs_obj *obj = NULL; + struct yaffs_obj *existing_target = NULL; + int force = 0; + int result; + struct yaffs_dev *dev; + + if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + BUG(); + return YAFFS_FAIL; + } + if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + BUG(); + return YAFFS_FAIL; + } + + dev = old_dir->my_dev; + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + /* Special case for case insemsitive systems. + * While look-up is case insensitive, the name isn't. + * Therefore we might want to change x.txt to X.txt + */ + if (old_dir == new_dir && + old_name && new_name && + strcmp(old_name, new_name) == 0) + force = 1; +#endif + + if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > + YAFFS_MAX_NAME_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + + if (old_name) + obj = yaffs_find_by_name(old_dir, old_name); + else{ + obj = old_dir; + old_dir = obj->parent; + } + + if (obj && obj->rename_allowed) { + /* Now handle an existing target, if there is one */ + existing_target = yaffs_find_by_name(new_dir, new_name); + if (yaffs_is_non_empty_dir(existing_target)) { + return YAFFS_FAIL; /* ENOTEMPTY */ + } else if (existing_target && existing_target != obj) { + /* Nuke the target first, using shadowing, + * but only if it isn't the same object. + * + * Note we must disable gc here otherwise it can mess + * up the shadowing. + * + */ + dev->gc_disable = 1; + yaffs_change_obj_name(obj, new_dir, new_name, force, + existing_target->obj_id); + existing_target->is_shadowed = 1; + yaffs_unlink_obj(existing_target); + dev->gc_disable = 0; + } + + result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); + + yaffs_update_parent(old_dir); + if (new_dir != old_dir) + yaffs_update_parent(new_dir); + + return result; + } + return YAFFS_FAIL; +} + +/*----------------------- Initialisation Scanning ---------------------- */ + +void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, + int backward_scanning) +{ + struct yaffs_obj *obj; + + if (backward_scanning) { + /* Handle YAFFS2 case (backward scanning) + * If the shadowed object exists then ignore. + */ + obj = yaffs_find_by_number(dev, obj_id); + if (obj) + return; + } + + /* Let's create it (if it does not exist) assuming it is a file so that + * it can do shrinking etc. + * We put it in unlinked dir to be cleaned up after the scanning + */ + obj = + yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); + if (!obj) + return; + obj->is_shadowed = 1; + yaffs_add_obj_to_dir(dev->unlinked_dir, obj); + obj->variant.file_variant.shrink_size = 0; + obj->valid = 1; /* So that we don't read any other info. */ +} + +void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) +{ + struct list_head *lh; + struct list_head *save; + struct yaffs_obj *hl; + struct yaffs_obj *in; + + list_for_each_safe(lh, save, hard_list) { + hl = list_entry(lh, struct yaffs_obj, hard_links); + in = yaffs_find_by_number(dev, + hl->variant.hardlink_variant.equiv_id); + + if (in) { + /* Add the hardlink pointers */ + hl->variant.hardlink_variant.equiv_obj = in; + list_add(&hl->hard_links, &in->hard_links); + } else { + /* Todo Need to report/handle this better. + * Got a problem... hardlink to a non-existant object + */ + hl->variant.hardlink_variant.equiv_obj = NULL; + INIT_LIST_HEAD(&hl->hard_links); + } + } +} + +static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) +{ + /* + * Sort out state of unlinked and deleted objects after scanning. + */ + struct list_head *i; + struct list_head *n; + struct yaffs_obj *l; + + if (dev->read_only) + return; + + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinked_dir->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + yaffs_del_obj(l); + } + + list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + yaffs_del_obj(l); + } +} + +/* + * This code iterates through all the objects making sure that they are rooted. + * Any unrooted objects are re-rooted in lost+found. + * An object needs to be in one of: + * - Directly under deleted, unlinked + * - Directly or indirectly under root. + * + * Note: + * This code assumes that we don't ever change the current relationships + * between directories: + * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL + * lost-n-found->parent == root_dir + * + * This fixes the problem where directories might have inadvertently been + * deleted leaving the object "hanging" without being rooted in the + * directory tree. + */ + +static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) +{ + return (obj == dev->del_dir || + obj == dev->unlinked_dir || obj == dev->root_dir); +} + +static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_obj *parent; + int i; + struct list_head *lh; + struct list_head *n; + int depth_limit; + int hanging; + + if (dev->read_only) + return; + + /* Iterate through the objects in each hash entry, + * looking at each object. + * Make sure it is rooted. + */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + parent = obj->parent; + + if (yaffs_has_null_parent(dev, obj)) { + /* These directories are not hanging */ + hanging = 0; + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + hanging = 1; + } else if (yaffs_has_null_parent(dev, parent)) { + hanging = 0; + } else { + /* + * Need to follow the parent chain to + * see if it is hanging. + */ + hanging = 0; + depth_limit = 100; + + while (parent != dev->root_dir && + parent->parent && + parent->parent->variant_type == + YAFFS_OBJECT_TYPE_DIRECTORY && + depth_limit > 0) { + parent = parent->parent; + depth_limit--; + } + if (parent != dev->root_dir) + hanging = 1; + } + if (hanging) { + yaffs_trace(YAFFS_TRACE_SCAN, + "Hanging object %d moved to lost and found", + obj->obj_id); + yaffs_add_obj_to_dir(dev->lost_n_found, obj); + } + } + } +} + +/* + * Delete directory contents for cleaning up lost and found. + */ +static void yaffs_del_dir_contents(struct yaffs_obj *dir) +{ + struct yaffs_obj *obj; + struct list_head *lh; + struct list_head *n; + + if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + BUG(); + + list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { + obj = list_entry(lh, struct yaffs_obj, siblings); + if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) + yaffs_del_dir_contents(obj); + yaffs_trace(YAFFS_TRACE_SCAN, + "Deleting lost_found object %d", + obj->obj_id); + yaffs_unlink_obj(obj); + } +} + +static void yaffs_empty_l_n_f(struct yaffs_dev *dev) +{ + yaffs_del_dir_contents(dev->lost_n_found); +} + + +struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, + const YCHAR *name) +{ + int sum; + struct list_head *i; + YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; + struct yaffs_obj *l; + + if (!name) + return NULL; + + if (!directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_find_by_name: null pointer directory" + ); + BUG(); + return NULL; + } + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "tragedy: yaffs_find_by_name: non-directory" + ); + BUG(); + } + + sum = yaffs_calc_name_sum(name); + + list_for_each(i, &directory->variant.dir_variant.children) { + l = list_entry(i, struct yaffs_obj, siblings); + + if (l->parent != directory) + BUG(); + + yaffs_check_obj_details_loaded(l); + + /* Special case for lost-n-found */ + if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { + if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) + return l; + } else if (l->sum == sum || l->hdr_chunk <= 0) { + /* LostnFound chunk called Objxxx + * Do a real check + */ + yaffs_get_obj_name(l, buffer, + YAFFS_MAX_NAME_LENGTH + 1); + if (strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH) == 0) + return l; + } + } + return NULL; +} + +/* GetEquivalentObject dereferences any hard links to get to the + * actual object. + */ + +struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) +{ + if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { + obj = obj->variant.hardlink_variant.equiv_obj; + yaffs_check_obj_details_loaded(obj); + } + return obj; +} + +/* + * A note or two on object names. + * * If the object name is missing, we then make one up in the form objnnn + * + * * ASCII names are stored in the object header's name field from byte zero + * * Unicode names are historically stored starting from byte zero. + * + * Then there are automatic Unicode names... + * The purpose of these is to save names in a way that can be read as + * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII + * system to share files. + * + * These automatic unicode are stored slightly differently... + * - If the name can fit in the ASCII character space then they are saved as + * ascii names as per above. + * - If the name needs Unicode then the name is saved in Unicode + * starting at oh->name[1]. + + */ +static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, + int buffer_size) +{ + /* Create an object name if we could not find one. */ + if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { + YCHAR local_name[20]; + YCHAR num_string[20]; + YCHAR *x = &num_string[19]; + unsigned v = obj->obj_id; + num_string[19] = 0; + while (v > 0) { + x--; + *x = '0' + (v % 10); + v /= 10; + } + /* make up a name */ + strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); + strcat(local_name, x); + strncpy(name, local_name, buffer_size - 1); + } +} + +int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) +{ + memset(name, 0, buffer_size * sizeof(YCHAR)); + yaffs_check_obj_details_loaded(obj); + if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { + strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); + } else if (obj->short_name[0]) { + strcpy(name, obj->short_name); + } else if (obj->hdr_chunk > 0) { + int result; + u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); + + struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; + + memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); + + if (obj->hdr_chunk > 0) { + result = yaffs_rd_chunk_tags_nand(obj->my_dev, + obj->hdr_chunk, + buffer, NULL); + } + yaffs_load_name_from_oh(obj->my_dev, name, oh->name, + buffer_size); + + yaffs_release_temp_buffer(obj->my_dev, buffer); + } + + yaffs_fix_null_name(obj, name, buffer_size); + + return strnlen(name, YAFFS_MAX_NAME_LENGTH); +} + +int yaffs_get_obj_length(struct yaffs_obj *obj) +{ + /* Dereference any hard linking */ + obj = yaffs_get_equivalent_obj(obj); + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + return obj->variant.file_variant.file_size; + if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { + if (!obj->variant.symlink_variant.alias) + return 0; + return strnlen(obj->variant.symlink_variant.alias, + YAFFS_MAX_ALIAS_LENGTH); + } else { + /* Only a directory should drop through to here */ + return obj->my_dev->data_bytes_per_chunk; + } +} + +int yaffs_get_obj_link_count(struct yaffs_obj *obj) +{ + int count = 0; + struct list_head *i; + + if (!obj->unlinked) + count++; /* the object itself */ + + list_for_each(i, &obj->hard_links) + count++; /* add the hard links; */ + + return count; +} + +int yaffs_get_obj_inode(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + + return obj->obj_id; +} + +unsigned yaffs_get_obj_type(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return DT_DIR; + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return DT_LNK; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + if (S_ISFIFO(obj->yst_mode)) + return DT_FIFO; + if (S_ISCHR(obj->yst_mode)) + return DT_CHR; + if (S_ISBLK(obj->yst_mode)) + return DT_BLK; + if (S_ISSOCK(obj->yst_mode)) + return DT_SOCK; + return DT_REG; + break; + default: + return DT_REG; + break; + } +} + +YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) +{ + obj = yaffs_get_equivalent_obj(obj); + if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) + return yaffs_clone_str(obj->variant.symlink_variant.alias); + else + return yaffs_clone_str(_Y("")); +} + +/*--------------------------- Initialisation code -------------------------- */ + +static int yaffs_check_dev_fns(const struct yaffs_dev *dev) +{ + /* Common functions, gotta have */ + if (!dev->param.erase_fn || !dev->param.initialise_flash_fn) + return 0; + + /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ + if (dev->param.write_chunk_tags_fn && + dev->param.read_chunk_tags_fn && + !dev->param.write_chunk_fn && + !dev->param.read_chunk_fn && + dev->param.bad_block_fn && dev->param.query_block_fn) + return 1; + + /* Can use the "spare" style interface for yaffs1 */ + if (!dev->param.is_yaffs2 && + !dev->param.write_chunk_tags_fn && + !dev->param.read_chunk_tags_fn && + dev->param.write_chunk_fn && + dev->param.read_chunk_fn && + !dev->param.bad_block_fn && !dev->param.query_block_fn) + return 1; + + return 0; /* bad */ +} + +static int yaffs_create_initial_dir(struct yaffs_dev *dev) +{ + /* Initialise the unlinked, deleted, root and lost+found directories */ + dev->lost_n_found = dev->root_dir = NULL; + dev->unlinked_dir = dev->del_dir = NULL; + dev->unlinked_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); + dev->del_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); + dev->root_dir = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, + YAFFS_ROOT_MODE | S_IFDIR); + dev->lost_n_found = + yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, + YAFFS_LOSTNFOUND_MODE | S_IFDIR); + + if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir + && dev->del_dir) { + yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); + return YAFFS_OK; + } + return YAFFS_FAIL; +} + +int yaffs_guts_initialise(struct yaffs_dev *dev) +{ + int init_failed = 0; + unsigned x; + int bits; + + yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_guts_initialise()"); + + /* Check stuff that must be set */ + + if (!dev) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "yaffs: Need a device" + ); + return YAFFS_FAIL; + } + + if (dev->is_mounted) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); + return YAFFS_FAIL; + } + + dev->internal_start_block = dev->param.start_block; + dev->internal_end_block = dev->param.end_block; + dev->block_offset = 0; + dev->chunk_offset = 0; + dev->n_free_chunks = 0; + + dev->gc_block = 0; + + if (dev->param.start_block == 0) { + dev->internal_start_block = dev->param.start_block + 1; + dev->internal_end_block = dev->param.end_block + 1; + dev->block_offset = 1; + dev->chunk_offset = dev->param.chunks_per_block; + } + + /* Check geometry parameters. */ + + if ((!dev->param.inband_tags && dev->param.is_yaffs2 && + dev->param.total_bytes_per_chunk < 1024) || + (!dev->param.is_yaffs2 && + dev->param.total_bytes_per_chunk < 512) || + (dev->param.inband_tags && !dev->param.is_yaffs2) || + dev->param.chunks_per_block < 2 || + dev->param.n_reserved_blocks < 2 || + dev->internal_start_block <= 0 || + dev->internal_end_block <= 0 || + dev->internal_end_block <= + (dev->internal_start_block + dev->param.n_reserved_blocks + 2) + ) { + /* otherwise it is too small */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", + dev->param.total_bytes_per_chunk, + dev->param.is_yaffs2 ? "2" : "", + dev->param.inband_tags); + return YAFFS_FAIL; + } + + if (yaffs_init_nand(dev) != YAFFS_OK) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); + return YAFFS_FAIL; + } + + /* Sort out space for inband tags, if required */ + if (dev->param.inband_tags) + dev->data_bytes_per_chunk = + dev->param.total_bytes_per_chunk - + sizeof(struct yaffs_packed_tags2_tags_only); + else + dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; + + /* Got the right mix of functions? */ + if (!yaffs_check_dev_fns(dev)) { + /* Function missing */ + yaffs_trace(YAFFS_TRACE_ALWAYS, + "device function(s) missing or wrong"); + + return YAFFS_FAIL; + } + + /* Finished with most checks. Further checks happen later on too. */ + + dev->is_mounted = 1; + + /* OK now calculate a few things for the device */ + + /* + * Calculate all the chunk size manipulation numbers: + */ + x = dev->data_bytes_per_chunk; + /* We always use dev->chunk_shift and dev->chunk_div */ + dev->chunk_shift = calc_shifts(x); + x >>= dev->chunk_shift; + dev->chunk_div = x; + /* We only use chunk mask if chunk_div is 1 */ + dev->chunk_mask = (1 << dev->chunk_shift) - 1; + + /* + * Calculate chunk_grp_bits. + * We need to find the next power of 2 > than internal_end_block + */ + + x = dev->param.chunks_per_block * (dev->internal_end_block + 1); + + bits = calc_shifts_ceiling(x); + + /* Set up tnode width if wide tnodes are enabled. */ + if (!dev->param.wide_tnodes_disabled) { + /* bits must be even so that we end up with 32-bit words */ + if (bits & 1) + bits++; + if (bits < 16) + dev->tnode_width = 16; + else + dev->tnode_width = bits; + } else { + dev->tnode_width = 16; + } + + dev->tnode_mask = (1 << dev->tnode_width) - 1; + + /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), + * so if the bitwidth of the + * chunk range we're using is greater than 16 we need + * to figure out chunk shift and chunk_grp_size + */ + + if (bits <= dev->tnode_width) + dev->chunk_grp_bits = 0; + else + dev->chunk_grp_bits = bits - dev->tnode_width; + + dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; + if (dev->tnode_size < sizeof(struct yaffs_tnode)) + dev->tnode_size = sizeof(struct yaffs_tnode); + + dev->chunk_grp_size = 1 << dev->chunk_grp_bits; + + if (dev->param.chunks_per_block < dev->chunk_grp_size) { + /* We have a problem because the soft delete won't work if + * the chunk group size > chunks per block. + * This can be remedied by using larger "virtual blocks". + */ + yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); + + return YAFFS_FAIL; + } + + /* Finished verifying the device, continue with initialisation */ + + /* More device initialisation */ + dev->all_gcs = 0; + dev->passive_gc_count = 0; + dev->oldest_dirty_gc_count = 0; + dev->bg_gcs = 0; + dev->gc_block_finder = 0; + dev->buffered_block = -1; + dev->doing_buffered_block_rewrite = 0; + dev->n_deleted_files = 0; + dev->n_bg_deletions = 0; + dev->n_unlinked_files = 0; + dev->n_ecc_fixed = 0; + dev->n_ecc_unfixed = 0; + dev->n_tags_ecc_fixed = 0; + dev->n_tags_ecc_unfixed = 0; + dev->n_erase_failures = 0; + dev->n_erased_blocks = 0; + dev->gc_disable = 0; + dev->has_pending_prioritised_gc = 1; + /* Assume the worst for now, will get fixed on first GC */ + INIT_LIST_HEAD(&dev->dirty_dirs); + dev->oldest_dirty_seq = 0; + dev->oldest_dirty_block = 0; + + /* Initialise temporary buffers and caches. */ + if (!yaffs_init_tmp_buffers(dev)) + init_failed = 1; + + dev->cache = NULL; + dev->gc_cleanup_list = NULL; + + if (!init_failed && dev->param.n_caches > 0) { + int i; + void *buf; + int cache_bytes = + dev->param.n_caches * sizeof(struct yaffs_cache); + + if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) + dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; + + dev->cache = kmalloc(cache_bytes, GFP_NOFS); + + buf = (u8 *) dev->cache; + + if (dev->cache) + memset(dev->cache, 0, cache_bytes); + + for (i = 0; i < dev->param.n_caches && buf; i++) { + dev->cache[i].object = NULL; + dev->cache[i].last_use = 0; + dev->cache[i].dirty = 0; + dev->cache[i].data = buf = + kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); + } + if (!buf) + init_failed = 1; + + dev->cache_last_use = 0; + } + + dev->cache_hits = 0; + + if (!init_failed) { + dev->gc_cleanup_list = + kmalloc(dev->param.chunks_per_block * sizeof(u32), + GFP_NOFS); + if (!dev->gc_cleanup_list) + init_failed = 1; + } + + if (dev->param.is_yaffs2) + dev->param.use_header_file_size = 1; + + if (!init_failed && !yaffs_init_blocks(dev)) + init_failed = 1; + + yaffs_init_tnodes_and_objs(dev); + + if (!init_failed && !yaffs_create_initial_dir(dev)) + init_failed = 1; + + if(!init_failed && dev->param.is_yaffs2 && + !dev->param.disable_summary && + !yaffs_summary_init(dev)) + init_failed = 1; + + if (!init_failed) { + /* Now scan the flash. */ + if (dev->param.is_yaffs2) { + if (yaffs2_checkpt_restore(dev)) { + yaffs_check_obj_details_loaded(dev->root_dir); + yaffs_trace(YAFFS_TRACE_CHECKPOINT | + YAFFS_TRACE_MOUNT, + "yaffs: restored from checkpoint" + ); + } else { + + /* Clean up the mess caused by an aborted + * checkpoint load then scan backwards. + */ + yaffs_deinit_blocks(dev); + + yaffs_deinit_tnodes_and_objs(dev); + + dev->n_erased_blocks = 0; + dev->n_free_chunks = 0; + dev->alloc_block = -1; + dev->alloc_page = -1; + dev->n_deleted_files = 0; + dev->n_unlinked_files = 0; + dev->n_bg_deletions = 0; + + if (!init_failed && !yaffs_init_blocks(dev)) + init_failed = 1; + + yaffs_init_tnodes_and_objs(dev); + + if (!init_failed + && !yaffs_create_initial_dir(dev)) + init_failed = 1; + + if (!init_failed && !yaffs2_scan_backwards(dev)) + init_failed = 1; + } + } else if (!yaffs1_scan(dev)) { + init_failed = 1; + } + + yaffs_strip_deleted_objs(dev); + yaffs_fix_hanging_objs(dev); + if (dev->param.empty_lost_n_found) + yaffs_empty_l_n_f(dev); + } + + if (init_failed) { + /* Clean up the mess */ + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: yaffs_guts_initialise() aborted."); + + yaffs_deinitialise(dev); + return YAFFS_FAIL; + } + + /* Zero out stats */ + dev->n_page_reads = 0; + dev->n_page_writes = 0; + dev->n_erasures = 0; + dev->n_gc_copies = 0; + dev->n_retried_writes = 0; + + dev->n_retired_blocks = 0; + + yaffs_verify_free_chunks(dev); + yaffs_verify_blocks(dev); + + /* Clean up any aborted checkpoint data */ + if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) + yaffs2_checkpt_invalidate(dev); + + yaffs_trace(YAFFS_TRACE_TRACING, + "yaffs: yaffs_guts_initialise() done."); + return YAFFS_OK; +} + +void yaffs_deinitialise(struct yaffs_dev *dev) +{ + if (dev->is_mounted) { + int i; + + yaffs_deinit_blocks(dev); + yaffs_deinit_tnodes_and_objs(dev); + yaffs_summary_deinit(dev); + + if (dev->param.n_caches > 0 && dev->cache) { + + for (i = 0; i < dev->param.n_caches; i++) { + kfree(dev->cache[i].data); + dev->cache[i].data = NULL; + } + + kfree(dev->cache); + dev->cache = NULL; + } + + kfree(dev->gc_cleanup_list); + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) + kfree(dev->temp_buffer[i].buffer); + + dev->is_mounted = 0; + + if (dev->param.deinitialise_flash_fn) + dev->param.deinitialise_flash_fn(dev); + } +} + +int yaffs_count_free_chunks(struct yaffs_dev *dev) +{ + int n_free = 0; + int b; + struct yaffs_block_info *blk; + + blk = dev->block_info; + for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { + switch (blk->block_state) { + case YAFFS_BLOCK_STATE_EMPTY: + case YAFFS_BLOCK_STATE_ALLOCATING: + case YAFFS_BLOCK_STATE_COLLECTING: + case YAFFS_BLOCK_STATE_FULL: + n_free += + (dev->param.chunks_per_block - blk->pages_in_use + + blk->soft_del_pages); + break; + default: + break; + } + blk++; + } + return n_free; +} + +int yaffs_get_n_free_chunks(struct yaffs_dev *dev) +{ + /* This is what we report to the outside world */ + int n_free; + int n_dirty_caches; + int blocks_for_checkpt; + int i; + + n_free = dev->n_free_chunks; + n_free += dev->n_deleted_files; + + /* Now count and subtract the number of dirty chunks in the cache. */ + + for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { + if (dev->cache[i].dirty) + n_dirty_caches++; + } + + n_free -= n_dirty_caches; + + n_free -= + ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); + + /* Now figure checkpoint space and report that... */ + blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); + + n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); + + if (n_free < 0) + n_free = 0; + + return n_free; +} diff --git a/fs/yaffs2-new/yaffs_guts.h b/fs/yaffs2-new/yaffs_guts.h new file mode 100644 index 00000000000..490122aa166 --- /dev/null +++ b/fs/yaffs2-new/yaffs_guts.h @@ -0,0 +1,938 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GUTS_H__ +#define __YAFFS_GUTS_H__ + +#include "yportenv.h" + +#define YAFFS_OK 1 +#define YAFFS_FAIL 0 + +/* Give us a Y=0x59, + * Give us an A=0x41, + * Give us an FF=0xff + * Give us an S=0x53 + * And what have we got... + */ +#define YAFFS_MAGIC 0x5941ff53 + +#define YAFFS_NTNODES_LEVEL0 16 +#define YAFFS_TNODES_LEVEL0_BITS 4 +#define YAFFS_TNODES_LEVEL0_MASK 0xf + +#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) +#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) +#define YAFFS_TNODES_INTERNAL_MASK 0x7 +#define YAFFS_TNODES_MAX_LEVEL 6 + + +/* Constants for YAFFS1 mode */ +#define YAFFS_BYTES_PER_SPARE 16 +#define YAFFS_BYTES_PER_CHUNK 512 +#define YAFFS_CHUNK_SIZE_SHIFT 9 +#define YAFFS_CHUNKS_PER_BLOCK 32 +#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) + +#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 +#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 + +#define YAFFS_MAX_CHUNK_ID 0x000fffff + +#define YAFFS_ALLOCATION_NOBJECTS 100 +#define YAFFS_ALLOCATION_NTNODES 100 +#define YAFFS_ALLOCATION_NLINKS 100 + +#define YAFFS_NOBJECT_BUCKETS 256 + +#define YAFFS_OBJECT_SPACE 0x40000 +#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) + +#define YAFFS_CHECKPOINT_VERSION 4 + +#ifdef CONFIG_YAFFS_UNICODE +#define YAFFS_MAX_NAME_LENGTH 127 +#define YAFFS_MAX_ALIAS_LENGTH 79 +#else +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 +#endif + +#define YAFFS_SHORT_NAME_LENGTH 15 + +/* Some special object ids for pseudo objects */ +#define YAFFS_OBJECTID_ROOT 1 +#define YAFFS_OBJECTID_LOSTNFOUND 2 +#define YAFFS_OBJECTID_UNLINKED 3 +#define YAFFS_OBJECTID_DELETED 4 + +/* Fake object Id for summary data */ +#define YAFFS_OBJECTID_SUMMARY 0x10 + +/* Pseudo object ids for checkpointing */ +#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 +#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 + +#define YAFFS_MAX_SHORT_OP_CACHES 20 + +#define YAFFS_N_TEMP_BUFFERS 6 + +/* We limit the number attempts at sucessfully saving a chunk of data. + * Small-page devices have 32 pages per block; large-page devices have 64. + * Default to something in the order of 5 to 10 blocks worth of chunks. + */ +#define YAFFS_WR_ATTEMPTS (5*64) + +/* Sequence numbers are used in YAFFS2 to determine block allocation order. + * The range is limited slightly to help distinguish bad numbers from good. + * This also allows us to perhaps in the future use special numbers for + * special purposes. + * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, + * and is a larger number than the lifetime of a 2GB device. + */ +#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 +#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 + +/* Special sequence number for bad block that failed to be marked bad */ +#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 + +/* ChunkCache is used for short read/write operations.*/ +struct yaffs_cache { + struct yaffs_obj *object; + int chunk_id; + int last_use; + int dirty; + int n_bytes; /* Only valid if the cache is dirty */ + int locked; /* Can't push out or flush while locked. */ + u8 *data; +}; + +/* yaffs1 tags structures in RAM + * NB This uses bitfield. Bitfields should not straddle a u32 boundary + * otherwise the structure size will get blown out. + */ + +struct yaffs_tags { + unsigned chunk_id:20; + unsigned serial_number:2; + unsigned n_bytes_lsb:10; + unsigned obj_id:18; + unsigned ecc:12; + unsigned n_bytes_msb:2; +}; + +union yaffs_tags_union { + struct yaffs_tags as_tags; + u8 as_bytes[8]; +}; + + +/* Stuff used for extended tags in YAFFS2 */ + +enum yaffs_ecc_result { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +}; + +enum yaffs_obj_type { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +}; + +#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL + +struct yaffs_ext_tags { + unsigned chunk_used; /* Status of the chunk: used or unused */ + unsigned obj_id; /* If 0 this is not used */ + unsigned chunk_id; /* If 0 this is a header, else a data chunk */ + unsigned n_bytes; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + enum yaffs_ecc_result ecc_result; + unsigned block_bad; + + /* YAFFS 1 stuff */ + unsigned is_deleted; /* The chunk is marked deleted */ + unsigned serial_number; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned seq_number; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extra_available; /* Extra info available if not zero */ + unsigned extra_parent_id; /* The parent object */ + unsigned extra_is_shrink; /* Is it a shrink header? */ + unsigned extra_shadows; /* Does this shadow another object? */ + + enum yaffs_obj_type extra_obj_type; /* What object type? */ + + unsigned extra_length; /* Length if it is a file */ + unsigned extra_equiv_id; /* Equivalent object for a hard link */ +}; + +/* Spare structure for YAFFS1 */ +struct yaffs_spare { + u8 tb0; + u8 tb1; + u8 tb2; + u8 tb3; + u8 page_status; /* set to 0 to delete the chunk */ + u8 block_status; + u8 tb4; + u8 tb5; + u8 ecc1[3]; + u8 tb6; + u8 tb7; + u8 ecc2[3]; +}; + +/*Special structure for passing through to mtd */ +struct yaffs_nand_spare { + struct yaffs_spare spare; + int eccres1; + int eccres2; +}; + +/* Block data in RAM */ + +enum yaffs_block_state { + YAFFS_BLOCK_STATE_UNKNOWN = 0, + + YAFFS_BLOCK_STATE_SCANNING, + /* Being scanned */ + + YAFFS_BLOCK_STATE_NEEDS_SCAN, + /* The block might have something on it (ie it is allocating or full, + * perhaps empty) but it needs to be scanned to determine its true + * state. + * This state is only valid during scanning. + * NB We tolerate empty because the pre-scanner might be incapable of + * deciding + * However, if this state is returned on a YAFFS2 device, + * then we expect a sequence number + */ + + YAFFS_BLOCK_STATE_EMPTY, + /* This block is empty */ + + YAFFS_BLOCK_STATE_ALLOCATING, + /* This block is partially allocated. + * At least one page holds valid data. + * This is the one currently being used for page + * allocation. Should never be more than one of these. + * If a block is only partially allocated at mount it is treated as + * full. + */ + + YAFFS_BLOCK_STATE_FULL, + /* All the pages in this block have been allocated. + * If a block was only partially allocated when mounted we treat + * it as fully allocated. + */ + + YAFFS_BLOCK_STATE_DIRTY, + /* The block was full and now all chunks have been deleted. + * Erase me, reuse me. + */ + + YAFFS_BLOCK_STATE_CHECKPOINT, + /* This block is assigned to holding checkpoint data. */ + + YAFFS_BLOCK_STATE_COLLECTING, + /* This block is being garbage collected */ + + YAFFS_BLOCK_STATE_DEAD + /* This block has failed and is not in use */ +}; + +#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) + +struct yaffs_block_info { + + int soft_del_pages:10; /* number of soft deleted pages */ + int pages_in_use:10; /* number of pages in use */ + unsigned block_state:4; /* One of the above block states. */ + /* NB use unsigned because enum is sometimes + * an int */ + u32 needs_retiring:1; /* Data has failed on this block, */ + /*need to get valid data off and retire*/ + u32 skip_erased_check:1;/* Skip the erased check on this block */ + u32 gc_prioritise:1; /* An ECC check or blank check has failed. + Block should be prioritised for GC */ + u32 chunk_error_strikes:3; /* How many times we've had ecc etc + failures on this block and tried to reuse it */ + u32 has_summary:1; /* The block has a summary */ + + u32 has_shrink_hdr:1; /* This block has at least one shrink header */ + u32 seq_number; /* block sequence number for yaffs2 */ + +}; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +struct yaffs_obj_hdr { + enum yaffs_obj_type type; + + /* Apply to everything */ + int parent_obj_id; + u16 sum_no_longer_used; /* checksum of name. No longer used */ + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* The following apply to all object types except for hard links */ + u32 yst_mode; /* protection */ + + u32 yst_uid; + u32 yst_gid; + u32 yst_atime; + u32 yst_mtime; + u32 yst_ctime; + + /* File size applies to files only */ + int file_size; + + /* Equivalent object id applies to hard links only. */ + int equiv_id; + + /* Alias is for symlinks only. */ + YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + u32 yst_rdev; /* stuff for block and char devices (major/min) */ + + u32 win_ctime[2]; + u32 win_atime[2]; + u32 win_mtime[2]; + + u32 inband_shadowed_obj_id; + u32 inband_is_shrink; + + u32 reserved[2]; + int shadows_obj; /* This object header shadows the + specified object if > 0 */ + + /* is_shrink applies to object headers written when wemake a hole. */ + u32 is_shrink; + +}; + +/*--------------------------- Tnode -------------------------- */ + +struct yaffs_tnode { + struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; +}; + +/*------------------------ Object -----------------------------*/ +/* An object can be one of: + * - a directory (no data, has children links + * - a regular file (data.... not prunes :->). + * - a symlink [symbolic link] (the alias). + * - a hard link + */ + +struct yaffs_file_var { + u32 file_size; + u32 scanned_size; + u32 shrink_size; + int top_level; + struct yaffs_tnode *top; +}; + +struct yaffs_dir_var { + struct list_head children; /* list of child links */ + struct list_head dirty; /* Entry for list of dirty directories */ +}; + +struct yaffs_symlink_var { + YCHAR *alias; +}; + +struct yaffs_hardlink_var { + struct yaffs_obj *equiv_obj; + u32 equiv_id; +}; + +union yaffs_obj_var { + struct yaffs_file_var file_variant; + struct yaffs_dir_var dir_variant; + struct yaffs_symlink_var symlink_variant; + struct yaffs_hardlink_var hardlink_variant; +}; + +struct yaffs_obj { + u8 deleted:1; /* This should only apply to unlinked files. */ + u8 soft_del:1; /* it has also been soft deleted */ + u8 unlinked:1; /* An unlinked file.*/ + u8 fake:1; /* A fake object has no presence on NAND. */ + u8 rename_allowed:1; /* Some objects cannot be renamed. */ + u8 unlink_allowed:1; + u8 dirty:1; /* the object needs to be written to flash */ + u8 valid:1; /* When the file system is being loaded up, this + * object might be created before the data + * is available + * ie. file data chunks encountered before + * the header. + */ + u8 lazy_loaded:1; /* This object has been lazy loaded and + * is missing some detail */ + + u8 defered_free:1; /* Object is removed from NAND, but is + * still in the inode cache. + * Free of object is defered. + * until the inode is released. + */ + u8 being_created:1; /* This object is still being created + * so skip some verification checks. */ + u8 is_shadowed:1; /* This object is shadowed on the way + * to being renamed. */ + + u8 xattr_known:1; /* We know if this has object has xattribs + * or not. */ + u8 has_xattr:1; /* This object has xattribs. + * Only valid if xattr_known. */ + + u8 serial; /* serial number of chunk in NAND.*/ + u16 sum; /* sum of the name to speed searching */ + + struct yaffs_dev *my_dev; /* The device I'm on */ + + struct list_head hash_link; /* list of objects in hash bucket */ + + struct list_head hard_links; /* hard linked object chain*/ + + /* directory structure stuff */ + /* also used for linking up the free list */ + struct yaffs_obj *parent; + struct list_head siblings; + + /* Where's my object header in NAND? */ + int hdr_chunk; + + int n_data_chunks; /* Number of data chunks for this file. */ + + u32 obj_id; /* the object id value */ + + u32 yst_mode; + + YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; + +#ifdef CONFIG_YAFFS_WINCE + u32 win_ctime[2]; + u32 win_mtime[2]; + u32 win_atime[2]; +#else + u32 yst_uid; + u32 yst_gid; + u32 yst_atime; + u32 yst_mtime; + u32 yst_ctime; +#endif + + u32 yst_rdev; + + void *my_inode; + + enum yaffs_obj_type variant_type; + + union yaffs_obj_var variant; + +}; + +struct yaffs_obj_bucket { + struct list_head list; + int count; +}; + +/* yaffs_checkpt_obj holds the definition of an object as dumped + * by checkpointing. + */ + +struct yaffs_checkpt_obj { + int struct_type; + u32 obj_id; + u32 parent_id; + int hdr_chunk; + enum yaffs_obj_type variant_type:3; + u8 deleted:1; + u8 soft_del:1; + u8 unlinked:1; + u8 fake:1; + u8 rename_allowed:1; + u8 unlink_allowed:1; + u8 serial; + int n_data_chunks; + u32 size_or_equiv_obj; +}; + +/*--------------------- Temporary buffers ---------------- + * + * These are chunk-sized working buffers. Each device has a few. + */ + +struct yaffs_buffer { + u8 *buffer; + int in_use; +}; + +/*----------------- Device ---------------------------------*/ + +struct yaffs_param { + const YCHAR *name; + + /* + * Entry parameters set up way early. Yaffs sets up the rest. + * The structure should be zeroed out before use so that unused + * and defualt values are zero. + */ + + int inband_tags; /* Use unband tags */ + u32 total_bytes_per_chunk; /* Should be >= 512, does not need to + be a power of 2 */ + int chunks_per_block; /* does not need to be a power of 2 */ + int spare_bytes_per_chunk; /* spare area size */ + int start_block; /* Start block we're allowed to use */ + int end_block; /* End block we're allowed to use */ + int n_reserved_blocks; /* Tuneable so that we can reduce + * reserved blocks on NOR and RAM. */ + + int n_caches; /* If <= 0, then short op caching is disabled, + * else the number of short op caches. + */ + int use_nand_ecc; /* Flag to decide whether or not to use + * NAND driver ECC on data (yaffs1) */ + int tags_9bytes; /* Use 9 byte tags */ + int no_tags_ecc; /* Flag to decide whether or not to do ECC + * on packed tags (yaffs2) */ + + int is_yaffs2; /* Use yaffs2 mode on this device */ + + int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ + + int refresh_period; /* How often to check for a block refresh */ + + /* Checkpoint control. Can be set before or after initialisation */ + u8 skip_checkpt_rd; + u8 skip_checkpt_wr; + + int enable_xattr; /* Enable xattribs */ + + /* NAND access functions (Must be set before calling YAFFS) */ + + int (*write_chunk_fn) (struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + const struct yaffs_spare *spare); + int (*read_chunk_fn) (struct yaffs_dev *dev, + int nand_chunk, u8 *data, + struct yaffs_spare *spare); + int (*erase_fn) (struct yaffs_dev *dev, int flash_block); + int (*initialise_flash_fn) (struct yaffs_dev *dev); + int (*deinitialise_flash_fn) (struct yaffs_dev *dev); + + /* yaffs2 mode functions */ + int (*write_chunk_tags_fn) (struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + const struct yaffs_ext_tags *tags); + int (*read_chunk_tags_fn) (struct yaffs_dev *dev, + int nand_chunk, u8 *data, + struct yaffs_ext_tags *tags); + int (*bad_block_fn) (struct yaffs_dev *dev, int block_no); + int (*query_block_fn) (struct yaffs_dev *dev, int block_no, + enum yaffs_block_state *state, + u32 *seq_number); + + /* The remove_obj_fn function must be supplied by OS flavours that + * need it. + * yaffs direct uses it to implement the faster readdir. + * Linux uses it to protect the directory during unlocking. + */ + void (*remove_obj_fn) (struct yaffs_obj *obj); + + /* Callback to mark the superblock dirty */ + void (*sb_dirty_fn) (struct yaffs_dev *dev); + + /* Callback to control garbage collection. */ + unsigned (*gc_control) (struct yaffs_dev *dev); + + /* Debug control flags. Don't use unless you know what you're doing */ + int use_header_file_size; /* Flag to determine if we should use + * file sizes from the header */ + int disable_lazy_load; /* Disable lazy loading on this device */ + int wide_tnodes_disabled; /* Set to disable wide tnodes */ + int disable_soft_del; /* yaffs 1 only: Set to disable the use of + * softdeletion. */ + + int defered_dir_update; /* Set to defer directory updates */ + +#ifdef CONFIG_YAFFS_AUTO_UNICODE + int auto_unicode; +#endif + int always_check_erased; /* Force chunk erased check always on */ + + int disable_summary; +}; + +struct yaffs_dev { + struct yaffs_param param; + + /* Context storage. Holds extra OS specific data for this device */ + + void *os_context; + void *driver_context; + + struct list_head dev_list; + + /* Runtime parameters. Set up by YAFFS. */ + int data_bytes_per_chunk; + + /* Non-wide tnode stuff */ + u16 chunk_grp_bits; /* Number of bits that need to be resolved if + * the tnodes are not wide enough. + */ + u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ + + /* Stuff to support wide tnodes */ + u32 tnode_width; + u32 tnode_mask; + u32 tnode_size; + + /* Stuff for figuring out file offset to chunk conversions */ + u32 chunk_shift; /* Shift value */ + u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ + u32 chunk_mask; /* Mask to use for power-of-2 case */ + + int is_mounted; + int read_only; + int is_checkpointed; + + /* Stuff to support block offsetting to support start block zero */ + int internal_start_block; + int internal_end_block; + int block_offset; + int chunk_offset; + + /* Runtime checkpointing stuff */ + int checkpt_page_seq; /* running sequence number of checkpt pages */ + int checkpt_byte_count; + int checkpt_byte_offs; + u8 *checkpt_buffer; + int checkpt_open_write; + int blocks_in_checkpt; + int checkpt_cur_chunk; + int checkpt_cur_block; + int checkpt_next_block; + int *checkpt_block_list; + int checkpt_max_blocks; + u32 checkpt_sum; + u32 checkpt_xor; + + int checkpoint_blocks_required; /* Number of blocks needed to store + * current checkpoint set */ + + /* Block Info */ + struct yaffs_block_info *block_info; + u8 *chunk_bits; /* bitmap of chunks in use */ + unsigned block_info_alt:1; /* allocated using alternative alloc */ + unsigned chunk_bits_alt:1; /* allocated using alternative alloc */ + int chunk_bit_stride; /* Number of bytes of chunk_bits per block. + * Must be consistent with chunks_per_block. + */ + + int n_erased_blocks; + int alloc_block; /* Current block being allocated off */ + u32 alloc_page; + int alloc_block_finder; /* Used to search for next allocation block */ + + /* Object and Tnode memory management */ + void *allocator; + int n_obj; + int n_tnodes; + + int n_hardlinks; + + struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; + u32 bucket_finder; + + int n_free_chunks; + + /* Garbage collection control */ + u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ + u32 n_clean_ups; + + unsigned has_pending_prioritised_gc; /* We think this device might + have pending prioritised gcs */ + unsigned gc_disable; + unsigned gc_block_finder; + unsigned gc_dirtiest; + unsigned gc_pages_in_use; + unsigned gc_not_done; + unsigned gc_block; + unsigned gc_chunk; + unsigned gc_skip; + struct yaffs_summary_tags *gc_sum_tags; + + /* Special directories */ + struct yaffs_obj *root_dir; + struct yaffs_obj *lost_n_found; + + int buffered_block; /* Which block is buffered here? */ + int doing_buffered_block_rewrite; + + struct yaffs_cache *cache; + int cache_last_use; + + /* Stuff for background deletion and unlinked files. */ + struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted + files live. */ + struct yaffs_obj *del_dir; /* Directory where deleted objects are + sent to disappear. */ + struct yaffs_obj *unlinked_deletion; /* Current file being + background deleted. */ + int n_deleted_files; /* Count of files awaiting deletion; */ + int n_unlinked_files; /* Count of unlinked files. */ + int n_bg_deletions; /* Count of background deletions. */ + + /* Temporary buffer management */ + struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; + int max_temp; + int temp_in_use; + int unmanaged_buffer_allocs; + int unmanaged_buffer_deallocs; + + /* yaffs2 runtime stuff */ + unsigned seq_number; /* Sequence number of currently + allocating block */ + unsigned oldest_dirty_seq; + unsigned oldest_dirty_block; + + /* Block refreshing */ + int refresh_skip; /* A skip down counter. + * Refresh happens when this gets to zero. */ + + /* Dirty directory handling */ + struct list_head dirty_dirs; /* List of dirty directories */ + + /* Summary */ + int chunks_per_summary; + struct yaffs_summary_tags *sum_tags; + + /* Statistics */ + u32 n_page_writes; + u32 n_page_reads; + u32 n_erasures; + u32 n_erase_failures; + u32 n_gc_copies; + u32 all_gcs; + u32 passive_gc_count; + u32 oldest_dirty_gc_count; + u32 n_gc_blocks; + u32 bg_gcs; + u32 n_retried_writes; + u32 n_retired_blocks; + u32 n_ecc_fixed; + u32 n_ecc_unfixed; + u32 n_tags_ecc_fixed; + u32 n_tags_ecc_unfixed; + u32 n_deletions; + u32 n_unmarked_deletions; + u32 refresh_count; + u32 cache_hits; + u32 tags_used; + u32 summary_used; + +}; + +/* The CheckpointDevice structure holds the device information that changes + *at runtime and must be preserved over unmount/mount cycles. + */ +struct yaffs_checkpt_dev { + int struct_type; + int n_erased_blocks; + int alloc_block; /* Current block being allocated off */ + u32 alloc_page; + int n_free_chunks; + + int n_deleted_files; /* Count of files awaiting deletion; */ + int n_unlinked_files; /* Count of unlinked files. */ + int n_bg_deletions; /* Count of background deletions. */ + + /* yaffs2 runtime stuff */ + unsigned seq_number; /* Sequence number of currently + * allocating block */ + +}; + +struct yaffs_checkpt_validity { + int struct_type; + u32 magic; + u32 version; + u32 head; +}; + +struct yaffs_shadow_fixer { + int obj_id; + int shadowed_id; + struct yaffs_shadow_fixer *next; +}; + +/* Structure for doing xattr modifications */ +struct yaffs_xattr_mod { + int set; /* If 0 then this is a deletion */ + const YCHAR *name; + const void *data; + int size; + int flags; + int result; +}; + +/*----------------------- YAFFS Functions -----------------------*/ + +int yaffs_guts_initialise(struct yaffs_dev *dev); +void yaffs_deinitialise(struct yaffs_dev *dev); + +int yaffs_get_n_free_chunks(struct yaffs_dev *dev); + +int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, + struct yaffs_obj *new_dir, const YCHAR * new_name); + +int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); +int yaffs_del_obj(struct yaffs_obj *obj); + +int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); +int yaffs_get_obj_length(struct yaffs_obj *obj); +int yaffs_get_obj_inode(struct yaffs_obj *obj); +unsigned yaffs_get_obj_type(struct yaffs_obj *obj); +int yaffs_get_obj_link_count(struct yaffs_obj *obj); + +/* File operations */ +int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, + int n_bytes); +int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, + int n_bytes, int write_trhrough); +int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); + +struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid); + +int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); + +/* Flushing and checkpointing */ +void yaffs_flush_whole_cache(struct yaffs_dev *dev); + +int yaffs_checkpoint_save(struct yaffs_dev *dev); +int yaffs_checkpoint_restore(struct yaffs_dev *dev); + +/* Directory operations */ +struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, + u32 mode, u32 uid, u32 gid); +struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, + const YCHAR *name); +struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); + +/* Link operations */ +struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, + struct yaffs_obj *equiv_obj); + +struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); + +/* Symlink operations */ +struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, const YCHAR *alias); +YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); + +/* Special inodes (fifos, sockets and devices) */ +struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, + const YCHAR *name, u32 mode, u32 uid, + u32 gid, u32 rdev); + +int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, + const void *value, int size, int flags); +int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, + int size); +int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); +int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); + +/* Special directories */ +struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); +struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); + +void yaffs_handle_defered_free(struct yaffs_obj *obj); + +void yaffs_update_dirty_dirs(struct yaffs_dev *dev); + +int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); + +/* Debug dump */ +int yaffs_dump_obj(struct yaffs_obj *obj); + +void yaffs_guts_test(struct yaffs_dev *dev); + +/* A few useful functions to be used within the core files*/ +void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, + int lyn); +int yaffs_check_ff(u8 *buffer, int n_bytes); +void yaffs_handle_chunk_error(struct yaffs_dev *dev, + struct yaffs_block_info *bi); + +u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); +void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); + +struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, + int number, + enum yaffs_obj_type type); +int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, + int nand_chunk, int in_scan); +void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); +void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, + const struct yaffs_obj_hdr *oh); +void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); +YCHAR *yaffs_clone_str(const YCHAR *str); +void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); +void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); +int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, + int force, int is_shrink, int shadows, + struct yaffs_xattr_mod *xop); +void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, + int backward_scanning); +int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); +struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); +struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id, + struct yaffs_tnode *passed_tn); + +int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, + int n_bytes, int write_trhrough); +void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); +void yaffs_skip_rest_of_block(struct yaffs_dev *dev); + +int yaffs_count_free_chunks(struct yaffs_dev *dev); + +struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, + struct yaffs_file_var *file_struct, + u32 chunk_id); + +u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, + unsigned pos); + +int yaffs_is_non_empty_dir(struct yaffs_obj *obj); +#endif diff --git a/fs/yaffs2-new/yaffs_malloc.h b/fs/yaffs2-new/yaffs_malloc.h new file mode 100644 index 00000000000..3a2ea50b664 --- /dev/null +++ b/fs/yaffs2-new/yaffs_malloc.h @@ -0,0 +1,39 @@ +#ifndef __YAFFS_MALLOC_H__ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* XXX U-BOOT XXX */ + +extern void *yaffs_malloc(size_t size); +void yaffs_free(void *ptr); + + +#ifndef kmalloc +#define kmalloc(x, flags) yaffs_malloc(x) +#endif + +#ifndef vmalloc +#define vmalloc(x) yaffs_malloc(x) +#endif + +#ifndef kfree +#define kfree(x) yaffs_free(x) +#endif + +#ifndef vfree +#define vfree(x) yaffs_free(x) +#endif + +#endif diff --git a/fs/yaffs2-new/yaffs_mtdif.c b/fs/yaffs2-new/yaffs_mtdif.c new file mode 100644 index 00000000000..2d77699663e --- /dev/null +++ b/fs/yaffs2-new/yaffs_mtdif.c @@ -0,0 +1,53 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yportenv.h" + +#include "yaffs_mtdif.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" +#include "linux/mtd/nand.h" + +#include "yaffs_direct.h" + +int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + u32 addr = + ((loff_t) block_no) * dev->param.total_bytes_per_chunk * + dev->param.chunks_per_block; + struct erase_info ei; + int retval = 0; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + retval = mtd->erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +int nandmtd_initialise(struct yaffs_dev *dev) +{ + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_mtdif.h b/fs/yaffs2-new/yaffs_mtdif.h new file mode 100644 index 00000000000..3ef5581fdbc --- /dev/null +++ b/fs/yaffs2-new/yaffs_mtdif.h @@ -0,0 +1,23 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF_H__ +#define __YAFFS_MTDIF_H__ + +#include "yaffs_guts.h" + +int nandmtd_erase_block(struct yaffs_dev *dev, int block_no); +int nandmtd_initialise(struct yaffs_dev *dev); +#endif diff --git a/fs/yaffs2-new/yaffs_mtdif2.h b/fs/yaffs2-new/yaffs_mtdif2.h new file mode 100644 index 00000000000..d4d18585b2c --- /dev/null +++ b/fs/yaffs2-new/yaffs_mtdif2.h @@ -0,0 +1,29 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF2_H__ +#define __YAFFS_MTDIF2_H__ + +#include "yaffs_guts.h" +int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, + const struct yaffs_ext_tags *tags); +int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, + u8 *data, struct yaffs_ext_tags *tags); +int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no); +int nandmtd2_query_block(struct yaffs_dev *dev, int block_no, + enum yaffs_block_state *state, u32 *seq_number); + +#endif diff --git a/fs/yaffs2-new/yaffs_mtdif2_single.c b/fs/yaffs2-new/yaffs_mtdif2_single.c new file mode 100644 index 00000000000..31aebb10820 --- /dev/null +++ b/fs/yaffs2-new/yaffs_mtdif2_single.c @@ -0,0 +1,209 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +/* mtd interface for YAFFS2 */ + +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_mtdif2.h" +#include "yaffs_packedtags2.h" +#include "yaffs_direct.h" +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" + + +/* NB For use with inband tags.... + * We assume that the data buffer is of size total_bytes_per_chunk so that + * we can also use it to load the tags. + */ +int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk, + const u8 *data, + const struct yaffs_ext_tags *tags) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + struct mtd_oob_ops ops; + int retval = 0; + loff_t addr; + struct yaffs_packed_tags2 pt; + int packed_tags_size = + dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); + void *packed_tags_ptr = + dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; + + yaffs_trace(YAFFS_TRACE_MTD, + "nandmtd2_write_chunk_tags chunk %d data %p tags %p", + nand_chunk, data, tags); + + addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; + + /* For yaffs2 writing there must be both data and tags. + * If we're using inband tags, then the tags are stuffed into + * the end of the data buffer. + */ + if (!data || !tags) + BUG(); + else if (dev->param.inband_tags) { + struct yaffs_packed_tags2_tags_only *pt2tp; + + pt2tp = + (struct yaffs_packed_tags2_tags_only *) + (data + dev->data_bytes_per_chunk); + yaffs_pack_tags2_tags_only(pt2tp, tags); + } else { + yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); + } + + ops.mode = MTD_OOB_AUTO; + ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size; + ops.len = dev->param.total_bytes_per_chunk; + ops.ooboffs = 0; + ops.datbuf = (u8 *) data; + ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr; + retval = mtd->write_oob(mtd, addr, &ops); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk, + u8 *data, struct yaffs_ext_tags *tags) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + struct mtd_oob_ops ops; + size_t dummy; + int retval = 0; + int local_data = 0; + loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; + struct yaffs_packed_tags2 pt; + int packed_tags_size = + dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); + void *packed_tags_ptr = + dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; + + yaffs_trace(YAFFS_TRACE_MTD, + "nandmtd2_read_chunk_tags chunk %d data %p tags %p", + nand_chunk, data, tags); + + if (dev->param.inband_tags && !data) { + local_data = 1; + data = yaffs_get_temp_buffer(dev); + } + + if (dev->param.inband_tags || (data && !tags)) { + retval = mtd->read(mtd, addr, dev->param.total_bytes_per_chunk, + &dummy, data); + } else if (tags) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = packed_tags_size; + ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size; + ops.ooboffs = 0; + ops.datbuf = data; + ops.oobbuf = yaffs_dev_to_lc(dev)->spare_buffer; + retval = mtd->read_oob(mtd, addr, &ops); + } + + if (dev->param.inband_tags && tags) { + struct yaffs_packed_tags2_tags_only *pt2tp; + + pt2tp = + (struct yaffs_packed_tags2_tags_only *) + &data[dev->data_bytes_per_chunk]; + yaffs_unpack_tags2_tags_only(tags, pt2tp); + } else if (tags) { + memcpy(packed_tags_ptr, + yaffs_dev_to_lc(dev)->spare_buffer, + packed_tags_size); + yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); + } + + if (local_data) + yaffs_release_temp_buffer(dev, data); + + if (tags && retval == -EBADMSG && + tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) { + tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; + dev->n_ecc_unfixed++; + } + if (tags && retval == -EUCLEAN && + tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) { + tags->ecc_result = YAFFS_ECC_RESULT_FIXED; + dev->n_ecc_fixed++; + } + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, + "nandmtd2_mark_block_bad %d", block_no); + + retval = + mtd->block_markbad(mtd, + block_no * dev->param.chunks_per_block * + dev->param.total_bytes_per_chunk); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} + +int nandmtd2_query_block(struct yaffs_dev *dev, int block_no, + enum yaffs_block_state *state, u32 * seq_number) +{ + struct mtd_info *mtd = yaffs_dev_to_mtd(dev); + int retval; + + yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_query_block %d", block_no); + retval = + mtd->block_isbad(mtd, + block_no * dev->param.chunks_per_block * + dev->param.total_bytes_per_chunk); + + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); + + *state = YAFFS_BLOCK_STATE_DEAD; + *seq_number = 0; + } else { + struct yaffs_ext_tags t; + + nandmtd2_read_chunk_tags(dev, block_no * + dev->param.chunks_per_block, NULL, &t); + + if (t.chunk_used) { + *seq_number = t.seq_number; + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + } else { + *seq_number = 0; + *state = YAFFS_BLOCK_STATE_EMPTY; + } + } + yaffs_trace(YAFFS_TRACE_MTD, + "block is bad seq %d state %d", *seq_number, *state); + + if (retval == 0) + return YAFFS_OK; + + return YAFFS_FAIL; +} diff --git a/fs/yaffs2-new/yaffs_nameval.c b/fs/yaffs2-new/yaffs_nameval.c new file mode 100644 index 00000000000..487b03e19fd --- /dev/null +++ b/fs/yaffs2-new/yaffs_nameval.c @@ -0,0 +1,207 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +/* + * This simple implementation of a name-value store assumes a small number of +* values and fits into a small finite buffer. + * + * Each attribute is stored as a record: + * sizeof(int) bytes record size. + * strnlen+1 bytes name null terminated. + * nbytes value. + * ---------- + * total size stored in record size + * + * This code has not been tested with unicode yet. + */ + +#include "yaffs_nameval.h" + +#include "yportenv.h" + +static int nval_find(const char *xb, int xb_size, const YCHAR *name, + int *exist_size) +{ + int pos = 0; + int size; + + memcpy(&size, xb, sizeof(int)); + while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { + if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), name, size)) { + if (exist_size) + *exist_size = size; + return pos; + } + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + if (exist_size) + *exist_size = 0; + return -ENODATA; +} + +static int nval_used(const char *xb, int xb_size) +{ + int pos = 0; + int size; + + memcpy(&size, xb + pos, sizeof(int)); + while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + return pos; +} + +int nval_del(char *xb, int xb_size, const YCHAR *name) +{ + int pos = nval_find(xb, xb_size, name, NULL); + int size; + + if (pos < 0 || pos >= xb_size) + return -ENODATA; + + /* Find size, shift rest over this record, + * then zero out the rest of buffer */ + memcpy(&size, xb + pos, sizeof(int)); + memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); + memset(xb + (xb_size - size), 0, size); + return 0; +} + +int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, + int bsize, int flags) +{ + int pos; + int namelen = strnlen(name, xb_size); + int reclen; + int size_exist = 0; + int space; + int start; + + pos = nval_find(xb, xb_size, name, &size_exist); + + if (flags & XATTR_CREATE && pos >= 0) + return -EEXIST; + if (flags & XATTR_REPLACE && pos < 0) + return -ENODATA; + + start = nval_used(xb, xb_size); + space = xb_size - start + size_exist; + + reclen = (sizeof(int) + namelen + 1 + bsize); + + if (reclen > space) + return -ENOSPC; + + if (pos >= 0) { + nval_del(xb, xb_size, name); + start = nval_used(xb, xb_size); + } + + pos = start; + + memcpy(xb + pos, &reclen, sizeof(int)); + pos += sizeof(int); + strncpy((YCHAR *) (xb + pos), name, reclen); + pos += (namelen + 1); + memcpy(xb + pos, buf, bsize); + return 0; +} + +int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, + int bsize) +{ + int pos = nval_find(xb, xb_size, name, NULL); + int size; + + if (pos >= 0 && pos < xb_size) { + + memcpy(&size, xb + pos, sizeof(int)); + pos += sizeof(int); /* advance past record length */ + size -= sizeof(int); + + /* Advance over name string */ + while (xb[pos] && size > 0 && pos < xb_size) { + pos++; + size--; + } + /*Advance over NUL */ + pos++; + size--; + + /* If bsize is zero then this is a size query. + * Return the size, but don't copy. + */ + if (!bsize) + return size; + + if (size <= bsize) { + memcpy(buf, xb + pos, size); + return size; + } + } + if (pos >= 0) + return -ERANGE; + + return -ENODATA; +} + +int nval_list(const char *xb, int xb_size, char *buf, int bsize) +{ + int pos = 0; + int size; + int name_len; + int ncopied = 0; + int filled = 0; + + memcpy(&size, xb + pos, sizeof(int)); + while (size > sizeof(int) && + size <= xb_size && + (pos + size) < xb_size && + !filled) { + pos += sizeof(int); + size -= sizeof(int); + name_len = strnlen((YCHAR *) (xb + pos), size); + if (ncopied + name_len + 1 < bsize) { + memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); + buf += name_len; + *buf = '\0'; + buf++; + if (sizeof(YCHAR) > 1) { + *buf = '\0'; + buf++; + } + ncopied += (name_len + 1); + } else { + filled = 1; + } + pos += size; + if (pos < xb_size - sizeof(int)) + memcpy(&size, xb + pos, sizeof(int)); + else + size = 0; + } + return ncopied; +} + +int nval_hasvalues(const char *xb, int xb_size) +{ + return nval_used(xb, xb_size) > 0; +} diff --git a/fs/yaffs2-new/yaffs_nameval.h b/fs/yaffs2-new/yaffs_nameval.h new file mode 100644 index 00000000000..951e64f872b --- /dev/null +++ b/fs/yaffs2-new/yaffs_nameval.h @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __NAMEVAL_H__ +#define __NAMEVAL_H__ + +#include "yportenv.h" + +int nval_del(char *xb, int xb_size, const YCHAR * name); +int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, + int bsize, int flags); +int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, + int bsize); +int nval_list(const char *xb, int xb_size, char *buf, int bsize); +int nval_hasvalues(const char *xb, int xb_size); +#endif diff --git a/fs/yaffs2-new/yaffs_nand.c b/fs/yaffs2-new/yaffs_nand.c new file mode 100644 index 00000000000..165d01004d6 --- /dev/null +++ b/fs/yaffs2-new/yaffs_nand.c @@ -0,0 +1,120 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_nand.h" +#include "yaffs_tagscompat.h" + +#include "yaffs_getblockinfo.h" +#include "yaffs_summary.h" + +int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, + u8 *buffer, struct yaffs_ext_tags *tags) +{ + int result; + struct yaffs_ext_tags local_tags; + int flash_chunk = nand_chunk - dev->chunk_offset; + + dev->n_page_reads++; + + /* If there are no tags provided use local tags. */ + if (!tags) + tags = &local_tags; + + if (dev->param.read_chunk_tags_fn) + result = + dev->param.read_chunk_tags_fn(dev, flash_chunk, buffer, + tags); + else + result = yaffs_tags_compat_rd(dev, + flash_chunk, buffer, tags); + if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { + + struct yaffs_block_info *bi; + bi = yaffs_get_block_info(dev, + nand_chunk / + dev->param.chunks_per_block); + yaffs_handle_chunk_error(dev, bi); + } + return result; +} + +int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, + int nand_chunk, + const u8 *buffer, struct yaffs_ext_tags *tags) +{ + int result; + int flash_chunk = nand_chunk - dev->chunk_offset; + + dev->n_page_writes++; + + if (tags) { + tags->seq_number = dev->seq_number; + tags->chunk_used = 1; + yaffs_trace(YAFFS_TRACE_WRITE, + "Writing chunk %d tags %d %d", + nand_chunk, tags->obj_id, tags->chunk_id); + } else { + yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); + BUG(); + return YAFFS_FAIL; + } + + if (dev->param.write_chunk_tags_fn) + result = dev->param.write_chunk_tags_fn(dev, flash_chunk, + buffer, tags); + else + result = yaffs_tags_compat_wr(dev, flash_chunk, buffer, tags); + + yaffs_summary_add(dev, tags, nand_chunk); + + return result; +} + +int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) +{ + block_no -= dev->block_offset; + if (dev->param.bad_block_fn) + return dev->param.bad_block_fn(dev, block_no); + + return yaffs_tags_compat_mark_bad(dev, block_no); +} + +int yaffs_query_init_block_state(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + block_no -= dev->block_offset; + if (dev->param.query_block_fn) + return dev->param.query_block_fn(dev, block_no, state, + seq_number); + + return yaffs_tags_compat_query_block(dev, block_no, state, seq_number); +} + +int yaffs_erase_block(struct yaffs_dev *dev, int flash_block) +{ + int result; + + flash_block -= dev->block_offset; + dev->n_erasures++; + result = dev->param.erase_fn(dev, flash_block); + return result; +} + +int yaffs_init_nand(struct yaffs_dev *dev) +{ + if (dev->param.initialise_flash_fn) + return dev->param.initialise_flash_fn(dev); + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_nand.h b/fs/yaffs2-new/yaffs_nand.h new file mode 100644 index 00000000000..71346627fc0 --- /dev/null +++ b/fs/yaffs2-new/yaffs_nand.h @@ -0,0 +1,38 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_NAND_H__ +#define __YAFFS_NAND_H__ +#include "yaffs_guts.h" + +int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, + u8 *buffer, struct yaffs_ext_tags *tags); + +int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, + int nand_chunk, + const u8 *buffer, struct yaffs_ext_tags *tags); + +int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); + +int yaffs_query_init_block_state(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + unsigned *seq_number); + +int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); + +int yaffs_init_nand(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2-new/yaffs_packedtags2.c b/fs/yaffs2-new/yaffs_packedtags2.c new file mode 100644 index 00000000000..820bc41023c --- /dev/null +++ b/fs/yaffs2-new/yaffs_packedtags2.c @@ -0,0 +1,181 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_packedtags2.h" +#include "yportenv.h" +#include "yaffs_trace.h" + +/* This code packs a set of extended tags into a binary structure for + * NAND storage + */ + +/* Some of the information is "extra" struff which can be packed in to + * speed scanning + * This is defined by having the EXTRA_HEADER_INFO_FLAG set. + */ + +/* Extra flags applied to chunk_id */ + +#define EXTRA_HEADER_INFO_FLAG 0x80000000 +#define EXTRA_SHRINK_FLAG 0x40000000 +#define EXTRA_SHADOWS_FLAG 0x20000000 +#define EXTRA_SPARE_FLAGS 0x10000000 + +#define ALL_EXTRA_FLAGS 0xf0000000 + +/* Also, the top 4 bits of the object Id are set to the object type. */ +#define EXTRA_OBJECT_TYPE_SHIFT (28) +#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) + +static void yaffs_dump_packed_tags2_tags_only( + const struct yaffs_packed_tags2_tags_only *ptt) +{ + yaffs_trace(YAFFS_TRACE_MTD, + "packed tags obj %d chunk %d byte %d seq %d", + ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); +} + +static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) +{ + yaffs_dump_packed_tags2_tags_only(&pt->t); +} + +static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) +{ + yaffs_trace(YAFFS_TRACE_MTD, + "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", + t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, + t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, + t->seq_number); + +} + +void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, + const struct yaffs_ext_tags *t) +{ + ptt->chunk_id = t->chunk_id; + ptt->seq_number = t->seq_number; + ptt->n_bytes = t->n_bytes; + ptt->obj_id = t->obj_id; + + if (t->chunk_id == 0 && t->extra_available) { + /* Store the extra header info instead */ + /* We save the parent object in the chunk_id */ + ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; + if (t->extra_is_shrink) + ptt->chunk_id |= EXTRA_SHRINK_FLAG; + if (t->extra_shadows) + ptt->chunk_id |= EXTRA_SHADOWS_FLAG; + + ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; + ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); + + if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) + ptt->n_bytes = t->extra_equiv_id; + else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) + ptt->n_bytes = t->extra_length; + else + ptt->n_bytes = 0; + } + + yaffs_dump_packed_tags2_tags_only(ptt); + yaffs_dump_tags2(t); +} + +void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, + const struct yaffs_ext_tags *t, int tags_ecc) +{ + yaffs_pack_tags2_tags_only(&pt->t, t); + + if (tags_ecc) + yaffs_ecc_calc_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &pt->ecc); +} + +void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, + struct yaffs_packed_tags2_tags_only *ptt) +{ + memset(t, 0, sizeof(struct yaffs_ext_tags)); + + if (ptt->seq_number == 0xffffffff) + return; + + t->block_bad = 0; + t->chunk_used = 1; + t->obj_id = ptt->obj_id; + t->chunk_id = ptt->chunk_id; + t->n_bytes = ptt->n_bytes; + t->is_deleted = 0; + t->serial_number = 0; + t->seq_number = ptt->seq_number; + + /* Do extra header info stuff */ + if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { + t->chunk_id = 0; + t->n_bytes = 0; + + t->extra_available = 1; + t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); + t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; + t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; + t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; + t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; + + if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) + t->extra_equiv_id = ptt->n_bytes; + else + t->extra_length = ptt->n_bytes; + } + yaffs_dump_packed_tags2_tags_only(ptt); + yaffs_dump_tags2(t); +} + +void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, + int tags_ecc) +{ + enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + + if (pt->t.seq_number != 0xffffffff && tags_ecc) { + /* Chunk is in use and we need to do ECC */ + + struct yaffs_ecc_other ecc; + int result; + yaffs_ecc_calc_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &ecc); + result = + yaffs_ecc_correct_other((unsigned char *)&pt->t, + sizeof(struct yaffs_packed_tags2_tags_only), + &pt->ecc, &ecc); + switch (result) { + case 0: + ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + break; + case 1: + ecc_result = YAFFS_ECC_RESULT_FIXED; + break; + case -1: + ecc_result = YAFFS_ECC_RESULT_UNFIXED; + break; + default: + ecc_result = YAFFS_ECC_RESULT_UNKNOWN; + } + } + yaffs_unpack_tags2_tags_only(t, &pt->t); + + t->ecc_result = ecc_result; + + yaffs_dump_packed_tags2(pt); + yaffs_dump_tags2(t); +} diff --git a/fs/yaffs2-new/yaffs_packedtags2.h b/fs/yaffs2-new/yaffs_packedtags2.h new file mode 100644 index 00000000000..675e719460a --- /dev/null +++ b/fs/yaffs2-new/yaffs_packedtags2.h @@ -0,0 +1,47 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ + +#ifndef __YAFFS_PACKEDTAGS2_H__ +#define __YAFFS_PACKEDTAGS2_H__ + +#include "yaffs_guts.h" +#include "yaffs_ecc.h" + +struct yaffs_packed_tags2_tags_only { + unsigned seq_number; + unsigned obj_id; + unsigned chunk_id; + unsigned n_bytes; +}; + +struct yaffs_packed_tags2 { + struct yaffs_packed_tags2_tags_only t; + struct yaffs_ecc_other ecc; +}; + +/* Full packed tags with ECC, used for oob tags */ +void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, + const struct yaffs_ext_tags *t, int tags_ecc); +void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, + int tags_ecc); + +/* Only the tags part (no ECC for use with inband tags */ +void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, + const struct yaffs_ext_tags *t); +void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, + struct yaffs_packed_tags2_tags_only *pt); +#endif diff --git a/fs/yaffs2-new/yaffs_qsort.c b/fs/yaffs2-new/yaffs_qsort.c new file mode 100644 index 00000000000..187519fbdb5 --- /dev/null +++ b/fs/yaffs2-new/yaffs_qsort.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "yportenv.h" +/* #include <linux/string.h> */ + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) do { \ + long i = (n) / sizeof (TYPE); \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} while (0) + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long) ? 0 : 1; + +static __inline void +swapfunc(char *a, char *b, int n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n); + else + swapcode(char, a, b, n); +} + +#define yswap(a, b) do { \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype); \ +} while (0) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static __inline char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a)) + : (cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c)); +} + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +void +yaffs_qsort(void *aa, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + register char *a = aa; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + yswap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); + } + yswap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + yswap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + yswap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + yswap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + yswap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min((long)(pd - pc), (long)(pn - pd - es)); + vecswap(pb, pn - r, r); + r = pb - pa; + if (r > es) + yaffs_qsort(a, r / es, es, cmp); + r = pd - pc; + if (r > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* yaffs_qsort(pn - r, r / es, es, cmp);*/ +} diff --git a/fs/yaffs2-new/yaffs_summary.c b/fs/yaffs2-new/yaffs_summary.c new file mode 100644 index 00000000000..4cf17111776 --- /dev/null +++ b/fs/yaffs2-new/yaffs_summary.c @@ -0,0 +1,252 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +/* Summaries write the useful part of the tags for the chunks in a block into an + * an array which is written to the last n chunks of the block. + * Reading the summaries gives all the tags for the block in one read. Much + * faster. + * + * Chunks holding summaries are marked with tags making it look like + * they are part of a fake file. + * + * The summary could also be used during gc. + * + */ + +#include "yaffs_summary.h" +#include "yaffs_packedtags2.h" +#include "yaffs_nand.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_bitmap.h" + +/* Summary tags don't need the sequence number because that is redundant. */ +struct yaffs_summary_tags { + unsigned obj_id; + unsigned chunk_id; + unsigned n_bytes; +}; + +static void yaffs_summary_clear(struct yaffs_dev *dev) +{ + if(!dev->sum_tags) + return; + memset(dev->sum_tags, 0, dev->chunks_per_summary * + sizeof(struct yaffs_summary_tags)); +} + +int yaffs_summary_init(struct yaffs_dev *dev) +{ + int sum_bytes; + int chunks_used; /* Number of chunks used by summary */ + + sum_bytes = dev->param.chunks_per_block * + sizeof(struct yaffs_summary_tags); + + chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ + dev->data_bytes_per_chunk; + dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; + dev->sum_tags = kmalloc(sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary, GFP_NOFS); + dev->gc_sum_tags = kmalloc(sizeof(struct yaffs_summary_tags) * + dev->chunks_per_summary, GFP_NOFS); + if(!dev->sum_tags || !dev->gc_sum_tags) { + kfree(dev->sum_tags); + kfree(dev->gc_sum_tags); + return YAFFS_FAIL; + } + + yaffs_summary_clear(dev); + + return YAFFS_OK; +} + +void yaffs_summary_deinit(struct yaffs_dev *dev) +{ + kfree(dev->sum_tags); + dev->sum_tags = NULL; + kfree(dev->gc_sum_tags); + dev->gc_sum_tags = NULL; + dev->chunks_per_summary = 0; +} + +static int yaffs_summary_write(struct yaffs_dev *dev, int blk) +{ + struct yaffs_ext_tags tags; + u8 *buffer; + u8 *sum_buffer = (u8 *)dev->sum_tags; + int n_bytes; + int chunk_in_nand; + int chunk_in_block; + int result; + int this_tx; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + + buffer = yaffs_get_temp_buffer(dev); + n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; + memset(&tags, 0, sizeof(struct yaffs_ext_tags)); + tags.obj_id = YAFFS_OBJECTID_SUMMARY; + tags.chunk_id = 1; + chunk_in_block = dev->chunks_per_summary; + chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + + dev-> chunks_per_summary; + do { + this_tx = n_bytes; + if (this_tx > dev->data_bytes_per_chunk) + this_tx = dev->data_bytes_per_chunk; + memcpy(buffer, sum_buffer, this_tx); + tags.n_bytes = this_tx; + result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, + buffer, &tags); + + if (result != YAFFS_OK) + break; + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + dev->n_free_chunks--; + + n_bytes -= this_tx; + sum_buffer += this_tx; + chunk_in_nand++; + chunk_in_block++; + tags.chunk_id++; + } while (result == YAFFS_OK && n_bytes > 0); + yaffs_release_temp_buffer(dev, buffer); + + + if (result == YAFFS_OK) + bi->has_summary = 1; + + + return result; +} + +int yaffs_summary_read(struct yaffs_dev *dev, + struct yaffs_summary_tags *st, + int blk) +{ + struct yaffs_ext_tags tags; + u8 *buffer; + u8 *sum_buffer = (u8 *)st; + int n_bytes; + int chunk_id; + int chunk_in_nand; + int chunk_in_block; + int result; + int this_tx; + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + + buffer = yaffs_get_temp_buffer(dev); + n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; + chunk_in_block = dev->chunks_per_summary; + chunk_in_nand = blk * dev->param.chunks_per_block + + dev->chunks_per_summary; + chunk_id = 1; + do { + this_tx = n_bytes; + if(this_tx > dev->data_bytes_per_chunk) + this_tx = dev->data_bytes_per_chunk; + result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, + buffer, &tags); + + if (tags.chunk_id != chunk_id || + tags.obj_id != YAFFS_OBJECTID_SUMMARY || + tags.chunk_used == 0 || + tags.ecc_result > YAFFS_ECC_RESULT_FIXED || + this_tx != tags.n_bytes) + result = YAFFS_FAIL; + if (result != YAFFS_OK) + break; + + if (st == dev->sum_tags) { + /* If we're scanning then update the block info */ + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + } + + memcpy(sum_buffer, buffer, this_tx); + n_bytes -= this_tx; + sum_buffer += this_tx; + chunk_in_nand++; + chunk_in_block++; + chunk_id++; + } while (result == YAFFS_OK && n_bytes > 0); + yaffs_release_temp_buffer(dev, buffer); + + if (st == dev->sum_tags && result == YAFFS_OK) + bi->has_summary = 1; + + return result; +} +int yaffs_summary_add(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_nand) +{ + struct yaffs_packed_tags2_tags_only tags_only; + struct yaffs_summary_tags *sum_tags; + int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; + int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; + + if(!dev->sum_tags) + return YAFFS_OK; + + if(chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { + yaffs_pack_tags2_tags_only(&tags_only, tags); + sum_tags = &dev->sum_tags[chunk_in_block]; + sum_tags->chunk_id = tags_only.chunk_id; + sum_tags->n_bytes = tags_only.n_bytes; + sum_tags->obj_id = tags_only.obj_id; + + if(chunk_in_block == dev->chunks_per_summary - 1) { + /* Time to write out the summary */ + yaffs_summary_write(dev, block_in_nand); + yaffs_summary_clear(dev); + yaffs_skip_rest_of_block(dev); + } + } + return YAFFS_OK; +} + +int yaffs_summary_fetch(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block) +{ + struct yaffs_packed_tags2_tags_only tags_only; + struct yaffs_summary_tags *sum_tags; + if(chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { + sum_tags = &dev->sum_tags[chunk_in_block]; + tags_only.chunk_id = sum_tags->chunk_id; + tags_only.n_bytes = sum_tags->n_bytes; + tags_only.obj_id = sum_tags->obj_id; + yaffs_unpack_tags2_tags_only(tags, &tags_only); + return YAFFS_OK; + } + return YAFFS_FAIL; +} + +void yaffs_summary_gc(struct yaffs_dev *dev, int blk) +{ + struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); + int i; + + if (!bi->has_summary) + return; + + for (i = dev->chunks_per_summary; i < dev->param.chunks_per_block; i++) { + if( yaffs_check_chunk_bit(dev, blk, i)) { + yaffs_clear_chunk_bit(dev, blk, i); + bi->pages_in_use--; + dev->n_free_chunks++; + } + } + +} diff --git a/fs/yaffs2-new/yaffs_summary.h b/fs/yaffs2-new/yaffs_summary.h new file mode 100644 index 00000000000..be141d07336 --- /dev/null +++ b/fs/yaffs2-new/yaffs_summary.h @@ -0,0 +1,37 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_SUMMARY_H__ +#define __YAFFS_SUMMARY_H__ + +#include "yaffs_packedtags2.h" + + +int yaffs_summary_init(struct yaffs_dev *dev); +void yaffs_summary_deinit(struct yaffs_dev *dev); + +int yaffs_summary_add(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block); +int yaffs_summary_fetch(struct yaffs_dev *dev, + struct yaffs_ext_tags *tags, + int chunk_in_block); +int yaffs_summary_read(struct yaffs_dev *dev, + struct yaffs_summary_tags *st, + int blk); +void yaffs_summary_gc(struct yaffs_dev *dev, int blk); + + +#endif diff --git a/fs/yaffs2-new/yaffs_tagscompat.c b/fs/yaffs2-new/yaffs_tagscompat.c new file mode 100644 index 00000000000..9ac5896da37 --- /dev/null +++ b/fs/yaffs2-new/yaffs_tagscompat.c @@ -0,0 +1,407 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_guts.h" +#include "yaffs_tagscompat.h" +#include "yaffs_ecc.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_trace.h" + +static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); + + +/********** Tags ECC calculations *********/ + +void yaffs_calc_ecc(const u8 *data, struct yaffs_spare *spare) +{ + yaffs_ecc_calc(data, spare->ecc1); + yaffs_ecc_calc(&data[256], spare->ecc2); +} + +void yaffs_calc_tags_ecc(struct yaffs_tags *tags) +{ + /* Calculate an ecc */ + unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; + unsigned i, j; + unsigned ecc = 0; + unsigned bit = 0; + + tags->ecc = 0; + + for (i = 0; i < 8; i++) { + for (j = 1; j & 0xff; j <<= 1) { + bit++; + if (b[i] & j) + ecc ^= bit; + } + } + tags->ecc = ecc; +} + +int yaffs_check_tags_ecc(struct yaffs_tags *tags) +{ + unsigned ecc = tags->ecc; + + yaffs_calc_tags_ecc(tags); + + ecc ^= tags->ecc; + + if (ecc && ecc <= 64) { + /* TODO: Handle the failure better. Retire? */ + unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; + + ecc--; + + b[ecc / 8] ^= (1 << (ecc & 7)); + + /* Now recvalc the ecc */ + yaffs_calc_tags_ecc(tags); + + return 1; /* recovered error */ + } else if (ecc) { + /* Wierd ecc failure value */ + /* TODO Need to do somethiong here */ + return -1; /* unrecovered error */ + } + return 0; +} + +/********** Tags **********/ + +static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, + struct yaffs_tags *tags_ptr) +{ + union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; + + yaffs_calc_tags_ecc(tags_ptr); + + spare_ptr->tb0 = tu->as_bytes[0]; + spare_ptr->tb1 = tu->as_bytes[1]; + spare_ptr->tb2 = tu->as_bytes[2]; + spare_ptr->tb3 = tu->as_bytes[3]; + spare_ptr->tb4 = tu->as_bytes[4]; + spare_ptr->tb5 = tu->as_bytes[5]; + spare_ptr->tb6 = tu->as_bytes[6]; + spare_ptr->tb7 = tu->as_bytes[7]; +} + +static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, + struct yaffs_spare *spare_ptr, + struct yaffs_tags *tags_ptr) +{ + union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; + int result; + + tu->as_bytes[0] = spare_ptr->tb0; + tu->as_bytes[1] = spare_ptr->tb1; + tu->as_bytes[2] = spare_ptr->tb2; + tu->as_bytes[3] = spare_ptr->tb3; + tu->as_bytes[4] = spare_ptr->tb4; + tu->as_bytes[5] = spare_ptr->tb5; + tu->as_bytes[6] = spare_ptr->tb6; + tu->as_bytes[7] = spare_ptr->tb7; + + result = yaffs_check_tags_ecc(tags_ptr); + if (result > 0) + dev->n_tags_ecc_fixed++; + else if (result < 0) + dev->n_tags_ecc_unfixed++; +} + +static void yaffs_spare_init(struct yaffs_spare *spare) +{ + memset(spare, 0xff, sizeof(struct yaffs_spare)); +} + +static int yaffs_wr_nand(struct yaffs_dev *dev, + int nand_chunk, const u8 *data, + struct yaffs_spare *spare) +{ + if (nand_chunk < dev->param.start_block * dev->param.chunks_per_block) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>> yaffs chunk %d is not valid", + nand_chunk); + return YAFFS_FAIL; + } + + return dev->param.write_chunk_fn(dev, nand_chunk, data, spare); +} + +static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, + struct yaffs_spare *spare, + enum yaffs_ecc_result *ecc_result, + int correct_errors) +{ + int ret_val; + struct yaffs_spare local_spare; + + if (!spare) { + /* If we don't have a real spare, then we use a local one. */ + /* Need this for the calculation of the ecc */ + spare = &local_spare; + } + + if (!dev->param.use_nand_ecc) { + ret_val = + dev->param.read_chunk_fn(dev, nand_chunk, data, spare); + if (data && correct_errors) { + /* Do ECC correction */ + /* Todo handle any errors */ + int ecc_result1, ecc_result2; + u8 calc_ecc[3]; + + yaffs_ecc_calc(data, calc_ecc); + ecc_result1 = + yaffs_ecc_correct(data, spare->ecc1, calc_ecc); + yaffs_ecc_calc(&data[256], calc_ecc); + ecc_result2 = + yaffs_ecc_correct(&data[256], spare->ecc2, + calc_ecc); + + if (ecc_result1 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error fix performed on chunk %d:0", + nand_chunk); + dev->n_ecc_fixed++; + } else if (ecc_result1 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error unfixed on chunk %d:0", + nand_chunk); + dev->n_ecc_unfixed++; + } + + if (ecc_result2 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error fix performed on chunk %d:1", + nand_chunk); + dev->n_ecc_fixed++; + } else if (ecc_result2 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>yaffs ecc error unfixed on chunk %d:1", + nand_chunk); + dev->n_ecc_unfixed++; + } + + if (ecc_result1 || ecc_result2) { + /* We had a data problem on this page */ + yaffs_handle_rd_data_error(dev, nand_chunk); + } + + if (ecc_result1 < 0 || ecc_result2 < 0) + *ecc_result = YAFFS_ECC_RESULT_UNFIXED; + else if (ecc_result1 > 0 || ecc_result2 > 0) + *ecc_result = YAFFS_ECC_RESULT_FIXED; + else + *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + } + } else { + /* Must allocate enough memory for spare+2*sizeof(int) */ + /* for ecc results from device. */ + struct yaffs_nand_spare nspare; + + memset(&nspare, 0, sizeof(nspare)); + + ret_val = dev->param.read_chunk_fn(dev, nand_chunk, data, + (struct yaffs_spare *) + &nspare); + memcpy(spare, &nspare, sizeof(struct yaffs_spare)); + if (data && correct_errors) { + if (nspare.eccres1 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>mtd ecc error fix performed on chunk %d:0", + nand_chunk); + } else if (nspare.eccres1 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>mtd ecc error unfixed on chunk %d:0", + nand_chunk); + } + + if (nspare.eccres2 > 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>mtd ecc error fix performed on chunk %d:1", + nand_chunk); + } else if (nspare.eccres2 < 0) { + yaffs_trace(YAFFS_TRACE_ERROR, + "**>>mtd ecc error unfixed on chunk %d:1", + nand_chunk); + } + + if (nspare.eccres1 || nspare.eccres2) { + /* We had a data problem on this page */ + yaffs_handle_rd_data_error(dev, nand_chunk); + } + + if (nspare.eccres1 < 0 || nspare.eccres2 < 0) + *ecc_result = YAFFS_ECC_RESULT_UNFIXED; + else if (nspare.eccres1 > 0 || nspare.eccres2 > 0) + *ecc_result = YAFFS_ECC_RESULT_FIXED; + else + *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; + + } + } + return ret_val; +} + +/* + * Functions for robustisizing + */ + +static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) +{ + int flash_block = nand_chunk / dev->param.chunks_per_block; + + /* Mark the block for retirement */ + yaffs_get_block_info(dev, flash_block + dev->block_offset)-> + needs_retiring = 1; + yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + "**>>Block %d marked for retirement", + flash_block); + + /* TODO: + * Just do a garbage collection on the affected block + * then retire the block + * NB recursion + */ +} + +int yaffs_tags_compat_wr(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, const struct yaffs_ext_tags *ext_tags) +{ + struct yaffs_spare spare; + struct yaffs_tags tags; + + yaffs_spare_init(&spare); + + if (ext_tags->is_deleted) + spare.page_status = 0; + else { + tags.obj_id = ext_tags->obj_id; + tags.chunk_id = ext_tags->chunk_id; + + tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); + + if (dev->data_bytes_per_chunk >= 1024) + tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; + else + tags.n_bytes_msb = 3; + + tags.serial_number = ext_tags->serial_number; + + if (!dev->param.use_nand_ecc && data) + yaffs_calc_ecc(data, &spare); + + yaffs_load_tags_to_spare(&spare, &tags); + } + return yaffs_wr_nand(dev, nand_chunk, data, &spare); +} + +int yaffs_tags_compat_rd(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, struct yaffs_ext_tags *ext_tags) +{ + struct yaffs_spare spare; + struct yaffs_tags tags; + enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; + static struct yaffs_spare spare_ff; + static int init; + int deleted; + + if (!init) { + memset(&spare_ff, 0xff, sizeof(spare_ff)); + init = 1; + } + + if (!yaffs_rd_chunk_nand(dev, nand_chunk, + data, &spare, &ecc_result, 1)) + return YAFFS_FAIL; + + /* ext_tags may be NULL */ + if (!ext_tags) + return YAFFS_OK; + + deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; + + ext_tags->is_deleted = deleted; + ext_tags->ecc_result = ecc_result; + ext_tags->block_bad = 0; /* We're reading it */ + /* therefore it is not a bad block */ + ext_tags->chunk_used = + memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; + + if (ext_tags->chunk_used) { + yaffs_get_tags_from_spare(dev, &spare, &tags); + ext_tags->obj_id = tags.obj_id; + ext_tags->chunk_id = tags.chunk_id; + ext_tags->n_bytes = tags.n_bytes_lsb; + + if (dev->data_bytes_per_chunk >= 1024) + ext_tags->n_bytes |= + (((unsigned)tags.n_bytes_msb) << 10); + + ext_tags->serial_number = tags.serial_number; + } + + return YAFFS_OK; +} + +int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) +{ + struct yaffs_spare spare; + + memset(&spare, 0xff, sizeof(struct yaffs_spare)); + + spare.block_status = 'Y'; + + yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, + &spare); + yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, + NULL, &spare); + + return YAFFS_OK; +} + +int yaffs_tags_compat_query_block(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number) +{ + struct yaffs_spare spare0, spare1; + static struct yaffs_spare spare_ff; + static int init; + enum yaffs_ecc_result dummy; + + if (!init) { + memset(&spare_ff, 0xff, sizeof(spare_ff)); + init = 1; + } + + *seq_number = 0; + + yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, NULL, + &spare0, &dummy, 1); + yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, + NULL, &spare1, &dummy, 1); + + if (hweight8(spare0.block_status & spare1.block_status) < 7) + *state = YAFFS_BLOCK_STATE_DEAD; + else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) + *state = YAFFS_BLOCK_STATE_EMPTY; + else + *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; + + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_tagscompat.h b/fs/yaffs2-new/yaffs_tagscompat.h new file mode 100644 index 00000000000..b3c66557725 --- /dev/null +++ b/fs/yaffs2-new/yaffs_tagscompat.h @@ -0,0 +1,36 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_TAGSCOMPAT_H__ +#define __YAFFS_TAGSCOMPAT_H__ + +#include "yaffs_guts.h" +int yaffs_tags_compat_wr(struct yaffs_dev *dev, + int nand_chunk, + const u8 *data, const struct yaffs_ext_tags *tags); +int yaffs_tags_compat_rd(struct yaffs_dev *dev, + int nand_chunk, + u8 *data, struct yaffs_ext_tags *tags); +int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); +int yaffs_tags_compat_query_block(struct yaffs_dev *dev, + int block_no, + enum yaffs_block_state *state, + u32 *seq_number); + +void yaffs_calc_tags_ecc(struct yaffs_tags *tags); +int yaffs_check_tags_ecc(struct yaffs_tags *tags); +int yaffs_count_bits(u8 byte); + +#endif diff --git a/fs/yaffs2-new/yaffs_trace.h b/fs/yaffs2-new/yaffs_trace.h new file mode 100644 index 00000000000..fd26054d391 --- /dev/null +++ b/fs/yaffs2-new/yaffs_trace.h @@ -0,0 +1,57 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YTRACE_H__ +#define __YTRACE_H__ + +extern unsigned int yaffs_trace_mask; +extern unsigned int yaffs_wr_attempts; + +/* + * Tracing flags. + * The flags masked in YAFFS_TRACE_ALWAYS are always traced. + */ + +#define YAFFS_TRACE_OS 0x00000002 +#define YAFFS_TRACE_ALLOCATE 0x00000004 +#define YAFFS_TRACE_SCAN 0x00000008 +#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 +#define YAFFS_TRACE_ERASE 0x00000020 +#define YAFFS_TRACE_GC 0x00000040 +#define YAFFS_TRACE_WRITE 0x00000080 +#define YAFFS_TRACE_TRACING 0x00000100 +#define YAFFS_TRACE_DELETION 0x00000200 +#define YAFFS_TRACE_BUFFERS 0x00000400 +#define YAFFS_TRACE_NANDACCESS 0x00000800 +#define YAFFS_TRACE_GC_DETAIL 0x00001000 +#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 +#define YAFFS_TRACE_MTD 0x00004000 +#define YAFFS_TRACE_CHECKPOINT 0x00008000 + +#define YAFFS_TRACE_VERIFY 0x00010000 +#define YAFFS_TRACE_VERIFY_NAND 0x00020000 +#define YAFFS_TRACE_VERIFY_FULL 0x00040000 +#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 + +#define YAFFS_TRACE_SYNC 0x00100000 +#define YAFFS_TRACE_BACKGROUND 0x00200000 +#define YAFFS_TRACE_LOCK 0x00400000 +#define YAFFS_TRACE_MOUNT 0x00800000 + +#define YAFFS_TRACE_ERROR 0x40000000 +#define YAFFS_TRACE_BUG 0x80000000 +#define YAFFS_TRACE_ALWAYS 0xf0000000 + +#endif diff --git a/fs/yaffs2-new/yaffs_uboot.c b/fs/yaffs2-new/yaffs_uboot.c new file mode 100644 index 00000000000..171bb7bd868 --- /dev/null +++ b/fs/yaffs2-new/yaffs_uboot.c @@ -0,0 +1,258 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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. + */ + +/* + * yaffscfg.c The configuration for the "direct" use of yaffs in U-boot. + * + */ + +#include <common.h> +#include "malloc.h" + +#include "yaffscfg.h" +#include "yaffsfs_errno.h" + +#include "yportenv.h" +#include "yaffsfs.h" +#include "yaffs_trace.h" + +void yaffsfs_Lock(void) +{ +} + +void yaffsfs_Unlock(void) +{ +} + +void *yaffs_malloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) + printf("%s failed to allocate %u bytes!\n", __FUNCTION__, (unsigned int)size); + return ptr; +} + +void yaffs_free(void *ptr) +{ + free(ptr); +} + +u32 yaffsfs_CurrentTime(void) +{ + return 0; +} + +unsigned int yaffs_wr_attempts; +unsigned int yaffs_trace_mask = 0; + +void yaffs_startup(void) +{ + yaffs_trace_mask = YAFFS_TRACE_ERROR | YAFFS_TRACE_BUG | YAFFS_TRACE_ALWAYS; + yaffsfs_initialise(); +} + +int cmd_yaffs_mount(const char *mp) +{ + int retval; + char *p; + + yaffsfs_set_error(0); + yaffs_startup(); + retval = yaffsfs_mount(mp); + if (retval == -1) { + retval = yaffsfs_get_error(&p); + printf("Failed: %s\n", p); + } + return yaffsfs_get_error(NULL); +} + +int cmd_yaffs_umount(char *mp) +{ + char *p; + int retval; + + yaffsfs_set_error(0); + if (yaffsfs_unmount(mp) == -1) { + retval = yaffsfs_get_error(&p); + printf("Failed: %s\n", p); + } + return yaffsfs_get_error(NULL); +} + +int cmd_yaffs_ls(const char *mountpt, int longlist) +{ + int i, ret; + yaffs_DIR *d; + yaffs_dirent *de; + struct yaffs_stat stat; + char tempstr[255]; + char *p; + + yaffsfs_set_error(0); + + d = yaffs_opendir(mountpt); + + if(!d) + { + ret = yaffsfs_get_error(&p); + printf("%s: %s\n", mountpt, p); + return ret; + } + else + { + for(i = 0; (de = yaffs_readdir(d)) != NULL; i++) + { + if (longlist) + { + sprintf(tempstr, "%s/%s", mountpt, de->d_name); + yaffs_stat(tempstr, &stat); + printf("%-25s\t%7ld\n",de->d_name, stat.st_size); + } + else + { + printf("%s\n",de->d_name); + } + } + } + return 0; +} + +int cmd_yaffs_df(const char *path, loff_t *size) +{ + loff_t free_space; + int ret = 0; + char *p; + + yaffsfs_set_error(0); + + free_space = yaffs_freespace(path); + if (free_space == -1) { + ret = yaffsfs_get_error(&p); + printf("Failed: %s\n", p); + } else + *size = free_space; + + return ret; +} + +int cmd_yaffs_mwrite_file(char *fn, char *addr, int size) +{ + int h, ret; + char *p; + + yaffsfs_set_error(0); + printf ("Copy 0x%p to %s ... ", addr, fn); + h = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE); + if (h >= 0) { + + yaffs_write(h,addr,size); + yaffs_close(h); + + } + + ret = yaffsfs_get_error(&p); + if (ret) + printf("[Failed: %s]\n", p); + else + printf("[DONE]\n"); + + return ret; +} + +int cmd_yaffs_mread_file(char *fn, char *addr, long *size) +{ + int h, ret; + struct yaffs_stat s; + char *p; + + yaffsfs_set_error(0); + printf ("Copy %s to 0x%p... ", fn, addr); + + yaffs_stat(fn,&s); + h = yaffs_open(fn, O_RDWR,0); + if (h<0) { + yaffsfs_set_error(-ENOENT); + } else { + yaffs_read(h,addr,(int)s.st_size); + yaffs_close(h); + } + + ret = yaffsfs_get_error(&p); + if (ret) + printf("[Failed: %s]\n", p); + else { + printf("[DONE]\n"); + *size = s.st_size; + } + + return ret; +} + +int cmd_yaffs_mkdir(const char *dir) +{ + int ret; + char *p; + + yaffsfs_set_error(0); + ret = yaffs_mkdir(dir, 0); + if (ret < 0) { + ret = yaffsfs_get_error(&p); + printf("[Failed: %p]\n", p); + } + return ret; +} + +int cmd_yaffs_rmdir(const char *dir) +{ + int ret; + char *p; + + yaffsfs_set_error(0); + ret = yaffs_rmdir(dir); + if (ret < 0) { + ret = yaffsfs_get_error(&p); + printf("[Failed: %s]\n", p); + } + return ret; +} + +int cmd_yaffs_rm(const char *path) +{ + int ret; + char *p; + + yaffsfs_set_error(0); + ret = yaffs_unlink(path); + if (ret < 0) { + ret = yaffsfs_get_error(&p); + printf("[Failed: %s]\n", p); + } + return ret; +} + +int cmd_yaffs_mv(const char *oldPath, const char *newPath) +{ + int ret; + char *p; + + yaffsfs_set_error(0); + ret = yaffs_rename(newPath, oldPath); + + if (ret < 0) { + ret = yaffsfs_get_error(&p); + printf("[Failed: %s]\n", p); + } + return ret; +} diff --git a/fs/yaffs2-new/yaffs_verify.c b/fs/yaffs2-new/yaffs_verify.c new file mode 100644 index 00000000000..b3e540dd52a --- /dev/null +++ b/fs/yaffs2-new/yaffs_verify.c @@ -0,0 +1,525 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_verify.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" + +int yaffs_skip_verification(struct yaffs_dev *dev) +{ + dev = dev; + return !(yaffs_trace_mask & + (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_skip_full_verification(struct yaffs_dev *dev) +{ + dev = dev; + return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_skip_nand_verification(struct yaffs_dev *dev) +{ + dev = dev; + return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); +} + +static const char * const block_state_name[] = { + "Unknown", + "Needs scan", + "Scanning", + "Empty", + "Allocating", + "Full", + "Dirty", + "Checkpoint", + "Collecting", + "Dead" +}; + +void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) +{ + int actually_used; + int in_use; + + if (yaffs_skip_verification(dev)) + return; + + /* Report illegal runtime states */ + if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has undefined state %d", + n, bi->block_state); + + switch (bi->block_state) { + case YAFFS_BLOCK_STATE_UNKNOWN: + case YAFFS_BLOCK_STATE_SCANNING: + case YAFFS_BLOCK_STATE_NEEDS_SCAN: + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has bad run-state %s", + n, block_state_name[bi->block_state]); + } + + /* Check pages in use and soft deletions are legal */ + + actually_used = bi->pages_in_use - bi->soft_del_pages; + + if (bi->pages_in_use < 0 || + bi->pages_in_use > dev->param.chunks_per_block || + bi->soft_del_pages < 0 || + bi->soft_del_pages > dev->param.chunks_per_block || + actually_used < 0 || actually_used > dev->param.chunks_per_block) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has illegal values pages_in_used %d soft_del_pages %d", + n, bi->pages_in_use, bi->soft_del_pages); + + /* Check chunk bitmap legal */ + in_use = yaffs_count_chunk_bits(dev, n); + if (in_use != bi->pages_in_use) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", + n, bi->pages_in_use, in_use); +} + +void yaffs_verify_collected_blk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, int n) +{ + yaffs_verify_blk(dev, bi, n); + + /* After collection the block should be in the erased state */ + + if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && + bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Block %d is in state %d after gc, should be erased", + n, bi->block_state); + } +} + +void yaffs_verify_blocks(struct yaffs_dev *dev) +{ + int i; + int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; + int illegal_states = 0; + + if (yaffs_skip_verification(dev)) + return; + + memset(state_count, 0, sizeof(state_count)); + + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); + yaffs_verify_blk(dev, bi, i); + + if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) + state_count[bi->block_state]++; + else + illegal_states++; + } + + yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); + + yaffs_trace(YAFFS_TRACE_VERIFY, + "%d blocks have illegal states", + illegal_states); + if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Too many allocating blocks"); + + for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) + yaffs_trace(YAFFS_TRACE_VERIFY, + "%s %d blocks", + block_state_name[i], state_count[i]); + + if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Checkpoint block count wrong dev %d count %d", + dev->blocks_in_checkpt, + state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); + + if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Erased block count wrong dev %d count %d", + dev->n_erased_blocks, + state_count[YAFFS_BLOCK_STATE_EMPTY]); + + if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Too many collecting blocks %d (max is 1)", + state_count[YAFFS_BLOCK_STATE_COLLECTING]); +} + +/* + * Verify the object header. oh must be valid, but obj and tags may be NULL in + * which case those tests will not be performed. + */ +void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, + struct yaffs_ext_tags *tags, int parent_check) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + if (!(tags && obj && oh)) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Verifying object header tags %p obj %p oh %p", + tags, obj, oh); + return; + } + + if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || + oh->type > YAFFS_OBJECT_TYPE_MAX) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header type is illegal value 0x%x", + tags->obj_id, oh->type); + + if (tags->obj_id != obj->obj_id) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch obj_id %d", + tags->obj_id, obj->obj_id); + + /* + * Check that the object's parent ids match if parent_check requested. + * + * Tests do not apply to the root object. + */ + + if (parent_check && tags->obj_id > 1 && !obj->parent) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch parent_id %d obj->parent is NULL", + tags->obj_id, oh->parent_obj_id); + + if (parent_check && obj->parent && + oh->parent_obj_id != obj->parent->obj_id && + (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || + obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header mismatch parent_id %d parent_obj_id %d", + tags->obj_id, oh->parent_obj_id, + obj->parent->obj_id); + + if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header name is NULL", + obj->obj_id); + + if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d header name is 0xff", + obj->obj_id); +} + +void yaffs_verify_file(struct yaffs_obj *obj) +{ + int required_depth; + int actual_depth; + u32 last_chunk; + u32 the_chunk; + u32 x; + u32 i; + struct yaffs_dev *dev; + struct yaffs_ext_tags tags; + struct yaffs_tnode *tn; + u32 obj_id; + + if (!obj) + return; + + if (yaffs_skip_verification(obj->my_dev)) + return; + + dev = obj->my_dev; + obj_id = obj->obj_id; + + /* Check file size is consistent with tnode depth */ + last_chunk = + obj->variant.file_variant.file_size / dev->data_bytes_per_chunk + 1; + x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; + required_depth = 0; + while (x > 0) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + required_depth++; + } + + actual_depth = obj->variant.file_variant.top_level; + + /* Check that the chunks in the tnode tree are all correct. + * We do this by scanning through the tnode tree and + * checking the tags for every chunk match. + */ + + if (yaffs_skip_nand_verification(dev)) + return; + + for (i = 1; i <= last_chunk; i++) { + tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); + + if (!tn) + continue; + + the_chunk = yaffs_get_group_base(dev, tn, i); + if (the_chunk > 0) { + yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, + &tags); + if (tags.obj_id != obj_id || tags.chunk_id != i) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", + obj_id, i, the_chunk, + tags.obj_id, tags.chunk_id); + } + } +} + +void yaffs_verify_link(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + /* Verify sane equivalent object */ +} + +void yaffs_verify_symlink(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; + + /* Verify symlink string */ +} + +void yaffs_verify_special(struct yaffs_obj *obj) +{ + if (obj && yaffs_skip_verification(obj->my_dev)) + return; +} + +void yaffs_verify_obj(struct yaffs_obj *obj) +{ + struct yaffs_dev *dev; + u32 chunk_min; + u32 chunk_max; + u32 chunk_id_ok; + u32 chunk_in_range; + u32 chunk_wrongly_deleted; + u32 chunk_valid; + + if (!obj) + return; + + if (obj->being_created) + return; + + dev = obj->my_dev; + + if (yaffs_skip_verification(dev)) + return; + + /* Check sane object header chunk */ + + chunk_min = dev->internal_start_block * dev->param.chunks_per_block; + chunk_max = + (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; + + chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && + ((unsigned)(obj->hdr_chunk)) <= chunk_max); + chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); + chunk_valid = chunk_in_range && + yaffs_check_chunk_bit(dev, + obj->hdr_chunk / dev->param.chunks_per_block, + obj->hdr_chunk % dev->param.chunks_per_block); + chunk_wrongly_deleted = chunk_in_range && !chunk_valid; + + if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has chunk_id %d %s %s", + obj->obj_id, obj->hdr_chunk, + chunk_id_ok ? "" : ",out of range", + chunk_wrongly_deleted ? ",marked as deleted" : ""); + + if (chunk_valid && !yaffs_skip_nand_verification(dev)) { + struct yaffs_ext_tags tags; + struct yaffs_obj_hdr *oh; + u8 *buffer = yaffs_get_temp_buffer(dev); + + oh = (struct yaffs_obj_hdr *)buffer; + + yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); + + yaffs_verify_oh(obj, oh, &tags, 1); + + yaffs_release_temp_buffer(dev, buffer); + } + + /* Verify it has a parent */ + if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has parent pointer %p which does not look like an object", + obj->obj_id, obj->parent); + } + + /* Verify parent is a directory */ + if (obj->parent && + obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d's parent is not a directory (type %d)", + obj->obj_id, obj->parent->variant_type); + } + + switch (obj->variant_type) { + case YAFFS_OBJECT_TYPE_FILE: + yaffs_verify_file(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_verify_symlink(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_verify_dir(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_verify_link(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_verify_special(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + yaffs_trace(YAFFS_TRACE_VERIFY, + "Obj %d has illegaltype %d", + obj->obj_id, obj->variant_type); + break; + } +} + +void yaffs_verify_objects(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + int i; + struct list_head *lh; + + if (yaffs_skip_verification(dev)) + return; + + /* Iterate through the objects in each hash entry */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each(lh, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + yaffs_verify_obj(obj); + } + } +} + +void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) +{ + struct list_head *lh; + struct yaffs_obj *list_obj; + int count = 0; + + if (!obj) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); + BUG(); + return; + } + + if (yaffs_skip_verification(obj->my_dev)) + return; + + if (!obj->parent) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); + BUG(); + return; + } + + if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); + BUG(); + } + + /* Iterate through the objects in each hash entry */ + + list_for_each(lh, &obj->parent->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings); + yaffs_verify_obj(list_obj); + if (obj == list_obj) + count++; + } + + if (count != 1) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Object in directory %d times", + count); + BUG(); + } +} + +void yaffs_verify_dir(struct yaffs_obj *directory) +{ + struct list_head *lh; + struct yaffs_obj *list_obj; + + if (!directory) { + BUG(); + return; + } + + if (yaffs_skip_full_verification(directory->my_dev)) + return; + + if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Directory has wrong type: %d", + directory->variant_type); + BUG(); + } + + /* Iterate through the objects in each hash entry */ + + list_for_each(lh, &directory->variant.dir_variant.children) { + list_obj = list_entry(lh, struct yaffs_obj, siblings); + if (list_obj->parent != directory) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Object in directory list has wrong parent %p", + list_obj->parent); + BUG(); + } + yaffs_verify_obj_in_dir(list_obj); + } +} + +static int yaffs_free_verification_failures; + +void yaffs_verify_free_chunks(struct yaffs_dev *dev) +{ + int counted; + int difference; + + if (yaffs_skip_verification(dev)) + return; + + counted = yaffs_count_free_chunks(dev); + + difference = dev->n_free_chunks - counted; + + if (difference) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Freechunks verification failure %d %d %d", + dev->n_free_chunks, counted, difference); + yaffs_free_verification_failures++; + } +} + +int yaffs_verify_file_sane(struct yaffs_obj *in) +{ + in = in; + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_verify.h b/fs/yaffs2-new/yaffs_verify.h new file mode 100644 index 00000000000..4f4af8d29aa --- /dev/null +++ b/fs/yaffs2-new/yaffs_verify.h @@ -0,0 +1,43 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_VERIFY_H__ +#define __YAFFS_VERIFY_H__ + +#include "yaffs_guts.h" + +void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, + int n); +void yaffs_verify_collected_blk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, int n); +void yaffs_verify_blocks(struct yaffs_dev *dev); + +void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, + struct yaffs_ext_tags *tags, int parent_check); +void yaffs_verify_file(struct yaffs_obj *obj); +void yaffs_verify_link(struct yaffs_obj *obj); +void yaffs_verify_symlink(struct yaffs_obj *obj); +void yaffs_verify_special(struct yaffs_obj *obj); +void yaffs_verify_obj(struct yaffs_obj *obj); +void yaffs_verify_objects(struct yaffs_dev *dev); +void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); +void yaffs_verify_dir(struct yaffs_obj *directory); +void yaffs_verify_free_chunks(struct yaffs_dev *dev); + +int yaffs_verify_file_sane(struct yaffs_obj *obj); + +int yaffs_skip_verification(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2-new/yaffs_yaffs1.c b/fs/yaffs2-new/yaffs_yaffs1.c new file mode 100644 index 00000000000..da6a40ff96f --- /dev/null +++ b/fs/yaffs2-new/yaffs_yaffs1.c @@ -0,0 +1,424 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_yaffs1.h" +#include "yportenv.h" +#include "yaffs_trace.h" +#include "yaffs_bitmap.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_nand.h" +#include "yaffs_attribs.h" + +int yaffs1_scan(struct yaffs_dev *dev) +{ + struct yaffs_ext_tags tags; + int blk; + int result; + int chunk; + int c; + int deleted; + enum yaffs_block_state state; + LIST_HEAD(hard_list); + struct yaffs_block_info *bi; + u32 seq_number; + struct yaffs_obj_hdr *oh; + struct yaffs_obj *in; + struct yaffs_obj *parent; + int alloc_failed = 0; + struct yaffs_shadow_fixer *shadow_fixers = NULL; + u8 *chunk_data; + + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs1_scan starts intstartblk %d intendblk %d...", + dev->internal_start_block, dev->internal_end_block); + + chunk_data = yaffs_get_temp_buffer(dev); + + dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; + + /* Scan all the blocks to determine their state */ + bi = dev->block_info; + for (blk = dev->internal_start_block; blk <= dev->internal_end_block; + blk++) { + yaffs_clear_chunk_bits(dev, blk); + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + + yaffs_query_init_block_state(dev, blk, &state, &seq_number); + + bi->block_state = state; + bi->seq_number = seq_number; + + if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) + bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; + + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, + "Block scanning block %d state %d seq %d", + blk, state, seq_number); + + if (state == YAFFS_BLOCK_STATE_DEAD) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is bad", blk); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); + dev->n_erased_blocks++; + dev->n_free_chunks += dev->param.chunks_per_block; + } + bi++; + } + + /* For each block.... */ + for (blk = dev->internal_start_block; + !alloc_failed && blk <= dev->internal_end_block; blk++) { + + cond_resched(); + + bi = yaffs_get_block_info(dev, blk); + state = bi->block_state; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + for (c = 0; + !alloc_failed && c < dev->param.chunks_per_block && + state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->param.chunks_per_block + c; + + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || + tags.is_deleted) { + /* YAFFS1 only... + * A deleted chunk + */ + deleted++; + dev->n_free_chunks++; + } else if (!tags.chunk_used) { + /* An unassigned chunk in the block + * This means that either the block is empty or + * this is the one being allocated from + */ + + if (c == 0) { + /* We're looking at the first chunk in + *the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + } else { + /* this is the block being allocated */ + yaffs_trace(YAFFS_TRACE_SCAN, + " Allocating from %d %d", + blk, c); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->alloc_block = blk; + dev->alloc_page = c; + dev->alloc_block_finder = blk; + + } + + dev->n_free_chunks += + (dev->param.chunks_per_block - c); + } else if (tags.chunk_id > 0) { + /* chunk_id > 0 so it is a data chunk... */ + unsigned int endpos; + + yaffs_set_chunk_bit(dev, blk, c); + bi->pages_in_use++; + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + YAFFS_OBJECT_TYPE_FILE); + /* PutChunkIntoFile checks for a clash + * (two data chunks with the same chunk_id). + */ + + if (!in) + alloc_failed = 1; + + if (in) { + if (!yaffs_put_chunk_in_file + (in, tags.chunk_id, chunk, 1)) + alloc_failed = 1; + } + + endpos = + (tags.chunk_id - 1) * + dev->data_bytes_per_chunk + + tags.n_bytes; + if (in && + in->variant_type == + YAFFS_OBJECT_TYPE_FILE && + in->variant.file_variant.scanned_size < + endpos) { + in->variant.file_variant.scanned_size = + endpos; + if (!dev->param.use_header_file_size) { + in->variant. + file_variant.file_size = + in->variant. + file_variant.scanned_size; + } + + } + } else { + /* chunk_id == 0, so it is an ObjectHeader. + * Make the object + */ + yaffs_set_chunk_bit(dev, blk, c); + bi->pages_in_use++; + + result = yaffs_rd_chunk_tags_nand(dev, chunk, + chunk_data, + NULL); + + oh = (struct yaffs_obj_hdr *)chunk_data; + + in = yaffs_find_by_number(dev, tags.obj_id); + if (in && in->variant_type != oh->type) { + /* This should not happen, but somehow + * Wev'e ended up with an obj_id that + * has been reused but not yet deleted, + * and worse still it has changed type. + * Delete the old object. + */ + + yaffs_del_obj(in); + in = NULL; + } + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + oh->type); + + if (!in) + alloc_failed = 1; + + if (in && oh->shadows_obj > 0) { + + struct yaffs_shadow_fixer *fixer; + fixer = + kmalloc(sizeof + (struct yaffs_shadow_fixer), + GFP_NOFS); + if (fixer) { + fixer->next = shadow_fixers; + shadow_fixers = fixer; + fixer->obj_id = tags.obj_id; + fixer->shadowed_id = + oh->shadows_obj; + yaffs_trace(YAFFS_TRACE_SCAN, + " Shadow fixer: %d shadows %d", + fixer->obj_id, + fixer->shadowed_id); + + } + + } + + if (in && in->valid) { + /* We have already filled this one. + * We have a duplicate and need to + * resolve it. */ + + unsigned existing_serial = in->serial; + unsigned new_serial = + tags.serial_number; + + if (((existing_serial + 1) & 3) == + new_serial) { + /* Use new one - destroy the + * exisiting one */ + yaffs_chunk_del(dev, + in->hdr_chunk, + 1, __LINE__); + in->valid = 0; + } else { + /* Use existing - destroy + * this one. */ + yaffs_chunk_del(dev, chunk, 1, + __LINE__); + } + } + + if (in && !in->valid && + (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == + YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle + * with directory structure */ + in->valid = 1; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + + } else if (in && !in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->variant_type = oh->type; + + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->hdr_chunk = chunk; + in->serial = tags.serial_number; + + yaffs_set_obj_name_from_oh(in, oh); + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + parent = + yaffs_find_or_create_by_number + (dev, oh->parent_obj_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + if (!parent) + alloc_failed = 1; + if (parent && parent->variant_type == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variant_type = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent-> + variant.dir_variant. + children); + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, a problem.... + * We're trying to use a + * non-directory as a directory + */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + ); + parent = dev->lost_n_found; + } + + yaffs_add_obj_to_dir(parent, in); + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + if (dev->param. + use_header_file_size) + + in->variant. + file_variant.file_size + = oh->file_size; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant. + hardlink_variant.equiv_id = + oh->equiv_id; + list_add(&in->hard_links, + &hard_list); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symlink_variant. + alias = + yaffs_clone_str(oh->alias); + if (!in->variant. + symlink_variant.alias) + alloc_failed = 1; + break; + } + } + } + } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* If we got this far while scanning, + * then the block is fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + } + + if (state == YAFFS_BLOCK_STATE_ALLOCATING) { + /* If the block was partially allocated then + * treat it as fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + dev->alloc_block = -1; + } + + bi->block_state = state; + + /* Now let's see if it was dirty */ + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state == YAFFS_BLOCK_STATE_FULL) + yaffs_block_became_dirty(dev, blk); + } + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add + * these hardlinks. + */ + + yaffs_link_fixup(dev, &hard_list); + + /* + * Fix up any shadowed objects. + * There should not be more than one of these. + */ + { + struct yaffs_shadow_fixer *fixer; + struct yaffs_obj *obj; + + while (shadow_fixers) { + fixer = shadow_fixers; + shadow_fixers = fixer->next; + /* Complete the rename transaction by deleting the + * shadowed object then setting the object header + to unshadowed. + */ + obj = yaffs_find_by_number(dev, fixer->shadowed_id); + if (obj) + yaffs_del_obj(obj); + + obj = yaffs_find_by_number(dev, fixer->obj_id); + + if (obj) + yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); + + kfree(fixer); + } + } + + yaffs_release_temp_buffer(dev, chunk_data); + + if (alloc_failed) + return YAFFS_FAIL; + + yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); + + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_yaffs1.h b/fs/yaffs2-new/yaffs_yaffs1.h new file mode 100644 index 00000000000..97e2fdd08a5 --- /dev/null +++ b/fs/yaffs2-new/yaffs_yaffs1.h @@ -0,0 +1,22 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS1_H__ +#define __YAFFS_YAFFS1_H__ + +#include "yaffs_guts.h" +int yaffs1_scan(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2-new/yaffs_yaffs2.c b/fs/yaffs2-new/yaffs_yaffs2.c new file mode 100644 index 00000000000..5761e960e64 --- /dev/null +++ b/fs/yaffs2-new/yaffs_yaffs2.c @@ -0,0 +1,1532 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 "yaffs_guts.h" +#include "yaffs_trace.h" +#include "yaffs_yaffs2.h" +#include "yaffs_checkptrw.h" +#include "yaffs_bitmap.h" +#include "yaffs_nand.h" +#include "yaffs_getblockinfo.h" +#include "yaffs_verify.h" +#include "yaffs_attribs.h" +#include "yaffs_summary.h" + +/* + * Checkpoints are really no benefit on very small partitions. + * + * To save space on small partitions don't bother with checkpoints unless + * the partition is at least this big. + */ +#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 +#define YAFFS_SMALL_HOLE_THRESHOLD 4 + +/* + * Oldest Dirty Sequence Number handling. + */ + +/* yaffs_calc_oldest_dirty_seq() + * yaffs2_find_oldest_dirty_seq() + * Calculate the oldest dirty sequence number if we don't know it. + */ +void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) +{ + int i; + unsigned seq; + unsigned block_no = 0; + struct yaffs_block_info *b; + + if (!dev->param.is_yaffs2) + return; + + /* Find the oldest dirty sequence number. */ + seq = dev->seq_number + 1; + b = dev->block_info; + for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { + if (b->block_state == YAFFS_BLOCK_STATE_FULL && + (b->pages_in_use - b->soft_del_pages) < + dev->param.chunks_per_block && + b->seq_number < seq) { + seq = b->seq_number; + block_no = i; + } + b++; + } + + if (block_no) { + dev->oldest_dirty_seq = seq; + dev->oldest_dirty_block = block_no; + } +} + +void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) +{ + if (!dev->param.is_yaffs2) + return; + + if (!dev->oldest_dirty_seq) + yaffs_calc_oldest_dirty_seq(dev); +} + +/* + * yaffs_clear_oldest_dirty_seq() + * Called when a block is erased or marked bad. (ie. when its seq_number + * becomes invalid). If the value matches the oldest then we clear + * dev->oldest_dirty_seq to force its recomputation. + */ +void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, + struct yaffs_block_info *bi) +{ + + if (!dev->param.is_yaffs2) + return; + + if (!bi || bi->seq_number == dev->oldest_dirty_seq) { + dev->oldest_dirty_seq = 0; + dev->oldest_dirty_block = 0; + } +} + +/* + * yaffs2_update_oldest_dirty_seq() + * Update the oldest dirty sequence number whenever we dirty a block. + * Only do this if the oldest_dirty_seq is actually being tracked. + */ +void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, + struct yaffs_block_info *bi) +{ + if (!dev->param.is_yaffs2) + return; + + if (dev->oldest_dirty_seq) { + if (dev->oldest_dirty_seq > bi->seq_number) { + dev->oldest_dirty_seq = bi->seq_number; + dev->oldest_dirty_block = block_no; + } + } +} + +int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) +{ + + if (!dev->param.is_yaffs2) + return 1; /* disqualification only applies to yaffs2. */ + + if (!bi->has_shrink_hdr) + return 1; /* can gc */ + + yaffs2_find_oldest_dirty_seq(dev); + + /* Can't do gc of this block if there are any blocks older than this + * one that have discarded pages. + */ + return (bi->seq_number <= dev->oldest_dirty_seq); +} + +/* + * yaffs2_find_refresh_block() + * periodically finds the oldest full block by sequence number for refreshing. + * Only for yaffs2. + */ +u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) +{ + u32 b; + u32 oldest = 0; + u32 oldest_seq = 0; + struct yaffs_block_info *bi; + + if (!dev->param.is_yaffs2) + return oldest; + + /* + * If refresh period < 10 then refreshing is disabled. + */ + if (dev->param.refresh_period < 10) + return oldest; + + /* + * Fix broken values. + */ + if (dev->refresh_skip > dev->param.refresh_period) + dev->refresh_skip = dev->param.refresh_period; + + if (dev->refresh_skip > 0) + return oldest; + + /* + * Refresh skip is now zero. + * We'll do a refresh this time around.... + * Update the refresh skip and find the oldest block. + */ + dev->refresh_skip = dev->param.refresh_period; + dev->refresh_count++; + bi = dev->block_info; + for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { + + if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { + + if (oldest < 1 || bi->seq_number < oldest_seq) { + oldest = b; + oldest_seq = bi->seq_number; + } + } + bi++; + } + + if (oldest > 0) { + yaffs_trace(YAFFS_TRACE_GC, + "GC refresh count %d selected block %d with seq_number %d", + dev->refresh_count, oldest, oldest_seq); + } + + return oldest; +} + +int yaffs2_checkpt_required(struct yaffs_dev *dev) +{ + int nblocks; + + if (!dev->param.is_yaffs2) + return 0; + + nblocks = dev->internal_end_block - dev->internal_start_block + 1; + + return !dev->param.skip_checkpt_wr && + !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); +} + +int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) +{ + int retval; + int n_bytes = 0; + int n_blocks; + int dev_blocks; + + if (!dev->param.is_yaffs2) + return 0; + + if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { + /* Not a valid value so recalculate */ + dev_blocks = dev->param.end_block - dev->param.start_block + 1; + n_bytes += sizeof(struct yaffs_checkpt_validity); + n_bytes += sizeof(struct yaffs_checkpt_dev); + n_bytes += dev_blocks * sizeof(struct yaffs_block_info); + n_bytes += dev_blocks * dev->chunk_bit_stride; + n_bytes += + (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * + dev->n_obj; + n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; + n_bytes += sizeof(struct yaffs_checkpt_validity); + n_bytes += sizeof(u32); /* checksum */ + + /* Round up and add 2 blocks to allow for some bad blocks, + * so add 3 */ + + n_blocks = + (n_bytes / + (dev->data_bytes_per_chunk * + dev->param.chunks_per_block)) + 3; + + dev->checkpoint_blocks_required = n_blocks; + } + + retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; + if (retval < 0) + retval = 0; + return retval; +} + +/*--------------------- Checkpointing --------------------*/ + +static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) +{ + struct yaffs_checkpt_validity cp; + + memset(&cp, 0, sizeof(cp)); + + cp.struct_type = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; + + return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; +} + +static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) +{ + struct yaffs_checkpt_validity cp; + int ok; + + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + + if (ok) + ok = (cp.struct_type == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && + (cp.head == ((head) ? 1 : 0)); + return ok ? 1 : 0; +} + +static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, + struct yaffs_dev *dev) +{ + cp->n_erased_blocks = dev->n_erased_blocks; + cp->alloc_block = dev->alloc_block; + cp->alloc_page = dev->alloc_page; + cp->n_free_chunks = dev->n_free_chunks; + + cp->n_deleted_files = dev->n_deleted_files; + cp->n_unlinked_files = dev->n_unlinked_files; + cp->n_bg_deletions = dev->n_bg_deletions; + cp->seq_number = dev->seq_number; + +} + +static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, + struct yaffs_checkpt_dev *cp) +{ + dev->n_erased_blocks = cp->n_erased_blocks; + dev->alloc_block = cp->alloc_block; + dev->alloc_page = cp->alloc_page; + dev->n_free_chunks = cp->n_free_chunks; + + dev->n_deleted_files = cp->n_deleted_files; + dev->n_unlinked_files = cp->n_unlinked_files; + dev->n_bg_deletions = cp->n_bg_deletions; + dev->seq_number = cp->seq_number; +} + +static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_dev cp; + u32 n_bytes; + u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + int ok; + + /* Write device runtime values */ + yaffs2_dev_to_checkpt_dev(&cp, dev); + cp.struct_type = sizeof(cp); + + ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; + + /* Write block info */ + n_bytes = n_blocks * sizeof(struct yaffs_block_info); + ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); + if (!ok) + return 0; + + /* Write chunk bits */ + n_bytes = n_blocks * dev->chunk_bit_stride; + ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) +{ + struct yaffs_checkpt_dev cp; + u32 n_bytes; + u32 n_blocks = + (dev->internal_end_block - dev->internal_start_block + 1); + int ok; + + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (!ok) + return 0; + + if (cp.struct_type != sizeof(cp)) + return 0; + + yaffs_checkpt_dev_to_dev(dev, &cp); + + n_bytes = n_blocks * sizeof(struct yaffs_block_info); + + ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); + + if (!ok) + return 0; + + n_bytes = n_blocks * dev->chunk_bit_stride; + + ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); + + return ok ? 1 : 0; +} + +static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, + struct yaffs_obj *obj) +{ + cp->obj_id = obj->obj_id; + cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; + cp->hdr_chunk = obj->hdr_chunk; + cp->variant_type = obj->variant_type; + cp->deleted = obj->deleted; + cp->soft_del = obj->soft_del; + cp->unlinked = obj->unlinked; + cp->fake = obj->fake; + cp->rename_allowed = obj->rename_allowed; + cp->unlink_allowed = obj->unlink_allowed; + cp->serial = obj->serial; + cp->n_data_chunks = obj->n_data_chunks; + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + cp->size_or_equiv_obj = obj->variant.file_variant.file_size; + else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) + cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; +} + +static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, + struct yaffs_checkpt_obj *cp) +{ + struct yaffs_obj *parent; + + if (obj->variant_type != cp->variant_type) { + yaffs_trace(YAFFS_TRACE_ERROR, + "Checkpoint read object %d type %d chunk %d does not match existing object type %d", + cp->obj_id, cp->variant_type, cp->hdr_chunk, + obj->variant_type); + return 0; + } + + obj->obj_id = cp->obj_id; + + if (cp->parent_id) + parent = yaffs_find_or_create_by_number(obj->my_dev, + cp->parent_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; + + if (parent) { + if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_trace(YAFFS_TRACE_ALWAYS, + "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", + cp->obj_id, cp->parent_id, + cp->variant_type, cp->hdr_chunk, + parent->variant_type); + return 0; + } + yaffs_add_obj_to_dir(parent, obj); + } + + obj->hdr_chunk = cp->hdr_chunk; + obj->variant_type = cp->variant_type; + obj->deleted = cp->deleted; + obj->soft_del = cp->soft_del; + obj->unlinked = cp->unlinked; + obj->fake = cp->fake; + obj->rename_allowed = cp->rename_allowed; + obj->unlink_allowed = cp->unlink_allowed; + obj->serial = cp->serial; + obj->n_data_chunks = cp->n_data_chunks; + + if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + obj->variant.file_variant.file_size = cp->size_or_equiv_obj; + else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; + + if (obj->hdr_chunk > 0) + obj->lazy_loaded = 1; + return 1; +} + +static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, + struct yaffs_tnode *tn, u32 level, + int chunk_offset) +{ + int i; + struct yaffs_dev *dev = in->my_dev; + int ok = 1; + u32 base_offset; + + if (!tn) + return 1; + + if (level > 0) { + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { + if (!tn->internal[i]) + continue; + ok = yaffs2_checkpt_tnode_worker(in, + tn->internal[i], + level - 1, + (chunk_offset << + YAFFS_TNODES_INTERNAL_BITS) + i); + } + return ok; + } + + /* Level 0 tnode */ + base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; + ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == + sizeof(base_offset)); + if (ok) + ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == + dev->tnode_size); + + return ok; +} + +static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) +{ + u32 end_marker = ~0; + int ok = 1; + + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return ok; + + ok = yaffs2_checkpt_tnode_worker(obj, + obj->variant.file_variant.top, + obj->variant.file_variant. + top_level, 0); + if (ok) + ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, + sizeof(end_marker)) == sizeof(end_marker)); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) +{ + u32 base_chunk; + int ok = 1; + struct yaffs_dev *dev = obj->my_dev; + struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; + struct yaffs_tnode *tn; + int nread = 0; + + ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == + sizeof(base_chunk)); + + while (ok && (~base_chunk)) { + nread++; + /* Read level 0 tnode */ + + tn = yaffs_get_tnode(dev); + if (tn) + ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == + dev->tnode_size); + else + ok = 0; + + if (tn && ok) + ok = yaffs_add_find_tnode_0(dev, + file_stuct_ptr, + base_chunk, tn) ? 1 : 0; + + if (ok) + ok = (yaffs2_checkpt_rd + (dev, &base_chunk, + sizeof(base_chunk)) == sizeof(base_chunk)); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint read tnodes %d records, last %d. ok %d", + nread, base_chunk, ok); + + return ok ? 1 : 0; +} + +static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_checkpt_obj cp; + int i; + int ok = 1; + struct list_head *lh; + + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ + + for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { + list_for_each(lh, &dev->obj_bucket[i].list) { + obj = list_entry(lh, struct yaffs_obj, hash_link); + if (!obj->defered_free) { + yaffs2_obj_checkpt_obj(&cp, obj); + cp.struct_type = sizeof(cp); + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", + cp.obj_id, cp.parent_id, + cp.variant_type, cp.hdr_chunk, obj); + + ok = (yaffs2_checkpt_wr(dev, &cp, + sizeof(cp)) == sizeof(cp)); + + if (ok && + obj->variant_type == + YAFFS_OBJECT_TYPE_FILE) + ok = yaffs2_wr_checkpt_tnodes(obj); + } + } + } + + /* Dump end of list */ + memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); + cp.struct_type = sizeof(cp); + + if (ok) + ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); + + return ok ? 1 : 0; +} + +static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) +{ + struct yaffs_obj *obj; + struct yaffs_checkpt_obj cp; + int ok = 1; + int done = 0; + LIST_HEAD(hard_list); + + + while (ok && !done) { + ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); + if (cp.struct_type != sizeof(cp)) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "struct size %d instead of %d ok %d", + cp.struct_type, (int)sizeof(cp), ok); + ok = 0; + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "Checkpoint read object %d parent %d type %d chunk %d ", + cp.obj_id, cp.parent_id, cp.variant_type, + cp.hdr_chunk); + + if (ok && cp.obj_id == ~0) { + done = 1; + } else if (ok) { + obj = + yaffs_find_or_create_by_number(dev, cp.obj_id, + cp.variant_type); + if (obj) { + ok = yaffs2_checkpt_obj_to_obj(obj, &cp); + if (!ok) + break; + if (obj->variant_type == + YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs2_rd_checkpt_tnodes(obj); + } else if (obj->variant_type == + YAFFS_OBJECT_TYPE_HARDLINK) { + list_add(&obj->hard_links, &hard_list); + } + } else { + ok = 0; + } + } + } + + if (ok) + yaffs_link_fixup(dev, &hard_list); + + return ok ? 1 : 0; +} + +static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) +{ + u32 checkpt_sum; + int ok; + + yaffs2_get_checkpt_sum(dev, &checkpt_sum); + + ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == + sizeof(checkpt_sum)); + + if (!ok) + return 0; + + return 1; +} + +static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) +{ + u32 checkpt_sum0; + u32 checkpt_sum1; + int ok; + + yaffs2_get_checkpt_sum(dev, &checkpt_sum0); + + ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == + sizeof(checkpt_sum1)); + + if (!ok) + return 0; + + if (checkpt_sum0 != checkpt_sum1) + return 0; + + return 1; +} + +static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) +{ + int ok = 1; + + if (!yaffs2_checkpt_required(dev)) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "skipping checkpoint write"); + ok = 0; + } + + if (ok) + ok = yaffs2_checkpt_open(dev, 1); + + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint validity"); + ok = yaffs2_wr_checkpt_validity_marker(dev, 1); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint device"); + ok = yaffs2_wr_checkpt_dev(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint objects"); + ok = yaffs2_wr_checkpt_objs(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "write checkpoint validity"); + ok = yaffs2_wr_checkpt_validity_marker(dev, 0); + } + + if (ok) + ok = yaffs2_wr_checkpt_sum(dev); + + if (!yaffs_checkpt_close(dev)) + ok = 0; + + if (ok) + dev->is_checkpointed = 1; + else + dev->is_checkpointed = 0; + + return dev->is_checkpointed; +} + +static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) +{ + int ok = 1; + + if (!dev->param.is_yaffs2) + ok = 0; + + if (ok && dev->param.skip_checkpt_rd) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "skipping checkpoint read"); + ok = 0; + } + + if (ok) + ok = yaffs2_checkpt_open(dev, 0); /* open for read */ + + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint validity"); + ok = yaffs2_rd_checkpt_validity_marker(dev, 1); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint device"); + ok = yaffs2_rd_checkpt_dev(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint objects"); + ok = yaffs2_rd_checkpt_objs(dev); + } + if (ok) { + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint validity"); + ok = yaffs2_rd_checkpt_validity_marker(dev, 0); + } + + if (ok) { + ok = yaffs2_rd_checkpt_sum(dev); + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "read checkpoint checksum %d", ok); + } + + if (!yaffs_checkpt_close(dev)) + ok = 0; + + if (ok) + dev->is_checkpointed = 1; + else + dev->is_checkpointed = 0; + + return ok ? 1 : 0; +} + +void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) +{ + if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { + dev->is_checkpointed = 0; + yaffs2_checkpt_invalidate_stream(dev); + } + if (dev->param.sb_dirty_fn) + dev->param.sb_dirty_fn(dev); +} + +int yaffs_checkpoint_save(struct yaffs_dev *dev) +{ + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "save entry: is_checkpointed %d", + dev->is_checkpointed); + + yaffs_verify_objects(dev); + yaffs_verify_blocks(dev); + yaffs_verify_free_chunks(dev); + + if (!dev->is_checkpointed) { + yaffs2_checkpt_invalidate(dev); + yaffs2_wr_checkpt_data(dev); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, + "save exit: is_checkpointed %d", + dev->is_checkpointed); + + return dev->is_checkpointed; +} + +int yaffs2_checkpt_restore(struct yaffs_dev *dev) +{ + int retval; + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "restore entry: is_checkpointed %d", + dev->is_checkpointed); + + retval = yaffs2_rd_checkpt_data(dev); + + if (dev->is_checkpointed) { + yaffs_verify_objects(dev); + yaffs_verify_blocks(dev); + yaffs_verify_free_chunks(dev); + } + + yaffs_trace(YAFFS_TRACE_CHECKPOINT, + "restore exit: is_checkpointed %d", + dev->is_checkpointed); + + return retval; +} + +int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) +{ + /* if new_size > old_file_size. + * We're going to be writing a hole. + * If the hole is small then write zeros otherwise write a start + * of hole marker. + */ + loff_t old_file_size; + int increase; + int small_hole; + int result = YAFFS_OK; + struct yaffs_dev *dev = NULL; + u8 *local_buffer = NULL; + int small_increase_ok = 0; + + if (!obj) + return YAFFS_FAIL; + + if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) + return YAFFS_FAIL; + + dev = obj->my_dev; + + /* Bail out if not yaffs2 mode */ + if (!dev->param.is_yaffs2) + return YAFFS_OK; + + old_file_size = obj->variant.file_variant.file_size; + + if (new_size <= old_file_size) + return YAFFS_OK; + + increase = new_size - old_file_size; + + if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && + yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) + small_hole = 1; + else + small_hole = 0; + + if (small_hole) + local_buffer = yaffs_get_temp_buffer(dev); + + if (local_buffer) { + /* fill hole with zero bytes */ + int pos = old_file_size; + int this_write; + int written; + memset(local_buffer, 0, dev->data_bytes_per_chunk); + small_increase_ok = 1; + + while (increase > 0 && small_increase_ok) { + this_write = increase; + if (this_write > dev->data_bytes_per_chunk) + this_write = dev->data_bytes_per_chunk; + written = + yaffs_do_file_wr(obj, local_buffer, pos, this_write, + 0); + if (written == this_write) { + pos += this_write; + increase -= this_write; + } else { + small_increase_ok = 0; + } + } + + yaffs_release_temp_buffer(dev, local_buffer); + + /* If out of space then reverse any chunks we've added */ + if (!small_increase_ok) + yaffs_resize_file_down(obj, old_file_size); + } + + if (!small_increase_ok && + obj->parent && + obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && + obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { + /* Write a hole start header with the old file size */ + yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); + } + + return result; +} + +struct yaffs_block_index { + int seq; + int block; +}; + +static int yaffs2_ybicmp(const void *a, const void *b) +{ + int aseq = ((struct yaffs_block_index *)a)->seq; + int bseq = ((struct yaffs_block_index *)b)->seq; + int ablock = ((struct yaffs_block_index *)a)->block; + int bblock = ((struct yaffs_block_index *)b)->block; + + if (aseq == bseq) + return ablock - bblock; + + return aseq - bseq; +} + +static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, + struct yaffs_block_info *bi, + int blk, int chunk_in_block, + int *found_chunks, + u8 *chunk_data, + struct list_head *hard_list, + int summary_available) +{ + struct yaffs_obj_hdr *oh; + struct yaffs_obj *in; + struct yaffs_obj *parent; + int equiv_id; + int file_size; + int is_shrink; + int is_unlinked; + struct yaffs_ext_tags tags; + int result; + int alloc_failed = 0; + int chunk = blk * dev->param.chunks_per_block + chunk_in_block; + struct yaffs_file_var *file_var; + struct yaffs_hardlink_var *hl_var; + struct yaffs_symlink_var *sl_var; + + if (summary_available) { + result = yaffs_summary_fetch(dev, &tags, chunk_in_block); + tags.seq_number = bi->seq_number; + } + + if (!summary_available || tags.obj_id == 0) { + result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); + dev->tags_used++; + } else { + dev->summary_used++; + } + + /* Let's have a good look at this chunk... */ + + if (!tags.chunk_used) { + /* An unassigned chunk in the block. + * If there are used chunks after this one, then + * it is a chunk that was skipped due to failing + * the erased check. Just skip it so that it can + * be deleted. + * But, more typically, We get here when this is + * an unallocated chunk and his means that + * either the block is empty or this is the one + * being allocated from + */ + + if (*found_chunks) { + /* This is a chunk that was skipped due + * to failing the erased check */ + } else if (chunk_in_block == 0) { + /* We're looking at the first chunk in + * the block so the block is unused */ + bi->block_state = YAFFS_BLOCK_STATE_EMPTY; + dev->n_erased_blocks++; + } else { + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { + if (dev->seq_number == bi->seq_number) { + /* Allocating from this block*/ + yaffs_trace(YAFFS_TRACE_SCAN, + " Allocating from %d %d", + blk, chunk_in_block); + + bi->block_state = + YAFFS_BLOCK_STATE_ALLOCATING; + dev->alloc_block = blk; + dev->alloc_page = chunk_in_block; + dev->alloc_block_finder = blk; + } else { + /* This is a partially written block + * that is not the current + * allocation block. + */ + yaffs_trace(YAFFS_TRACE_SCAN, + "Partially written block %d detected. gc will fix this.", + blk); + } + } + } + + dev->n_free_chunks++; + + } else if (tags.ecc_result == + YAFFS_ECC_RESULT_UNFIXED) { + yaffs_trace(YAFFS_TRACE_SCAN, + " Unfixed ECC in chunk(%d:%d), chunk ignored", + blk, chunk_in_block); + dev->n_free_chunks++; + } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || + tags.chunk_id > YAFFS_MAX_CHUNK_ID || + tags.obj_id == YAFFS_OBJECTID_SUMMARY || + (tags.chunk_id > 0 && + tags.n_bytes > dev->data_bytes_per_chunk) || + tags.seq_number != bi->seq_number) { + yaffs_trace(YAFFS_TRACE_SCAN, + "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", + blk, chunk_in_block, tags.obj_id, + tags.chunk_id, tags.n_bytes); + dev->n_free_chunks++; + } else if (tags.chunk_id > 0) { + /* chunk_id > 0 so it is a data chunk... */ + unsigned int endpos; + u32 chunk_base = (tags.chunk_id - 1) * + dev->data_bytes_per_chunk; + + *found_chunks = 1; + + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + YAFFS_OBJECT_TYPE_FILE); + if (!in) + /* Out of memory */ + alloc_failed = 1; + + if (in && + in->variant_type == YAFFS_OBJECT_TYPE_FILE && + chunk_base < in->variant.file_variant.shrink_size) { + /* This has not been invalidated by + * a resize */ + if (!yaffs_put_chunk_in_file(in, tags.chunk_id, + chunk, -1)) + alloc_failed = 1; + + /* File size is calculated by looking at + * the data chunks if we have not + * seen an object header yet. + * Stop this practice once we find an + * object header. + */ + endpos = chunk_base + tags.n_bytes; + + if (!in->valid && + in->variant.file_variant.scanned_size < endpos) { + in->variant.file_variant. + scanned_size = endpos; + in->variant.file_variant. + file_size = endpos; + } + } else if (in) { + /* This chunk has been invalidated by a + * resize, or a past file deletion + * so delete the chunk*/ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + } + } else { + /* chunk_id == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make + * the object + */ + *found_chunks = 1; + + yaffs_set_chunk_bit(dev, blk, chunk_in_block); + bi->pages_in_use++; + + oh = NULL; + in = NULL; + + if (tags.extra_available) { + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, + tags.extra_obj_type); + if (!in) + alloc_failed = 1; + } + + if (!in || + (!in->valid && dev->param.disable_lazy_load) || + tags.extra_shadows || + (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { + + /* If we don't have valid info then we + * need to read the chunk + * TODO In future we can probably defer + * reading the chunk and living with + * invalid data until needed. + */ + + result = yaffs_rd_chunk_tags_nand(dev, + chunk, + chunk_data, + NULL); + + oh = (struct yaffs_obj_hdr *)chunk_data; + + if (dev->param.inband_tags) { + /* Fix up the header if they got + * corrupted by inband tags */ + oh->shadows_obj = + oh->inband_shadowed_obj_id; + oh->is_shrink = + oh->inband_is_shrink; + } + + if (!in) { + in = yaffs_find_or_create_by_number(dev, + tags.obj_id, oh->type); + if (!in) + alloc_failed = 1; + } + } + + if (!in) { + /* TODO Hoosterman we have a problem! */ + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: Could not make object for object %d at chunk %d during scan", + tags.obj_id, chunk); + return YAFFS_FAIL; + } + + if (in->valid) { + /* We have already filled this one. + * We have a duplicate that will be + * discarded, but we first have to suck + * out resize info if it is a file. + */ + if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && + ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || + (tags.extra_available && + tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) + )) { + u32 this_size = (oh) ? + oh->file_size : + tags.extra_length; + u32 parent_obj_id = (oh) ? + oh->parent_obj_id : + tags.extra_parent_id; + + is_shrink = (oh) ? + oh->is_shrink : + tags.extra_is_shrink; + + /* If it is deleted (unlinked + * at start also means deleted) + * we treat the file size as + * being zeroed at this point. + */ + if (parent_obj_id == YAFFS_OBJECTID_DELETED || + parent_obj_id == YAFFS_OBJECTID_UNLINKED) { + this_size = 0; + is_shrink = 1; + } + + if (is_shrink && + in->variant.file_variant.shrink_size > + this_size) + in->variant.file_variant.shrink_size = + this_size; + + if (is_shrink) + bi->has_shrink_hdr = 1; + } + /* Use existing - destroy this one. */ + yaffs_chunk_del(dev, chunk, 1, __LINE__); + } + + if (!in->valid && in->variant_type != + (oh ? oh->type : tags.extra_obj_type)) + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan", + oh ? oh->type : tags.extra_obj_type, + in->variant_type, tags.obj_id, + chunk); + + if (!in->valid && + (tags.obj_id == YAFFS_OBJECTID_ROOT || + tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle + * with directory structure */ + in->valid = 1; + + if (oh) { + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + in->lazy_loaded = 0; + } else { + in->lazy_loaded = 1; + } + in->hdr_chunk = chunk; + + } else if (!in->valid) { + /* we need to load this info */ + in->valid = 1; + in->hdr_chunk = chunk; + if (oh) { + in->variant_type = oh->type; + in->yst_mode = oh->yst_mode; + yaffs_load_attribs(in, oh); + + if (oh->shadows_obj > 0) + yaffs_handle_shadowed_obj(dev, + oh->shadows_obj, 1); + + yaffs_set_obj_name_from_oh(in, oh); + parent = yaffs_find_or_create_by_number(dev, + oh->parent_obj_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + file_size = oh->file_size; + is_shrink = oh->is_shrink; + equiv_id = oh->equiv_id; + } else { + in->variant_type = tags.extra_obj_type; + parent = yaffs_find_or_create_by_number(dev, + tags.extra_parent_id, + YAFFS_OBJECT_TYPE_DIRECTORY); + file_size = tags.extra_length; + is_shrink = tags.extra_is_shrink; + equiv_id = tags.extra_equiv_id; + in->lazy_loaded = 1; + } + in->dirty = 0; + + if (!parent) + alloc_failed = 1; + + /* directory stuff... + * hook up to parent + */ + + if (parent && + parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variant_type = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent-> + variant.dir_variant.children); + } else if (!parent || + parent->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY) { + /* Hoosterman, another problem.... + * Trying to use a non-directory as a directory + */ + + yaffs_trace(YAFFS_TRACE_ERROR, + "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." + ); + parent = dev->lost_n_found; + } + yaffs_add_obj_to_dir(parent, in); + + is_unlinked = (parent == dev->del_dir) || + (parent == dev->unlinked_dir); + + if (is_shrink) + /* Mark the block */ + bi->has_shrink_hdr = 1; + + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent + * object is scanned we put them all in a list. + * After scanning is complete, we should have all the + * objects, so we run through this list and fix up all + * the chains. + */ + + switch (in->variant_type) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + file_var = &in->variant.file_variant; + if (file_var->scanned_size < file_size) { + /* This covers the case where the file + * size is greater than the data held. + * This will happen if the file is + * resized to be larger than its + * current data extents. + */ + file_var->file_size = file_size; + file_var->scanned_size = file_size; + } + + if (file_var->shrink_size > file_size) + file_var->shrink_size = file_size; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + hl_var = &in->variant.hardlink_variant; + if (!is_unlinked) { + hl_var->equiv_id = equiv_id; + list_add(&in->hard_links, hard_list); + } + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + sl_var = &in->variant.symlink_variant; + if (oh) { + sl_var->alias = + yaffs_clone_str(oh->alias); + if (!sl_var->alias) + alloc_failed = 1; + } + break; + } + } + } + return alloc_failed ? YAFFS_FAIL : YAFFS_OK; +} + +int yaffs2_scan_backwards(struct yaffs_dev *dev) +{ + int blk; + int block_iter; + int start_iter; + int end_iter; + int n_to_scan = 0; + enum yaffs_block_state state; + int c; + int deleted; + LIST_HEAD(hard_list); + struct yaffs_block_info *bi; + u32 seq_number; + int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; + u8 *chunk_data; + int found_chunks; + int alloc_failed = 0; + struct yaffs_block_index *block_index = NULL; + int alt_block_index = 0; + int summary_available; + + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", + dev->internal_start_block, dev->internal_end_block); + + dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; + + block_index = + kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); + + if (!block_index) { + block_index = + vmalloc(n_blocks * sizeof(struct yaffs_block_index)); + alt_block_index = 1; + } + + if (!block_index) { + yaffs_trace(YAFFS_TRACE_SCAN, + "yaffs2_scan_backwards() could not allocate block index!" + ); + return YAFFS_FAIL; + } + + dev->blocks_in_checkpt = 0; + + chunk_data = yaffs_get_temp_buffer(dev); + + /* Scan all the blocks to determine their state */ + bi = dev->block_info; + for (blk = dev->internal_start_block; blk <= dev->internal_end_block; + blk++) { + yaffs_clear_chunk_bits(dev, blk); + bi->pages_in_use = 0; + bi->soft_del_pages = 0; + + yaffs_query_init_block_state(dev, blk, &state, &seq_number); + + bi->block_state = state; + bi->seq_number = seq_number; + + if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; + if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) + bi->block_state = YAFFS_BLOCK_STATE_DEAD; + + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, + "Block scanning block %d state %d seq %d", + blk, bi->block_state, seq_number); + + if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { + dev->blocks_in_checkpt++; + + } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is bad", blk); + } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); + dev->n_erased_blocks++; + dev->n_free_chunks += dev->param.chunks_per_block; + } else if (bi->block_state == + YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* Determine the highest sequence number */ + if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && + seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + block_index[n_to_scan].seq = seq_number; + block_index[n_to_scan].block = blk; + n_to_scan++; + if (seq_number >= dev->seq_number) + dev->seq_number = seq_number; + } else { + /* TODO: Nasty sequence number! */ + yaffs_trace(YAFFS_TRACE_SCAN, + "Block scanning block %d has bad sequence number %d", + blk, seq_number); + } + } + bi++; + } + + yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan); + + cond_resched(); + + /* Sort the blocks by sequence number */ + sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), + yaffs2_ybicmp, NULL); + + cond_resched(); + + yaffs_trace(YAFFS_TRACE_SCAN, "...done"); + + /* Now scan the blocks looking at the data. */ + start_iter = 0; + end_iter = n_to_scan - 1; + yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); + + /* For each block.... backwards */ + for (block_iter = end_iter; + !alloc_failed && block_iter >= start_iter; + block_iter--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ + cond_resched(); + + /* get the block to scan in the correct order */ + blk = block_index[block_iter].block; + bi = yaffs_get_block_info(dev, blk); + deleted = 0; + + summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); + + /* For each chunk in each block that needs scanning.... */ + found_chunks = 0; + if(summary_available) + c = dev->chunks_per_summary - 1; + else + c = dev->param.chunks_per_block - 1; + + for (/* c is already initialised */; + !alloc_failed && c >= 0 && + (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || + bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); + c--) { + /* Scan backwards... + * Read the tags and decide what to do + */ + if (yaffs2_scan_chunk(dev, bi, blk, c, + &found_chunks, chunk_data, + &hard_list, summary_available) == + YAFFS_FAIL) + alloc_failed = 1; + } + + if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { + /* If we got this far while scanning, then the block + * is fully allocated. */ + bi->block_state = YAFFS_BLOCK_STATE_FULL; + } + + /* Now let's see if it was dirty */ + if (bi->pages_in_use == 0 && + !bi->has_shrink_hdr && + bi->block_state == YAFFS_BLOCK_STATE_FULL) { + yaffs_block_became_dirty(dev, blk); + } + } + + yaffs_skip_rest_of_block(dev); + + if (alt_block_index) + vfree(block_index); + else + kfree(block_index); + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_link_fixup(dev, &hard_list); + + yaffs_release_temp_buffer(dev, chunk_data); + + if (alloc_failed) + return YAFFS_FAIL; + + yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); + + return YAFFS_OK; +} diff --git a/fs/yaffs2-new/yaffs_yaffs2.h b/fs/yaffs2-new/yaffs_yaffs2.h new file mode 100644 index 00000000000..2363bfd8bca --- /dev/null +++ b/fs/yaffs2-new/yaffs_yaffs2.h @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_YAFFS2_H__ +#define __YAFFS_YAFFS2_H__ + +#include "yaffs_guts.h" + +void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); +void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); +void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, + struct yaffs_block_info *bi); +void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, + struct yaffs_block_info *bi); +int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); +u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); +int yaffs2_checkpt_required(struct yaffs_dev *dev); +int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); + +void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); +int yaffs2_checkpt_save(struct yaffs_dev *dev); +int yaffs2_checkpt_restore(struct yaffs_dev *dev); + +int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); +int yaffs2_scan_backwards(struct yaffs_dev *dev); + +#endif diff --git a/fs/yaffs2-new/yaffscfg.h b/fs/yaffs2-new/yaffscfg.h new file mode 100644 index 00000000000..3503dc863fb --- /dev/null +++ b/fs/yaffs2-new/yaffscfg.h @@ -0,0 +1,45 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * Header file for using yaffs in an application via + * a direct interface. + */ + + +#ifndef __YAFFSCFG_H__ +#define __YAFFSCFG_H__ + + +#include "devextras.h" + +#define YAFFSFS_N_HANDLES 200 + + +typedef struct { + const char *prefix; + struct yaffs_DeviceStruct *dev; +} yaffsfs_DeviceConfiguration; + + +void yaffsfs_Lock(void); +void yaffsfs_Unlock(void); + +__u32 yaffsfs_CurrentTime(void); + +void yaffsfs_SetError(int err); +int yaffsfs_GetError(void); + +#endif diff --git a/fs/yaffs2-new/yaffsfs.c b/fs/yaffs2-new/yaffsfs.c new file mode 100644 index 00000000000..bbe91dbd924 --- /dev/null +++ b/fs/yaffs2-new/yaffsfs.c @@ -0,0 +1,1741 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * 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 */ +#include <common.h> +#include "malloc.h" + +#include <jffs2/load_kernel.h> + + +#include "mtd_parts.h" +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +#include "yaffscfg.h" +#include "yaffs_guts.h" +#include "yportenv.h" +#include "yaffs_mtdif.h" +#include "yaffs_mtdif2.h" +#include "yaffs_direct.h" +#include "yaffsfs_errno.h" + +#include "yaffsfs.h" + +#if (CONFIG_SYS_MALLOC_LEN < (512 << 10)) +#error Malloc Area too small for YAFFS, increas CONFIG_SYS_MALLOC_LEN to >= 512KiB +#endif + +/* YAFFSFS_RW_SIZE must be a power of 2 */ +#define YAFFSFS_RW_SHIFT (13) +#define YAFFSFS_RW_SIZE (1<<YAFFSFS_RW_SHIFT) + +typedef struct { + int count; /* Number of handles accessing this inode */ + struct yaffs_obj *iObj; +} yaffsfs_Inode; + +typedef struct{ + u8 reading:1; + u8 writing:1; + u8 append:1; + u8 shareRead:1; + u8 shareWrite:1; + int inodeId:12; /* Index to corresponding yaffsfs_Inode */ + int handleCount:10; /* Number of handles for this fd */ + u32 position; /* current position in file */ +}yaffsfs_FileDes; + +typedef struct { + short int fdId; + short int useCount; +} yaffsfs_Handle; + +static yaffsfs_Inode yaffsfs_inode[YAFFSFS_N_HANDLES]; +static yaffsfs_FileDes yaffsfs_fd[YAFFSFS_N_HANDLES]; +static yaffsfs_Handle yaffsfs_handle[YAFFSFS_N_HANDLES]; + +static int yaffsfs_handlesInitialised; + +/* + * yaffsfs_InitHandle + * Inilitalise handle management on start-up. + */ + +static void yaffsfs_InitHandles(void) +{ + int i; + if(yaffsfs_handlesInitialised) + return; + + memset(yaffsfs_inode,0,sizeof(yaffsfs_inode)); + memset(yaffsfs_fd,0,sizeof(yaffsfs_fd)); + memset(yaffsfs_handle,0,sizeof(yaffsfs_handle)); + + for(i = 0; i < YAFFSFS_N_HANDLES; i++) + yaffsfs_fd[i].inodeId = -1; + for(i = 0; i < YAFFSFS_N_HANDLES; i++) + yaffsfs_handle[i].fdId = -1; +} + +static yaffsfs_Handle *yaffsfs_HandleToPointer(int h) +{ + if(h >= 0 && h <= YAFFSFS_N_HANDLES) + return &yaffsfs_handle[h]; + return NULL; +} + +static yaffsfs_FileDes *yaffsfs_HandleToFileDes(int handle) +{ + yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); + + if(h && h->useCount > 0 && h->fdId >= 0 && h->fdId < YAFFSFS_N_HANDLES) + return &yaffsfs_fd[h->fdId]; + + return NULL; +} + +static yaffsfs_Inode *yaffsfs_HandleToInode(int handle) +{ + yaffsfs_FileDes *fd = yaffsfs_HandleToFileDes(handle); + + if(fd && fd->handleCount > 0 && + fd->inodeId >= 0 && fd->inodeId < YAFFSFS_N_HANDLES) + return &yaffsfs_inode[fd->inodeId]; + + return NULL; +} + +static struct yaffs_obj *yaffsfs_HandleToObject(int handle) +{ + yaffsfs_Inode *in = yaffsfs_HandleToInode(handle); + + if(in) + return in->iObj; + + return NULL; +} + + +/* + * yaffsfs_FindInodeIdForObject + * Find the inode entry for an object, if it exists. + */ + +static int yaffsfs_FindInodeIdForObject(struct yaffs_obj *obj) +{ + int i; + int ret = -1; + + if(obj) + obj = yaffs_get_equivalent_obj(obj); + + /* Look for it in open inode table*/ + for(i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++){ + if(yaffsfs_inode[i].iObj == obj) + ret = i; + } + return ret; +} + +/* + * yaffsfs_GetInodeIdForObject + * Grab an inode entry when opening a new inode. + */ +static int yaffsfs_GetInodeIdForObject(struct yaffs_obj *obj) +{ + int i; + int ret; + yaffsfs_Inode *in = NULL; + + if(obj) + obj = yaffs_get_equivalent_obj(obj); + + ret = yaffsfs_FindInodeIdForObject(obj); + + for(i = 0; i < YAFFSFS_N_HANDLES && ret < 0; i++){ + if(!yaffsfs_inode[i].iObj) + ret = i; + } + + if(ret>=0){ + in = &yaffsfs_inode[ret]; + if(!in->iObj) + in->count = 0; + in->iObj = obj; + in->count++; + } + + + return ret; +} + +/* yaffsfs_FindDevice + * yaffsfs_FindRoot + * Scan the parttion list to find the root; split out rest of path from + * Initialial partition. path must be /<partition>/<dir-or-file> */ + +#define N_MAX_PARTLEN 64 + +#include "nand.h" +#include "yaffsfs_errno.h" +#include "yaffs_trace.h" + +typedef struct +{ + u32 magic; + yaffs_dirent de; /* directory entry being used by this dsc */ + YCHAR name[NAME_MAX+1]; /* name of directory being searched */ + struct yaffs_obj *dirObj; /* ptr to directory being searched */ + struct yaffs_obj *nextReturn; /* obj to be returned by next readddir */ + int offset; + struct list_head others; +} yaffsfs_DirectorySearchContext; + +static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj); +static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc); + +typedef enum { + YAFFSFS_ACTION_CREATE, + YAFFSFS_ACTION_FIND, + YAFFSFS_ACTION_DESTROY +} yaffsfs_action_t; + +extern nand_info_t nand_info[]; + +static struct yaffs_dev *yaffsfs_FindDevice(const char *path, char **restOfPath, yaffsfs_action_t action) +{ + int slash_cnt, i, ret; + char partname[N_MAX_PARTLEN]; + void *part_priv; + loff_t part_off, part_size; + int part_idx; + struct mtd_device *dev; + void *cookie; + struct yaffs_dev *flashDev; + struct yaffs_param *param; + struct yaffs_direct_context *context = NULL; + struct mtd_info *mtd; + + if (!path) + return NULL; + if (*path != '/') { + printf("Path '%s' does not start with '/'!\n", path); + return NULL; + } + path++; /* Skip over leading '/' */ + + for (slash_cnt=i=0; i<N_MAX_PARTLEN && path[i]; ++i) { + if (path[i] == '/') + break; + partname[i] = path[i]; + } + partname[i] = '\0'; + + /* save rest of path(if exists) */ + *restOfPath = (char *)path+i; + if (**restOfPath == '/') + (*restOfPath)++; + + /* Find the partition */ + ret = mtd_get_part_priv(partname, &part_idx, &dev, &part_off, &part_size, &cookie, &part_priv, 1); + if (ret) + return NULL; + + switch(action) { + case YAFFSFS_ACTION_FIND: + return (struct yaffs_dev *)part_priv; + + case YAFFSFS_ACTION_CREATE: + if (part_priv) { + printf("Huh? mount of partition '%s' already has device %p\n", partname, part_priv); + return NULL; + } + + /* First time we're seeing this device, create it using + information inside of the MTD_DEVICE structure */ + flashDev = malloc(sizeof(struct yaffs_dev)); + if (!flashDev) { + printf("%s:%d out of memory!\n", __FUNCTION__, __LINE__); + return NULL; + } + memset(flashDev, 0, sizeof(*flashDev)); + + context = malloc(sizeof(struct yaffs_direct_context)); + if (!context) { + printf("%s:%d out of memory!\n", __FUNCTION__, __LINE__); + free(flashDev); + return NULL; + } + + /* Side effect of mtd_get_part_priv() is to set nand_curr_device */ + mtd = &nand_info[nand_curr_device]; + + flashDev->driver_context = mtd; + + memset(context, 0, sizeof(*context)); + flashDev->os_context = context; + yaffs_dev_to_lc(flashDev)->spare_buffer = + malloc(mtd->oobsize); + + param = &(flashDev->param); + + param->start_block = part_off / mtd->erasesize; + param->end_block = (part_off + part_size - 1) / mtd->erasesize; +#if 0 + printf("%s: part_off %x part_size %x startBlock %u endBlock %u\n", __FUNCTION__, (unsigned int)part_off, (unsigned int)part_size, param->start_block, param->end_block); +#endif + i = param->end_block - param->start_block + 1; + + /* limit number of reserved blocks to 10% of available space; + * 3 minimum, 8 maximum */ + param->n_reserved_blocks = i/10; + if (param->n_reserved_blocks > 8) + param->n_reserved_blocks = 8; + if (param->n_reserved_blocks < 3) + param->n_reserved_blocks = 3; + + + + param->write_chunk_tags_fn = nandmtd2_write_chunk_tags; + param->read_chunk_tags_fn = nandmtd2_read_chunk_tags; + param->bad_block_fn = nandmtd2_mark_block_bad; + param->query_block_fn = nandmtd2_query_block; + param->is_yaffs2 = 1; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + param->total_bytes_per_chunk = mtd->writesize; + param->chunks_per_block = mtd->erasesize / mtd->writesize; +#else + param->total_bytes_per_chunk = mtd->oobblock; + param->chunks_per_block = mtd->erasesize / mtd->oobblock; +#endif + /* ... and common functions */ + param->erase_fn = nandmtd_erase_block; + param->initialise_flash_fn = nandmtd_initialise; + param->remove_obj_fn = yaffsfs_RemoveObjectCallback; + + /* If the OOB area has only 16 bytes available can't + ECC the tag (must assume OOB is ECC'd already) */ + if (mtd->ecclayout && mtd->ecclayout->oobavail <= 16) { + printf("ECC oobavail <= 16; forcing \"tags-ecc-off\"\n"); + param->no_tags_ecc = 1; + } + + mtd_set_part_priv(cookie, flashDev); + + return flashDev; + + case YAFFSFS_ACTION_DESTROY: + flashDev = (struct yaffs_dev *)part_priv; + if (yaffs_dev_to_lc(flashDev)->spare_buffer) + free(yaffs_dev_to_lc(flashDev)->spare_buffer); + if (flashDev->os_context) + free(flashDev->os_context); + free(part_priv); + mtd_set_part_priv(cookie, NULL); + return NULL; + + default: + printf("%s: unknown action %d\n", __FUNCTION__, (int)action); + return NULL; + } + +} + +static struct yaffs_obj *yaffsfs_FindRoot(const char *path, char **restOfPath) +{ + + struct yaffs_dev *dev; + + dev= yaffsfs_FindDevice(path,restOfPath, YAFFSFS_ACTION_FIND); + if(dev && dev->is_mounted) + { + return dev->root_dir; + } + return NULL; +} + +loff_t yaffs_freespace(const char *path) +{ + loff_t retVal=-1; + struct yaffs_dev *dev=NULL; + char *dummy; + + yaffsfs_Lock(); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_FIND); + if (!dev) { + yaffsfs_set_error(-ENODEV); + } else if(dev->is_mounted) { + retVal = yaffs_get_n_free_chunks(dev); + retVal *= dev->data_bytes_per_chunk; + } else { + yaffsfs_set_error(-EPERM); + } + + yaffsfs_Unlock(); + return retVal; +} + +#define yaffsfs_IsPathDivider(x) ((x) == '/') + +/* Forward references */ +static struct yaffs_obj *yaffsfs_find_object(struct yaffs_obj *rel_dir, + const char *path, int sym_depth, int getEquiv, + struct yaffs_obj **dir_out, int *not_dir, int *loop); + +static struct yaffs_obj *yaffsfs_follow_link(struct yaffs_obj *obj,int symDepth, int *loop) +{ + if (obj) + obj = yaffs_get_equivalent_obj(obj); + + while(obj && obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) + { + YCHAR *alias = obj->variant.symlink_variant.alias; + + if(yaffsfs_IsPathDivider(*alias)) + /* Starts with a /, need to scan from root up */ + obj = yaffsfs_find_object(NULL,alias,symDepth++, + 1,NULL,NULL,loop); + else + /* Relative to here, so use the parent of the symlink as a start */ + obj = yaffsfs_find_object(obj->parent,alias,symDepth++, + 1,NULL,NULL,loop); + } + return obj; +} + +#define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5 + +// yaffsfs_find_directory +// Parse a path to determine the directory and the name within the directory. +// +// eg. "/data/xx/ff" --> puts name="ff" and returns the directory "/data/xx" + +static struct yaffs_obj *yaffsfs_do_find_directory(struct yaffs_obj *startDir, + const YCHAR *path, YCHAR **name, int symDepth, + int *notDir,int *loop) +{ + struct yaffs_obj *dir; + YCHAR *restOfPath; + YCHAR str[YAFFS_MAX_NAME_LENGTH+1]; + int i; + + if(symDepth > YAFFSFS_MAX_SYMLINK_DEREFERENCES){ + if(loop) + *loop = 1; + return NULL; + } + + if(startDir){ + dir = startDir; + restOfPath = (YCHAR *)path; + } + else + dir = yaffsfs_FindRoot(path,&restOfPath); + + + while(dir){ + /* + * parse off /. + * curve ball: also throw away surplus '/' + * eg. "/ram/x////ff" gets treated the same as "/ram/x/ff" + */ + while(yaffsfs_IsPathDivider(*restOfPath)) + restOfPath++; /* get rid of '/' */ + + *name = restOfPath; + i = 0; + + while(*restOfPath && !yaffsfs_IsPathDivider(*restOfPath)){ + if (i < YAFFS_MAX_NAME_LENGTH){ + str[i] = *restOfPath; + str[i+1] = '\0'; + i++; + } + restOfPath++; + } + + if(!*restOfPath) + /* got to the end of the string */ + return dir; + else{ + if(strcmp(str,_Y(".")) == 0){ + /* Do nothing */ + } else if(strcmp(str,_Y("..")) == 0) { + dir = dir->parent; + } else{ + dir = yaffs_find_by_name(dir,str); + + dir = yaffsfs_follow_link(dir,symDepth,loop); + + if(dir && dir->variant_type != + YAFFS_OBJECT_TYPE_DIRECTORY){ + if(notDir) + *notDir = 1; + dir = NULL; + } + + } + } + } + /* directory did not exist. */ + return NULL; +} + +static struct yaffs_obj *yaffsfs_find_directory(struct yaffs_obj *relDir, + const YCHAR *path, + YCHAR **name, + int symDepth, + int *notDir, + int *loop) +{ + return yaffsfs_do_find_directory(relDir,path,name,symDepth,notDir,loop); +} + + +static struct yaffs_obj *yaffsfs_find_object(struct yaffs_obj *relative_dir, + const char *path, int sym_depth, int get_equiv, + struct yaffs_obj **dir_out, int *not_dir, int *loop) +{ + struct yaffs_obj *dir; + struct yaffs_obj *obj; + char *name; + + dir = yaffsfs_find_directory(relative_dir, path, &name, sym_depth, not_dir, loop); + + if (dir_out) + *dir_out = dir; + + + if (dir && *name) + obj = yaffs_find_by_name(dir, name); + else + obj = dir; + + if (get_equiv) + obj = yaffs_get_equivalent_obj(obj); + + return obj; +} + +int yaffsfs_mount(const char *path) +{ + int retVal=-1; + int result=YAFFS_FAIL; + struct yaffs_dev *dev=NULL; + char *dummy; + + yaffs_trace(YAFFS_TRACE_ALWAYS, "yaffs: Mounting %s",path); + + yaffsfs_Lock(); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_CREATE); + if(dev) + { + if(!dev->is_mounted) + { + result = yaffs_guts_initialise(dev); + if(result == YAFFS_FAIL) + { + yaffsfs_FindDevice(path, &dummy, YAFFSFS_ACTION_DESTROY); + // todo error - mount failed + yaffsfs_set_error(-ENOMEM); + } + retVal = result ? 0 : -1; + + } + else + { + //todo error - already mounted. + yaffsfs_set_error(-EBUSY); + } + } + else + { + // todo error - no device + yaffsfs_set_error(-ENODEV); + } + yaffsfs_Unlock(); + return retVal; + +} + +int yaffsfs_unmount(const char *path) +{ + int retVal=-1; + struct yaffs_dev *dev=NULL; + char *dummy; + + yaffsfs_Lock(); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_FIND); + if(dev) + { + if(dev->is_mounted) + { + yaffs_flush_whole_cache(dev); + yaffs_checkpoint_save(dev); + + yaffs_deinitialise(dev); + + yaffsfs_FindDevice(path, &dummy, YAFFSFS_ACTION_DESTROY); + + retVal = 0; + } + else + { + //todo error - not mounted. + yaffsfs_set_error(-EINVAL); + + } + } + else + { + // todo error - no device + yaffsfs_set_error(-ENODEV); + } + yaffsfs_Unlock(); + return retVal; + +} + +static struct list_head search_contexts; + +static int yaffsfs_CheckPath(const YCHAR *path) +{ + int n=0; + int divs=0; + while(*path && n < YAFFS_MAX_NAME_LENGTH && divs < 100){ + if(yaffsfs_IsPathDivider(*path)){ + n=0; + divs++; + } else + n++; + path++; + } + + return (*path) ? -1 : 0; +} + +static void yaffsfs_SetDirRewound(yaffsfs_DirectorySearchContext *dsc) +{ + if(dsc && + dsc->dirObj && + dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){ + + dsc->offset = 0; + + if( list_empty(&dsc->dirObj->variant.dir_variant.children)) + dsc->nextReturn = NULL; + else + dsc->nextReturn = list_entry(dsc->dirObj->variant.dir_variant.children.next, + struct yaffs_obj,siblings); + } else { + /* Hey someone isn't playing nice! */ + } +} + +static void yaffsfs_RemoveObjectCallback(struct yaffs_obj *obj) +{ + + struct list_head *i; + yaffsfs_DirectorySearchContext *dsc; + + /* if search contexts not initilised then skip */ + if(!search_contexts.next) + return; + + /* Iterate through the directory search contexts. + * If any are the one being removed, then advance the dsc to + * the next one to prevent a hanging ptr. + */ + list_for_each(i, &search_contexts) { + if (i) { + dsc = list_entry(i, yaffsfs_DirectorySearchContext,others); + if(dsc->nextReturn == obj) + yaffsfs_DirAdvance(dsc); + } + } + +} + +yaffs_DIR *yaffs_opendir(const YCHAR *dirname) +{ + yaffs_DIR *dir = NULL; + struct yaffs_obj *obj = NULL; + yaffsfs_DirectorySearchContext *dsc = NULL; + int notDir = 0; + int loop = 0; + + if(!dirname){ + yaffsfs_set_error(-EFAULT); + return NULL; + } + + if(yaffsfs_CheckPath(dirname) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return NULL; + } + + yaffsfs_Lock(); + + obj = yaffsfs_find_object(NULL,dirname,0,1,NULL,¬Dir,&loop); + + if(!obj && notDir) + yaffsfs_set_error(-ENOTDIR); + else if(loop) + yaffsfs_set_error(-ELOOP); + else if(!obj) + yaffsfs_set_error(-ENOENT); + else if(obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + yaffsfs_set_error(-ENOTDIR); + else { + + dsc = kmalloc(sizeof(yaffsfs_DirectorySearchContext), 0); + dir = (yaffs_DIR *)dsc; + + if(dsc){ + memset(dsc,0,sizeof(yaffsfs_DirectorySearchContext)); + dsc->magic = YAFFS_MAGIC; + dsc->dirObj = obj; + strncpy(dsc->name,dirname,NAME_MAX); + INIT_LIST_HEAD(&dsc->others); + + if(!search_contexts.next) + INIT_LIST_HEAD(&search_contexts); + + list_add(&dsc->others,&search_contexts); + yaffsfs_SetDirRewound(dsc); + } + + } + + yaffsfs_Unlock(); + + return dir; +} + +static void yaffsfs_DirAdvance(yaffsfs_DirectorySearchContext *dsc) +{ + if(dsc && + dsc->dirObj && + dsc->dirObj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){ + + if( dsc->nextReturn == NULL || + list_empty(&dsc->dirObj->variant.dir_variant.children)) + dsc->nextReturn = NULL; + else { + struct list_head *next = dsc->nextReturn->siblings.next; + + if( next == &dsc->dirObj->variant.dir_variant.children) + dsc->nextReturn = NULL; /* end of list */ + else + dsc->nextReturn = list_entry(next,struct yaffs_obj,siblings); + } + } else { + /* Hey someone isn't playing nice! */ + } +} + +static int yaffsfs_alt_dir_path(const YCHAR *path, YCHAR **ret_path) +{ + YCHAR *alt_path = NULL; + int path_length; + int i; + + /* + * We don't have a definition for max path length. + * We will use 3 * max name length instead. + */ + *ret_path = NULL; + path_length = strnlen(path,(YAFFS_MAX_NAME_LENGTH+1)*3 +1); + + /* If the last character is a path divider, then we need to + * trim it back so that the name look-up works properly. + * eg. /foo/new_dir/ -> /foo/newdir + * Curveball: Need to handle multiple path dividers: + * eg. /foof/sdfse///// -> /foo/sdfse + */ + if(path_length > 0 && + yaffsfs_IsPathDivider(path[path_length-1])){ + alt_path = kmalloc(path_length + 1, 0); + if(!alt_path) + return -1; + strcpy(alt_path, path); + for(i = path_length-1; + i >= 0 && yaffsfs_IsPathDivider(alt_path[i]); + i--) + alt_path[i] = (YCHAR) 0; + } + *ret_path = alt_path; + return 0; +} + +struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp) +{ + yaffsfs_DirectorySearchContext *dsc = (yaffsfs_DirectorySearchContext *)dirp; + struct yaffs_dirent *retVal = NULL; + + yaffsfs_Lock(); + + if(dsc && dsc->magic == YAFFS_MAGIC){ + yaffsfs_set_error(0); + if(dsc->nextReturn){ + dsc->de.d_ino = yaffs_get_equivalent_obj(dsc->nextReturn)->obj_id; + dsc->de.d_dont_use = (unsigned)dsc->nextReturn; + dsc->de.d_off = dsc->offset++; + yaffs_get_obj_name(dsc->nextReturn,dsc->de.d_name,NAME_MAX); + if(strnlen(dsc->de.d_name,NAME_MAX+1) == 0) + { + /* this should not happen! */ + strcpy(dsc->de.d_name,_Y("zz")); + } + dsc->de.d_reclen = sizeof(struct yaffs_dirent); + retVal = &dsc->de; + yaffsfs_DirAdvance(dsc); + } else + retVal = NULL; + } else + yaffsfs_set_error(-EBADF); + + yaffsfs_Unlock(); + + return retVal; + +} + +int yaffsfs_do_unlink(const YCHAR *path,int isDirectory) +{ + struct yaffs_obj *dir = NULL; + struct yaffs_obj *obj = NULL; + YCHAR *name; + int result = YAFFS_FAIL; + int notDir = 0; + int loop = 0; + + if(!path){ + yaffsfs_set_error(-EFAULT); + return -1; + } + + if(yaffsfs_CheckPath(path) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return -1; + } + + yaffsfs_Lock(); + + obj = yaffsfs_find_object(NULL,path,0,0,NULL,NULL,NULL); + dir = yaffsfs_find_directory(NULL,path,&name,0,¬Dir,&loop); + + if(!dir && notDir) + yaffsfs_set_error(-ENOTDIR); + else if(loop) + yaffsfs_set_error(-ELOOP); + else if(!dir) + yaffsfs_set_error(-ENOENT); + else if(strncmp(name,_Y("."),2) == 0) + yaffsfs_set_error(-EINVAL); + else if(!obj) + yaffsfs_set_error(-ENOENT); + else if(obj->my_dev->read_only) + yaffsfs_set_error(-EROFS); + else if(!isDirectory && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) + yaffsfs_set_error(-EISDIR); + else if(isDirectory && obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + yaffsfs_set_error(-ENOTDIR); + else if(isDirectory && obj == obj->my_dev->root_dir) + yaffsfs_set_error(-EBUSY); /* Can't rmdir a root */ + else { + result = yaffs_unlinker(dir,name); + + if(result == YAFFS_FAIL && isDirectory) + yaffsfs_set_error(-ENOTEMPTY); + } + + yaffsfs_Unlock(); + + return (result == YAFFS_FAIL) ? -1 : 0; +} + +int yaffs_unlink(const YCHAR *path) +{ + return yaffsfs_do_unlink(path,0); +} + + +int yaffs_rename(const YCHAR *oldPath, const YCHAR *newPath) +{ + struct yaffs_obj *olddir = NULL; + struct yaffs_obj *newdir = NULL; + struct yaffs_obj *obj = NULL; + struct yaffs_obj *newobj = NULL; + YCHAR *oldname; + YCHAR *newname; + int result= YAFFS_FAIL; + int rename_allowed = 1; + int notOldDir = 0; + int notNewDir = 0; + int oldLoop = 0; + int newLoop = 0; + + YCHAR *alt_newpath=NULL; + + if(!oldPath || !newPath){ + yaffsfs_set_error(-EFAULT); + return -1; + } + + if(yaffsfs_CheckPath(oldPath) < 0 || + yaffsfs_CheckPath(newPath) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return -1; + } + + if(yaffsfs_alt_dir_path(newPath, &alt_newpath) < 0){ + yaffsfs_set_error(-ENOMEM); + return -1; + } + if(alt_newpath) + newPath = alt_newpath; + + yaffsfs_Lock(); + + + olddir = yaffsfs_find_directory(NULL,oldPath,&oldname,0,¬OldDir,&oldLoop); + newdir = yaffsfs_find_directory(NULL,newPath,&newname,0,¬NewDir,&newLoop); + obj = yaffsfs_find_object(NULL,oldPath,0,0,NULL,NULL,NULL); + newobj = yaffsfs_find_object(NULL,newPath,0,0,NULL,NULL,NULL); + + /* If the object being renamed is a directory and the + * path ended with a "/" then the olddir == obj. + * We pass through NULL for the old name to tell the lower layers + * to use olddir as the object. + */ + + if(olddir == obj) + oldname = NULL; + + if((!olddir && notOldDir) || (!newdir && notNewDir)) { + yaffsfs_set_error(-ENOTDIR); + rename_allowed = 0; + } else if(oldLoop || newLoop) { + yaffsfs_set_error(-ELOOP); + rename_allowed = 0; + } else if (olddir && oldname && strncmp(oldname, _Y("."),2) == 0){ + yaffsfs_set_error(-EINVAL); + rename_allowed = 0; + }else if(!olddir || !newdir || !obj) { + yaffsfs_set_error(-ENOENT); + rename_allowed = 0; + } else if(obj->my_dev->read_only){ + yaffsfs_set_error(-EROFS); + rename_allowed = 0; + } else if(yaffs_is_non_empty_dir(newobj)){ + yaffsfs_set_error(-ENOTEMPTY); + rename_allowed = 0; + } else if(olddir->my_dev != newdir->my_dev) { + /* Rename must be on same device */ + yaffsfs_set_error(-EXDEV); + rename_allowed = 0; + } else if(obj && obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { + /* + * It is a directory, check that it is not being renamed to + * being its own decendent. + * Do this by tracing from the new directory back to the root, + * checking for obj + */ + + struct yaffs_obj *xx = newdir; + + while( rename_allowed && xx){ + if(xx == obj) + rename_allowed = 0; + xx = xx->parent; + } + if(!rename_allowed) + yaffsfs_set_error(-EINVAL); + } + + if(rename_allowed) + result = yaffs_rename_obj(olddir,oldname,newdir,newname); + + yaffsfs_Unlock(); + + if(alt_newpath) + kfree(alt_newpath); + + return (result == YAFFS_FAIL) ? -1 : 0; +} + +static int yaffsfs_DoStat(struct yaffs_obj *obj,struct yaffs_stat *buf) +{ + int retVal = -ENOENT; + + obj = yaffs_get_equivalent_obj(obj); + + if(obj && buf){ + buf->st_dev = (int)obj->my_dev->os_context; + buf->st_ino = obj->obj_id; + buf->st_mode = obj->yst_mode & ~S_IFMT; /* clear out file type bits */ + + if(obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) + buf->st_mode |= S_IFDIR; + else if(obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) + buf->st_mode |= S_IFLNK; + else if(obj->variant_type == YAFFS_OBJECT_TYPE_FILE) + buf->st_mode |= S_IFREG; + + buf->st_nlink = yaffs_get_obj_link_count(obj); + buf->st_uid = 0; + buf->st_gid = 0;; + buf->st_rdev = obj->yst_rdev; + buf->st_size = yaffs_get_obj_length(obj); + buf->st_blksize = obj->my_dev->data_bytes_per_chunk; + buf->st_blocks = (buf->st_size + buf->st_blksize -1)/buf->st_blksize; +#if CONFIG_YAFFS_WINCE + buf->yst_wince_atime[0] = obj->win_atime[0]; + buf->yst_wince_atime[1] = obj->win_atime[1]; + buf->yst_wince_ctime[0] = obj->win_ctime[0]; + buf->yst_wince_ctime[1] = obj->win_ctime[1]; + buf->yst_wince_mtime[0] = obj->win_mtime[0]; + buf->yst_wince_mtime[1] = obj->win_mtime[1]; +#else + buf->yst_atime = obj->yst_atime; + buf->yst_ctime = obj->yst_ctime; + buf->yst_mtime = obj->yst_mtime; +#endif + retVal = 0; + } + return retVal; +} + +static int yaffsfs_DoStatOrLStat(const char *path, struct yaffs_stat *buf,int doLStat) +{ + struct yaffs_obj *obj; + struct yaffs_obj *dir = NULL; + + int retVal = -1; + int notDir = 0; + int loop = 0; + + yaffsfs_Lock(); + obj = yaffsfs_find_object(NULL,path,0,1,&dir,¬Dir,&loop); + + if(!doLStat && obj) + { + obj = yaffsfs_follow_link(obj,0,&loop); + } + + if(obj) + { + retVal = yaffsfs_DoStat(obj,buf); + } + else + { + // todo error not found + yaffsfs_set_error((retVal = -ENOENT)); + } + + yaffsfs_Unlock(); + + return retVal; + +} + +static void yaffsfs_ReleaseInode(yaffsfs_Inode *in) +{ + struct yaffs_obj *obj; + + obj = in->iObj; + + if(obj->unlinked) + yaffs_del_obj(obj); + + obj->my_inode = NULL; + in->iObj = NULL; + +} + +static void yaffsfs_PutInode(int inodeId) +{ + if(inodeId >= 0 && inodeId < YAFFSFS_N_HANDLES){ + yaffsfs_Inode *in = & yaffsfs_inode[inodeId]; + in->count--; + if(in->count <= 0){ + yaffsfs_ReleaseInode(in); + in->count = 0; + } + } +} + +static int yaffsfs_NewHandle(yaffsfs_Handle **hptr) +{ + int i; + yaffsfs_Handle *h; + + for(i = 0; i < YAFFSFS_N_HANDLES; i++){ + h = &yaffsfs_handle[i]; + if(h->useCount < 1){ + memset(h,0,sizeof(yaffsfs_Handle)); + h->fdId=-1; + h->useCount=1; + if(hptr) + *hptr = h; + return i; + } + } + return -1; +} + +static int yaffsfs_NewHandleAndFileDes(void) +{ + int i; + yaffsfs_FileDes *fd; + yaffsfs_Handle *h = NULL; + int handle = yaffsfs_NewHandle(&h); + + if(handle < 0) + return -1; + + for(i = 0; i < YAFFSFS_N_HANDLES; i++){ + fd = &yaffsfs_fd[i]; + if(fd->handleCount < 1){ + memset(fd,0,sizeof(yaffsfs_FileDes)); + fd->inodeId=-1; + fd->handleCount=1; + h->fdId = i; + return handle; + } + } + + /* Dump the handle because we could not get a fd */ + h->useCount = 0; + return -1; +} + +/* + * yaffs_get_handle + * Increase use of handle when reading/writing a file + * Also gets the file descriptor. + */ + +static int yaffsfs_GetHandle(int handle) +{ + yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); + + if(h && h->useCount > 0){ + h->useCount++; + return 0; + } + return -1; +} + +/* + * yaffs_put_handle + * Let go of a handle when closing a file or aborting an open or + * ending a read or write. + */ + +static int yaffsfs_PutFileDes(int fdId) +{ + yaffsfs_FileDes *fd; + + if(fdId >= 0 && fdId < YAFFSFS_N_HANDLES){ + fd = &yaffsfs_fd[fdId]; + fd->handleCount--; + if(fd->handleCount < 1){ + if(fd->inodeId >= 0){ + yaffsfs_PutInode(fd->inodeId); + fd->inodeId = -1; + } + } + } + return 0; +} + +static int yaffsfs_PutHandle(int handle) +{ + yaffsfs_Handle *h = yaffsfs_HandleToPointer(handle); + + if(h && h->useCount > 0){ + h->useCount--; + if(h->useCount < 1){ + yaffsfs_PutFileDes(h->fdId); + h->fdId = -1; + } + } + + return 0; +} + + +int yaffsfs_open_sharing(const YCHAR *path, int oflag, int mode, int sharing) +{ + struct yaffs_obj *obj = NULL; + struct yaffs_obj *dir = NULL; + YCHAR *name; + int handle = -1; + yaffsfs_FileDes *fd = NULL; + int openDenied = 0; + int symDepth = 0; + int errorReported = 0; + int rwflags = oflag & ( O_RDWR | O_RDONLY | O_WRONLY); + u8 shareRead = (sharing & YAFFS_SHARE_READ) ? 1 : 0; + u8 shareWrite = (sharing & YAFFS_SHARE_WRITE) ? 1 : 0; + u8 sharedReadAllowed; + u8 sharedWriteAllowed; + u8 alreadyReading; + u8 alreadyWriting; + u8 readRequested; + u8 writeRequested; + int notDir = 0; + int loop = 0; + + if(!path) { + yaffsfs_set_error(-EFAULT); + return -1; + } + + if(yaffsfs_CheckPath(path) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return -1; + } + + /* O_EXCL only has meaning if O_CREAT is specified */ + if(!(oflag & O_CREAT)) + oflag &= ~(O_EXCL); + + /* O_TRUNC has no meaning if (O_CREAT | O_EXCL) is specified */ + if( (oflag & O_CREAT) & (oflag & O_EXCL)) + oflag &= ~(O_TRUNC); + + /* Todo: Are there any more flag combos to sanitise ? */ + + /* Figure out if reading or writing is requested */ + + readRequested = (rwflags == O_RDWR || rwflags == O_RDONLY) ? 1 : 0; + writeRequested = (rwflags == O_RDWR || rwflags == O_WRONLY) ? 1 : 0; + + yaffsfs_Lock(); + + handle = yaffsfs_NewHandleAndFileDes(); + + if(handle < 0){ + yaffsfs_set_error(-ENFILE); + errorReported = 1; + } else { + + fd = yaffsfs_HandleToFileDes(handle); + + /* try to find the exisiting object */ + obj = yaffsfs_find_object(NULL,path,0,1,NULL,NULL,NULL); + + obj = yaffsfs_follow_link(obj,symDepth++,&loop); + + if(obj && + obj->variant_type != YAFFS_OBJECT_TYPE_FILE && + obj->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) + obj = NULL; + + + if(obj){ + + /* The file already exists or it might be a directory */ + + /* If it is a directory then we can't open it as a file */ + if(obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY){ + openDenied = 1; + yaffsfs_set_error(-EISDIR); + errorReported = 1; + } + + /* Open should fail if O_CREAT and O_EXCL are specified since + * the file exists + */ + if(!errorReported && (oflag & O_EXCL) && (oflag & O_CREAT)){ + openDenied = 1; + yaffsfs_set_error(-EEXIST); + errorReported = 1; + } + + /* Check file permissions */ + if( readRequested && !(obj->yst_mode & S_IREAD)) + openDenied = 1; + + if( writeRequested && !(obj->yst_mode & S_IWRITE)) + openDenied = 1; + + if( !errorReported && writeRequested && + obj->my_dev->read_only){ + openDenied = 1; + yaffsfs_set_error(-EROFS); + errorReported = 1; + } + + if(openDenied && !errorReported ) { + /* Error if the file exists but permissions are refused. */ + yaffsfs_set_error(-EACCES); + errorReported = 1; + } + + /* Check sharing of an existing object. */ + if(!openDenied){ + yaffsfs_FileDes *fdx; + int i; + + sharedReadAllowed = 1; + sharedWriteAllowed = 1; + alreadyReading = 0; + alreadyWriting = 0; + for( i = 0; i < YAFFSFS_N_HANDLES; i++){ + fdx = &yaffsfs_fd[i]; + if(fdx->handleCount > 0 && + fdx->inodeId >= 0 && + yaffsfs_inode[fdx->inodeId].iObj == obj){ + if(!fdx->shareRead) + sharedReadAllowed = 0; + if(!fdx->shareWrite) + sharedWriteAllowed = 0; + if(fdx->reading) + alreadyReading = 1; + if(fdx->writing) + alreadyWriting = 1; + } + } + + + + if((!sharedReadAllowed && readRequested)|| + (!shareRead && alreadyReading) || + (!sharedWriteAllowed && writeRequested) || + (!shareWrite && alreadyWriting)){ + openDenied = 1; + yaffsfs_set_error(-EBUSY); + errorReported=1; + } + } + + } + + /* If we could not open an existing object, then let's see if + * the directory exists. If not, error. + */ + if(!obj && !errorReported){ + dir = yaffsfs_find_directory(NULL,path,&name,0,¬Dir,&loop); + if(!dir && notDir){ + yaffsfs_set_error(-ENOTDIR); + errorReported = 1; + } else if(loop){ + yaffsfs_set_error(-ELOOP); + errorReported = 1; + } else if(!dir){ + yaffsfs_set_error(-ENOENT); + errorReported = 1; + } + } + + if(!obj && dir && !errorReported && (oflag & O_CREAT)) { + /* Let's see if we can create this file if it does not exist. */ + if(dir->my_dev->read_only){ + yaffsfs_set_error(-EROFS); + errorReported = 1; + } else + obj = yaffs_create_file(dir,name,mode,0,0); + + if(!obj && !errorReported){ + yaffsfs_set_error(-ENOSPC); + errorReported = 1; + } + } + + if(!obj && dir && !errorReported && !(oflag & O_CREAT)) { + /* Error if the file does not exist and CREAT is not set. */ + yaffsfs_set_error(-ENOENT); + errorReported = 1; + } + + if(obj && !openDenied) { + int inodeId = yaffsfs_GetInodeIdForObject(obj); + + if(inodeId<0) { + /* + * Todo: Fix any problem if inodes run out, though that + * can't happen if the number of inode items >= number of handles. + */ + } + + fd->inodeId = inodeId; + fd->reading = readRequested; + fd->writing = writeRequested; + fd->append = (oflag & O_APPEND) ? 1 : 0; + fd->position = 0; + fd->shareRead = shareRead; + fd->shareWrite = shareWrite; + + /* Hook inode to object */ + obj->my_inode = (void*) &yaffsfs_inode[inodeId]; + + if((oflag & O_TRUNC) && fd->writing) + yaffs_resize_file(obj,0); + } else { + yaffsfs_PutHandle(handle); + if(!errorReported) + yaffsfs_set_error(0); /* Problem */ + handle = -1; + } + } + + yaffsfs_Unlock(); + + return handle; +} + +int yaffs_open(const YCHAR *path, int oflag, int mode) +{ + return yaffsfs_open_sharing(path, oflag, mode, + YAFFS_SHARE_READ | YAFFS_SHARE_WRITE); +} + +int yaffs_stat(const char *path, struct yaffs_stat *buf) +{ + return yaffsfs_DoStatOrLStat(path,buf,0); +} + +int yaffs_close(int handle) +{ + yaffsfs_Handle *h = NULL; + struct yaffs_obj *obj = NULL; + int retVal = -1; + + yaffsfs_Lock(); + + h = yaffsfs_HandleToPointer(handle); + obj = yaffsfs_HandleToObject(handle); + + if(!h || !obj) + yaffsfs_set_error(-EBADF); + else { + /* clean up */ + yaffs_flush_file(obj,1,0); + yaffsfs_PutHandle(handle); + retVal = 0; + } + + yaffsfs_Unlock(); + + return retVal; +} + +int yaffsfs_do_write(int handle, const void *buf, unsigned int nbyte) +{ + yaffsfs_FileDes *fd = NULL; + struct yaffs_obj *obj = NULL; + int pos = 0; + int nWritten = -1; + int writeThrough = 0; + + yaffsfs_Lock(); + fd = yaffsfs_HandleToFileDes(handle); + obj = yaffsfs_HandleToObject(handle); + + if(!fd || !obj) + { + // bad handle + yaffsfs_set_error(-EBADF); + } + else if( fd && obj && !fd->writing) + { + yaffsfs_set_error(-EPERM); + } + else if( fd && obj) + { + if(fd->append) + { + pos = yaffs_get_obj_length(obj); + } + else + { + pos = fd->position; + } + + nWritten = yaffs_wr_file(obj,buf,pos,nbyte,writeThrough); + + if(nWritten >= 0) + { + fd->position = pos + nWritten; + + /* If amount written not same as nbytes then + * filesystem must have run out of space */ + if (nWritten != nbyte) + yaffsfs_set_error(-ENOSPC); + } + else + { + //todo error + } + + } + + yaffsfs_Unlock(); + + + return (nWritten >= 0) ? nWritten : -1; + +} + +int yaffs_write(int handle, void *buf, unsigned int nbyte) +{ + return yaffsfs_do_write(handle, buf, nbyte); +} + +int yaffsfs_do_read(int handle, void *vbuf, unsigned int nbyte, int isPread, int offset) +{ + yaffsfs_FileDes *fd = NULL; + struct yaffs_obj *obj = NULL; + int pos = 0; + int startPos = 0; + int endPos = 0; + int nRead = 0; + int nToRead = 0; + int totalRead = 0; + unsigned int maxRead; + u8 *buf = (u8 *)vbuf; + + if(!vbuf){ + yaffsfs_set_error(-EFAULT); + return -1; + } + + yaffsfs_Lock(); + fd = yaffsfs_HandleToFileDes(handle); + obj = yaffsfs_HandleToObject(handle); + + if(!fd || !obj){ + /* bad handle */ + yaffsfs_set_error(-EBADF); + totalRead = -1; + } else if(!fd->reading){ + /* Not a reading handle */ + yaffsfs_set_error(-EINVAL); + totalRead = -1; + } else if(nbyte > YAFFS_MAX_FILE_SIZE){ + yaffsfs_set_error(-EINVAL); + totalRead = -1; + } else { + if(isPread) + startPos = offset; + else + startPos = fd->position; + + pos = startPos; + + if(yaffs_get_obj_length(obj) > pos) + maxRead = yaffs_get_obj_length(obj) - pos; + else + maxRead = 0; + + if(nbyte > maxRead) + nbyte = maxRead; + + + yaffsfs_GetHandle(handle); + + endPos = pos + nbyte; + + if(pos < 0 || pos > YAFFS_MAX_FILE_SIZE || + nbyte > YAFFS_MAX_FILE_SIZE || + endPos < 0 || endPos > YAFFS_MAX_FILE_SIZE){ + totalRead = -1; + nbyte = 0; + } + + while(nbyte > 0) { + nToRead = YAFFSFS_RW_SIZE - (pos & (YAFFSFS_RW_SIZE -1)); + if(nToRead > nbyte) + nToRead = nbyte; + + /* Tricky bit... + * Need to reverify object in case the device was + * unmounted in another thread. + */ + obj = yaffsfs_HandleToObject(handle); + if(!obj) + nRead = 0; + else + nRead = yaffs_file_rd(obj,buf,pos,nToRead); + + if(nRead > 0){ + totalRead += nRead; + pos += nRead; + buf += nRead; + } + + if(nRead == nToRead) + nbyte-=nRead; + else + nbyte = 0; /* no more to read */ + + + if(nbyte > 0){ + yaffsfs_Unlock(); + yaffsfs_Lock(); + } + + } + + yaffsfs_PutHandle(handle); + + if(!isPread) { + if(totalRead >= 0) + fd->position = startPos + totalRead; + else + yaffsfs_set_error(-EINVAL); + } + + } + + yaffsfs_Unlock(); + + return (totalRead >= 0) ? totalRead : -1; + +} + +int yaffs_read(int handle, void *buf, unsigned int nbyte) +{ + return yaffsfs_do_read(handle, buf, nbyte, 0, 0); +} + + +int yaffs_mkdir(const YCHAR *path, mode_t mode) +{ + struct yaffs_obj *parent = NULL; + struct yaffs_obj *dir = NULL; + YCHAR *name; + YCHAR *alt_path = NULL; + int retVal= -1; + int notDir = 0; + int loop = 0; + + if(!path){ + yaffsfs_set_error(-EFAULT); + return -1; + } + + if(yaffsfs_CheckPath(path) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return -1; + } + + if(yaffsfs_alt_dir_path(path, &alt_path) < 0){ + yaffsfs_set_error(-ENOMEM); + return -1; + } + if(alt_path) + path = alt_path; + + yaffsfs_Lock(); + parent = yaffsfs_find_directory(NULL,path,&name,0,¬Dir,&loop); + if(!parent && notDir) + yaffsfs_set_error(-ENOTDIR); + else if(loop) + yaffsfs_set_error(-ELOOP); + else if(!parent) + yaffsfs_set_error(-ENOENT); + else if(strnlen(name,5) == 0){ + /* Trying to make the root itself */ + yaffsfs_set_error(-EEXIST); + } else if(parent->my_dev->read_only) + yaffsfs_set_error(-EROFS); + else { + dir = yaffs_create_dir(parent,name,mode,0,0); + if(dir) + retVal = 0; + else if (yaffs_find_by_name(parent,name)) + yaffsfs_set_error(-EEXIST); /* the name already exists */ + else + yaffsfs_set_error(-ENOSPC); /* just assume no space */ + } + + yaffsfs_Unlock(); + + if(alt_path) + kfree(alt_path); + + return retVal; +} + +int yaffs_rmdir(const YCHAR *path) +{ + int result; + YCHAR *alt_path; + + if(!path){ + yaffsfs_set_error(-EFAULT); + return -1; + } + + if(yaffsfs_CheckPath(path) < 0){ + yaffsfs_set_error(-ENAMETOOLONG); + return -1; + } + + if(yaffsfs_alt_dir_path(path, &alt_path) < 0){ + yaffsfs_set_error(-ENOMEM); + return -1; + } + if(alt_path) + path = alt_path; + result = yaffsfs_do_unlink(path,1); + if(alt_path) + kfree(alt_path); + return result; +} + +int yaffs_DumpDevStruct(const char *path) +{ + char *rest; + + struct yaffs_obj *obj = yaffsfs_FindRoot(path,&rest); + + if(obj) + { + struct yaffs_dev *dev = obj->my_dev; + + printf("\n" + "nStartBlock.......... %d\n" + "nEndBlock............ %d\n" + "nReserveredBlocks.... %d\n" + "nPageWrites.......... %d\n" + "nPageReads........... %d\n" + "nBlockErasures....... %d\n" + "nGCCopies............ %d\n" + "garbageCollections... %d\n" + "passiveGarbageColl'ns %d\n", + dev->param.start_block, + dev->param.end_block, + dev->param.n_reserved_blocks, + dev->n_page_writes, + dev->n_page_reads, + dev->n_erasures, + dev->n_gc_copies, + dev->all_gcs, + dev->passive_gc_count + ); + + } + return 0; +} + +void yaffsfs_initialise(void) +{ + yaffsfs_InitHandles(); +} diff --git a/fs/yaffs2-new/yaffsfs.h b/fs/yaffs2-new/yaffsfs.h new file mode 100644 index 00000000000..1e7040ea90a --- /dev/null +++ b/fs/yaffs2-new/yaffsfs.h @@ -0,0 +1,64 @@ + +extern int cmd_yaffs_mount(const char *path); +extern int cmd_yaffs_unmount(const char *path); +extern int cmd_yaffs_ls(const char *mountpt, int longlist); +extern int cmd_yaffs_df(const char *path, loff_t *size); +extern int cmd_yaffs_mwrite_file(char *fn, char *addr, int size); +extern int cmd_yaffs_mread_file(char *fn, char *addr, long *size); +extern int cmd_yaffs_mkdir(const char *dir); +extern int cmd_yaffs_rmdir(const char *dir); +extern int cmd_yaffs_rm(const char *path); +extern int cmd_yaffs_mv(const char *oldPath, const char *newPath); + +extern int yaffsfs_mount(const char *path); +extern int yaffsfs_unmount(const char *path); + + +#define YAFFS_SHARE_READ 1 +#define YAFFS_SHARE_WRITE 2 + +#define YAFFS_MAX_FILE_SIZE (0x7ffffff) + +struct yaffs_dirent{ + long d_ino; /* inode number */ + off_t d_off; /* offset to this dirent */ + unsigned short d_reclen; /* length of this d_name */ + char d_name [NAME_MAX+1]; /* file name (null-terminated) */ + unsigned d_dont_use; /* debug pointer, not for public consumption */ +}; + +typedef struct yaffs_dirent yaffs_dirent; + + +typedef struct __opaque yaffs_DIR; + +struct yaffs_stat { + int st_dev; /* device */ + int st_ino; /* inode */ + mode_t st_mode; /* protection */ + int st_nlink; /* number of hard links */ + int st_uid; /* user ID of owner */ + int st_gid; /* group ID of owner */ + unsigned st_rdev; /* device type (if inode device) */ + off_t st_size; /* total size, in bytes */ + unsigned long st_blksize; /* blocksize for filesystem I/O */ + unsigned long st_blocks; /* number of blocks allocated */ + unsigned long yst_atime; /* time of last access */ + unsigned long yst_mtime; /* time of last modification */ + unsigned long yst_ctime; /* time of last change */ +}; + +extern loff_t yaffs_freespace(const YCHAR *path); +extern int yaffs_unlink(const YCHAR *path); +extern int yaffs_open(const char *path, int oflag, int mode) ; +extern int yaffs_read(int fd, void *buf, unsigned int nbyte) ; +extern int yaffs_write(int fd, void *buf, unsigned int nbyte) ; +extern int yaffs_close(int fd); +extern yaffs_DIR *yaffs_opendir(const char *dirname); +extern struct yaffs_dirent *yaffs_readdir(yaffs_DIR *dirp); +extern int yaffs_stat(const char *path, struct yaffs_stat *buf) ; +extern int yaffs_mkdir(const char *path, mode_t mode); +extern int yaffs_rmdir(const char *path); +extern int yaffs_rename(const char *new_path, const char *old_path); + +extern void yaffsfs_initialise(void); diff --git a/fs/yaffs2-new/yaffsfs_errno.c b/fs/yaffs2-new/yaffsfs_errno.c new file mode 100644 index 00000000000..380dfd60cc6 --- /dev/null +++ b/fs/yaffs2-new/yaffsfs_errno.c @@ -0,0 +1,119 @@ +/* + * (C) Copyright 2011 Logic Product Development, Inc. + * Author : + * Peter Barada <peter.barada@logicpd.com> + * + * YAFFS Errno handling + * + * 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> +#include "yaffsfs_errno.h" + +int yaffsfs_errno; +char *yaffsfs_errstr; +void yaffsfs_set_error(int x) +{ + yaffsfs_errno = x; + switch(x) { + case 0: + yaffsfs_errstr = "Success"; + break; + case -EPERM: + yaffsfs_errstr = "Operation not permitted"; + break; + case -ENOENT: + yaffsfs_errstr = "No such file or directory"; + break; + case -EBADF: + yaffsfs_errstr = "Bad file number"; + break; + case -ENOMEM: + yaffsfs_errstr = "Out of memory"; + break; + case -EACCES: + yaffsfs_errstr = "Permission denied"; + break; + case -EFAULT: + yaffsfs_errstr = "Bad address"; + break; + case -EBUSY: + yaffsfs_errstr = "Device or resource busy"; + break; + case -EEXIST: + yaffsfs_errstr = "File exists"; + break; + case -ENODEV: + yaffsfs_errstr = "No such device"; + break; + case -ENOTDIR: + yaffsfs_errstr = "Not a directory"; + break; + case -EISDIR: + yaffsfs_errstr = "Is a directory"; + break; + case -EINVAL: + yaffsfs_errstr = "Invalid argument"; + break; + case -ENFILE: + yaffsfs_errstr = "File table overflow"; + break; + case -ENOSPC: + yaffsfs_errstr = "No space left on device"; + break; + case -EROFS: + yaffsfs_errstr = "Read-only file system"; + break; + case -ERANGE: + yaffsfs_errstr = "Math result not representable"; + break; + case -ENAMETOOLONG: + yaffsfs_errstr = "File name too long"; + break; + case -ENOTEMPTY: + yaffsfs_errstr = "Directory not empty"; + break; + case -ELOOP: + yaffsfs_errstr = "Too many symbolic links encountered"; + break; + case -ENODATA: + yaffsfs_errstr = "No data available"; + break; + case -EBADMSG: + yaffsfs_errstr = "Not a data message"; + break; + case -EUCLEAN: + yaffsfs_errstr = "Structure needs cleaning"; + break; + default: + printf("%s: Huh? yaffs_errno called from %p with %d\n", + __FUNCTION__, __builtin_return_address(0), x); + yaffsfs_errstr = "Unknown!"; + break; + } +} + +int yaffsfs_get_error(char **errstr) +{ + if (errstr) + *errstr = yaffsfs_errstr; + return yaffsfs_errno; +} + diff --git a/fs/yaffs2-new/yaffsfs_errno.h b/fs/yaffs2-new/yaffsfs_errno.h new file mode 100644 index 00000000000..cad7a0d0b71 --- /dev/null +++ b/fs/yaffs2-new/yaffsfs_errno.h @@ -0,0 +1,120 @@ +/* + * (C) Copyright 2011 Logic Product Development, Inc. + * Author : + * Peter Barada <peter.barada@logicpd.com> + * + * YAFFS Errno handling + * + * 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 + */ + +extern void yaffsfs_set_error(int x); +extern int yaffsfs_get_error(char **errstr); + +#ifndef EPERM +#define EPERM 1 +#endif + +#ifndef ENOENT +#define ENOENT 2 +#endif + +#ifndef EBADF +#define EBADF 9 +#endif + +#ifndef ENOMEM +#define ENOMEM 12 +#endif + +#ifndef EACCES +#define EACCES 13 +#endif + +#ifndef EFAULT +#define EFAULT 14 +#endif + +#ifndef EBUSY +#define EBUSY 16 +#endif + +#ifndef EEXIST +#define EEXIST 17 +#endif + +#ifndef EXDEV +#define EXDEV 18 +#endif + +#ifndef ENODEV +#define ENODEV 19 +#endif + +#ifndef ENOTDIR +#define ENOTDIR 20 +#endif + +#ifndef EISDIR +#define EISDIR 21 +#endif + +#ifndef EINVAL +#define EINVAL 22 +#endif + +#ifndef ENFILE +#define ENFILE 23 +#endif + +#ifndef ENOSPC +#define ENOSPC 28 +#endif + +#ifndef EROFS +#define EROFS 30 +#endif + +#ifndef ERANGE +#define ERANGE 34 +#endif + +#ifndef ENAMETOOLONG +#define ENAMETOOLONG 36 +#endif + +#ifndef ENOTEMPTY +#define ENOTEMPTY 39 +#endif + +#ifndef ELOOP +#define ELOOP 40 +#endif + +#ifndef ENODATA +#define ENODATA 61 +#endif + +#ifndef EBADMSG +#define EBADMSG 74 +#endif + +#ifndef EUCLEAN +#define EUCLEAN 117 +#endif diff --git a/fs/yaffs2-new/ydirectenv.h b/fs/yaffs2-new/ydirectenv.h new file mode 100644 index 00000000000..e9a0174f666 --- /dev/null +++ b/fs/yaffs2-new/ydirectenv.h @@ -0,0 +1,97 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * ydirectenv.h: Environment wrappers for YAFFS direct. + */ + +#ifndef __YDIRECTENV_H__ +#define __YDIRECTENV_H__ + +/* Direct interface */ + +#include "devextras.h" + +/* XXX U-BOOT XXX */ +#if 0 +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "assert.h" +#endif +#if 1 +#include "yaffs_malloc.h" +#endif + +/* XXX U-BOOT XXX */ +#if 0 +#define YBUG() assert(1) +#endif + +#define YCHAR char +#define _Y(x) x +#if 0 +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strncmp(a,b,c) strncmp(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) +#endif + +#ifdef NO_Y_INLINE +#define Y_INLINE +#else +#define Y_INLINE inline +#endif + +#define cond_resched() do {} while(0) + +#define abs(x) ((x) < 0 ? -(x) : (x)) + +#define YYIELD() do {} while(0) + +void yaffs_qsort(void *aa, size_t n, size_t es, + int (*cmp)(const void *, const void *)); + +#define sort(base, n, sz, cmp_fn, swp) yaffs_qsort(base, n, sz, cmp_fn) + +#if 0 +//#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) +//#define YALERT(s) YINFO(s) + + +#define TENDSTR "\n" +#define TSTR(x) x +#define TOUT(p) printf p +#endif + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" +//#define YPRINTF(x) printf x + +#include "yaffscfg.h" + +#define Y_CURRENT_TIME yaffsfs_CurrentTime() +#define Y_TIME_CONVERT(x) x + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + + +#endif diff --git a/fs/yaffs2-new/yportenv.h b/fs/yaffs2-new/yportenv.h new file mode 100644 index 00000000000..0d8215049cd --- /dev/null +++ b/fs/yaffs2-new/yportenv.h @@ -0,0 +1,239 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YPORTENV_H__ +#define __YPORTENV_H__ + +/* XXX U-BOOT XXX */ +#ifndef CONFIG_YAFFS_DIRECT +#define CONFIG_YAFFS_DIRECT +#endif + +#if defined CONFIG_YAFFS_WINCE + +#include "ywinceenv.h" + +/* XXX U-BOOT XXX */ +#elif 0 /* defined __KERNEL__ */ + +#include "moduleconfig.h" + +/* Linux kernel */ +#include <linux/version.h> +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +#include <linux/config.h> +#endif +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#define YCHAR char +#define YUCHAR unsigned char +#if 0 +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strncmp(a,b,c) strncmp(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) +#endif + +#define Y_INLINE inline + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" + +/* #define YPRINTF(x) printk x */ +#define YMALLOC(x) kmalloc(x,GFP_KERNEL) +#define YFREE(x) kfree(x) +#define YMALLOC_ALT(x) vmalloc(x) +#define YFREE_ALT(x) vfree(x) +#define YMALLOC_DMA(x) YMALLOC(x) + +// KR - added for use in scan so processes aren't blocked indefinitely. +#define YYIELD() schedule() + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#define Y_CURRENT_TIME CURRENT_TIME.tv_sec +#define Y_TIME_CONVERT(x) (x).tv_sec +#else +#define Y_CURRENT_TIME CURRENT_TIME +#define Y_TIME_CONVERT(x) (x) +#endif + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#define TENDSTR "\n" +#define TSTR(x) KERN_WARNING x +#define TOUT(p) printk p + +#define yaffs_trace(mask, fmt, args...) \ + do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \ + printk(KERN_WARNING "yaffs: " fmt, ## args); \ + } while (0) + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + +#elif defined CONFIG_YAFFS_DIRECT + +/* Direct interface */ +#include "ydirectenv.h" + +#elif defined CONFIG_YAFFS_UTIL + +/* Stuff for YAFFS utilities */ + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#include "devextras.h" + +#define YMALLOC(x) malloc(x) +#define YFREE(x) free(x) +#define YMALLOC_ALT(x) malloc(x) +#define YFREE_ALT(x) free(x) + +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) + +#define Y_INLINE inline + +/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */ +/* #define YALERT(s) YINFO(s) */ + +#define TENDSTR "\n" +#define TSTR(x) x +#define TOUT(p) printf p + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" +/* #define YPRINTF(x) printf x */ + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#else +/* Should have specified a configuration type */ +#error Unknown configuration + +#endif + +#ifndef NAME_MAX +#define NAME_MAX 256 +#endif + +/* Mode flags */ + +#ifndef XATTR_CREATE +#define XATTR_CREATE 1 +#endif + +#ifndef XATTR_REPLACE +#define XATTR_REPLACE 2 +#endif + +#ifndef S_IFMT +#define S_IFMT 0170000 +#endif + +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif + +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif + +#ifndef S_IFREG +#define S_IFREG 0100000 +#endif + +#ifndef S_IREAD +#define S_IREAD 0000400 +#endif + +#ifndef S_IWRITE +#define S_IWRITE 0000200 +#endif + +#ifndef O_RDONLY +#define O_RDONLY 00 +#endif + +#ifndef O_WRONLY +#define O_WRONLY 01 +#endif + +#ifndef O_RDWR +#define O_RDWR 02 +#endif + +#ifndef O_CREAT +#define O_CREAT 0100 +#endif + +#ifndef O_EXCL +#define O_EXCL 0200 +#endif + +#ifndef O_TRUNC +#define O_TRUNC 01000 +#endif + +#ifndef O_APPEND +#define O_APPEND 02000 +#endif + +/* Errno entries */ + +#include "yaffsfs_errno.h" + +/* see yaffs_fs.c */ +extern unsigned int yaffs_traceMask; +extern unsigned int yaffs_wr_attempts; + +#define CONFIG_YAFFS_DEBUG + +#ifdef CONFIG_YAFFS_DEBUG +#define yaffs_trace(msk, fmt, ...) do { \ + if (yaffs_trace_mask & (msk)) \ + printf(fmt "\n", ##__VA_ARGS__); \ + } while (0) +#else +#define yaffs_trace(msk, fmt, ...) do { \ + } while (0) +#endif + +#endif diff --git a/fs/yaffs2/devextras.h b/fs/yaffs2/devextras.h index f6e53610d6b..b0147b85a43 100644 --- a/fs/yaffs2/devextras.h +++ b/fs/yaffs2/devextras.h @@ -41,169 +41,7 @@ typedef unsigned __u32; #endif #include <asm/types.h> - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -#define prefetch(x) 1 - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static __inline__ void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static __inline__ void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static __inline__ void list_add_tail(struct list_head *new, - struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static __inline__ void __list_del(struct list_head *prev, - struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is - * in an undefined state. - */ -static __inline__ void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static __inline__ void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static __inline__ int list_empty(struct list_head *head) -{ - return head->next == head; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static __inline__ void list_splice(struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - - if (first != list) { - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next, prefetch(pos->next); pos != (head); \ - pos = pos->next, prefetch(pos->next)) - -/** - * list_for_each_safe - iterate over a list safe against removal - * of list entry - * @pos: the &struct list_head to use as a loop counter. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) +#include <linux/list.h> /* * File types diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index b368844296a..29d70aa49fc 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -78,11 +78,15 @@ static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, int isShrink, int shadows); static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); static int yaffs_CheckStructures(void); +#ifdef YAFFS_USE_DELETEWORKER static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, int chunkOffset, int *limit); +#endif static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); +#if 0 static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); +#endif static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo); static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, @@ -396,10 +400,12 @@ static int yaffs_SkipVerification(yaffs_Device *dev) return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); } +#ifdef YAFFS_USE_SKIPFULLVERIFICATION static int yaffs_SkipFullVerification(yaffs_Device *dev) { return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); } +#endif static int yaffs_SkipNANDVerification(yaffs_Device *dev) { @@ -596,7 +602,7 @@ static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, } - +#ifdef YAFFS_USE_VERIFYTNODEWORKER static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, __u32 level, int chunkOffset) { @@ -642,7 +648,7 @@ static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, return ok; } - +#endif static void yaffs_VerifyFile(yaffs_Object *obj) { @@ -1546,7 +1552,7 @@ static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, return -1; } - +#ifdef YAFFS_USE_DELETEWORKER /* DeleteWorker scans backwards through the tnode tree and deletes all the * chunks and tnodes in the file * Returns 1 if the tree was deleted. @@ -1643,6 +1649,7 @@ static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, return 1; } +#endif static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) { @@ -2140,7 +2147,7 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, { yaffs_Object *theObject; - yaffs_Tnode *tn; + yaffs_Tnode *tn = NULL; if (number < 0) { number = yaffs_CreateNewObjectNumber(dev); @@ -2255,7 +2262,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, const YCHAR * aliasString, __u32 rdev) { yaffs_Object *in; - YCHAR *str; + YCHAR *str = NULL; yaffs_Device *dev = parent->myDev; @@ -4605,8 +4612,8 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, int nBytes) { - int chunk; - int start; + __u32 chunk; + __u32 start; int nToCopy; int n = nBytes; int nDone = 0; @@ -4725,8 +4732,8 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, int nBytes, int writeThrough) { - int chunk; - int start; + __u32 chunk; + __u32 start; int nToCopy; int n = nBytes; int nDone = 0; @@ -4960,8 +4967,8 @@ int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) { int oldFileSize = in->variant.fileVariant.fileSize; - int newSizeOfPartialChunk; - int newFullChunks; + __u32 newSizeOfPartialChunk; + __u32 newFullChunks; yaffs_Device *dev = in->myDev; @@ -5391,7 +5398,7 @@ static int yaffs_Scan(yaffs_Device * dev) yaffs_BlockState state; yaffs_Object *hardList = NULL; yaffs_BlockInfo *bi; - int sequenceNumber; + __u32 sequenceNumber; yaffs_ObjectHeader *oh; yaffs_Object *in; yaffs_Object *parent; @@ -5958,7 +5965,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) yaffs_BlockState state; yaffs_Object *hardList = NULL; yaffs_BlockInfo *bi; - int sequenceNumber; + __u32 sequenceNumber; yaffs_ObjectHeader *oh; yaffs_Object *in; yaffs_Object *parent; @@ -7232,7 +7239,7 @@ int yaffs_GutsInitialise(yaffs_Device * dev) dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; } - buf = dev->srCache = YMALLOC(srCacheBytes); + buf = (__u8 *)(dev->srCache = YMALLOC(srCacheBytes)); if(dev->srCache) memset(dev->srCache,0,srCacheBytes); @@ -7489,3 +7496,18 @@ static int yaffs_CheckStructures(void) return YAFFS_OK; } + +#ifdef NO_Y_INLINE +/* Function to manipulate block info */ +yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return &dev->blockInfo[blk - dev->internalStartBlock]; +} +#endif diff --git a/fs/yaffs2/yaffs_guts.h b/fs/yaffs2/yaffs_guts.h index 1f75efd0981..ee3dd260955 100644 --- a/fs/yaffs2/yaffs_guts.h +++ b/fs/yaffs2/yaffs_guts.h @@ -584,7 +584,7 @@ struct yaffs_DeviceStruct { yaffs_ExtendedTags * tags); int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, - yaffs_BlockState * state, int *sequenceNumber); + yaffs_BlockState * state, __u32 *sequenceNumber); #endif int isYaffs2; @@ -801,6 +801,9 @@ typedef struct { __u32 head; } yaffs_CheckpointValidity; +#ifdef NO_Y_INLINE +extern yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk); +#else /* Function to manipulate block info */ static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) { @@ -813,6 +816,7 @@ static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) } return &dev->blockInfo[blk - dev->internalStartBlock]; } +#endif /*----------------------- YAFFS Functions -----------------------*/ diff --git a/fs/yaffs2/yaffs_malloc.h b/fs/yaffs2/yaffs_malloc.h index 3ed6175163a..67d09e82c0b 100644 --- a/fs/yaffs2/yaffs_malloc.h +++ b/fs/yaffs2/yaffs_malloc.h @@ -19,7 +19,11 @@ #include <stdlib.h> #endif -void *yaffs_malloc(size_t size); +#ifdef YAFFS_DEBUG_MALLOC +extern void *yaffs_malloc(const char *func, int line, size_t size); +#else +extern void *yaffs_malloc(size_t size); +#endif void yaffs_free(void *ptr); #endif diff --git a/fs/yaffs2/yaffs_mtdif2.c b/fs/yaffs2/yaffs_mtdif2.c index f569b999f48..8a7f72778b6 100644 --- a/fs/yaffs2/yaffs_mtdif2.c +++ b/fs/yaffs2/yaffs_mtdif2.c @@ -192,7 +192,7 @@ int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) } int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, - yaffs_BlockState * state, int *sequenceNumber) + yaffs_BlockState * state, __u32 *sequenceNumber) { struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); int retval; diff --git a/fs/yaffs2/yaffs_mtdif2.h b/fs/yaffs2/yaffs_mtdif2.h index b67ba52aa42..19f63b33680 100644 --- a/fs/yaffs2/yaffs_mtdif2.h +++ b/fs/yaffs2/yaffs_mtdif2.h @@ -24,6 +24,6 @@ int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, yaffs_ExtendedTags * tags); int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, - yaffs_BlockState * state, int *sequenceNumber); + yaffs_BlockState * state, __u32 *sequenceNumber); #endif diff --git a/fs/yaffs2/yaffs_nand.c b/fs/yaffs2/yaffs_nand.c index e790be61a54..40776346933 100644 --- a/fs/yaffs2/yaffs_nand.c +++ b/fs/yaffs2/yaffs_nand.c @@ -101,7 +101,7 @@ int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo) int yaffs_QueryInitialBlockState(yaffs_Device * dev, int blockNo, yaffs_BlockState * state, - unsigned *sequenceNumber) + __u32 *sequenceNumber) { blockNo -= dev->blockOffset; diff --git a/fs/yaffs2/yaffs_nand.h b/fs/yaffs2/yaffs_nand.h index 48e3f7ec6dd..9a17c02f209 100644 --- a/fs/yaffs2/yaffs_nand.h +++ b/fs/yaffs2/yaffs_nand.h @@ -33,7 +33,7 @@ int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo); int yaffs_QueryInitialBlockState(yaffs_Device * dev, int blockNo, yaffs_BlockState * state, - unsigned *sequenceNumber); + __u32 *sequenceNumber); int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, int blockInNAND); diff --git a/fs/yaffs2/yaffs_tagscompat.c b/fs/yaffs2/yaffs_tagscompat.c index 70a8a8c72a4..ae14beb80ea 100644 --- a/fs/yaffs2/yaffs_tagscompat.c +++ b/fs/yaffs2/yaffs_tagscompat.c @@ -502,7 +502,7 @@ int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState * state, - int *sequenceNumber) + __u32 *sequenceNumber) { yaffs_Spare spare0, spare1; diff --git a/fs/yaffs2/yaffs_tagscompat.h b/fs/yaffs2/yaffs_tagscompat.h index a61e3ba14f1..169be0ce66e 100644 --- a/fs/yaffs2/yaffs_tagscompat.h +++ b/fs/yaffs2/yaffs_tagscompat.h @@ -31,7 +31,7 @@ int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState * - state, int *sequenceNumber); + state, __u32 *sequenceNumber); void yaffs_CalcTagsECC(yaffs_Tags * tags); int yaffs_CheckECCOnTags(yaffs_Tags * tags); diff --git a/fs/yaffs2/yaffscfg.c b/fs/yaffs2/yaffscfg.c index 16e84a42103..e5739a8452f 100644 --- a/fs/yaffs2/yaffscfg.c +++ b/fs/yaffs2/yaffscfg.c @@ -61,10 +61,25 @@ __u32 yaffsfs_CurrentTime(void) return 0; } +#ifdef YAFFS_DEBUG_MALLOC +void *yaffs_malloc(const char *func, int line, size_t size) +{ + void *ptr; + ptr = malloc(size); + if (!ptr) + printf("%s:%d %s failed to allocate %u bytes!\n", func, line, __FUNCTION__, (unsigned int) size); + return ptr; +} +#else void *yaffs_malloc(size_t size) { - return malloc(size); + void *ptr; + + ptr = malloc(size); + if (!ptr) + printf("%s failed to allocate %u bytes!\n", __FUNCTION__, (unsigned int)size); } +#endif void yaffs_free(void *ptr) { @@ -92,179 +107,15 @@ static int isMounted = 0; #define MOUNT_POINT "/flash" extern nand_info_t nand_info[]; -/* XXX U-BOOT XXX */ -#if 0 -static yaffs_Device ramDev; -static yaffs_Device bootDev; -static yaffs_Device flashDev; -#endif - -static yaffsfs_DeviceConfiguration yaffsfs_config[] = { -/* XXX U-BOOT XXX */ -#if 0 - { "/ram", &ramDev}, - { "/boot", &bootDev}, - { "/flash", &flashDev}, -#else - { MOUNT_POINT, 0}, -#endif - {(void *)0,(void *)0} -}; int yaffs_StartUp(void) { - struct mtd_info *mtd = &nand_info[0]; - int yaffsVersion = 2; - int nBlocks; - - yaffs_Device *flashDev = calloc(1, sizeof(yaffs_Device)); - yaffsfs_config[0].dev = flashDev; - - /* store the mtd device for later use */ - flashDev->genericDevice = mtd; - - // Stuff to configure YAFFS - // Stuff to initialise anything special (eg lock semaphore). - yaffsfs_LocalInitialisation(); - - // Set up devices - -/* XXX U-BOOT XXX */ -#if 0 - // /ram - ramDev.nBytesPerChunk = 512; - ramDev.nChunksPerBlock = 32; - ramDev.nReservedBlocks = 2; // Set this smaller for RAM - ramDev.startBlock = 1; // Can't use block 0 - ramDev.endBlock = 127; // Last block in 2MB. - ramDev.useNANDECC = 1; - ramDev.nShortOpCaches = 0; // Disable caching on this device. - ramDev.genericDevice = (void *) 0; // Used to identify the device in fstat. - ramDev.writeChunkWithTagsToNAND = yramdisk_WriteChunkWithTagsToNAND; - ramDev.readChunkWithTagsFromNAND = yramdisk_ReadChunkWithTagsFromNAND; - ramDev.eraseBlockInNAND = yramdisk_EraseBlockInNAND; - ramDev.initialiseNAND = yramdisk_InitialiseNAND; - - // /boot - bootDev.nBytesPerChunk = 612; - bootDev.nChunksPerBlock = 32; - bootDev.nReservedBlocks = 5; - bootDev.startBlock = 1; // Can't use block 0 - bootDev.endBlock = 127; // Last block in 2MB. - bootDev.useNANDECC = 0; // use YAFFS's ECC - bootDev.nShortOpCaches = 10; // Use caches - bootDev.genericDevice = (void *) 1; // Used to identify the device in fstat. - bootDev.writeChunkToNAND = yflash_WriteChunkToNAND; - bootDev.readChunkFromNAND = yflash_ReadChunkFromNAND; - bootDev.eraseBlockInNAND = yflash_EraseBlockInNAND; - bootDev.initialiseNAND = yflash_InitialiseNAND; -#endif - - // /flash - flashDev->nReservedBlocks = 5; -// flashDev->nShortOpCaches = (options.no_cache) ? 0 : 10; - flashDev->nShortOpCaches = 10; // Use caches - flashDev->useNANDECC = 0; // do not use YAFFS's ECC - - if (yaffsVersion == 2) - { - flashDev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND; - flashDev->readChunkWithTagsFromNAND = nandmtd2_ReadChunkWithTagsFromNAND; - flashDev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; - flashDev->queryNANDBlock = nandmtd2_QueryNANDBlock; - flashDev->spareBuffer = YMALLOC(mtd->oobsize); - flashDev->isYaffs2 = 1; -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) - flashDev->nDataBytesPerChunk = mtd->writesize; - flashDev->nChunksPerBlock = mtd->erasesize / mtd->writesize; -#else - flashDev->nDataBytesPerChunk = mtd->oobblock; - flashDev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; -#endif - nBlocks = mtd->size / mtd->erasesize; - - flashDev->nCheckpointReservedBlocks = 10; - flashDev->startBlock = 0; - flashDev->endBlock = nBlocks - 1; - } - else - { - flashDev->writeChunkToNAND = nandmtd_WriteChunkToNAND; - flashDev->readChunkFromNAND = nandmtd_ReadChunkFromNAND; - flashDev->isYaffs2 = 0; - nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); - flashDev->startBlock = 320; - flashDev->endBlock = nBlocks - 1; - flashDev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK; - flashDev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK; - } - - /* ... and common functions */ - flashDev->eraseBlockInNAND = nandmtd_EraseBlockInNAND; - flashDev->initialiseNAND = nandmtd_InitialiseNAND; - - yaffs_initialise(yaffsfs_config); + yaffs_initialise(NULL); return 0; } - -void make_a_file(char *yaffsName,char bval,int sizeOfFile) -{ - int outh; - int i; - unsigned char buffer[100]; - - outh = yaffs_open(yaffsName, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE); - if (outh < 0) - { - printf("Error opening file: %d\n", outh); - return; - } - - memset(buffer,bval,100); - - do{ - i = sizeOfFile; - if(i > 100) i = 100; - sizeOfFile -= i; - - yaffs_write(outh,buffer,i); - - } while (sizeOfFile > 0); - - - yaffs_close(outh); -} - -void read_a_file(char *fn) -{ - int h; - int i = 0; - unsigned char b; - - h = yaffs_open(fn, O_RDWR,0); - if(h<0) - { - printf("File not found\n"); - return; - } - - while(yaffs_read(h,&b,1)> 0) - { - printf("%02x ",b); - i++; - if(i > 32) - { - printf("\n"); - i = 0;; - } - } - printf("\n"); - yaffs_close(h); -} - void cmd_yaffs_mount(char *mp) { yaffs_StartUp(); @@ -275,45 +126,20 @@ void cmd_yaffs_mount(char *mp) printf("Error mounting %s, return value: %d\n", mp, yaffsfs_GetError()); } -static void checkMount(void) -{ - if( !isMounted ) - { - cmd_yaffs_mount(MOUNT_POINT); - } -} - void cmd_yaffs_umount(char *mp) { - checkMount(); if( yaffs_unmount(mp) == -1) printf("Error umounting %s, return value: %d\n", mp, yaffsfs_GetError()); } -void cmd_yaffs_write_file(char *yaffsName,char bval,int sizeOfFile) -{ - checkMount(); - make_a_file(yaffsName,bval,sizeOfFile); -} - - -void cmd_yaffs_read_file(char *fn) -{ - checkMount(); - read_a_file(fn); -} - - -void cmd_yaffs_mread_file(char *fn, char *addr) +void cmd_yaffs_mread_file(char *fn, char *addr, long *size) { int h; struct yaffs_stat s; - checkMount(); - yaffs_stat(fn,&s); - printf ("Copy %s to 0x%08x... ", fn, addr); + printf ("Copy %s to 0x%p... ", fn, addr); h = yaffs_open(fn, O_RDWR,0); if(h<0) { @@ -323,6 +149,7 @@ void cmd_yaffs_mread_file(char *fn, char *addr) yaffs_read(h,addr,(int)s.st_size); printf("\t[DONE]\n"); + *size = s.st_size; yaffs_close(h); } @@ -332,7 +159,6 @@ void cmd_yaffs_mwrite_file(char *fn, char *addr, int size) { int outh; - checkMount(); outh = yaffs_open(fn, O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE); if (outh < 0) { @@ -353,7 +179,6 @@ void cmd_yaffs_ls(const char *mountpt, int longlist) struct yaffs_stat stat; char tempstr[255]; - checkMount(); d = yaffs_opendir(mountpt); if(!d) @@ -368,7 +193,7 @@ void cmd_yaffs_ls(const char *mountpt, int longlist) { sprintf(tempstr, "%s/%s", mountpt, de->d_name); yaffs_stat(tempstr, &stat); - printf("%-25s\t%7d\n",de->d_name, stat.st_size); + printf("%-25s\t%7ld\n",de->d_name, stat.st_size); } else { @@ -378,11 +203,22 @@ void cmd_yaffs_ls(const char *mountpt, int longlist) } } +int cmd_yaffs_df(const char *path, loff_t *space) +{ + loff_t free_space; + int ret = 0; + + free_space = yaffs_freespace(path); + if (free_space == -1) { + ret = yaffsfs_GetError(); + } else + *space = free_space; + + return ret; +} void cmd_yaffs_mkdir(const char *dir) { - checkMount(); - int retval = yaffs_mkdir(dir, 0); if ( retval < 0) @@ -391,8 +227,6 @@ void cmd_yaffs_mkdir(const char *dir) void cmd_yaffs_rmdir(const char *dir) { - checkMount(); - int retval = yaffs_rmdir(dir); if ( retval < 0) @@ -401,8 +235,6 @@ void cmd_yaffs_rmdir(const char *dir) void cmd_yaffs_rm(const char *path) { - checkMount(); - int retval = yaffs_unlink(path); if ( retval < 0) @@ -411,8 +243,6 @@ void cmd_yaffs_rm(const char *path) void cmd_yaffs_mv(const char *oldPath, const char *newPath) { - checkMount(); - int retval = yaffs_rename(newPath, oldPath); if ( retval < 0) diff --git a/fs/yaffs2/yaffsfs.c b/fs/yaffs2/yaffsfs.c index 111cb348c0d..1636baab976 100644 --- a/fs/yaffs2/yaffsfs.c +++ b/fs/yaffs2/yaffsfs.c @@ -15,14 +15,24 @@ #include <common.h> #include <malloc.h> +#include <jffs2/load_kernel.h> + #include "yaffsfs.h" #include "yaffs_guts.h" #include "yaffscfg.h" #include "yportenv.h" -/* XXX U-BOOT XXX */ -#if 0 -#include <string.h> // for memset +#include "yaffs_mtdif.h" +#include "yaffs_mtdif2.h" +#include "yaffs_flashif.h" +#include "nand.h" + +#include "mtd_parts.h" +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +#if (CONFIG_SYS_MALLOC_LEN < (512 << 10)) +#error Malloc Area too small for YAFFS, increas CONFIG_SYS_MALLOC_LEN to >= 512KiB #endif #define YAFFSFS_MAX_SYMLINK_DEREFERENCES 5 @@ -34,10 +44,6 @@ const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.18 2007/07/18 19:40:38 charles Exp $"; -// configurationList is the list of devices that are supported -static yaffsfs_DeviceConfiguration *yaffsfs_configurationList; - - /* Some forward references */ static yaffs_Object *yaffsfs_FindObject(yaffs_Object *relativeDirectory, const char *path, int symDepth); static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj); @@ -151,58 +157,135 @@ int yaffsfs_Match(char a, char b) return (a == b); } -// yaffsfs_FindDevice -// yaffsfs_FindRoot -// Scan the configuration list to find the root. -// Curveballs: Should match paths that end in '/' too -// Curveball2 Might have "/x/ and "/x/y". Need to return the longest match -static yaffs_Device *yaffsfs_FindDevice(const char *path, char **restOfPath) +/* yaffsfs_FindDevice + * yaffsfs_FindRoot + * Scan the parttion list to find the root; split out rest of path from + * Initialial partition. path must be /<partition>/<dir-or-file> */ + +#define N_MAX_PARTLEN 64 + +typedef enum { + YAFFSFS_ACTION_CREATE, + YAFFSFS_ACTION_FIND, + YAFFSFS_ACTION_DESTROY +} yaffsfs_action_t; + +static struct yaffs_dev *yaffsfs_FindDevice(const char *path, char **restOfPath, yaffsfs_action_t action) { - yaffsfs_DeviceConfiguration *cfg = yaffsfs_configurationList; - const char *leftOver; - const char *p; - yaffs_Device *retval = NULL; - int thisMatchLength; - int longestMatch = -1; - - // Check all configs, choose the one that: - // 1) Actually matches a prefix (ie /a amd /abc will not match - // 2) Matches the longest. - while(cfg && cfg->prefix && cfg->dev) - { - leftOver = path; - p = cfg->prefix; - thisMatchLength = 0; - - while(*p && //unmatched part of prefix - strcmp(p,"/") && // the rest of the prefix is not / (to catch / at end) - *leftOver && - yaffsfs_Match(*p,*leftOver)) - { - p++; - leftOver++; - thisMatchLength++; + int slash_cnt, i, ret; + char partname[N_MAX_PARTLEN]; + void *part_priv; + loff_t part_off, part_size; + int part_idx; + struct mtd_device *dev; + void *cookie; + struct yaffs_dev *flashDev; + struct mtd_info *mtd; + + if (!path) + return NULL; + if (*path != '/') { + printf("Path '%s' does not start with '/'!\n", path); + return NULL; + } + path++; /* Skip over leading '/' */ + + for (slash_cnt=i=0; i<N_MAX_PARTLEN && path[i]; ++i) { + if (path[i] == '/') + break; + partname[i] = path[i]; + } + partname[i] = '\0'; + + /* save rest of path(if exists) */ + *restOfPath = (char *)path+i; + if (**restOfPath == '/') + (*restOfPath)++; + + /* Find the partition */ + ret = mtd_get_part_priv(partname, &part_idx, &dev, &part_off, &part_size, &cookie, &part_priv); + if (ret) + return NULL; + + switch(action) { + case YAFFSFS_ACTION_FIND: + return (struct yaffs_dev *)part_priv; + + case YAFFSFS_ACTION_CREATE: + if (part_priv) { + printf("Huh? mount of partition '%s' already has device %p\n", partname, part_priv); + return NULL; } - if((!*p || strcmp(p,"/") == 0) && // end of prefix - (!*leftOver || *leftOver == '/') && // no more in this path name part - (thisMatchLength > longestMatch)) - { - // Matched prefix - *restOfPath = (char *)leftOver; - retval = cfg->dev; - longestMatch = thisMatchLength; + + /* First time we're seeing this device, create it using + information inside of the MTD_DEVICE structure */ + flashDev = malloc(sizeof(struct yaffs_dev)); + if (!flashDev) { + printf("%s:%d out of memory!\n", __FUNCTION__, __LINE__); + return NULL; } - cfg++; + memset(flashDev, 0, sizeof(*flashDev)); + + + printf("%s: initialise struct yaffs_dev %p\n", __FUNCTION__, flashDev); + + /* Side effect of mtd_get_part_priv() is to set nand_curr_device */ + mtd = &nand_info[nand_curr_device]; + + flashDev->genericDevice = mtd; + flashDev->startBlock = part_off / mtd->erasesize; + flashDev->endBlock = (part_off + part_size - 1) / mtd->erasesize; +#if 0 + printf("%s: part_off %x part_size %x startBlock %u endBlock %u\n", __FUNCTION__, (unsigned int)part_off, (unsigned int)part_size, flashDev->startBlock, flashDev->endBlock); +#endif + flashDev->nReservedBlocks = 5; + + flashDev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND; + flashDev->readChunkWithTagsFromNAND = nandmtd2_ReadChunkWithTagsFromNAND; + flashDev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; + flashDev->queryNANDBlock = nandmtd2_QueryNANDBlock; + flashDev->isYaffs2 = 1; +#if 1 + Yaffs_DeviceToContext(dev)->spareBuffer = YMALLOC(mtd->oobsize); +#else + flashDev->spareBuffer = YMALLOC(mtd->oobsize); +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + flashDev->nDataBytesPerChunk = mtd->writesize; + flashDev->nChunksPerBlock = mtd->erasesize / mtd->writesize; +#else + flashDev->nDataBytesPerChunk = mtd->oobblock; + flashDev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; +#endif + flashDev->nCheckpointReservedBlocks = 10; + + /* ... and common functions */ + flashDev->eraseBlockInNAND = nandmtd_EraseBlockInNAND; + flashDev->initialiseNAND = nandmtd_InitialiseNAND; + flashDev->removeObjectCallback = yaffsfs_RemoveObjectCallback; + + mtd_set_part_priv(cookie, flashDev); + + return flashDev; + + case YAFFSFS_ACTION_DESTROY: + free(part_priv); + mtd_set_part_priv(cookie, NULL); + return NULL; + + default: + printf("%s: unknown action %d\n", __FUNCTION__, (int)action); + return NULL; } - return retval; + } static yaffs_Object *yaffsfs_FindRoot(const char *path, char **restOfPath) { - yaffs_Device *dev; + struct yaffs_dev *dev; - dev= yaffsfs_FindDevice(path,restOfPath); + dev= yaffsfs_FindDevice(path,restOfPath, YAFFSFS_ACTION_FIND); if(dev && dev->isMounted) { return dev->rootDir; @@ -1032,13 +1115,13 @@ int yaffs_mount(const char *path) { int retVal=-1; int result=YAFFS_FAIL; - yaffs_Device *dev=NULL; + struct yaffs_dev *dev=NULL; char *dummy; T(YAFFS_TRACE_ALWAYS,("yaffs: Mounting %s\n",path)); yaffsfs_Lock(); - dev = yaffsfs_FindDevice(path,&dummy); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_CREATE); if(dev) { if(!dev->isMounted) @@ -1071,11 +1154,11 @@ int yaffs_mount(const char *path) int yaffs_unmount(const char *path) { int retVal=-1; - yaffs_Device *dev=NULL; + struct yaffs_dev *dev=NULL; char *dummy; yaffsfs_Lock(); - dev = yaffsfs_FindDevice(path,&dummy); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_FIND); if(dev) { if(dev->isMounted) @@ -1098,6 +1181,8 @@ int yaffs_unmount(const char *path) { yaffs_Deinitialise(dev); + yaffsfs_FindDevice(path, &dummy, YAFFSFS_ACTION_DESTROY); + retVal = 0; } else @@ -1127,11 +1212,11 @@ int yaffs_unmount(const char *path) loff_t yaffs_freespace(const char *path) { loff_t retVal=-1; - yaffs_Device *dev=NULL; + struct yaffs_dev *dev=NULL; char *dummy; yaffsfs_Lock(); - dev = yaffsfs_FindDevice(path,&dummy); + dev = yaffsfs_FindDevice(path,&dummy, YAFFSFS_ACTION_FIND); if(dev && dev->isMounted) { retVal = yaffs_GetNumberOfFreeChunks(dev); @@ -1151,21 +1236,7 @@ loff_t yaffs_freespace(const char *path) void yaffs_initialise(yaffsfs_DeviceConfiguration *cfgList) { - - yaffsfs_DeviceConfiguration *cfg; - - yaffsfs_configurationList = cfgList; - yaffsfs_InitHandles(); - - cfg = yaffsfs_configurationList; - - while(cfg && cfg->prefix && cfg->dev) - { - cfg->dev->isMounted = 0; - cfg->dev->removeObjectCallback = yaffsfs_RemoveObjectCallback; - cfg++; - } } @@ -1487,7 +1558,7 @@ int yaffs_DumpDevStruct(const char *path) if(obj) { - yaffs_Device *dev = obj->myDev; + struct yaffs_dev *dev = obj->myDev; printf("\n" "nPageWrites.......... %d\n" diff --git a/fs/yaffs2/ydirectenv.h b/fs/yaffs2/ydirectenv.h index b5558108441..12b910b7d06 100644 --- a/fs/yaffs2/ydirectenv.h +++ b/fs/yaffs2/ydirectenv.h @@ -54,12 +54,18 @@ #define Y_INLINE inline #endif +#ifdef YAFFS_DEBUG_MALLOC +#define YMALLOC(x) yaffs_malloc(__FUNCTION__, __LINE__, (x)) +#define YMALLOC_ALT(x) yaffs_malloc(__FUNCTION__, __LINE__, (x)) +#define YMALLOC_DMA(x) yaffs_malloc(__FUNCTION__, __LINE__, (x)) +#else #define YMALLOC(x) yaffs_malloc(x) -#define YFREE(x) free(x) #define YMALLOC_ALT(x) yaffs_malloc(x) +#define YMALLOC_DMA(x) yaffs_malloc(x) +#endif +#define YFREE(x) free(x) #define YFREE_ALT(x) free(x) -#define YMALLOC_DMA(x) yaffs_malloc(x) #define YYIELD() do {} while(0) diff --git a/fs/yaffs2/yportenv.h b/fs/yaffs2/yportenv.h index bbab14d503d..28bdff386f1 100644 --- a/fs/yaffs2/yportenv.h +++ b/fs/yaffs2/yportenv.h @@ -96,6 +96,8 @@ #elif defined CONFIG_YAFFS_DIRECT +#define YAFFS_DEBUG_MALLOC /* Complain if yaffs_malloc() fails */ + /* Direct interface */ #include "ydirectenv.h" diff --git a/include/common.h b/include/common.h index 1e21b7a3d5c..8b32431b568 100644 --- a/include/common.h +++ b/include/common.h @@ -307,6 +307,13 @@ void pciinfo (int, int); int misc_init_f (void); int misc_init_r (void); +/* Environment/nand support functions */ +extern void nand_setup_default_ecc_method(void); +extern void nand_switch_ecc_default(int *ecc_method); +extern void nand_switch_ecc_method(int ecc_method); +extern int setenv_reserved_name(const char *name); +extern void touchup_env(void); + /* common/exports.c */ void jumptable_init(void); @@ -724,6 +731,14 @@ int pcmcia_init (void); * Board-specific Platform code can reimplement show_boot_progress () if needed */ void show_boot_progress(int val); +#ifdef CONFIG_BOARD_LCD_SETMEM +ulong board_lcd_setmem (ulong addr); +#endif + +#ifdef CONFIG_LCD_PERCENT +extern void lcd_percent_init(int total_size); +extern void lcd_percent_update(int size); +#endif /* Multicore arch functions */ #ifdef CONFIG_MP diff --git a/include/configs/omap3logic.h b/include/configs/omap3logic.h new file mode 100644 index 00000000000..1aaf2920893 --- /dev/null +++ b/include/configs/omap3logic.h @@ -0,0 +1,633 @@ +/* + * (C) Copyright 2006-2008 + * Texas Instruments. + * Author : + * Manikandan Pillai <mani.pillai@ti.com> + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff <r-woodruff2@ti.com> + * Syed Mohammed Khasim <khasim@ti.com> + * + * Manikandan Pillai <mani.pillai@ti.com> + * + * Configuration settings for the TI OMAP3 EVM board. + * + * 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 __CONFIG_H +#define __CONFIG_H + +/* + * High Level Configuration Options + */ +#define CONFIG_ARMV7 1 /* This is an ARM V7 CPU core */ +#define CONFIG_OMAP 1 /* in a TI OMAP core */ +#define CONFIG_OMAP34XX 1 /* which is a 34XX */ +#define CONFIG_OMAP3430 1 /* which is in a 3430 */ +#define CONFIG_OMAP3_LOGIC 1 /* working with Logic OMAP boards */ + +/* Define following to enable new product ID code. */ +#define CONFIG_OMAP3_LOGIC_USE_NEW_PRODUCT_ID 1 + +#define CONFIG_SDRC /* The chip has SDRC controller */ + +#include <asm/arch/cpu.h> /* get chip and board defs */ +#include <asm/arch/omap3.h> + +/* + * Display CPU and Board information + */ +#define CONFIG_DISPLAY_CPUINFO 1 +#define CONFIG_DISPLAY_BOARDINFO 1 + +/* Clock Defines */ +#define V_OSCK 26000000 /* Clock output from T2 */ +#define V_SCLK (V_OSCK >> 1) + +#undef CONFIG_USE_IRQ /* no support for IRQs */ +#define CONFIG_MISC_INIT_R + +#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */ +#define CONFIG_SETUP_MEMORY_TAGS 1 +#define CONFIG_INITRD_TAG 1 +#define CONFIG_REVISION_TAG 1 + +#define CONFIG_CMDLINE_EDITING 1 /* cmd line edit/history */ +#define CONFIG_ZERO_BOOTDELAY_CHECK 1 /* check keypress with zero bootdelay */ + +/* + * Size of malloc() pool + */ +#define CONFIG_ENV_SIZE (128 << 10) /* 128 KiB */ + /* Sector */ +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (6 << 20)) + +/* + * Add GDB information (section starts) into bdinfo; used in gdb + * "add-symbol-file" to specify start of .data, .bss, .rodata sections + */ +#define CONFIG_GDB_SECTION_STARTS + +/* + * Hardware drivers + */ + +/* + * NS16550 Configuration + */ +#define V_NS16550_CLK 48000000 /* 48MHz (APLL96/2) */ + +#define CONFIG_SYS_NS16550 +#define CONFIG_SYS_NS16550_SERIAL +#define CONFIG_SYS_NS16550_REG_SIZE (-4) +#define CONFIG_SYS_NS16550_CLK V_NS16550_CLK + +/* + * select serial console configuration + */ +#define CONFIG_CONS_INDEX 1 +#define CONFIG_SYS_NS16550_COM1 OMAP34XX_UART1 +#define CONFIG_SERIAL1 1 /* UART1 on OMAP3 EVMOMAP Logic boards */ + +/* allow to overwrite serial and ethaddr */ +#define CONFIG_ENV_OVERWRITE +/* But don't allow overwriting "defaultecc" */ +#define CONFIG_CHECK_SETENV + +#define CONFIG_BAUDRATE 115200 +#define CONFIG_SYS_BAUDRATE_TABLE {4800, 9600, 19200, 38400, 57600,\ + 115200} +#define CONFIG_MMC 1 +#define CONFIG_OMAP3_MMC 1 +#define CONFIG_DOS_PARTITION 1 + +/* DDR - I use Micron DDR */ +#define CONFIG_OMAP3_MICRON_DDR 1 + +/* USB + * Enable CONFIG_MUSB_HCD for Host functionalities MSC, keyboard + * Enable CONFIG_MUSB_UDD for Device functionalities. + */ +#define CONFIG_USB_OMAP3 1 +#define CONFIG_MUSB_HCD 1 +/* #define CONFIG_MUSB_UDC 1 */ + +#ifdef CONFIG_USB_OMAP3 + +#ifdef CONFIG_MUSB_HCD +#define CONFIG_CMD_USB + +#define CONFIG_USB_STORAGE +#define CONGIG_CMD_STORAGE +#define CONFIG_CMD_FAT + +#ifdef CONFIG_USB_KEYBOARD +#define CONFIG_SYS_USB_EVENT_POLL +// #define CONFIG_PREBOOT "usb start" +#endif /* CONFIG_USB_KEYBOARD */ + +#endif /* CONFIG_MUSB_HCD */ + +#ifdef CONFIG_MUSB_UDC +/* USB device configuration */ +#define CONFIG_USB_DEVICE 1 +#define CONFIG_USB_TTY 1 +#define CONFIG_SYS_CONSOLE_IS_IN_ENV 1 +/* Change these to suit your needs */ +#define CONFIG_USBD_VENDORID 0x0451 +#define CONFIG_USBD_PRODUCTID 0x5678 +#define CONFIG_USBD_MANUFACTURER "Texas Instruments" +#define CONFIG_USBD_PRODUCT_NAME "EVM" +#endif /* CONFIG_MUSB_UDC */ + +#endif /* CONFIG_USB_OMAP3 */ + +/* commands to include */ +#include <config_cmd_default.h> + +#define CONFIG_CMD_EXT2 /* EXT2 Support */ +#define CONFIG_CMD_FAT /* FAT support */ +#define CONFIG_CMD_JFFS2 /* JFFS2 Support */ +#define CONFIG_CMD_NAND_YAFFS /* YAFFS NAND Support */ +// #define CONFIG_YAFFS2 /* YAFFS2 Support */ +#define CONFIG_YAFFS2_NEW /* Newer YAFFS2 Support */ + +#define CONFIG_CMD_I2C /* I2C serial bus support */ +#define CONFIG_CMD_MMC /* MMC support */ +#define CONFIG_CMD_NAND /* NAND support */ +#define CONFIG_MTD_DEVICE /* needed for MTD mtdparts support */ +#define CONFIG_CMD_MTDPARTS /* MTD partition support */ +#define MTDIDS_DEFAULT "nand0=omap2-nand.0" +#define MTDPARTS_DEFAULT "mtdparts=omap2-nand.0:512k(x-loader),"\ + "1664k(u-boot),384k(u-boot-env),"\ + "5m(kernel),20m(ramdisk),-(fs)" +#define CONFIG_NAND_SET_DEFAULT /* Set default NAND access method */ +#define CONFIG_NAND_MULTIPLE_ECC /* NAND has multiple ECC methods */ +#define CONFIG_TOUCHUP_ENV /* Set board-specific environment vars */ +#define CONFIG_CMD_NAND_LOCK_UNLOCK /* nand (un)lock commands */ +#define CONFIG_CMD_DHCP +#define CONFIG_CMD_PING +#define CONFIG_CMD_CACHE /* Cache control */ +#define CONFIG_CMD_TIME /* time command */ + +#define CONFIG_L2_OFF 1 /* Keep L2 Cache Disabled */ + +#define BOARD_LATE_INIT + +#define CONFIG_CMD_FLASH /* flinfo, erase, protect */ +#undef CONFIG_CMD_FPGA /* FPGA configuration Support */ +#undef CONFIG_CMD_IMI /* iminfo */ +#undef CONFIG_CMD_IMLS /* List all found images */ + +#define CONFIG_CMD_GPMC_CONFIG /* gpmc_config */ +#define CONFIG_CMD_MUX_CONFIG /* mux_config */ +#define CONFIG_CMD_SDRC_CONFIG /* sdrc_config */ + +#define CONFIG_HARD_I2C 1 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SLAVE 1 +#define CONFIG_SYS_I2C_BUS 0 +#define CONFIG_SYS_I2C_BUS_SELECT 1 +#define CONFIG_DRIVER_OMAP34XX_I2C 1 +#define CONFIG_I2C_MULTI_BUS 1 + +#define CONFIG_MTD_DEBUG 1 +#define CONFIG_MTD_DEBUG_VERBOSE -1 +#define CONFIG_MTD_SKIP_BBTSCAN 1 /* Skip NAND bad block scan */ +#define CONFIG_TOOL_SIGNGP 1 + +/* + * TWL4030 + */ +#define CONFIG_TWL4030_POWER 1 +#define CONFIG_TWL4030_GPIO 1 +#define CONFIG_TWL4030_PWM 1 +#define CONFIG_TWL4030_CHARGING 1 + +/* Charge the batter unless $disablecharging == "yes" */ +#define CONFIG_ENABLE_TWL4030_CHARGING 1 + +/* + * Board NAND Info. + */ +#define CONFIG_SYS_NAND_ADDR NAND_BASE /* physical address */ + /* to access nand */ +#define CONFIG_SYS_NAND_BASE NAND_BASE /* physical address */ + /* to access */ + /* nand at CS0 */ + +#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of */ + /* NAND devices */ +#define CONFIG_JFFS2_NAND +/* nand device jffs2 lives on */ +#define CONFIG_JFFS2_DEV "nand0" +/* start of jffs2 partition */ +#define CONFIG_JFFS2_PART_OFFSET 0x680000 +#define CONFIG_JFFS2_PART_SIZE 0xf980000 /* sz of jffs2 part */ + +/* Environment information */ +#define CONFIG_BOOTDELAY 3 + +#define CONFIG_BOOTFILE uImage + +#define CONFIG_PREBOOT \ + "echo ; " \ + "echo =================================== NOTICE ===================================;" \ + "echo \"The U-Boot environment was not found. If the display is not set properly \";"\ + "echo \"linux will not have video support.\";" \ + "echo ; " \ + "echo \"Valid display options are:\";" \ + "echo \" 2 == LQ121S1DG31 TFT SVGA (12.1) Sharp\";" \ + "echo \" 3 == LQ036Q1DA01 TFT QVGA (3.6) Sharp w/ASIC\";" \ + "echo \" 5 == LQ064D343 TFT VGA (6.4) Sharp\";" \ + "echo \" 7 == LQ10D368 TFT VGA (10.4) Sharp\";" \ + "echo \" 15 == LQ043T1DG01 TFT WQVGA (4.3) Sharp (DEFAULT)\";" \ + "echo \" vga[-16 OR -24] LCD VGA 640x480\";" \ + "echo \" svga[-16 OR -24] LCD SVGA 800x600\";" \ + "echo \" xga[-16 OR -24] LCD XGA 1024x768\";" \ + "echo \" 720p[-16 OR -24] LCD 720P 1280x720\";" \ + "echo \" sxga[-16 OR -24] LCD SXGA 1280x1024\";" \ + "echo \" uxga[-16 OR -24] LCD UXGA 1600x1200\";" \ + "echo ; " \ + "echo \"Default `display` environment variable is now being set to: 15\";" \ + "setenv display 15;" \ + "setenv preboot;" \ + "echo ; " \ + "echo \"At the U-Boot prompt type commands: `setenv display <num>`, then type\";" \ + "echo \"`saveenv` to save the environment to NAND flash. This will avoid seeing\";" \ + "echo \"this notice on future boots\" ; " \ + "echo =================================== NOTICE ===================================;" \ + "echo ; " + +#ifdef CONFIG_USB_TTY +#define OMAP3LOGIC_USBTTY "usbtty=cdc_acm\0" +#else +#define OMAP3LOGIC_USBTTY +#endif + +#define CONFIG_BOOTCOMMAND \ + "run autoboot" + +#define CONFIG_MMC_BOOTSCRIPT_NAME \ + "boot.scr" + +/*****************************************************************************/ +/* Default Environment */ +/* */ +/* When reconfiguring the boot environment, be sure to set the environment */ +/* variables as indicated below for a successful boot. */ +/* */ +/* - $kernel_location */ +/* Must always be set. Values can be ram, nand, mmc, or tftp. */ +/* */ +/* - $rootfs_location */ +/* Must always be set. Values can be ram, tftp, /dev, nfs, mmc, or nand. */ +/* */ +/* - $rootfs_type */ +/* Must always be set. Values can be ramdisk, jffs, yaffs, ext3, or nfs. */ +/* */ +/* - $loadaddr */ +/* Must always be set. */ +/* */ +/* - If booting from /dev based file system, then $rootfs_device must be */ +/* set. */ +/* - If booting from a ramdisk image, then $ramdisksize, and $ramdiskaddr */ +/* must be set. */ +/* - If booting from an nfs location, then $serverip, $nfsrootpath, and */ +/* $nfsoptions must be set. */ +/* - If booting from nand, $ramdisk_nand_offset, $ramdisk_nand_size, */ +/* $kernel_nand_offset, and $kernel_nand_size must be set. */ +/* - If booting from a file system, $ramdiskimage, and $kernelimage must be */ +/* set. */ +/* - Optionally, a boot script named "boot.scr" can be placed in SD to */ +/* override any other boot scripts. */ +/*****************************************************************************/ +#define CONFIG_EXTRA_ENV_SETTINGS \ + OMAP3LOGIC_USBTTY \ + "bootargs=\0" \ + "otherbootargs=ignore_loglevel early_printk no_console_suspend \0" \ + "consoledevice=ttyO0\0" \ + "setconsole=setenv console ${consoledevice},${baudrate}n8\0" \ + "mtdids=" MTDIDS_DEFAULT "\0" \ + "mtdparts=" MTDPARTS_DEFAULT "\0" \ + "disablecharging=no\0" \ + "mmc_bootscript_addr=0x80FF0000\0" \ + "disablecharging no\0" \ + "display=15\0" \ + "loadaddr=0x81000000\0" \ + "kernel_location=mmc \0" \ + "rootfs_location=mmc \0" \ + "rootfs_type=ramdisk \0" \ + "rootfs_device=/dev/mtdblock4 \0" \ + "ramdisksize=64000\0" \ + "ramdiskaddr=0x82000000\0" \ + "ramdiskimage=rootfs.ext2.gz.uboot\0" \ + "ramdisk_nand_offset=0x00680000\0" \ + "ramdisk_nand_size=0x00d40000\0" \ + "kernel_nand_offset=0x00280000 \0" \ + "kernel_nand_size=0x00400000 \0" \ + "kernelimage=uImage \0" \ + "serverip=192.168.3.5\0" \ + "nfsrootpath=/opt/nfs-exports/ltib-omap\0" \ + "nfsoptions=,wsize=1500,rsize=1500\0" \ + "rotation=0\0" \ + "autoboot=echo \"\n== Checking mmc1 for alternate boot script " CONFIG_MMC_BOOTSCRIPT_NAME " ==\"; " \ + "if mmc init; then " \ + "if run loadbootscript; then " \ + "echo \"\"; " \ + "echo \"== Found script on mmc 1, starting ==\"; " \ + "run bootscript; " \ + "else " \ + "echo \"\"; " \ + "echo \"== Script not found on mmc 1, proceeding with defaultboot ==\"; " \ + "run defaultboot;" \ + "fi; " \ + "else run defaultboot; fi\0" \ + "loadbootscript=fatload mmc 1 $mmc_bootscript_addr " CONFIG_MMC_BOOTSCRIPT_NAME "\0" \ + "bootscript=source ${mmc_bootscript_addr}\0" \ + "vrfb_arg=if itest ${rotation} -ne 0; then " \ + "setenv bootargs ${bootargs} omapfb.vrfb=y omapfb.rotate=${rotation}; " \ + "fi\0" \ + "dump_bootargs=echo \"\"; echo \"== Kernel bootargs ==\"; echo $bootargs; echo \"\"; \0" \ + "dump_boot_sources=echo \"kernel_location: $kernel_location, " \ + "rootfs_location: $rootfs_location, " \ + "rootfs_type: $rootfs_type\"; " \ + "echo \"\"; " \ + "\0" \ + "load_kernel=if test $kernel_location = 'ram'; then " \ + "echo \"== kernel located at $loadaddr ==\"; " \ + "echo \"\"; " \ + "setenv bootm_arg1 ${loadaddr};" \ + "else if test $kernel_location = 'nand'; then " \ + "echo \"== Loading kernel from nand to $loadaddr ==\"; " \ + "nand read.i $loadaddr $kernel_nand_offset $kernel_nand_size; " \ + "echo \"\"; " \ + "setenv bootm_arg1 ${loadaddr};" \ + "else if test $kernel_location = 'mmc'; then " \ + "echo \"== Loading kernel file $kernelimage to $loadaddr ==\"; " \ + "mmc init; " \ + "fatload mmc 1 $loadaddr $kernelimage; " \ + "echo \"\"; " \ + "setenv bootm_arg1 ${loadaddr};" \ + "else if test $kernel_location = 'tftp'; then " \ + "echo \"== Loading kernel file $kernelimage to $loadaddr ==\"; " \ + "tftpboot $loadaddr $kernelimage; " \ + "echo \"\"; " \ + "setenv bootm_arg1 ${loadaddr};" \ + "else "\ + "echo \"== kernel_location must be set to ram, nand, mmc, or tftp!! ==\"; " \ + "echo \"\"; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi " \ + "\0" \ + "load_rootfs=if test $rootfs_location = 'ram'; then " \ + "echo \"== rootfs located at $ramdiskaddr ==\"; " \ + "echo \"\"; " \ + "setenv bootm_arg2 ${ramdiskaddr}; " \ + "else if test $rootfs_location = 'tftp'; then " \ + "echo \"== Loading rootfs file $ramdiskimage to $ramdiskaddr ==\"; " \ + "tftpboot $ramdiskaddr $ramdiskimage;" \ + "echo \"\"; " \ + "setenv bootm_arg2 ${ramdiskaddr}; " \ + "else if test $rootfs_location = '/dev'; then " \ + "echo \"== rootfs located in $rootfs_device ==\"; " \ + "echo \"\"; " \ + "setenv bootargs ${bootargs} root=${rootfs_device}; " \ + "setenv bootm_arg2; " \ + "else if test $rootfs_location = 'nfs'; then " \ + "echo \"== rootfs located at $nfs_root_path on server $serverip ==\"; " \ + "echo \"\"; " \ + "setenv bootargs ${bootargs} root=/dev/nfs; " \ + "setenv bootm_arg2; " \ + "else if test $rootfs_location = 'mmc'; then " \ + "echo \"== Loading rootfs file $ramdiskimage to $ramdiskaddr ==\"; " \ + "fatload mmc 1 ${ramdiskaddr} ${ramdiskimage}; "\ + "setenv bootm_arg2 ${ramdiskaddr}; " \ + "else if test $rootfs_location = 'nand'; then " \ + "echo \"== Loading rootfs from nand to $ramdiskaddr ==\"; " \ + "nand read.i $ramdiskaddr $ramdisk_nand_offset $ramdisk_nand_size; " \ + "setenv bootm_arg2 ${ramdiskaddr}; " \ + "else "\ + "echo \"== rootfs_location must be set to ram, tftp, /dev, nfs, mmc, or nand!! == \"; " \ + "echo \"\"; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi " \ + "\0" \ + "set_rootfs_type=if test $rootfs_type = 'ramdisk'; then " \ + "setenv bootargs ${bootargs} root=/dev/ram rw ramdisk_size=${ramdisksize}; " \ + "else if test $rootfs_type = 'jffs'; then " \ + "setenv bootargs ${bootargs} rw rootfstype=jffs2;" \ + "else if test $rootfs_type = 'yaffs'; then " \ + "setenv bootargs ${bootargs} rw rootfstype=yaffs2;" \ + "else if test $rootfs_type = 'ext3'; then " \ + "setenv bootargs ${bootargs} rw rootfstype=ext3 rootwait; " \ + "else if test $rootfs_type = 'nfs'; then " \ + "setenv bootargs ${bootargs} rw nfsroot=${serverip}:${nfsrootpath}${nfsoptions} ip=dhcp; " \ + "else "\ + "echo \"$rootfs_type must be set to ramdisk, jffs, yaffs, ext3, or nfs\"; " \ + "echo \"\"; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "fi " \ + "\0" \ + "addmtdparts=setenv bootargs ${bootargs} ${mtdparts} \0" \ + "common_bootargs=" \ + " setenv bootargs ${bootargs} display=${display} ${otherbootargs}; " \ + " run addmtdparts; " \ + " run vrfb_arg; " \ + " \0" \ + "dump_run_bootm=" \ + " echo \"bootm $bootm_arg1 $bootm_arg2\"; " \ + " echo \"\"; " \ + " bootm $bootm_arg1 $bootm_arg2\0" \ + "defaultboot=" \ + " run dump_boot_sources; " \ + " run setconsole; setenv bootargs console=${console}; " \ + " run common_bootargs; " \ + " run load_kernel; " \ + " run load_rootfs; " \ + " run set_rootfs_type; " \ + " run dump_bootargs; " \ + " run dump_run_bootm; " \ + "\0" + + +#define CONFIG_AUTO_COMPLETE 1 +/* + * Miscellaneous configurable options + */ +#define CONFIG_SYS_LONGHELP /* undef to save memory */ +#define CONFIG_SYS_HUSH_PARSER /* use "hush" command parser */ +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " +#define CONFIG_SYS_PROMPT "OMAP Logic # " +#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ +/* Print Buffer Size */ +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ + sizeof(CONFIG_SYS_PROMPT) + 16) +#define CONFIG_SYS_MAXARGS 64 /* max number of command */ + /* args */ +/* Boot Argument Buffer Size */ +#define CONFIG_SYS_BARGSIZE (CONFIG_SYS_CBSIZE) +/* memtest works on */ +#define CONFIG_SYS_MEMTEST_START (OMAP34XX_SDRC_CS0) +#define CONFIG_SYS_MEMTEST_END (OMAP34XX_SDRC_CS0 + \ + 0x01F00000) /* 31MB */ + +#define CONFIG_SYS_LOAD_ADDR (OMAP34XX_SDRC_CS0) /* default load */ + /* address */ + +/* + * OMAP3 has 12 GP timers, they can be driven by the system clock + * (12/13/16.8/19.2/38.4MHz) or by 32KHz clock. We use 13MHz (V_SCLK). + * This rate is divided by a local divisor. + */ +#define CONFIG_SYS_TIMERBASE OMAP34XX_GPT2 +#define CONFIG_SYS_PTV 2 /* Divisor: 2^(PTV+1) => 8 */ +#define CONFIG_SYS_HZ 1000 + +/*----------------------------------------------------------------------- + * Stack sizes + * + * The stack sizes are set up in start.S using the settings below + */ +#define CONFIG_STACKSIZE (128 << 10) /* regular stack 128 KiB */ +#ifdef CONFIG_USE_IRQ +#define CONFIG_STACKSIZE_IRQ (4 << 10) /* IRQ stack 4 KiB */ +#define CONFIG_STACKSIZE_FIQ (4 << 10) /* FIQ stack 4 KiB */ +#endif + +/*----------------------------------------------------------------------- + * Physical Memory Map + */ +#define CONFIG_NR_DRAM_BANKS 2 /* CS1 may or may not be populated */ +#define PHYS_SDRAM_1 OMAP34XX_SDRC_CS0 +#define PHYS_SDRAM_1_SIZE (32 << 20) /* at least 32 MiB */ +#define PHYS_SDRAM_2 OMAP34XX_SDRC_CS1 + +/* SDRAM Bank Allocation method */ +#define SDRC_R_B_C 1 + +/*----------------------------------------------------------------------- + * FLASH and environment organization + */ + +/* variable that's non-zero if flash exists */ +#define CONFIG_SYS_FLASH_PRESENCE omap3logic_flash_exists +#ifndef __ASSEMBLY__ +extern int omap3logic_flash_exists; +#endif +#define CONFIG_SYS_FLASH_BASE 0x10000000 /* FLASH base address */ +#define CONFIG_SYS_FLASH_SIZE 8 /* 8MB */ +#define CONFIG_SYS_MAX_FLASH_SECT (64+8) /* 8MB/128K */ +#define CONFIG_SYS_MAX_FLASH_BANKS 1 +#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE +#undef CONFIG_SYS_FLASH_CHECKSUM +#define CONFIG_SYS_FLASH_CFI /* use the Common Flash Interface */ +#define CONFIG_FLASH_CFI_DRIVER /* use the CFI driver */ + + +/* **** PISMO SUPPORT *** */ + +/* Configure the PISMO */ +#define PISMO1_NAND_SIZE GPMC_SIZE_128M + +#define CONFIG_SYS_MONITOR_LEN (256 << 10) /* Reserve 2 sectors */ + +/* Monitor at start of flash */ +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE + +#define SMNAND_ENV_OFFSET 0x220000 /* environment starts here */ + +#if defined(CONFIG_CMD_NAND) +#define CONFIG_NAND_OMAP_GPMC +#define CONFIG_MTD_NAND_BCH +#define CONFIG_MTD_NAND_ECC_BCH +#define CONFIG_BCH +#define GPMC_NAND_ECC_LP_x16_LAYOUT 1 +#define CONFIG_ENV_IS_IN_NAND +#define CONFIG_ENV_OFFSET SMNAND_ENV_OFFSET +#endif + +#define CONFIG_ENV_ADDR CONFIG_ENV_OFFSET +#define CONFIG_ENV_RANGE (3 * CONFIG_ENV_SIZE) + +/* + * Support for relocation + */ +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 +#define CONFIG_SYS_INIT_RAM_ADDR 0x4020f800 +#define CONFIG_SYS_INIT_RAM_SIZE 0x800 +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_INIT_RAM_ADDR + \ + CONFIG_SYS_INIT_RAM_SIZE - \ + GENERATED_GBL_DATA_SIZE) + +/* + * Define the board revision statically + */ +/* #define CONFIG_STATIC_BOARD_REV OMAP3EVM_BOARD_GEN_2 */ + +/*---------------------------------------------------------------------------- + * SMSC9115 Ethernet from SMSC9118 family + *---------------------------------------------------------------------------- + */ +#if defined(CONFIG_CMD_NET) + +#define CONFIG_NET_MULTI +#define CONFIG_SMC911X +#define CONFIG_SMC911X_16_BIT +#define CONFIG_SMC911X_BASE 0x08000000 + +#endif /* (CONFIG_CMD_NET) */ + +/* + * BOOTP fields + */ + +#define CONFIG_BOOTP_SUBNETMASK 0x00000001 +#define CONFIG_BOOTP_GATEWAY 0x00000002 +#define CONFIG_BOOTP_HOSTNAME 0x00000004 +#define CONFIG_BOOTP_BOOTPATH 0x00000010 + +/* Add graphics support */ +#define CONFIG_VIDEO_OMAP3 +#define CONFIG_LCD +#define LCD_BPP LCD_COLOR16 +#define CONFIG_CMD_BMP +#define CONFIG_BMP_16BPP +#define CONFIG_VIDEO_BMP_GZIP +#define CONFIG_SYS_VIDEO_LOGO_MAX_SIZE (2 << 20) +#define CONFIG_SPLASH_SCREEN +#define CONFIG_SPLASH_SCREEN_ALIGN 1 +#define CONFIG_SYS_WHITE_ON_BLACK /* White characters on black background */ + +/* Have board_set_lcdmem() function */ +#define CONFIG_BOARD_LCD_SETMEM + +/* Have percent ouptut function for LCD */ +#define CONFIG_LCD_PERCENT + +#endif /* __CONFIG_H */ diff --git a/include/environment.h b/include/environment.h index 53d92df1f29..5180320b62a 100644 --- a/include/environment.h +++ b/include/environment.h @@ -172,6 +172,9 @@ void set_default_env(const char *s); /* Import from binary representation into hash table */ int env_import(const char *buf, int check); +/* Check a read environment to see if its valid */ +int env_check_valid(const char *buf); + #endif #endif /* _ENVIRONMENT_H_ */ diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index 906eb3d3cdb..7d549d957fe 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -77,7 +77,7 @@ struct mtdids { /* common/cmd_jffs2.c */ extern int mtdparts_init(void); extern int find_dev_and_part(const char *id, struct mtd_device **dev, - u8 *part_num, struct part_info **part); + u8 *part_num, struct part_info **part, int quiet); extern struct mtd_device *device_find(u8 type, u8 num); #endif /* load_kernel_h */ diff --git a/include/lcd.h b/include/lcd.h index 0e098d925ec..f01cba71810 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -34,6 +34,7 @@ extern char lcd_is_enabled; extern int lcd_line_length; extern int lcd_color_fg; extern int lcd_color_bg; +extern int console_color_black, console_color_white; /* * Frame buffer memory information @@ -235,6 +236,7 @@ void lcd_show_board_info(void); #define LCD_COLOR4 2 #define LCD_COLOR8 3 #define LCD_COLOR16 4 +#define LCD_COLOR24 5 /*----------------------------------------------------------------------*/ #if defined(CONFIG_LCD_INFO_BELOW_LOGO) @@ -261,42 +263,6 @@ void lcd_show_board_info(void); #define NCOLORS(bit_code) (1 << NBITS(bit_code)) /************************************************************************/ -/* ** CONSOLE CONSTANTS */ -/************************************************************************/ -#if LCD_BPP == LCD_MONOCHROME - -/* - * Simple black/white definitions - */ -# define CONSOLE_COLOR_BLACK 0 -# define CONSOLE_COLOR_WHITE 1 /* Must remain last / highest */ - -#elif LCD_BPP == LCD_COLOR8 - -/* - * 8bpp color definitions - */ -# define CONSOLE_COLOR_BLACK 0 -# define CONSOLE_COLOR_RED 1 -# define CONSOLE_COLOR_GREEN 2 -# define CONSOLE_COLOR_YELLOW 3 -# define CONSOLE_COLOR_BLUE 4 -# define CONSOLE_COLOR_MAGENTA 5 -# define CONSOLE_COLOR_CYAN 6 -# define CONSOLE_COLOR_GREY 14 -# define CONSOLE_COLOR_WHITE 15 /* Must remain last / highest */ - -#else - -/* - * 16bpp color definitions - */ -# define CONSOLE_COLOR_BLACK 0x0000 -# define CONSOLE_COLOR_WHITE 0xffff /* Must remain last / highest */ - -#endif /* color definitions */ - -/************************************************************************/ #ifndef PAGE_SIZE # define PAGE_SIZE 4096 #endif diff --git a/include/linux/bch.h b/include/linux/bch.h new file mode 100644 index 00000000000..295b4ef153b --- /dev/null +++ b/include/linux/bch.h @@ -0,0 +1,79 @@ +/* + * Generic binary BCH encoding/decoding library + * + * 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. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic <ivan.djelic@parrot.com> + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. +*/ +#ifndef _BCH_H +#define _BCH_H + +#include <linux/types.h> + +/** + * struct bch_control - BCH control structure + * @m: Galois field order + * @n: maximum codeword size in bits (= 2^m-1) + * @t: error correction capability in bits + * @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) + * @ecc_bytes: ecc max size (m*t bits) in bytes + * @a_pow_tab: Galois field GF(2^m) exponentiation lookup table + * @a_log_tab: Galois field GF(2^m) log lookup table + * @mod8_tab: remainder generator polynomial lookup tables + * @ecc_buf: ecc parity words buffer + * @ecc_buf2: ecc parity words buffer + * @xi_tab: GF(2^m) base for solving degree 2 polynomial roots + * @syn: syndrome buffer + * @cache: log-based polynomial representation buffer + * @elp: error locator polynomial + * @poly_2t: temporary polynomials of degree 2t + */ +struct bch_control { + unsigned int m; + unsigned int n; + unsigned int t; + unsigned int ecc_bits; + unsigned int ecc_bytes; +/* private: */ + uint16_t *a_pow_tab; + uint16_t *a_log_tab; + uint32_t *mod8_tab; + uint32_t *ecc_buf; + uint32_t *ecc_buf2; + unsigned int *xi_tab; + unsigned int *syn; + int *cache; + struct gf_poly *elp; + struct gf_poly *poly_2t[4]; +}; + +struct bch_control *init_bch(int m, int t, unsigned int prim_poly); + +void free_bch(struct bch_control *bch); + +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc); + +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc); + +#endif /* _BCH_H */ diff --git a/include/linux/mtd/compat.h b/include/linux/mtd/compat.h index 39c693f7a8c..8727a36707d 100644 --- a/include/linux/mtd/compat.h +++ b/include/linux/mtd/compat.h @@ -17,11 +17,21 @@ #define KERN_INFO #define KERN_DEBUG +#ifndef kmalloc #define kmalloc(size, flags) malloc(size) +#endif +#ifndef kzalloc #define kzalloc(size, flags) calloc(size, 1) +#endif +#ifndef vmalloc #define vmalloc(size) malloc(size) +#endif +#ifndef kfree #define kfree(ptr) free(ptr) +#endif +#ifndef vfree #define vfree(ptr) free(ptr) +#endif #define DECLARE_WAITQUEUE(...) do { } while (0) #define add_wait_queue(...) do { } while (0) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 3b18d7d6886..74ef50737f8 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -293,11 +293,13 @@ static inline void mtd_erase_callback(struct erase_info *instr) #define MTD_DEBUG_LEVEL1 (1) /* Audible */ #define MTD_DEBUG_LEVEL2 (2) /* Loud */ #define MTD_DEBUG_LEVEL3 (3) /* Noisy */ +#define MTD_DEBUG_LEVEL4 (4) /* Picky */ #ifdef CONFIG_MTD_DEBUG +extern int mtd_debug_verbose; #define MTDDEBUG(n, args...) \ do { \ - if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ + if (n <= mtd_debug_verbose) \ printk(KERN_INFO args); \ } while(0) #else /* CONFIG_MTD_DEBUG */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 987a2ec85d1..d2032b47c50 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -88,6 +88,8 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_READID 0x90 #define NAND_CMD_PARAM 0xec #define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_SETFEATURE 0xee +#define NAND_CMD_GETFEATURE 0xef #define NAND_CMD_RESET 0xff /* Extended commands for large page devices */ @@ -95,6 +97,11 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 +/* Extended commands for ONFI devices */ +#define NAND_CMD_READ_PARAM 0xec +#define NAND_CMD_GET_FEATURES 0xee +#define NAND_CMD_SET_FEATURES 0xef + /* Extended commands for AG-AND device */ /* * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but @@ -132,6 +139,9 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, + NAND_ECC_CHIP, /* ECC hardware in-chip */ + NAND_ECC_SOFT_BCH, /* Soft BCH ECC engine */ + NAND_ECC_HW_BCH, /* If NAND uses soft-BCH instead of ECC_CHIP */ } nand_ecc_modes_t; /* @@ -308,6 +318,7 @@ struct nand_hw_control { * @prepad: padding information for syndrome based ecc generators * @postpad: padding information for syndrome based ecc generators * @layout: ECC layout control struct pointer + * @priv: pointer to private ecc control data * @hwctl: function to control hardware ecc generator. Must only * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware @@ -328,6 +339,7 @@ struct nand_ecc_ctrl { int prepad; int postpad; struct nand_ecclayout *layout; + void *priv; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, @@ -438,6 +450,10 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; + uint8_t maf_id, dev_id; /* manufacturer/device identifier */ + uint8_t has_chip_ecc; /* !0 if chip has intern ECC engine */ + uint8_t ecc_status; /* status of read w/ECC */ + uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); @@ -623,4 +639,20 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) return chip->priv; } +extern int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page); +extern int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi); +extern void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf); +extern int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page); +extern void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf); +extern int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd); +extern int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page); + + #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h new file mode 100644 index 00000000000..74acf536755 --- /dev/null +++ b/include/linux/mtd/nand_bch.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + * + * 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. + * + * This file is the header for the NAND BCH ECC implementation. + */ + +#ifndef __MTD_NAND_BCH_H__ +#define __MTD_NAND_BCH_H__ + +struct mtd_info; +struct nand_bch_control; + +#if defined(CONFIG_MTD_NAND_ECC_BCH) + +static inline int mtd_nand_has_bch(void) { return 1; } + +/* + * Calculate BCH ecc code + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code); + +/* + * Detect and correct bit errors + */ +int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, + u_char *calc_ecc); +/* + * Initialize BCH encoder/decoder + */ +struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, + unsigned int eccbytes, struct nand_ecclayout **ecclayout); +/* + * Release BCH encoder/decoder resources + */ +void nand_bch_free(struct nand_bch_control *nbc); + +#else /* !CONFIG_MTD_NAND_ECC_BCH */ + +static inline int mtd_nand_has_bch(void) { return 0; } + +static inline int +nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return -1; +} + +static inline int +nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + return -1; +} + +static inline struct nand_bch_control * +nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, + unsigned int eccbytes, struct nand_ecclayout **ecclayout) +{ + return NULL; +} + +static inline void nand_bch_free(struct nand_bch_control *nbc) {} + +#endif /* CONFIG_MTD_NAND_ECC_BCH */ + +#endif /* __MTD_NAND_BCH_H__ */ diff --git a/include/mtd_parts.h b/include/mtd_parts.h new file mode 100644 index 00000000000..87e2882c697 --- /dev/null +++ b/include/mtd_parts.h @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2011 + * Logic Product Devleopemnt <www.logicpd.com> + * Peter Barada <peter.barada@logicpd.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 + * version 2 as published by the Free Software Foundation. + * + * 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 __MTD_PARTS_H__ +#define __MTD_PARTS_H__ +extern int mtd_get_part_priv(const char *partname, int *idx, + struct mtd_device **dev, loff_t *off, + loff_t *size, void **cookie, void **priv, int quiet); + +extern void mtd_set_part_priv(void *cookie, void *priv); + +extern int mtd_id_parse(const char *id, const char **ret_id, + u8 *dev_type, u8 *dev_num, int quiet); +extern int mtdparts_init(void); +#endif diff --git a/include/nand.h b/include/nand.h index 7459bd0330a..f7b0a60b8ab 100644 --- a/include/nand.h +++ b/include/nand.h @@ -25,6 +25,7 @@ #define _NAND_H_ extern void nand_init(void); +extern int nand_set_dev(int idx); #include <linux/mtd/compat.h> #include <linux/mtd/mtd.h> @@ -126,6 +127,9 @@ int nand_lock( nand_info_t *meminfo, int tight ); int nand_unlock( nand_info_t *meminfo, ulong start, ulong length ); int nand_get_lock_status(nand_info_t *meminfo, loff_t offset); +int nand_get_features( nand_info_t *meminfo, uint8_t faddr, uint8_t *features); +int nand_set_features( nand_info_t *meminfo, uint8_t faddr, uint8_t *features); + #ifdef CONFIG_SYS_NAND_SELECT_DEVICE void board_nand_select_device(struct nand_chip *nand, int chip); #endif diff --git a/include/twl4030.h b/include/twl4030.h index 930c285c26f..6d5bb1553ac 100644 --- a/include/twl4030.h +++ b/include/twl4030.h @@ -356,6 +356,22 @@ #define TWL4030_KEYPAD_CTRL_SOFTMODEN (1 << 1) #define TWL4030_KEYPAD_CTRL_SOFT_NRST (1 << 0) +/* Main charge */ +#define TWL4030_MAIN_CHARGE_BCIMFKEY 0x85 +#define TWL4030_MAIN_CHARGE_BCIMFEN1 0x86 +#define TWL4030_MAIN_CHARGE_BCIMFTH1 0x8A + +#define TWL4030_MAIN_CHARGE_BCIMFKEY_MFKEY1 0x57 +#define TWL4030_MAIN_CHARGE_BCIMFKEY_MFKEY5 0xD2 +#define TWL4030_MAIN_CHARGE_BCIMFEN1_VBATOV1EN (1 << 7) +#define TWL4030_MAIN_CHARGE_BCIMFTH1_VBATOV1TH_MASK 0x0F +#define TWL4030_MAIN_CHARGE_BCIMFTH1_VBATOV1TH_2636_mV 0x00 + +/* INTBR */ +#define TWL4030_INTBR_GPBR1 0x91 +#define TWL4030_INTBR_GPBR1_MADC_HFCLK_EN 0x80 +#define TWL4030_INTBR_GPBR1_DEFAULT_MADC_CLK_EN 0x10 + /* USB */ #define TWL4030_USB_VENDOR_ID_LO 0x00 #define TWL4030_USB_VENDOR_ID_HI 0x01 @@ -482,6 +498,58 @@ #define TWL4030_USB_PHY_CLK_CTRL_STS 0xFF /* + * GPIO Block Register offsets (use TWL4030_CHIP_GPIO) + */ + +#define REG_GPIODATAIN1 0x0 +#define REG_GPIODATAIN2 0x1 +#define REG_GPIODATAIN3 0x2 +#define REG_GPIODATADIR1 0x3 +#define REG_GPIODATADIR2 0x4 +#define REG_GPIODATADIR3 0x5 +#define REG_GPIODATAOUT1 0x6 +#define REG_GPIODATAOUT2 0x7 +#define REG_GPIODATAOUT3 0x8 +#define REG_CLEARGPIODATAOUT1 0x9 +#define REG_CLEARGPIODATAOUT2 0xA +#define REG_CLEARGPIODATAOUT3 0xB +#define REG_SETGPIODATAOUT1 0xC +#define REG_SETGPIODATAOUT2 0xD +#define REG_SETGPIODATAOUT3 0xE +#define REG_GPIO_DEBEN1 0xF +#define REG_GPIO_DEBEN2 0x10 +#define REG_GPIO_DEBEN3 0x11 +#define REG_GPIO_CTRL 0x12 +#define REG_GPIOPUPDCTR1 0x13 +#define REG_GPIOPUPDCTR2 0x14 +#define REG_GPIOPUPDCTR3 0x15 +#define REG_GPIOPUPDCTR4 0x16 +#define REG_GPIOPUPDCTR5 0x17 +#define REG_GPIO_ISR1A 0x19 +#define REG_GPIO_ISR2A 0x1A +#define REG_GPIO_ISR3A 0x1B +#define REG_GPIO_IMR1A 0x1C +#define REG_GPIO_IMR2A 0x1D +#define REG_GPIO_IMR3A 0x1E +#define REG_GPIO_ISR1B 0x1F +#define REG_GPIO_ISR2B 0x20 +#define REG_GPIO_ISR3B 0x21 +#define REG_GPIO_IMR1B 0x22 +#define REG_GPIO_IMR2B 0x23 +#define REG_GPIO_IMR3B 0x24 +#define REG_GPIO_EDR1 0x28 +#define REG_GPIO_EDR2 0x29 +#define REG_GPIO_EDR3 0x2A +#define REG_GPIO_EDR4 0x2B +#define REG_GPIO_EDR5 0x2C +#define REG_GPIO_SIH_CTRL 0x2D + +/* Up to 18 signals are available as GPIOs, when their + * pins are not assigned to another use (such as ULPI/USB). + */ +#define TWL4030_GPIO_MAX 18 + +/* * Convience functions to read and write from TWL4030 * * chip_no is the i2c address, it must be one of the chip addresses @@ -511,6 +579,8 @@ static inline int twl4030_i2c_read_u8(u8 chip_no, u8 *val, u8 reg) /* For hardware resetting */ void twl4030_power_reset_init(void); +/* For power down */ +void twl4030_power_off(void); /* For setting device group and voltage */ void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val, u8 dev_grp, u8 dev_grp_sel); @@ -518,6 +588,8 @@ void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val, void twl4030_power_init(void); /* For initializing mmc power */ void twl4030_power_mmc_init(void); +/* For charging */ +int twl4030_enable_charging(void); /* * LED @@ -529,4 +601,19 @@ void twl4030_led_init(unsigned char ledon_mask); */ int twl4030_usb_ulpi_init(void); +/* + * GPIO + */ +extern int twl4030_set_gpio_direction(unsigned int gpio, unsigned int is_input); +extern int twl4030_set_gpio_dataout(unsigned int gpio, unsigned int enable); +extern int twl4030_get_gpio_datain(unsigned int gpio); +extern int twl4030_request_gpio(unsigned int gpio); +extern void twl4030_free_gpio(unsigned int gpio); + +/* + * PWM + */ +extern int twl4030_set_pwm0(int level, int max_brightness); +extern void twl4030_dump_pwm0(void); + #endif /* TWL4030_H */ diff --git a/lib/Makefile b/lib/Makefile index afa6914e1be..71603bf82e7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o COBJS-$(CONFIG_USB_TTY) += circbuf.o +COBJS-$(CONFIG_BCH) += bch.o COBJS-y += crc7.o COBJS-y += crc16.o COBJS-y += crc32.o diff --git a/lib/bch.c b/lib/bch.c new file mode 100644 index 00000000000..8a41fb8cb65 --- /dev/null +++ b/lib/bch.c @@ -0,0 +1,1409 @@ +/* + * Generic binary BCH encoding/decoding library + * + * 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. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright © 2011 Parrot S.A. + * + * Author: Ivan Djelic <ivan.djelic@parrot.com> + * + * Description: + * + * This library provides runtime configurable encoding/decoding of binary + * Bose-Chaudhuri-Hocquenghem (BCH) codes. + * + * Call init_bch to get a pointer to a newly allocated bch_control structure for + * the given m (Galois field order), t (error correction capability) and + * (optional) primitive polynomial parameters. + * + * Call encode_bch to compute and store ecc parity bytes to a given buffer. + * Call decode_bch to detect and locate errors in received data. + * + * On systems supporting hw BCH features, intermediate results may be provided + * to decode_bch in order to skip certain steps. See decode_bch() documentation + * for details. + * + * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of + * parameters m and t; thus allowing extra compiler optimizations and providing + * better (up to 2x) encoding performance. Using this option makes sense when + * (m,t) are fixed and known in advance, e.g. when using BCH error correction + * on a particular NAND flash device. + * + * Algorithmic details: + * + * Encoding is performed by processing 32 input bits in parallel, using 4 + * remainder lookup tables. + * + * The final stage of decoding involves the following internal steps: + * a. Syndrome computation + * b. Error locator polynomial computation using Berlekamp-Massey algorithm + * c. Error locator root finding (by far the most expensive step) + * + * In this implementation, step c is not performed using the usual Chien search. + * Instead, an alternative approach described in [1] is used. It consists in + * factoring the error locator polynomial using the Berlekamp Trace algorithm + * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial + * solving techniques [2] are used. The resulting algorithm, called BTZ, yields + * much better performance than Chien search for usual (m,t) values (typically + * m >= 13, t < 32, see [1]). + * + * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields + * of characteristic 2, in: Western European Workshop on Research in Cryptology + * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. + * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over + * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. + */ + +#if 1 +#if 1 +#include <config.h> +#include <common.h> +#include <linux/mtd/compat.h> +#include <malloc.h> +#include <errno.h> +#endif +// #include <linux/kernel.h> +// #include <linux/errno.h> +// #include <linux/init.h> +// #include <linux/module.h> +// #include <linux/slab.h> +#include <linux/bitops.h> +#include <asm/byteorder.h> +#include <linux/bch.h> +#else +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <asm/byteorder.h> +#include <linux/bch.h> +#endif + +#if defined(CONFIG_BCH_CONST_PARAMS) +#define GF_M(_p) (CONFIG_BCH_CONST_M) +#define GF_T(_p) (CONFIG_BCH_CONST_T) +#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) +#else +#define GF_M(_p) ((_p)->m) +#define GF_T(_p) ((_p)->t) +#define GF_N(_p) ((_p)->n) +#endif + +#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) +#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) + +#ifdef DEBUG_BCH +#define dbg(args...) printf(args); +#define gf_poly_str(x) "x" +#else +#ifndef dbg +#define dbg(_fmt, args...) do {} while (0) +#endif +#endif + +/* + * represent a polynomial over GF(2^m) + */ +struct gf_poly { + unsigned int deg; /* polynomial degree */ + unsigned int c[0]; /* polynomial terms */ +}; + +/* given its degree, compute a polynomial size in bytes */ +#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) + +/* polynomial of degree 1 */ +struct gf_poly_deg1 { + struct gf_poly poly; + unsigned int c[2]; +}; + +/* + * same as encode_bch(), but process input data one byte at a time + */ +static void encode_bch_unaligned(struct bch_control *bch, + const unsigned char *data, unsigned int len, + uint32_t *ecc) +{ + int i; + const uint32_t *p; + const int l = BCH_ECC_WORDS(bch)-1; + + while (len--) { + p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); + + for (i = 0; i < l; i++) + ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); + + ecc[l] = (ecc[l] << 8)^(*p); + } +} + +/* + * convert ecc bytes to aligned, zero-padded 32-bit ecc words + */ +static void load_ecc8(struct bch_control *bch, uint32_t *dst, + const uint8_t *src) +{ + uint8_t pad[4] = {0, 0, 0, 0}; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++, src += 4) + dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; + + memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); + dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; +} + +/* + * convert 32-bit ecc words to ecc bytes + */ +static void store_ecc8(struct bch_control *bch, uint8_t *dst, + const uint32_t *src) +{ + uint8_t pad[4]; + unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; + + for (i = 0; i < nwords; i++) { + *dst++ = (src[i] >> 24); + *dst++ = (src[i] >> 16) & 0xff; + *dst++ = (src[i] >> 8) & 0xff; + *dst++ = (src[i] >> 0) & 0xff; + } + pad[0] = (src[nwords] >> 24); + pad[1] = (src[nwords] >> 16) & 0xff; + pad[2] = (src[nwords] >> 8) & 0xff; + pad[3] = (src[nwords] >> 0) & 0xff; + memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); +} + +/** + * encode_bch - calculate BCH ecc parity of data + * @bch: BCH control structure + * @data: data to encode + * @len: data length in bytes + * @ecc: ecc parity data, must be initialized by caller + * + * The @ecc parity array is used both as input and output parameter, in order to + * allow incremental computations. It should be of the size indicated by member + * @ecc_bytes of @bch, and should be initialized to 0 before the first call. + * + * The exact number of computed ecc parity bits is given by member @ecc_bits of + * @bch; it may be less than m*t for large values of t. + */ +void encode_bch(struct bch_control *bch, const uint8_t *data, + unsigned int len, uint8_t *ecc) +{ + const unsigned int l = BCH_ECC_WORDS(bch)-1; + unsigned int i, mlen; + unsigned long m; + uint32_t w, r[l+1]; + const uint32_t * const tab0 = bch->mod8_tab; + const uint32_t * const tab1 = tab0 + 256*(l+1); + const uint32_t * const tab2 = tab1 + 256*(l+1); + const uint32_t * const tab3 = tab2 + 256*(l+1); + const uint32_t *pdata, *p0, *p1, *p2, *p3; + + if (ecc) { + /* load ecc parity bytes into internal 32-bit buffer */ + load_ecc8(bch, bch->ecc_buf, ecc); + } else { + memset(bch->ecc_buf, 0, sizeof(r)); + } + + /* process first unaligned data bytes */ + m = ((unsigned long)data) & 3; + if (m) { + mlen = (len < (4-m)) ? len : 4-m; + encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); + data += mlen; + len -= mlen; + } + + /* process 32-bit aligned data words */ + pdata = (uint32_t *)data; + mlen = len/4; + data += 4*mlen; + len -= 4*mlen; + memcpy(r, bch->ecc_buf, sizeof(r)); + + /* + * split each 32-bit word into 4 polynomials of weight 8 as follows: + * + * 31 ...24 23 ...16 15 ... 8 7 ... 0 + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt + * tttttttt mod g = r0 (precomputed) + * zzzzzzzz 00000000 mod g = r1 (precomputed) + * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) + * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) + * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 + */ + while (mlen--) { + /* input data is read in big-endian format */ + w = r[0]^cpu_to_be32(*pdata++); + p0 = tab0 + (l+1)*((w >> 0) & 0xff); + p1 = tab1 + (l+1)*((w >> 8) & 0xff); + p2 = tab2 + (l+1)*((w >> 16) & 0xff); + p3 = tab3 + (l+1)*((w >> 24) & 0xff); + + for (i = 0; i < l; i++) + r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; + + r[l] = p0[l]^p1[l]^p2[l]^p3[l]; + } + memcpy(bch->ecc_buf, r, sizeof(r)); + + /* process last unaligned bytes */ + if (len) + encode_bch_unaligned(bch, data, len, bch->ecc_buf); + + /* store ecc parity bytes into original parity buffer */ + if (ecc) + store_ecc8(bch, ecc, bch->ecc_buf); +} +// EXPORT_SYMBOL_GPL(encode_bch); + +static inline int modulo(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + while (v >= n) { + v -= n; + v = (v & n) + (v >> GF_M(bch)); + } + return v; +} + +/* + * shorter and faster modulo function, only works when v < 2N. + */ +static inline int mod_s(struct bch_control *bch, unsigned int v) +{ + const unsigned int n = GF_N(bch); + return (v < n) ? v : v-n; +} + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly)-1; +} + +static inline int parity(unsigned int x) +{ + /* + * public domain code snippet, lifted from + * http://www-graphics.stanford.edu/~seander/bithacks.html + */ + x ^= x >> 1; + x ^= x >> 2; + x = (x & 0x11111111U) * 0x11111111U; + return (x >> 28) & 1; +} + +/* Galois field basic operations: multiply, divide, inverse, etc. */ + +static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) +{ + return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; +} + +static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, + unsigned int b) +{ + return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ + GF_N(bch)-bch->a_log_tab[b])] : 0; +} + +static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) +{ + return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; +} + +static inline unsigned int a_pow(struct bch_control *bch, int i) +{ + return bch->a_pow_tab[modulo(bch, i)]; +} + +static inline int a_log(struct bch_control *bch, unsigned int x) +{ + return bch->a_log_tab[x]; +} + +static inline int a_ilog(struct bch_control *bch, unsigned int x) +{ + return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); +} + +/* + * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t + */ +static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, + unsigned int *syn) +{ + int i, j, s; + unsigned int m; + uint32_t poly; + const int t = GF_T(bch); + + s = bch->ecc_bits; + + /* make sure extra bits in last ecc word are cleared */ + m = ((unsigned int)s) & 31; + if (m) + ecc[s/32] &= ~((1u << (32-m))-1); + memset(syn, 0, 2*t*sizeof(*syn)); + + /* compute v(a^j) for j=1 .. 2t-1 */ + do { + poly = *ecc++; + s -= 32; + while (poly) { + i = deg(poly); + for (j = 0; j < 2*t; j += 2) + syn[j] ^= a_pow(bch, (j+1)*(i+s)); + + poly ^= (1 << i); + } + } while (s > 0); + + /* v(a^(2j)) = v(a^j)^2 */ + for (j = 0; j < t; j++) + syn[2*j+1] = gf_sqr(bch, syn[j]); +} + +static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) +{ + memcpy(dst, src, GF_POLY_SZ(src->deg)); +} + +static int compute_error_locator_polynomial(struct bch_control *bch, + const unsigned int *syn) +{ + const unsigned int t = GF_T(bch); + const unsigned int n = GF_N(bch); + unsigned int i, j, tmp, l, pd = 1, d = syn[0]; + struct gf_poly *elp = bch->elp; + struct gf_poly *pelp = bch->poly_2t[0]; + struct gf_poly *elp_copy = bch->poly_2t[1]; + int k, pp = -1; + + memset(pelp, 0, GF_POLY_SZ(2*t)); + memset(elp, 0, GF_POLY_SZ(2*t)); + + pelp->deg = 0; + pelp->c[0] = 1; + elp->deg = 0; + elp->c[0] = 1; + + /* use simplified binary Berlekamp-Massey algorithm */ + for (i = 0; (i < t) && (elp->deg <= t); i++) { + if (d) { + k = 2*i-pp; + gf_poly_copy(elp_copy, elp); + /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ + tmp = a_log(bch, d)+n-a_log(bch, pd); + for (j = 0; j <= pelp->deg; j++) { + if (pelp->c[j]) { + l = a_log(bch, pelp->c[j]); + elp->c[j+k] ^= a_pow(bch, tmp+l); + } + } + /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ + tmp = pelp->deg+k; + if (tmp > elp->deg) { + elp->deg = tmp; + gf_poly_copy(pelp, elp_copy); + pd = d; + pp = 2*i; + } + } + /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ + if (i < t-1) { + d = syn[2*i+2]; + for (j = 1; j <= elp->deg; j++) + d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); + } + } + dbg("elp=%s\n", gf_poly_str(elp)); + return (elp->deg > t) ? -1 : (int)elp->deg; +} + +/* + * solve a m x m linear system in GF(2) with an expected number of solutions, + * and return the number of found solutions + */ +static int solve_linear_system(struct bch_control *bch, unsigned int *rows, + unsigned int *sol, int nsol) +{ + const int m = GF_M(bch); + unsigned int tmp, mask; + int rem, c, r, p, k, param[m]; + + k = 0; + mask = 1 << m; + + /* Gaussian elimination */ + for (c = 0; c < m; c++) { + rem = 0; + p = c-k; + /* find suitable row for elimination */ + for (r = p; r < m; r++) { + if (rows[r] & mask) { + if (r != p) { + tmp = rows[r]; + rows[r] = rows[p]; + rows[p] = tmp; + } + rem = r+1; + break; + } + } + if (rem) { + /* perform elimination on remaining rows */ + tmp = rows[p]; + for (r = rem; r < m; r++) { + if (rows[r] & mask) + rows[r] ^= tmp; + } + } else { + /* elimination not needed, store defective row index */ + param[k++] = c; + } + mask >>= 1; + } + /* rewrite system, inserting fake parameter rows */ + if (k > 0) { + p = k; + for (r = m-1; r >= 0; r--) { + if ((r > m-1-k) && rows[r]) + /* system has no solution */ + return 0; + + rows[r] = (p && (r == param[p-1])) ? + p--, 1u << (m-r) : rows[r-p]; + } + } + + if (nsol != (1 << k)) + /* unexpected number of solutions */ + return 0; + + for (p = 0; p < nsol; p++) { + /* set parameters for p-th solution */ + for (c = 0; c < k; c++) + rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); + + /* compute unique solution */ + tmp = 0; + for (r = m-1; r >= 0; r--) { + mask = rows[r] & (tmp|1); + tmp |= parity(mask) << (m-r); + } + sol[p] = tmp >> 1; + } + return nsol; +} + +/* + * this function builds and solves a linear system for finding roots of a degree + * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). + */ +static int find_affine4_roots(struct bch_control *bch, unsigned int a, + unsigned int b, unsigned int c, + unsigned int *roots) +{ + int i, j, k; + const int m = GF_M(bch); + unsigned int mask = 0xff, t, rows[16] = {0,}; + + j = a_log(bch, b); + k = a_log(bch, a); + rows[0] = c; + + /* buid linear system to solve X^4+aX^2+bX+c = 0 */ + for (i = 0; i < m; i++) { + rows[i+1] = bch->a_pow_tab[4*i]^ + (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ + (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); + j++; + k += 2; + } + /* + * transpose 16x16 matrix before passing it to linear solver + * warning: this code assumes m < 16 + */ + for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { + for (k = 0; k < 16; k = (k+j+1) & ~j) { + t = ((rows[k] >> j)^rows[k+j]) & mask; + rows[k] ^= (t << j); + rows[k+j] ^= t; + } + } + return solve_linear_system(bch, rows, roots, 4); +} + +/* + * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) + */ +static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0; + + if (poly->c[0]) + /* poly[X] = bX+c with c!=0, root=c/b */ + roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ + bch->a_log_tab[poly->c[1]]); + return n; +} + +/* + * compute roots of a degree 2 polynomial over GF(2^m) + */ +static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int n = 0, i, l0, l1, l2; + unsigned int u, v, r; + + if (poly->c[0] && poly->c[1]) { + + l0 = bch->a_log_tab[poly->c[0]]; + l1 = bch->a_log_tab[poly->c[1]]; + l2 = bch->a_log_tab[poly->c[2]]; + + /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ + u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); + /* + * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): + * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = + * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) + * i.e. r and r+1 are roots iff Tr(u)=0 + */ + r = 0; + v = u; + while (v) { + i = deg(v); + r ^= bch->xi_tab[i]; + v ^= (1 << i); + } + /* verify root */ + if ((gf_sqr(bch, r)^r) == u) { + /* reverse z=a/bX transformation and compute log(1/r) */ + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r]+l2); + roots[n++] = modulo(bch, 2*GF_N(bch)-l1- + bch->a_log_tab[r^1]+l2); + } + } + return n; +} + +/* + * compute roots of a degree 3 polynomial over GF(2^m) + */ +static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, n = 0; + unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; + + if (poly->c[0]) { + /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ + e3 = poly->c[3]; + c2 = gf_div(bch, poly->c[0], e3); + b2 = gf_div(bch, poly->c[1], e3); + a2 = gf_div(bch, poly->c[2], e3); + + /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ + c = gf_mul(bch, a2, c2); /* c = a2c2 */ + b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ + a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ + + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a, b, c, tmp) == 4) { + /* remove a2 from final list of roots */ + for (i = 0; i < 4; i++) { + if (tmp[i] != a2) + roots[n++] = a_ilog(bch, tmp[i]); + } + } + } + return n; +} + +/* + * compute roots of a degree 4 polynomial over GF(2^m) + */ +static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, + unsigned int *roots) +{ + int i, l, n = 0; + unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; + + if (poly->c[0] == 0) + return 0; + + /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ + e4 = poly->c[4]; + d = gf_div(bch, poly->c[0], e4); + c = gf_div(bch, poly->c[1], e4); + b = gf_div(bch, poly->c[2], e4); + a = gf_div(bch, poly->c[3], e4); + + /* use Y=1/X transformation to get an affine polynomial */ + if (a) { + /* first, eliminate cX by using z=X+e with ae^2+c=0 */ + if (c) { + /* compute e such that e^2 = c/a */ + f = gf_div(bch, c, a); + l = a_log(bch, f); + l += (l & 1) ? GF_N(bch) : 0; + e = a_pow(bch, l/2); + /* + * use transformation z=X+e: + * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d + * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d + * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d + * z^4 + az^3 + b'z^2 + d' + */ + d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; + b = gf_mul(bch, a, e)^b; + } + /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ + if (d == 0) + /* assume all roots have multiplicity 1 */ + return 0; + + c2 = gf_inv(bch, d); + b2 = gf_div(bch, a, d); + a2 = gf_div(bch, b, d); + } else { + /* polynomial is already affine */ + c2 = d; + b2 = c; + a2 = b; + } + /* find the 4 roots of this affine polynomial */ + if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { + for (i = 0; i < 4; i++) { + /* post-process roots (reverse transformations) */ + f = a ? gf_inv(bch, roots[i]) : roots[i]; + roots[i] = a_ilog(bch, f^e); + } + n = 4; + } + return n; +} + +/* + * build monic, log-based representation of a polynomial + */ +static void gf_poly_logrep(struct bch_control *bch, + const struct gf_poly *a, int *rep) +{ + int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); + + /* represent 0 values with -1; warning, rep[d] is not set to 1 */ + for (i = 0; i < d; i++) + rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; +} + +/* + * compute polynomial Euclidean division remainder in GF(2^m)[X] + */ +static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, int *rep) +{ + int la, p, m; + unsigned int i, j, *c = a->c; + const unsigned int d = b->deg; + + if (a->deg < d) + return; + + /* reuse or compute log representation of denominator */ + if (!rep) { + rep = bch->cache; + gf_poly_logrep(bch, b, rep); + } + + for (j = a->deg; j >= d; j--) { + if (c[j]) { + la = a_log(bch, c[j]); + p = j-d; + for (i = 0; i < d; i++, p++) { + m = rep[i]; + if (m >= 0) + c[p] ^= bch->a_pow_tab[mod_s(bch, + m+la)]; + } + } + } + a->deg = d-1; + while (!c[a->deg] && a->deg) + a->deg--; +} + +/* + * compute polynomial Euclidean division quotient in GF(2^m)[X] + */ +static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, + const struct gf_poly *b, struct gf_poly *q) +{ + if (a->deg >= b->deg) { + q->deg = a->deg-b->deg; + /* compute a mod b (modifies a) */ + gf_poly_mod(bch, a, b, NULL); + /* quotient is stored in upper part of polynomial a */ + memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); + } else { + q->deg = 0; + q->c[0] = 0; + } +} + +/* + * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] + */ +static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, + struct gf_poly *b) +{ + struct gf_poly *tmp; + + dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); + + if (a->deg < b->deg) { + tmp = b; + b = a; + a = tmp; + } + + while (b->deg > 0) { + gf_poly_mod(bch, a, b, NULL); + tmp = b; + b = a; + a = tmp; + } + + dbg("%s\n", gf_poly_str(a)); + + return a; +} + +/* + * Given a polynomial f and an integer k, compute Tr(a^kX) mod f + * This is used in Berlekamp Trace algorithm for splitting polynomials + */ +static void compute_trace_bk_mod(struct bch_control *bch, int k, + const struct gf_poly *f, struct gf_poly *z, + struct gf_poly *out) +{ + const int m = GF_M(bch); + int i, j; + + /* z contains z^2j mod f */ + z->deg = 1; + z->c[0] = 0; + z->c[1] = bch->a_pow_tab[k]; + + out->deg = 0; + memset(out, 0, GF_POLY_SZ(f->deg)); + + /* compute f log representation only once */ + gf_poly_logrep(bch, f, bch->cache); + + for (i = 0; i < m; i++) { + /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ + for (j = z->deg; j >= 0; j--) { + out->c[j] ^= z->c[j]; + z->c[2*j] = gf_sqr(bch, z->c[j]); + z->c[2*j+1] = 0; + } + if (z->deg > out->deg) + out->deg = z->deg; + + if (i < m-1) { + z->deg *= 2; + /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ + gf_poly_mod(bch, z, f, bch->cache); + } + } + while (!out->c[out->deg] && out->deg) + out->deg--; + + dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); +} + +/* + * factor a polynomial using Berlekamp Trace algorithm (BTA) + */ +static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, + struct gf_poly **g, struct gf_poly **h) +{ + struct gf_poly *f2 = bch->poly_2t[0]; + struct gf_poly *q = bch->poly_2t[1]; + struct gf_poly *tk = bch->poly_2t[2]; + struct gf_poly *z = bch->poly_2t[3]; + struct gf_poly *gcd; + + dbg("factoring %s...\n", gf_poly_str(f)); + + *g = f; + *h = NULL; + + /* tk = Tr(a^k.X) mod f */ + compute_trace_bk_mod(bch, k, f, z, tk); + + if (tk->deg > 0) { + /* compute g = gcd(f, tk) (destructive operation) */ + gf_poly_copy(f2, f); + gcd = gf_poly_gcd(bch, f2, tk); + if (gcd->deg < f->deg) { + /* compute h=f/gcd(f,tk); this will modify f and q */ + gf_poly_div(bch, f, gcd, q); + /* store g and h in-place (clobbering f) */ + *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; + gf_poly_copy(*g, gcd); + gf_poly_copy(*h, q); + } + } +} + +/* + * find roots of a polynomial, using BTZ algorithm; see the beginning of this + * file for details + */ +static int find_poly_roots(struct bch_control *bch, unsigned int k, + struct gf_poly *poly, unsigned int *roots) +{ + int cnt; + struct gf_poly *f1, *f2; + + switch (poly->deg) { + /* handle low degree polynomials with ad hoc techniques */ + case 1: + cnt = find_poly_deg1_roots(bch, poly, roots); + break; + case 2: + cnt = find_poly_deg2_roots(bch, poly, roots); + break; + case 3: + cnt = find_poly_deg3_roots(bch, poly, roots); + break; + case 4: + cnt = find_poly_deg4_roots(bch, poly, roots); + break; + default: + /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ + cnt = 0; + if (poly->deg && (k <= GF_M(bch))) { + factor_polynomial(bch, k, poly, &f1, &f2); + if (f1) + cnt += find_poly_roots(bch, k+1, f1, roots); + if (f2) + cnt += find_poly_roots(bch, k+1, f2, roots+cnt); + } + break; + } + return cnt; +} + +#if defined(USE_CHIEN_SEARCH) +/* + * exhaustive root search (Chien) implementation - not used, included only for + * reference/comparison tests + */ +static int chien_search(struct bch_control *bch, unsigned int len, + struct gf_poly *p, unsigned int *roots) +{ + int m; + unsigned int i, j, syn, syn0, count = 0; + const unsigned int k = 8*len+bch->ecc_bits; + + /* use a log-based representation of polynomial */ + gf_poly_logrep(bch, p, bch->cache); + bch->cache[p->deg] = 0; + syn0 = gf_div(bch, p->c[0], p->c[p->deg]); + + for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { + /* compute elp(a^i) */ + for (j = 1, syn = syn0; j <= p->deg; j++) { + m = bch->cache[j]; + if (m >= 0) + syn ^= a_pow(bch, m+j*i); + } + if (syn == 0) { + roots[count++] = GF_N(bch)-i; + if (count == p->deg) + break; + } + } + return (count == p->deg) ? count : 0; +} +#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) +#endif /* USE_CHIEN_SEARCH */ + +/** + * decode_bch - decode received codeword and find bit error locations + * @bch: BCH control structure + * @data: received data, ignored if @calc_ecc is provided + * @len: data length in bytes, must always be provided + * @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc + * @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data + * @syn: hw computed syndrome data (if NULL, syndrome is calculated) + * @errloc: output array of error locations + * + * Returns: + * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if + * invalid parameters were provided + * + * Depending on the available hw BCH support and the need to compute @calc_ecc + * separately (using encode_bch()), this function should be called with one of + * the following parameter configurations - + * + * by providing @data and @recv_ecc only: + * decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc) + * + * by providing @recv_ecc and @calc_ecc: + * decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc) + * + * by providing ecc = recv_ecc XOR calc_ecc: + * decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc) + * + * by providing syndrome results @syn: + * decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc) + * + * Once decode_bch() has successfully returned with a positive value, error + * locations returned in array @errloc should be interpreted as follows - + * + * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for + * data correction) + * + * if (errloc[n] < 8*len), then n-th error is located in data and can be + * corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8); + * + * Note that this function does not perform any data correction by itself, it + * merely indicates error locations. + */ +int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, + const uint8_t *recv_ecc, const uint8_t *calc_ecc, + const unsigned int *syn, unsigned int *errloc) +{ + const unsigned int ecc_words = BCH_ECC_WORDS(bch); + unsigned int nbits; + int i, err, nroots; + uint32_t sum; + +#ifdef DEBUG_BCH + printf("%s:%d syn %p\n", __FUNCTION__, __LINE__, syn); +#endif + /* sanity check: make sure data length can be handled */ + if (8*len > (bch->n-bch->ecc_bits)) + return -EINVAL; + + /* if caller does not provide syndromes, compute them */ + if (!syn) { + if (!calc_ecc) { + /* compute received data ecc into an internal buffer */ + if (!data || !recv_ecc) + return -EINVAL; + encode_bch(bch, data, len, NULL); + } else { + /* load provided calculated ecc */ + load_ecc8(bch, bch->ecc_buf, calc_ecc); + } + /* load received ecc or assume it was XORed in calc_ecc */ + if (recv_ecc) { + load_ecc8(bch, bch->ecc_buf2, recv_ecc); + /* XOR received and calculated ecc */ + for (i = 0, sum = 0; i < (int)ecc_words; i++) { + bch->ecc_buf[i] ^= bch->ecc_buf2[i]; + sum |= bch->ecc_buf[i]; + } + if (!sum) { + /* no error found */ +#ifdef DEBUG_BCH + printf("%s:%d\n", __FUNCTION__, __LINE__); +#endif + return 0; + } + } + compute_syndromes(bch, bch->ecc_buf, bch->syn); + syn = bch->syn; + } + + err = compute_error_locator_polynomial(bch, syn); +#ifdef DEBUG_BCH + printf("%s:%d err %d\n", __FUNCTION__, __LINE__, err); +#endif + if (err > 0) { + nroots = find_poly_roots(bch, 1, bch->elp, errloc); + if (err != nroots) { +#ifdef DEBUG_BCH + printf("%s:%d err %d nroots %d\n", __FUNCTION__, __LINE__, err, nroots); +#endif + err = -1; + } + } + if (err > 0) { + /* post-process raw error locations for easier correction */ + nbits = (len*8)+bch->ecc_bits; + for (i = 0; i < err; i++) { + if (errloc[i] >= nbits) { + err = -1; + break; + } + errloc[i] = nbits-1-errloc[i]; + errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); + } + } +#ifdef DEBUG_BCH + if (err < 0) + printf("%s:%d err %d\n", __FUNCTION__, __LINE__, err); +#endif + return (err >= 0) ? err : -EBADMSG; +} +// EXPORT_SYMBOL_GPL(decode_bch); + +/* + * generate Galois field lookup tables + */ +static int build_gf_tables(struct bch_control *bch, unsigned int poly) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + + /* primitive polynomial must be of degree m */ + if (k != (1u << GF_M(bch))) + return -1; + + for (i = 0; i < GF_N(bch); i++) { + bch->a_pow_tab[i] = x; + bch->a_log_tab[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ + return -1; + x <<= 1; + if (x & k) + x ^= poly; + } + bch->a_pow_tab[GF_N(bch)] = 1; + bch->a_log_tab[0] = 0; + + return 0; +} + +/* + * compute generator polynomial remainder tables for fast encoding + */ +static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) +{ + int i, j, b, d; + uint32_t data, hi, lo, *tab; + const int l = BCH_ECC_WORDS(bch); + const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); + const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); + + memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); + + for (i = 0; i < 256; i++) { + /* p(X)=i is a small polynomial of weight <= 8 */ + for (b = 0; b < 4; b++) { + /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ + tab = bch->mod8_tab + (b*256+i)*l; + data = i << (8*b); + while (data) { + d = deg(data); + /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ + data ^= g[0] >> (31-d); + for (j = 0; j < ecclen; j++) { + hi = (d < 31) ? g[j] << (d+1) : 0; + lo = (j+1 < plen) ? + g[j+1] >> (31-d) : 0; + tab[j] ^= hi|lo; + } + } + } + } +} + +/* + * build a base for factoring degree 2 polynomials + */ +static int build_deg2_base(struct bch_control *bch) +{ + const int m = GF_M(bch); + int i, j, r; + unsigned int sum, x, y, remaining, ak = 0, xi[m]; + + /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ + for (i = 0; i < m; i++) { + for (j = 0, sum = 0; j < m; j++) + sum ^= a_pow(bch, i*(1 << j)); + + if (sum) { + ak = bch->a_pow_tab[i]; + break; + } + } + /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ + remaining = m; + memset(xi, 0, sizeof(xi)); + + for (x = 0; (x <= GF_N(bch)) && remaining; x++) { + y = gf_sqr(bch, x)^x; + for (i = 0; i < 2; i++) { + r = a_log(bch, y); + if (y && (r < m) && !xi[r]) { + bch->xi_tab[r] = x; + xi[r] = 1; + remaining--; + dbg("x%d = %x\n", r, x); + break; + } + y ^= ak; + } + } + /* should not happen but check anyway */ + return remaining ? -1 : 0; +} + +static void *bch_alloc(size_t size, int *err) +{ + void *ptr; + + ptr = kmalloc(size, GFP_KERNEL); + if (ptr == NULL) + *err = 1; + return ptr; +} + +/* + * compute generator polynomial for given (m,t) parameters. + */ +static uint32_t *compute_generator_polynomial(struct bch_control *bch) +{ + const unsigned int m = GF_M(bch); + const unsigned int t = GF_T(bch); + int n, err = 0; + unsigned int i, j, nbits, r, word, *roots; + struct gf_poly *g; + uint32_t *genpoly; + + g = bch_alloc(GF_POLY_SZ(m*t), &err); + roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); + genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); + + if (err) { + kfree(genpoly); + genpoly = NULL; + goto finish; + } + + /* enumerate all roots of g(X) */ + memset(roots , 0, (bch->n+1)*sizeof(*roots)); + for (i = 0; i < t; i++) { + for (j = 0, r = 2*i+1; j < m; j++) { + roots[r] = 1; + r = mod_s(bch, 2*r); + } + } + /* build generator polynomial g(X) */ + g->deg = 0; + g->c[0] = 1; + for (i = 0; i < GF_N(bch); i++) { + if (roots[i]) { + /* multiply g(X) by (X+root) */ + r = bch->a_pow_tab[i]; + g->c[g->deg+1] = 1; + for (j = g->deg; j > 0; j--) + g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; + + g->c[0] = gf_mul(bch, g->c[0], r); + g->deg++; + } + } + /* store left-justified binary representation of g(X) */ + n = g->deg+1; + i = 0; + + while (n > 0) { + nbits = (n > 32) ? 32 : n; + for (j = 0, word = 0; j < nbits; j++) { + if (g->c[n-1-j]) + word |= 1u << (31-j); + } + genpoly[i++] = word; + n -= nbits; + } + bch->ecc_bits = g->deg; + +finish: + kfree(g); + kfree(roots); + + return genpoly; +} + +/** + * init_bch - initialize a BCH encoder/decoder + * @m: Galois field order, should be in the range 5-15 + * @t: maximum error correction capability, in bits + * @prim_poly: user-provided primitive polynomial (or 0 to use default) + * + * Returns: + * a newly allocated BCH control structure if successful, NULL otherwise + * + * This initialization can take some time, as lookup tables are built for fast + * encoding/decoding; make sure not to call this function from a time critical + * path. Usually, init_bch() should be called on module/driver init and + * free_bch() should be called to release memory on exit. + * + * You may provide your own primitive polynomial of degree @m in argument + * @prim_poly, or let init_bch() use its default polynomial. + * + * Once init_bch() has successfully returned a pointer to a newly allocated + * BCH control structure, ecc length in bytes is given by member @ecc_bytes of + * the structure. + */ +struct bch_control *init_bch(int m, int t, unsigned int prim_poly) +{ + int err = 0; + unsigned int i, words; + uint32_t *genpoly; + struct bch_control *bch = NULL; + + const int min_m = 5; + const int max_m = 15; + + /* default primitive polynomials */ + static const unsigned int prim_poly_tab[] = { + 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, + 0x402b, 0x8003, + }; + +#if defined(CONFIG_BCH_CONST_PARAMS) + if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { + printk(KERN_ERR "bch encoder/decoder was configured to support " + "parameters m=%d, t=%d only!\n", + CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); + goto fail; + } +#endif + if ((m < min_m) || (m > max_m)) + /* + * values of m greater than 15 are not currently supported; + * supporting m > 15 would require changing table base type + * (uint16_t) and a small patch in matrix transposition + */ + goto fail; + + /* sanity checks */ + if ((t < 1) || (m*t >= ((1 << m)-1))) + /* invalid t value */ + goto fail; + + /* select a primitive polynomial for generating GF(2^m) */ + if (prim_poly == 0) + prim_poly = prim_poly_tab[m-min_m]; + + bch = kzalloc(sizeof(*bch), GFP_KERNEL); + if (bch == NULL) + goto fail; + + bch->m = m; + bch->t = t; + bch->n = (1 << m)-1; + words = DIV_ROUND_UP(m*t, 32); + bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); + bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); + bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); + bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); + bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); + bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); + bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); + bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); + bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); + bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); + + if (err) + goto fail; + + err = build_gf_tables(bch, prim_poly); + if (err) + goto fail; + + /* use generator polynomial for computing encoding tables */ + genpoly = compute_generator_polynomial(bch); + if (genpoly == NULL) + goto fail; + + build_mod8_tables(bch, genpoly); + kfree(genpoly); + + err = build_deg2_base(bch); + if (err) + goto fail; + + return bch; + +fail: + free_bch(bch); + return NULL; +} +// EXPORT_SYMBOL_GPL(init_bch); + +/** + * free_bch - free the BCH control structure + * @bch: BCH control structure to release + */ +void free_bch(struct bch_control *bch) +{ + unsigned int i; + + if (bch) { + kfree(bch->a_pow_tab); + kfree(bch->a_log_tab); + kfree(bch->mod8_tab); + kfree(bch->ecc_buf); + kfree(bch->ecc_buf2); + kfree(bch->xi_tab); + kfree(bch->syn); + kfree(bch->cache); + kfree(bch->elp); + + for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) + kfree(bch->poly_2t[i]); + + kfree(bch); + } +} +// EXPORT_SYMBOL_GPL(free_bch); + +// MODULE_LICENSE("GPL"); +// MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); +// MODULE_DESCRIPTION("Binary BCH encoder/decoder"); @@ -8,6 +8,8 @@ # (C) 2002-2010 DENX Software Engineering, Wolfgang Denk <wd@denx.de> # +#set -x + APPEND=no # Default: Create new config file BOARD_NAME="" # Name to print in make output TARGETS="" @@ -139,6 +141,14 @@ fi # # Create board specific header file # + +if [ -f config.h ]; then + mv config.h config.h.bak + cp config.h.bak config.h +else + touch config.h.bak +fi + if [ "$APPEND" = "yes" ] # Append to existing config file then echo >> config.h @@ -160,4 +170,31 @@ cat << EOF >> config.h #include <asm/config.h> EOF +# +# Add TEXT_BASE from board config.mk in comment to config.h; this +# causes config.h to change if the TEXT_BASE in board config.mk changes +# +config_mk=$SRCTREE/board +if [ "$VENDOR" != "" ]; then + config_mk=$config_mk/$VENDOR +fi +if [ "$BOARD" != "" ]; then + config_mk=$config_mk/$BOARD +fi + +if [ -d $config_mk -a -f $config_mk/config.mk ]; then + TEXT_BASE=`grep CONFIG_SYS_TEXT_BASE $config_mk/config.mk` +fi +if [ ! -z "$TEXT_BASE" ]; then + echo "/* $TEXT_BASE */" >> config.h +else + rm config.h.bak +fi + +cmp -s config.h config.h.bak && { + echo "same config.h file..." + rm config.h + mv config.h.bak config.h +} + exit 0 diff --git a/tools/Makefile b/tools/Makefile index 97f83f84319..2117752c084 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -69,6 +69,7 @@ BIN_FILES-$(CONFIG_INCA_IP) += inca-swap-bytes$(SFX) BIN_FILES-y += mkimage$(SFX) BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX) BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX) +BIN_FILES-$(CONFIG_TOOL_SIGNGP) += sign-nand-image$(SFX) # Source files which exist outside the tools directory EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o @@ -83,6 +84,7 @@ OBJ_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo.o NOPED_OBJ_FILES-y += default_image.o OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc.o NOPED_OBJ_FILES-y += fit_image.o +OBJ_FILES-$(CONFIG_TOOL_SIGNGP) += sign-nand-image.o OBJ_FILES-$(CONFIG_CMD_NET) += gen_eth_addr.o OBJ_FILES-$(CONFIG_CMD_LOADS) += img2srec.o OBJ_FILES-$(CONFIG_INCA_IP) += inca-swap-bytes.o @@ -204,6 +206,10 @@ $(obj)ncb$(SFX): $(obj)ncb.o $(obj)ubsha1$(SFX): $(obj)os_support.o $(obj)sha1.o $(obj)ubsha1.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ +$(obj)sign-nand-image$(SFX): $(obj)sign-nand-image.o + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ + $(HOSTSTRIP) $@ + # Some of the tool objects need to be accessed from outside the tools directory $(obj)%.o: $(SRCTREE)/common/%.c $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< diff --git a/tools/sign-nand-image.c b/tools/sign-nand-image.c new file mode 100644 index 00000000000..324ae227e4a --- /dev/null +++ b/tools/sign-nand-image.c @@ -0,0 +1,80 @@ +/* + * signGP.c + * Read the x-load.bin file and write out the x-load.bin.ift file. + * The signed image is the original pre-pended with the size of the image + * and the load address. If not entered on command line, file name is + * assumed to be x-load.bin in current directory and load address is + * 0x40200800. */ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <malloc.h> + +#define SIGNATURE (('S' << 24) | ('g' << 16) | ('n' << 8) | 'P') + +int main(int argc, char *argv[]) +{ + int i; + size_t ret; + char ifname[FILENAME_MAX], ofname[FILENAME_MAX], ch; + FILE *ifile, *ofile; + unsigned long loadaddr, len; + struct stat sinfo; + unsigned long signature = SIGNATURE; + + /* Default to x-load.bin and 0x40200800. */ + strcpy(ifname, "x-load.bin"); + loadaddr = 0x40200800; + + if ((argc == 2) || (argc == 3)) + strcpy(ifname, argv[1]); + + if (argc == 3) + loadaddr = strtoul(argv[2], NULL, 16); + + /* Form the output file name. */ + strcpy(ofname, ifname); + strcat(ofname, ".ift"); + + /* Open the input file. */ + ifile = fopen(ifname, "rb"); + if (ifile == NULL) { + printf("Cannot open %s\n", ifname); + exit(0); + } + /* Get file length. */ + stat(ifname, &sinfo); + len = sinfo.st_size; + + /* Open the output file and write it. */ + ofile = fopen(ofname, "wb"); + if (ofile == NULL) { + printf("Cannot open %s\n", ofname); + fclose(ifile); + exit(0); + } + /* Pad 1 sector of zeroes. + * ch = 0x00; + * for (i=0; i<0x200; i++) + * fwrite(&ch, 1, 1, ofile); */ + + fwrite(&signature, 1, 4, ofile); + fwrite(&len, 1, 4, ofile); + fwrite(&loadaddr, 1, 4, ofile); + for (i = 0; i < len; i++) { + ret = fread(&ch, 1, 1, ifile); + if (ret != 1) { + fprintf(stderr, "fread didn't return 1!\n"); + exit(-1); + } + fwrite(&ch, 1, 1, ofile); + } + + fclose(ifile); + fclose(ofile); + + return 0; +} |