Index: arm/ipod/button-clickwheel.c =================================================================== --- arm/ipod/button-clickwheel.c (revision 22868) +++ arm/ipod/button-clickwheel.c (working copy) @@ -40,15 +40,36 @@ #include "power.h" #include "powermgmt.h" +#ifdef CPU_PP +/* PortalPlayer uses the USEC timer */ #define WHEEL_FAST_OFF_TIMEOUT 250000 /* timeout for acceleration = 250ms */ #define WHEEL_REPEAT_TIMEOUT 250000 /* timeout for button repeat = 250ms */ #define WHEEL_UNTOUCH_TIMEOUT 150000 /* timeout for untouching wheel = 150ms */ +#else +/* Other targets use current_tick */ +#define WHEEL_FAST_OFF_TIMEOUT (HZ/4) /* timeout for acceleration = 250ms */ +#define WHEEL_REPEAT_TIMEOUT (HZ/4) /* timeout for button repeat = 250ms */ +#define WHEEL_UNTOUCH_TIMEOUT ((HZ*15)/100) /* timeout for untouching wheel = 150ms */ + +#endif + +#ifdef CPU_PP +#define CLICKWHEEL_DATA (*(volatile unsigned long*)(0x7000c140)) +#elif CONFIG_CPU==S5L8701 +#define CLICKWHEEL00 (*(volatile unsigned long*)(0x3c200000)) +#define CLICKWHEEL10 (*(volatile unsigned long*)(0x3c200010)) +#define CLICKWHEELINT (*(volatile unsigned long*)(0x3c200014)) +#define CLICKWHEEL_DATA (*(volatile unsigned long*)(0x3c200018)) +#else +#error CPU architecture not supported! +#endif + #define WHEELCLICKS_PER_ROTATION 96 /* wheelclicks per full rotation */ /* This amount of clicks is needed for at least scrolling 1 item. Choose small values * to have high sensitivity but few precision, choose large values to have less * sensitivity and good precision. */ -#if defined(IPOD_NANO) +#if defined(IPOD_NANO) || defined(IPOD_NANO2G) #define WHEEL_SENSITIVITY 6 /* iPod nano has smaller wheel, lower sensitivity needed */ #else #define WHEEL_SENSITIVITY 4 /* default sensitivity */ @@ -71,6 +92,7 @@ static bool send_events = true; #endif +#ifdef CPU_PP static void opto_i2c_init(void) { DEV_EN |= DEV_OPTO; @@ -82,19 +104,18 @@ outl(0xc00a1f00, 0x7000c100); outl(0x01000000, 0x7000c104); } +#endif static inline int ipod_4g_button_read(void) { int whl = -1; int btn = BUTTON_NONE; - - /* The following delay was 250 in the ipodlinux source, but 50 seems to - work fine - tested on Nano, Color/Photo and Video. */ - udelay(50); +#ifdef CPU_PP if ((inl(0x7000c104) & 0x04000000) != 0) { - unsigned status = inl(0x7000c140); +#endif + unsigned status = CLICKWHEEL_DATA; if ((status & 0x800000ff) == 0x8000001a) { @@ -110,7 +131,11 @@ btn |= BUTTON_MENU; if (status & 0x40000000) { +#ifdef CPU_PP unsigned long usec = USEC_TIMER; +#else + unsigned long usec = current_tick; +#endif /* Highest wheel = 0x5F, clockwise increases */ new_wheel_value = (status >> 16) & 0x7f; @@ -239,7 +264,9 @@ } } +#ifdef CPU_PP } +#endif #ifdef HAVE_WHEEL_POSITION /* Save the new absolute wheel position */ @@ -260,10 +287,15 @@ } #endif +#ifdef CPU_PP void ipod_4g_button_int(void) { CPU_HI_INT_DIS = I2C_MASK; + /* The following delay was 250 in the ipodlinux source, but 50 seems to + work fine - tested on Nano, Color/Photo and Video. */ + udelay(50); + int_btn = ipod_4g_button_read(); outl(inl(0x7000c104) | 0x0c000000, 0x7000c104); @@ -285,6 +317,43 @@ CPU_HI_INT_EN = I2C_MASK; } +bool button_hold(void) +{ + return (GPIOA_INPUT_VAL & 0x20)?false:true; +} + +bool headphones_inserted(void) +{ + return (GPIOA_INPUT_VAL & 0x80)?true:false; +} +#else +void INT_SPI(void) +{ + int clickwheel_events = CLICKWHEELINT; + + /* Clear interrupts */ + if (clickwheel_events & 4) CLICKWHEELINT = 4; + if (clickwheel_events & 2) CLICKWHEELINT = 2; + if (clickwheel_events & 1) CLICKWHEELINT = 1; + + int_btn = ipod_4g_button_read(); +} + +void button_init_device(void) +{ + CLICKWHEEL00 = 0x280000; + CLICKWHEEL10 = 3; + INTMOD = 0; + INTMSK |= (1<<26); + PCON10 &= ~0xF00; +} + +bool button_hold(void) +{ + return ((PDAT14 & (1 << 6)) == 0); +} +#endif + /* * Get button pressed from hardware */ @@ -303,14 +372,17 @@ if (hold_button) { +#ifdef CPU_PP /* lock -> disable wheel sensor */ DEV_EN &= ~DEV_OPTO; +#endif } else { +#ifdef CPU_PP /* unlock -> enable wheel sensor */ - DEV_EN |= DEV_OPTO; opto_i2c_init(); +#endif } } @@ -321,13 +393,3 @@ return int_btn; #endif } - -bool button_hold(void) -{ - return (GPIOA_INPUT_VAL & 0x20)?false:true; -} - -bool headphones_inserted(void) -{ - return (GPIOA_INPUT_VAL & 0x80)?true:false; -} Index: arm/s5l8700/ata-nand-s5l8700.c =================================================================== --- arm/s5l8700/ata-nand-s5l8700.c (revision 22868) +++ arm/s5l8700/ata-nand-s5l8700.c (working copy) @@ -18,15 +18,17 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "nand.h" +#include "config.h" #include "ata_idle_notify.h" #include "system.h" #include #include "thread.h" #include "led.h" #include "disk.h" +#include "storage.h" #include "panic.h" #include "usb.h" +#include "ftl-target.h" /* for compatibility */ long last_disk_activity = -1; @@ -47,19 +49,13 @@ int nand_read_sectors(IF_MD2(int drive,) unsigned long start, int incount, void* inbuf) { - (void)start; - (void)incount; - (void)inbuf; - return 0; + return ftl_read(start, incount, inbuf); } int nand_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* outbuf) { - (void)start; - (void)count; - (void)outbuf; - return 0; + return ftl_write(start, count, outbuf); } void nand_spindown(int seconds) @@ -92,6 +88,8 @@ int nand_init(void) { + if (ftl_init()) return 1; + initialized = true; return 0; } Index: arm/s5l8700/boot.lds =================================================================== --- arm/s5l8700/boot.lds (revision 22868) +++ arm/s5l8700/boot.lds (working copy) @@ -1,103 +1,164 @@ -#include "config.h" - -ENTRY(_start) -#ifdef ROCKBOX_LITTLE_ENDIAN -OUTPUT_FORMAT(elf32-littlearm) -#else -OUTPUT_FORMAT(elf32-bigarm) -#endif -OUTPUT_ARCH(arm) -STARTUP(target/arm/s5l8700/crt0.o) - -/* DRAMORIG is in fact 0x08000000 but remapped to 0x0 */ -#define DRAMORIG 0x08000000 -#define DRAMSIZE (MEMORYSIZE * 0x100000) - -#define IRAMORIG 0x22000000 -#if CONFIG_CPU==S5L8701 -#define IRAMSIZE 176K -#else -#define IRAMSIZE 256K -#endif - -#ifdef MEIZU_M6SL -#define DFULOADADDR IRAMORIG -#else -#define DFULOADADDR (IRAMORIG+0x20000) -#endif - -/* This is not available in all versions of the S5L8700 */ -#define FLASHORIG 0x24000000 -#define FLASHSIZE 1M - -MEMORY -{ - DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE - IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE - FLASH : ORIGIN = FLASHORIG, LENGTH = FLASHSIZE -} - -#ifdef IPOD_NANO2G -#define LOAD_AREA IRAM -#else -#define LOAD_AREA FLASH -#endif - -SECTIONS -{ - .intvect : { - _intvectstart = . ; - *(.intvect) - _intvectend = _newstart ; - } >IRAM AT> LOAD_AREA - _intvectcopy = LOADADDR(.intvect) ; - - .text : { - *(.init.text) - *(.text*) - *(.glue_7*) - } > LOAD_AREA - - .rodata : { - *(.rodata*) - . = ALIGN(0x4); - } > LOAD_AREA - - .data : { - _datastart = . ; - *(.irodata) - *(.icode) - *(.idata) - *(.data*) - *(.ncdata*); - . = ALIGN(0x4); - _dataend = . ; - } > IRAM AT> LOAD_AREA - _datacopy = LOADADDR(.data) ; - - .stack : - { - *(.stack) - _stackbegin = .; - stackbegin = .; - . += 0x2000; - _stackend = .; - stackend = .; - _irqstackbegin = .; - . += 0x400; - _irqstackend = .; - _fiqstackbegin = .; - . += 0x400; - _fiqstackend = .; - } > IRAM - - .bss : { - _edata = .; - *(.bss*); - *(.ibss); - *(.ncbss*); - *(COMMON); - . = ALIGN(0x4); - _end = .; - } > IRAM -} +#include "config.h" + +ENTRY(start) +#ifdef ROCKBOX_LITTLE_ENDIAN +OUTPUT_FORMAT(elf32-littlearm) +#else +OUTPUT_FORMAT(elf32-bigarm) +#endif +OUTPUT_ARCH(arm) +STARTUP(target/arm/s5l8700/crt0.o) + +/* DRAMORIG is in fact 0x08000000 but remapped to 0x0 */ +#define DRAMORIG 0x08000000 +#define DRAMSIZE (MEMORYSIZE * 0x100000) + +#define IRAMORIG 0 +#if CONFIG_CPU==S5L8701 +#define IRAMSIZE 176K +#else +#define IRAMSIZE 256K +#endif + +#ifdef MEIZU_M6SL +#define DFULOADADDR IRAMORIG +#else +#define DFULOADADDR (IRAMORIG+0x20000) +#endif + +/* This is not available in all versions of the S5L8700 */ +#define FLASHORIG 0x24000000 +#define FLASHSIZE 1M + +MEMORY +{ + DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE + IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE + FLASH : ORIGIN = FLASHORIG, LENGTH = FLASHSIZE +} + +#ifdef IPOD_NANO2G +#define LOAD_AREA DRAM +#else +#define LOAD_AREA FLASH +#endif + +#ifdef IPOD_NANO2G +SECTIONS +{ + .intvect : { + _intvectstart = . ; + *(.intvect) + _intvectend = _newstart ; + } >IRAM AT> LOAD_AREA + _intvectcopy = LOADADDR(.intvect) ; + + .text : { + *(.init.text) + *(.text*) + *(.glue_7*) + } > LOAD_AREA + + .rodata : { + *(.rodata*) + . = ALIGN(0x4); + } > LOAD_AREA + + .data : { + _datastart = . ; + *(.irodata) + *(.icode) + *(.idata) + *(.data*) + *(.ncdata*); + . = ALIGN(0x4); + _dataend = . ; + } > DRAM AT> LOAD_AREA + _datacopy = LOADADDR(.data) ; + + .stack : + { + *(.stack) + _stackbegin = .; + stackbegin = .; + . += 0x2000; + _stackend = .; + stackend = .; + _irqstackbegin = .; + . += 0x400; + _irqstackend = .; + _fiqstackbegin = .; + . += 0x400; + _fiqstackend = .; + } > DRAM + + .bss : { + _edata = .; + *(.bss*); + *(.ibss); + *(.ncbss*); + *(COMMON); + . = ALIGN(0x4); + _end = .; + } > DRAM +} +#else +SECTIONS +{ + .intvect : { + _intvectstart = . ; + *(.intvect) + _intvectend = _newstart ; + } >IRAM AT> LOAD_AREA + _intvectcopy = LOADADDR(.intvect) ; + + .text : { + *(.init.text) + *(.text*) + *(.glue_7*) + } > LOAD_AREA + + .rodata : { + *(.rodata*) + . = ALIGN(0x4); + } > LOAD_AREA + + .data : { + _datastart = . ; + *(.irodata) + *(.icode) + *(.idata) + *(.data*) + *(.ncdata*); + . = ALIGN(0x4); + _dataend = . ; + } > IRAM AT> LOAD_AREA + _datacopy = LOADADDR(.data) ; + + .stack : + { + *(.stack) + _stackbegin = .; + stackbegin = .; + . += 0x2000; + _stackend = .; + stackend = .; + _irqstackbegin = .; + . += 0x400; + _irqstackend = .; + _fiqstackbegin = .; + . += 0x400; + _fiqstackend = .; + } > IRAM + + .bss : { + _edata = .; + *(.bss*); + *(.ibss); + *(.ncbss*); + *(COMMON); + . = ALIGN(0x4); + _end = .; + } > IRAM +} +#endif Index: arm/s5l8700/crt0.S =================================================================== --- arm/s5l8700/crt0.S (revision 22868) +++ arm/s5l8700/crt0.S (working copy) @@ -1,336 +1,344 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: crt0.S 18776 2008-10-11 18:32:17Z gevaerts $ - * - * Copyright (C) 2008 by Marcoen Hirschberg - * Copyright (C) 2008 by Denes Balatoni - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "config.h" -#include "cpu.h" - - .section .intvect,"ax",%progbits - .global _start - .global _newstart - /* Exception vectors */ -_start: - b _newstart - ldr pc, =undef_instr_handler - ldr pc, =software_int_handler - ldr pc, =prefetch_abort_handler - ldr pc, =data_abort_handler - ldr pc, =reserved_handler - ldr pc, =irq_handler - ldr pc, =fiq_handler -#if CONFIG_CPU==S5L8700 - .word 0x43554644 /* DFUC */ -#endif - .ltorg -_newstart: - ldr pc, =newstart2 // we do not want to execute from 0x0 as iram will be mapped there - .section .init.text,"ax",%progbits -newstart2: - msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */ - -#ifdef ROCKBOX_BIG_ENDIAN - mov r1, #0x80 - mrc 15, 0, r0, c1, c0, 0 - orr r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // set bigendian -#endif - - ldr r1, =0x3c800000 // disable watchdog - mov r0, #0xa5 - str r0, [r1] - - mov r0, #0 - ldr r1, =0x39c00008 - str r0, [r1] // mask all interrupts - ldr r1, =0x39c00020 - str r0, [r1] // mask all external interrupts - mvn r0, #0 - mov r1, #0x39c00000 - str r0, [r1] // irq priority - ldr r1, =0x39c00010 - str r0, [r1] // clear pending interrupts - ldr r1, =0x39c0001c - str r0, [r1] // clear pending external interrupts - -// ldr r1, =0x3cf00000 -// ldr r0, [r1] -// mvn r2, #0x30 -// and r0, r0, r2 -// mov r2, #0x10 -// orr r0, r0, r2 -// str r0, [r1] -// ldr r1, =0x3cf00004 -// ldr r0, [r1] -// mov r2, #4 -// orr r0, r0, r2 -// str r0, [r1] // switch backlight on - -#ifndef IPOD_NANO2G -/* Currently disabled for the Nano2G as it doesn't appear to be - correct - e.g. audio doesn't work with this code enabled. */ - ldr r1, =0x3c500000 // CLKCON - ldr r0, =0x00800080 - str r0, [r1] - ldr r1, =0x3c500024 // PLLCON - mov r0, #0 - str r0, [r1] - ldr r1, =0x3c500004 // PLL0PMS -#ifdef IPOD_NANO2G - ldr r0, =0x21200 // pdiv=2, mdiv=?? sdiv=0 -#else - ldr r0, =0x1ad200 -#endif - str r0, [r1] - ldr r1, =0x3c500014 // PLL0LCNT - ldr r0, =8100 - str r0, [r1] - ldr r1, =0x3c500024 // PLLCON - mov r0, #1 - str r0, [r1] - ldr r1, =0x3c500020 // PLLLOCK -1: - ldr r0, [r1] - tst r0, #1 - beq 1b - ldr r1, =0x3c50003c // CLKCON2 - mov r0, #0x80 - str r0, [r1] - ldr r1, =0x3c500000 // CLKCON - ldr r0, =0x20803180 - str r0, [r1] // FCLK_CPU = 200MHz, HCLK = 100MHz, PCLK = 50MHz, other clocks off - - ldr r2, =0xc0000078 - mrc 15, 0, r0, c1, c0, 0 - mvn r1, #0xc0000000 - and r0, r0, r1 - orr r0, r0, r2 - mcr 15, 0, r0, c1, c0, 0 // asynchronous clocking mode - nop - nop - nop - nop -#endif - -// ldr r0, =0x10100000 -// ldr r1, =0x38200034 -// str r0, [r1] // SRAM0/1 data width 16 bit -// ldr r0, =0x00220922 -// ldr r7, =0x38200038 -// str r0, [r7] // SRAM0/1 clocks -// ldr r0, =0x00220922 -// ldr r9, =0x3820003c -// str r0, [r9] // SRAM2/3 clocks -// nop -// nop -// nop -// nop - - ldr r1, =0x3c500000 - mov r0, #0 // 0x0 - str r0, [r1, #40] // enable clock for all peripherals - mov r0, #0 // 0x0 - str r0, [r1, #44] // do not enter any power saving mode - - mov r1, #0x1 - mrc 15, 0, r0, c1, c0, 0 - bic r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // disable protection unit - - mov r1, #0x4 - mrc 15, 0, r0, c1, c0, 0 - bic r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // dcache disable - - mov r1, #0x1000 - mrc 15, 0, r0, c1, c0, 0 - bic r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // icache disable - - mov r1, #0 -1: - mov r0, #0 -2: - orr r2, r1, r0 - mcr 15, 0, r2, c7, c14, 2 // clean and flush dcache single entry - add r0, r0, #0x10 - cmp r0, #0x40 - bne 2b - add r1, r1, #0x4000000 - cmp r1, #0x0 - bne 1b - nop - nop - mov r0, #0 - mcr 15, 0, r0, c7, c10, 4 // clean and flush whole dcache - - mov r0, #0 - mcr 15, 0, r0, c7, c5, 0 // flush icache - - mov r0, #0 - mcr 15, 0, r0, c7, c6, 0 // flush dcache - - mov r0, #0x3f - mcr 15, 0, r0, c6, c0, 1 - mov r0, #0x2f - mcr 15, 0, r0, c6, c1, 1 - ldr r0, =0x08000031 - mcr 15, 0, r0, c6, c2, 1 - ldr r0, =0x22000023 - mcr 15, 0, r0, c6, c3, 1 - ldr r0, =0x24000027 - mcr 15, 0, r0, c6, c4, 1 - mov r0, #0x3f - mcr 15, 0, r0, c6, c0, 0 - mov r0, #0x2f - mcr 15, 0, r0, c6, c1, 0 - ldr r0, =0x08000031 - mcr 15, 0, r0, c6, c2, 0 - ldr r0, =0x22000023 - mcr 15, 0, r0, c6, c3, 0 - ldr r0, =0x24000029 - mcr 15, 0, r0, c6, c4, 0 - mov r0, #0x1e - mcr 15, 0, r0, c2, c0, 1 - mov r0, #0x1e - mcr 15, 0, r0, c2, c0, 0 - mov r0, #0x1e - mcr 15, 0, r0, c3, c0, 0 - ldr r0, =0x0000ffff - mcr 15, 0, r0, c5, c0, 1 - ldr r0, =0x0000ffff - mcr 15, 0, r0, c5, c0, 0 // set up protection and caching - - mov r1, #0x4 - mrc 15, 0, r0, c1, c0, 0 - orr r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // dcache enable - - mov r1, #0x1000 - mrc 15, 0, r0, c1, c0, 0 - orr r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // icache enable - - mov r1, #0x1 - mrc 15, 0, r0, c1, c0, 0 - orr r0, r0, r1 - mcr 15, 0, r0, c1, c0, 0 // enable protection unit - - -#if CONFIG_CPU==S5L8700 - /* Copy interrupt vectors to iram */ - ldr r2, =_intvectstart - ldr r3, =_intvectend - ldr r4, =_intvectcopy -1: - cmp r3, r2 - ldrhi r1, [r4], #4 - strhi r1, [r2], #4 - bhi 1b -#endif - - /* Initialise bss section to zero */ - ldr r2, =_edata - ldr r3, =_end - mov r4, #0 -1: - cmp r3, r2 - strhi r4, [r2], #4 - bhi 1b - -#if CONFIG_CPU==S5L8700 - /* Copy icode and data to ram */ - ldr r2, =_datastart - ldr r3, =_dataend - ldr r4, =_datacopy -1: - cmp r3, r2 - ldrhi r1, [r4], #4 - strhi r1, [r2], #4 - bhi 1b -#endif - - /* Set up some stack and munge it with 0xdeadbeef */ - ldr sp, =_stackend - ldr r2, =_stackbegin - ldr r3, =0xdeadbeef -1: - cmp sp, r2 - strhi r3, [r2], #4 - bhi 1b - - /* Set up stack for IRQ mode */ - msr cpsr_c, #0xd2 - ldr sp, =_irqstackend - - /* Set up stack for FIQ mode */ - msr cpsr_c, #0xd1 - ldr sp, =_fiqstackend - - /* Let abort and undefined modes use IRQ stack */ - msr cpsr_c, #0xd7 - ldr sp, =_irqstackend - msr cpsr_c, #0xdb - ldr sp, =_irqstackend - - /* Switch back to supervisor mode */ - msr cpsr_c, #0xd3 - -// if we did not switch remap on, device -// would crash when MENU is pressed, -// as that button is connected to BOOT_MODE pin -#if CONFIG_CPU==S5L8700 - ldr r1, =0x38200000 - ldr r0, [r1] - mvn r2, #0x10000 - and r0, r0, r2 - mov r2, #0x1 - orr r0, r0, r2 - str r0, [r1] // remap iram to address 0x0 -#endif - - bl main - - .text -/* .global UIE*/ - -/* All illegal exceptions call into UIE with exception address as first - * parameter. This is calculated differently depending on which exception - * we're in. Second parameter is exception number, used for a string lookup - * in UIE. */ -undef_instr_handler: - mov r0, lr - mov r1, #0 - b UIE - -/* We run supervisor mode most of the time, and should never see a software - * exception being thrown. Perhaps make it illegal and call UIE? */ -software_int_handler: -reserved_handler: - movs pc, lr - -prefetch_abort_handler: - sub r0, lr, #4 - mov r1, #1 - b UIE - -data_abort_handler: - sub r0, lr, #8 - mov r1, #2 - b UIE +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: crt0.S 18776 2008-10-11 18:32:17Z gevaerts $ + * + * Copyright (C) 2008 by Marcoen Hirschberg + * Copyright (C) 2008 by Denes Balatoni + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "cpu.h" + + .section .intvect,"ax",%progbits + .global start + .global _newstart + /* Exception vectors */ +start: + b _newstart + ldr pc, =undef_instr_handler + ldr pc, =software_int_handler + ldr pc, =prefetch_abort_handler + ldr pc, =data_abort_handler + ldr pc, =reserved_handler + ldr pc, =irq_handler + ldr pc, =fiq_handler +#ifdef IPOD_NANO2G + b _newstart +#endif +#if CONFIG_CPU==S5L8700 + .word 0x43554644 /* DFUC */ +#endif + .ltorg +_newstart: + ldr pc, =newstart2 // we do not want to execute from 0x0 as iram will be mapped there + .section .init.text,"ax",%progbits +newstart2: + msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */ + +#ifdef ROCKBOX_BIG_ENDIAN + mov r1, #0x80 + mrc 15, 0, r0, c1, c0, 0 + orr r0, r0, r1 + mcr 15, 0, r0, c1, c0, 0 // set bigendian +#endif + + ldr r1, =0x3c800000 // disable watchdog + mov r0, #0xa5 + str r0, [r1] + +#if defined(IPOD_NANO2G) && defined(DEBUG) + mov r0, #0x00010000 +#else + mov r0, #0 +#endif + ldr r1, =0x39c00008 + str r0, [r1] // mask all interrupts +#if defined(IPOD_NANO2G) && defined(DEBUG) + mov r0, #0 +#endif + ldr r1, =0x39c00020 + str r0, [r1] // mask all external interrupts + mvn r0, #0 + ldr r1, =0x39c0001c + str r0, [r1] // clear pending external interrupts + mov r1, #0x39c00000 + str r0, [r1] // irq priority + ldr r1, =0x39c00010 + str r0, [r1] // clear pending interrupts + +#if defined(IPOD_NANO2G) && !defined(DEBUG) + LDR R1, =0x38200000 // Remap IRAM to address zero + LDR R2, [R1] + ORR R2, R2, #1 + BIC R2, R2, #0x10000 + STR R2, [R1] +#endif + +// ldr r1, =0x3cf00000 +// ldr r0, [r1] +// mvn r2, #0x30 +// and r0, r0, r2 +// mov r2, #0x10 +// orr r0, r0, r2 +// str r0, [r1] +// ldr r1, =0x3cf00004 +// ldr r0, [r1] +// mov r2, #4 +// orr r0, r0, r2 +// str r0, [r1] // switch backlight on + + ldr r1, =0x3c500000 // CLKCON + ldr r0, =0x00800080 + str r0, [r1] + ldr r1, =0x3c500024 // PLLCON + mov r0, #0 + str r0, [r1] + ldr r1, =0x3c500004 // PLL0PMS +#ifdef IPOD_NANO2G + ldr r0, =0x21200 // pdiv=2, mdiv=?? sdiv=0 +#else + ldr r0, =0x1ad200 +#endif + str r0, [r1] + ldr r1, =0x3c500014 // PLL0LCNT + ldr r0, =8100 + str r0, [r1] + ldr r1, =0x3c500024 // PLLCON + mov r0, #1 + str r0, [r1] + ldr r1, =0x3c500020 // PLLLOCK +1: + ldr r0, [r1] + tst r0, #1 + beq 1b + ldr r1, =0x3c50003c // CLKCON2 + mov r0, #0x80 + str r0, [r1] + ldr r1, =0x3c500000 // CLKCON + ldr r0, =0x20803180 + str r0, [r1] // FCLK_CPU = 200MHz, HCLK = 100MHz, PCLK = 50MHz, other clocks off + + ldr r2, =0xc0000078 + mrc 15, 0, r0, c1, c0, 0 + mvn r1, #0xc0000000 + and r0, r0, r1 + orr r0, r0, r2 + mcr 15, 0, r0, c1, c0, 0 // asynchronous clocking mode + nop + nop + nop + nop + +// ldr r0, =0x10100000 +// ldr r1, =0x38200034 +// str r0, [r1] // SRAM0/1 data width 16 bit +// ldr r0, =0x00220922 +// ldr r7, =0x38200038 +// str r0, [r7] // SRAM0/1 clocks +// ldr r0, =0x00220922 +// ldr r9, =0x3820003c +// str r0, [r9] // SRAM2/3 clocks +// nop +// nop +// nop +// nop + + mrc 15, 0, r0, c1, c0, 0 + bic r0, r0, #0x1000 + bic r0, r0, #0x5 + mcr 15, 0, r0, c1, c0, 0 // disable caches and protection unit + + mov r1, #0 +1: + mov r0, #0 +2: + orr r2, r1, r0 + mcr 15, 0, r2, c7, c14, 2 // clean and flush dcache single entry + add r0, r0, #0x10 + cmp r0, #0x40 + bne 2b + add r1, r1, #0x4000000 + cmp r1, #0x0 + bne 1b + nop + nop + mov r0, #0 + mcr 15, 0, r0, c7, c10, 4 // clean and flush whole dcache + mcr 15, 0, r0, c7, c5, 0 // flush icache + mcr 15, 0, r0, c7, c6, 0 // flush dcache + + mov r0, #0x3f + mcr 15, 0, r0, c6, c0, 1 // CS0: 4GB at offset 0 - everything + mcr 15, 0, r0, c6, c0, 0 // DS0: 4GB at offset 0 - everything +#ifdef IPOD_NANO2G + mov r0, #0x31 // FIXME: calculate that from MEMORYSIZE +#else + mov r0, #0x2f // FIXME: calculate that from MEMORYSIZE +#endif + mcr 15, 0, r0, c6, c1, 1 // CS1: SRAM/SDRAM mirror + mcr 15, 0, r0, c6, c1, 0 // DS1: SRAM/SDRAM mirror + add r0, r0, #0x08000000 + mcr 15, 0, r0, c6, c2, 1 // CS2: SDRAM + mcr 15, 0, r0, c6, c2, 0 // DS2: SDRAM + ldr r0, =0x22000023 + mcr 15, 0, r0, c6, c3, 1 // CS3: SRAM + mcr 15, 0, r0, c6, c3, 0 // DS3: SRAM + ldr r0, =0x24000027 + mcr 15, 0, r0, c6, c4, 1 // CS4: NOR flash + mcr 15, 0, r0, c6, c4, 0 // DS4: NOR flash + mov r0, #0 + mcr 15, 0, r0, c6, c5, 1 // CS5: unused + mcr 15, 0, r0, c6, c5, 0 // DS5: unused + mcr 15, 0, r0, c6, c6, 1 // CS6: unused + mcr 15, 0, r0, c6, c6, 0 // DS6: unused + mcr 15, 0, r0, c6, c7, 1 // CS7: unused + mcr 15, 0, r0, c6, c7, 0 // DS7: unused + mov r0, #0x1e + mcr 15, 0, r0, c2, c0, 1 // CS1-4: cacheable + mcr 15, 0, r0, c2, c0, 0 // DS1-4: cacheable + mcr 15, 0, r0, c3, c0, 0 // DS1-4: write cacheable + ldr r0, =0x000003ff + mcr 15, 0, r0, c5, c0, 1 // CS0-4: full access + mcr 15, 0, r0, c5, c0, 0 // DS0-4: full access + + mrc 15, 0, r0, c1, c0, 0 + orr r0, r0, #0x5 + orr r0, r0, #0x1000 + mcr 15, 0, r0, c1, c0, 0 // re-enable protection unit and caches + +#if !defined(IPOD_NANO2G) || !defined(DEBUG) + /* Copy interrupt vectors to iram */ + ldr r2, =_intvectstart + ldr r3, =_intvectend + ldr r4, =_intvectcopy +1: + cmp r3, r2 + ldrhi r1, [r4], #4 + strhi r1, [r2], #4 + bhi 1b +#endif + + /* Initialise bss section to zero */ + ldr r2, =_edata + ldr r3, =_end + mov r4, #0 +1: + cmp r3, r2 + strhi r4, [r2], #4 + bhi 1b + +#if CONFIG_CPU==S5L8700 && defined(BOOTLOADER) + /* Copy icode and data to ram */ + ldr r2, =_datastart + ldr r3, =_dataend + ldr r4, =_datacopy +1: + cmp r3, r2 + ldrhi r1, [r4], #4 + strhi r1, [r2], #4 + bhi 1b +#endif + +#ifndef BOOTLOADER + /* Copy icode and data to ram */ + ldr r2, =_iramstart + ldr r3, =_iramend + ldr r4, =_iramcopy +1: + cmp r3, r2 + ldrhi r1, [r4], #4 + strhi r1, [r2], #4 + bhi 1b + + /* Initialise ibss section to zero */ + ldr r2, =_iedata + ldr r3, =_iend + mov r4, #0 +1: + cmp r3, r2 + strhi r4, [r2], #4 + bhi 1b +#endif + + /* Set up some stack and munge it with 0xdeadbeef */ + ldr sp, =stackend + ldr r2, =stackbegin + ldr r3, =0xdeadbeef +1: + cmp sp, r2 + strhi r3, [r2], #4 + bhi 1b + + /* Set up stack for IRQ mode */ + msr cpsr_c, #0xd2 + ldr sp, =_irqstackend + + /* Set up stack for FIQ mode */ + msr cpsr_c, #0xd1 + ldr sp, =_fiqstackend + + /* Let abort and undefined modes use IRQ stack */ + msr cpsr_c, #0xd7 + ldr sp, =_irqstackend + msr cpsr_c, #0xdb + ldr sp, =_irqstackend + + /* Switch back to supervisor mode */ + msr cpsr_c, #0xd3 + +// if we did not switch remap on, device +// would crash when MENU is pressed, +// as that button is connected to BOOT_MODE pin +#if CONFIG_CPU==S5L8700 + ldr r1, =0x38200000 + ldr r0, [r1] + mvn r2, #0x10000 + and r0, r0, r2 + mov r2, #0x1 + orr r0, r0, r2 + str r0, [r1] // remap iram to address 0x0 +#endif + + bl main + + .text +/* .global UIE*/ + +/* All illegal exceptions call into UIE with exception address as first + * parameter. This is calculated differently depending on which exception + * we're in. Second parameter is exception number, used for a string lookup + * in UIE. */ +undef_instr_handler: + mov r0, lr + mov r1, #0 + b UIE + +/* We run supervisor mode most of the time, and should never see a software + * exception being thrown. Perhaps make it illegal and call UIE? */ +software_int_handler: +reserved_handler: + movs pc, lr + +prefetch_abort_handler: + sub r0, lr, #4 + mov r1, #1 + b UIE + +data_abort_handler: + sub r0, lr, #8 + mov r1, #2 + b UIE + Index: arm/s5l8700/ipodnano2g/button-nano2g.c =================================================================== --- arm/s5l8700/ipodnano2g/button-nano2g.c (revision 22868) +++ arm/s5l8700/ipodnano2g/button-nano2g.c (working copy) @@ -1,82 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id: button-m3.c 21787 2009-07-11 20:00:07Z bertrik $ - * - * Copyright (C) 2009 Dave Chapman - * - * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include -#include "config.h" - -#include "s5l8700.h" -#include "button-target.h" - -#define CLICKWHEEL00 (*(volatile unsigned long*)(0x3c200000)) -#define CLICKWHEEL10 (*(volatile unsigned long*)(0x3c200010)) -#define CLICKWHEELINT (*(volatile unsigned long*)(0x3c200014)) -#define CLICKWHEEL_DATA (*(volatile unsigned long*)(0x3c200018)) - -static int buttons = 0; - -void INT_SPI(void) -{ - int clickwheel_events; - int btn =0; - int status; - - clickwheel_events = CLICKWHEELINT; - - if (clickwheel_events & 4) CLICKWHEELINT = 4; - if (clickwheel_events & 2) CLICKWHEELINT = 2; - if (clickwheel_events & 1) CLICKWHEELINT = 1; - - status = CLICKWHEEL_DATA; - if ((status & 0x800000ff) == 0x8000001a) - { - if (status & 0x00000100) - btn |= BUTTON_SELECT; - if (status & 0x00000200) - btn |= BUTTON_RIGHT; - if (status & 0x00000400) - btn |= BUTTON_LEFT; - if (status & 0x00000800) - btn |= BUTTON_PLAY; - if (status & 0x00001000) - btn |= BUTTON_MENU; - } - - buttons = btn; -} - -void button_init_device(void) -{ - CLICKWHEEL00 = 0x280000; - CLICKWHEEL10 = 3; - INTMOD = 0; - INTMSK |= (1<<26); - PCON10 &= ~0xF00; -} - -int button_read_device(void) -{ - return buttons; -} - -bool button_hold(void) -{ - return ((PDAT14 & (1 << 6)) == 0); -} Index: arm/s5l8700/ipodnano2g/ftl-nano2g.c =================================================================== --- arm/s5l8700/ipodnano2g/ftl-nano2g.c (revision 0) +++ arm/s5l8700/ipodnano2g/ftl-nano2g.c (revision 0) @@ -0,0 +1,1821 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2009 by Michael Sparmann + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + + +#include +//#include +#include "s5l8701.h" +#include +#include +#include + + + +typedef struct FTLLogType // Keeps the state of a scattered page block +{ + + uint32_t usn; // The ftl_cxt.nextblockusn at the time the block was allocated, + // needed in order to be able to remove the oldest ones first. + + uint16_t scatteredvblock; // The vBlock number at which the scattered pages are stored + + uint16_t logicalvblock; // the lBlock number for which those pages are + + uint16_t* pageoffsets; // Pointer to ftl_offsets, contains the mapping which lpage is + // currently stored at which scattered vPage. + + uint16_t pagesused; // Pages used in the vBlock, i.e. next page number to be written + + uint16_t pagescurrent; // Pages that are still up to date in this block, i.e. need to be + // moved when this vBlock is deallocated. + + uint32_t issequential; // A flag whether all pages are still sequential in this block. + // Initialized to 1 on allocation, zeroed as soon as anything is + // written out of sequence, so that the block will need copying + // when committing to get the pages back into the right order. + // This is used to half the number of block erases needed when + // writing huge amounts of sequential data. + +} __attribute__((packed)) FTLLogType; + + +typedef struct FTLCxtType // Keeps the state of the FTL +{ + + uint32_t usn; // Update sequence number of the FTL context, decremented + // every time a new revision of FTL meta data is written. + + uint32_t nextblockusn; // Update sequence number for user data blocks. Incremented + // every time a portion of user pages is written, so that + // a consistency check can determine which copy of a user + // page is the most recent one. + + uint16_t freecount; // Count of currently free pages in the block pool + + uint16_t nextfreeidx; // Index to the first free block in the blockpool ring buffer + + uint16_t swapcounter; // This is a counter that is used to better distribute block + // wear. It is incremented on every block erase, and if it + // gets too high (300 on writes, 20 on sync), the most and + // least worn block will be swapped (inferring an additional + // block write) and the counter will be decreased by 20. + + uint16_t blockpool[0x14]; // Ring buffer of currently free blocks. nextfreeidx is the + // index to freecount free ones, the other ones are currently + // allocated for scattered page blocks. + + uint16_t field_36; // Alignment to 32 bits + + uint32_t ftl_map_pages[8]; // vPages where the block map is stored + + uint8_t field_58[0x28]; // Probably additional map page number space for bigger chips + + uint32_t ftl_erasectr_pages[8]; // vPages where the erase counters are stored + + uint8_t field_A0[0x70]; // Seems to be padding + + uint32_t ftl_map_ptr; // Pointer to ftl_map used by Whimory, unused by us + + uint32_t ftl_erasectr_ptr; // Pointer to ftl_erasectr used by Whimory, unused by us + + uint32_t ftl_log_ptr; // Pointer to ftl_log used by Whimory, unused by us + + uint32_t erasedirty; // Flag used to indicate that some erase counter pages should + // be committed as they were changed more than 100 times since + // the last commit. + + uint16_t field_120; // Seems to be unused + + uint16_t ftlctrlblocks[3]; // vBlocks used to store the FTL context, map, and erase + // counter pages. This is also a ring buffer, and the oldest + // page gets swapped with the least used page from the block + // pool ring buffer when a new one is allocated. + + uint32_t ftlctrlpage; // The last used vPage number from ftlctrlblocks + + uint32_t clean_flag; // Set on context sync, reset on write, so obviously never + // zero in the context written to the flash + + uint8_t field_130[0x15C]; // Seems to be unused, but gets loaded by Whimory. + +} __attribute__((packed)) FTLCxtType; + + +typedef struct FTLVFLCxtType // One per bank. Keeps the state of the bank's VFL. +{ + + uint32_t usn; // Cross-bank update sequence number, incremented on every VFL + // context commit on any bank. + + uint16_t ftlctrlblocks[3]; // See ftl_cxt.ftlctrlblocks. This is stored to the VFL contexts + // in order to be able to find the most recent FTL context copy + // when mounting the FTL. The VFL context number this will be + // written to on an FTL context commit is chosen semi-randomly. + + uint8_t field_A[2]; // Alignment to 32 bits + + uint32_t updatecount; // Decrementing update counter for VFL context commits per bank + + uint16_t activecxtblock; // Number of the currently active VFL context block, it's an index + // into vflcxtblocks. + + uint16_t nextcxtpage; // Number of the first free page in the active FTL context block + + uint8_t field_14[4]; // Seems to be unused + + uint16_t field_18; // Seems to be unused + + uint16_t spareused; // Number of spare blocks used + + uint16_t firstspare; // pBlock number of the first spare block + + uint16_t sparecount; // Total number of spare blocks + + uint16_t remaptable[0x334]; // Block remap table. Contains the vBlock number the n-th spare + // block is used as a replacement for. 0 = unused, 0xFFFF = bad. + + uint8_t bbt[0x11A]; // Bad block table. Each bit represents 8 blocks. 1 = OK, 0 = Bad. + // If the entry is zero, you should look at the remap table to see + // if the block is remapped, and if yes, where the replacement is. + + uint16_t vflcxtblocks[4]; // pBlock numbers used to store the VFL context. This is a ring + // buffer. On a VFL context write, always 8 pages are written, + // and it passes if at least 4 of them can be read back. + + uint16_t scheduledstart; // Blocks scheduled for remapping are stored at the end of the + // remap table. This is the first index used for them. + + uint8_t field_7AC[0x4C]; // Probably padding + + uint32_t checksum1; // First checksum (addition) + + uint32_t checksum2; // Second checksum (XOR), there is a bug in whimory regarding this. + +} __attribute__((packed)) FTLVFLCxtType; + + +typedef union FTLSpareDataType // Layout of the spare bytes of each page +{ + + struct FTLSpareDataFTLType // The layout used for actual user data (types 0x40 and 0x41) + { + + uint32_t lpn; // The lPage, i.e. Sector, number + + uint32_t usn; // The update sequence number of that page, see ftl_cxt.nextblockusn + + uint8_t field_8; // Seems to be unused + + uint8_t type; // Type field, 0x40 (data page) or 0x41 (last data page of block) + + uint8_t eccmark; // ECC mark, usually 0xFF. If an error occurred while reading the + // page during a copying operation earlier, this will be 0x55. + + uint8_t field_B; // Seems to be unused + + uint8_t dataecc[0x28]; // ECC data for the user data + + uint8_t spareecc[0xC]; // ECC data for the first 0xC bytes above + + } __attribute__((packed)) ftl; + + struct FTLSpareDataVFLType // The layout used for meta data (other types) + { + + uint32_t usn; // ftl_cxt.usn for FTL stuff, ftl_vfl_cxt.updatecount for VFL stuff + + uint16_t idx; // Index of the thing inside the page, i.e. number of the map page + + uint8_t field_6; // Seems to be unused + + uint8_t field_7; // Seems to be unused + + uint8_t field_8; // Seems to be unused + + uint8_t type; // Type field: + // 0x43: FTL context page + // 0x44: Block map page + // 0x46: Erase counter page + // 0x47: "FTL is currently mounted", i.e. unclean shutdown, mark + // 0x80: VFL context page + + uint8_t eccmark; // ECC mark, usually 0xFF. If an error occurred while reading the + // page during a copying operation earlier, this will be 0x55. + + uint8_t field_B; // Seems to be unused + + uint8_t dataecc[0x28]; // ECC data for the user data + + uint8_t spareecc[0xC]; // ECC data for the first 0xC bytes above + + } __attribute__((packed)) vfl; + +} FTLSpareDataType; + + +typedef struct FTLTroubleType // Keeps track (temporarily) of troublesome blocks +{ + + uint16_t block; // vBlock number of the block giving trouble + + uint8_t bank; // Bank of the block giving trouble + + uint8_t errors; // Error counter, incremented by 3 on error, decremented by 1 on erase, + // remap will happen when it reaches 6. + +} __attribute__((packed)) FTLTroubleType; + + + +const NANDDeviceType* ftl_nand_type; // Pointer to an info structure regarding the flash type used + +uint32_t ftl_banks; // Number of banks we detected a chip on + +uint16_t ftl_map[0x2000]; // Block map, used vor pBlock to vBlock mapping + +FTLVFLCxtType ftl_vfl_cxt[4]; // VFL context for each bank + +FTLCxtType ftl_cxt; // FTL context + +uint8_t ftl_buffer[0x800]; // Temporary data buffer for internal use by the FTL + +FTLSpareDataType ftl_sparebuffer; // Temporary spare byte buffer for internal use by the FTL + + +#ifndef FTL_READONLY + +uint8_t ftl_bbt[4][0x410]; // Lowlevel BBT for each bank + +uint16_t ftl_erasectr[0x2000]; // Erase countes for the vBlocks + +uint16_t ftl_offsets[0x11][0x200]; // Used by ftl_log + +FTLLogType ftl_log[0x11]; // Structs keeping record of scattered page blocks + +uint32_t ftl_vfl_usn; // Global cross-bank update sequence number of the VFL context + +FTLTroubleType ftl_troublelog[5]; // Keeps track (temporarily) of troublesome blocks + +uint8_t ftl_erasectr_dirt[8]; // Counts erase counter page changes, after 100 the affected + // page will be committed to the flash. +#endif + + + +// Finds a device info page for the specified bank and returns its number. +// Used to check if one is present, and to read the lowlevel BBT. +uint32_t ftl_find_devinfo(uint32_t bank) +{ + uint32_t lowestBlock = (*ftl_nand_type).blocks - ((*ftl_nand_type).blocks / 10); + uint32_t block, page, pagenum; + for (block = (*ftl_nand_type).blocks - 1; block >= lowestBlock; block--) + { + page = (*ftl_nand_type).pagesPerBlock - 8; + for (; page < (*ftl_nand_type).pagesPerBlock; page++) + { + pagenum = block * (*ftl_nand_type).pagesPerBlock + page; + if ((nand_read_page(bank, pagenum, ftl_buffer, &ftl_sparebuffer, 1, 0) & 0x11F) != 0) + continue; + if (memcmp(ftl_buffer, "DEVICEINFOSIGN\0", 0x10) == 0) return pagenum; + } + } + return 0; +} + + +// Checks if all banks have proper device info pages +uint32_t ftl_has_devinfo() +{ + uint32_t i; + for (i = 0; i < ftl_banks; i++) if (ftl_find_devinfo(i) == 0) return 0; + return 1; +} + + +// Loads the lowlevel BBT for a bank to the specified pointer +uint32_t ftl_load_bbt(uint32_t bank, uint8_t* bbt) +{ + uint32_t i, j; + uint32_t pagebase, page = ftl_find_devinfo(bank), page2; + uint32_t unk1, unk2, unk3; + if (page == 0) return 1; + pagebase = page & ~((*ftl_nand_type).pagesPerBlock - 1); + if ((nand_read_page(bank, page, ftl_buffer, (uint32_t*)0, 1, 0) & 0x11F) != 0) return 1; + if (memcmp(&ftl_buffer[0x18], "BBT", 4) != 0) return 1; + unk1 = ((uint16_t*)ftl_buffer)[0x10]; + unk2 = ((uint16_t*)ftl_buffer)[0x11]; + unk3 = ((uint16_t*)ftl_buffer)[((uint32_t*)ftl_buffer)[4] * 0xC + 10] + + ((uint16_t*)ftl_buffer)[((uint32_t*)ftl_buffer)[4] * 0xC + 11]; + for (i = 0; i < unk1; i++) + { + for (j = 0; ; j++) + { + page2 = unk2 + i + unk3 * j; + if (page2 >= (uint32_t)((*ftl_nand_type).pagesPerBlock - 8)) break; + if ((nand_read_page(bank, pagebase + page2, ftl_buffer, (void*)0, 1, 0) & 0x11F) == 0) + { + memcpy(bbt, ftl_buffer, 0x410); + return 0; + } + } + } + return 1; +} + + +// Calculates the checksums for the VFL context page of the specified bank +void ftl_vfl_calculate_checksum(uint32_t bank, uint32_t* checksum1, uint32_t* checksum2) +{ + uint32_t i; + *checksum1 = 0xAABBCCDD; + *checksum2 = 0xAABBCCDD; + for (i = 0; i < 0x1FE; i++) + { + *checksum1 += ((uint32_t*)(&ftl_vfl_cxt[bank]))[i]; + *checksum2 ^= ((uint32_t*)(&ftl_vfl_cxt[bank]))[i]; + } +} + + +// Checks if the checksums of the VFL context of the specified bank are correct +uint32_t ftl_vfl_verify_checksum(uint32_t bank) +{ + uint32_t checksum1, checksum2; + ftl_vfl_calculate_checksum(bank, &checksum1, &checksum2); + if (checksum1 == ftl_vfl_cxt[bank].checksum1) return 0; + if (checksum2 != ftl_vfl_cxt[bank].checksum2) return 0; // Obvious whimory bug! + return 1; +} + + +#ifndef FTL_READONLY +// Updates the checksums of the VFL context of the specified bank +void ftl_vfl_update_checksum(uint32_t bank) +{ + ftl_vfl_calculate_checksum(bank, &ftl_vfl_cxt[bank].checksum1, &ftl_vfl_cxt[bank].checksum2); +} +#endif + + +#ifndef FTL_READONLY +// Writes 8 copies of the VFL context of the specified bank to flash, +// and succeeds if at least 4 can be read back properly. +uint32_t ftl_vfl_store_cxt(uint32_t bank) +{ + uint32_t i; + ftl_vfl_cxt[bank].updatecount--; + ftl_vfl_cxt[bank].usn = ++ftl_vfl_usn; + ftl_vfl_cxt[bank].nextcxtpage += 8; + ftl_vfl_update_checksum(bank); + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.vfl.usn = ftl_vfl_cxt[bank].updatecount; + ftl_sparebuffer.vfl.field_8 = 0; + ftl_sparebuffer.vfl.type = 0x80; + for (i = 1; i <= 8; i++) + { + uint32_t page = ftl_vfl_cxt[bank].vflcxtblocks[ftl_vfl_cxt[bank].activecxtblock] + * (*ftl_nand_type).pagesPerBlock + ftl_vfl_cxt[bank].nextcxtpage - i; + nand_write_page(bank, page, &ftl_vfl_cxt[bank], &ftl_sparebuffer, 1); + } + uint32_t good = 0; + for (i = 0; i < 8; i++) + { + uint32_t page = ftl_vfl_cxt[bank].vflcxtblocks[ftl_vfl_cxt[bank].activecxtblock] + * (*ftl_nand_type).pagesPerBlock + ftl_vfl_cxt[bank].nextcxtpage - i; + if ((nand_read_page(bank, page, ftl_buffer, &ftl_sparebuffer, 1, 0) & 0x11F) != 0) + continue; + if (memcmp(ftl_buffer, &ftl_vfl_cxt[bank], 0x7AC) != 0) continue; + if (ftl_sparebuffer.vfl.usn != ftl_vfl_cxt[bank].updatecount) continue; + if (ftl_sparebuffer.vfl.field_8 == 0 && ftl_sparebuffer.vfl.type == 0x80) good++; + } + return good > 3 ? 0 : 1; +} +#endif + + +#ifndef FTL_READONLY +// Commits the VFL context of the specified bank to flash, +// retries until it works or all available pages have been tried +uint32_t ftl_vfl_commit_cxt(uint32_t bank) +{ +#ifdef VFL_DEBUG + debug_printf("FTL: VFL: Committing context on bank %d\n", bank); +#endif + if (ftl_vfl_cxt[bank].nextcxtpage + 8 <= (*ftl_nand_type).pagesPerBlock) + if (ftl_vfl_store_cxt(bank) == 0) return 0; + uint32_t current = ftl_vfl_cxt[bank].activecxtblock; + uint32_t i = current, j; + while (1) + { + i = (i + 1) & 3; + if (i == current) break; + if (ftl_vfl_cxt[bank].vflcxtblocks[i] == 0xFFFF) continue; + for (j = 0; j < 4; j++) + if (nand_block_erase(bank, ftl_vfl_cxt[bank].vflcxtblocks[i] + * (*ftl_nand_type).pagesPerBlock) == 0) break; + if (j == 4) continue; + ftl_vfl_cxt[bank].activecxtblock = i; + ftl_vfl_cxt[bank].nextcxtpage = 0; + if (ftl_vfl_store_cxt(bank) == 0) return 0; + } + return 1; +} +#endif + + +// Returns a pointer to the most recently updated VFL context, +// used to find out the current FTL context vBlock numbers (planetbeing's "maxthing") +FTLVFLCxtType* ftl_vfl_get_newest_cxt() +{ + uint32_t i, maxusn; + FTLVFLCxtType* cxt; + maxusn = 0; + for (i = 0; i < ftl_banks; i++) + if (ftl_vfl_cxt[i].usn >= maxusn) + { + cxt = &ftl_vfl_cxt[i]; + maxusn = ftl_vfl_cxt[i].usn; + } + return cxt; +} + + +// Checks if the specified pBlock is marked bad in the supplied lowlevel BBT. +// Only used while mounting the VFL. +uint32_t ftl_is_good_block(uint8_t* bbt, uint32_t block) +{ + if ((bbt[block >> 3] & (1 << (block & 7))) == 0) return 0; + else return 1; +} + + +// Checks if the specified vBlock could be remapped +uint32_t ftl_vfl_is_good_block(uint32_t bank, uint32_t block) +{ + if ((ftl_vfl_cxt[bank].bbt[block >> 6] & (1 << ((7 - (block >> 3)) & 7))) == 0) return 0; + else return 1; +} + + +#ifndef FTL_READONLY +// Sets or unsets the bad bit of the specified vBlock in the specified bank's VFL context +void ftl_vfl_set_good_block(uint32_t bank, uint32_t block, uint32_t isgood) +{ + if (isgood == 1) ftl_vfl_cxt[bank].bbt[block >> 6] |= 1 << ((7 - (block >> 3)) & 7); + else ftl_vfl_cxt[bank].bbt[block >> 6] &= ~(1 << ((7 - (block >> 3)) & 7)); +} +#endif + + +// Tries to read a VFL context from the specified bank, pBlock and page +uint32_t ftl_vfl_read_page(uint32_t bank, uint32_t block, uint32_t page, + void* databuffer, FTLSpareDataType* sparebuffer) +{ + uint32_t i; + for (i = 0; i < 8; i++) + if ((nand_read_page(bank, block * (*ftl_nand_type).pagesPerBlock + page + i, + databuffer, sparebuffer, 1, 1) & 0x11F) == 0) + if ((*sparebuffer).vfl.field_8 == 0 && (*sparebuffer).vfl.type == 0x80) return 0; + return 1; +} + + +// Translates a bank and vBlock to a pBlock, following remaps +uint32_t ftl_vfl_get_physical_block(uint32_t bank, uint32_t block) +{ + if (ftl_vfl_is_good_block(bank, block) == 1) return block; + + uint32_t spareindex; + for (spareindex = 0; spareindex < ftl_vfl_cxt[bank].spareused; spareindex++) + { + if (ftl_vfl_cxt[bank].remaptable[spareindex] == block) + { +#ifdef VFL_TRACE + debug_printf("FTL: VFL: Following remapped block: %d => %d\n", + block, ftl_vfl_cxt[bank].firstspare + spareindex); +#endif + return ftl_vfl_cxt[bank].firstspare + spareindex; + } + } + + return block; +} + + +#ifndef FTL_READONLY +// Checks if remapping is scheduled for the specified bank and vBlock +uint32_t ftl_vfl_check_remap_scheduled(uint32_t bank, uint32_t block) +{ + uint32_t i; + for (i = 0x333; i > 0 && i > ftl_vfl_cxt[bank].scheduledstart; i--) + if (ftl_vfl_cxt[bank].remaptable[i] == block) return 1; + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Schedules remapping for the specified bank and vBlock +void ftl_vfl_schedule_block_for_remap(uint32_t bank, uint32_t block) +{ + if (ftl_vfl_check_remap_scheduled(bank, block) == 1) return; + if (ftl_vfl_cxt[bank].scheduledstart == ftl_vfl_cxt[bank].spareused) return; + ftl_vfl_cxt[bank].remaptable[--ftl_vfl_cxt[bank].scheduledstart] = block; + ftl_vfl_commit_cxt(bank); +} +#endif + + +#ifndef FTL_READONLY +// Removes the specified bank and vBlock combination from the remap scheduled list +void ftl_vfl_mark_remap_done(uint32_t bank, uint32_t block) +{ + uint32_t i; + uint32_t start = ftl_vfl_cxt[bank].scheduledstart; + for (i = 0x333; i > 0 && i > start; i--) + if (ftl_vfl_cxt[bank].remaptable[i] == block) + { + if (i != start && i != 0x333) + ftl_vfl_cxt[bank].remaptable[i] = ftl_vfl_cxt[bank].remaptable[start]; + ftl_vfl_cxt[bank].scheduledstart++; + return; + } +} +#endif + + +#ifndef FTL_READONLY +// Logs that there is trouble for the specified vBlock on the specified bank. +// The vBlock will be scheduled for remap of there is too much trouble with it. +void ftl_vfl_log_trouble(uint32_t bank, uint32_t vblock) +{ + uint32_t i; + for (i = 0; i < 5; i++) + if (ftl_troublelog[i].block == vblock && ftl_troublelog[i].bank == bank) + { + ftl_troublelog[i].errors += 3; + if (ftl_troublelog[i].errors > 5) + { + ftl_vfl_schedule_block_for_remap(bank, vblock); + ftl_troublelog[i].block = 0xFFFF; + } + return; + } + for (i = 0; i < 5; i++) + if (ftl_troublelog[i].block == 0xFFFF) + { + ftl_troublelog[i].block = vblock; + ftl_troublelog[i].bank = bank; + ftl_troublelog[i].errors = 3; + return; + } +} +#endif + + +#ifndef FTL_READONLY +// Logs a successful erase for the specified vBlock on the specified bank +void ftl_vfl_log_success(uint32_t bank, uint32_t vblock) +{ + uint32_t i; + for (i = 0; i < 5; i++) + if (ftl_troublelog[i].block == vblock && ftl_troublelog[i].bank == bank) + { + if (--ftl_troublelog[i].errors == 0) ftl_troublelog[i].block = 0xFFFF; + return; + } +} +#endif + + +#ifndef FTL_READONLY +// Tries to remap the specified vBlock on the specified bank, not caring about data in there. +// If it worked, it will return the new pBlock number, if not (no more spare blocks), it will zero. +uint32_t ftl_vfl_remap_block(uint32_t bank, uint32_t block) +{ + uint32_t i; + uint32_t newblock = 0, newidx; + if (bank >= ftl_banks || block >= (*ftl_nand_type).blocks) return 0; + for (i = 0; i < ftl_vfl_cxt[bank].sparecount; i++) + if (ftl_vfl_cxt[bank].remaptable[i] == 0) + { + newblock = ftl_vfl_cxt[bank].firstspare + i; + newidx = i; + break; + } + if (newblock == 0) return 0; + for (i = 0; i < 9; i++) + if (nand_block_erase(bank, newblock * (*ftl_nand_type).pagesPerBlock) == 0) break; + for (i = 0; i < newidx; i++) + if (ftl_vfl_cxt[bank].remaptable[i] == block) + ftl_vfl_cxt[bank].remaptable[i] = 0xFFFF; + ftl_vfl_cxt[bank].remaptable[newidx] = block; + ftl_vfl_cxt[bank].spareused++; + ftl_vfl_set_good_block(bank, block, 0); + return newblock; +} +#endif + + +// Reads the specified vPage, dealing with all kinds of trouble +uint32_t ftl_vfl_read(uint32_t vpage, void* buffer, void* sparebuffer, + uint32_t checkempty, uint32_t remaponfail) +{ +#ifdef VFL_TRACE + debug_printf("FTL: VFL: Reading page %d\n", vpage); +#endif + + uint32_t syshyperblocks = (*ftl_nand_type).blocks - (*ftl_nand_type).userBlocks - 0x17; + uint32_t abspage = vpage + ((*ftl_nand_type).pagesPerBlock * ftl_banks * syshyperblocks); + if (abspage >= (uint32_t)((*ftl_nand_type).blocks * ftl_banks * (*ftl_nand_type).pagesPerBlock) + || abspage < (uint32_t)((*ftl_nand_type).pagesPerBlock * ftl_banks)) + { +#ifdef VFL_DEBUG + debug_printf("FTL: VFL: vPage %d is out of range!\n", vpage); +#endif + return 4; + } + + uint32_t bank = abspage % ftl_banks; + uint32_t block = abspage / ((*ftl_nand_type).pagesPerBlock * ftl_banks); + uint32_t page = (abspage / ftl_banks) % (*ftl_nand_type).pagesPerBlock; + uint32_t physblock = ftl_vfl_get_physical_block(bank, block); + uint32_t physpage = physblock * (*ftl_nand_type).pagesPerBlock + page; + + uint32_t ret = nand_read_page(bank, physpage, buffer, sparebuffer, 1, checkempty); + + if ((ret & 0x11D) != 0 && (ret & 2) == 0) + { + nand_reset(bank); + ret = nand_read_page(bank, physpage, buffer, sparebuffer, 1, checkempty); +#ifndef FTL_READONLY + if (remaponfail == 1 &&(ret & 0x11D) != 0 && (ret & 2) == 0) + { +#ifdef VFL_DEBUG + debug_printf("FTL: VFL: Scheduling vBlock %d for remapping!\n", block); +#endif + ftl_vfl_schedule_block_for_remap(bank, block); + } +#endif + return ret; + } + + return ret; +} + + +#ifndef FTL_READONLY +// Writes the specified vPage, dealing with all kinds of trouble +uint32_t ftl_vfl_write(uint32_t vpage, void* buffer, void* sparebuffer) +{ +#ifdef VFL_TRACE + debug_printf("FTL: VFL: Writing page %d\n", vpage); +#endif + + uint32_t syshyperblocks = ((*ftl_nand_type).blocks - (*ftl_nand_type).userBlocks - 0x17); + uint32_t abspage = vpage + ((*ftl_nand_type).pagesPerBlock * ftl_banks * syshyperblocks); + if (abspage >= (uint32_t)((*ftl_nand_type).blocks * ftl_banks * (*ftl_nand_type).pagesPerBlock) + || abspage < (uint32_t)((*ftl_nand_type).pagesPerBlock * ftl_banks)) + { +#ifdef VFL_DEBUG + debug_printf("FTL: VFL: vPage %d is out of range!\n", vpage); +#endif + return 1; + } + + uint32_t bank = abspage % ftl_banks; + uint32_t block = abspage / ((*ftl_nand_type).pagesPerBlock * ftl_banks); + uint32_t page = (abspage / ftl_banks) % (*ftl_nand_type).pagesPerBlock; + uint32_t physblock = ftl_vfl_get_physical_block(bank, block); + uint32_t physpage = physblock * (*ftl_nand_type).pagesPerBlock + page; + + if (nand_write_page(bank, physpage, buffer, sparebuffer, 1) == 0) return 0; +#ifdef VFL_TRACE + debug_printf("FTL: VFL: Trouble writing bank %d page %d, trying readback\n", bank, physpage); +#endif + if ((nand_read_page(bank, physpage, ftl_buffer, &ftl_sparebuffer, 1, 1) & 0x11F) == 0) + return 0; +#ifdef VFL_TRACE + debug_printf("FTL: VFL: Readback failed, logging problem\n", bank, physpage); +#endif + ftl_vfl_log_trouble(bank, block); + return 1; +} +#endif + + +// Stub for some FTL repair function I haven't investigated yet. +uint32_t sub_22005DEC() +{ + return 0; +} + + +// Stub for some FTL repair function I haven't investigated yet. +void sub_22005934(uint32_t unk) +{ +} + +// Stub for some FTL repair function I haven't investigated yet. +uint32_t sub_22005A28() +{ + return 1; +} + + +// Stub for some FTL repair function I haven't investigated yet. +uint32_t sub_2200300C() +{ + return 1; +} + + +// Stub for some FTL repair function I haven't investigated yet. +void sub_2200B1F8() +{ +} + + +// Stub for some FTL repair function I haven't investigated yet. +// Gets called by ftl_open() if it fails. +uint32_t ftl_restore() +{ + return 1; +} + + +// Mounts the VFL on all banks +uint32_t ftl_vfl_open() +{ + uint32_t i, j, k; + uint32_t minusn, vflcxtidx, last; + FTLVFLCxtType* cxt; + uint16_t vflcxtblock[4]; +#ifndef FTL_READONLY + ftl_vfl_usn = 0; +#else + uint8_t bbt[0x410]; // Temporary BBT buffer if we're readonly, as we won't need it again +#endif + for (i = 0; i < ftl_banks; i++) +#ifndef FTL_READONLY + if (ftl_load_bbt(i, ftl_bbt[i]) == 0) +#else + if (ftl_load_bbt(i, bbt) == 0) +#endif + { + for (j = 1; j <= (*ftl_nand_type).blocks - (*ftl_nand_type).userBlocks - 0x18; j++) +#ifndef FTL_READONLY + if (ftl_is_good_block(ftl_bbt[i], j) != 0) +#else + if (ftl_is_good_block(bbt, j) != 0) +#endif + if (ftl_vfl_read_page(i, j, 0, ftl_buffer, &ftl_sparebuffer) == 0) + { + memcpy(vflcxtblock, &(*((FTLVFLCxtType*)ftl_buffer)).vflcxtblocks, 8); + minusn = 0xFFFFFFFF; + vflcxtidx = 4; + for (k = 0; k < 4; k++) + if (vflcxtblock[k] != 0xFFFF) + if (ftl_vfl_read_page(i, vflcxtblock[k], 0, + ftl_buffer, &ftl_sparebuffer) == 0) + if (ftl_sparebuffer.vfl.usn > 0 + && ftl_sparebuffer.vfl.usn <= minusn) + { + minusn = ftl_sparebuffer.vfl.usn; + vflcxtidx = k; + } + if (vflcxtidx == 4) return 1; + last = 0; + for (k = 8; k < (*ftl_nand_type).pagesPerBlock; k += 8) + { + if (ftl_vfl_read_page(i, vflcxtblock[vflcxtidx], + k, ftl_buffer, &ftl_sparebuffer) != 0) + break; + last = k; + } + if (ftl_vfl_read_page(i, vflcxtblock[vflcxtidx], + last, ftl_buffer, &ftl_sparebuffer) != 0) + return 1; + memcpy(&ftl_vfl_cxt[i], ftl_buffer, 0x800); + if (ftl_vfl_verify_checksum(i) != 0) return 1; +#ifndef FTL_READONLY + if (ftl_vfl_usn < ftl_vfl_cxt[i].usn) ftl_vfl_usn = ftl_vfl_cxt[i].usn; +#endif + break; + } + } + else return 1; + cxt = ftl_vfl_get_newest_cxt(); + for (i = 0; i < ftl_banks; i++) memcpy(ftl_vfl_cxt[i].ftlctrlblocks, (*cxt).ftlctrlblocks, 6); + return 0; +} + + +// Mounts the actual FTL +uint32_t ftl_open() +{ + uint32_t i; + uint32_t ret; + FTLVFLCxtType* cxt = ftl_vfl_get_newest_cxt(); + + uint32_t ftlcxtblock = 0xffffffff; + uint32_t minlpn = 0xffffffff; + for (i = 0; i < 3; i++) + { + ret = ftl_vfl_read((*ftl_nand_type).pagesPerBlock * ftl_banks * (*cxt).ftlctrlblocks[i], + ftl_buffer, &ftl_sparebuffer, 1, 0); + if ((ret &= 0x11F) != 0) continue; + if (ftl_sparebuffer.ftl.type - 0x43 > 4) continue; + if (ftlcxtblock != 0xffffffff && ftl_sparebuffer.ftl.lpn >= minlpn) continue; + minlpn = ftl_sparebuffer.ftl.lpn; + ftlcxtblock = (*cxt).ftlctrlblocks[i]; + } + + if (ftlcxtblock == 0xffffffff) + { +#ifdef FTL_DEBUG + debug_printf("FTL: Cannot find FTL context block!\n"); +#endif + goto ftl_open_error; + } + +#ifdef FTL_DEBUG + debug_printf("FTL: Found FTL context block: vBlock %d\n", ftlcxtblock); +#endif + + uint32_t ftlcxtfound = 0; + for (i = (*ftl_nand_type).pagesPerBlock * ftl_banks - 1; i > 0; i--) + { + ret = ftl_vfl_read((*ftl_nand_type).pagesPerBlock * ftl_banks * ftlcxtblock + i, + ftl_buffer, &ftl_sparebuffer, 1, 0); + if ((ret & 0x11F) != 0) continue; + else if (ftl_sparebuffer.ftl.type == 0x43) + { + memcpy(&ftl_cxt, ftl_buffer, 0x28C); + ftlcxtfound = 1; + break; + } + else + { + // This will trip if there was an unclean unmount before. +#ifdef FTL_DEBUG + debug_printf("FTL: Found junk block: %2X at %d\n", ftl_sparebuffer.ftl.type, + (*ftl_nand_type).pagesPerBlock * ftl_banks * ftlcxtblock + i); +#endif + break; + } + } + + if (ftlcxtfound == 0) goto ftl_open_error; + +#ifdef FTL_DEBUG + debug_printf("FTL: Successfully read FTL context block\n"); +#endif + + uint32_t pagestoread = (*ftl_nand_type).userBlocks >> 10; + if (((*ftl_nand_type).userBlocks & 0x1FF) != 0) pagestoread++; + + for (i = 0; i < pagestoread; i++) + { + if ((ftl_vfl_read(ftl_cxt.ftl_map_pages[i], + ftl_buffer, &ftl_sparebuffer, 1, 1) & 0x11F) != 0) + goto ftl_open_error; + + uint32_t toread = 2048; + if (toread > ((*ftl_nand_type).userBlocks << 1) - (i << 11)) + toread = ((*ftl_nand_type).userBlocks << 1) - (i << 11); + + memcpy(&ftl_map[i << 10], ftl_buffer, toread); + } + +#ifndef FTL_READONLY + pagestoread = ((*ftl_nand_type).userBlocks + 23) >> 10; + if ((((*ftl_nand_type).userBlocks + 23) & 0x1FF) != 0) pagestoread++; + + for (i = 0; i < pagestoread; i++) + { + if ((ftl_vfl_read(ftl_cxt.ftl_erasectr_pages[i], + ftl_buffer, &ftl_sparebuffer, 1, 1) & 0x11F) != 0) + goto ftl_open_error; + + uint32_t toread = 2048; + if (toread > (((*ftl_nand_type).userBlocks + 23) << 1) - (i << 11)) + toread = (((*ftl_nand_type).userBlocks + 23) << 1) - (i << 11); + + memcpy(&ftl_erasectr[i << 10], ftl_buffer, toread); + } + + for (i = 0; i < 0x11; i++) + { + ftl_log[i].scatteredvblock = 0xFFFF; + ftl_log[i].logicalvblock = 0xFFFF; + ftl_log[i].pageoffsets = ftl_offsets[i]; + } + + memset(ftl_troublelog, 0xFF, 20); + memset(ftl_erasectr_dirt, 0, 8); +#endif + +#ifdef FTL_DEBUG + uint32_t j, k; + for (i = 0; i < ftl_banks; i++) + { + uint32_t badblocks = 0; +#ifndef FTL_READONLY + for (j = 0; j < (*ftl_nand_type).blocks >> 3; j++) + { + uint8_t bbtentry = ftl_bbt[i][j]; + for (k = 0; k < 8; k++) if ((bbtentry & (1 << k)) == 0) badblocks++; + } + debug_printf("FTL: BBT for bank %d: %d bad blocks\n", i, badblocks); + badblocks = 0; +#endif + for (j = 0; j < ftl_vfl_cxt[i].sparecount; j++) + if (ftl_vfl_cxt[i].remaptable[j] == 0xFFFF) badblocks++; + debug_printf("FTL: VFL: Bank %d: %d of %d spare blocks are bad\n", + i, badblocks, ftl_vfl_cxt[i].sparecount); + debug_printf("FTL: VFL: Bank %d: %d blocks remapped\n", + i, ftl_vfl_cxt[i].spareused); + debug_printf("FTL: VFL: Bank %d: %d blocks scheduled for remapping\n", + i, 0x334 - ftl_vfl_cxt[i].scheduledstart); + } +#ifndef FTL_READONLY + uint32_t min = 0xFFFFFFFF, max = 0, total = 0; + for (i = 0; i < (*ftl_nand_type).userBlocks + 23; i++) + { + if (ftl_erasectr[i] > max) max = ftl_erasectr[i]; + if (ftl_erasectr[i] < min) min = ftl_erasectr[i]; + total += ftl_erasectr[i]; + } + debug_printf("FTL: Erase counters: Minimum: %d, maximum %d, average: %d, total: %d\n", + min, max, total / ((*ftl_nand_type).userBlocks + 23), total); +#endif +#endif + + return 0; + +ftl_open_error: +#ifdef FTL_DEBUG + debug_printf("FTL: ftl_open detected a problem!\n"); +#endif + + return ftl_restore(); +} + + +// Stub for some FTL repair function I haven't investigated yet. +uint32_t sub_220061E8() +{ + return 1; +} + + +// Stub for some FTL repair function I haven't investigated yet. +// This seems to be called when a backlevel FTL is found. +uint32_t ftl_upgrade_v3() +{ +/* for (i = 0; i < ftl_banks; i++) + if (ftl_load_bbt(i) == 0) + { + ; + } + else return 1; +*/ return 1; +} + + +#ifndef FTL_READONLY +// Returns a pointer to the ftl_log entry for the specified vBlock, or null, if there is none +FTLLogType* ftl_get_log_entry(uint32_t block) +{ + uint32_t i; + for (i = 0; i < 0x11; i++) + { + if (ftl_log[i].scatteredvblock == 0xFFFF) continue; + if (ftl_log[i].logicalvblock == block) return &ftl_log[i]; + } + return (FTLLogType*)0; +} +#endif + +// Exposed function: Reads highlevel sectors +uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer) +{ + uint32_t i; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t error = 0; + +#ifdef FTL_TRACE + debug_printf("FTL: Reading %d sectors starting at %d\n", count, sector); +#endif + + if (sector + count > (*ftl_nand_type).userBlocks * ppb) + { +#ifdef FTL_DEBUG + debug_printf("FTL: Sector %d is out of range!\n", sector + count - 1); +#endif + return 1; + } + if (count == 0) return 0; + + for (i = 0; i < count; i++) + { + uint32_t block = (sector + i) / ppb; + uint32_t page = (sector + i) % ppb; + + uint32_t abspage = ftl_map[block] * ppb + page; +#ifndef FTL_READONLY + FTLLogType* logentry = ftl_get_log_entry(block); + if (logentry != (FTLLogType*)0) + { +#ifdef FTL_TRACE + debug_printf("FTL: Block %d has a log entry\n", block); +#endif + if ((*logentry).scatteredvblock != 0xFFFF && (*logentry).pageoffsets[page] != 0xFFFF) + { +#ifdef FTL_TRACE + debug_printf("FTL: Found page %d at block %d, page %d\n", page, + (*logentry).scatteredvblock, (*logentry).pageoffsets[page]); +#endif + abspage = (*logentry).scatteredvblock * ppb + (*logentry).pageoffsets[page]; + } + } +#endif + + uint32_t ret = ftl_vfl_read(abspage, &((uint8_t*)buffer)[i << 11], &ftl_sparebuffer, 1, 1); + if ((ret & 2) != 0) memset(&((uint8_t*)buffer)[i << 11], 0, 0x800); + if ((ret & 0x11F) != 0 || ftl_sparebuffer.ftl.eccmark != 0xFF) + { +#ifdef FTL_DEBUG + debug_printf("FTL: Error while reading sector %d!\n", (sector + i)); +#endif + error = 1; + memset(&((uint8_t*)buffer)[i << 11], 0, 0x800); + } + } + return error; +} + + +#ifndef FTL_READONLY +// Performs a vBlock erase, dealing with hardware, remapping and all kinds of trouble +uint32_t ftl_erase_block_internal(uint32_t block) +{ + uint32_t i, j; + block = block + (*ftl_nand_type).blocks - (*ftl_nand_type).userBlocks - 0x17; + if (block == 0 || block >= (*ftl_nand_type).blocks) return 1; + for (i = 0; i < ftl_banks; i++) + { + if (ftl_vfl_check_remap_scheduled(i, block) == 1) + { + ftl_vfl_remap_block(i, block); + ftl_vfl_mark_remap_done(i, block); + } + ftl_vfl_log_success(i, block); + uint32_t pblock = ftl_vfl_get_physical_block(i, block); + uint32_t rc; + for (j = 0; j < 3; j++) + { + rc = nand_block_erase(i, pblock * (*ftl_nand_type).pagesPerBlock); + if (rc == 0) break; + } + if (rc != 0) + { + if (pblock != block) + ftl_vfl_cxt[i].remaptable[pblock - ftl_vfl_cxt[i].firstspare] = 0xFFFF; + ftl_vfl_cxt[i].field_18++; + if (ftl_vfl_remap_block(i, block) == 0) return 1; + if (ftl_vfl_commit_cxt(i) != 0) return 1; + memset(&ftl_sparebuffer, 0, 0x40); + nand_write_page(i, pblock, &ftl_vfl_cxt[0], &ftl_sparebuffer, 1); + } + } + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Highlevel vBlock erase, that increments the erase counter for the block +uint32_t ftl_erase_block(uint32_t block) +{ + ftl_erasectr[block]++; + if (ftl_erasectr_dirt[block >> 10] == 100) ftl_cxt.erasedirty = 1; + else ftl_erasectr_dirt[block >> 10]++; + return ftl_erase_block_internal(block); +} +#endif + + +#ifndef FTL_READONLY +// Allocates a block from the pool, returning its vBlock number, or 0 on error +uint32_t ftl_allocate_pool_block() +{ + uint32_t i; + uint32_t erasectr = 0xFFFFFFFF, bestidx, block; + for (i = 0; i < ftl_cxt.freecount; i++) + { + uint32_t idx = ftl_cxt.nextfreeidx + i; + if (idx >= 0x14) idx -= 0x14; + if (ftl_erasectr[ftl_cxt.blockpool[idx]] < erasectr) + { + erasectr = ftl_erasectr[ftl_cxt.blockpool[idx]]; + bestidx = idx; + } + } + block = ftl_cxt.blockpool[bestidx]; + if (bestidx != ftl_cxt.nextfreeidx) + { + ftl_cxt.blockpool[bestidx] = ftl_cxt.blockpool[ftl_cxt.nextfreeidx]; + ftl_cxt.blockpool[ftl_cxt.nextfreeidx] = block; + } + if (block > (*ftl_nand_type).userBlocks) return 0; + if (ftl_erase_block(block) != 0) return 0; + if (++ftl_cxt.nextfreeidx == 0x14) ftl_cxt.nextfreeidx = 0; + ftl_cxt.freecount--; + return block; +} +#endif + + +#ifndef FTL_READONLY +// Releases a vBlock back into the pool +void ftl_release_pool_block(uint32_t block) +{ + if (block >= (*ftl_nand_type).userBlocks + 0x17) return; + uint32_t idx = ftl_cxt.nextfreeidx + ftl_cxt.freecount++; + if (idx >= 0x14) idx -= 0x14; + ftl_cxt.blockpool[idx] = block; +} +#endif + + +#ifndef FTL_READONLY +// Commits the location of the FTL context blocks to a semi-randomly chosen VFL context +uint32_t ftl_store_ctrl_block_list() +{ + uint32_t i; + for (i = 0; i < ftl_banks; i++) + memcpy(ftl_vfl_cxt[i].ftlctrlblocks, ftl_cxt.ftlctrlblocks, 6); + return ftl_vfl_commit_cxt(ftl_vfl_usn % ftl_banks); +} +#endif + + +#ifndef FTL_READONLY +// Saves the n-th erase counter page to the flash, because it is too dirty or needs to be moved. +uint32_t ftl_save_erasectr_page(uint32_t index) +{ + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.vfl.usn = ftl_cxt.usn; + ftl_sparebuffer.vfl.idx = index; + ftl_sparebuffer.vfl.type = 0x46; + if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_erasectr[index << 10], &ftl_sparebuffer) != 0) + return 1; + if ((ftl_vfl_read(ftl_cxt.ftlctrlpage, ftl_buffer, &ftl_sparebuffer, 1, 1) & 0x11F) != 0) + return 1; + if (memcmp(ftl_buffer, &ftl_erasectr[index << 10], 0x800) != 0) return 1; + if (ftl_sparebuffer.vfl.type != 0x46) return 1; + if (ftl_sparebuffer.vfl.idx != index) return 1; + if (ftl_sparebuffer.vfl.usn != ftl_cxt.usn) return 1; + ftl_cxt.ftl_erasectr_pages[index] = ftl_cxt.ftlctrlpage; + ftl_erasectr_dirt[index] = 0; + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Increments ftl_cxt.ftlctrlpage to the next available FTL context page, +// allocating a new context block if neccessary. +uint32_t ftl_next_ctrl_pool_page() +{ + uint32_t i; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + if (++ftl_cxt.ftlctrlpage % ppb != 0) return 0; + for (i = 0; i < 3; i++) if ((ftl_cxt.ftlctrlblocks[i] + 1) * ppb == ftl_cxt.ftlctrlpage) break; + i = (i + 1) % 3; + uint32_t oldblock = ftl_cxt.ftlctrlblocks[i]; + uint32_t newblock = ftl_allocate_pool_block(); + if (newblock == 0) return 1; + ftl_cxt.ftlctrlblocks[i] = newblock; + ftl_cxt.ftlctrlpage = newblock * ppb; +#ifdef FTL_TRACE + debug_printf("Starting new FTL control block at %d\n", ftl_cxt.ftlctrlpage); +#endif + uint32_t pagestoread = ((*ftl_nand_type).userBlocks + 23) >> 10; + if ((((*ftl_nand_type).userBlocks + 23) & 0x1FF) != 0) pagestoread++; + for (i = 0; i < pagestoread; i++) + if (oldblock * ppb <= ftl_cxt.ftl_erasectr_pages[i] + && (oldblock + 1) * ppb > ftl_cxt.ftl_erasectr_pages[i]) + { + ftl_cxt.usn--; + if (ftl_save_erasectr_page(i) != 0) + { + ftl_cxt.ftlctrlblocks[i] = oldblock; + ftl_cxt.ftlctrlpage = oldblock * (ppb + 1) - 1; + ftl_release_pool_block(newblock); + return 1; + } + ftl_cxt.ftlctrlpage++; + } + ftl_release_pool_block(oldblock); + return ftl_store_ctrl_block_list(); +} +#endif + + +#ifndef FTL_READONLY +// Copies a vPage from one location to another +uint32_t ftl_copy_page(uint32_t source, uint32_t destination, uint32_t lpn, uint32_t type) +{ + uint8_t buffer[0x800]; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t rc = ftl_vfl_read(source, buffer, &ftl_sparebuffer, 1, 1) & 0x11F; + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.ftl.lpn = lpn; + ftl_sparebuffer.ftl.usn = ++ftl_cxt.nextblockusn; + ftl_sparebuffer.ftl.type = 0x40; + if ((rc & 2) != 0) memset(buffer, 0, 0x800); + else if (rc != 0) ftl_sparebuffer.ftl.eccmark = 0x55; + if (type == 1 && destination % ppb == ppb - 1) ftl_sparebuffer.ftl.type = 0x41; + return ftl_vfl_write(destination, buffer, &ftl_sparebuffer); +} +#endif + + +#ifndef FTL_READONLY +// Copies a pBlock to a vBlock +uint32_t ftl_copy_block(uint32_t source, uint32_t destination) +{ + uint32_t i; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t error = 0; + uint8_t buffer[0x800]; + ftl_cxt.nextblockusn++; + for (i = 0; i < ppb; i++) + { + uint32_t rc = ftl_read(source * ppb + i, 1, buffer) & 0x11D; + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.ftl.lpn = source * ppb + i; + ftl_sparebuffer.ftl.usn = ftl_cxt.nextblockusn; + ftl_sparebuffer.ftl.type = 0x40; + if (rc != 0) ftl_sparebuffer.ftl.eccmark = 0x55; + if (i == ppb - 1) ftl_sparebuffer.ftl.type = 0x41; + if (ftl_vfl_write(destination * ppb + i, buffer, &ftl_sparebuffer) != 0) + { + error = 1; + break; + } + } + if (error != 0) + { + ftl_erase_block(destination); + return 1; + } + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Clears ftl_log.issequential, if something violating that is written. +void ftl_check_still_sequential(FTLLogType* entry, uint32_t page) +{ + if ((*entry).pagesused != (*entry).pagescurrent || (*entry).pageoffsets[page] != page) + (*entry).issequential = 0; +} +#endif + + +#ifndef FTL_READONLY +// Copies all pages that are currently used from the scattered page block in use by the supplied +// ftl_log entry to a newly-allocated one, and releases the old one. +// In other words: It kicks the pages containing old garbage out of it to make space again. +// This is usually done when a scattered page block is being removed because it is full, but less +// than half of the pages in there are still in use, just filled with old crap. +uint32_t ftl_compact_scattered(FTLLogType* entry) +{ + uint32_t i, j; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t pagecount = (*entry).pagescurrent; + uint32_t error; + FTLLogType backup; + if ((*entry).pagescurrent == 0) + { + ftl_release_pool_block((*entry).scatteredvblock); + (*entry).scatteredvblock = 0xFFFF; + return 0; + } + backup = *entry; + for (i = 0; i < 4; i++) + { + uint32_t block = ftl_allocate_pool_block(); + (*entry).pagesused = 0; + (*entry).pagescurrent = 0; + (*entry).issequential = 1; + if (block == 0) return 1; + error = 0; + for (j = 0; j < ppb; j++) + if ((*entry).pageoffsets[j] != 0xFFFF) + { + uint32_t lpn = (*entry).logicalvblock * ppb + j; + uint32_t newpage = block * ppb + (*entry).pagesused; + uint32_t oldpage = (*entry).scatteredvblock * ppb + (*entry).pageoffsets[j]; + if (ftl_copy_page(oldpage, newpage, lpn, (*entry).issequential) != 0) + { + error = 1; + break; + } + (*entry).pageoffsets[j] = (*entry).pagesused++; + (*entry).pagescurrent++; + ftl_check_still_sequential(entry, j); + } + if (pagecount != (*entry).pagescurrent) error = 1; + if (error == 0) break; + *entry = backup; + } + return error; +} +#endif + + +#ifndef FTL_READONLY +// Commits an ftl_log entry to proper blocks, no matter what's in there. +uint32_t ftl_commit_scattered(FTLLogType* entry) +{ + uint32_t i; + uint32_t error; + uint32_t block; + for (i = 0; i < 4; i++) + { + block = ftl_allocate_pool_block(); + if (block == 0) return 1; + error = ftl_copy_block((*entry).logicalvblock, block); + if (error == 0) break; + ftl_release_pool_block(block); + } + if (error != 0) return 1; + ftl_release_pool_block((*entry).scatteredvblock); + (*entry).scatteredvblock = 0xFFFF; + ftl_release_pool_block(ftl_map[(*entry).logicalvblock]); + ftl_map[(*entry).logicalvblock] = block; + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Fills the rest of a scattered page block that was actually written sequentially until now, +// in order to be able to save a block erase by committing it without needing to copy it again. +// If this fails for whatever reason, it will be committed the usual way. +uint32_t ftl_commit_sequential(FTLLogType* entry) +{ + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t error = 0; + if ((*entry).issequential != 1 || (*entry).pagescurrent != (*entry).pagesused) return 1; + for (; (*entry).pagesused < ppb; (*entry).pagesused++) + { + uint32_t lpn = (*entry).logicalvblock * ppb + (*entry).pagesused; + uint32_t newpage = (*entry).scatteredvblock * ppb + (*entry).pagesused; + uint32_t oldpage = ftl_map[(*entry).logicalvblock] * ppb + (*entry).pagesused; + if ((*entry).pageoffsets[(*entry).pagesused] != 0xFFFF + || ftl_copy_page(oldpage, newpage, lpn, 1) != 0) + { + error = 1; + break; + } + } + if (error != 0) return ftl_commit_scattered(entry); + ftl_release_pool_block(ftl_map[(*entry).logicalvblock]); + ftl_map[(*entry).logicalvblock] = (*entry).scatteredvblock; + (*entry).scatteredvblock = 0xFFFF; + return 0; +} +#endif + + +#ifndef FTL_READONLY +// If a log entry is supplied, its scattered page block will be removed in whatever way seems +// most appropriate. Else, the oldest scattered page block will be freed by committing it. +uint32_t ftl_remove_scattered_block(FTLLogType* entry) +{ + uint32_t i; + uint32_t age = 0xFFFFFFFF, used = 0; + if (entry == (FTLLogType*)0) + { + for (i = 0; i < 0x11; i++) + { + if (ftl_log[i].scatteredvblock == 0xFFFF) continue; + if (ftl_log[i].pagesused == 0 || ftl_log[i].pagescurrent == 0) return 1; + if (ftl_log[i].usn < age || (ftl_log[i].usn == age && ftl_log[i].pagescurrent > used)) + { + age = ftl_log[i].usn; + used = ftl_log[i].pagescurrent; + entry = &ftl_log[i]; + } + } + if (entry == (FTLLogType*)0) return 1; + } + else if ((*entry).pagescurrent < ((*ftl_nand_type).pagesPerBlock * ftl_banks) / 2) + { + ftl_cxt.swapcounter++; + return ftl_compact_scattered(entry); + } + ftl_cxt.swapcounter++; + if ((*entry).issequential == 1) return ftl_commit_sequential(entry); + else return ftl_commit_scattered(entry); +} +#endif + + +#ifndef FTL_READONLY +// Initialize a log entry to the values for an empty scattered page block +void ftl_init_log_entry(FTLLogType* entry) +{ + (*entry).issequential = 1; + (*entry).pagescurrent = 0; + (*entry).pagesused = 0; + memset((*entry).pageoffsets, 0xFF, 0x400); +} +#endif + + +#ifndef FTL_READONLY +// Allocates a log entry for the specified vBlock, first making space, if neccessary. +FTLLogType* ftl_allocate_log_entry(uint32_t block) +{ + uint32_t i; + FTLLogType* entry = ftl_get_log_entry(block); + if (entry != (FTLLogType*)0) return entry; + + for (i = 0; i < 0x11; i++) + { + if (ftl_log[i].scatteredvblock == 0xFFFF) continue; + if (ftl_log[i].pagesused == 0) + { + entry = &ftl_log[i]; + break; + } + } + + if (entry == (FTLLogType*)0) + { + if (ftl_cxt.freecount == 3) + if (ftl_remove_scattered_block((FTLLogType*)0) != 0) return (FTLLogType*)0; + entry = ftl_log; + while ((*entry).scatteredvblock != 0xFFFF) entry = &entry[1]; + (*entry).scatteredvblock = ftl_allocate_pool_block(); + if ((*entry).scatteredvblock == 0) + { + (*entry).scatteredvblock = 0xFFFF; + return (FTLLogType*)0; + } + } + + (*entry).logicalvblock = block; + ftl_init_log_entry(entry); + (*entry).usn = ftl_cxt.nextblockusn - 1; + + return entry; +} +#endif + + +#ifndef FTL_READONLY +// Commits the FTL block map, erase counters, and context to flash +uint32_t ftl_commit_cxt() +{ + uint32_t i; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + uint32_t mappages = (*ftl_nand_type).userBlocks >> 10; + uint32_t ctrpages = ((*ftl_nand_type).userBlocks + 23) >> 10; + uint32_t endpage = ftl_cxt.ftlctrlpage + mappages + ctrpages + 1; +#ifdef FTL_TRACE + debug_printf("FTL: Committing context\n"); +#endif + if (endpage % ppb > ppb - 1) + ftl_cxt.ftlctrlpage |= ppb - 1; + for (i = 0; i < ctrpages; i++) + { + if (ftl_next_ctrl_pool_page() != 0) return 1; + if (ftl_save_erasectr_page(i) != 0) return 1; + } + for (i = 0; i < mappages; i++) + { + if (ftl_next_ctrl_pool_page() != 0) return 1; + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.vfl.usn = ftl_cxt.usn; + ftl_sparebuffer.vfl.idx = i; + ftl_sparebuffer.vfl.type = 0x44; + if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_map[i << 10], &ftl_sparebuffer) != 0) return 1; + ftl_cxt.ftl_map_pages[i] = ftl_cxt.ftlctrlpage; + } + if (ftl_next_ctrl_pool_page() != 0) return 1; + ftl_cxt.clean_flag = 1; + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.vfl.usn = ftl_cxt.usn; + ftl_sparebuffer.vfl.type = 0x43; + if (ftl_vfl_write(ftl_cxt.ftlctrlpage, &ftl_cxt, &ftl_sparebuffer) != 0) return 1; +#ifdef FTL_TRACE + debug_printf("FTL: Wrote context to page %d\n", ftl_cxt.ftlctrlpage); +#endif + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Swaps the most and least worn block on the flash, to better distribute wear. +// It will refuse to do anything if the wear spread is lower than 5 erases. +uint32_t ftl_swap_blocks() +{ + uint32_t i; + uint32_t min = 0xFFFFFFFF, max = 0, maxidx = 0x14, minidx, minvb, maxvb; + for (i = 0; i < ftl_cxt.freecount; i++) + { + uint32_t idx = ftl_cxt.nextfreeidx + i; + if (idx >= 0x14) idx -= 0x14; + if (ftl_erasectr[ftl_cxt.blockpool[idx]] > max) + { + maxidx = idx; + maxvb = ftl_cxt.blockpool[idx]; + max = ftl_erasectr[maxidx]; + } + } + if (maxidx == 0x14) return 0; + for (i = 0; i < (*ftl_nand_type).userBlocks; i++) + { + if (ftl_erasectr[ftl_map[i]] > max) max = ftl_erasectr[ftl_map[i]]; + if (ftl_get_log_entry(i) != 0) continue; + if (ftl_erasectr[ftl_map[i]] < min) + { + minidx = i; + minvb = ftl_map[i]; + min = ftl_erasectr[minidx]; + } + } + if (max - min < 5) return 0; + if (minvb == maxvb) return 0; + if (ftl_erase_block(maxvb) != 0) return 1; + if (ftl_copy_block(minidx, maxvb) != 0) return 1; + ftl_cxt.blockpool[maxidx] = minvb; + ftl_map[minidx] = maxvb; + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Exposed function: Writes highlevel sectors +uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer) +{ + uint32_t i, j; + uint32_t ppb = (*ftl_nand_type).pagesPerBlock * ftl_banks; + +#ifdef FTL_TRACE + debug_printf("FTL: Writing %d sectors starting at %d\n", count, sector); +#endif + + if (sector + count > (*ftl_nand_type).userBlocks * ppb) + { +#ifdef FTL_TRACE + debug_printf("FTL: Sector %d is out of range!\n", sector + count - 1); +#endif + return 1; + } + if (count == 0) return 0; + + if (ftl_cxt.clean_flag == 1) + { + for (i = 0; i < 3; i++) + { +#ifdef FTL_TRACE + debug_printf("FTL: Marking dirty, try %d\n", i); +#endif + if (ftl_next_ctrl_pool_page() != 0) return 1; + memset(ftl_buffer, 0xFF, 0x800); + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.vfl.usn = ftl_cxt.usn; + ftl_sparebuffer.vfl.type = 0x47; + if (ftl_vfl_write(ftl_cxt.ftlctrlpage, ftl_buffer, &ftl_sparebuffer) == 0) + break; + } + if (i == 3) return 1; +#ifdef FTL_TRACE + debug_printf("FTL: Wrote dirty mark to %d\n", ftl_cxt.ftlctrlpage); +#endif + ftl_cxt.clean_flag = 0; + } + + for (i = 0; i < count; ) + { + uint32_t block = (sector + i) / ppb; + uint32_t page = (sector + i) % ppb; + + FTLLogType* logentry = ftl_allocate_log_entry(block); + if (logentry == (FTLLogType*)0) return 1; + if (page == 0 && count - i >= ppb) + { +#ifdef FTL_TRACE + debug_printf("FTL: Going to write a full hyperblock in one shot\n"); +#endif + uint32_t vblock = (*logentry).scatteredvblock; + (*logentry).scatteredvblock = 0xFFFF; + if ((*logentry).pagesused != 0) + { +#ifdef FTL_TRACE + debug_printf("FTL: Scattered block had some pages already used, committing\n"); +#endif + ftl_release_pool_block(vblock); + vblock = ftl_allocate_pool_block(); + if (vblock == 0) return 1; + } + ftl_cxt.nextblockusn++; + for (j = 0; j < ppb; j++) + { + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.ftl.lpn = sector + i + j; + ftl_sparebuffer.ftl.usn = ftl_cxt.nextblockusn; + ftl_sparebuffer.ftl.type = 0x40; + if (j == ppb - 1) ftl_sparebuffer.ftl.type = 0x41; + while (ftl_vfl_write(vblock * ppb + j, &((uint8_t*)buffer)[(i + j) << 11], + &ftl_sparebuffer) != 0); + } + ftl_release_pool_block(ftl_map[block]); + ftl_map[block] = vblock; + i += ppb; + } + else + { + if ((*logentry).pagesused == ppb) + { +#ifdef FTL_TRACE + debug_printf("FTL: Scattered block is full, committing\n"); +#endif + ftl_remove_scattered_block(logentry); + logentry = ftl_allocate_log_entry(block); + if (logentry == (FTLLogType*)0) return 1; + } + memset(&ftl_sparebuffer, 0xFF, 0x40); + ftl_sparebuffer.ftl.lpn = sector + i; + ftl_sparebuffer.ftl.usn = ++ftl_cxt.nextblockusn; + ftl_sparebuffer.ftl.type = 0x40; + if (ftl_vfl_write((*logentry).scatteredvblock * ppb + (*logentry).pagesused++, + &((uint8_t*)buffer)[i << 11], &ftl_sparebuffer) == 0) + { + if ((*logentry).pageoffsets[page] == 0xFFFF) (*logentry).pagescurrent++; + (*logentry).pageoffsets[page] = (*logentry).pagesused - 1; + i++; + } + } + } + if (ftl_cxt.swapcounter >= 300) + { + ftl_cxt.swapcounter -= 20; + for (i = 0; i < 4; i++) if (ftl_swap_blocks() == 0) break; + } + if (ftl_cxt.erasedirty == 1) + { + ftl_cxt.erasedirty = 0; + for (i = 0; i < 8; i++) + if (ftl_erasectr_dirt[i] >= 100) + { + ftl_next_ctrl_pool_page(); + ftl_save_erasectr_page(i); + } + } + return 0; +} +#endif + + +#ifndef FTL_READONLY +// Exposed function: Performes a sync / unmount, i.e. commits all scattered page blocks, +// distributes wear, and commits the FTL context. +uint32_t ftl_sync() +{ + uint32_t i; + uint32_t rc = 0; + if (ftl_cxt.clean_flag == 1) return 0; + +#ifdef FTL_TRACE + debug_printf("FTL: Syncing\n"); +#endif + + if (ftl_cxt.swapcounter >= 20) + for (i = 0; i < 4; i++) + if (ftl_swap_blocks() == 0) + { + ftl_cxt.swapcounter -= 20; + break; + } + for (i = 0; i < 0x11; i++) + { + if (ftl_log[i].scatteredvblock == 0xFFFF) continue; + ftl_cxt.nextblockusn++; + if (ftl_log[i].issequential == 1) rc |= ftl_commit_sequential(&ftl_log[i]); + else rc |= ftl_commit_scattered(&ftl_log[i]); + } + if (rc == 0) + { + for (i = 0; i < 5; i++) + if (ftl_commit_cxt() == 0) return 0; + else ftl_cxt.ftlctrlpage |= (*ftl_nand_type).pagesPerBlock * ftl_banks - 1; + return 1; + } + return 0; +} +#endif + + +// Initializes and mounts the FTL. As long as nothing was written, you won't need to unmount it. +// Before shutting down after writing something, call ftl_sync(), +// which will just return if everything is clean anyways. +uint32_t ftl_init() +{ + uint32_t i; + uint32_t result, foundsignature, founddevinfo, blockwiped, repaired, skip; + if (nand_device_init() != 0) return 1; + ftl_banks = 0; + for (i = 0; i < 4; i++) if (nand_get_device_type(i) != 0) ftl_banks = i + 1; + ftl_nand_type = nand_get_device_type(0); + foundsignature = 0; + blockwiped = 1; + for (i = 0; i < (*ftl_nand_type).pagesPerBlock; i++) + { + result = nand_read_page(0, i, ftl_buffer, (uint32_t*)0, 1, 1); + if ((result & 0x11F) == 0) + { + blockwiped = 0; + if (((uint32_t*)ftl_buffer)[0] != 0x41303034) continue; +#ifdef FTL_TRACE + debug_printf("FTL: Found Whimory V4 header signature\n"); +#endif + foundsignature = 1; + break; + } + else if ((result & 2) != 2) blockwiped = 0; + } + + founddevinfo = ftl_has_devinfo(); + + repaired = 0; + skip = 0; + if (blockwiped != 0) + { + if (founddevinfo == 0) + { + if (sub_220061E8(0, 0) != 0) return 1; + repaired = 1; + } + } + else if (foundsignature == 0 && founddevinfo != 0 && ((uint32_t*)ftl_buffer)[0] == 0x41303033) + { +#ifdef FTL_DEBUG + debug_printf("FTL: Found Whimory V3 header signature!\n"); +#endif + if (ftl_upgrade_v3() == 0) foundsignature = 1; + } + if (blockwiped == 0 && founddevinfo == 0) + { + if (sub_22005DEC() != 0) return 1; + if (sub_220061E8(0, 0) != 0) return 1; + repaired = 1; + } + else if (foundsignature != 0) + { + if ((result & 0x11F) != 0) return 1; + skip = 1; + } + if (skip == 0) + { + if (repaired == 0) sub_22005934(0); + if (sub_22005A28() != 0) return 1; + if (sub_2200300C() != 0) return 1; + sub_2200B1F8(); + } + if (ftl_vfl_open() == 0) + if (ftl_open() == 0) return 0; +// nand_block_erase(0, 0); + return 1; +} Index: arm/s5l8700/ipodnano2g/ftl-target.h =================================================================== --- arm/s5l8700/ipodnano2g/ftl-target.h (revision 0) +++ arm/s5l8700/ipodnano2g/ftl-target.h (revision 0) @@ -0,0 +1,34 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2009 by Michael Sparmann + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __FTL_H__ +#define __FTL_H__ + +#include "config.h" +#include "inttypes.h" + +uint32_t ftl_init(void); +uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer); +uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer); +uint32_t ftl_sync(void); + + +#endif Index: arm/s5l8700/ipodnano2g/nand-nano2g.c =================================================================== --- arm/s5l8700/ipodnano2g/nand-nano2g.c (revision 0) +++ arm/s5l8700/ipodnano2g/nand-nano2g.c (revision 0) @@ -0,0 +1,411 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2009 by Michael Sparmann + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include "config.h" +//#include "system.h" +#include +//#include "cpu.h" +#include "s5l8701.h" +#include "inttypes.h" +#include "nand-target.h" +#include "mmu-s5l8700.h" + + +#define NAND_CMD_READ 0x00 +#define NAND_CMD_PROGCNFRM 0x10 +#define NAND_CMD_READ2 0x30 +#define NAND_CMD_BLOCKERASE 0x60 +#define NAND_CMD_GET_STATUS 0x70 +#define NAND_CMD_PROGRAM 0x80 +#define NAND_CMD_ERASECNFRM 0xD0 +#define NAND_CMD_RESET 0xFF + +#define NAND_STATUS_READY 0x40 + +#define nand_DeviceInfoTableEntries 33 + +static const NANDDeviceType nand_DeviceInfoTable[] = +{ + {0x1580F1EC, 1024, 968, 0x40, 6, 2, 1, 2, 1}, + {0x1580DAEC, 2048, 1936, 0x40, 6, 2, 1, 2, 1}, + {0x15C1DAEC, 2048, 1936, 0x40, 6, 2, 1, 2, 1}, + {0x1510DCEC, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0x95C1DCEC, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0x2514DCEC, 2048, 1936, 0x80, 7, 2, 1, 2, 1}, + {0x2514D3EC, 4096, 3872, 0x80, 7, 2, 1, 2, 1}, + {0x2555D3EC, 4096, 3872, 0x80, 7, 2, 1, 2, 1}, + {0x2555D5EC, 8192, 7744, 0x80, 7, 2, 1, 2, 1}, + {0x2585D3AD, 4096, 3872, 0x80, 7, 3, 2, 3, 2}, + {0x9580DCAD, 4096, 3872, 0x40, 6, 3, 2, 3, 2}, + {0xA514D3AD, 4096, 3872, 0x80, 7, 3, 2, 3, 2}, + {0xA550D3AD, 4096, 3872, 0x80, 7, 3, 2, 3, 2}, + {0xA560D5AD, 4096, 3872, 0x80, 7, 3, 2, 3, 2}, + {0xA555D5AD, 8192, 7744, 0x80, 7, 3, 2, 3, 2}, + {0xA585D598, 8320, 7744, 0x80, 7, 3, 1, 2, 1}, + {0xA584D398, 4160, 3872, 0x80, 7, 3, 1, 2, 1}, + {0x95D1D32C, 8192, 7744, 0x40, 6, 2, 1, 2, 1}, + {0x1580DC2C, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0x15C1D32C, 8192, 7744, 0x40, 6, 2, 1, 2, 1}, + {0x9590DC2C, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0xA594D32C, 4096, 3872, 0x80, 7, 2, 1, 2, 1}, + {0x2584DC2C, 2048, 1936, 0x80, 7, 2, 1, 2, 1}, + {0xA5D5D52C, 8192, 7744, 0x80, 7, 3, 2, 2, 1}, + {0x95D1D389, 8192, 7744, 0x40, 6, 2, 1, 2, 1}, + {0x1580DC89, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0x15C1D389, 8192, 7744, 0x40, 6, 2, 1, 2, 1}, + {0x9590DC89, 4096, 3872, 0x40, 6, 2, 1, 2, 1}, + {0xA594D389, 4096, 3872, 0x80, 7, 2, 1, 2, 1}, + {0x2584DC89, 2048, 1936, 0x80, 7, 2, 1, 2, 1}, + {0xA5D5D589, 8192, 7744, 0x80, 7, 2, 1, 2, 1}, + {0xA514D320, 4096, 3872, 0x80, 7, 2, 1, 2, 1}, + {0xA555D520, 8192, 3872, 0x80, 7, 2, 1, 2, 1} +}; + +uint8_t nand_tUNK1[4]; +uint8_t nand_tWP[4]; +uint8_t nand_tUNK2[4]; +uint8_t nand_tUNK3[4]; +uint32_t nand_type[4]; + +static uint8_t nand_aligned_data[0x800] __attribute__((aligned(32))); +static uint8_t nand_aligned_ctrl[0x200] __attribute__((aligned(32))); +static uint8_t nand_aligned_spare[0x40] __attribute__((aligned(32))); +static uint8_t nand_aligned_ecc[0x28] __attribute__((aligned(32))); +#define nand_uncached_data ((uint8_t*)(((uint32_t)nand_aligned_data) | 0x40000000)) +#define nand_uncached_ctrl ((uint8_t*)(((uint32_t)nand_aligned_ctrl) | 0x40000000)) +#define nand_uncached_spare ((uint8_t*)(((uint32_t)nand_aligned_spare) | 0x40000000)) +#define nand_uncached_ecc ((uint8_t*)(((uint32_t)nand_aligned_ecc) | 0x40000000)) + + +uint32_t nand_wait_rbbdone(void) +{ + uint32_t timeout = 0x40000; + while ((FMCSTAT & FMCSTAT_RBBDone) == 0) if (timeout-- == 0) return 1; + FMCSTAT = FMCSTAT_RBBDone; + return 0; +} + +uint32_t nand_wait_cmddone(void) +{ + uint32_t timeout = 0x40000; + while ((FMCSTAT & FMCSTAT_CMDDone) == 0) if (timeout-- == 0) return 1; + FMCSTAT = FMCSTAT_CMDDone; + return 0; +} + +uint32_t nand_wait_addrdone(void) +{ + uint32_t timeout = 0x40000; + while ((FMCSTAT & FMCSTAT_AddrDone) == 0) if (timeout-- == 0) return 1; + FMCSTAT = FMCSTAT_AddrDone; + return 0; +} + +uint32_t nand_wait_chip_ready(uint32_t bank) +{ + uint32_t timeout = 0x40000; + while ((FMCSTAT & (FMCSTAT_Bank0Ready << bank)) == 0) if (timeout-- == 0) return 1; + FMCSTAT = (FMCSTAT_Bank0Ready << bank); + return 0; +} + +void nand_set_fmctrl0(uint32_t bank, uint32_t flags) +{ + FMCTRL0 = (nand_tUNK1[bank] << 16) | (nand_tWP[bank] << 12) + | (1 << 11) | 1 | (1 << (bank + 1)) | flags; +} + +uint32_t nand_send_cmd(uint32_t cmd) +{ + FMCMD = cmd; + return nand_wait_rbbdone(); +} + +uint32_t nand_send_address(uint32_t page, uint32_t offset) +{ + FMCANUM = 4; + FMCADDR0 = (page << 16) | offset; + FMCADDR1 = (page >> 16) & 0xFF; + FMCTRL1 = FMCTRL1_DoTransAddr; + return nand_wait_cmddone(); +} + +uint32_t nand_reset(uint32_t bank) +{ + nand_set_fmctrl0(bank, 0); + if (nand_send_cmd(NAND_CMD_RESET) != 0) return 1; + if (nand_wait_chip_ready(bank) != 0) return 1; + FMCTRL1 = FMCTRL1_ClearRFIFO | FMCTRL1_ClearWFIFO; + return 0; +} + +uint32_t nand_wait_status_ready(uint32_t bank) +{ + uint32_t timeout = 0x4000; + nand_set_fmctrl0(bank, 0); + if ((FMCSTAT & (FMCSTAT_Bank0Ready << bank)) != 0) FMCSTAT = (FMCSTAT_Bank0Ready << bank); + FMCTRL1 = FMCTRL1_ClearRFIFO | FMCTRL1_ClearWFIFO; + if (nand_send_cmd(NAND_CMD_GET_STATUS) != 0) return 1; + while (1) + { + if (timeout-- == 0) return 1; + FMCDNUM = 0; + FMCTRL1 = FMCTRL1_DoReadData; + if (nand_wait_addrdone() != 0) return 1; + if ((FMCFIFO0 & NAND_STATUS_READY) != 0) break; + FMCTRL1 = FMCTRL1_ClearRFIFO; + } + FMCTRL1 = FMCTRL1_ClearRFIFO; + return nand_send_cmd(NAND_CMD_READ); +} + +uint32_t nand_transfer_data(uint32_t bank, uint32_t direction, void* buffer, uint32_t size) +{ + uint32_t timeout = 0x40000; + nand_set_fmctrl0(bank, FMCTRL0_EnableDMA); + FMCDNUM = size - 1; + FMCTRL1 = FMCTRL1_DoReadData << direction; + DMACON3 = (2 << DMACON_Device_Shift) | (direction << DMACON_Direction_Shift) + | (2 << DMACON_Data_Size_Shift) | (3 << DMACON_Burst_Len_Shift); + while ((DMAALLST & DMAALLST_CHAN3_Mask) != 0) DMACOM3 = DMACOM_ClearBothDone; + DMABASE3 = (uint32_t)buffer; + DMATCNT3 = (size >> 4) - 1; + DMACOM3 = 4; + while ((DMAALLST & DMAALLST_DMABUSY3) != 0) if (timeout-- == 0) return 1; + if (nand_wait_addrdone() != 0) return 1; + if (direction == 0) FMCTRL1 = FMCTRL1_ClearRFIFO | FMCTRL1_ClearWFIFO; + return 0; +} + +uint32_t ecc_decode(uint32_t size, void* databuffer, void* sparebuffer) +{ + uint32_t timeout = 0x40000; + ECCINTCLR = 1; + SRCPND = INT_ECC; + ECCUNK1 = size; + ECCDATAPTR = (uint32_t)databuffer; + ECCSPAREPTR = (uint32_t)sparebuffer; + ECCCTRL = ECCCTRL_StartDecoding; + while ((SRCPND & INT_ECC) == 0) if (timeout-- == 0) return 1; + ECCINTCLR = 1; + SRCPND = INT_ECC; + return ECCRESULT; +} + +uint32_t ecc_encode(uint32_t size, void* databuffer, void* sparebuffer) +{ + uint32_t timeout = 0x40000; + ECCINTCLR = 1; + SRCPND = INT_ECC; + ECCUNK1 = size; + ECCDATAPTR = (uint32_t)databuffer; + ECCSPAREPTR = (uint32_t)sparebuffer; + ECCCTRL = ECCCTRL_StartEncoding; + while ((SRCPND & INT_ECC) == 0) if (timeout-- == 0) return 1; + ECCINTCLR = 1; + SRCPND = INT_ECC; + return 0; +} + +uint32_t nand_check_empty(uint8_t* buffer) +{ + uint32_t i, count; + count = 0; + for (i = 0; i < 0x40; i++) if (buffer[i] != 0xFF) count++; + if (count < 2) return 1; + return 0; +} + +uint32_t nand_get_chip_type(uint32_t bank) +{ + uint32_t result; + if (nand_reset(bank) != 0) return 0xFFFFFFFF; + if (nand_send_cmd(0x90) != 0) return 0xFFFFFFFF; + FMCANUM = 0; + FMCADDR0 = 0; + FMCTRL1 = FMCTRL1_DoTransAddr; + if (nand_wait_cmddone() != 0) return 0xFFFFFFFF; + FMCDNUM = 4; + FMCTRL1 = FMCTRL1_DoReadData; + if (nand_wait_addrdone() != 0) return 0xFFFFFFFF; + result = FMCFIFO0; + FMCTRL1 = FMCTRL1_ClearRFIFO | FMCTRL1_ClearWFIFO; + return result; +} + +uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer, void* sparebuffer, + uint32_t doecc, uint32_t checkempty) +{ + uint32_t rc, eccresult; +#ifdef NAND_TRACE + debug_printf("NAND: Read bank %d, page %d\n", bank, page); +#endif + nand_set_fmctrl0(bank, FMCTRL0_EnableDMA); + if (nand_send_cmd(NAND_CMD_READ) != 0) return 1; + if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0) return 1; + if (nand_send_cmd(NAND_CMD_READ2) != 0) return 1; + if (nand_wait_status_ready(bank) != 0) return 1; + if (databuffer != 0) if (nand_transfer_data(bank, 0, nand_uncached_data, 0x800) != 0) return 1; + if (doecc == 0) + { + memcpy(databuffer, nand_uncached_data, 0x800); + if (sparebuffer != 0) + { + if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0) return 1; + memcpy(sparebuffer, nand_uncached_spare, 0x800); + if (checkempty != 0) return nand_check_empty((uint8_t*)sparebuffer) << 1; + } + return 0; + } + rc = 0; + if (nand_transfer_data(bank, 0, nand_uncached_spare, 0x40) != 0) return 1; + memcpy(nand_uncached_ecc, &nand_uncached_spare[0xC], 0x28); + rc |= (ecc_decode(3, nand_uncached_data, nand_uncached_ecc) & 0xF) << 4; + if (databuffer != 0) memcpy(databuffer, nand_uncached_data, 0x800); + memset(nand_uncached_ctrl, 0xFF, 0x200); + memcpy(nand_uncached_ctrl, nand_uncached_spare, 0xC); + memcpy(nand_uncached_ecc, &nand_uncached_spare[0x34], 0xC); + eccresult = ecc_decode(0, nand_uncached_ctrl, nand_uncached_ecc); + rc |= (eccresult & 0xF) << 8; + if (sparebuffer != 0) + { + memcpy(sparebuffer, nand_uncached_spare, 0x40); + if ((eccresult & 1) != 0) memset(sparebuffer, 0xFF, 0xC); + else memcpy(sparebuffer, nand_uncached_ctrl, 0xC); + } + if (checkempty != 0) rc |= nand_check_empty(nand_uncached_spare) << 1; +#ifdef NAND_DEBUG + if ((rc & 2) == 0) + { + if ((rc & 0x10) != 0) + debug_printf("NAND: ECC failed to correct bank %d page %d user data!\n", + bank, page); + if ((rc & 0xE0) != 0) + debug_printf("NAND: ECC corrected %d errors in bank %d page %d user data!\n", + rc >> 5, bank, page); + if ((rc & 0x100) != 0) + debug_printf("NAND: ECC failed to correct bank %d page %d control data!\n", + bank, page); + if ((rc & 0xE00) != 0) + debug_printf("NAND: ECC corrected %d errors in bank %d page %d control data!\n", + (rc >> 9) & 7, bank, page); + } +#ifdef NAND_TRACE + else debug_printf("NAND: Bank %d page %d: Erased page!\n", bank, page); +#endif +#endif + +#ifdef NAND_TRACE + debug_printf("NAND: Read success, RC=%X\n", rc); +#endif + return rc; +} + +uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer, void* sparebuffer, + uint32_t doecc) +{ +#ifdef NAND_TRACE + debug_printf("NAND: Write bank %d, page %d\n", bank, page); +#endif + if (sparebuffer != 0) memcpy(nand_uncached_spare, sparebuffer, 0x40); + else memset(nand_uncached_spare, 0xFF, 0x40); + if (doecc != 0) + { + memcpy(nand_uncached_data, databuffer, 0x800); + if (ecc_encode(3, nand_uncached_data, nand_uncached_ecc) != 0) return 1; + memcpy(&nand_uncached_spare[0xC], nand_uncached_ecc, 0x28); + memset(nand_uncached_ctrl, 0xFF, 0x200); + memcpy(nand_uncached_ctrl, nand_uncached_spare, 0xC); + if (ecc_encode(0, nand_uncached_ctrl, nand_uncached_ecc) != 0) return 1; + memcpy(&nand_uncached_spare[0x34], nand_uncached_ecc, 0xC); + } + nand_set_fmctrl0(bank, FMCTRL0_EnableDMA); + if (nand_send_cmd(NAND_CMD_PROGRAM) != 0) return 1; + if (nand_send_address(page, (databuffer == 0) ? 0x800 : 0) != 0) return 1; + if (databuffer != 0) if (nand_transfer_data(bank, 1, nand_uncached_data, 0x800) != 0) return 1; + if (sparebuffer != 0 || doecc != 0) + if (nand_transfer_data(bank, 1, nand_uncached_spare, 0x40) != 0) return 1; + if (nand_send_cmd(NAND_CMD_PROGCNFRM) != 0) return 1; + return nand_wait_status_ready(bank); +} + +uint32_t nand_block_erase(uint32_t bank, uint32_t page) +{ +#ifdef NAND_TRACE + debug_printf("NAND: Block erase starting at bank %d, page %d\n", bank, page); +#endif + nand_set_fmctrl0(bank, 0); + if (nand_send_cmd(NAND_CMD_BLOCKERASE) != 0) return 1; + FMCANUM = 2; + FMCADDR0 = page; + FMCTRL1 = FMCTRL1_DoTransAddr; + if (nand_wait_cmddone() != 0) return 1; + if (nand_send_cmd(NAND_CMD_ERASECNFRM) != 0) return 1; + return nand_wait_status_ready(bank); +} + +const NANDDeviceType* nand_get_device_type(uint32_t bank) +{ + if (nand_type[bank] == 0xFFFFFFFF) return (NANDDeviceType*)0; + return &nand_DeviceInfoTable[nand_type[bank]]; +} + +uint32_t nand_device_init(void) +{ + uint32_t type; + uint32_t i, j; + PCON2 = 0x33333333; + PDAT2 = 0; + PCON3 = 0x11113333; + PDAT3 = 0; + PCON4 = 0x33333333; + PDAT4 = 0; + for (i = 0; i < 4; i++) + { + nand_tUNK1[i] = 7; + nand_tWP[i] = 7; + nand_tUNK2[i] = 7; + nand_tUNK3[i] = 7; + type = nand_get_chip_type(i); +#ifdef NAND_DEBUG + if (type == 0xFFFFFFFF) debug_printf("NAND: Bank %d: No chip found\n", i); + else debug_printf("NAND: Bank %d: Chip type %8X\n", i, type); +#endif + nand_type[i] = 0xFFFFFFFF; + if (type == 0xFFFFFFFF) continue; + for (j = 0; ; j++) + { + if (j == nand_DeviceInfoTableEntries) break; + else if (nand_DeviceInfoTable[j].id == type) + { + nand_type[i] = j; + break; + } + } + nand_tUNK1[i] = nand_DeviceInfoTable[nand_type[i]].tUNK1; + nand_tWP[i] = nand_DeviceInfoTable[nand_type[i]].tWP; + nand_tUNK2[i] = nand_DeviceInfoTable[nand_type[i]].tUNK2; + nand_tUNK3[i] = nand_DeviceInfoTable[nand_type[i]].tUNK3; + } + if (nand_type[0] == 0xFFFFFFFF) return 1; + return 0; +} Index: arm/s5l8700/ipodnano2g/nand-target.h =================================================================== --- arm/s5l8700/ipodnano2g/nand-target.h (revision 0) +++ arm/s5l8700/ipodnano2g/nand-target.h (revision 0) @@ -0,0 +1,53 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2009 by Michael Sparmann + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __NAND_H__ +#define __NAND_H__ + +#include "config.h" +#include "inttypes.h" + + +typedef struct NANDDeviceType +{ + uint32_t id; + uint16_t blocks; + uint32_t userBlocks; + uint16_t pagesPerBlock; + uint8_t blockSizeExponent; + uint8_t tUNK1; + uint8_t tWP; + uint8_t tUNK2; + uint8_t tUNK3; +} __attribute__((packed)) NANDDeviceType; + +uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer, void* sparebuffer, + uint32_t doecc, uint32_t checkempty); +uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer, void* sparebuffer, + uint32_t doecc); +uint32_t nand_block_erase(uint32_t bank, uint32_t page); + +const NANDDeviceType* nand_get_device_type(uint32_t bank); +uint32_t nand_reset(uint32_t bank); +uint32_t nand_device_init(void); + + +#endif Index: arm/s5l8700/ipodnano2g/s5l8701.h =================================================================== --- arm/s5l8700/ipodnano2g/s5l8701.h (revision 0) +++ arm/s5l8700/ipodnano2g/s5l8701.h (revision 0) @@ -0,0 +1,225 @@ +// +// +// Copyright 2009 TheSeven +// +// +// This file is part of iLoader. +// +// TheSeven's iBugger 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. +// +// TheSeven's iBugger 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 iLoader. If not, see . +// +// + + +#ifndef __HARDWARE__S5L8700_H__ +#define __HARDWARE__S5L8700_H__ + +/////CLKCON///// +#define CLKCON *((volatile uint32_t*)(0x3C500000)) +#define PLL0PMS *((volatile uint32_t*)(0x3C500004)) +#define PLL1PMS *((volatile uint32_t*)(0x3C500008)) +#define PLL2PMS *((volatile uint32_t*)(0x3C50000C)) +#define PLL0LCNT *((volatile uint32_t*)(0x3C500014)) +#define PLL1LCNT *((volatile uint32_t*)(0x3C500018)) +#define PLL2LCNT *((volatile uint32_t*)(0x3C50001C)) +#define PLLLOCK *((volatile uint32_t*)(0x3C500020)) +#define PLLCON *((volatile uint32_t*)(0x3C500024)) +#define PWRCON *((volatile uint32_t*)(0x3C500028)) +#define PWRMODE *((volatile uint32_t*)(0x3C50002C)) +#define SWRCON *((volatile uint32_t*)(0x3C500030)) +#define RSTSR *((volatile uint32_t*)(0x3C500034)) +#define DSPCLKMD *((volatile uint32_t*)(0x3C500038)) +#define CLKCON2 *((volatile uint32_t*)(0x3C50003C)) +#define PWRCONEXT *((volatile uint32_t*)(0x3C500040)) + + +/////ICU///// +#define SRCPND *((volatile uint32_t*)(0x39C00000)) +#define INTMOD *((volatile uint32_t*)(0x39C00004)) +#define INTMSK *((volatile uint32_t*)(0x39C00008)) +#define INTPRIO *((volatile uint32_t*)(0x39C0000C)) +#define INTPND *((volatile uint32_t*)(0x39C00010)) +#define INTOFFSET *((volatile uint32_t*)(0x39C00014)) +#define EINTPOL *((volatile uint32_t*)(0x39C00018)) +#define EINTPEND *((volatile uint32_t*)(0x39C0001C)) +#define EINTMSK *((volatile uint32_t*)(0x39C00020)) + + +/////GPIO///// +#define PCON0 *((volatile uint32_t*)(0x3CF00000)) +#define PDAT0 *((volatile uint32_t*)(0x3CF00004)) +#define PCON1 *((volatile uint32_t*)(0x3CF00010)) +#define PDAT1 *((volatile uint32_t*)(0x3CF00014)) +#define PCON2 *((volatile uint32_t*)(0x3CF00020)) +#define PDAT2 *((volatile uint32_t*)(0x3CF00024)) +#define PCON3 *((volatile uint32_t*)(0x3CF00030)) +#define PDAT3 *((volatile uint32_t*)(0x3CF00034)) +#define PCON4 *((volatile uint32_t*)(0x3CF00040)) +#define PDAT4 *((volatile uint32_t*)(0x3CF00044)) +#define PCON5 *((volatile uint32_t*)(0x3CF00050)) +#define PDAT5 *((volatile uint32_t*)(0x3CF00054)) +#define PCON6 *((volatile uint32_t*)(0x3CF00060)) +#define PDAT6 *((volatile uint32_t*)(0x3CF00064)) +#define PCON7 *((volatile uint32_t*)(0x3CF00070)) +#define PDAT7 *((volatile uint32_t*)(0x3CF00074)) +#define PCON10 *((volatile uint32_t*)(0x3CF00010)) + + +/////IODMA///// +#define DMABASE0 *((volatile uint32_t*)(0x38400000)) +#define DMACON0 *((volatile uint32_t*)(0x38400004)) +#define DMATCNT0 *((volatile uint32_t*)(0x38400008)) +#define DMACADDR0 *((volatile uint32_t*)(0x3840000C)) +#define DMACTCNT0 *((volatile uint32_t*)(0x38400010)) +#define DMACOM0 *((volatile uint32_t*)(0x38400014)) +#define DMANOF0 *((volatile uint32_t*)(0x38400018)) +#define DMABASE1 *((volatile uint32_t*)(0x38400020)) +#define DMACON1 *((volatile uint32_t*)(0x38400024)) +#define DMATCNT1 *((volatile uint32_t*)(0x38400028)) +#define DMACADDR1 *((volatile uint32_t*)(0x3840002C)) +#define DMACTCNT1 *((volatile uint32_t*)(0x38400030)) +#define DMACOM1 *((volatile uint32_t*)(0x38400034)) +#define DMABASE2 *((volatile uint32_t*)(0x38400040)) +#define DMACON2 *((volatile uint32_t*)(0x38400044)) +#define DMATCNT2 *((volatile uint32_t*)(0x38400048)) +#define DMACADDR2 *((volatile uint32_t*)(0x3840004C)) +#define DMACTCNT2 *((volatile uint32_t*)(0x38400050)) +#define DMACOM2 *((volatile uint32_t*)(0x38400054)) +#define DMABASE3 *((volatile uint32_t*)(0x38400060)) +#define DMACON3 *((volatile uint32_t*)(0x38400064)) +#define DMATCNT3 *((volatile uint32_t*)(0x38400068)) +#define DMACADDR3 *((volatile uint32_t*)(0x3840006C)) +#define DMACTCNT3 *((volatile uint32_t*)(0x38400070)) +#define DMACOM3 *((volatile uint32_t*)(0x38400074)) +#define DMABASE4 *((volatile uint32_t*)(0x38400080)) +#define DMACON4 *((volatile uint32_t*)(0x38400084)) +#define DMATCNT4 *((volatile uint32_t*)(0x38400088)) +#define DMACADDR4 *((volatile uint32_t*)(0x3840008C)) +#define DMACTCNT4 *((volatile uint32_t*)(0x38400090)) +#define DMACOM4 *((volatile uint32_t*)(0x38400094)) +#define DMABASE5 *((volatile uint32_t*)(0x384000A0)) +#define DMACON5 *((volatile uint32_t*)(0x384000A4)) +#define DMATCNT5 *((volatile uint32_t*)(0x384000A8)) +#define DMACADDR5 *((volatile uint32_t*)(0x384000AC)) +#define DMACTCNT5 *((volatile uint32_t*)(0x384000B0)) +#define DMACOM5 *((volatile uint32_t*)(0x384000B4)) +#define DMABASE6 *((volatile uint32_t*)(0x384000C0)) +#define DMACON6 *((volatile uint32_t*)(0x384000C4)) +#define DMATCNT6 *((volatile uint32_t*)(0x384000C8)) +#define DMACADDR6 *((volatile uint32_t*)(0x384000CC)) +#define DMACTCNT6 *((volatile uint32_t*)(0x384000D0)) +#define DMACOM6 *((volatile uint32_t*)(0x384000D4)) +#define DMABASE7 *((volatile uint32_t*)(0x384000E0)) +#define DMACON7 *((volatile uint32_t*)(0x384000E4)) +#define DMATCNT7 *((volatile uint32_t*)(0x384000E8)) +#define DMACADDR7 *((volatile uint32_t*)(0x384000EC)) +#define DMACTCNT7 *((volatile uint32_t*)(0x384000F0)) +#define DMACOM7 *((volatile uint32_t*)(0x384000F4)) +#define DMAALLST *((volatile uint32_t*)(0x38400180)) +#define DMACON_Device_Shift 30 +#define DMACON_Direction_Shift 29 +#define DMACON_Data_Size_Shift 22 +#define DMACON_Burst_Len_Shift 19 +#define DMACOM_Start 4 +#define DMACOM_ClearBothDone 7 +#define DMAALLST_WCOM0 (1 << 0) +#define DMAALLST_HCOM0 (1 << 1) +#define DMAALLST_DMABUSY0 (1 << 2) +#define DMAALLST_HOLD_SKIP (1 << 3) +#define DMAALLST_WCOM1 (1 << 4) +#define DMAALLST_HCOM1 (1 << 5) +#define DMAALLST_DMABUSY1 (1 << 6) +#define DMAALLST_WCOM2 (1 << 8) +#define DMAALLST_HCOM2 (1 << 9) +#define DMAALLST_DMABUSY2 (1 << 10) +#define DMAALLST_WCOM3 (1 << 12) +#define DMAALLST_HCOM3 (1 << 13) +#define DMAALLST_DMABUSY3 (1 << 14) +#define DMAALLST_CHAN0_Mask (0xF << 0) +#define DMAALLST_CHAN1_Mask (0xF << 4) +#define DMAALLST_CHAN2_Mask (0xF << 8) +#define DMAALLST_CHAN3_Mask (0xF << 12) + + +/////FMC///// +#define FMCTRL0 *((volatile uint32_t*)(0x39400000)) +#define FMCTRL1 *((volatile uint32_t*)(0x39400004)) +#define FMCMD *((volatile uint32_t*)(0x39400008)) +#define FMCADDR0 *((volatile uint32_t*)(0x3940000C)) +#define FMCADDR1 *((volatile uint32_t*)(0x39400010)) +#define FMCANUM *((volatile uint32_t*)(0x3940002C)) +#define FMCDNUM *((volatile uint32_t*)(0x39400030)) +#define FMCSTAT *((volatile uint32_t*)(0x39400048)) +#define FMCFIFO0 *((volatile uint32_t*)(0x39400080)) +#define RS_ECC_CTRL *((volatile uint32_t*)(0x39400100)) +#define FMCTRL0_EnableDMA (1 << 10) +#define FMCTRL0_UNK1 (1 << 11) +#define FMCTRL1_DoTransAddr (1 << 0) +#define FMCTRL1_DoReadData (1 << 1) +#define FMCTRL1_DoWriteData (1 << 2) +#define FMCTRL1_ClearWFIFO (1 << 6) +#define FMCTRL1_ClearRFIFO (1 << 7) +#define FMCSTAT_RBB (1 << 0) +#define FMCSTAT_RBBDone (1 << 1) +#define FMCSTAT_CMDDone (1 << 2) +#define FMCSTAT_AddrDone (1 << 3) +#define FMCSTAT_Bank0Ready (1 << 4) +#define FMCSTAT_Bank1Ready (1 << 5) +#define FMCSTAT_Bank2Ready (1 << 6) +#define FMCSTAT_Bank3Ready (1 << 7) + + +/////ECC///// +#define ECCDATAPTR *((volatile uint32_t*)(0x39E00004)) +#define ECCSPAREPTR *((volatile uint32_t*)(0x39E00008)) +#define ECCCTRL *((volatile uint32_t*)(0x39E0000C)) +#define ECCRESULT *((volatile uint32_t*)(0x39E00010)) +#define ECCUNK1 *((volatile uint32_t*)(0x39E00014)) +#define ECCINTCLR *((volatile uint32_t*)(0x39E00040)) +#define INT_ECC 0x80000 +#define ECCCTRL_StartDecoding (1 << 0) +#define ECCCTRL_StartEncoding (1 << 1) +#define ECCCTRL_StartDecNoSynd (1 << 2) + + +/////CLICKWHEEL///// +#define WHEEL00 *((volatile uint32_t*)(0x3C200000)) +#define WHEEL10 *((volatile uint32_t*)(0x3C200010)) +#define WHEELINT *((volatile uint32_t*)(0x3C200014)) +#define WHEELDATA *((volatile uint32_t*)(0x3C200018)) + + +/////AES///// +#define AESCONTROL *((volatile uint32_t*)(0x39800000)) +#define AESGO *((volatile uint32_t*)(0x39800004)) +#define AESUNKREG0 *((volatile uint32_t*)(0x39800008)) +#define AESSTATUS *((volatile uint32_t*)(0x3980000C)) +#define AESUNKREG1 *((volatile uint32_t*)(0x39800010)) +#define AESKEYLEN *((volatile uint32_t*)(0x39800014)) +#define AESOUTSIZE *((volatile uint32_t*)(0x39800018)) +#define AESOUTADDR *((volatile uint32_t*)(0x39800020)) +#define AESINSIZE *((volatile uint32_t*)(0x39800024)) +#define AESINADDR *((volatile uint32_t*)(0x39800028)) +#define AESAUXSIZE *((volatile uint32_t*)(0x3980002C)) +#define AESAUXADDR *((volatile uint32_t*)(0x39800030)) +#define AESSIZE3 *((volatile uint32_t*)(0x39800034)) +#define AESTYPE *((volatile uint32_t*)(0x3980006C)) + + +/////HASH///// +#define HASHCTRL *((volatile uint32_t*)(0x3C600000)) +#define HASHRESULT ((volatile uint32_t*)(0x3C600020)) +#define HASHDATAIN ((volatile uint32_t*)(0x3C600040)) + + +#endif Index: arm/s5l8700/mmu-s5l8700.h =================================================================== --- arm/s5l8700/mmu-s5l8700.h (revision 0) +++ arm/s5l8700/mmu-s5l8700.h (revision 0) @@ -0,0 +1,58 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: mmu-arm.h 21647 2009-07-05 01:17:25Z saratoga $ + * + * Copyright (C) 2006,2007 by Greg White + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* This file MUST be included in your system-target.h file if you want arm + * cache coherence functions to be called (I.E. during codec load, etc). + */ + +#ifndef MMU_S5L8700_H +#define MMU_S5L8700_H + +/* Cleans entire DCache */ +void clean_dcache(void); + +/* Invalidate entire DCache */ +/* will do writeback */ +void invalidate_dcache(void); + +/* Invalidate DCache for this range */ +/* will do writeback */ +void invalidate_dcache_range(const void *base, unsigned int size); + +/* clean DCache for this range */ +/* forces DCache writeback for the specified range */ +void clean_dcache_range(const void *base, unsigned int size); + +/* Dump DCache for this range */ +/* Will *NOT* do write back except for buffer ends not on a line boundary */ +void dump_dcache_range(const void *base, unsigned int size); + +/* Invalidate entire ICache and DCache */ +/* will do writeback */ +void invalidate_idcache(void); + +void disable_dcache(void); +void enable_dcache(void); + +#define HAVE_CPUCACHE_INVALIDATE +#define HAVE_CPUCACHE_FLUSH + +#endif /* MMU_S5L8700_H */ Index: arm/s5l8700/mmu-s5l8700.S =================================================================== --- arm/s5l8700/mmu-s5l8700.S (revision 0) +++ arm/s5l8700/mmu-s5l8700.S (revision 0) @@ -0,0 +1,277 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: mmu-arm.S 19980 2009-02-11 23:56:00Z jethead71 $ + * + * Copyright (C) 2006,2007 by Greg White + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "cpu.h" + +/** Cache coherency **/ + +/* + * Invalidate DCache for this range + * will do write back + * void invalidate_dcache_range(const void *base, unsigned int size); + */ + .section .text, "ax", %progbits + .align 2 + .global invalidate_dcache_range + .type invalidate_dcache_range, %function + @ MVA format: 31:5 = Modified virtual address, 4:0 = SBZ +invalidate_dcache_range: + add r1, r0, r1 @ size -> end + cmp r1, r0 @ end <= start? + bxls lr @ + bic r0, r0, #31 @ Align start to cache line (down) +1: @ inv_start @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + add r0, r0, #32 @ + cmp r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + bhi 1b @ inv_start @ + mov r0, #0 @ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + bx lr @ + .size invalidate_dcache_range, .-invalidate_dcache_range + +/* + * clean DCache for this range + * forces DCache writeback for the specified range + * void clean_dcache_range(const void *base, unsigned int size); + */ + .section .text, "ax", %progbits + .align 2 + .global clean_dcache_range + .type clean_dcache_range, %function + @ MVA format: 31:5 = Modified virtual address, 4:0 = SBZ +clean_dcache_range: + add r1, r0, r1 @ size -> end + cmp r1, r0 @ end <= start? + bxls lr @ + bic r0, r0, #31 @ Align start to cache line (down) +1: @ clean_start @ + mcr p15, 0, r0, c7, c10, 2 @ Clean line by MVA + add r0, r0, #32 @ + cmp r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c10, 2 @ Clean line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + bhi 1b @clean_start @ + mov r0, #0 @ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + bx lr @ + .size clean_dcache_range, .-clean_dcache_range + +/* + * Dump DCache for this range + * will *NOT* do write back except for buffer edges not on a line boundary + * void dump_dcache_range(const void *base, unsigned int size); + */ + .section .text, "ax", %progbits + .align 2 + .global dump_dcache_range + .type dump_dcache_range, %function + @ MVA format: 31:5 = Modified virtual address, 4:0 = SBZ + dump_dcache_range: + add r1, r0, r1 @ size -> end + cmp r1, r0 @ end <= start? + bxls lr @ + tst r0, #31 @ Check first line for bits set + bicne r0, r0, #31 @ Clear low five bits (down) + mcrne p15, 0, r0, c7, c14, 2 @ Clean and invalidate line by MVA + @ if not cache aligned + addne r0, r0, #32 @ Move to the next cache line + @ + tst r1, #31 @ Check last line for bits set + bicne r1, r1, #31 @ Clear low five bits (down) + mcrne p15, 0, r1, c7, c14, 2 @ Clean and invalidate line by MVA + @ if not cache aligned + cmp r1, r0 @ end <= start now? +1: @ dump_start @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + mcrhi p15, 0, r0, c7, c6, 2 @ Invalidate line by MVA + addhi r0, r0, #32 @ + cmphi r1, r0 @ + bhi 1b @ dump_start @ + mov r0, #0 @ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + bx lr @ + .size dump_dcache_range, .-dump_dcache_range + +/* + * Cleans entire DCache + * void clean_dcache(void); + */ + .section .text, "ax", %progbits + .align 2 + .global clean_dcache + .type clean_dcache, %function + .global cpucache_flush @ Alias +clean_dcache: +cpucache_flush: + @ Index format: 31:26 = index, 7:5 = segment, remainder = SBZ + mov r0, #0x00000000 @ +1: @ clean_start @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c10, 2 @ Clean entry by index + sub r0, r0, #0x000000e0 @ + adds r0, r0, #0x04000000 @ will wrap to zero at loop end + bne 1b @ clean_start @ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + bx lr @ + .size clean_dcache, .-clean_dcache + +/* + * Invalidate entire DCache + * will do writeback + * void invalidate_dcache(void); + */ + .section .text, "ax", %progbits + .align 2 + .global invalidate_dcache + .type invalidate_dcache, %function +invalidate_dcache: + @ Index format: 31:26 = index, 7:5 = segment, remainder = SBZ + mov r0, #0x00000000 @ +1: @ inv_start @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + add r0, r0, #0x00000020 @ + mcr p15, 0, r0, c7, c14, 2 @ Clean and invalidate entry by index + sub r0, r0, #0x000000e0 @ + adds r0, r0, #0x04000000 @ will wrap to zero at loop end + bne 1b @ inv_start @ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer + bx lr @ + .size invalidate_dcache, .-invalidate_dcache + +/* + * Invalidate entire ICache and DCache + * will do writeback + * void invalidate_idcache(void); + */ + .section .text, "ax", %progbits + .align 2 + .global invalidate_idcache + .type invalidate_idcache, %function + .global cpucache_invalidate @ Alias +invalidate_idcache: +cpucache_invalidate: + mov r1, lr @ save lr to r1, call uses r0 only + bl invalidate_dcache @ Clean and invalidate entire DCache + mcr p15, 0, r0, c7, c5, 0 @ Invalidate ICache (r0=0 from call) + mov pc, r1 @ + .size invalidate_idcache, .-invalidate_idcache + + .global disable_dcache +disable_dcache: + mrc 15, 0, r0, c1, c0, 0 + bic r0, r0, #0x4 + mcr 15, 0, r0, c1, c0, 0 + mov pc, lr + .size disable_dcache, .-disable_dcache + + .global enable_dcache +enable_dcache: + mrc 15, 0, r0, c1, c0, 0 + orr r0, r0, #0x4 + mcr 15, 0, r0, c1, c0, 0 + mov pc, lr + .size enable_dcache, .-enable_dcache Index: arm/s5l8700/system-s5l8700.c =================================================================== --- arm/s5l8700/system-s5l8700.c (revision 22868) +++ arm/s5l8700/system-s5l8700.c (working copy) @@ -105,13 +105,24 @@ asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */ "sub sp, sp, #8 \n"); /* Reserve stack */ + int sources = SRCPND; int irq_no = INTOFFSET; irqvector[irq_no](); +#ifdef IPOD_NANO2G + /* DMA interrupts cause a freeze, so temporarily disable them */ + if (irq_no==10) { INTMSK &= ~(1<<10); } +#endif + /* clear interrupt */ - SRCPND = (1 << irq_no); - INTPND = INTPND; +#if 0 + SRCPND = (1 < irq_no); + INTPND = INTPND +#else + SRCPND = sources; + INTPND = sources; +#endif asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */ "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */