From 4f01b49dacfa4437c485c78c2abbbb4fda750c1f Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Fri, 20 Apr 2012 02:23:22 +0400 Subject: [PATCH 01/11] Fixed compilation error (in case without networking) --- security/commoncap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/commoncap.c b/security/commoncap.c index 1322b6aa648..da21e7c93e4 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -87,10 +87,12 @@ EXPORT_SYMBOL(cap_netlink_recv); int cap_capable(struct task_struct *tsk, const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { +#ifdef CONFIG_ANDROID_PARANOID_NETWORK if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0; +#endif for (;;) { /* The creator of the user namespace has all caps. */ From e573cd3902562fee68e3e77655b08d5405bba1d1 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Fri, 20 Apr 2012 03:00:28 +0400 Subject: [PATCH 02/11] Fixed clock warning messages --- arch/arm/plat-samsung/clock.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index a3101e54e89..9346f7a952c 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -159,9 +159,11 @@ void _clk_enable(struct clk *clk) return; _clk_enable(clk->parent); - pr_debug("%s update hardware clock %s %d %pS\n", - __func__, clk->name, clk->id, clk->dev); - (clk->enable)(clk, 1); + if (clk->enable) { + pr_debug("%s update hardware clock %s %d %pS\n", + __func__, clk->name, clk->id, clk->dev); + (clk->enable)(clk, 1); + } } int clk_enable(struct clk *clk) @@ -396,7 +398,12 @@ int s3c24xx_register_clock(struct clk *clk) struct clk *c; list_for_each_entry(c, &clocks, list) { if (c->enable == clk->enable && +#ifdef CONFIG_PLAT_S5L + /* ctrlbit isn't bit really */ + c->ctrlbit == clk->ctrlbit) { +#else c->ctrlbit & clk->ctrlbit) { +#endif pr_warning("%s: new clock %s, id %d, dev %p " "uses same enable bit as " "%s, id %d, dev %p\n", __func__, From fc3518f9c2772fb6b68edb2bcd53ceb4e835f77a Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Fri, 20 Apr 2012 03:02:19 +0400 Subject: [PATCH 03/11] Added ipt4_defconfig for iPodTouch4G --- arch/arm/configs/ipt4_defconfig | 998 ++++++++++++++++++++++++++++++++ 1 file changed, 998 insertions(+) create mode 100644 arch/arm/configs/ipt4_defconfig diff --git a/arch/arm/configs/ipt4_defconfig b/arch/arm/configs/ipt4_defconfig new file mode 100644 index 00000000000..a3073bcac0f --- /dev/null +++ b/arch/arm/configs/ipt4_defconfig @@ -0,0 +1,998 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 3.0.8 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_NO_IOPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-eabi-" +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_FHANDLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_HAVE_SPARSE_IRQ=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_CHIP=y +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +CONFIG_TINY_RCU=y +# CONFIG_PREEMPT_RCU is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_CGROUPS is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_ASHMEM is not set +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +CONFIG_ARCH_S5L=y +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS4 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +CONFIG_PLAT_SAMSUNG=y + +# +# Boot options +# +# CONFIG_S3C_BOOT_ERROR_RESET is not set +CONFIG_S3C_BOOT_UART_FORCE_FIFO=y +CONFIG_S3C_LOWLEVEL_UART_PORT=0 +CONFIG_SAMSUNG_CLKSRC=y +CONFIG_SAMSUNG_IRQ_VIC_TIMER=y +CONFIG_SAMSUNG_IRQ_UART=y +CONFIG_S3C_GPIO_CFG_S3C24XX=y +CONFIG_S3C_GPIO_CFG_S3C64XX=y +CONFIG_S3C_GPIO_PULL_UPDOWN=y +CONFIG_SAMSUNG_GPIO_EXTRA=0 +CONFIG_S3C_GPIO_SPACE=0 +CONFIG_S3C_GPIO_TRACK=y +# CONFIG_S3C_ADC is not set +CONFIG_S3C_DEV_HSMMC=y +CONFIG_S3C_DEV_I2C1=y +CONFIG_S3C_DEV_USB_HSOTG=y +# CONFIG_S3C24XX_PWM is not set + +# +# Power management +# + +# +# Power Domain +# + +# +# System MMU +# +# CONFIG_S5P_HIGH_RES_TIMERS is not set +# CONFIG_S5P_DEV_MFC is not set +# CONFIG_S5P_SETUP_MFC is not set + +# +# Machine Types +# +CONFIG_PLAT_S5L=y +CONFIG_S5L_IRQ=y +# CONFIG_MACH_IPHONE_2G is not set +# CONFIG_MACH_IPHONE_3G is not set +# CONFIG_MACH_IPOD_TOUCH_1G is not set +CONFIG_CPU_S5L8930=y +# CONFIG_MACH_IPHONE_4 is not set +CONFIG_MACH_IPOD_TOUCH_4G=y +# CONFIG_MACH_IPAD_1G is not set +# CONFIG_MACH_APPLETV_2G is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_754322 is not set +CONFIG_ARM_VIC=y +CONFIG_ARM_VIC_NR=4 +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_SUSPEND is not set +# CONFIG_PM_RUNTIME is not set +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_MG_DISK is not set +# CONFIG_IPHONE_BLK is not set +CONFIG_BLK_DEV_APPLE=y +CONFIG_BLK_DEV_APPLE_VSVFL=y +CONFIG_BLK_DEV_APPLE_LEGACY_VFL=y +CONFIG_BLK_DEV_APPLE_YAFTL=y +CONFIG_BLK_DEV_APPLE_LEGACY_FTL=y +CONFIG_BLK_DEV_H2FMI=y +CONFIG_BLK_DEV_S5L8900=y +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYPAD_CYPRESS_TOUCH is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_UARTS_4=y +CONFIG_SERIAL_SAMSUNG_UARTS=4 +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_S5L8900=y +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +# CONFIG_I2C is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +CONFIG_SPI_BITBANG=y +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_S5L=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set +# CONFIG_MFD_SUPPORT is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_ION is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=y +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_S5P_MIPI_DSI=y +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_APPLE_IPHONE is not set +CONFIG_S5L_DISPLAY_PIPE=y +CONFIG_FB_S5L_CLCD=y +# CONFIG_FB_S5L_RGBOUT is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_NFC_DEVICES is not set +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +# CONFIG_TIMB_DMA is not set +CONFIG_S5L_CDMA=y +CONFIG_DMA_ENGINE=y + +# +# DMA Clients +# +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_STAGING is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +CONFIG_DEBUG_OBJECTS=y +# CONFIG_DEBUG_OBJECTS_SELFTEST is not set +# CONFIG_DEBUG_OBJECTS_FREE is not set +# CONFIG_DEBUG_OBJECTS_TIMERS is not set +CONFIG_DEBUG_OBJECTS_WORK=y +# CONFIG_DEBUG_OBJECTS_RCU_HEAD is not set +# CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER is not set +CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set +CONFIG_DEBUG_S3C_UART=0 + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +# CONFIG_CRYPTO is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y +# CONFIG_AVERAGE is not set From f5d196cdff26dda709bcc131ab78e9f9c03ff043 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sun, 22 Apr 2012 01:02:49 +0400 Subject: [PATCH 04/11] Supported interrupt for s5l8930 uart. Shell through ttySAC0 works, with some issues --- arch/arm/configs/ipt4_defconfig | 1 - arch/arm/mach-s5l8930/cpu.c | 29 +- arch/arm/mach-s5l8930/include/mach/irqs.h | 10 +- arch/arm/plat-s5l/Kconfig | 1 - arch/arm/plat-s5l/Makefile | 2 +- arch/arm/plat-s5l/include/plat/irq-uart.h | 23 ++ arch/arm/plat-s5l/include/plat/irq.h | 2 +- arch/arm/plat-s5l/include/plat/irqs.h | 40 +++ arch/arm/plat-s5l/include/plat/regs-uart.h | 295 +++++++++++++++++++++ arch/arm/plat-s5l/irq-uart.c | 124 +++++++++ arch/arm/plat-s5l/irq.c | 27 +- arch/arm/plat-samsung/Kconfig | 2 +- drivers/tty/serial/samsung.c | 8 + 13 files changed, 529 insertions(+), 35 deletions(-) create mode 100644 arch/arm/plat-s5l/include/plat/irq-uart.h create mode 100644 arch/arm/plat-s5l/include/plat/regs-uart.h create mode 100644 arch/arm/plat-s5l/irq-uart.c diff --git a/arch/arm/configs/ipt4_defconfig b/arch/arm/configs/ipt4_defconfig index a3073bcac0f..89f1d67d2f3 100644 --- a/arch/arm/configs/ipt4_defconfig +++ b/arch/arm/configs/ipt4_defconfig @@ -252,7 +252,6 @@ CONFIG_S3C_BOOT_UART_FORCE_FIFO=y CONFIG_S3C_LOWLEVEL_UART_PORT=0 CONFIG_SAMSUNG_CLKSRC=y CONFIG_SAMSUNG_IRQ_VIC_TIMER=y -CONFIG_SAMSUNG_IRQ_UART=y CONFIG_S3C_GPIO_CFG_S3C24XX=y CONFIG_S3C_GPIO_CFG_S3C64XX=y CONFIG_S3C_GPIO_PULL_UPDOWN=y diff --git a/arch/arm/mach-s5l8930/cpu.c b/arch/arm/mach-s5l8930/cpu.c index 93c86d18e90..e894b32ec6e 100644 --- a/arch/arm/mach-s5l8930/cpu.c +++ b/arch/arm/mach-s5l8930/cpu.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -224,18 +224,18 @@ static struct cpu_table cpu_id[] __initdata = { }, }; -#define S5L8930_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ - S3C2410_UCON_RXILEVEL | \ - S3C2410_UCON_TXIRQMODE | \ - S3C2410_UCON_RXIRQMODE | \ - S3C2410_UCON_RXFIFO_TOI | \ - S3C2443_UCON_RXERR_IRQEN) +#define S5L8930_UCON_DEFAULT (S5L8930_UCON_TXIRQMODE | \ + S5L8930_UCON_RXIRQMODE) -#define S5L8930_ULCON_DEFAULT S3C2410_LCON_CS8 +#define S5L8930_ULCON_DEFAULT S5L8930_LCON_CS8 +#if 0 -#define S5L8930_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ +#define S5L8930_UFCON_DEFAULT (S5L8930_UFCON_FIFOMODE | \ S3C2440_UFCON_RXTRIG8 | \ S3C2440_UFCON_TXTRIG16) +#else +#define S5L8930_UFCON_DEFAULT (S5L8930_UFCON_FIFOMODE) +#endif static struct s3c24xx_uart_clksrc s5l8930_uart_srcs[] = { [0] = { @@ -300,18 +300,11 @@ void __init s5l8930_map_io(void) s3c24xx_init_uarts(s5l8930_uartcfgs, ARRAY_SIZE(s5l8930_uartcfgs)); } -static void __initdata __iomem *s5l8930_vics[] = { - VA_VIC0, - VA_VIC1, - VA_VIC2, - VA_VIC3, -}; - void __init s5l8930_init_irq(void) { + u32 vic[] = {~0, ~0, ~0, ~0}; printk("%s\n", __func__); - s5l_init_vics(s5l8930_vics, ARRAY_SIZE(s5l8930_vics)); - s3c_init_vic_timer_irq(1, IRQ_TIMER); + s5l_init_irq(vic, ARRAY_SIZE(vic)); printk("%s done\n", __func__); } diff --git a/arch/arm/mach-s5l8930/include/mach/irqs.h b/arch/arm/mach-s5l8930/include/mach/irqs.h index 531404cc24e..2a1b6eaaaad 100644 --- a/arch/arm/mach-s5l8930/include/mach/irqs.h +++ b/arch/arm/mach-s5l8930/include/mach/irqs.h @@ -13,8 +13,6 @@ #include -#define IRQ_TIMER 0x11 - #define IRQ_TIMER0_VIC 300 #define IRQ_TIMER1_VIC 301 #define IRQ_TIMER2_VIC 302 @@ -32,10 +30,10 @@ #define IRQ_GPIO 0x74 -#define IRQ_UART0 0x16 -#define IRQ_UART1 0x17 -#define IRQ_UART2 0x18 -#define IRQ_UART3 0x19 +#define IRQ_UART0 S5L_IRQ_VIC0(22) +#define IRQ_UART1 S5L_IRQ_VIC0(23) +#define IRQ_UART2 S5L_IRQ_VIC0(24) +#define IRQ_UART3 S5L_IRQ_VIC0(25) #define IRQ_HSMMC0 0x26 diff --git a/arch/arm/plat-s5l/Kconfig b/arch/arm/plat-s5l/Kconfig index f3519b302fb..61699ee0de1 100644 --- a/arch/arm/plat-s5l/Kconfig +++ b/arch/arm/plat-s5l/Kconfig @@ -17,7 +17,6 @@ config PLAT_S5L select S3C_GPIO_CFG_S3C24XX select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER - select SAMSUNG_IRQ_UART select GENERIC_CLOCKEVENTS select GENERIC_IRQ_CHIP help diff --git a/arch/arm/plat-s5l/Makefile b/arch/arm/plat-s5l/Makefile index def8a0950bf..37b6b8b5b22 100644 --- a/arch/arm/plat-s5l/Makefile +++ b/arch/arm/plat-s5l/Makefile @@ -3,7 +3,7 @@ # This file is part of the iDroid Project # (http://www.idroidproject.org/). -obj-y := dev-uart.o +obj-y := dev-uart.o irq-uart.o obj-m := obj-n := obj- := diff --git a/arch/arm/plat-s5l/include/plat/irq-uart.h b/arch/arm/plat-s5l/include/plat/irq-uart.h new file mode 100644 index 00000000000..aa8f8db3293 --- /dev/null +++ b/arch/arm/plat-s5l/include/plat/irq-uart.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com). + * + * Based on arch/arm/plat-samsung/include/plat/irq-uart.h + * + * Internal header file for Samsung S5L8930 serial ports (UART0-3) + * Header file for Samsung SoC UART IRQ demux for S5L8930 + * + * This file is part of the iDroid Project. (http://www.idroidproject.org). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct s5l_uart_irq { + void __iomem *regs; + unsigned int base_irq; + unsigned int parent_irq; +}; + +extern void s5l_init_uart_irqs(struct s5l_uart_irq *irq, unsigned int nr_irqs); + diff --git a/arch/arm/plat-s5l/include/plat/irq.h b/arch/arm/plat-s5l/include/plat/irq.h index 972ec2fb5f9..6aa92d8b568 100644 --- a/arch/arm/plat-s5l/include/plat/irq.h +++ b/arch/arm/plat-s5l/include/plat/irq.h @@ -14,6 +14,6 @@ #include #include -extern void s5l_init_vics(void __iomem **_bases, uint32_t _count); +extern void s5l_init_irq(u32 *vic, u32 num_vic); #endif //_S5L_IRQ_ diff --git a/arch/arm/plat-s5l/include/plat/irqs.h b/arch/arm/plat-s5l/include/plat/irqs.h index 5b00066ec42..0bd09e091ba 100644 --- a/arch/arm/plat-s5l/include/plat/irqs.h +++ b/arch/arm/plat-s5l/include/plat/irqs.h @@ -1,4 +1,5 @@ /** + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com). * Copyright (c) 2011 Richard Ian Taylor. * * Portions Copyright (c) 2009 Samsung Electronics Co., Ltd. @@ -15,6 +16,30 @@ #include +/* we keep the first set of CPU IRQs out of the range of + * the ISA space, so that the PC104 has them to itself + * and we don't end up having to do horrible things to the + * standard ISA drivers.... + * + * note, since we're using the VICs, our start must be a + * mulitple of 32 to allow the common code to work + */ + +#define S5L_IRQ_OFFSET (0) + +#define S5L_IRQ(x) ((x) + S5L_IRQ_OFFSET) + +#define S5L_VIC0_BASE S5L_IRQ(0) +#define S5L_VIC1_BASE S5L_IRQ(32) +#define S5L_VIC2_BASE S5L_IRQ(64) +#define S5L_VIC3_BASE S5L_IRQ(96) + +#define VIC_BASE(x) (S5L_VIC0_BASE + ((x)*32)) + +#define IRQ_VIC0_BASE S5L_VIC0_BASE +#define IRQ_VIC1_BASE S5L_VIC1_BASE +#define IRQ_VIC2_BASE S5L_VIC2_BASE + /* UART interrupts, each UART has 4 interrupts per channel so * use the space between the ISA and S3C main interrupts. Note, these * are not in the same order as the S3C24XX series! */ @@ -50,4 +75,19 @@ #define IRQ_S3CUART_RX2 IRQ_S5L_UART_RX2 #define IRQ_S3CUART_RX3 IRQ_S5L_UART_RX3 +/* VIC based IRQs */ + +#define S5L_IRQ_VIC0(x) (S5L_VIC0_BASE + (x)) +#define S5L_IRQ_VIC1(x) (S5L_VIC1_BASE + (x)) +#define S5L_IRQ_VIC2(x) (S5L_VIC2_BASE + (x)) +#define S5L_IRQ_VIC3(x) (S5L_VIC3_BASE + (x)) + +#define S5L_TIMER_IRQ(x) (11 + (x)) + +#define IRQ_TIMER0 S5L_TIMER_IRQ(0) +#define IRQ_TIMER1 S5L_TIMER_IRQ(1) +#define IRQ_TIMER2 S5L_TIMER_IRQ(2) +#define IRQ_TIMER3 S5L_TIMER_IRQ(3) +#define IRQ_TIMER4 S5L_TIMER_IRQ(4) + #endif //_S5L_IRQS_ diff --git a/arch/arm/plat-s5l/include/plat/regs-uart.h b/arch/arm/plat-s5l/include/plat/regs-uart.h new file mode 100644 index 00000000000..10e0ff436aa --- /dev/null +++ b/arch/arm/plat-s5l/include/plat/regs-uart.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com). + * + * Based on arch/arm/plat-samsung/include/plat/regs-serial.h + * + * Internal header file for Samsung S5L8930 serial ports (UART0-3) + * + * This file is part of the iDroid Project. (http://www.idroidproject.org). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARM_REGS_SERIAL_H +#define __ASM_ARM_REGS_SERIAL_H + +#if 0 +#define S3C24XX_VA_UART0 (S3C_VA_UART) +#define S3C24XX_VA_UART1 (S3C_VA_UART + 0x4000 ) +#define S3C24XX_VA_UART2 (S3C_VA_UART + 0x8000 ) +#define S3C24XX_VA_UART3 (S3C_VA_UART + 0xC000 ) + +#define S5L8930_PA_UART0 (S3C24XX_PA_UART) +#define S5L8930_PA_UART1 (S3C24XX_PA_UART + 0x4000 ) +#define S5L8930_PA_UART2 (S3C24XX_PA_UART + 0x8000 ) +#define S3C2443_PA_UART3 (S3C24XX_PA_UART + 0xC000 ) +#endif +#define S5L8930_ULCON (0x00) +#define S5L8930_UCON (0x04) +#define S5L8930_UFCON (0x08) +#define S5L8930_UMCON (0x0C) +#define S5L8930_UTRSTAT (0x10) +#define S5L8930_UERSTAT (0x14) +#define S5L8930_UFSTAT (0x18) +#define S5L8930_UMSTAT (0x1C) +#define S5L8930_UTXH (0x20) +#define S5L8930_URXH (0x24) +#define S5L8930_UDIVSLOT (0x2C) +#define S5L8930_UBRDIV (0x28) +#if 0 +#define S5L8930_UINTMSK (0x38) + +#define S5L8930_LCON_CFGMASK ((0xF<<3)|(0x3)) + +#define S5L8930_LCON_CS5 (0x0) +#define S5L8930_LCON_CS6 (0x1) +#define S5L8930_LCON_CS7 (0x2) +#endif +#define S5L8930_LCON_CS8 (0x3) +#if 0 +#define S5L8930_LCON_CSMASK (0x3) + +#define S5L8930_LCON_PNONE (0x0) +#define S5L8930_LCON_PEVEN (0x5 << 3) +#define S5L8930_LCON_PODD (0x4 << 3) +#define S5L8930_LCON_PMASK (0x7 << 3) + +#define S5L8930_LCON_STOPB (1<<2) +#define S5L8930_LCON_IRM (1<<6) + +#define S3C2440_UCON_CLKMASK (3<<10) +#define S3C2440_UCON_PCLK (0<<10) +#define S3C2440_UCON_UCLK (1<<10) +#define S3C2440_UCON_PCLK2 (2<<10) +#define S3C2440_UCON_FCLK (3<<10) +#define S3C2443_UCON_EPLL (3<<10) + +#define S3C6400_UCON_CLKMASK (3<<10) +#define S3C6400_UCON_PCLK (0<<10) +#define S3C6400_UCON_PCLK2 (2<<10) +#define S3C6400_UCON_UCLK0 (1<<10) +#define S3C6400_UCON_UCLK1 (3<<10) + +#define S3C2440_UCON2_FCLK_EN (1<<15) +#define S3C2440_UCON0_DIVMASK (15 << 12) +#define S3C2440_UCON1_DIVMASK (15 << 12) +#define S3C2440_UCON2_DIVMASK (7 << 12) +#define S3C2440_UCON_DIVSHIFT (12) + +#define S3C2412_UCON_CLKMASK (3<<10) +#define S3C2412_UCON_UCLK (1<<10) +#define S3C2412_UCON_USYSCLK (3<<10) +#define S3C2412_UCON_PCLK (0<<10) +#define S3C2412_UCON_PCLK2 (2<<10) + +#define S5L8930_UCON_UCLK (1<<10) +#define S5L8930_UCON_SBREAK (1<<4) +#endif +#define S5L8930_UCON_TXILEVEL (1<<13) +#define S5L8930_UCON_RXILEVEL (1<<12) +#define S5L8930_UCON_TXIRQMODE (1<<2) +#define S5L8930_UCON_RXIRQMODE (1<<0) +#if 0 +#define S5L8930_UCON_RXFIFO_TOI (1<<7) +#define S3C2443_UCON_RXERR_IRQEN (1<<6) +#define S3C2443_UCON_LOOPBACK (1<<5) + +#define S5L8930_UCON_DEFAULT (S5L8930_UCON_TXILEVEL | \ + S5L8930_UCON_RXILEVEL | \ + S5L8930_UCON_TXIRQMODE | \ + S5L8930_UCON_RXIRQMODE | \ + S5L8930_UCON_RXFIFO_TOI) + +#endif +#define S5L8930_UFCON_FIFOMODE (1<<0) +/* TODO: Change it later */ +#define S3C2440_UFCON_RXTRIG8 (1<<4) +#define S3C2440_UFCON_TXTRIG16 (1<<6) +#if 0 +#define S5L8930_UFCON_TXTRIG0 (0<<6) +#define S5L8930_UFCON_RXTRIG8 (1<<4) +#define S5L8930_UFCON_RXTRIG12 (2<<4) + +/* S3C2440 FIFO trigger levels */ +#define S3C2440_UFCON_RXTRIG1 (0<<4) +#define S3C2440_UFCON_RXTRIG8 (1<<4) +#define S3C2440_UFCON_RXTRIG16 (2<<4) +#define S3C2440_UFCON_RXTRIG32 (3<<4) + +#define S3C2440_UFCON_TXTRIG0 (0<<6) +#define S3C2440_UFCON_TXTRIG16 (1<<6) +#define S3C2440_UFCON_TXTRIG32 (2<<6) +#define S3C2440_UFCON_TXTRIG48 (3<<6) + +#define S5L8930_UFCON_RESETBOTH (3<<1) +#define S5L8930_UFCON_RESETTX (1<<2) +#define S5L8930_UFCON_RESETRX (1<<1) + +#define S5L8930_UFCON_DEFAULT (S5L8930_UFCON_FIFOMODE | \ + S5L8930_UFCON_TXTRIG0 | \ + S5L8930_UFCON_RXTRIG8 ) + +#define S5L8930_UMCOM_AFC (1<<4) +#define S5L8930_UMCOM_RTS_LOW (1<<0) + +#define S3C2412_UMCON_AFC_63 (0<<5) /* same as s3c2443 */ +#define S3C2412_UMCON_AFC_56 (1<<5) +#define S3C2412_UMCON_AFC_48 (2<<5) +#define S3C2412_UMCON_AFC_40 (3<<5) +#define S3C2412_UMCON_AFC_32 (4<<5) +#define S3C2412_UMCON_AFC_24 (5<<5) +#define S3C2412_UMCON_AFC_16 (6<<5) +#define S3C2412_UMCON_AFC_8 (7<<5) + +#define S5L8930_UFSTAT_TXFULL (1<<9) +#define S5L8930_UFSTAT_RXFULL (1<<8) +#define S5L8930_UFSTAT_TXMASK (15<<4) +#define S5L8930_UFSTAT_TXSHIFT (4) +#define S5L8930_UFSTAT_RXMASK (15<<0) +#define S5L8930_UFSTAT_RXSHIFT (0) + +/* UFSTAT S3C24A0 */ +#define S3C24A0_UFSTAT_TXFULL (1 << 14) +#define S3C24A0_UFSTAT_RXFULL (1 << 6) +#define S3C24A0_UFSTAT_TXMASK (63 << 8) +#define S3C24A0_UFSTAT_TXSHIFT (8) +#define S3C24A0_UFSTAT_RXMASK (63) +#define S3C24A0_UFSTAT_RXSHIFT (0) + +/* UFSTAT S3C2443 same as S3C2440 */ +#define S3C2440_UFSTAT_TXFULL (1<<14) +#define S3C2440_UFSTAT_RXFULL (1<<6) +#define S3C2440_UFSTAT_TXSHIFT (8) +#define S3C2440_UFSTAT_RXSHIFT (0) +#define S3C2440_UFSTAT_TXMASK (63<<8) +#define S3C2440_UFSTAT_RXMASK (63) + +#endif +#define S5L8930_UTRSTAT_TXIRQ (1<<5) +#define S5L8930_UTRSTAT_RXIRQ (1<<4) +#define S5L8930_UTRSTAT_TXE (1<<2) +#define S5L8930_UTRSTAT_TXFE (1<<1) +#define S5L8930_UTRSTAT_RXDR (1<<0) +#if 0 + +#define S5L8930_UERSTAT_OVERRUN (1<<0) +#define S5L8930_UERSTAT_FRAME (1<<2) +#define S5L8930_UERSTAT_BREAK (1<<3) +#define S3C2443_UERSTAT_PARITY (1<<1) + +#define S5L8930_UERSTAT_ANY (S5L8930_UERSTAT_OVERRUN | \ + S5L8930_UERSTAT_FRAME | \ + S5L8930_UERSTAT_BREAK) + +#define S5L8930_UMSTAT_CTS (1<<0) +#define S5L8930_UMSTAT_DeltaCTS (1<<2) + +#define S3C2443_DIVSLOT (0x2C) + +/* S3C64XX interrupt registers. */ +#define S3C64XX_UINTP 0x30 +#define S3C64XX_UINTSP 0x34 +#define S3C64XX_UINTM 0x38 + +/* S5V210 interrupt registers. */ +#define S5P_UINTP 0x30 +#define S5P_UINTSP 0x34 +#define S5P_UINTM 0x38 + +/* Following are specific to S5PV210 */ +#define S5PV210_UCON_CLKMASK (1<<10) +#define S5PV210_UCON_PCLK (0<<10) +#define S5PV210_UCON_UCLK (1<<10) + +#define S5PV210_UFCON_TXTRIG0 (0<<8) +#define S5PV210_UFCON_TXTRIG4 (1<<8) +#define S5PV210_UFCON_TXTRIG8 (2<<8) +#define S5PV210_UFCON_TXTRIG16 (3<<8) +#define S5PV210_UFCON_TXTRIG32 (4<<8) +#define S5PV210_UFCON_TXTRIG64 (5<<8) +#define S5PV210_UFCON_TXTRIG128 (6<<8) +#define S5PV210_UFCON_TXTRIG256 (7<<8) + +#define S5PV210_UFCON_RXTRIG1 (0<<4) +#define S5PV210_UFCON_RXTRIG4 (1<<4) +#define S5PV210_UFCON_RXTRIG8 (2<<4) +#define S5PV210_UFCON_RXTRIG16 (3<<4) +#define S5PV210_UFCON_RXTRIG32 (4<<4) +#define S5PV210_UFCON_RXTRIG64 (5<<4) +#define S5PV210_UFCON_RXTRIG128 (6<<4) +#define S5PV210_UFCON_RXTRIG256 (7<<4) + +#define S5PV210_UFSTAT_TXFULL (1<<24) +#define S5PV210_UFSTAT_RXFULL (1<<8) +#define S5PV210_UFSTAT_TXMASK (255<<16) +#define S5PV210_UFSTAT_TXSHIFT (16) +#define S5PV210_UFSTAT_RXMASK (255<<0) +#define S5PV210_UFSTAT_RXSHIFT (0) + +#define NO_NEED_CHECK_CLKSRC 1 +#endif +#ifndef __ASSEMBLY__ + +/* struct s3c24xx_uart_clksrc + * + * this structure defines a named clock source that can be used for the + * uart, so that the best clock can be selected for the requested baud + * rate. + * + * min_baud and max_baud define the range of baud-rates this clock is + * acceptable for, if they are both zero, it is assumed any baud rate that + * can be generated from this clock will be used. + * + * divisor gives the divisor from the clock to the one seen by the uart +*/ + +struct s3c24xx_uart_clksrc { + const char *name; + unsigned int divisor; + unsigned int min_baud; + unsigned int max_baud; +}; + +/* configuration structure for per-machine configurations for the + * serial port + * + * the pointer is setup by the machine specific initialisation from the + * arch/arm/mach-s3c2410/ directory. +*/ + +struct s3c2410_uartcfg { + unsigned char hwport; /* hardware port number */ + unsigned char unused; + unsigned short flags; +#if !defined(CONFIG_CPU_S5PV210) + upf_t uart_flags; /* default uart flags */ +#else + unsigned long uart_flags; /* default uart flags */ +#endif + + unsigned int has_fracval; + + unsigned long ucon; /* value of ucon for port */ + unsigned long ulcon; /* value of ulcon for port */ + unsigned long ufcon; /* value of ufcon for port */ + + struct s3c24xx_uart_clksrc *clocks; + unsigned int clocks_size; + + void (*wake_peer)(struct uart_port *); +}; + +/* s3c24xx_uart_devs + * + * this is exported from the core as we cannot use driver_register(), + * or platform_add_device() before the console_initcall() +*/ + +extern struct platform_device *s3c24xx_uart_devs[4]; + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ARM_REGS_SERIAL_H */ + diff --git a/arch/arm/plat-s5l/irq-uart.c b/arch/arm/plat-s5l/irq-uart.c new file mode 100644 index 00000000000..c65d4116333 --- /dev/null +++ b/arch/arm/plat-s5l/irq-uart.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com). + * + * S5L - UART Interrupt handling (Software demuxing) + * + * This file is part of the iDroid Project. (http://www.idroidproject.org). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] + * are consecutive when looking up the interrupt in the demux routines. + */ +static void s5l_irq_demux_uart(unsigned int irq, struct irq_desc *desc) +{ + struct s5l_uart_irq *uirq = desc->irq_data.handler_data; + u32 utrstat = __raw_readl(uirq->regs + S5L8930_UTRSTAT); + int base = uirq->base_irq; + + if (utrstat & S5L8930_UTRSTAT_RXIRQ && utrstat & S5L8930_UTRSTAT_RXDR) + generic_handle_irq(base); + if (utrstat & S5L8930_UTRSTAT_TXIRQ) + generic_handle_irq(base + 2); +} + +static inline void s5l_irq_uart_ack(struct irq_data *data) +{ + struct s5l_uart_irq *uirq = (struct s5l_uart_irq*) data->chip_data; + u32 utrstat; + + utrstat = __raw_readl(uirq->regs + S5L8930_UTRSTAT); + __raw_writel(utrstat, uirq->regs + S5L8930_UTRSTAT); +} + +static inline void s5l_irq_uart_mask(struct irq_data *data) +{ + struct s5l_uart_irq *uirq = (struct s5l_uart_irq*) data->chip_data; + u32 ucon, mask; + + if (data->irq - uirq->base_irq == 0) + mask = ~S5L8930_UCON_RXILEVEL; + else if (data->irq - uirq->base_irq == 2) + mask = ~S5L8930_UCON_TXILEVEL; + else + return; + + ucon = __raw_readl(uirq->regs + S5L8930_UCON); + __raw_writel(ucon & mask, uirq->regs + S5L8930_UCON); +} + +static void s5l_irq_uart_unmask(struct irq_data *data) +{ + struct s5l_uart_irq *uirq = (struct s5l_uart_irq*) data->chip_data; + u32 ucon, mask; + + if (data->irq - uirq->base_irq == 0) + mask = S5L8930_UCON_RXILEVEL; + else if (data->irq - uirq->base_irq == 2) + mask = S5L8930_UCON_TXILEVEL; + else + return; + + ucon = __raw_readl(uirq->regs + S5L8930_UCON); + __raw_writel(ucon | mask, uirq->regs + S5L8930_UCON); +} + +static struct irq_chip s5l_irq_uart = { + .name = "s5l-uart", + .irq_mask = s5l_irq_uart_mask, + .irq_unmask = s5l_irq_uart_unmask, +// .irq_mask_ack = s5l_irq_uart_maskack, + .irq_ack = s5l_irq_uart_ack, +// .irq_set_type = s5l_irq_uart_set_type, +#ifdef CONFIG_PM +// .irq_set_wake = s3c_irqext_wake, +#endif +}; +static void __init s5l_init_uart_irq(struct s5l_uart_irq *uirq) +{ + u32 ucon; + + /* mask all interrupts at the start. */ + ucon = __raw_readl(uirq->regs + S5L8930_UCON); + __raw_writel(ucon & ~(S5L8930_UCON_RXILEVEL | S5L8930_UCON_TXILEVEL), + uirq->regs + S5L8930_UCON); + + irq_set_chip_and_handler(uirq->base_irq, &s5l_irq_uart, handle_level_irq); + set_irq_flags(uirq->base_irq, IRQF_VALID); + irq_set_chip_data(uirq->base_irq, uirq); + + irq_set_chip_and_handler(uirq->base_irq + 2, &s5l_irq_uart, handle_level_irq); + set_irq_flags(uirq->base_irq + 2, IRQF_VALID); + irq_set_chip_data(uirq->base_irq + 2, uirq); + + irq_set_handler_data(uirq->parent_irq, uirq); + irq_set_chained_handler(uirq->parent_irq, s5l_irq_demux_uart); +} + +/** + * s5l_init_uart_irqs() - initialise UART IRQs and the necessary demuxing + * @irq: The interrupt data for registering + * @nr_irqs: The number of interrupt descriptions in @irq. + * + * Register the UART interrupts specified by @irq including the demuxing + * routines. This supports the S5L8930 based devices. + */ +void __init s5l_init_uart_irqs(struct s5l_uart_irq *irq, unsigned int nr_irqs) +{ + for (; nr_irqs > 0; nr_irqs--, irq++) + s5l_init_uart_irq(irq); +} diff --git a/arch/arm/plat-s5l/irq.c b/arch/arm/plat-s5l/irq.c index c2a062520d0..367258110ee 100644 --- a/arch/arm/plat-s5l/irq.c +++ b/arch/arm/plat-s5l/irq.c @@ -1,5 +1,17 @@ +/* + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com). + * + * S5L - Interrupt Initialization + * + * This file is part of the iDroid Project. (http://www.idroidproject.org). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ #include #include +#include #include #include @@ -9,7 +21,7 @@ #include #include -static struct s3c_uart_irq uart_irqs[] = { +static struct s5l_uart_irq uart_irqs[] = { [0] = { .regs = VA_UART0, .base_irq = IRQ_S5L_UART_BASE0, @@ -27,11 +39,14 @@ static struct s3c_uart_irq uart_irqs[] = { }, }; -void s5l_init_vics(void __iomem **_bases, uint32_t _count) +void s5l_init_irq(u32 *vic, u32 num_vic) { - uint32_t i; - for(i = 0; i < _count; i++) - vic_init(_bases[i], i*32, 0xffffffff, 0); +#ifdef CONFIG_ARM_VIC + int irq; + for(irq = 0; irq < num_vic; irq++) + vic_init(VA_VIC(irq), VIC_BASE(irq), vic[irq], 0); +#endif - s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); +/* s3c_init_vic_timer_irq(1, 11);*/ + s5l_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 4d79519d19a..9343eebcdf2 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -6,7 +6,7 @@ config PLAT_SAMSUNG bool - depends on PLAT_S3C24XX || ARCH_S3C64XX || PLAT_S5P + depends on PLAT_S3C24XX || ARCH_S3C64XX || PLAT_S5P || PLAT_S5L select NO_IOPORT select GENERIC_IRQ_CHIP default y diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 06cc2077a00..219733dfcb4 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -133,6 +133,10 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) } } +#ifdef CONFIG_PLAT_S5L +static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id); +#endif + static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -144,6 +148,10 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) enable_irq(ourport->tx_irq); tx_enabled(port) = 1; } +#ifdef CONFIG_PLAT_S5L + else + s3c24xx_serial_tx_chars(ourport->tx_irq, ourport); +#endif } From c9027a03fe989eb1ec29dd42de40dc1c06fc1e94 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sun, 22 Apr 2012 12:38:24 +0400 Subject: [PATCH 05/11] irq-uart.c: Added rx timeout logic --- arch/arm/plat-s5l/include/plat/regs-uart.h | 8 ++- arch/arm/plat-s5l/irq-uart.c | 66 ++++++++++++++++++++-- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/arch/arm/plat-s5l/include/plat/regs-uart.h b/arch/arm/plat-s5l/include/plat/regs-uart.h index 10e0ff436aa..47f9079e405 100644 --- a/arch/arm/plat-s5l/include/plat/regs-uart.h +++ b/arch/arm/plat-s5l/include/plat/regs-uart.h @@ -87,8 +87,11 @@ #define S5L8930_UCON_UCLK (1<<10) #define S5L8930_UCON_SBREAK (1<<4) #endif -#define S5L8930_UCON_TXILEVEL (1<<13) -#define S5L8930_UCON_RXILEVEL (1<<12) +#define S5L8930_UCON_RXSIRQMASK (1<<17) +#define S5L8930_UCON_RXSIRQEN (1<<16) +#define S5L8930_UCON_RXS (S5L8930_UCON_RXSIRQMASK | S5L8930_UCON_RXSIRQEN) +#define S5L8930_UCON_TXIRQEN (1<<13) +#define S5L8930_UCON_RXIRQEN (1<<12) #define S5L8930_UCON_TXIRQMODE (1<<2) #define S5L8930_UCON_RXIRQMODE (1<<0) #if 0 @@ -167,6 +170,7 @@ #define S3C2440_UFSTAT_RXMASK (63) #endif +#define S5L8930_UTRSTAT_RXSIRQ (1<<8) #define S5L8930_UTRSTAT_TXIRQ (1<<5) #define S5L8930_UTRSTAT_RXIRQ (1<<4) #define S5L8930_UTRSTAT_TXE (1<<2) diff --git a/arch/arm/plat-s5l/irq-uart.c b/arch/arm/plat-s5l/irq-uart.c index c65d4116333..eec41637351 100644 --- a/arch/arm/plat-s5l/irq-uart.c +++ b/arch/arm/plat-s5l/irq-uart.c @@ -21,6 +21,28 @@ #include #include +#define SOFTWARE_RX_TIMEOUT + +#ifdef SOFTWARE_RX_TIMEOUT +/* TODO: each timer for each uart channel */ +static struct timer_list rx_timeout_timer; + +static void rx_timeout_timer_handler(unsigned long data) +{ + struct s5l_uart_irq *uirq = (struct s5l_uart_irq*) data; + struct irq_desc *desc = irq_to_desc(uirq->base_irq); + + del_timer(&rx_timeout_timer); + + if (desc) { + struct irqaction *action = desc->action; + action->handler(uirq->base_irq, action->dev_id); + } + + __raw_writel(__raw_readl(uirq->regs + S5L8930_UCON) | + S5L8930_UCON_RXSIRQMASK, uirq->regs + S5L8930_UCON); +} +#endif /* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] * are consecutive when looking up the interrupt in the demux routines. */ @@ -30,10 +52,27 @@ static void s5l_irq_demux_uart(unsigned int irq, struct irq_desc *desc) u32 utrstat = __raw_readl(uirq->regs + S5L8930_UTRSTAT); int base = uirq->base_irq; - if (utrstat & S5L8930_UTRSTAT_RXIRQ && utrstat & S5L8930_UTRSTAT_RXDR) + if (utrstat & S5L8930_UTRSTAT_RXIRQ && utrstat & S5L8930_UTRSTAT_RXDR) { +#ifdef SOFTWARE_RX_TIMEOUT + del_timer(&rx_timeout_timer); generic_handle_irq(base); + __raw_writel(__raw_readl(uirq->regs + S5L8930_UCON) | + S5L8930_UCON_RXSIRQMASK, uirq->regs + S5L8930_UCON); +#else + generic_handle_irq(base); +#endif + } if (utrstat & S5L8930_UTRSTAT_TXIRQ) generic_handle_irq(base + 2); +#ifdef SOFTWARE_RX_TIMEOUT + if (utrstat & S5L8930_UTRSTAT_RXSIRQ) { + /* ack */ + __raw_writel(utrstat, uirq->regs + S5L8930_UTRSTAT); + /* 1/16 sec timeout */ + rx_timeout_timer.expires = jiffies + (HZ >> 4); + add_timer(&rx_timeout_timer); + } +#endif } static inline void s5l_irq_uart_ack(struct irq_data *data) @@ -51,9 +90,13 @@ static inline void s5l_irq_uart_mask(struct irq_data *data) u32 ucon, mask; if (data->irq - uirq->base_irq == 0) - mask = ~S5L8930_UCON_RXILEVEL; +#ifdef SOFTWARE_RX_TIMEOUT + mask = ~(S5L8930_UCON_RXIRQEN | S5L8930_UCON_RXS); +#else + mask = ~S5L8930_UCON_RXIRQEN; +#endif else if (data->irq - uirq->base_irq == 2) - mask = ~S5L8930_UCON_TXILEVEL; + mask = ~S5L8930_UCON_TXIRQEN; else return; @@ -67,9 +110,13 @@ static void s5l_irq_uart_unmask(struct irq_data *data) u32 ucon, mask; if (data->irq - uirq->base_irq == 0) - mask = S5L8930_UCON_RXILEVEL; +#ifdef SOFTWARE_RX_TIMEOUT + mask = S5L8930_UCON_RXIRQEN | S5L8930_UCON_RXS; +#else + mask = S5L8930_UCON_RXIRQEN; +#endif else if (data->irq - uirq->base_irq == 2) - mask = S5L8930_UCON_TXILEVEL; + mask = S5L8930_UCON_TXIRQEN; else return; @@ -94,7 +141,8 @@ static void __init s5l_init_uart_irq(struct s5l_uart_irq *uirq) /* mask all interrupts at the start. */ ucon = __raw_readl(uirq->regs + S5L8930_UCON); - __raw_writel(ucon & ~(S5L8930_UCON_RXILEVEL | S5L8930_UCON_TXILEVEL), + __raw_writel(ucon & ~(S5L8930_UCON_RXIRQEN | S5L8930_UCON_TXIRQEN | + S5L8930_UCON_RXS), uirq->regs + S5L8930_UCON); irq_set_chip_and_handler(uirq->base_irq, &s5l_irq_uart, handle_level_irq); @@ -119,6 +167,12 @@ static void __init s5l_init_uart_irq(struct s5l_uart_irq *uirq) */ void __init s5l_init_uart_irqs(struct s5l_uart_irq *irq, unsigned int nr_irqs) { +#ifdef SOFTWARE_RX_TIMEOUT + init_timer(&rx_timeout_timer); + rx_timeout_timer.data = (unsigned long)irq; + rx_timeout_timer.function = rx_timeout_timer_handler; +#endif + for (; nr_irqs > 0; nr_irqs--, irq++) s5l_init_uart_irq(irq); } From b4258bbdbf7c58d1fe3d59ebe842e8fa04cf49b9 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sun, 22 Apr 2012 14:36:51 +0400 Subject: [PATCH 06/11] Implemented arch_reset --- arch/arm/mach-s5l8930/include/mach/system.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-s5l8930/include/mach/system.h b/arch/arm/mach-s5l8930/include/mach/system.h index cee53237ffb..fa1e23b9cee 100644 --- a/arch/arm/mach-s5l8930/include/mach/system.h +++ b/arch/arm/mach-s5l8930/include/mach/system.h @@ -1,4 +1,5 @@ /** + * Copyright (c) 2012 Alexey Makhalov (makhaloff@gmail.com) * Copyright (c) 2011 Richard Ian Taylor. * * This file is part of the iDroid Project. (http://www.idroidproject.org). @@ -14,6 +15,7 @@ #include #include #include +#include #include static inline void arch_idle(void) @@ -26,7 +28,13 @@ static inline void arch_idle(void) static inline void arch_reset(char mode, const char* cmd) { - // TODO: Implement PMGR. + while (1) { + __raw_writel(0, VA_PMGR0 + 0x202c); + __raw_writel(1, VA_PMGR0 + 0x2024); + __raw_writel(0x80000000, VA_PMGR0 + 0x2020); + __raw_writel(4, VA_PMGR0 + 0x202c); + __raw_writel(0, VA_PMGR0 + 0x2020); + } } #endif //_S5L8930_SYSTEM_ From 09a488bbe3594b1e0e62b53e211e65cdb9fcd8df Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Thu, 3 May 2012 17:16:06 +0400 Subject: [PATCH 07/11] Added vfl/ftl layers (from OiB) and block device pattern. Compile but dont work. --- drivers/block/apple/ftl.c | 216 ++ drivers/block/apple/ftl.h | 91 + drivers/block/apple/vfl.h | 302 +++ drivers/block/apple/vsvfl.h | 106 + drivers/block/apple/yaftl/Makefile | 2 + drivers/block/apple/yaftl/l2v.c | 426 ++++ drivers/block/apple/yaftl/l2v.h | 50 + drivers/block/apple/yaftl/yaftl.c | 2455 ++++++++++++++++++++++ drivers/block/apple/yaftl/yaftl.h | 14 + drivers/block/apple/yaftl/yaftl_common.c | 679 ++++++ drivers/block/apple/yaftl/yaftl_common.h | 340 +++ drivers/block/apple/yaftl/yaftl_gc.c | 1108 ++++++++++ drivers/block/apple/yaftl/yaftl_gc.h | 20 + drivers/block/apple/yaftl/yaftl_mem.c | 88 + drivers/block/apple/yaftl/yaftl_mem.h | 25 + 15 files changed, 5922 insertions(+) create mode 100644 drivers/block/apple/ftl.c create mode 100644 drivers/block/apple/ftl.h create mode 100644 drivers/block/apple/vfl.h create mode 100644 drivers/block/apple/vsvfl.h create mode 100644 drivers/block/apple/yaftl/l2v.c create mode 100644 drivers/block/apple/yaftl/l2v.h create mode 100644 drivers/block/apple/yaftl/yaftl.c create mode 100644 drivers/block/apple/yaftl/yaftl.h create mode 100644 drivers/block/apple/yaftl/yaftl_common.c create mode 100644 drivers/block/apple/yaftl/yaftl_common.h create mode 100644 drivers/block/apple/yaftl/yaftl_gc.c create mode 100644 drivers/block/apple/yaftl/yaftl_gc.h create mode 100644 drivers/block/apple/yaftl/yaftl_mem.c create mode 100644 drivers/block/apple/yaftl/yaftl_mem.h diff --git a/drivers/block/apple/ftl.c b/drivers/block/apple/ftl.c new file mode 100644 index 00000000000..4e68aac68d0 --- /dev/null +++ b/drivers/block/apple/ftl.c @@ -0,0 +1,216 @@ +/* + * ftl.c - OpeniBoot Flash transition layer + * + * Copyright 2010 iDroid Project + * + * This file is part of iDroid. An android distribution for Apple products. + * For more information, please visit http://www.idroidproject.org/. + * + * 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 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "vfl.h" +#include "ftl.h" + +// FTL types +#include "yaftl/yaftl.h" + +#ifdef YUSTAS_FIXME +static error_t ftl_read_mtd(mtd_t *_dev, void *_dest, uint64_t _off, int _amt) +{ + ftl_device_t *ftl = CONTAINER_OF(ftl_device_t, mtd, _dev); + uint8_t* curLoc = (uint8_t*) _dest; + uint32_t block_size = ftl->block_size; + int curPage = _off / block_size; + int toRead = _amt; + int pageOffset = _off - (curPage * block_size); + uint8_t* tBuffer = (uint8_t*) malloc(block_size); + error_t ret = SUCCESS; + + while(toRead > 0) + { + size_t this_amt = toRead; + if(this_amt > (block_size-pageOffset)) + this_amt = block_size-pageOffset; + + ret = ftl_read_single_page(ftl, curPage, tBuffer); + if(FAILED(ret)) + break; + + memcpy(curLoc, tBuffer + pageOffset, this_amt); + + curLoc += this_amt; + toRead -= this_amt; + pageOffset = 0; + curPage++; + } + + if(!FAILED(ret)) + ret = _amt; + + free(tBuffer); + return ret; +} + +static error_t ftl_write_mtd(mtd_t *_dev, void *_src, uint64_t _off, int _amt) +{ + ftl_device_t *ftl = CONTAINER_OF(ftl_device_t, mtd, _dev); + uint8_t* curLoc = (uint8_t*) _src; + uint32_t block_size = ftl->block_size; + int curPage = _off / block_size; + int toWrite = _amt; + int pageOffset = _off - (curPage * block_size); + + error_t ret = SUCCESS; + uint8_t* tBuffer = (uint8_t*) malloc(block_size); + + while(toWrite > 0) + { + size_t this_amt = toWrite; + if(this_amt > (block_size-pageOffset)) + this_amt = block_size-pageOffset; + + memset(tBuffer, 0, block_size); + ret = ftl_read_single_page(ftl, curPage, tBuffer); + if(FAILED(ret)) + break; + + memcpy(tBuffer + pageOffset, curLoc, this_amt); + + ret = ftl_write_single_page(ftl, curPage, tBuffer); + if(FAILED(ret)) + break; + + curLoc += this_amt; + toWrite -= this_amt; + pageOffset = 0; + curPage++; + } + + if(!FAILED(ret)) + ret = _amt; + + free(tBuffer); + return ret; +} + +static int64_t ftl_block_size(mtd_t *_dev) +{ + ftl_device_t *dev = CONTAINER_OF(ftl_device_t, mtd, _dev); + return dev->block_size; +} + +static error_t ftl_prepare(mtd_t *_dev) +{ + //ftl_device_t *dev = CONTAINER_OF(ftl_device_t, mtd, _dev); + return SUCCESS; +} + +static mtd_t ftl_mtd = { + .device = { + .fourcc = FOURCC('F', 'T', 'L', '\0'), + .name = "FTL Layer", + }, + + .read = ftl_read_mtd, + .write = ftl_write_mtd, + .prepare = ftl_prepare, + + .block_size = ftl_block_size, + + .usage = mtd_filesystem, +}; + +error_t ftl_init(ftl_device_t *_dev) +{ + _dev->open = FALSE; + + memcpy(&_dev->mtd, &ftl_mtd, sizeof(ftl_mtd)); + error_t ret = mtd_init(&_dev->mtd); + if(FAILED(ret)) + return ret; + + return SUCCESS; +} + +void ftl_cleanup(ftl_device_t *_dev) +{ + mtd_cleanup(&_dev->mtd); +} + +error_t ftl_register(ftl_device_t *_dev) +{ + error_t ret = mtd_register(&_dev->mtd); + if(FAILED(ret)) + return ret; + + return SUCCESS; +} + +void ftl_unregister(ftl_device_t *_dev) +{ + mtd_unregister(&_dev->mtd); +} +#endif + +error_t ftl_open(ftl_device_t *_dev, vfl_device_t *_vfl) +{ + if(_dev->open) + return _dev->open(_dev, _vfl); + + return ENOENT; +} + +void ftl_close(ftl_device_t *_dev) +{ + if(_dev->close) + _dev->close(_dev); +} + +error_t ftl_read_single_page(ftl_device_t *_dev, uint32_t _page, uint8_t *_buffer) +{ + if(_dev->read_single_page) + return _dev->read_single_page(_dev, _page, _buffer); + + return ENOENT; +} + +error_t ftl_write_single_page(ftl_device_t *_dev, uint32_t _page, uint8_t *_buffer) +{ + if(_dev->write_single_page) + return _dev->write_single_page(_dev, _page, _buffer); + + return ENOENT; +} + +error_t ftl_detect(ftl_device_t **_dev, vfl_device_t *_vfl) +{ + // TODO: Implement FTL detection! -- Ricky26 + +#ifdef CONFIG_BLK_DEV_APPLE_YAFTL + ftl_yaftl_device_t *yaftl = ftl_yaftl_device_allocate(); + *_dev = &yaftl->ftl; + + ftl_open(*_dev, _vfl); +#ifdef YUSTAS_FIXME + ftl_register(*_dev); +#endif + + return 0; +#endif + + return ENOENT; +} + diff --git a/drivers/block/apple/ftl.h b/drivers/block/apple/ftl.h new file mode 100644 index 00000000000..f5564d96516 --- /dev/null +++ b/drivers/block/apple/ftl.h @@ -0,0 +1,91 @@ + +/** + * @file + * + * Header file for OiB's Flash Translation Layer + * + * @defgroup FTL + */ + +/** + * ftl.h + * + * Copyright 2011 iDroid Project + * + * This file is part of iDroid. An android distribution for Apple products. + * For more information, please visit http://www.idroidproject.org/. + * + * 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 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FTL_H +#define FTL_H + +#include "vfl.h" + +/** + * FTL Device Prototypes + * + * @ingroup FTL + */ +struct _ftl_device; + +typedef error_t (*ftl_device_open_t)(struct _ftl_device *, vfl_device_t *_vfl); +typedef void (*ftl_device_close_t)(struct _ftl_device *); + +typedef error_t (*ftl_read_single_page_t)(struct _ftl_device *, uint32_t _page, uint8_t *_buffer); +typedef error_t (*ftl_write_single_page_t)(struct _ftl_device *, uint32_t _page, uint8_t *_buffer); + +/** + * FTL Device Structure + * + * @ingroup FTL + */ +typedef struct _ftl_device +{ +#ifdef YUSTAS_FIXME + mtd_t mtd; +#endif + + size_t block_size; + + ftl_device_open_t open; + ftl_device_close_t close; + + ftl_read_single_page_t read_single_page; + ftl_write_single_page_t write_single_page; + +} ftl_device_t; + +/** + * FTL Device Functions + * + * @ingroup FTL + */ +error_t ftl_init(ftl_device_t *_dev); +void ftl_cleanup(ftl_device_t *_dev); + +error_t ftl_register(ftl_device_t *_dev); +void ftl_unregister(ftl_device_t *_dev); + +error_t ftl_open(ftl_device_t *_dev, vfl_device_t *_vfl); +void ftl_close(ftl_device_t *_dev); + +error_t ftl_read_single_page(ftl_device_t *_dev, uint32_t _page, uint8_t *_buffer); +error_t ftl_write_single_page(ftl_device_t *_dev, uint32_t _page, uint8_t *_buffer); + +error_t ftl_detect(ftl_device_t **_dev, vfl_device_t *_vfl); + +#endif //FTL_H diff --git a/drivers/block/apple/vfl.h b/drivers/block/apple/vfl.h new file mode 100644 index 00000000000..cca4524bb79 --- /dev/null +++ b/drivers/block/apple/vfl.h @@ -0,0 +1,302 @@ +/** + * @file + * + * Header file for OpeniBoot's VFL interface. + * + * Functions in this file are for manipulating a NAND device + * as a contiguous block of data. + * + * @defgroup VFL + */ + +/** + * vfl.h + * + * Copyright 2011 iDroid Project + * + * This file is part of iDroid. An android distribution for Apple products. + * For more information, please visit http://www.idroidproject.org/. + * + * 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 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef VFL_H +#define VFL_H +#include +#include + +typedef int error_t; + +/** + * The VFL device info enumeration. + * + * This is used by vfl_get_info to determine + * which info to obtain. + * + * @ingroup VFL + */ +typedef enum _vfl_info +{ + diPagesPerBlockTotalBanks, + diSomeThingFromVFLCXT, + diFTLType, + diBytesPerPageFTL, + diMetaBytes0xC, + diUnkn20_1, + diTotalBanks, +} vfl_info_t; + +/** + * The VFL signature style enumeration. + * + * This is used to determine how the signature will + * be read by vfl_detect. + * + * @ingroup VFL + */ +typedef enum _vfl_signature_style +{ + vfl_old_signature = 0x64, + vfl_new_signature = 0x65, +} vfl_signature_style_t; + +// VFL Device Prototypes +struct _vfl_device; + +typedef error_t (*vfl_open_t)(struct _vfl_device *); +typedef void (*vfl_close_t)(struct _vfl_device *); + +typedef struct apple_nand *(*vfl_get_device_t)(struct _vfl_device *); + +typedef void* *(*vfl_get_stats_t)(struct _vfl_device *, uint32_t *size); + +typedef error_t (*vfl_read_single_page_t)(struct _vfl_device *, uint32_t _page, uint8_t *_buffer, + uint8_t *_sparebuffer, int _empty_ok, int *_refresh, uint32_t _disable_aes); + +typedef error_t (*vfl_write_single_page_t)(struct _vfl_device *, uint32_t _page, uint8_t *_buffer, + uint8_t *_sparebuffer, int _scrub); + +typedef error_t (*vfl_erase_single_block_t)(struct _vfl_device *, uint32_t _block, int _replace_bad_block); + +typedef error_t (*vfl_write_context_t)(struct _vfl_device *, uint16_t *_control_block); + +typedef uint16_t *(*vfl_get_ftl_ctrl_block_t)(struct _vfl_device *); + +typedef error_t (*vfl_get_info_t)(struct _vfl_device *, vfl_info_t _item, void * _result, size_t _sz); + +/** + * The VFL device structure. + * + * This structure represents a VFL device, + * which is basically a driver for a specific + * VFL on a specific NAND device. One vfl_device_t + * is created for each NAND device. + * + * @ingroup VFL + */ +typedef struct _vfl_device +{ + vfl_open_t open; /**< Used by vfl_open(). */ + vfl_close_t close; /**< Used by vfl_close(). */ + +// vfl_get_device_t get_device; /**< Used by vfl_get_device(). */ + vfl_get_stats_t get_stats; /**< Used by vfl_get_stats (). */ + + vfl_read_single_page_t read_single_page; /**< Used by vfl_read_single_page(). */ + vfl_write_single_page_t write_single_page; /**< Used by vfl_write_single_page(). */ + vfl_erase_single_block_t erase_single_block; /**< Used by vfl_erase_single_block(). */ + vfl_write_context_t write_context; /**< Used by vfl_write_context(). */ + + vfl_get_ftl_ctrl_block_t get_ftl_ctrl_block; /**< Used by vfl_get_ftl_ctrl_block(). */ + + vfl_get_info_t get_info; /**< Used by vfl_get_info(). */ + +} vfl_device_t; + +// +// VFL Device Functions +// + +/** + * Initialise a VFL device. + * + * This should be called before any vfl_device_t structure + * is used. Usually, VFL device drivers call it in their + * own init function. + * + * @param _vfl the device to initialise. + * + * @ingroup VFL + */ +void vfl_init(vfl_device_t *_vfl); + +/** + * Clean up a VFL device. + * + * This should be called when a VFL structure is finished + * with (you should call close seperately too if you've + * opened it. + * + * @param _vfl the device to clean up. + * + * @ingroup VFL + */ +void vfl_cleanup(vfl_device_t *_vfl); + +/** + * Open the VFL on a given NAND device. + * + * This should be called to open the VFL before + * any other vfl functions are called (such as vfl_read_single_page). + * + * @param _vfl the VFL device to open. + * @param _dev the NAND device to open it from. + * @return Whether any problems occurred. + * + * @ingroup VFL + */ +error_t vfl_open(vfl_device_t *_vfl); + +/** + * Close a VFL device. + * + * This should be called when operations on the VFL + * are finished with. + * + * @param _vfl the VFL device to close. + * + * @ingroup VFL + */ +void vfl_close(vfl_device_t *_vfl); + +/** + * Get the NAND device associated with this VFL device. + * + * @return The NAND device associated with this VFL device. + * + * @ingroup VFL + */ +struct apple_nand *vfl_get_device(vfl_device_t *_vfl); + +/** + * Get the statistics associated with this VFL device. + * + * @return The statistics associated with this VFL device. + * + * @ingroup VFL + */ +void* *vfl_get_stats(vfl_device_t *_vfl, uint32_t *size); + +/** + * Read a single page from the VFL. + * + * This reads a single page from the VFL, that is given a single page number, + * the physical location of the page is calculated and the page is read. + * + * @param _vfl the VFL device to read from. + * @param _page the page number to read. + * @param _buffer the buffer to store the page data into. You should use device_get_info + * to calculate the VFL's page size. + * @param _spare the buffer to store the spare data, this can be NULL. + * @param _empty_ok if this is non-zero, an empty page will not return an error. + * @param _refresh_page if the pointed integer is non-zero the read will be attempted twice. + * @param _disable_aes if this flag is true, AES decryption won't be done by the hardware. + * @return Whether an error occurred. + * + * @ingroup VFL + */ +error_t vfl_read_single_page(vfl_device_t *_vfl, uint32_t _page, uint8_t* _buffer, uint8_t* _spare, + int _empty_ok, int* _refresh_page, uint32_t _disable_aes); + +/** + * Write a single page to the VFL. + * + * Given a virtual page number, the VFL device calculates the physical location of the page, + * and writes the given data to it. + * + * @param _vfl the VFL device to read from. + * @param _page the page number to read. + * @param _buffer the buffer to store into the page. You should use device_get_info + * to calculate the VFL's page size. + * @param _spare the buffer to read the spare data from. + * @return Whether an error occurred. + * + * @ingroup VFL + */ +error_t vfl_write_single_page(vfl_device_t *_vfl, uint32_t _page, uint8_t* _buffer, uint8_t* _spare, int _scrub); + +/** + * Erase a single block in the VFL. + * + * Given a virtual block number, the VFL device erases the content of this block. + * Erases must be done in a block-level; a single page erase is not possible. + * + * @param _vfl the VFL device to erase from. + * @param _block the block number to erase. + * @param _replace_bad_block if true, the VFL device will replace a bad block if it detects one. + * @return Whether an error occurred. + * + * @ingroup VFL + */ +error_t vfl_erase_single_block(vfl_device_t *_vfl, uint32_t _block, int _replace_bad_block); + +/** + * Write a new VFL Context. + * + * @param _vfl the VFL device to write the context on. + * @param _control_block pointer to the control block array. + * @return Whether an error occured. + * + * @ingroup VFL + */ +error_t vfl_write_context(vfl_device_t *_vfl, uint16_t *_control_block); + +/** + * Get the FTL control blocks buffer. + * + * @return A buffer of three uint16_t block numbers, each is an FTL control block. + * + * @ingroup VFL + */ +uint16_t *vfl_get_ftl_ctrl_block(vfl_device_t *_vfl); + +/** + * Get a specific info about the VFL device. + * + * Given a vfl_info_t item descriptor, the VFL device will store the data into the given pointer, + * not exceeding the maximum allowed size. + * + * @param _vfl the VFL device to read from. + * @param _item the item to get. + * @param _result the buffer in which to save the result. + * @param _sz maximum number of bytes to write. + * @return Whether an error occurred. + * + * @ingroup VFL + */ +error_t vfl_get_info(vfl_device_t *_vfl, vfl_info_t _item, void *_result, size_t _sz); + +/** + * Attempt to detect and open the VFL on a given NAND device. + * + * @param _vfl the pointer to store the output VFL device in. + * @param _nand the NAND device where the VFL resides. + * @param _sign the signature style. + * + * @ingroup VFL + */ +error_t vfl_detect(vfl_device_t **_vfl); + +#endif //VFL_H diff --git a/drivers/block/apple/vsvfl.h b/drivers/block/apple/vsvfl.h new file mode 100644 index 00000000000..7abbf103be8 --- /dev/null +++ b/drivers/block/apple/vsvfl.h @@ -0,0 +1,106 @@ +#ifndef VFL_VSVFL_H +#define VFL_VSVFL_H + +#include "vfl.h" +#include + +/** + * @file + * + * This file is where the VSVFL VFL implementation is defined. + * + * VSVFL is the newer VFL implementation, which caters to + * newer devices using VSVFL on their NAND. + * + * This includes devices from the iPhone 3GS onwards. + */ + +typedef struct _vfl_vsvfl_geometry +{ + uint16_t pages_per_block; + uint16_t pages_per_block_2; + uint16_t pages_per_sublk; + uint32_t pages_total; + uint32_t some_page_mask; + uint32_t some_sublk_mask; + uint16_t blocks_per_ce; + uint16_t bytes_per_page; +#ifdef YUSTAS_FIXME + uint32_t num_ecc_bytes; +#endif + uint32_t bytes_per_spare; +#ifdef YUSTAS_FIXME + uint32_t one; +#endif + uint16_t num_ce; +#ifdef YUSTAS_FIXME + uint16_t ecc_bits; +#endif + uint16_t reserved_blocks; + uint16_t vfl_blocks; + uint16_t some_crazy_val; + uint16_t fs_start_block; + uint32_t unk; + uint32_t banks_per_ce; + uint16_t banks_total; + uint16_t bank_address_space; + uint32_t blocks_per_bank; + uint32_t blocks_per_bank_vfl; +} vfl_vsvfl_geometry_t; + +struct _vfl_vsvfl_context; +struct _vfl_vsvfl_device; + +typedef struct VSVFLStats { + uint64_t counter0; + uint64_t counter1; + uint64_t counter2; + uint64_t counter3; + uint64_t counter4; + uint64_t counter5; + uint64_t counter6; + uint64_t counter7; + uint64_t counter8; + uint64_t counter9; + uint64_t counter10; + uint64_t counter11; +} __attribute__((packed)) VSVFLStats; + +// VSVFL conversion functions prototypes +typedef void (*vsvfl_virtual_to_physical_t)(struct _vfl_vsvfl_device *_vfl, uint32_t _vBank, uint32_t _vPage, uint32_t *_pCE, uint32_t *_pPage); + +typedef void (*vsvfl_physical_to_virtual_t)(struct _vfl_vsvfl_device *_vfl, uint32_t, uint32_t, uint32_t *, uint32_t *); + +// VFL-VSVFL Device Struct +/** + * This is the structure for the VSVFL device. + * + * @implements _vfl_device + * @ingroup VFL + */ +typedef struct _vfl_vsvfl_device +{ + vfl_device_t vfl; + + uint32_t current_version; + struct _vfl_vsvfl_context *contexts; + vfl_vsvfl_geometry_t geometry; + uint8_t *bbt[16]; + + uint32_t *pageBuffer; + uint16_t *chipBuffer; + uint16_t *blockBuffer; + + vsvfl_virtual_to_physical_t virtual_to_physical; + vsvfl_physical_to_virtual_t physical_to_virtual; + + VSVFLStats *stats; +} vfl_vsvfl_device_t; + +// VFL-VFL Functions +error_t vfl_vsvfl_device_init(vfl_vsvfl_device_t *_vfl); +void vfl_vsvfl_device_cleanup(vfl_vsvfl_device_t *_vfl); + +vfl_vsvfl_device_t *vfl_vsvfl_device_allocate(void); + +#endif //VFL_VSVFL_H diff --git a/drivers/block/apple/yaftl/Makefile b/drivers/block/apple/yaftl/Makefile index e69de29bb2d..f232a0024fd 100644 --- a/drivers/block/apple/yaftl/Makefile +++ b/drivers/block/apple/yaftl/Makefile @@ -0,0 +1,2 @@ +yaftl_apple-y += l2v.o yaftl.o yaftl_common.o yaftl_gc.o yaftl_mem.o +obj-$(CONFIG_BLK_DEV_APPLE_YAFTL) += yaftl_apple.o diff --git a/drivers/block/apple/yaftl/l2v.c b/drivers/block/apple/yaftl/l2v.c new file mode 100644 index 00000000000..4668bed21e8 --- /dev/null +++ b/drivers/block/apple/yaftl/l2v.c @@ -0,0 +1,426 @@ +#include "l2v.h" +#include "yaftl_mem.h" + +static void L2V_SetFirstNode(L2VNode* node); + +static L2VDesc L2V; +//static L2VStruct L2VS; + +error_t L2V_Init(uint32_t totalPages, uint32_t numBlocks, uint32_t pagesPerSublk) +{ + int32_t i; + + if (numBlocks * pagesPerSublk > 0x1FF0001) + printk(KERN_DEBUG "PANIC!!!: L2V: Tree bitspace not sufficient for geometry %dx%d\r\n", numBlocks, pagesPerSublk); + + L2V.numBlocks = numBlocks; + L2V.pagesPerSublk = pagesPerSublk; + L2V.numRoots = (totalPages >> 15) + 1; + L2V.nextNode = NULL; + + L2V.Tree = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); + if (!L2V.Tree) + printk(KERN_DEBUG "PANIC!!!: L2V: tree allocation has failed\r\n"); + + L2V.TreeNodes = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); + if (!L2V.TreeNodes) + printk(KERN_DEBUG "PANIC!!!: L2V: tree nodes allocation has failed\r\n"); + + L2V.UpdatesSinceRepack = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); + if (!L2V.UpdatesSinceRepack) + printk(KERN_DEBUG "PANIC!!!: L2V: updates since repack allocation has failed\r\n"); + + for (i = 0; i < L2V.numRoots; ++i) + L2V.Tree[i] = 0x7FC000Cu; + + L2V.previousNode = NULL; + L2V.Pool = (L2VNode*)yaftl_alloc(0x10000 * sizeof(L2VNode)); + if (!L2V.Pool) + return ENOMEM; + + L2V.firstNode = NULL; + L2V.nodeCount = 0; + for (i = 0xFFF0; i >= 0; i -= 0x10) + L2V_SetFirstNode(&L2V.Pool[i]); + + return 0; +} + +void L2V_Open() +{ + uint32_t tocIndex = 0; + uint32_t gcIndex = 0; + uint32_t i; + uint8_t* pageBuffer = sInfo.gc.index.pageBuffer2; + SpareData* spareArray = sInfo.gc.index.spareArray; + + while (tocIndex < sInfo.tocArrayLength && L2V.nodeCount > 128) { + // Update the GC zone. + gcIndex = 0; + while (gcIndex < sInfo.gcPages && tocIndex < sInfo.tocArrayLength) { + if (sInfo.tocArray[tocIndex].indexPage != 0xFFFFFFFF) + sInfo.gc.index.zone[gcIndex++] = sInfo.tocArray[tocIndex].indexPage; + + ++tocIndex; + } + + if (YAFTL_readMultiPages(sInfo.gc.index.zone, gcIndex, pageBuffer, + spareArray, 0, 0) != 1) + { + // Manually read each page. + for (i = 0; i < gcIndex; ++i) { + if (YAFTL_readPage(sInfo.gc.index.zone[i], + &pageBuffer[i * sGeometry.bytesPerPage], + &spareArray[i], 0, 1, 0) != 0) + { + spareArray[i].lpn = 0xFFFFFFFF; + } + } + } + + // Update L2V with the read data. + for (i = 0; i < gcIndex && L2V.nodeCount > 128; i++) { + if (spareArray[i].type & PAGETYPE_INDEX + && spareArray[i].lpn < sInfo.tocArrayLength) + { + L2V_UpdateFromTOC(spareArray[i].lpn, + (uint32_t*)&pageBuffer[i * sGeometry.bytesPerPage]); + } + } + } +} + +void L2V_UpdateFromTOC(uint32_t _tocIdx, uint32_t* _tocBuffer) +{ + uint32_t i; + uint32_t entry = 0xFFFFFFFF; + uint32_t count = 0; + uint32_t lpn = _tocIdx * sInfo.tocEntriesPerPage; + uint32_t firstLpn = 0xFFFFFFFF; + + for (i = 0; i < sInfo.tocEntriesPerPage && L2V.nodeCount > 128; ++i, ++lpn) + { + if (count == 0) { + // This should be the first LPN. + entry = _tocBuffer[i]; + firstLpn = lpn; + ++count; + continue; + } + + if (entry == 0xFFFFFFFF) { + if (_tocBuffer[i] == 0xFFFFFFFF) { + // We're still in a row, because both indices are invalid. + ++count; + continue; + } + + // Bummer, we lost our row. + entry = L2V_VPN_SPECIAL; + } else if (entry + count == _tocBuffer[i]) { + // Yay! still a row. + ++count; + continue; + } + + // Row is done: update L2V, and start a new one. + L2V_Update(firstLpn, count, entry); + entry = _tocBuffer[i]; + firstLpn = lpn; + count = 1; + } +} + +static void L2V_SetFirstNode(L2VNode* node) +{ + if (node < &L2V.Pool[0] || node > &L2V.Pool[0x10000]) + printk(KERN_DEBUG "PANIC!!!: L2V: new first node must be in pool range\r\n"); + + node->next = L2V.firstNode; + L2V.firstNode = node; + ++L2V.nodeCount; +} + +L2VNode* L2V_EraseFirstNode(void) +{ + L2VNode* node; + if (!L2V.firstNode) + printk(KERN_DEBUG "PANIC!!!: L2V: currentNode not set\r\n"); + + node = L2V.firstNode; + L2V.firstNode = node->next; + --L2V.nodeCount; + return node; +} + +void L2V_Search(GCReadC* _c) +{ + // for now + _c->vpn = L2V_VPN_MISS; + _c->span++; + return; +#if 0 + uint32_t nodeSize; + uint32_t pageNum; + uint32_t span, Span, vpn, Vpn; + uint32_t bits, setting, Setting, value; + + uint32_t* node = (uint32_t*)((uint8_t*)&_c->node->next + _c->next_nOfs); + + if (L2V.previousNode != _c->field_24 || _c->pageIndex != _c->field_14) + pageNum = _c->pageIndex; + else if (_c->node->next == NULL || _c->next_nOfs > (_c->nodeSize - 4) || + *node == 0xFFFFFFFF) + pageNum = _c->field_14; + else + { + if (*node & 1) + { + bits = 14; + span = GET_BITS(*node, 18, bits); //low bits for span + vpn = GET_BITS(*node, 2, 16); + setting = 1; + } + else + { + bits = 5; + span = GET_BITS(*node, 27, bits);//low bits for span + vpn = GET_BITS(*node, 2, 25); + setting = 0; + } + + if ((*node >> 1) & 1) + { + _c->nodeSize -= 2; + span += *(uint16_t*)((uint8_t*)&_c->node->next + _c->nodeSize) << bits; // high bits for span + + if ((_c->next_nOfs) > (_c->nodeSize - 6)) //sizeof(lPtr_t) = 6 + printk(KERN_DEBUG "PANIC!!!: _c->next_nOfs) <= _c->nodeSize - sizeof(lPtr_t)"); + } + + if (span == 0) + printk(KERN_DEBUG "PANIC!!!: L2V: span mustn't be zero!"); + + if (!setting) + { + _c->vpn = vpn; + _c->field_4 = 0; + _c->span = span; + _c->next_nOfs += 4; + _c->field_14 = _c->pageIndex + _c->span; + return; + } + + pageNum = _c->pageIndex; + } + + _c->field_14 = 0xFFFFFFFF; + _c->node->next = NULL; + _c->field_20 = 0; + _c->vpn = L2V_VPN_MISS; + + uint32_t targTofs = pageNum % 0x8000; + uint32_t targTree = pageNum / 0x8000; // root number + if(L2V.numRoots <= targTree) + printk(KERN_DEBUG "PANIC!!!: L2V: targTree has to be less than L2V.numRoots"); + + if (L2V.Tree[targTree] & 1) + { + vpn = GET_BITS(L2V.Tree[targTree], 2, 16); + value = 1; + } else { + vpn = GET_BITS(L2V.Tree[targTree], 2, 25); + value = 0; + } + + _c->next_nOfs = 0; + + L2VNode* pool = NULL; + uint32_t offset = 0; + uint32_t nextSpan = 0; + + while (1) + { + if (_c->field_20 > 31) + printk(KERN_DEBUG "PANIC!!!: L2V_Search() fail!\r\n"); + + if (value == 0) + { + _c->field_4 = targTofs - offset; // targTofs = pageNum % 0x8000 + + _c->vpn = vpn; + if (vpn <= 0x1FF0001) + _c->vpn = vpn + targTofs - offset; + + _c->span = 0x8000 - _c->field_4; + _c->node->next = pool; + _c->field_14 = _c->pageIndex + 0x8000 - _c->field_4; + _c->field_24 = L2V.previousNode; + + if (_c->field_20 > 25) + L2V_Repack(targTree); + + return; + } else { + pool = &L2V.Pool[vpn]; + nodeSize = 0x40; + uint32_t nOfs = 0; + while (1) + { + node = (uint32_t*)((uint8_t*)&pool->next + nOfs); + if (*node == 0xFFFFFFFF) + printk(KERN_DEBUG "PANIC!!!: NAND index: bad tree\n"); + if (*node & 1) + { + bits = 14; + Vpn = GET_BITS(*node, 2, bits); + Span = GET_BITS(*node, 18, 14); + Setting = 1; + } else { + bits = 5; + Vpn = GET_BITS(*node, 27, bits); + Span = GET_BITS(*node, 2, 25); + Setting = 0; + } + + if ((*node >> 1) & 1) + { + nodeSize -= 2; + Span += *(uint16_t*)((uint8_t*)&pool->next + nodeSize) << bits; + if (nOfs > (nodeSize - 4)) //sizeof(lPtr_t) = 6 + printk(KERN_DEBUG "PANIC!!!: _c->next_nOfs) <= c->nodeSize - sizeof(lPtr_t)\n"); + } + + if (Span == 0); + printk(KERN_DEBUG "PANIC!!!: L2V_Search fail!!\r\n"); + + if (targTofs >= (Span + nextSpan)) + { + nOfs += sizeof(uint16_t); + nextSpan += Span; + if(nOfs > nodeSize - 6) + printk(KERN_DEBUG "PANIC!!!: NAND index: bad tree\n"); + continue; + } else { + _c->next_nOfs = nOfs + 4; + _c->nodeSize = nodeSize; + value = Setting; + vpn = Vpn; + break; + } + } + } + } +#endif +} + +void L2V_Repack(uint32_t rootNum) +{ +/* + int nodeSize = 64; + uint32_t vpn; + + if (!(L2V.Tree[rootNum] & 1)) + return; + + L2V.previousNode++; + L2VS.bottomIdx = 0; + L2VS.ovhSize = 0; + L2VS.nodeSize = nodeSize; + + int i; + for (i = 0; i < 3276; i++) { + L2VS.Node[i] = NULL; + L2VS.span[i] = 0; // 3276 to 6551 + } + + vpn = GET_BITS(L2V.Tree[rootNum], 2, 16); + sub_80609390(L2VS,&L2V.Pool[vpn]); + + if (L2VS.ovhSize != 0) + { + void* memOffset = (void*)&L2VS.Node[L2VS.bottomIdx] + L2VS.ovhSize; + memset(memOffset, 0xFF, L2VS.nodeSize - L2VS.ovhSize); + } + else + { + void* memOffset = &L2VS.Node[L2VS.bottomIdx]; + L2V_SetFirstNode(memOffset); + L2VS.bottomIdx--; + } + + uint32_t index = L2VS.bottomIdx + 1; + while (L2VS.bottomIdx != 0) + { + L2VNode* node1 = L2V_EraseFirstNode(); + + nOfs = 0; + nodes = 0; + spanCount = 0; + index++; + i = 0; + + do + { + if (nOfs + 6 > nodeSize) + { + memset((void*)node1 + nOfs, 0xFF, nodeSize - nOfs); + &L2VS.Node[nodes] = node1; + L2VS.span[nodes] = spanCount; + node1 = sub_80608E5C(); + nodes++; + nOfs = 0; + spanCount = 0; + index++; + } + + if (L2VS.span[i] == 0) + printk(KERN_DEBUG "PANIC!!!: ((cu).span) != (0)\n"); + + // [0:1]=config, [2:17] = vpn, [18:31] = span-lo + *(uint32_t*)(&node1.Ofs + nOfs)= (L2VS.span[i] << 18) | (((&L2VS.Node[i] - &L2V.Pool[0]) / 0x40) & 0xFFFF) << 2 | 3;//repacking + nodeSize -= 2; + *(uint16_t*)(&node1.Ofs + nodeSize) = (uint16_t)L2VS.span[i] >> 14; //span-hi + spanCount += L2VS.span[i]; + nOfs += 4; + i++; + } while (i < L2VS.bottomIdx); + + if (nOfs == 0) + { + yaFTL_L2V_SetNode(node1); + nodes--; + index--; + } + else + { + L2VS.Node[nodes] = node1; + L2VS.span[i] = spanCount; + memset(node2 + nOfs, 0xFF; nodeSize - nOfs); + } + + if (nodes == 0 && spanCount != 0x8000) + printk(KERN_DEBUG "PANIC!!!: (thisSpan) == ((1 << 15)) \n"); + + L2VS.bottomIdx = nodes; + } + + L2V.TreeNodes[rootNum] = index; + + if (L2VS.bottomIdx != 0) + printk(KERN_DEBUG "PANIC!!!: (r->bottomIdx) == (0)\n"); + + L2V.Tree[rootNum] = (((&L2VS.Node[0] - &L2V.Pool[0]) / 0x40) & 0xFFFF) << 2 | 1; // span = 0 + L2V.UpdatesSinceRepack[rootNum] = 0; + + if (L2VS.field_66EC > 0x1F) + L2VS.field_66EC = 0; + + L2VS.field_666C[L2VS.field_66EC] = rootNum; + L2VS.field_66EC++; + + return; +*/ +} + +void L2V_Update(uint32_t _start, uint32_t _count, uint32_t _vpn) {}; diff --git a/drivers/block/apple/yaftl/l2v.h b/drivers/block/apple/yaftl/l2v.h new file mode 100644 index 00000000000..6d925e1f097 --- /dev/null +++ b/drivers/block/apple/yaftl/l2v.h @@ -0,0 +1,50 @@ +#ifndef FTL_L2V_H +#define FTL_L2V_H + +#include "yaftl_common.h" + +#define L2V_VPN_SPECIAL (0x1FF0002) +#define L2V_VPN_MISS (0x1FF0003) + +#define L2V_VPN_ISNORMAL(v) ((v) < L2V_VPN_SPECIAL) + +/* Types */ + +typedef struct { + uint32_t numRoots; + uint32_t* Tree; + uint32_t* TreeNodes; + uint32_t* UpdatesSinceRepack; + L2VNode* previousNode; + L2VNode* nextNode; + uint32_t numBlocks; + uint32_t pagesPerSublk; + L2VNode* Pool; + L2VNode* firstNode; + uint32_t nodeCount; +} L2VDesc; + +typedef struct +{ + L2VNode* Node[204]; //offset 0 - 0x3300 + uint32_t span[3276]; //offset 0x3330 + uint32_t bottomIdx; //offset 0x6660 + uint32_t ovhSize; //offset 0x6664 + uint32_t nodeSize; //offset 0x6668 + uint32_t field_666C[32]; + uint32_t field_66EC; +} L2VStruct; + +error_t L2V_Init(uint32_t totalPages, uint32_t numBlocks, uint32_t pagesPerSublk); + +void L2V_Open(void); + +void L2V_Update(uint32_t _start, uint32_t _count, uint32_t _vpn); + +void L2V_UpdateFromTOC(uint32_t _tocIdx, uint32_t* _tocBuffer); + +void L2V_Search(GCReadC* _c); + +void L2V_Repack(uint32_t _rootNum); + +#endif // FTL_L2V_H diff --git a/drivers/block/apple/yaftl/yaftl.c b/drivers/block/apple/yaftl/yaftl.c new file mode 100644 index 00000000000..b2d162531d5 --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl.c @@ -0,0 +1,2455 @@ +#include +#include +#include +#include "yaftl.h" +#include "yaftl_mem.h" +#include "l2v.h" +#include "yaftl_gc.h" +#include "../vfl.h" +#include "../ftl.h" + +#define CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define h2fmi_set_emf(a,b) {} +#define h2fmi_get_emf() 0 +#define nand_device_set_ftl_region(a, b, c, d, e) {} + +uint64_t divide64(uint64_t n, uint32_t base) +{ + uint64_t tmp = n; + do_div(tmp, base); + return tmp; +} + +/* Generic */ + +static uint32_t BTOC_Init(void) +{ + uint32_t i; + + sInfo.numBtocCaches = 4; + sInfo.btocCurrUsn = 0; + sInfo.unkAC_2 = 2; + sInfo.unkB0_1 = 1; + + sInfo.btocCacheUsn = yaftl_alloc(sInfo.numBtocCaches * sizeof(int32_t)); + sInfo.btocCacheBlocks = yaftl_alloc(sInfo.numBtocCaches * sizeof(int32_t)); + sInfo.btocCaches = yaftl_alloc(sInfo.numBtocCaches * sizeof(uint32_t*)); + sInfo.unkB8_buffer = yaftl_alloc(sInfo.unkAC_2 << 2); + sInfo.unkB4_buffer = yaftl_alloc(sInfo.unkAC_2 << 2); + + if (!sInfo.btocCacheUsn || !sInfo.btocCacheBlocks || !sInfo.btocCaches + || !sInfo.unkB8_buffer || !sInfo.unkB4_buffer) { + return ENOMEM; + } + + for (i = 0; i < sInfo.numBtocCaches; i++) + sInfo.btocCacheBlocks[i] = -1; + + for (i = 0; i < sInfo.unkAC_2; i++) { + sInfo.unkB4_buffer[i] = -1; + sInfo.unkB8_buffer[i] = yaftl_alloc(sGeometry.pagesPerSublk<<2); + memset(sInfo.unkB8_buffer[i], 0xFF, sGeometry.pagesPerSublk<<2); + } + + sInfo.btocCacheMissing = (1 << sInfo.numBtocCaches) - 1; + sInfo.unk80_3 = 3; + + bufzone_init(&sInfo.btocCacheBufzone); + + for (i = 0; i < sInfo.numBtocCaches; i++) { + sInfo.btocCaches[i] = bufzone_alloc(&sInfo.btocCacheBufzone, + sGeometry.bytesPerPage * sInfo.tocPagesPerBlock); + } + + if (bufzone_finished_allocs(&sInfo.btocCacheBufzone)) + return ENOMEM; + + for (i = 0; i < sInfo.numBtocCaches; i++) { + sInfo.btocCaches[i] = bufzone_rebase(&sInfo.btocCacheBufzone, + sInfo.btocCaches[i]); + } + + if (bufzone_finished_rebases(&sInfo.btocCacheBufzone)) + return ENOMEM; + + return 0; +} + +static void updateBtocCache(void) +{ + uint32_t i; + for (i = 0; i < sInfo.numBtocCaches; ++i) { + if (sInfo.btocCacheBlocks[i] == -3) + sInfo.btocCacheBlocks[i] = sInfo.latestUserBlk.blockNum; + + if (sInfo.btocCacheBlocks[i] == -2) + sInfo.btocCacheBlocks[i] = sInfo.latestIndexBlk.blockNum; + } +} + +static uint32_t YAFTL_Init(void) +{ + uint16_t metaBytes, unkn20_1; + error_t error = 0; + + if (yaftl_inited) + printk(KERN_DEBUG "PANIC!!!: Oh shit, yaftl already initialized!\r\n"); + + memset(&sInfo, 0, sizeof(sInfo)); + memset(&sFTLStats, 0, sizeof(sFTLStats)); + + sInfo.lastTOCPageRead = 0xFFFFFFFF; + sInfo.ftlCxtPage = 0xFFFFFFFF; + sInfo.numCaches = 10; + sInfo.unk188_0x63 = 0x63; + + error |= vfl_get_info(vfl, diPagesPerBlockTotalBanks, + &sGeometry.pagesPerSublk, sizeof(sGeometry.pagesPerSublk)); + error |= vfl_get_info(vfl, diSomeThingFromVFLCXT, &sGeometry.numBlocks, + sizeof(sGeometry.numBlocks)); + error |= vfl_get_info(vfl, diBytesPerPageFTL, &sGeometry.bytesPerPage, + sizeof(sGeometry.bytesPerPage)); + error |= vfl_get_info(vfl, diTotalBanks, &sGeometry.total_banks_ftl, + sizeof(sGeometry.total_banks_ftl)); + error |= vfl_get_info(vfl, diMetaBytes0xC, &metaBytes, sizeof(metaBytes)); + error |= vfl_get_info(vfl, diUnkn20_1, &unkn20_1, sizeof(unkn20_1)); + + sGeometry.spareDataSize = metaBytes * unkn20_1; + sGeometry.total_usable_pages = + sGeometry.pagesPerSublk * sGeometry.numBlocks; + + if (error) + printk(KERN_DEBUG "PANIC!!!: yaftl: vfl get info failed!\r\n"); + + if (sGeometry.spareDataSize != 0xC) + printk(KERN_DEBUG "PANIC!!!: yaftl: spareDataSize isn't 0xC!\r\n"); + + printk(KERN_ERR "yaftl: got information from VFL.\r\n"); + printk(KERN_ERR "pagesPerSublk: %d\r\n", sGeometry.pagesPerSublk); + printk(KERN_ERR "numBlocks: %d\r\n", sGeometry.numBlocks); + printk(KERN_ERR "bytesPerPage: %d\r\n", sGeometry.bytesPerPage); + printk(KERN_ERR "total_banks_ftl: %d\r\n", sGeometry.total_banks_ftl); + printk(KERN_ERR "metaBytes: %d\r\n", metaBytes); + printk(KERN_ERR "unkn20_1: %d\r\n", unkn20_1); + printk(KERN_ERR "total_usable_pages: %d\r\n", sGeometry.total_usable_pages); + + sInfo.tocPagesPerBlock = (sGeometry.pagesPerSublk * sizeof(uint32_t)) + / sGeometry.bytesPerPage; + if (((sGeometry.pagesPerSublk * sizeof(uint32_t)) % sGeometry.bytesPerPage) + != 0) { + sInfo.tocPagesPerBlock++; + } + + sInfo.tocEntriesPerPage = sGeometry.bytesPerPage / sizeof(uint32_t); + bufzone_init(&sInfo.zone); + sInfo.pageBuffer = bufzone_alloc(&sInfo.zone, sGeometry.bytesPerPage); + sInfo.tocPageBuffer = bufzone_alloc(&sInfo.zone, sGeometry.bytesPerPage); + sInfo.pageBuffer2 = bufzone_alloc(&sInfo.zone, sGeometry.bytesPerPage + * sInfo.tocPagesPerBlock); + sInfo.spareBuffer3 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer4 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer5 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer6 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer7 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer8 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer9 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer10 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer11 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer12 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer13 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer14 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer15 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer16 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer17 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer18 = bufzone_alloc(&sInfo.zone, sizeof(SpareData)); + sInfo.spareBuffer19 = bufzone_alloc(&sInfo.zone, 32 + * sGeometry.total_banks_ftl * sizeof(SpareData)); + sInfo.buffer20 = bufzone_alloc(&sInfo.zone, sGeometry.pagesPerSublk + * sizeof(SpareData)); + + if (bufzone_finished_allocs(&sInfo.zone)) + printk(KERN_DEBUG "PANIC!!!: YAFTL_Init: bufzone_finished_allocs failed!"); + + sInfo.pageBuffer = bufzone_rebase(&sInfo.zone, sInfo.pageBuffer); + sInfo.tocPageBuffer = bufzone_rebase(&sInfo.zone, sInfo.tocPageBuffer); + sInfo.pageBuffer2 = bufzone_rebase(&sInfo.zone, sInfo.pageBuffer2); + sInfo.spareBuffer3 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer3); + sInfo.spareBuffer4 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer4); + sInfo.spareBuffer5 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer5); + sInfo.spareBuffer6 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer6); + sInfo.spareBuffer7 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer7); + sInfo.spareBuffer8 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer8); + sInfo.spareBuffer9 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer9); + sInfo.spareBuffer10 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer10); + sInfo.spareBuffer11 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer11); + sInfo.spareBuffer12 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer12); + sInfo.spareBuffer13 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer13); + sInfo.spareBuffer14 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer14); + sInfo.spareBuffer15 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer15); + sInfo.spareBuffer16 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer16); + sInfo.spareBuffer17 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer17); + sInfo.spareBuffer18 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer18); + sInfo.spareBuffer19 = bufzone_rebase(&sInfo.zone, sInfo.spareBuffer19); + sInfo.buffer20 = bufzone_rebase(&sInfo.zone, sInfo.buffer20); + + if (bufzone_finished_rebases(&sInfo.zone)) + return -1; + + // Number of index blocks? + sInfo.numIBlocks = CEIL_DIVIDE((sGeometry.pagesPerSublk - + sInfo.tocPagesPerBlock) - (sGeometry.numBlocks - 8), + (sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock) * + sInfo.tocEntriesPerPage) * 3; + + sInfo.unknCalculatedValue1 = sGeometry.numBlocks - sInfo.numIBlocks - 3; + + sInfo.totalPages = (sGeometry.numBlocks - 8) + * (sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock) + - sInfo.numIBlocks * sGeometry.pagesPerSublk; + + sInfo.tocArrayLength = CEIL_DIVIDE(sGeometry.pagesPerSublk + * sGeometry.numBlocks * sizeof(uint32_t), sGeometry.bytesPerPage); + + sInfo.tocArray = yaftl_alloc(sInfo.tocArrayLength * sizeof(TOCStruct)); + if (!sInfo.tocArray) + printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate tocArray\r\n"); + + sInfo.blockArray = yaftl_alloc(sGeometry.numBlocks * sizeof(BlockStruct)); + if (!sInfo.blockArray) + printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate blockArray\r\n"); + + sInfo.unknBuffer3_ftl = yaftl_alloc(sGeometry.pagesPerSublk << 2); + if (!sInfo.unknBuffer3_ftl) + printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate unknBuffer3_ftl\r\n"); + + // Initialize number of pages in a context slot. + sInfo.nPagesTocPageIndices = CEIL_DIVIDE(sInfo.tocArrayLength + * sizeof(sInfo.tocArray->indexPage), sGeometry.bytesPerPage); + sInfo.nPagesBlockStatuses = CEIL_DIVIDE(sGeometry.numBlocks + * sizeof(sInfo.blockArray->status), sGeometry.bytesPerPage); + sInfo.nPagesBlockReadCounts = CEIL_DIVIDE(sGeometry.numBlocks + * sizeof(sInfo.blockArray->readCount), sGeometry.bytesPerPage); + sInfo.nPagesBlockValidPagesDNumbers = sInfo.nPagesBlockReadCounts; + sInfo.nPagesBlockValidPagesINumbers = sInfo.nPagesBlockReadCounts; + sInfo.nPagesBlockEraseCounts = CEIL_DIVIDE(sGeometry.numBlocks + * sizeof(sInfo.blockArray->eraseCount), sGeometry.bytesPerPage); + + sInfo.totalEraseCount = 0; + sInfo.field_78 = 0; + + sInfo.ctrlBlockPageOffset = + sInfo.nPagesTocPageIndices + + sInfo.nPagesBlockStatuses + + sInfo.nPagesBlockReadCounts + + sInfo.nPagesBlockEraseCounts + + sInfo.nPagesBlockValidPagesDNumbers + + sInfo.nPagesBlockValidPagesINumbers + + 2 * sInfo.tocPagesPerBlock + + 2; + + sInfo.ftlCxtUsn = 0; + sInfo.ftlCxtPage = 0xFFFFFFFF; + + memset(&sInfo.blockStats, 0, sizeof(sInfo.blockStats)); + memset(&sStats, 0, sizeof(sStats)); + + gcResetReadCache(&sInfo.readc); + + if (gcInit()) + printk(KERN_DEBUG "PANIC!!!: YAFTL: GC initialization has failed\r\n"); + + if (BTOC_Init()) + printk(KERN_DEBUG "PANIC!!!: YAFTL: BTOC initialization has failed\r\n"); + + sInfo.latestUserBlk.tocBuffer = YAFTL_allocBTOC(-3); + sInfo.latestIndexBlk.tocBuffer = YAFTL_allocBTOC(-2); + + if (L2V_Init(sInfo.totalPages, sGeometry.numBlocks, + sGeometry.pagesPerSublk)) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: L2V initialization has failed\r\n"); + } + + yaftl_inited = 1; + return 0; +} + +static void initBlockStats(void) +{ + uint32_t i; + + sInfo.blockStats.numAvailable = 0; + sInfo.blockStats.numAllocated = 0; + sInfo.blockStats.numIAvailable = 0; + sInfo.blockStats.numIAllocated = 0; + sInfo.blockStats.numFree = 0; + + for (i = 0; i < sGeometry.numBlocks; i++) { + switch (sInfo.blockArray[i].status) { + case BLOCKSTATUS_ALLOCATED: + case BLOCKSTATUS_GC: + sInfo.blockStats.numAllocated++; + break; + + case BLOCKSTATUS_I_ALLOCATED: + case BLOCKSTATUS_I_GC: + sInfo.blockStats.numIAllocated++; + break; + + case BLOCKSTATUS_FREE: + sInfo.blockStats.numFree++; + break; + } + } + + sInfo.blockStats.numIAvailable = sInfo.numIBlocks + - sInfo.blockStats.numIAllocated; + + sInfo.blockStats.numAvailable = sInfo.blockStats.numFree + - sInfo.blockStats.numIAvailable; +} + +static void copyQwords(uint64_t* _src, uint64_t* _dest, size_t _bytes) +{ + uint64_t tmp; + size_t i; + + for (i = 0; i < _bytes / sizeof(uint64_t); ++i) { + tmp = _src[i]; + _dest[i] = tmp; + } +} + +/* Flush */ + +static int writeCxtInfo(uint32_t _page, void* _buf) +{ + int ret = 0; + uint32_t pageToWrite = 0; + uint32_t i, j; + uint32_t temp; + uint32_t perPage, left; + uint8_t* in = NULL; + SpareData* spare = sInfo.spareBuffer8; + YAFTLCxt* cxt = (YAFTLCxt*)_buf; + uint8_t* buf8 = (uint8_t*)_buf; + uint16_t* buf16 = (uint16_t*)_buf; + uint32_t* buf32 = (uint32_t*)_buf; + uint32_t vSize = 0; + void* sVSVFLStats; + + if (sInfo.field_78) + return 0; + + ++sInfo.ftlCxtUsn; + YAFTL_setupCleanSpare(spare); + memcpy(cxt, "CX01", 4); + cxt->tocPagesPerBlock = sInfo.tocPagesPerBlock; + cxt->tocEntriesPerPage = sInfo.tocEntriesPerPage; + cxt->numIBlocks = sInfo.numIBlocks; + cxt->totalPages = sInfo.totalPages; + cxt->tocArrayLength = sInfo.tocArrayLength; + cxt->numFreeCaches = sInfo.numFreeCaches; + cxt->field_64 = sInfo.field_5C; + cxt->latestUserBlk = sInfo.latestUserBlk.blockNum; + cxt->maxIndexUsn = sInfo.maxIndexUsn; + cxt->pagesUsedInLatestUserBlk = sInfo.latestUserBlk.usedPages; + cxt->numAvailableBlocks = sInfo.blockStats.numAvailable; + cxt->latestIndexBlk = sInfo.latestIndexBlk.blockNum; + cxt->maxIndexUsn2 = sInfo.maxIndexUsn; + cxt->pagesUsedInLatestIdxBlk = sInfo.latestIndexBlk.usedPages; + cxt->numIAvailableBlocks = sInfo.blockStats.numIAvailable; + cxt->numAllocatedBlocks = sInfo.blockStats.numAllocated; + cxt->numIAllocatedBlocks = sInfo.blockStats.numIAllocated; + cxt->numCaches = sInfo.numCaches; + cxt->unk188_0x63 = sInfo.unk188_0x63; + cxt->totalEraseCount = sInfo.totalEraseCount; + cxt->field_30 = sInfo.field_6C; + + pageToWrite = _page; + + // Write the context + ret = YAFTL_writePage(pageToWrite, (uint8_t*)cxt, spare); + if (ret != 0) + return ret; + ++pageToWrite; + + // Write latestUserBlk.tocBuffer + in = (uint8_t*)sInfo.latestUserBlk.tocBuffer; + for (i = 0; i < sInfo.tocPagesPerBlock; ++i) { + ret = YAFTL_writePage(pageToWrite + i, &in[i * sGeometry.bytesPerPage], + spare); + if (ret != 0) + return ret; + } + pageToWrite += sInfo.tocPagesPerBlock; + + // Write latestIndexBlk.tocBuffer + in = (uint8_t*)sInfo.latestIndexBlk.tocBuffer; + for (i = 0; i < sInfo.tocPagesPerBlock; ++i) { + ret = YAFTL_writePage(pageToWrite + i, &in[i * sGeometry.bytesPerPage], + spare); + if (ret != 0) + return ret; + } + pageToWrite += sInfo.tocPagesPerBlock; + + // Write statistics + sVSVFLStats = vfl->get_stats(vfl, &vSize); + + temp = 0x10003; + memset(_buf, 0, sGeometry.bytesPerPage); + memcpy(_buf, &sStats, sizeof(sStats)); + memcpy(_buf + 0x200, sVSVFLStats, vSize); + memcpy(_buf + 0x400, &sFTLStats, sizeof(sFTLStats)); + memcpy(&buf8[sGeometry.bytesPerPage - 4], &temp, 4); + ret = YAFTL_writePage(pageToWrite, _buf, spare); + if (ret != 0) + return ret; + ++pageToWrite; + + // Write tocArray's indexPage + perPage = sGeometry.bytesPerPage / sizeof(uint32_t); + left = sInfo.tocArrayLength; + for (i = 0; i < sInfo.nPagesTocPageIndices; ++i) { + for (j = 0; j < perPage && j < left; ++j) + buf32[j] = sInfo.tocArray[i * perPage + j].indexPage; + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesTocPageIndices; + + // Write blockArray's statuses + perPage = sGeometry.bytesPerPage; + left = sGeometry.numBlocks; + for (i = 0; i < sInfo.nPagesBlockStatuses; ++i) { + for (j = 0; j < perPage && j < left; ++j) { + uint8_t status = sInfo.blockArray[i * perPage + j].status; + + if (status == BLOCKSTATUS_CURRENT) + status = BLOCKSTATUS_ALLOCATED; + else if (status == BLOCKSTATUS_I_CURRENT) + status = BLOCKSTATUS_I_ALLOCATED; + + buf8[j] = status; + } + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesBlockStatuses; + + // Write blockArray's readCounts + perPage = sGeometry.bytesPerPage / sizeof(uint16_t); + left = sGeometry.numBlocks; + for (i = 0; i < sInfo.nPagesBlockReadCounts; ++i) { + for (j = 0; j < perPage && j < left; ++j) + buf16[j] = sInfo.blockArray[i * perPage + j].readCount; + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesBlockReadCounts; + + // Write blockArray's eraseCounts + perPage = sGeometry.bytesPerPage / sizeof(uint32_t); + left = sGeometry.numBlocks; + for (i = 0; i < sInfo.nPagesBlockEraseCounts; ++i) { + for (j = 0; j < perPage && j < left; ++j) + buf32[j] = sInfo.blockArray[i * perPage + j].eraseCount; + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesBlockEraseCounts; + + // Write blockArray's validPagesINos + perPage = sGeometry.bytesPerPage / sizeof(uint16_t); + left = sGeometry.numBlocks; + for (i = 0; i < sInfo.nPagesBlockValidPagesINumbers; ++i) { + for (j = 0; j < perPage && j < left; ++j) + buf16[j] = sInfo.blockArray[i * perPage + j].validPagesINo; + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesBlockValidPagesINumbers; + + // Write blockArray's validPagesDNos + perPage = sGeometry.bytesPerPage / sizeof(uint16_t); + left = sGeometry.numBlocks; + for (i = 0; i < sInfo.nPagesBlockValidPagesDNumbers; ++i) { + for (j = 0; j < perPage && j < left; ++j) + buf16[j] = sInfo.blockArray[i * perPage + j].validPagesDNo; + + ret = YAFTL_writePage(pageToWrite + i, _buf, spare); + if (ret != 0) + return ret; + + left -= perPage; + } + pageToWrite += sInfo.nPagesBlockValidPagesDNumbers; + + sInfo.ftlCxtPage = _page; + sInfo.field_78 = 1; + return 0; +} + +static int flushQuick(void) +{ + int32_t block = 0; + uint32_t page = 0; + uint32_t ftlCxtBlock = 0; + uint32_t lastBlock = 0; + void* tempBuf = NULL; + int result = 0; + + if (sInfo.field_78) + return 0; + + for (block = 0; block < sGeometry.numBlocks; block++) { + if (sInfo.blockArray[block].status == BLOCKSTATUS_FREE + && sInfo.blockArray[block].unkn5 != 0) + { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + if (!sInfo.field_79) { + if (vfl_erase_single_block(vfl, block, 1)) + printk(KERN_DEBUG "PANIC!!!: yaftl: erase block %d failed\r\n", block); + + ++sInfo.blockArray[block].eraseCount; + ++sInfo.field_DC[3]; + if (sInfo.blockArray[block].eraseCount > + sInfo.maxBlockEraseCount) + { + sInfo.maxBlockEraseCount = + sInfo.blockArray[block].eraseCount; + } + } + + sInfo.blockArray[block].unkn5 = 0; + } + } + + sInfo.field_79 = 0; + tempBuf = yaftl_alloc(MAX(sGeometry.bytesPerPage, sizeof(YAFTLInfo))); + if (tempBuf == NULL) { + printk(KERN_ERR "yaftl: flushQuick out of memory\r\n"); + return ENOMEM; + } + + if (sInfo.ftlCxtPage == 0xFFFFFFFF) { + int result; + block = sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]; + result = writeCxtInfo(block * sGeometry.pagesPerSublk, tempBuf); + + if (result != 0) + goto FLUSHQUICK_REPLACE_BLOCK; + + kfree(tempBuf); + return result; + } + + page = sInfo.ftlCxtPage + sInfo.ctrlBlockPageOffset + 1; + ftlCxtBlock = sInfo.ftlCxtPage / sGeometry.pagesPerSublk; + lastBlock = (sInfo.ftlCxtPage + 2 * sInfo.ctrlBlockPageOffset + 2) + / sGeometry.pagesPerSublk; + + // If everything fits: + if (ftlCxtBlock == page / sGeometry.pagesPerSublk + && ftlCxtBlock == lastBlock) + { + result = writeCxtInfo(page, tempBuf); + if (result != 0) + goto FLUSHQUICK_REPLACE_BLOCK; + + kfree(tempBuf); + return 0; + } + + // Else, replace the block :( + block = sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]; + sInfo.blockArray[block].status = BLOCKSTATUS_FTLCTRL; + + // Choose the next one. + ++sInfo.selCtrlBlockIndex; + sInfo.selCtrlBlockIndex %= 3; + block = sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]; + + sInfo.blockArray[block].status = BLOCKSTATUS_FTLCTRL_SEL; + vfl_erase_single_block(vfl, block, 1); + ++sInfo.blockArray[block].eraseCount; + ++sInfo.totalEraseCount; + + result = writeCxtInfo(block * sGeometry.pagesPerSublk, tempBuf); + if (result != 0) + goto FLUSHQUICK_REPLACE_BLOCK; + + kfree(tempBuf); + return 0; + +FLUSHQUICK_REPLACE_BLOCK: + // Erase the failed block + vfl_erase_single_block(vfl, block, 1); + ++sInfo.blockArray[block].eraseCount; + ++sInfo.totalEraseCount; + sInfo.blockArray[block].status = BLOCKSTATUS_FTLCTRL; + + // Choose a new block + ++sInfo.selCtrlBlockIndex; + sInfo.selCtrlBlockIndex %= 3; + block = sInfo.selCtrlBlockIndex; + + sInfo.blockArray[block].status = BLOCKSTATUS_FTLCTRL_SEL; + vfl_erase_single_block(vfl, block, 1); + ++sInfo.blockArray[block].eraseCount; + sInfo.ftlCxtPage = 0xFFFFFFFF; + ++sInfo.totalEraseCount; + kfree(tempBuf); + return result; +} + +void YAFTL_Flush(void) +{ + static uint16_t numFlushes = 0; + + uint16_t oldBlks[3]; + uint32_t block; + uint32_t found = 0; + uint32_t i; + + gcPrepareToFlush(); + if (numFlushes <= 1000) + goto FLUSH_QUICK; + + // Too many flushes - lets replace blocks. + numFlushes = 0; + + // If there are not enough blocks, there's no choice. + if (sInfo.blockStats.numAvailable < 3) + goto FLUSH_QUICK; + + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + for (i = 0; i < 3; ++i) + oldBlks[i] = sInfo.FTLCtrlBlock[i]; + + // Replace all the blocks. + for (block = 0; block < sGeometry.numBlocks && found < 3; block++) { + // Must be a kfree block, with much better erase count. + if (!sInfo.blockArray[block].status == BLOCKSTATUS_FREE + || sInfo.blockArray[block].eraseCount + 50 >= + sInfo.blockArray[oldBlks[found]].eraseCount) + { + continue; + } + + if (sInfo.blockArray[block].unkn5 != 0) { + // Need to erase. + if (vfl_erase_single_block(vfl, block, 1)) + printk(KERN_DEBUG "PANIC!!!: yaftl: erase block %d failed\r\n", block); + + sInfo.blockArray[block].unkn5 = 0; + } + + sInfo.blockArray[block].status = BLOCKSTATUS_FTLCTRL; + sInfo.FTLCtrlBlock[found++] = block; + } + + if (found == 0) + goto FLUSH_QUICK; + + vfl_write_context(vfl, sInfo.FTLCtrlBlock); + + sInfo.ftlCxtPage = 0xFFFFFFFF; + sInfo.selCtrlBlockIndex = 0; + sInfo.blockArray[sInfo.FTLCtrlBlock[0]].status = BLOCKSTATUS_FTLCTRL_SEL; + + // Erase the old blocks. + for (i = 0; i < 3; ++i) { + block = oldBlks[i]; + vfl_erase_single_block(vfl, oldBlks[i], 1); + sInfo.blockArray[block].status = BLOCKSTATUS_FREE; + ++sInfo.blockArray[block].eraseCount; + sInfo.blockArray[block].readCount = 0; + sInfo.blockArray[block].validPagesINo = 0; + sInfo.blockArray[block].validPagesDNo = 0; + sInfo.blockArray[block].unkn5 = 0; + if (sInfo.blockArray[block].eraseCount > sInfo.maxBlockEraseCount) + sInfo.maxBlockEraseCount = sInfo.blockArray[block].eraseCount; + } + + // Mark the old blocks which remained as FTLCtrl blocks. + for (i = found; i < 3; ++i) + sInfo.blockArray[sInfo.FTLCtrlBlock[i]].status = BLOCKSTATUS_FTLCTRL; + +FLUSH_QUICK: + while (flushQuick() != 0) + printk(KERN_ERR "yaftl: flushQuick failed\r\n"); + + sInfo.field_DC[2] = 0; + sInfo.field_DC[3] = 0; + ++sStats.flushes; +} + +/* Open & restore */ + +static int setStatisticsFromCxt(void *_cxt) +{ + int statBlockSize = 0x200; + uint32_t vSize = 0; + void* sVSVFLStats = vfl->get_stats(vfl, &vSize); + + copyQwords((uint64_t*)_cxt, (uint64_t*)&sStats, sizeof(sStats)); + copyQwords((uint64_t*)(_cxt + statBlockSize), (uint64_t*)sVSVFLStats, vSize);// size = 0x58 + copyQwords((uint64_t*)(_cxt + 2 * statBlockSize), (uint64_t*)&sFTLStats, sizeof(sFTLStats)); // size = 0x1C0 + + return 1; +} + +static uint32_t YAFTL_readCxtInfo(uint32_t _page, uint8_t* _ptr, uint8_t _extended, uint32_t *pSupported) +{ + uint32_t pageToRead = 0; + uint32_t i = 0, j = 0; + uint16_t* _ptr16 = (uint16_t*)_ptr; + uint32_t* _ptr32 = (uint32_t*)_ptr; + uint32_t leftToRead = 0, perPage = 0; + uint8_t* buf = NULL; + SpareData* spare = sInfo.spareBuffer5; + YAFTLCxt *pCxt = (YAFTLCxt*)_ptr; + + + if (YAFTL_readPage(_page, _ptr, spare, 0, 1, 0)) + return ERROR_ARG; + + if (!(spare->type & PAGETYPE_FTL_CLEAN)) + return ERROR_ARG; + + if (memcmp(pCxt->version, "CX01", 4)) { + if (pSupported) + *pSupported = 0; + + printk(KERN_ERR "yaftl: Wrong context version: %s\r\n", (char*)pCxt); + return ERROR_ARG; + } + + if (spare->usn != 0xFFFFFFFF) + sInfo.ftlCxtUsn = spare->usn; + + if (_extended) { + sInfo.tocPagesPerBlock = pCxt->tocPagesPerBlock; + sInfo.tocEntriesPerPage = pCxt->tocEntriesPerPage; + sInfo.numIBlocks = pCxt->numIBlocks; + sInfo.totalPages = pCxt->totalPages; + sInfo.tocArrayLength = pCxt->tocArrayLength; + sInfo.numFreeCaches = pCxt->numFreeCaches; + sInfo.latestUserBlk.blockNum = pCxt->latestUserBlk; + sInfo.latestUserBlk.usedPages = pCxt->pagesUsedInLatestUserBlk; + sInfo.latestIndexBlk.blockNum = pCxt->latestIndexBlk; + sInfo.latestIndexBlk.usedPages = pCxt->pagesUsedInLatestIdxBlk; + sInfo.blockStats.numAvailable = pCxt->numAvailableBlocks; + sInfo.blockStats.numIAvailable = pCxt->numIAvailableBlocks; + sInfo.blockStats.numAllocated = pCxt->numAllocatedBlocks; + sInfo.blockStats.numIAllocated = pCxt->numIAllocatedBlocks; + sInfo.field_5C = pCxt->field_64; + sInfo.maxIndexUsn = MAX(pCxt->maxIndexUsn, pCxt->maxIndexUsn2); + sInfo.totalEraseCount = pCxt->totalEraseCount; + sInfo.field_6C = pCxt->field_30; + sInfo.numCaches = pCxt->numCaches; + sInfo.unk188_0x63 = pCxt->unk188_0x63; + + printk(KERN_DEBUG "yaftl: reading latestUserBlk.tocBuffer\r\n"); + + // Read the user TOC into latestUserBlk.tocBuffer. + pageToRead = _page + 1; + buf = (uint8_t*)sInfo.latestUserBlk.tocBuffer; + + for (i = 0; i < sInfo.tocPagesPerBlock; i++) { + if (YAFTL_readPage(pageToRead + i, + &buf[i * sGeometry.bytesPerPage], spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + } + + printk(KERN_DEBUG "yaftl: reading latestIndexBlk.tocBuffer\r\n"); + + // Read the index TOC into latestIndexBlk.tocBuffer. + pageToRead += sInfo.tocPagesPerBlock; + buf = (uint8_t*)sInfo.latestIndexBlk.tocBuffer; + + for (i = 0; i < sInfo.tocPagesPerBlock; i++) { + if (YAFTL_readPage(pageToRead + i, + &buf[i * sGeometry.bytesPerPage], spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + } + + printk(KERN_DEBUG "yaftl: reading tocArray's index pages\r\n"); + } + + // Read statistics. + if (YAFTL_readPage(_page + sInfo.tocPagesPerBlock*2 + 1, _ptr, spare, 0, + 1, 0) || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + setStatisticsFromCxt(_ptr); + + if (_extended) { + // Read tocArray's index pages. + pageToRead += sInfo.tocPagesPerBlock + 1; + leftToRead = sInfo.tocArrayLength; + perPage = sGeometry.bytesPerPage / sizeof(sInfo.tocArray->indexPage); + + for (i = 0; i < sInfo.nPagesTocPageIndices; i++) { + if (YAFTL_readPage(pageToRead + i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) + sInfo.tocArray[i * perPage + j].indexPage = _ptr32[j]; + + leftToRead -= perPage; + } + + printk(KERN_DEBUG "yaftl: reading blockArrays's statuses\r\n"); + + // Read blockArray's statuses. + pageToRead += sInfo.nPagesTocPageIndices; + leftToRead = sGeometry.numBlocks; + perPage = sGeometry.bytesPerPage / sizeof(sInfo.blockArray->status); + + for (i = 0; i < sInfo.nPagesBlockStatuses; i++) { + if (YAFTL_readPage(pageToRead + i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) + sInfo.blockArray[i * perPage + j].status = _ptr[j]; + + leftToRead -= perPage; + } + } else { + // Skip. + pageToRead = _page + (sInfo.tocPagesPerBlock * 2) + 2 + + sInfo.nPagesTocPageIndices; + } + + printk(KERN_DEBUG "yaftl: reading blockArrays's read counts\r\n"); + + // Read blockArray's readCounts. + pageToRead += sInfo.nPagesBlockStatuses; + leftToRead = sGeometry.numBlocks; + perPage = sGeometry.bytesPerPage / sizeof(sInfo.blockArray->readCount); + + for (i = 0; i < sInfo.nPagesBlockReadCounts; i++) { + if (YAFTL_readPage(pageToRead + i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) + sInfo.blockArray[i * perPage + j].readCount = _ptr16[j]; + + leftToRead -= perPage; + } + + printk(KERN_DEBUG "yaftl: reading blockArrays's eraseCounts\r\n"); + + // Read blockArray's eraseCounts. + pageToRead += sInfo.nPagesBlockReadCounts; + leftToRead = sGeometry.numBlocks; + perPage = sGeometry.bytesPerPage / sizeof(sInfo.blockArray->eraseCount); + + for (i = 0; i < sInfo.nPagesBlockEraseCounts; i++) { + if (YAFTL_readPage(pageToRead + i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) + sInfo.blockArray[i * perPage + j].eraseCount = _ptr32[j]; + + leftToRead -= perPage; + } + + if (_extended) { + printk(KERN_DEBUG "yaftl: reading blockArrays's validPagesINo\r\n"); + + // Read blockArray's validPagesINo. + pageToRead += sInfo.nPagesBlockEraseCounts; + leftToRead = sGeometry.numBlocks; + perPage = sGeometry.bytesPerPage + / sizeof(sInfo.blockArray->validPagesINo); + + for (i = 0; i < sInfo.nPagesBlockValidPagesINumbers; i++) { + if (YAFTL_readPage(pageToRead+i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) { + sInfo.blockArray[i * perPage + j].validPagesINo = _ptr16[j]; + sInfo.blockStats.numValidIPages += _ptr16[j]; + } + + leftToRead -= perPage; + } + + printk(KERN_DEBUG "yaftl: reading blockArrays's validPagesDNo\r\n"); + + // Read blockArray's validPagesDNo. + pageToRead += sInfo.nPagesBlockValidPagesINumbers; + leftToRead = sGeometry.numBlocks; + perPage = sGeometry.bytesPerPage + / sizeof(sInfo.blockArray->validPagesDNo); + + for (i = 0; i < sInfo.nPagesBlockValidPagesDNumbers; i++) { + if (YAFTL_readPage(pageToRead + i, _ptr, spare, 0, 1, 0) + || !(spare->type & PAGETYPE_FTL_CLEAN)) + { + sInfo.field_78 = 0; + return ERROR_ARG; + } + + for (j = 0; j < leftToRead && j < perPage; j++) { + sInfo.blockArray[i * perPage + j].validPagesDNo = _ptr16[j]; + sInfo.blockStats.numValidDPages += _ptr16[j]; + } + + leftToRead -= perPage; + } + + sStats.indexPages = sInfo.blockStats.numValidIPages; + sStats.dataPages = sInfo.blockStats.numValidDPages; + } + + return 0; +} + +static BlockListNode *addBlockToList(BlockListNode *listHead, uint32_t blockNumber, uint32_t usn) +{ + BlockListNode *block = yaftl_alloc(sizeof(BlockListNode)); + + if (!block) + return NULL; + + block->usn = usn; + block->next = NULL; + block->prev = NULL; + block->blockNumber = blockNumber; + + // If the list isn't empty, insert our block. + // We sort the blocks by their USN, in descending order, + // so we will consider the newest blocks first when we restore. + if (listHead) { + BlockListNode *curr; + for (curr = listHead; curr->next && usn < curr->usn; curr = curr->next); + + if (usn < curr->usn) { + // Our node is the last. + block->prev = curr; + curr->next = block; + } else { + // Insert our node. + BlockListNode *prev = curr->prev; + + if (usn == curr->usn) + printk(KERN_ERR "yaftl: found two blocks (%d, %d) with the same usn.\r\n", blockNumber, curr->blockNumber); + + block->next = curr; + block->prev = prev; + curr->prev = block; + + if (prev) + prev->next = block; + else + listHead = block; + } + } else { + listHead = block; + } + + return listHead; +} + +static uint32_t restoreIndexBlock(uint16_t blockNumber, uint8_t first) +{ + uint32_t i; + uint32_t result, readSucceeded; + uint32_t blockOffset = blockNumber * sGeometry.pagesPerSublk; + uint32_t *BTOCBuffer = sInfo.pageBuffer2; + SpareData *spare = sInfo.spareBuffer3; + + result = YAFTL_readPage(blockOffset, (uint8_t*)sInfo.tocPageBuffer, spare, 0, 1, 0); + + if (!result && !(spare->type & PAGETYPE_INDEX)) { + printk(KERN_ERR "yaftl: restoreIndexBlock called with a non-index block %d.\r\n", blockNumber); + return 0; + } + + // Read all the block-table-of-contents pages of this block. + result = YAFTL_readBTOCPages(blockOffset + sGeometry.pagesPerSublk + - sInfo.tocPagesPerBlock, BTOCBuffer, spare, 0, 0, + sInfo.tocArrayLength); + + if (result > 1) { + // Read failed. + readSucceeded = 0; + sInfo.blockArray[blockNumber].unkn5 = 0x80; + } else { + readSucceeded = 1; + } + + if (first) { + // The first block has the largest USN. + sInfo.latestIndexBlk.blockNum = blockNumber; + sInfo.blockArray[blockNumber].status = BLOCKSTATUS_I_GC; + } else { + sInfo.blockArray[blockNumber].status = BLOCKSTATUS_I_ALLOCATED; + } + + // If we read non-empty pages, and they are indeed closed BTOCs: + if (result == 0 && (spare->type & PAGETYPE_INDEX) + && (spare->type & PAGETYPE_CLOSED)) { + // Loop through the index pages in this block, and fill the information according to the BTOC. + for (i = 0; i < sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; i++) { + uint32_t index = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock - i - 1; + + if (BTOCBuffer[index] < sInfo.tocArrayLength + && sInfo.tocArray[BTOCBuffer[index]].indexPage == 0xFFFFFFFF) { + // Valid index page number, and the data is missing. Fill in the information. + sInfo.tocArray[BTOCBuffer[index]].indexPage = blockOffset + index; + ++sInfo.blockArray[blockNumber].validPagesINo; + ++sInfo.blockStats.numValidIPages; + } + } + + if (first) { + // If this is the latest index block (i.e., the first in the list), + // update latestIndexBlk.tocBuffer, and maxIndexUsn. + sInfo.latestIndexBlk.usedPages = sGeometry.pagesPerSublk; + YAFTL_copyBTOC(sInfo.latestIndexBlk.tocBuffer, BTOCBuffer, + sInfo.tocArrayLength); + + if (spare->usn > sInfo.maxIndexUsn) + sInfo.maxIndexUsn = spare->usn; + } + + return 0; + } + + // Else - reading the BTOC has failed. Must go through each page. + if (first) { + sInfo.latestIndexBlk.usedPages = sGeometry.pagesPerSublk; + + if (!readSucceeded) + sInfo.latestIndexBlk.usedPages -= sInfo.tocPagesPerBlock; + } + + // Loop through each non-BTOC page (i.e., index page) in this block. + for (i = 0; i < sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; i++) { + uint32_t page = blockOffset + + sGeometry.pagesPerSublk + - sInfo.tocPagesPerBlock + - i - 1; + uint32_t lpn; + + result = YAFTL_readPage(page, (uint8_t*)sInfo.tocPageBuffer, spare, 0, 1, 0); + + lpn = spare->lpn; + + if (result > 1 || (!result && (!(spare->type & PAGETYPE_INDEX) || lpn >= sInfo.tocArrayLength))) { + // Read failed, or non-empty page with invalid data: + // Not marked as index, or lpn is too high. + sInfo.blockArray[blockNumber].unkn5 = 0x80; + readSucceeded = 0; + continue; + } + + if (lpn != 0xFFFFFFFF) { + // Valid LPN. + if (first) { + // First (most updated) index block - update latestIndexBlk.tocBuffer and maxIndexUsn. + sInfo.latestIndexBlk.tocBuffer[page - blockOffset] = lpn; + + if (spare->usn > sInfo.maxIndexUsn) + sInfo.maxIndexUsn = spare->usn; + } + + if (sInfo.tocArray[lpn].indexPage == 0xFFFFFFFF) { + // The information about this index page is missing - fill it in. + sInfo.tocArray[lpn].indexPage = page; + ++sInfo.blockArray[blockNumber].validPagesINo; + ++sInfo.blockStats.numValidIPages; + } + + // WTF? It must be another variable then. --Oranav + readSucceeded = 0; + continue; + } + + if (first && readSucceeded) + sInfo.latestIndexBlk.usedPages = page - blockOffset; + } + + return 0; +} + +static uint32_t restoreUserBlock(uint32_t blockNumber, uint32_t *bootBuffer, uint8_t firstBlock, uint32_t lpnOffset, uint32_t bootLength, BlockLpn *blockLpns) +{ + uint8_t readSucceeded; + uint32_t result, i; + uint32_t blockOffset = blockNumber * sGeometry.pagesPerSublk; + uint32_t *buff = sInfo.pageBuffer2; + SpareData *spare = sInfo.spareBuffer4; + + // If the lpn offset is off-boundaries, or this block is full of valid index pages - don't restore. + if ((lpnOffset && (lpnOffset > blockLpns[blockNumber].lpnMax || blockLpns[blockNumber].lpnMin - lpnOffset >= bootLength)) + || sInfo.blockArray[blockNumber].validPagesINo == sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock) { + return 0; + } + + if (lpnOffset && firstBlock) { + memset( + &sInfo.latestUserBlk.tocBuffer[sInfo.latestUserBlk.usedPages], + 0xFF, + (sGeometry.pagesPerSublk - sInfo.latestUserBlk.usedPages) * sizeof(uint32_t)); + + YAFTL_copyBTOC(buff, sInfo.latestUserBlk.tocBuffer, sInfo.totalPages); + + result = 0; + spare->type = 0x8; + readSucceeded = 1; + } else { + result = YAFTL_readBTOCPages(blockOffset + sGeometry.pagesPerSublk + - sInfo.tocPagesPerBlock, buff, spare, 0, 0, sInfo.totalPages); + if (result > 1) { + // Read failed. + sInfo.blockArray[blockNumber].unkn5 = 0x80; + readSucceeded = 0; + } else { + readSucceeded = 1; + } + } + + if (!lpnOffset) { + // First time we're in this block. + if (firstBlock) { + sInfo.latestUserBlk.blockNum = blockNumber; + sInfo.blockArray[blockNumber].status = BLOCKSTATUS_GC; + } else { + sInfo.blockArray[blockNumber].status = BLOCKSTATUS_ALLOCATED; + } + } + + // If we read non-index closed pages: + if (result == 0 && !(spare->type & PAGETYPE_INDEX) + && (spare->type & PAGETYPE_CLOSED)) { + // BTOC reading succeeded, process the information. + uint32_t nUserPages; + + if (lpnOffset && firstBlock) + nUserPages = sInfo.latestUserBlk.usedPages; + else + nUserPages = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + + for (i = 0; i < nUserPages; i++) { + uint32_t lpn = buff[nUserPages - 1 - i]; + + if (lpn >= sInfo.totalPages) + lpn = 0xFFFFFFFF; + + if (lpnOffset) { + uint32_t lpnMax = blockLpns[blockNumber].lpnMax; + uint32_t lpnMin = blockLpns[blockNumber].lpnMin; + + if (lpnMax != lpnMin || lpnMax != 0xFFFFFFFF) { + if (lpnMax < lpn) + blockLpns[blockNumber].lpnMax = lpn; + if (lpnMin > lpn) + blockLpns[blockNumber].lpnMin = lpn; + } else { + blockLpns[blockNumber].lpnMax = lpn; + blockLpns[blockNumber].lpnMin = lpn; + } + } + + if (lpnOffset <= lpn && lpn - lpnOffset < bootLength) { + ++sInfo.blockArray[blockNumber].validPagesINo; + + if (lpn != 0xFFFFFFFF && bootBuffer[lpn - lpnOffset] == 0xFFFFFFFF) { + bootBuffer[lpn - lpnOffset] = blockOffset + nUserPages - 1 - i; + ++sInfo.blockArray[blockNumber].validPagesDNo; + ++sInfo.blockStats.numValidDPages; + } + } + } + + if (firstBlock && !lpnOffset) { + sInfo.latestUserBlk.usedPages = sGeometry.pagesPerSublk; + YAFTL_copyBTOC(sInfo.latestUserBlk.tocBuffer, buff, + sInfo.totalPages); + } + + return 0; + } + + // If we're here, then reading the user-block BTOC has failed. + if (firstBlock) { + if (result == 0) + sInfo.latestUserBlk.usedPages = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + else + sInfo.latestUserBlk.usedPages = sGeometry.pagesPerSublk; + + // Calculate the number of user pages in this block, and the number of index pages. + for (i = 0; i < sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; i++) { + // If we find a non-user page, stop here. + if (YAFTL_readPage(blockOffset + i, (uint8_t*)sInfo.tocPageBuffer, spare, 0, 1, 0) != 0 + || !(spare->type & PAGETYPE_LBN) + || spare->lpn >= sInfo.totalPages) { + break; + } + } + + sInfo.latestUserBlk.usedPages = i; + sInfo.blockArray[blockNumber].validPagesINo = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock - i; + } + + // Read each user page in this block. + for (i = 0; i < sInfo.latestUserBlk.usedPages; i++) { + // Read the current page. + result = YAFTL_readPage(blockOffset + sInfo.latestUserBlk.usedPages - 1 - i, (uint8_t*)sInfo.tocPageBuffer, spare, 0, 1, 0); + + // If the read has failed, or the page wasn't a user page, mark as error. + if (result > 1 + || (result == 0 && (!(spare->type & PAGETYPE_LBN) || spare->lpn >= sInfo.totalPages))) { + if (!lpnOffset) + ++sInfo.blockArray[blockNumber].validPagesINo; + sInfo.blockArray[blockNumber].unkn5 = 0x80; + readSucceeded = 0; + } + + if (!lpnOffset && spare->lpn == 0xFFFFFFFF) + ++sInfo.blockArray[blockNumber].validPagesINo; + + if (firstBlock && spare->lpn == 0xFFFFFFFF && readSucceeded) { + sInfo.latestUserBlk.usedPages -= i + 1; + continue; + } + + if (!lpnOffset && spare->lpn < sInfo.totalPages) { + // First time we're in this block, and this is a user page with a valid LPN. + // Update lpnMax and lpnMin for this block. + uint32_t lpnMax = blockLpns[blockNumber].lpnMax; + uint32_t lpnMin = blockLpns[blockNumber].lpnMin; + + if (lpnMax != lpnMin || lpnMin != 0xFFFFFFFF) { + if (spare->lpn > lpnMax) + blockLpns[blockNumber].lpnMax = spare->lpn; + if (spare->lpn < lpnMin) + blockLpns[blockNumber].lpnMin = spare->lpn; + } else { + blockLpns[blockNumber].lpnMax = spare->lpn; + blockLpns[blockNumber].lpnMin = spare->lpn; + } + } + + if (firstBlock) + sInfo.latestUserBlk.tocBuffer[sInfo.latestUserBlk.usedPages - 1 - i] = spare->lpn; + + if (lpnOffset > spare->lpn || spare->lpn - lpnOffset >= bootLength) { + readSucceeded = 0; + continue; + } + + ++sInfo.blockArray[blockNumber].validPagesINo; + + if (bootBuffer[spare->lpn - lpnOffset] == 0xFFFFFFFF) { + bootBuffer[spare->lpn - lpnOffset] = blockOffset + sInfo.latestUserBlk.usedPages - 1 - i; + ++sInfo.blockArray[blockNumber].validPagesDNo; + ++sInfo.blockStats.numValidDPages; + } + + readSucceeded = 0; + } + + return 0; +} + +static uint32_t YAFTL_Restore(uint8_t ftlCtrlBlockPresent) +{ + uint32_t i, j, result = 0; + uint8_t *tempBuffer; + BlockListNode *userListHead = NULL, *indexListHead = NULL; + + + // TODO + printk(KERN_DEBUG "PANIC!!!: yaftl: sorry, no restore + write support yet :(\r\n"); + + printk(KERN_ERR "yaftl: context is invalid. performing a read-only restore...\r\n"); + + tempBuffer = yaftl_alloc(sGeometry.bytesPerPage); + if (!tempBuffer) { + printk(KERN_ERR "yaftl: out of memory.\r\n"); + return EIO; + } + + // Reset blockArray. + for (i = 0; i < sGeometry.numBlocks; i++) { + sInfo.blockArray[i].validPagesDNo = 0; + sInfo.blockArray[i].validPagesINo = 0; + + if (sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL + && sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL_SEL) { + sInfo.blockArray[i].status = 0; + } + } + + // Reset tocArray and blockStats. + sInfo.latestIndexBlk.blockNum = 0xFFFFFFFF; + memset(sInfo.tocArray, 0xFF, sizeof(TOCStruct) * sInfo.tocArrayLength); + memset(&sInfo.blockStats, 0, sizeof(BlockStats)); + + // Add all available blocks to the block lists. + for (i = 0; i < sGeometry.numBlocks; i++) { + uint8_t status = sInfo.blockArray[i].status; + uint8_t type; + + // Ignore FTL blocks. + if (ftlCtrlBlockPresent && (status == BLOCKSTATUS_FTLCTRL || status == BLOCKSTATUS_FTLCTRL_SEL)) + continue; + + // Try to find a readable page. + result = 0xFFFFFFFF; + for (j = 0; j < sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; j++) { + result = YAFTL_readPage(i * sGeometry.pagesPerSublk + j, + tempBuffer, sInfo.spareBuffer10, 0, 1, 0); + + if (result <= 1) + break; + } + + // If no readable page was found, kfree this block. + if (result > 1) { + if(!vfl_erase_single_block(vfl, i, 1)) + sInfo.blockArray[i].status = BLOCKSTATUS_FREE; + ++sInfo.blockArray[i].eraseCount; + continue; + } + + type = sInfo.spareBuffer10->type; + + if ((type != 0xFF || sInfo.spareBuffer10->lpn != 0xFFFFFFFF) && result != 1) { + // User or index page. Determine which. + if (type & PAGETYPE_INDEX) { + if (!(indexListHead = addBlockToList(indexListHead, i, sInfo.spareBuffer10->usn))) + printk(KERN_DEBUG "PANIC!!!: yaftl: out of memory.\r\n"); + } else if (type & PAGETYPE_LBN) { + if (!(userListHead = addBlockToList(userListHead, i, sInfo.spareBuffer10->usn))) + printk(KERN_DEBUG "PANIC!!!: yaftl: out of memory.\r\n"); + } else { + printk(KERN_ERR "yaftl: warning - block %d doesn't belong to anything (type 0x%02x).\r\n", i, type); + if(!vfl_erase_single_block(vfl, i, 1)) + sInfo.blockArray[i].status = BLOCKSTATUS_FREE; + ++sInfo.blockArray[i].eraseCount; + } + } else { + sInfo.blockArray[i].status = BLOCKSTATUS_FREE; + sInfo.blockArray[i].unkn5 = 1; + } + } + + sInfo.numFreeCaches = sInfo.numCaches; + + // Restore index blocks. + if (indexListHead) { + BlockListNode *prevIndexNode = indexListHead; + + while (indexListHead) { + printk(KERN_DEBUG "yaftl: restoring index block %d\r\n", indexListHead->blockNumber); + result = restoreIndexBlock(indexListHead->blockNumber, indexListHead == prevIndexNode); + + if (result) + goto restore_error; + + prevIndexNode = indexListHead; + indexListHead = indexListHead->next; + kfree(prevIndexNode); + } + } + + // If we haven't found any index block, use a kfree one. + if (sInfo.latestIndexBlk.blockNum == 0xFFFFFFFF) { + for (i = 0; i < sGeometry.numBlocks; i++) { + if (sInfo.blockArray[i].status == BLOCKSTATUS_FREE) { + sInfo.latestIndexBlk.blockNum = i; + sInfo.latestIndexBlk.usedPages = 0; + memset(sInfo.latestIndexBlk.tocBuffer, 0xFF, sInfo.tocPagesPerBlock * sGeometry.bytesPerPage); + sInfo.blockArray[i].status = BLOCKSTATUS_I_GC; + break; + } + } + } + + // Restore user blocks. + if (userListHead) { + BlockListNode *currUserNode, *prevUserNode; + BlockLpn *blockLpns = (BlockLpn*)yaftl_alloc(sGeometry.numBlocks * sizeof(BlockLpn)); + + // TODO: figure this out. + uint32_t *bootBuffer = (uint32_t*)yaftl_alloc(0x800000); + uint32_t entriesInBootBuffer = 0x800000 / sizeof(uint32_t); + + if (!blockLpns || !bootBuffer) { + printk(KERN_ERR "yaftl: out of memory.\r\n"); + + if (blockLpns) + kfree(blockLpns); + else if (bootBuffer) + kfree(bootBuffer); + + goto restore_error; + } + + memset(blockLpns, 0xFF, sGeometry.numBlocks * sizeof(BlockLpn)); + + for (i = 0; i < sInfo.totalPages; i += entriesInBootBuffer) { + uint32_t pagesInBootBuffer; + memset(bootBuffer, 0xFF, entriesInBootBuffer * sizeof(uint32_t)); + currUserNode = prevUserNode = userListHead; + + while (currUserNode) { + printk(KERN_DEBUG "yaftl: restoring user block %d\r\n", currUserNode->blockNumber); + result = restoreUserBlock( + currUserNode->blockNumber, + bootBuffer, + currUserNode == prevUserNode, + i, + entriesInBootBuffer, + blockLpns); + + if (result) { + kfree(bootBuffer); + kfree(blockLpns); + goto restore_error; + } + + prevUserNode = currUserNode; + currUserNode = currUserNode->next; + if (i + entriesInBootBuffer >= sInfo.totalPages) { + kfree(prevUserNode); + } + } + + memset(tempBuffer, 0xFF, sGeometry.bytesPerPage); + + pagesInBootBuffer = entriesInBootBuffer / sInfo.tocEntriesPerPage; + + for (j = 0; j < pagesInBootBuffer && (i + j * sInfo.tocEntriesPerPage) < sInfo.totalPages; j++) { + uint32_t bootBufferOffset = j * sInfo.tocEntriesPerPage; + uint32_t tocNum = i / sInfo.tocEntriesPerPage + j; + uint32_t indexPage = sInfo.tocArray[tocNum].indexPage; + uint32_t cacheNum; + + memset(tempBuffer, 0xFF, sGeometry.bytesPerPage); + + if (memcmp(&bootBuffer[bootBufferOffset], tempBuffer, sGeometry.bytesPerPage) != 0) { + if (indexPage != 0xFFFFFFFF && YAFTL_readPage(indexPage, tempBuffer, 0, 0, 1, 0) != 0) { + printk(KERN_ERR "yaftl: index page is unreadable at 0x%08x\r\n", indexPage); + memset(tempBuffer, 0xFF, sGeometry.bytesPerPage); + } + + if (memcmp(&bootBuffer[bootBufferOffset], tempBuffer, sGeometry.bytesPerPage) != 0) { + TOCCache *tocCache; + if (indexPage != 0xFFFFFFFF) { + --sInfo.blockArray[indexPage / sGeometry.pagesPerSublk].validPagesINo; + --sInfo.blockStats.numValidIPages; + } + + cacheNum = YAFTL_findFreeTOCCache(); + if (cacheNum == 0xFFFF) { + kfree(bootBuffer); + kfree(blockLpns); + printk(KERN_ERR "yaftl: out of TOC caches, but restore might have already succeeded.\r\n"); + goto restore_final; + } + + tocCache = &sInfo.tocCaches[cacheNum]; + memcpy(tocCache->buffer, &bootBuffer[bootBufferOffset], sGeometry.bytesPerPage); + tocCache->state = 1; + tocCache->page = tocNum; + sInfo.tocArray[tocNum].cacheNum = cacheNum; + sInfo.tocArray[tocNum].indexPage = 0xFFFFFFFF; + } + } else { + if (indexPage != 0xFFFFFFFF && sInfo.blockArray[indexPage].validPagesINo) + --sInfo.blockArray[indexPage].validPagesINo; + + cacheNum = sInfo.tocArray[tocNum].cacheNum; + if (cacheNum != 0xFFFF) { + sInfo.tocCaches[cacheNum].state = CACHESTATE_FREE; + sInfo.tocCaches[cacheNum].useCount = 0; + } + + sInfo.tocArray[tocNum].indexPage = 0xFFFFFFFF; + sInfo.tocArray[tocNum].cacheNum = 0xFFFF; + } + } + } + + if (!ftlCtrlBlockPresent) { + for (i = 0, j = 0; i < sGeometry.numBlocks && j <= 2; i++) { + if (sInfo.blockArray[i].status == BLOCKSTATUS_FREE) { + sInfo.blockArray[i].status = BLOCKSTATUS_FTLCTRL; + sInfo.FTLCtrlBlock[j++] = i; + } + } + + vfl_write_context(vfl, sInfo.FTLCtrlBlock); + } + + kfree(bootBuffer); + kfree(blockLpns); + + initBlockStats(); + + ++sStats.field_58; + sStats.freeBlocks = sInfo.blockStats.numAvailable + sInfo.blockStats.numIAvailable; + sStats.dataPages = sInfo.blockStats.numValidDPages; + sStats.indexPages = sInfo.blockStats.numValidIPages; + + for (i = 0; i < sGeometry.numBlocks; i++) { + if (sInfo.blockStats.numAvailable <= 4 || sInfo.blockStats.numIAvailable <= 1) + break; + + if (sInfo.blockArray[i].status == BLOCKSTATUS_ALLOCATED) { + if (!sInfo.blockArray[i].validPagesDNo && sInfo.blockStats.numAvailable <= 4) { + sInfo.blockArray[i].validPagesINo = 0; + gcFreeBlock(i, 0); + } + } else if (sInfo.blockArray[i].status == BLOCKSTATUS_I_ALLOCATED) { + if (!sInfo.blockArray[i].validPagesINo && sInfo.blockStats.numIAvailable <= 1) { + gcFreeIndexPages(i, 0); + } + } + } + + gcFreeIndexPages(sInfo.latestIndexBlk.blockNum, 0); + sInfo.blockArray[sInfo.latestUserBlk.blockNum].validPagesINo = 0; + gcFreeBlock(sInfo.latestUserBlk.blockNum, 0); + + while (sInfo.numIBlocks < sInfo.blockStats.numIAllocated + 2) + gcFreeIndexPages(0xFFFFFFFF, 1); + + for (i = 0; i < sGeometry.numBlocks; i++) { + uint8_t status = sInfo.blockArray[i].status; + + if (status == BLOCKSTATUS_ALLOCATED || status == BLOCKSTATUS_GC) + sInfo.blockArray[i].validPagesINo = 0; + + if (status != BLOCKSTATUS_FREE && sInfo.blockArray[i].unkn5 == 0x80) { + if (status == BLOCKSTATUS_ALLOCATED || status == BLOCKSTATUS_GC) + gcFreeBlock(i, 0); + else + gcFreeIndexPages(i, 0); + } + } + } else { + // No user pages. Find a kfree block, and choose it to be the latest user page. + for (i = 0; i < sGeometry.numBlocks && sInfo.blockArray[i].status != BLOCKSTATUS_FREE; i++); + + if (i < sGeometry.numBlocks) { + sInfo.latestUserBlk.blockNum = i; + sInfo.blockArray[i].status = BLOCKSTATUS_GC; + } + + sInfo.latestUserBlk.usedPages = 0; + memset(sInfo.latestUserBlk.tocBuffer, 0xFF, sInfo.tocPagesPerBlock * sGeometry.bytesPerPage); + + if (!ftlCtrlBlockPresent) { + for (i = 0, j = 0; i < sGeometry.numBlocks && j <= 2; i++) { + if (sInfo.blockArray[i].status == BLOCKSTATUS_FREE) { + sInfo.blockArray[i].status = BLOCKSTATUS_FTLCTRL; + sInfo.FTLCtrlBlock[j++] = i; + } + } + + vfl_write_context(vfl, sInfo.FTLCtrlBlock); + } + + initBlockStats(); + + ++sStats.field_58; + sStats.freeBlocks = sInfo.blockStats.numAvailable + sInfo.blockStats.numIAvailable; + sStats.dataPages = 0; + sStats.indexPages = 0; + } + + sInfo.blockStats.field_1C = 1; + +restore_final: + updateBtocCache(); + + if (sInfo.ftlCxtPage != 0xFFFFFFFF) { + sInfo.ftlCxtPage = + sGeometry.pagesPerSublk + + (sInfo.ftlCxtPage / sGeometry.pagesPerSublk) * sGeometry.pagesPerSublk + - 1; + } + + // I know this seems strange, but that's the logic they do here. --Oranav + for (i = 0; i < sGeometry.numBlocks; i++) { + sInfo.maxBlockEraseCount = 0; + sInfo.minBlockEraseCount = 0xFFFFFFFF; + + if (sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL + && sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL_SEL) { + if (sInfo.blockArray[i].eraseCount) + sInfo.maxBlockEraseCount = sInfo.blockArray[i].eraseCount; + + if (sInfo.blockArray[i].eraseCount < sInfo.minBlockEraseCount) + sInfo.minBlockEraseCount = sInfo.blockArray[i].eraseCount; + } + } + + sInfo.pagesAvailable = sInfo.unk188_0x63 * (sInfo.totalPages - 1) / 0x64; + sInfo.bytesPerPage = sGeometry.bytesPerPage; + + L2V_Open(); + + printk(KERN_ERR "yaftl: restore done.\r\n"); + + kfree(tempBuffer); + return 0; + +restore_error: + kfree(tempBuffer); + return result; +} + +static uint32_t YAFTL_Open(uint32_t signature_bit) +{ + uint16_t ftlCtrlBlockBuffer[3]; + uint32_t versionSupported = 1; + uint32_t i; + SpareData* spare; + uint32_t maxUsn = 0; + uint32_t ftlCtrlBlock = 0; + uint32_t some_val; + int restoreNeeded = 0; + + memset(sInfo.tocArray, 0xFF, sInfo.tocArrayLength * sizeof(TOCStruct)); + memset(sInfo.blockArray, 0xFF, sGeometry.numBlocks * sizeof(BlockStruct)); + + for (i = 0; i < sGeometry.numBlocks; i++) { + sInfo.blockArray[i].eraseCount = 0; + sInfo.blockArray[i].validPagesDNo = 0; + sInfo.blockArray[i].validPagesINo = 0; + sInfo.blockArray[i].readCount = 0; + sInfo.blockArray[i].status = 0; + sInfo.blockArray[i].unkn5 = 0; + } + + memcpy(ftlCtrlBlockBuffer, vfl_get_ftl_ctrl_block(vfl), + sizeof(ftlCtrlBlockBuffer)); + + if (ftlCtrlBlockBuffer[0] == 0xFFFF) { + // No FTL ctrl block was found. + return YAFTL_Restore(0); + } + + for (i = 0; i < 3; i++) { + sInfo.FTLCtrlBlock[i] = ftlCtrlBlockBuffer[i]; + sInfo.blockArray[ftlCtrlBlockBuffer[i]].status = BLOCKSTATUS_FTLCTRL; + } + + if (!sInfo.pageBuffer) { + printk(KERN_DEBUG "PANIC!!!: yaftl: This can't happen. This shouldn't happen. Whatever, it's doing something else then.\r\n"); + return -1; + } + + // Find the latest (with max USN) FTL context block. + spare = sInfo.spareBuffer6; + + for (i = 0; i < 3; i++) { + error_t result = + YAFTL_readPage(ftlCtrlBlockBuffer[i] * sGeometry.pagesPerSublk, + sInfo.pageBuffer, spare, 0, 0, 0); + + if (result == 0) { + if (spare->usn != 0xFFFFFFFF && spare->usn > maxUsn) { + maxUsn = spare->usn; + ftlCtrlBlock = ftlCtrlBlockBuffer[i]; + sInfo.selCtrlBlockIndex = i; + } + } else if (result != 1) { + /* + vfl_erase_single_block(vfl, ftlCtrlBlockBuffer[i], 1); + ++sInfo.blockArray[ftlCtrlBlockBuffer[i]].eraseCount; + */ + // TODO: verify that (result != 1) is the condition we want here. + } + } + + if (!maxUsn) { + sInfo.blockArray[ftlCtrlBlockBuffer[0]].status + = BLOCKSTATUS_FTLCTRL_SEL; + sInfo.selCtrlBlockIndex = 0; + some_val = 5; + } else { + sInfo.blockArray[ftlCtrlBlock].status = BLOCKSTATUS_FTLCTRL_SEL; + i = 0; + while (1) { + spare = sInfo.spareBuffer6; + + if (i < sGeometry.pagesPerSublk - sInfo.ctrlBlockPageOffset) + sInfo.ftlCxtUsn = spare->usn; + + if (i >= sGeometry.pagesPerSublk - sInfo.ctrlBlockPageOffset + || YAFTL_readPage(sGeometry.pagesPerSublk * ftlCtrlBlock + i, + sInfo.pageBuffer, spare, 0, 1, 0) != 0) + { + printk(KERN_ERR "yaftl: no valid context slot was found, a restore is definitely needed.\r\n"); + sInfo.ftlCxtPage = ~sInfo.ctrlBlockPageOffset + + sGeometry.pagesPerSublk * ftlCtrlBlock + i; + if (spare->usn != 0xFFFFFFFF) + sInfo.ftlCxtUsn = spare->usn; + + sInfo.field_78 = 0; + YAFTL_readCxtInfo(sInfo.ftlCxtPage, sInfo.pageBuffer, 0, + &versionSupported); + some_val = 5; + break; + } + + if (YAFTL_readPage(sGeometry.pagesPerSublk * ftlCtrlBlock + i + + sInfo.ctrlBlockPageOffset, sInfo.pageBuffer, spare, 0, + 1, 0) == 1) + { + sInfo.ftlCxtPage = sGeometry.pagesPerSublk * ftlCtrlBlock + i; + if (YAFTL_readCxtInfo(sInfo.ftlCxtPage, sInfo.pageBuffer, 1, + &versionSupported) != 0) + { + some_val = 5; + sInfo.field_78 = 0; + } else { + some_val = 0; + sInfo.field_78 = 1; + } + break; + } + + i += sInfo.ctrlBlockPageOffset + 1; + } + } + + printk(KERN_ERR "yaftl: ftl context is at logical page %d\r\n", sInfo.ftlCxtPage); + + if (versionSupported != 1) { + printk(KERN_ERR "yaftl: unsupported low-level format version.\r\n"); + return EINVAL; + } + + if (sStats.refreshes == 0xFFFFFFFF) { + printk(KERN_ERR "yaftl: clearing refresh stats\r\n"); + sStats.refreshes = 0; + } + + if (sInfo.ftlCxtUsn != 0 && sInfo.ftlCxtUsn != 0xFFFFFFFF) { + for (i = 0; i < 3; ++i) { + if (sInfo.blockArray[sInfo.FTLCtrlBlock[i]].eraseCount == 0) { + sInfo.blockArray[sInfo.FTLCtrlBlock[i]].eraseCount = + divide64 (sInfo.ftlCxtUsn, + (sGeometry.pagesPerSublk / sInfo.ctrlBlockPageOffset)); + } + } + } + + if (some_val || signature_bit) { + restoreNeeded = 1; + sInfo.numCaches *= 100; + } + + sInfo.tocCaches = yaftl_alloc(sInfo.numCaches * sizeof(TOCCache)); + if (!sInfo.tocCaches) { + printk(KERN_ERR "yaftl: error allocating memory\r\n"); + return -1; + } + + bufzone_init(&sInfo.segment_info_temp); + + for (i = 0; i < sInfo.numCaches; i++) + sInfo.tocCaches[i].buffer = bufzone_alloc(&sInfo.segment_info_temp, sGeometry.bytesPerPage); + + if (bufzone_finished_allocs(&sInfo.segment_info_temp)) + return -1; + + for (i = 0; i < sInfo.numCaches; i++) { + sInfo.tocCaches[i].buffer = bufzone_rebase(&sInfo.segment_info_temp, sInfo.tocCaches[i].buffer); + sInfo.tocCaches[i].state = CACHESTATE_FREE; + sInfo.tocCaches[i].page = 0xFFFF; + sInfo.tocCaches[i].useCount = 0; + memset(sInfo.tocCaches[i].buffer, 0xFF, sGeometry.bytesPerPage); + } + + if (bufzone_finished_rebases(&sInfo.segment_info_temp)) + return -1; + + if (restoreNeeded) + return YAFTL_Restore(1); + + sInfo.pagesAvailable = sInfo.unk188_0x63 * (sInfo.totalPages - 1) / 100; + sInfo.bytesPerPage = sGeometry.bytesPerPage; + + sInfo.maxBlockEraseCount = 0; + sInfo.minBlockEraseCount = 0xFFFFFFFF; + + for (i = 0; i < sGeometry.numBlocks; i++) { + if (sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL + && sInfo.blockArray[i].status != BLOCKSTATUS_FTLCTRL_SEL) + { + if (sInfo.blockArray[i].eraseCount > sInfo.maxBlockEraseCount) + sInfo.maxBlockEraseCount = sInfo.blockArray[i].eraseCount; + + if (sInfo.blockArray[i].eraseCount < sInfo.minBlockEraseCount) + sInfo.minBlockEraseCount = sInfo.blockArray[i].eraseCount; + } + } + + initBlockStats(); + updateBtocCache(); + L2V_Open(); + + printk(KERN_ERR "yaftl: successfully opened!\r\n"); + + return 0; +} + +/* Read */ + +static int verifyUserSpares(uint32_t _start, SpareData* _spares, size_t _count) +{ + uint32_t i; + for (i = 0; i < _count; i++) { + if (_spares[i].lpn != (_start + i)) + { + printk(KERN_ERR "YAFTL_Read: mismatch between lpn and metadata!\r\n"); + return 0; + } + + if (_spares[i].type & PAGETYPE_MAGIC) + return 0; + } + + return 1; +} + +static int YAFTL_Read(uint32_t lpn, uint32_t nPages, uint8_t* pBuf) +{ +#ifdef YUSTAS_FIXME + uint32_t emf = h2fmi_get_emf(); +#endif + int ret = 0; + uint32_t tocPageNum, tocEntry, tocCacheNum, kfreeTOCCacheNum; + uint32_t testMode, pagesRead = 0, numPages = 0; + uint32_t i; + uint8_t* data = NULL; + uint8_t* readBuf = pBuf; + + h2fmi_set_emf(0, 0); + + if (pBuf == NULL && nPages != 1) + return EINVAL; + + if (pBuf != NULL) + ++sStats.reads; + + if ((lpn + nPages) >= sInfo.totalPages) + return EINVAL; + + nand_device_set_ftl_region(vfl->get_device(vfl), lpn, 0, nPages, pBuf); + + sInfo.lastTOCPageRead = 0xFFFFFFFF; + sInfo.readc.span = 0; + + testMode = (!pBuf && nPages == 1); + + if (testMode) + *sInfo.unknBuffer3_ftl = 0xFFFFFFFF; + + while (pagesRead != nPages) { + // TODO: Use L2V, which should be much, much faster. + tocPageNum = (lpn + pagesRead) / sInfo.tocEntriesPerPage; + if ((sInfo.tocArray[tocPageNum].cacheNum == 0xFFFF) + && (sInfo.tocArray[tocPageNum].indexPage == 0xFFFFFFFF)) + { + if (testMode) + return 0; + + h2fmi_set_emf(emf, 0); + if (!YAFTL_readMultiPages(sInfo.unknBuffer3_ftl, numPages, data, 0, 0, 1) + || !verifyUserSpares(lpn + (data - pBuf) / sGeometry.bytesPerPage, sInfo.buffer20, numPages)) + ret = EIO; + h2fmi_set_emf(0, 0); + + numPages = 0; + readBuf += sGeometry.bytesPerPage; + } else { + // Should lookup TOC. + tocCacheNum = sInfo.tocArray[tocPageNum].cacheNum; + + if (tocCacheNum != 0xFFFF) { + // There's a cache for it! + sInfo.tocCaches[tocCacheNum].useCount++; + tocEntry = sInfo.tocCaches[tocCacheNum].buffer[(lpn + pagesRead) % sInfo.tocEntriesPerPage]; + } else { + // TOC cache isn't valid, find whether we can cache the data. + if ((kfreeTOCCacheNum = YAFTL_findFreeTOCCache()) != 0xFFFF) + { + // There's a kfree cache. + ret = YAFTL_readPage( + sInfo.tocArray[tocPageNum].indexPage, + (uint8_t*)sInfo.tocCaches[kfreeTOCCacheNum].buffer, + 0, 0, 1, 1); + + if (ret) + goto YAFTL_READ_RETURN; + + L2V_UpdateFromTOC(tocPageNum, + sInfo.tocCaches[kfreeTOCCacheNum].buffer); + sInfo.numFreeCaches--; + sInfo.tocCaches[kfreeTOCCacheNum].state = CACHESTATE_CLEAN; + sInfo.tocCaches[kfreeTOCCacheNum].useCount = 1; + sInfo.tocCaches[kfreeTOCCacheNum].page = tocPageNum; + sInfo.tocArray[tocPageNum].cacheNum = kfreeTOCCacheNum; + + tocEntry = sInfo.tocCaches[kfreeTOCCacheNum].buffer[(lpn + pagesRead) % sInfo.tocEntriesPerPage]; + } else { + // No kfree cache. Is this the last TOC page we read? + if (sInfo.lastTOCPageRead != tocPageNum) { + // No, it isn't. Read it. + ret = YAFTL_readPage( + sInfo.tocArray[tocPageNum].indexPage, + (uint8_t*)sInfo.tocPageBuffer, + 0, 0, 1, 1); + if(ret) + goto YAFTL_READ_RETURN; + + sInfo.lastTOCPageRead = tocPageNum; + } + + tocEntry = sInfo.tocPageBuffer[(lpn + pagesRead) % sInfo.tocEntriesPerPage]; + } + } + + // Okay, obtained the TOC entry. + if (tocEntry == 0xFFFFFFFF) { + if (testMode) + return 0; + + h2fmi_set_emf(emf, 0); + if (YAFTL_readMultiPages(sInfo.unknBuffer3_ftl, numPages, + data, 0, 0, 1) == 0 + || verifyUserSpares( + lpn + (data - pBuf) / sGeometry.bytesPerPage, + sInfo.buffer20, numPages) == 0) + { + ret = EIO; + } + h2fmi_set_emf(0, 0); + + numPages = 0; + data = NULL; + } else { + sInfo.unknBuffer3_ftl[numPages] = tocEntry; + numPages++; + + if (data == NULL) + data = readBuf; + + if (numPages == sGeometry.pagesPerSublk) { + if (testMode) + return 0; + + h2fmi_set_emf(emf, 0); + if (!YAFTL_readMultiPages(sInfo.unknBuffer3_ftl, numPages, data, 0, 0, 1) + || !verifyUserSpares(lpn + (data - pBuf) / sGeometry.bytesPerPage, + sInfo.buffer20, + numPages)) + ret = EIO; + h2fmi_set_emf(0, 0); + + numPages = 0; + data = NULL; + } + } + + readBuf += sGeometry.bytesPerPage; + } + + pagesRead++; + } + + if (numPages == 0 && testMode) { + ret = EIO; + goto YAFTL_READ_RETURN_NOINDEX; + } else if (numPages != 0) { + if (testMode) { + ret = 0; + goto YAFTL_READ_RETURN_NOINDEX; + } + + h2fmi_set_emf(emf, 0); + if (!YAFTL_readMultiPages(sInfo.unknBuffer3_ftl, numPages, data, 0, 0, 1) + || !verifyUserSpares(lpn + (data - pBuf) / sGeometry.bytesPerPage, + sInfo.buffer20, + numPages)) + { + ret = EIO; + } + h2fmi_set_emf(0, 0); + } + + sStats.pagesRead += nPages; + if (sInfo.refreshNeeded) { + for (i = 0; i < sGeometry.numBlocks; ++i) { + if (sInfo.blockArray[i].readCount > REFRESH_TRIGGER) { + uint8_t status = sInfo.blockArray[i].status; + + if (status == BLOCKSTATUS_I_GC + || status == BLOCKSTATUS_I_ALLOCATED) + { + ++sStats.blocksRefreshed; + gcFreeIndexPages(i, 1); + } else if (status == BLOCKSTATUS_GC + || status == BLOCKSTATUS_ALLOCATED) + { + ++sStats.blocksRefreshed; + gcFreeBlock(i, 1); + } + } + } + + sInfo.refreshNeeded = 0; + } + + if (sInfo.field_DC[2] >= sGeometry.total_usable_pages / 2 + || sInfo.field_DC[3] >= sGeometry.numBlocks / 2) + { + YAFTL_Flush(); + } + + goto YAFTL_READ_RETURN_NOINDEX; + +YAFTL_READ_RETURN: + nand_device_set_ftl_region(vfl->get_device(vfl), 0, 0, 0, 0); + + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + return ret; + +YAFTL_READ_RETURN_NOINDEX: + nand_device_set_ftl_region(vfl->get_device(vfl), 0, 0, 0, 0); + return ret; +} + +/* Write */ + +static uint32_t* refreshTOCCaches(uint32_t _page, uint32_t* _pCacheEntry) +{ + SpareData* pSpare = sInfo.spareBuffer13; + uint32_t tocIndex = _page / sInfo.tocEntriesPerPage; + uint32_t indexPage = sInfo.tocArray[tocIndex].indexPage; + uint32_t cacheNum = sInfo.tocArray[tocIndex].cacheNum; + uint32_t kfreeCacheSlot; + + *_pCacheEntry = _page % sInfo.tocEntriesPerPage; + + if (cacheNum != 0xFFFF) { + kfreeCacheSlot = cacheNum; + } else { + // Not cached. Retreive a kfree cache. + kfreeCacheSlot = YAFTL_findFreeTOCCache(); + + if (kfreeCacheSlot == 0xFFFF) { + // No kfree caches. Clear one and use it. + kfreeCacheSlot = YAFTL_clearEntryInCache(0xFFFF); + + if (kfreeCacheSlot == 0xFFFF) + printk(KERN_DEBUG "PANIC!!!: YAFTL: refreshTOCCaches is out of caches\r\n"); + } + } + + sInfo.tocCaches[kfreeCacheSlot].page = _page / sInfo.tocEntriesPerPage; + sInfo.tocArray[_page / sInfo.tocEntriesPerPage].cacheNum = kfreeCacheSlot; + + sInfo.tocCaches[kfreeCacheSlot].state = CACHESTATE_DIRTY; + ++sInfo.tocCaches[kfreeCacheSlot].useCount; + + if (indexPage != 0xFFFFFFFF) { + if (cacheNum == 0xFFFF) { + // Read the index page into the newly obtained cache. + error_t result = YAFTL_readPage(indexPage, + (uint8_t*)sInfo.tocCaches[kfreeCacheSlot].buffer, pSpare, + 0, 1, 1); + + if (result) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: refreshTOCCaches failed to read an index" + " page\r\n"); + } + } + + // Invalidate the index page, since it's already cached. + --sInfo.blockArray[indexPage / sGeometry.pagesPerSublk].validPagesINo; + --sInfo.blockStats.numValidIPages; + --sStats.indexPages; + sInfo.tocArray[tocIndex].indexPage = 0xFFFFFFFF; + } + + return sInfo.tocCaches[kfreeCacheSlot].buffer; +} + +static void invalidatePages(uint32_t _start, uint32_t _count) +{ + uint32_t blocksFreed = 0; + uint32_t* cacheBuffer = NULL; + uint32_t pageIndex = 0; + uint32_t i; + + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + L2V_Update(_start, _count, 0x1FF0002); + + for (i = 0; i < _count; ++i) { + if (cacheBuffer == NULL) + cacheBuffer = refreshTOCCaches(_start + i, &pageIndex); + + if (cacheBuffer[pageIndex] == 0xFFFFFFFF) { + // Yay, already invalid. + ++pageIndex; + } else { + uint32_t block = cacheBuffer[pageIndex] / sGeometry.pagesPerSublk; + --sInfo.blockStats.numValidDPages; + + if (sInfo.blockArray[block].validPagesDNo == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: tried to invalidate pages in block %d, " + "but it had no valid pages at all!\r\n", block); + } + + --sInfo.blockArray[block].validPagesDNo; + --sStats.dataPages; + cacheBuffer[pageIndex++] = 0xFFFFFFFF; + + if (sInfo.blockArray[block].validPagesDNo == 0 && + sInfo.blockArray[block].status == BLOCKSTATUS_ALLOCATED) { + // This block is kfree now! + ++blocksFreed; + } + } + + // If this TOC cache is over, mark as done. + if (pageIndex >= sInfo.tocEntriesPerPage) + cacheBuffer = NULL; + } + + // Collect garbage for each block which should be kfreed. + for (i = 0; i < blocksFreed; ++i) + gcFreeBlock(0xFFFFFFFF, 1); +} + +static void initUserSpareDatas(SpareData* _pSpare, uint32_t _page, + uint32_t _count) +{ + uint32_t i; + + if (sInfo.lastWrittenBlock != sInfo.latestUserBlk.blockNum) { + sInfo.lastWrittenBlock = sInfo.latestUserBlk.blockNum; + ++sInfo.maxIndexUsn; + } + + for (i = 0; i < _count; ++i) { + _pSpare[i].lpn = _page + i; + _pSpare[i].usn = sInfo.maxIndexUsn; + _pSpare[i].type = PAGETYPE_LBN; + } +} + +static int YAFTL_Write(uint32_t _pageStart, uint32_t _numPages, uint8_t* _pBuf) +{ + uint32_t i; + int finished = 0; + + struct { + uint32_t pageStart; + uint32_t numPages; + uint8_t *pBuf; + } try; + + // Write index TOC if necessary + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + ++sStats.writes; + + if (_pageStart + _numPages >= sInfo.totalPages) + return EINVAL; + + nand_device_set_ftl_region(vfl->get_device(vfl), + _pageStart, 0, _numPages, _pBuf); + invalidatePages(_pageStart, _numPages); + + try.pageStart = _pageStart; + try.pBuf = _pBuf; + try.numPages = _numPages; + + while (!finished) { + uint32_t currentPage = try.pageStart; + uint32_t toWrite = try.numPages; + uint8_t* pBufOffset = try.pBuf; + + gcPrepareToWrite(try.numPages); + + while (toWrite != 0) { + error_t result; + uint32_t maxWritePages; + uint32_t physWriteLimit; + uint32_t* cache; + uint32_t cacheNum; + + // Check whether latest user block is full + if (sInfo.latestUserBlk.usedPages >= sGeometry.pagesPerSublk - + sInfo.tocPagesPerBlock) { + YAFTL_closeLatestBlock(1); + YAFTL_allocateNewBlock(1); + + try.pageStart = currentPage; + try.numPages = toWrite; + try.pBuf = pBufOffset; + } + + physWriteLimit = 32 * sGeometry.total_banks_ftl - + sInfo.latestUserBlk.usedPages % sGeometry.total_banks_ftl; + + maxWritePages = sGeometry.pagesPerSublk - + (sInfo.tocPagesPerBlock + sInfo.latestUserBlk.usedPages); + + if (maxWritePages > physWriteLimit) + maxWritePages = physWriteLimit; + + if (maxWritePages > toWrite) + maxWritePages = toWrite; + + initUserSpareDatas(sInfo.spareBuffer19, currentPage, maxWritePages); + + // TODO: Use VSVFL_writeMultiplePagesInVb. + result = 0; + for (i = 0; i < maxWritePages && !result; ++i) { + uint32_t page = sInfo.latestUserBlk.blockNum + * sGeometry.pagesPerSublk + + sInfo.latestUserBlk.usedPages + i; + + // FIXME: Should we really use sGeometry.bytesPerPage??? + result = vfl_write_single_page(vfl, page, + &pBufOffset[sGeometry.bytesPerPage * i], + (uint8_t*)&sInfo.spareBuffer19[i], 1); + } + + if (result) { + YAFTL_allocateNewBlock(1); + invalidatePages(try.pageStart, try.numPages); + + if (sInfo.blockArray[sInfo.latestUserBlk.blockNum].status + != BLOCKSTATUS_FREE) { + gcFreeBlock(sInfo.latestUserBlk.blockNum, 1); + } + + break; + } + + // TODO: Maybe a better code logic? + // This iteration was successful, so set finished to 1. + finished = 1; + + // Update latestUserBlk.tocBuffer + for (i = 0; i < maxWritePages; ++i) { + sInfo.latestUserBlk.tocBuffer[sInfo.latestUserBlk.usedPages + + i] = currentPage + i; + } + + // Update L2V + L2V_Update(currentPage, maxWritePages, sInfo.latestUserBlk.usedPages + + (sInfo.latestUserBlk.blockNum + * sGeometry.pagesPerSublk)); + + // Update caches + cache = refreshTOCCaches(currentPage, &cacheNum); + for (i = 0; i < maxWritePages; ++i) { + cache[cacheNum++] = sInfo.latestUserBlk.usedPages + + (sInfo.latestUserBlk.blockNum + * sGeometry.pagesPerSublk) + i; + + // If we've reached the end of this TOC cache, get the next one. + if (cacheNum >= sInfo.tocEntriesPerPage) { + cache = refreshTOCCaches(currentPage + i + 1, + &cacheNum); + } + } + + // Update info data + currentPage += maxWritePages; + sInfo.latestUserBlk.usedPages += maxWritePages; + sInfo.blockStats.numValidDPages += maxWritePages; + sInfo.blockArray[sInfo.latestUserBlk.blockNum].validPagesDNo + += maxWritePages; + sStats.dataPages += maxWritePages; + + toWrite -= maxWritePages; + pBufOffset += sGeometry.bytesPerPage * maxWritePages; + } + } + + sStats.field_0 += _numPages; + sInfo.field_DC[2] += _numPages; + if (sInfo.field_DC[2] >= sGeometry.total_usable_pages / 2 + || sInfo.field_DC[3] >= sGeometry.numBlocks / 2) { + YAFTL_Flush(); + } + + nand_device_set_ftl_region(vfl->get_device(vfl), 0, 0, 0, 0); + return 0; +} + + +error_t ftl_yaftl_open(ftl_device_t *_ftl, vfl_device_t *_vfl) +{ + vfl = _vfl; + + if(YAFTL_Init()) + return EIO; + if(YAFTL_Open(0)) + return EIO; + + _ftl->block_size = sInfo.bytesPerPage; + + return 0; +} + +error_t ftl_yaftl_read_single_page(ftl_device_t *_ftl, uint32_t _page, uint8_t *_buffer) +{ +#ifdef YUSTAS_FIXME + uint32_t emf = h2fmi_get_emf(); + if((&_ftl->mtd.bdev + && (_ftl->mtd.bdev.part_mode == partitioning_gpt || _ftl->mtd.bdev.part_mode == partitioning_lwvm) + && _ftl->mtd.bdev.handle->pIdx == 1) + || emf) { + h2fmi_set_emf(1, _page); + } + else + h2fmi_set_emf(0, 0); + + error_t ret = YAFTL_Read(_page, 1, _buffer); + h2fmi_set_emf(0, 0); + return ret; +#else + return YAFTL_Read(_page, 1, _buffer); +#endif +} + +error_t ftl_yaftl_write_single_page(ftl_device_t *_ftl, uint32_t _page, uint8_t *_buffer) +{ +#ifdef YUSTAS_FIXME + uint32_t emf = h2fmi_get_emf(); + if((&_ftl->mtd.bdev + && (_ftl->mtd.bdev.part_mode == partitioning_gpt || _ftl->mtd.bdev.part_mode == partitioning_lwvm) + && _ftl->mtd.bdev.handle->pIdx == 1) + || emf) { + h2fmi_set_emf(1, _page); + } + else + h2fmi_set_emf(0, 0); + + error_t ret = YAFTL_Write(_page, 1, _buffer); + h2fmi_set_emf(0, 0); + return ret; +#else + return YAFTL_Write(_page, 1, _buffer); +#endif +} + +error_t ftl_yaftl_device_init(ftl_yaftl_device_t *_ftl) +{ +#ifdef YUSTAS_FIXME + error_t ret = ftl_init(&_ftl->ftl); + if(ret) + return ret; +#endif + _ftl->ftl.read_single_page = ftl_yaftl_read_single_page; + _ftl->ftl.write_single_page = ftl_yaftl_write_single_page; + + _ftl->ftl.open = ftl_yaftl_open; + + return 0; +} + +ftl_yaftl_device_t *ftl_yaftl_device_allocate() +{ + ftl_yaftl_device_t *ret = kmalloc(sizeof(ftl_yaftl_device_t), GFP_KERNEL); + ftl_yaftl_device_init(ret); + return ret; +} diff --git a/drivers/block/apple/yaftl/yaftl.h b/drivers/block/apple/yaftl/yaftl.h new file mode 100644 index 00000000000..7c2227426f4 --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl.h @@ -0,0 +1,14 @@ +#ifndef FTL_YAFTL_H +#define FTL_YAFTL_H + +#include "../ftl.h" +#include "../vfl.h" +#include "yaftl_common.h" + +typedef struct _ftl_yaftl_device { + ftl_device_t ftl; +} ftl_yaftl_device_t; + +ftl_yaftl_device_t *ftl_yaftl_device_allocate(void); + +#endif // FTL_YAFTL_H diff --git a/drivers/block/apple/yaftl/yaftl_common.c b/drivers/block/apple/yaftl/yaftl_common.c new file mode 100644 index 00000000000..ea11664d4a2 --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_common.c @@ -0,0 +1,679 @@ +#include "../vfl.h" +#include "yaftl_common.h" +#include "yaftl_gc.h" + +FTLStats sFTLStats; +YAFTLInfo sInfo; +YAFTLGeometry sGeometry; +YAFTLStats sStats; +uint32_t yaftl_inited = 0; +vfl_device_t* vfl = 0; + +/* Internals */ + +static void deallocBTOC(uint32_t* btoc) +{ + uint32_t i; + + for (i = 0; i != sInfo.numBtocCaches; ++i) { + if (sInfo.btocCaches[i] == btoc) { + sInfo.btocCacheMissing |= 1 << i; + return; + } + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: couldn't deallocate BTOC %p\r\n", btoc); +} + +static void setupIndexSpare(SpareData* _pSpare, uint32_t _lpn) +{ + // TODO: Load lastWrittenBlock properly (i.e. in YAFTL_Open)!!! + if (sInfo.lastWrittenBlock != sInfo.latestIndexBlk.blockNum) { + sInfo.lastWrittenBlock = sInfo.latestIndexBlk.blockNum; + ++sInfo.maxIndexUsn; + } + + _pSpare->lpn = _lpn; + _pSpare->type = PAGETYPE_INDEX; + _pSpare->usn = sInfo.maxIndexUsn; +} + +static int writeIndexPage(void* _pBuf, SpareData* _pSpare) +{ + uint32_t page; + + while (1) { + if (sInfo.latestIndexBlk.usedPages >= sGeometry.pagesPerSublk - + sInfo.tocPagesPerBlock) { + // Current index block is full, obtain a fresh one. + YAFTL_closeLatestBlock(0); + YAFTL_allocateNewBlock(0); + + if (sInfo.latestIndexBlk.usedPages != 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: writeIndexPage expected a fresh index " + "block, but %d of its pages are used\r\n", + sInfo.latestIndexBlk.usedPages); + } + } + + page = sInfo.latestIndexBlk.blockNum * sGeometry.pagesPerSublk + + sInfo.latestIndexBlk.usedPages; + + _pSpare->usn = sInfo.maxIndexUsn; + sInfo.latestIndexBlk.tocBuffer[sInfo.latestIndexBlk.usedPages] = + _pSpare->lpn; + + if (YAFTL_writePage(page, (uint8_t*)_pBuf, _pSpare) == 0) + break; + + gcListPushBack(&sInfo.gc.index.list, sInfo.latestIndexBlk.blockNum); + YAFTL_allocateNewBlock(0); + } + + ++sInfo.blockArray[sInfo.latestIndexBlk.blockNum].validPagesINo; + ++sInfo.blockStats.numValidIPages; + ++sStats.indexPages; + ++sInfo.latestIndexBlk.usedPages; + sInfo.tocArray[_pSpare->lpn].indexPage = page; + return 0; +} + +/* Externals */ + +void YAFTL_setupCleanSpare(SpareData* _spare_ptr) +{ + _spare_ptr->usn = ++sInfo.maxIndexUsn; + _spare_ptr->type = PAGETYPE_FTL_CLEAN; +} + +int YAFTL_readMultiPages(uint32_t* pagesArray, uint32_t nPages, uint8_t* dataBuffer, SpareData* metaBuffers, uint32_t disableAES, uint32_t scrub) +{ + uint32_t block, page, i, j; + int ret, succeeded, status = 0, unkn = 0; + int pagesToRead = nPages; + + if (metaBuffers == NULL) + metaBuffers = sInfo.buffer20; + + for (i = 0; pagesToRead > sGeometry.pagesPerSublk; i++) { + // TODO + //ret = VFL_ReadScatteredPagesInVb(&buf[i * sGeometry.pagesPerSublk],sGeometry.pagesPerSublk, databuffer + i * sGeometry.pagesPerSublk * sGeometry.bytesPerPage, metabuf_array, &unkn, 0, a4, &status); + ret = 0; + + if (ret) { + if (status) + return 0; + } else { + //printk(KERN_ERR "yaftl: _readMultiPages: We got read failure!!\r\n"); + succeeded = 1; + + for (j = 0; j < sGeometry.pagesPerSublk; j++) { + page = i * sGeometry.pagesPerSublk + j; + ret = YAFTL_readPage( + pagesArray[page], + &dataBuffer[page * sGeometry.bytesPerPage], + &metaBuffers[page], + disableAES, + 1, + scrub); + + if (ret) + succeeded = 0; + + status = ret; + } + + if (!succeeded) { + printk(KERN_ERR "yaftl: _readMultiPages: We weren't able to overcome read failure.\r\n"); + return 0; + } + + //printk(KERN_ERR "yaftl:_readMultiPages: We were able to overcome read failure!!\r\n"); + } + + block = pagesArray[(i + 1) * sGeometry.pagesPerSublk] / sGeometry.pagesPerSublk; + sInfo.blockArray[block].readCount++; + + pagesToRead -= sGeometry.pagesPerSublk; + } + + if (pagesToRead == 0) + return 1; + + block = pagesArray[i * sGeometry.pagesPerSublk] / sGeometry.pagesPerSublk; + sInfo.blockArray[block].readCount++; + + unkn = 0; + // ret = VFL_ReadScatteredPagesInVb(&buf[i * sGeometry.pagesPerSublk], pagesToRead % sGeometry.pagesPerSublk, databuffer + i * sGeometry.pagesPerSublk * sGeometry.bytesPerPage, metabuf_array, &unkn, 0, a4, &status); + ret = 0; // For now. --Oranav + + if (ret) + return !status; + else { + //printk(KERN_ERR "yaftl:_readMultiPages: We got read failure!!\r\n"); + succeeded = 1; + + for (j = 0; j != pagesToRead; j++) { + page = i * sGeometry.pagesPerSublk + j; + ret = YAFTL_readPage( + pagesArray[page], + &dataBuffer[page * sGeometry.bytesPerPage], + &metaBuffers[page], + disableAES, + 1, + scrub); + + if (ret) + succeeded = 0; + + status = ret; + } + + if (!succeeded) + return 0; + + //printk(KERN_ERR "yaftl:_readMultiPages: We were able to overcome read failure!!\r\n"); + } + + return 1; +} + +int YAFTL_writeMultiPages(uint32_t _block, uint32_t _start, size_t _numPages, uint8_t* _pBuf, SpareData* _pSpare, uint8_t _scrub) +{ + error_t result; + uint32_t i; + + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + for (i = 0; i < _numPages; ++i) { + result = vfl_write_single_page( + vfl, + _block * sGeometry.pagesPerSublk + _start + i, + _pBuf, + (uint8_t*)_pSpare, + _scrub); + + if (result) { + printk(KERN_ERR "YAFTL: writeMultiPages got write failure: block %d, " + "page %d, result %08x, block status %d\r\n", _block, + _start + i, result, sInfo.blockArray[_block].status); + + return 1; + } + } + + return 0; +} + +void YAFTL_allocateNewBlock(uint8_t isUserBlock) +{ + uint32_t bestBlk = 0xFFFFFFFF; + uint32_t minEraseCnt = 0xFFFFFFFF; + uint32_t currBlk; + + ++sInfo.field_DC[0]; + + if (isUserBlock) + sInfo.latestUserBlk.usn = sInfo.maxIndexUsn; + else + sInfo.latestIndexBlk.usn = sInfo.maxIndexUsn; + + for (currBlk = 0; currBlk < sGeometry.numBlocks; ++currBlk) { + uint8_t status = sInfo.blockArray[currBlk].status; + uint8_t unkn5 = sInfo.blockArray[currBlk].unkn5; + + // Skip non-free blocks -- we can't use them. + if (status != BLOCKSTATUS_FREE) + continue; + + // Erase blocks which need it. + if ((isUserBlock && unkn5 == 2) || (!isUserBlock && unkn5 == 4)) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + if (vfl_erase_single_block(vfl, currBlk, 1)) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock failed to erase block %d\r\n", + currBlk); + } + + sInfo.blockArray[currBlk].unkn5 = 0; + ++sInfo.blockArray[currBlk].eraseCount; + ++sInfo.field_DC[3]; + + if (sInfo.blockArray[currBlk].eraseCount > sInfo.maxBlockEraseCount) + sInfo.maxBlockEraseCount = sInfo.blockArray[currBlk].eraseCount; + } + + if (unkn5 != 2 && unkn5 != 5) { + // Candidate found. + if (sInfo.blockArray[currBlk].eraseCount < minEraseCnt) { + if (sInfo.blockArray[currBlk].validPagesINo) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" + " (%d) with validPagesINo != 0\r\n", currBlk); + } + + if (sInfo.blockArray[currBlk].validPagesDNo) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" + " (%d) with validPagesDNo != 0\r\n", currBlk); + } + + minEraseCnt = sInfo.blockArray[currBlk].eraseCount; + bestBlk = currBlk; + } + } + } + + if (bestBlk == 0xFFFFFFFF) + printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock is out of blocks\r\n"); + + if (isUserBlock) { + deallocBTOC(sInfo.latestUserBlk.tocBuffer); + } else { + deallocBTOC(sInfo.latestIndexBlk.tocBuffer); + } + + if (sInfo.blockArray[bestBlk].unkn5 == 1) { + // Needs erasing. + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + if (vfl_erase_single_block(vfl, bestBlk, 1)) + return; + + ++sInfo.blockArray[bestBlk].eraseCount; + ++sInfo.field_DC[3]; + sInfo.blockArray[bestBlk].unkn5 = 0; + } + + // Store the data about the new chosen block. + if (isUserBlock) { + sInfo.blockArray[sInfo.latestUserBlk.blockNum].status = + BLOCKSTATUS_ALLOCATED; + + sInfo.latestUserBlk.blockNum = bestBlk; + sInfo.blockArray[bestBlk].status = BLOCKSTATUS_GC; + sInfo.latestUserBlk.usedPages = 0; + sInfo.latestUserBlk.tocBuffer = YAFTL_allocBTOC(bestBlk); + memset(sInfo.latestUserBlk.tocBuffer, 0xFF, sInfo.tocPagesPerBlock * + sGeometry.bytesPerPage); + + --sInfo.blockStats.numAvailable; + ++sInfo.blockStats.numAllocated; + } else { + sInfo.blockArray[sInfo.latestIndexBlk.blockNum].status = + BLOCKSTATUS_I_ALLOCATED; + + sInfo.latestIndexBlk.blockNum = bestBlk; + sInfo.blockArray[bestBlk].status = BLOCKSTATUS_I_GC; + sInfo.latestIndexBlk.usedPages = 0; + sInfo.latestIndexBlk.tocBuffer = YAFTL_allocBTOC(bestBlk); + memset(sInfo.latestIndexBlk.tocBuffer, 0xFF, sInfo.tocPagesPerBlock * + sGeometry.bytesPerPage); + + --sInfo.blockStats.numIAvailable; + ++sInfo.blockStats.numIAllocated; + } + + --sInfo.blockStats.numFree; + --sStats.freeBlocks; +} + +error_t YAFTL_closeLatestBlock(uint8_t isUserBlock) +{ + SpareData* spare = sInfo.spareBuffer12; + uint32_t usedPages; + uint32_t blockNum; + uint8_t* tocBuffer; + uint8_t i; + + // FIXME: We must initialize sInfo.latest*Block.usn somewhere! + if (isUserBlock) { + spare->type = PAGETYPE_CLOSED; + spare->usn = sInfo.latestUserBlk.usn; + + usedPages = sInfo.latestUserBlk.usedPages; + blockNum = sInfo.latestUserBlk.blockNum; + tocBuffer = (uint8_t*)sInfo.latestUserBlk.tocBuffer; + } else { + spare->type = PAGETYPE_CLOSED | PAGETYPE_INDEX; + spare->usn = sInfo.latestIndexBlk.usn; + + usedPages = sInfo.latestIndexBlk.usedPages; + blockNum = sInfo.latestIndexBlk.blockNum; + tocBuffer = (uint8_t*)sInfo.latestIndexBlk.tocBuffer; + } + + if (usedPages == sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock) { + // Block is indeed full; close it. + uint32_t page = blockNum * sGeometry.pagesPerSublk + usedPages; + + for (i = 0; i < sInfo.tocPagesPerBlock; ++i) { + error_t result = YAFTL_writePage(page, + &tocBuffer[sGeometry.bytesPerPage * i], spare); + + if (result) + return EINVAL; + } + } + + return 0; +} + +uint32_t* YAFTL_allocBTOC(uint32_t _block) +{ + int32_t minUsn = INT_MAX; + int32_t found = -1; + uint32_t i; + + sInfo.unkB0_1 = (sInfo.unkB0_1 + 1) % sInfo.unkAC_2; + sInfo.unkB4_buffer[sInfo.unkB0_1] = _block; + memset(sInfo.unkB8_buffer[sInfo.unkB0_1], 0xFF, + sGeometry.pagesPerSublk * sizeof(uint32_t)); + + sInfo.btocCurrUsn++; + + for (i = 0; i < sInfo.numBtocCaches; i++) { + if ((sInfo.unk80_3 & (1< maxUsn) { + cache = sInfo.btocCaches[i]; + maxUsn = sInfo.btocCacheUsn[i]; + } + } + + return cache; +} + +error_t YAFTL_readBTOCPages(uint32_t offset, uint32_t *data, SpareData *spare, uint8_t disable_aes, uint8_t scrub, uint32_t max) +{ + uint32_t i, j, result; + uint8_t *rawBuff = (uint8_t*)data; + + // Read all the BTOC (block table of contents) pages. + for (i = 0; i < sInfo.tocPagesPerBlock; i++) { + result = YAFTL_readPage(offset + i, + &rawBuff[i * sGeometry.bytesPerPage], + spare, disable_aes, 1, scrub); + + if (result) + return result; + } + + // Validate the data. + for (j = 0; j < (sInfo.tocPagesPerBlock * sGeometry.bytesPerPage) / sizeof(uint32_t); j++) { + if (data[j] >= max) + data[j] = 0xFFFFFFFF; + } + + return 0; +} + +error_t YAFTL_readPage(uint32_t _page, uint8_t* _data_ptr, SpareData* _spare_ptr, uint32_t _disable_aes, uint32_t _empty_ok, uint32_t _scrub) +{ + error_t result; + int refreshPage = 0; + uint32_t block = _page / sGeometry.pagesPerSublk; + + uint8_t* meta_ptr = (uint8_t*)(_spare_ptr ? _spare_ptr : sInfo.spareBuffer18); + + result = vfl_read_single_page(vfl, _page, _data_ptr, meta_ptr, _empty_ok, + &refreshPage, _disable_aes); + + if (result) { + printk(KERN_ERR "YAFTL_readPage: We got read failure: page %d, block %d, block status %d, scrub %d.\r\n", + _page, + block, + sInfo.blockArray[block].status, + _scrub); + + return ERROR_ARG; + } + + sInfo.blockArray[block].readCount++; + + if (sInfo.blockArray[block].readCount > REFRESH_TRIGGER) { + sInfo.refreshNeeded = 1; + + if (!refreshPage) + return result; + } else { + if (!refreshPage) + return result; + + sInfo.refreshNeeded = 1; + } + + printk(KERN_ERR "YAFTL: refresh triggered at page %d\r\n", _page); + ++sStats.refreshes; + sInfo.blockArray[block].readCount = REFRESH_TRIGGER + 1; + + return result; +} + +uint32_t YAFTL_writePage(uint32_t _page, uint8_t* _data_ptr, SpareData* _spare_ptr) +{ + error_t result; + uint32_t block = _page / sGeometry.pagesPerSublk; + + if (sInfo.field_78) + { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + result = vfl_write_single_page(vfl, _page, _data_ptr, (uint8_t*)_spare_ptr, 1); + + if (result) { + printk(KERN_ERR "YAFTL_writePage: We got write failure: page %d, block %d, block status %d.\r\n", + _page, + block, + sInfo.blockArray[block].status); + + return ERROR_ARG; + } + + return 0; +} + +error_t YAFTL_writeIndexTOC() +{ + int result; + + printk(KERN_ERR "WARNING - You NEED to call ftl_flush before rebooting. - WARNING\r\n"); + + YAFTL_setupCleanSpare(sInfo.spareBuffer7); + + // Try to write the new index TOC. + result = vfl_write_single_page(vfl, + sInfo.ctrlBlockPageOffset + sInfo.ftlCxtPage, + (uint8_t*)sInfo.latestIndexBlk.tocBuffer, + (uint8_t*)sInfo.spareBuffer7, 1); + + if (result) { + // Must erase: first, erase the current FTL ctrl block. + vfl_erase_single_block(vfl, sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex], 1); + sInfo.blockArray[sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]].eraseCount++; + sInfo.totalEraseCount++; + sInfo.blockArray[sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]].status = BLOCKSTATUS_FTLCTRL; + + // Select the new FTL ctrl block. + ++sInfo.selCtrlBlockIndex; + if (sInfo.selCtrlBlockIndex > 2) + sInfo.selCtrlBlockIndex = 0; + + // Erase it. + sInfo.blockArray[sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]].status = BLOCKSTATUS_FTLCTRL_SEL; + vfl_erase_single_block(vfl, sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex], 1); + sInfo.blockArray[sInfo.FTLCtrlBlock[sInfo.selCtrlBlockIndex]].eraseCount++; + sInfo.totalEraseCount++; + sInfo.ftlCxtPage = 0xFFFFFFFF; + } + + return 0; +} + +uint32_t YAFTL_findFreeTOCCache() +{ + uint32_t i; + for (i = 0; i < sInfo.numCaches; i++) { + if (sInfo.tocCaches[i].state == CACHESTATE_FREE) + return i; + } + + return 0xFFFF; +} + +uint32_t YAFTL_clearEntryInCache(uint16_t _cacheIdx) +{ + typedef struct { + uint16_t useCount; + uint16_t idx; + } bestcache_t; + + bestcache_t bestDirty, bestClean; + uint32_t i; + + if (_cacheIdx == 0xFFFF) { + // Any cache is okay. + bestDirty.useCount = 0xFFFF; + bestDirty.idx = 0xFFFF; + bestClean.useCount = 0xFFFF; + bestClean.idx = 0xFFFF; + + for (i = 0; i < sInfo.numCaches; ++i) { + TOCCache* cache = &sInfo.tocCaches[i]; + + if (cache->state == CACHESTATE_FREE) { + return i; + } else if (cache->state == CACHESTATE_DIRTY + && cache->useCount < bestDirty.useCount) { + bestDirty.useCount = cache->useCount; + bestDirty.idx = i; + } else if (cache->state == CACHESTATE_CLEAN + && cache->useCount < bestClean.useCount) { + bestClean.useCount = cache->useCount; + bestClean.idx = i; + } + } + } else { + TOCCache* cache = &sInfo.tocCaches[_cacheIdx]; + bestClean.idx = 0xFFFF; + bestDirty.idx = 0xFFFF; + if (cache->state == CACHESTATE_FREE) { + return _cacheIdx; + } else if (cache->state == CACHESTATE_CLEAN) { + if (cache->useCount == 0xFFFF) + bestClean.idx = 0xFFFF; + else + bestClean.idx = _cacheIdx; + } else if (cache->state == CACHESTATE_DIRTY) { + if (cache->useCount == 0xFFFF) + bestDirty.idx = 0xFFFF; + else + bestDirty.idx = _cacheIdx; + } + } + + if (bestClean.idx != 0xFFFF) { + // Clear this clean cache. + sInfo.tocArray[sInfo.tocCaches[bestClean.idx].page].cacheNum = 0xFFFF; + memset(sInfo.tocCaches[bestClean.idx].buffer, 0xFF, + sGeometry.bytesPerPage); + + sInfo.tocCaches[bestClean.idx].state = CACHESTATE_FREE; + sInfo.tocCaches[bestClean.idx].useCount = 0; + ++sInfo.numFreeCaches; + return bestClean.idx; + } + + if (bestDirty.idx != 0xFFFF) { + // Oh well. Let's clear this dirty cache. + SpareData* pSpare = sInfo.spareBuffer11; + uint32_t oldPage = sInfo.tocCaches[bestDirty.idx].page; + uint32_t oldIndexPage = sInfo.tocArray[oldPage].indexPage; + error_t result; + + sInfo.tocArray[oldPage].cacheNum = 0xFFFF; + setupIndexSpare(pSpare, oldPage); + + if (oldIndexPage != 0xFFFFFFFF) { + // Invalidate the index page. + uint32_t oldIndexBlock = oldIndexPage / sGeometry.pagesPerSublk; + + if (sInfo.blockArray[oldIndexBlock].validPagesINo == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: clearEntryInCache tried to invalidate an " + "index page in block %d, but it has no index pages\r\n", + oldIndexBlock); + } + + --sInfo.blockArray[oldIndexBlock].validPagesINo; + --sInfo.blockStats.numValidIPages; + --sStats.indexPages; + } + + // Write the dirty index page. + result = writeIndexPage(sInfo.tocCaches[bestDirty.idx].buffer, + pSpare); + + if (result) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: clearEntryInCache failed to write dirty index" + " page in %p\r\n", sInfo.tocCaches[bestDirty.idx].buffer); + } + + sInfo.tocArray[oldPage].cacheNum = 0xFFFF; + sInfo.tocCaches[bestDirty.idx].state = CACHESTATE_FREE; + sInfo.tocCaches[bestDirty.idx].useCount = 0; + memset(sInfo.tocCaches[bestDirty.idx].buffer, 0xFF, + sGeometry.bytesPerPage); + + ++sInfo.numFreeCaches; + return bestDirty.idx; + } + + return 0xFFFF; +} + +void YAFTL_copyBTOC(uint32_t *dest, uint32_t *src, uint32_t maxVal) +{ + uint32_t i; + uint32_t val; + + for (i = 0; i < (sInfo.tocPagesPerBlock * sGeometry.bytesPerPage) / sizeof(uint32_t); i++) { + val = src[i]; + if (val >= maxVal) + val = 0xFFFFFFFF; + + dest[i] = val; + } +} diff --git a/drivers/block/apple/yaftl/yaftl_common.h b/drivers/block/apple/yaftl/yaftl_common.h new file mode 100644 index 00000000000..399d472839a --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_common.h @@ -0,0 +1,340 @@ +#ifndef FTL_YAFTL_COMMON_H +#define FTL_YAFTL_COMMON_H + +#include "../vfl.h" +#include "yaftl_mem.h" + +/* Macros */ + +// YAFTL errors +#define ERROR_ARG 0x80000001 +#define ERROR_NAND 0x80000002 +#define ERROR_EMPTY 0x80000003 + +// Page types (as defined in the spare data "type" bitfield) +#define PAGETYPE_INDEX (0x4) // Index block indicator +#define PAGETYPE_CLOSED (0x8) // Closed (full) block +#define PAGETYPE_LBN (0x10) // User data (also called lbn: maybe logical block number? lBlock 0 is system and lBlock 1 is user?) +#define PAGETYPE_FTL_CLEAN (0x20) // FTL context (unmounted, clean) +#define PAGETYPE_MAGIC (0x40) +#define PAGETYPE_VFL (0x80) // VFL context + +// Block status (as defined in the BlockStruct structure) +#define BLOCKSTATUS_ALLOCATED (0x1) +#define BLOCKSTATUS_FTLCTRL (0x2) +#define BLOCKSTATUS_GC (0x4) +#define BLOCKSTATUS_CURRENT (0x8) +#define BLOCKSTATUS_FTLCTRL_SEL (0x10) +#define BLOCKSTATUS_I_GC (0x20) +#define BLOCKSTATUS_I_ALLOCATED (0x40) +#define BLOCKSTATUS_I_CURRENT (0x80) +#define BLOCKSTATUS_FREE (0xFF) + +// Cache states +#define CACHESTATE_DIRTY (0x1) +#define CACHESTATE_CLEAN (0x2) +#define CACHESTATE_FREE (0xFFFF) + +// Size of GCList +#define GCLIST_MAX (16) + +// Number of read counts which would trigger a refresh +#define REFRESH_TRIGGER (10000) + +/* Types */ + +typedef struct { + uint32_t lpn; // Logical page number + uint32_t usn; // Update sequence number + uint8_t field_8; + uint8_t type; // Page type + uint16_t field_A; +} __attribute__((packed)) SpareData; + +typedef struct { + uint32_t eraseCount; + uint16_t validPagesDNo; // Data validity counter + uint16_t validPagesINo; // Index validity counter + uint16_t readCount; // Number of reads + uint8_t status; // Block status + uint8_t unkn5; +} BlockStruct; + +typedef struct { + uint32_t indexPage; + uint16_t cacheNum; + uint16_t TOCUnkMember2; +} __attribute__((packed)) TOCStruct; + +typedef struct { + uint32_t* buffer; + uint32_t page; + uint16_t useCount; + uint16_t state; +} __attribute__((packed)) TOCCache; + +typedef struct { + uint32_t numAllocated; // 00 + uint32_t numAvailable; // 04 + uint32_t numValidDPages; // 08 + uint32_t numIAllocated; // 0C + uint32_t numIAvailable; // 10 + uint32_t numValidIPages; // 14 + uint32_t numFree; // 18 + uint32_t field_1C; // 1C +} __attribute__((packed)) BlockStats; + +typedef struct { + uint32_t blockNum; + uint32_t* tocBuffer; + uint32_t usedPages; + uint16_t field_A; + uint32_t usn; +} BlockToUse; + +typedef struct _L2VNode { + struct _L2VNode* next; +} L2VNode; + +typedef struct { + uint32_t block[GCLIST_MAX + 1]; + uint32_t head; + uint32_t tail; +} GCList; + +typedef struct { + uint32_t pageIndex; + uint32_t field_4; + uint32_t vpn; + uint32_t span; + L2VNode* node; + uint32_t field_14; + uint32_t next_nOfs; + uint32_t nodeSize; + uint32_t field_20; + L2VNode* field_24; +} GCReadC; + +typedef struct { + uint32_t victim; + GCList list; + uint32_t chosenBlock; + uint32_t totalValidPages; + uint32_t eraseCount; + uint32_t* btoc; + uint8_t* pageBuffer1; + uint32_t* zone; + uint8_t* pageBuffer2; + SpareData* spareArray; + uint32_t curZoneSize; + uint32_t uECC; + uint32_t state; + uint32_t btocIdx; + uint32_t dataPagesPerSublk; + uint32_t numInvalidatedPages; + GCReadC read_c; +} GCData; + +typedef struct { + GCData data; + GCData index; +} GC; + +typedef struct { + bufzone_t zone; + bufzone_t segment_info_temp; + uint16_t tocPagesPerBlock; // 38 + uint16_t tocEntriesPerPage; // 3A + uint32_t numIBlocks; // 3C + uint32_t unknCalculatedValue1; // 40 + uint32_t totalPages; // 44 + uint16_t tocArrayLength; // 48 + uint16_t numFreeCaches; // 4A + uint16_t field_5C; + uint64_t ftlCxtUsn; + uint32_t selCtrlBlockIndex; + uint32_t field_6C; + uint8_t totalEraseCount; + uint32_t refreshNeeded; + BlockToUse latestUserBlk; + BlockToUse latestIndexBlk; + uint32_t maxIndexUsn; // 6C + uint32_t lastWrittenBlock; + uint8_t field_78; // Heh, wtf? + uint8_t field_79; + uint32_t numBtocCaches; + int32_t btocCurrUsn; + uint32_t btocCacheMissing; // A bitfield + uint32_t unk80_3; + uint32_t** btocCaches; + int32_t* btocCacheBlocks; + int32_t* btocCacheUsn; + bufzone_t btocCacheBufzone; + uint32_t unkAC_2; + uint32_t unkB0_1; + uint32_t* unkB4_buffer; + uint8_t** unkB8_buffer; + BlockStats blockStats; // BC + uint32_t field_DC[4]; // DC + TOCStruct* tocArray; // EC + TOCCache* tocCaches; // F0 + BlockStruct* blockArray; // F4 + uint32_t* pageBuffer2; + SpareData* spareBuffer3; + SpareData* spareBuffer4; + SpareData* spareBuffer5; + SpareData* spareBuffer6; + SpareData* spareBuffer7; + SpareData* spareBuffer8; + SpareData* spareBuffer9; + SpareData* spareBuffer10; + SpareData* spareBuffer11; + SpareData* spareBuffer12; + SpareData* spareBuffer13; + SpareData* spareBuffer14; + SpareData* spareBuffer15; + SpareData* spareBuffer16; + SpareData* spareBuffer17; + uint8_t* pageBuffer; + uint32_t* tocPageBuffer; + uint32_t lastTOCPageRead; + SpareData* spareBuffer19; + uint32_t* unknBuffer3_ftl; // 148 + SpareData* buffer20; + SpareData* spareBuffer18; + uint32_t ctrlBlockPageOffset; + uint32_t nPagesTocPageIndices; + uint32_t nPagesBlockStatuses; + uint32_t nPagesBlockReadCounts; + uint32_t nPagesBlockEraseCounts; + uint32_t nPagesBlockValidPagesDNumbers; + uint32_t nPagesBlockValidPagesINumbers; + uint32_t ftlCxtPage; + uint16_t FTLCtrlBlock[3]; + uint16_t unkn_1; + uint32_t maxBlockEraseCount; + uint32_t minBlockEraseCount; + uint32_t numCaches; + uint8_t unk188_0x63; + uint32_t pagesAvailable; + uint32_t bytesPerPage; + uint32_t gcPages; + GC gc; + bufzone_t gcBufZone; + SpareData* gcSpareBuffer; + GCReadC readc; +} YAFTLInfo; + +typedef struct { + uint16_t pagesPerSublk; + uint16_t numBlocks; + uint16_t bytesPerPage; + uint16_t spareDataSize; + uint16_t total_banks_ftl; + uint32_t total_usable_pages; +} YAFTLGeometry; + +typedef struct { + char version[4]; // 0 + uint32_t numIBlocks; // 4 + uint32_t totalPages; // 8 + uint32_t latestUserBlk; // C + uint32_t maxIndexUsn; // 10 + uint32_t latestIndexBlk; // 14 + uint32_t maxIndexUsn2; // 18 + uint32_t numAvailableBlocks; // 1C + uint32_t numIAvailableBlocks; // 20 + uint32_t numAllocatedBlocks; // 24 + uint32_t numIAllocatedBlocks; // 28 + uint32_t numCaches; // 2C + uint32_t field_30; // 30 + uint32_t cxt_unkn1[10]; // placeholder + uint16_t tocArrayLength; // 5C + uint16_t tocPagesPerBlock; // 5E + uint16_t tocEntriesPerPage; // 60 + uint16_t numFreeCaches; // 62 + uint16_t field_64; // 64 + uint16_t pagesUsedInLatestUserBlk; // 66 + uint16_t pagesUsedInLatestIdxBlk; // 68 + uint32_t cxt_unkn2[10]; // placeholder + uint16_t field_92; // 92 + uint8_t unk188_0x63; // 94 + uint8_t totalEraseCount; // 95 +} __attribute__((packed)) YAFTLCxt; + +typedef struct { + uint64_t field_0; + uint64_t pagesRead; + uint64_t writes; + uint64_t reads; + uint64_t field_20; + uint64_t freeIndexOps; // Number of times gcFreeIndexPages was called + uint64_t field_30; + uint64_t field_38; + uint64_t dataPages; + uint64_t indexPages; + uint64_t freeBlocks; + uint64_t field_58; + uint64_t blocksRefreshed; + uint64_t wearLevels; + uint64_t flushes; + uint64_t refreshes; +} __attribute__((packed)) YAFTLStats; + +typedef struct { + uint8_t idx[0x1C0]; +} __attribute__((packed)) FTLStats; + +typedef struct _BlockListNode { + uint32_t usn; + uint16_t blockNumber; + uint16_t field_6; + struct _BlockListNode *next; + struct _BlockListNode *prev; +} __attribute__((packed)) BlockListNode; + +typedef struct { + uint32_t lpnMin; + uint32_t lpnMax; +} __attribute__((packed)) BlockLpn; + +/* Externs */ + +extern FTLStats sFTLStats; +extern YAFTLInfo sInfo; +extern YAFTLGeometry sGeometry; +extern YAFTLStats sStats; +extern uint32_t yaftl_inited; +extern vfl_device_t* vfl; + +/* Functions */ + +void YAFTL_setupCleanSpare(SpareData* _spare_ptr); + +int YAFTL_readMultiPages(uint32_t* pagesArray, uint32_t nPages, uint8_t* dataBuffer, SpareData* metaBuffers, uint32_t disableAES, uint32_t scrub); + +int YAFTL_writeMultiPages(uint32_t _block, uint32_t _start, size_t _numPages, uint8_t* _pBuf, SpareData* _pSpare, uint8_t _scrub); + +void YAFTL_allocateNewBlock(uint8_t isUserBlock); + +error_t YAFTL_closeLatestBlock(uint8_t isUserBlock); + +uint32_t* YAFTL_allocBTOC(uint32_t _arg0); + +uint32_t* YAFTL_getBTOC(uint32_t _block); + +error_t YAFTL_readBTOCPages(uint32_t offset, uint32_t *data, SpareData *spare, uint8_t disable_aes, uint8_t scrub, uint32_t max); + +error_t YAFTL_readPage(uint32_t _page, uint8_t* _data_ptr, SpareData* _spare_ptr, uint32_t _disable_aes, uint32_t _empty_ok, uint32_t _scrub); + +uint32_t YAFTL_writePage(uint32_t _page, uint8_t* _data_ptr, SpareData* _spare_ptr); + +error_t YAFTL_writeIndexTOC(void); + +uint32_t YAFTL_findFreeTOCCache(void); + +uint32_t YAFTL_clearEntryInCache(uint16_t _cacheIdx); + +void YAFTL_copyBTOC(uint32_t* dest, uint32_t* src, uint32_t maxVal); + +#endif // FTL_YAFTL_COMMON_H diff --git a/drivers/block/apple/yaftl/yaftl_gc.c b/drivers/block/apple/yaftl/yaftl_gc.c new file mode 100644 index 00000000000..383a3851822 --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_gc.c @@ -0,0 +1,1108 @@ +#include "yaftl.h" +#include "yaftl_gc.h" +#include "l2v.h" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Internals */ + +static void gcPopulateBTOC(GCData* _data, uint8_t _scrub, uint32_t _max) +{ + SpareData* pSpare = sInfo.gcSpareBuffer; + uint32_t* btocCache; + uint32_t page; + error_t result; + + _data->dataPagesPerSublk = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + + if ((btocCache = YAFTL_getBTOC(_data->chosenBlock)) != NULL) { + // BTOC is cached, use it. + YAFTL_copyBTOC(_data->btoc, btocCache, _max); + return; + } + + // Read the BTOC from the end of the block. + page = _data->chosenBlock * sGeometry.pagesPerSublk + + sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + + result = YAFTL_readBTOCPages(page, _data->btoc, pSpare, 0, + _scrub, _max); + + if (result || !(pSpare->type & PAGETYPE_CLOSED)) { + // Must read each page individually. + uint32_t i; + + for (i = 0; i < sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; ++i) { + page = _data->chosenBlock * sGeometry.pagesPerSublk + i; + result = YAFTL_readPage(page, _data->pageBuffer1, pSpare, 0, + 1, _scrub); + + if (!result && pSpare->lpn < _max) + _data->btoc[i] = pSpare->lpn; + } + } +} + +static uint32_t gcListPopFront(GCList* _list) +{ + uint32_t block; + + if (_list->head == _list->tail) + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcListPopFront was called but list is empty\r\n"); + + block = _list->block[_list->head++]; + if (_list->head > GCLIST_MAX) + _list->head = 0; + + return block; +} + +static int gcFillIndex(uint32_t _lpn, uint32_t* _pBuf) +{ + uint32_t basePage = sInfo.tocEntriesPerPage * _lpn; + uint32_t i = 0, j = 0; + + sInfo.gc.index.read_c.span = 0; + + while (i < sInfo.tocEntriesPerPage) { + uint32_t vpn; + uint32_t count; + + if (sInfo.gc.index.read_c.span == 0) { + sInfo.gc.index.read_c.pageIndex = basePage + i; + L2V_Search(&sInfo.gc.index.read_c); + + if (sInfo.gc.index.read_c.span == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFillIndex -- no such page %d\r\n", + basePage + i); + } + } + + vpn = sInfo.gc.index.read_c.vpn; + count = MIN(sInfo.tocEntriesPerPage - i, sInfo.gc.index.read_c.span); + + if (vpn == L2V_VPN_MISS) { + return 0; + } else if (vpn == L2V_VPN_SPECIAL) { + for (j = 0; j < count; ++j) + _pBuf[i + j] = 0xFFFFFFFF; + } else if (L2V_VPN_ISNORMAL(vpn)) { + for (j = 0; j < count; ++j) + _pBuf[i + j] = vpn++; + + sInfo.gc.index.read_c.vpn += count; + } else { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFillIndex got an invalid vpn %x\r\n", vpn); + } + + i += count; + sInfo.gc.index.read_c.span -= count; + } + + return 1; +} + +static error_t gcReadZoneData(GCData* _data, uint8_t _isIndex, uint8_t _scrub) +{ + uint32_t* zone; + SpareData* spareArray; + uint8_t* pageBuffer; + uint32_t i; + + if (_isIndex) { + uint8_t readNeeded = 0; + + zone = sInfo.gc.index.zone; + spareArray = sInfo.gc.index.spareArray; + pageBuffer = sInfo.gc.index.pageBuffer2; + + for (i = 0; i < _data->curZoneSize; ++i) { + uint32_t entry = _data->btoc[zone[i] % sGeometry.pagesPerSublk]; + spareArray[i].lpn = entry; + if (entry == 0xFFFFFFFF) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcReadZoneData read an invalid LPN " + "%d\r\n", entry); + } + + if (!gcFillIndex(entry, + (uint32_t*)&pageBuffer[i * sGeometry.bytesPerPage])) { + readNeeded = 1; + break; + } + } + + if (!readNeeded) + return 0; + } else { + zone = sInfo.gc.data.zone; + spareArray = sInfo.gc.data.spareArray; + pageBuffer = sInfo.gc.data.pageBuffer2; + } + + if (YAFTL_readMultiPages(zone, _data->curZoneSize, pageBuffer, spareArray, + !_isIndex, _scrub) != 1) { + // TODO: Don't panic, manually read the pages. + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcReadZoneData couldn't read multi pages\r\n"); + } + + return 0; +} + +static int gcHandleVpnMiss(GCData* _data, uint8_t _scrub) +{ + uint32_t btocEntry = _data->btoc[_data->btocIdx]; + uint32_t indexPageNo = btocEntry / sInfo.tocEntriesPerPage; + uint32_t page; + uint32_t cache; + SpareData* pSpare = sInfo.gcSpareBuffer; + + if (indexPageNo >= sInfo.tocArrayLength) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss got an out-of-range index page " + "%d\r\n", indexPageNo); + } + + page = sInfo.tocArray[indexPageNo].indexPage; + cache = sInfo.tocArray[indexPageNo].cacheNum; + + if (cache == 0xFFFF) { + error_t status; + + if (page == 0xFFFFFFFF) { + return -2; + } + + // There is an index page, but it's not cached. Obtain it. + cache = YAFTL_findFreeTOCCache(); + if (cache == 0xFFFF) { + cache = YAFTL_clearEntryInCache(0xFFFF); + if (cache == 0xFFFF) { + _data->state = 3; + return -1; + } + } + + status = YAFTL_readPage(page, (uint8_t*)sInfo.tocCaches[cache].buffer, + pSpare, 0, 1, _scrub); + + if (status != 0) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 1; + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss Index UECC page 0x%08x status" + " %08x\r\n", page, status); + } else { + if (!(pSpare->type & PAGETYPE_INDEX)) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 1; + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss Invalid index metadata " + "0x%02x\r\n", pSpare->type); + } else { + --sInfo.numFreeCaches; + sInfo.tocCaches[cache].state = CACHESTATE_CLEAN; + sInfo.tocCaches[cache].useCount = 1; + sInfo.tocCaches[cache].page = indexPageNo; + sInfo.tocArray[indexPageNo].cacheNum = cache; + } + } + } + + return sInfo.tocCaches[cache].buffer[btocEntry % sInfo.tocEntriesPerPage]; +} + +static void gcChooseBlock(GCData* _data, uint8_t _filter) +{ + uint32_t block = _data->victim; + uint32_t i; + uint32_t best = 0xFFFFFFFF; + uint32_t bestValid = 0xFFFFFFFF; + uint32_t bestErases = 0xFFFFFFFF; + + if (block != 0xFFFFFFFF) { + if (sInfo.blockArray[block].status == BLOCKSTATUS_I_GC) + YAFTL_allocateNewBlock(0); + + if (sInfo.blockArray[block].status == BLOCKSTATUS_GC) + YAFTL_allocateNewBlock(1); + } + + if (_data->list.head != _data->list.tail) { + if (block != 0xFFFFFFFF) + gcListPushBack(&_data->list, block); + + block = gcListPopFront(&_data->list); + _data->victim = block; + } + + if (block != 0xFFFFFFFF) { + uint8_t status = sInfo.blockArray[block].status; + + _data->chosenBlock = block; + _data->totalValidPages = sInfo.blockArray[block].validPagesDNo + + sInfo.blockArray[block].validPagesINo; + _data->eraseCount = sInfo.blockArray[block].eraseCount; + + if (status != _filter) { + if (_filter == BLOCKSTATUS_ALLOCATED + && status != BLOCKSTATUS_CURRENT + && status != BLOCKSTATUS_GC) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock chose a block which doesn't" + " match the filter -- status %02x\r\n", status); + } else if (_filter == BLOCKSTATUS_I_ALLOCATED + && status != BLOCKSTATUS_I_CURRENT + && status != BLOCKSTATUS_I_GC) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock chose an index block which " + "doesn't match the filter -- status %02x\r\n", status); + } + } + + _data->victim = 0xFFFFFFFF; + return; + } + + for (i = 0; i < sGeometry.numBlocks; ++i) { + BlockStruct* blk = &sInfo.blockArray[i]; + uint32_t valid = blk->validPagesDNo + blk->validPagesINo; + + if (blk->status == _filter && (valid < bestValid || (valid == bestValid + && blk->eraseCount < bestErases))) { + best = i; + bestValid = valid; + bestErases = blk->eraseCount; + } + } + + if (best == 0xFFFFFFFF || bestValid == 0xFFFFFFFF + || bestErases == 0xFFFFFFFF) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock couldn't find a block\r\n"); + } + + _data->chosenBlock = best; + _data->totalValidPages = bestValid; + _data->eraseCount = bestErases; +} + +static void gcSanityCheckValid(GCData* _data) +{ + BlockStruct* blk = &sInfo.blockArray[_data->chosenBlock]; + + if (blk->validPagesINo + blk->validPagesDNo > 0) { + printk(KERN_ERR "YAFTL: something could be wrong with GC, because we " + "have %d valid index and %d valid data\r\n", + blk->validPagesINo, blk->validPagesDNo); + + if (_data->uECC < blk->validPagesINo + blk->validPagesDNo) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: non-zero validity counter; block %d, " + "uECC %d\r\n", _data->chosenBlock, _data->uECC); + } else { + printk(KERN_ERR "YAFTL: %d uECCs caused it. fixing up counters\r\n", + _data->uECC); + blk->validPagesINo = 0; + blk->validPagesDNo = 0; + } + } +} + +static void setupDataSpares(SpareData* _pSpare, size_t _count) +{ + uint32_t i; + + if (sInfo.lastWrittenBlock != sInfo.latestIndexBlk.blockNum) { + sInfo.lastWrittenBlock = sInfo.latestIndexBlk.blockNum; + ++sInfo.maxIndexUsn; + } + + for (i = 0; i < _count; ++i) { + _pSpare[i].usn = sInfo.maxIndexUsn; + _pSpare[i].type = (_pSpare[i].type & PAGETYPE_MAGIC) | PAGETYPE_LBN; + } +} + +static void sub_80606960(uint32_t _lpn, uint32_t _vpn) +{ + uint32_t i; + + for (i = 0; i < sInfo.unkAC_2; ++i) { + if (sInfo.unkB4_buffer[i] == _lpn / sGeometry.pagesPerSublk) + sInfo.unkB8_buffer[i][_lpn % sGeometry.pagesPerSublk] = _vpn; + } +} + +static int gcWriteDataPages(GCData* _data, uint8_t _scrub) +{ + uint32_t numPages = _data->curZoneSize; + uint32_t page = 0; + uint32_t i; + error_t status; + SpareData* spare = sInfo.gcSpareBuffer; + + // Write each page. + while (numPages != 0) { + uint32_t userPages = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + + if (sInfo.latestUserBlk.usedPages < userPages) { + // There's still room in our current block. Write in it. + uint32_t pagesToWrite = MIN(numPages, + userPages - sInfo.latestUserBlk.usedPages); + + setupDataSpares(&sInfo.gc.data.spareArray[page], pagesToWrite); + + // Write the pages. + if (YAFTL_writeMultiPages( + sInfo.latestUserBlk.blockNum, + sInfo.latestUserBlk.usedPages, + pagesToWrite, + &sInfo.gc.data.pageBuffer2[page * sGeometry.bytesPerPage], + &sInfo.gc.data.spareArray[page], + 1)) + { + gcListPushBack(&_data->list, _data->chosenBlock); + gcListPushBack(&_data->list, sInfo.latestUserBlk.blockNum); + YAFTL_allocateNewBlock(1); + return ERROR_ARG; + } + + // Update caches. + for (i = 0; i < pagesToWrite; ++i) { + uint32_t offsetInBlk = sInfo.latestUserBlk.usedPages + i; + + sInfo.latestUserBlk.tocBuffer[offsetInBlk] = + sInfo.gc.data.spareArray[page + i].lpn; + sub_80606960(sInfo.latestUserBlk.blockNum + * sGeometry.pagesPerSublk + offsetInBlk, + sInfo.gc.data.zone[page + i]); + sInfo.gc.data.zone[page + i] = sInfo.latestUserBlk.blockNum + * sGeometry.pagesPerSublk + offsetInBlk; + } + + sInfo.latestUserBlk.usedPages += pagesToWrite; + page += pagesToWrite; + numPages -= pagesToWrite; + if (numPages == 0) + break; + } + + if (sInfo.latestUserBlk.usedPages >= userPages) { + // Block is filled up. Get a new one. + YAFTL_closeLatestBlock(1); + YAFTL_allocateNewBlock(1); + } + } + + // Update the TOC cache for each page. + for (i = 0; i < _data->curZoneSize; ++i) + { + uint32_t indexPageNo; + TOCStruct* toc; + uint32_t cache; + uint32_t tocEntry; + uint32_t vpn; + + L2V_Update(sInfo.gc.data.spareArray[i].lpn, 1, sInfo.gc.data.zone[i]); + + indexPageNo = sInfo.gc.data.spareArray[i].lpn / sInfo.tocEntriesPerPage; + tocEntry = sInfo.gc.data.spareArray[i].lpn % sInfo.tocEntriesPerPage; + toc = &sInfo.tocArray[indexPageNo]; + cache = toc->cacheNum; + + if (toc->indexPage == 0xFFFFFFFF && cache == 0xFFFF) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteDataPages failed to find TOC %d\r\n", + indexPageNo); + } + + if (cache == 0xFFFF) { + // Need to find a new cache. + cache = YAFTL_findFreeTOCCache(); + + if (cache == 0xFFFF) + cache = YAFTL_clearEntryInCache(0xFFFF); + + if (cache == 0xFFFF) + printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to find a TOC cache\r\n"); + + status = YAFTL_readPage( + toc->indexPage, (uint8_t*)sInfo.tocCaches[cache].buffer, spare, + 0, 1, _scrub); + + if (status) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: uecc toc page 0x%08x status 0x%08x\r\n", + toc->indexPage, status); + return status; + } + + --sInfo.numFreeCaches; + sInfo.tocCaches[cache].useCount = 0; + } + + vpn = sInfo.tocCaches[cache].buffer[tocEntry]; + + if (vpn != 0xFFFFFFFF) { + uint32_t blk = vpn / sGeometry.pagesPerSublk; + + if (sInfo.blockArray[blk].validPagesDNo == 0) { + if (sInfo.field_78) { + YAFTL_writeIndexTOC(); + sInfo.field_78 = 0; + } + + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteDataPages tried to move a page from" + " a block with no valid data pages %d\r\n", blk); + } + + --sInfo.blockArray[blk].validPagesDNo; + --sInfo.blockStats.numValidDPages; + --sStats.dataPages; + } + + sInfo.tocCaches[cache].buffer[tocEntry] = sInfo.gc.data.zone[i]; + ++sInfo.blockArray[sInfo.gc.data.zone[i] / sGeometry.pagesPerSublk] + .validPagesDNo; + ++sInfo.blockStats.numValidDPages; + ++sStats.dataPages; + sInfo.tocCaches[cache].page = indexPageNo; + toc->cacheNum = cache; + + if (toc->indexPage != 0xFFFFFFFF) { + --sInfo.blockArray[toc->indexPage / sGeometry.pagesPerSublk] + .validPagesINo; + --sInfo.blockStats.numValidIPages; + --sStats.indexPages; + toc->indexPage = 0xFFFFFFFF; + } + + sInfo.tocCaches[cache].state = CACHESTATE_DIRTY; + ++sInfo.tocCaches[cache].useCount; + } + + return 0; +} + +static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) +{ + while (1) { + if (sInfo.gc.data.state == 0) { + while (sInfo.blockStats.numIAvailable <= 1) + gcFreeIndexPages(0xFFFFFFFF, _scrub); + + ++sStats.field_20; + gcChooseBlock(&sInfo.gc.data, BLOCKSTATUS_ALLOCATED); + if (sInfo.gc.data.totalValidPages != 0) { + sInfo.gc.data.btocIdx = 0; + sInfo.gc.data.numInvalidatedPages = 0; + gcPopulateBTOC(&sInfo.gc.data, _scrub, sInfo.totalPages); + sInfo.blockArray[sInfo.gc.data.chosenBlock].status = + BLOCKSTATUS_CURRENT; + + sInfo.gc.data.uECC = 0; + sInfo.gc.data.state = 1; + return 0; + } else { + ++sStats.field_30; + sInfo.gc.data.state = 2; + } + } else if (sInfo.gc.data.state == 1) { + uint32_t gcPageNum; + uint8_t continueAfterWhile = 1; + + if (sInfo.blockArray[sInfo.gc.data.chosenBlock].validPagesINo + != 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages chose a block with no" + " valid index pages\r\n"); + } + + if (sInfo.blockArray[sInfo.gc.data.chosenBlock].validPagesDNo + == 0) { + sInfo.gc.data.state = 2; + continue; + } + + // TODO: Understand this. Some sort of sorting score? + gcPageNum = sInfo.gcPages; + + if (_numPages > 0) { + uint32_t invalidPages = sGeometry.pagesPerSublk - + sInfo.tocPagesPerBlock - sInfo.gc.data.totalValidPages; + + if (invalidPages == 0) + invalidPages = 1; + + gcPageNum = sGeometry.pagesPerSublk * _numPages / invalidPages; + } + + sInfo.gc.data.curZoneSize = 0; + gcResetReadCache(&sInfo.gc.data.read_c); + + while (sInfo.gc.data.curZoneSize < gcPageNum && + sInfo.gc.data.btocIdx < sInfo.gc.data.dataPagesPerSublk && + sInfo.gc.data.numInvalidatedPages < + sInfo.gc.data.totalValidPages) { + uint32_t btocEntry = + sInfo.gc.data.btoc[sInfo.gc.data.btocIdx]; + + if (btocEntry != 0xFFFFFFFF) { + if (sInfo.gc.data.read_c.span == 0 || + sInfo.gc.data.read_c.pageIndex != btocEntry) { + uint32_t vpn; + sInfo.gc.data.read_c.pageIndex = btocEntry; + L2V_Search(&sInfo.gc.data.read_c); + if (sInfo.gc.data.read_c.span == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages has called " + "L2V_Search but span is still 0\r\n"); + } + + vpn = sInfo.gc.data.read_c.vpn; + + ++sInfo.gc.data.read_c.pageIndex; + --sInfo.gc.data.read_c.span; + + if (L2V_VPN_ISNORMAL(vpn)) { + ++sInfo.gc.data.read_c.vpn; + } else if (vpn == L2V_VPN_MISS) { + int ret = gcHandleVpnMiss(&sInfo.gc.data, _scrub); + + if (ret >= 0) { + vpn = ret; + } else { + if (ret == -1) + continueAfterWhile = 0; + + break; + } + } else if (vpn != L2V_VPN_SPECIAL) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages doesn't know " + "what vpn %X is\r\n", vpn); + } + + if (vpn != L2V_VPN_SPECIAL) { + uint32_t ourPage = sInfo.gc.data.chosenBlock + * sGeometry.pagesPerSublk + + sInfo.gc.data.btocIdx; + + if (vpn == ourPage) { + sInfo.gc.data.zone[ + sInfo.gc.data.curZoneSize++] = vpn; + ++sInfo.gc.data.numInvalidatedPages; + } + } + } + } // if (btocEntry != 0xFFFFFFFF) + + if (sInfo.gcPages - sInfo.latestUserBlk.usedPages % + sGeometry.total_banks_ftl + <= sInfo.gc.data.curZoneSize) { + if (sInfo.blockArray[sInfo.gc.data.chosenBlock]. + validPagesDNo < sInfo.gc.data.curZoneSize) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages tried to free " + "more pages than it should\r\n"); + } + + if (gcReadZoneData(&sInfo.gc.data, 0, _scrub) == 0) { + if (gcWriteDataPages(&sInfo.gc.data, _scrub) == 0) { + sInfo.gc.data.curZoneSize = 0; + } else { + sInfo.gc.data.state = 4; + continueAfterWhile = 0; + break; + } + } else { + if (sInfo.gc.data.state == 1) + sInfo.gc.data.state = 0; + + continueAfterWhile = 0; + break; + } + } + + ++sInfo.gc.data.btocIdx; + } // while + + if (!continueAfterWhile) + break; + + // If there are still pages left in the zone, free them. + if (sInfo.gc.data.curZoneSize > 0) { + if (sInfo.blockArray[sInfo.gc.data.chosenBlock].validPagesDNo < + sInfo.gc.data.curZoneSize) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages had pages left in the" + " zone, but there aren't valid pages at all\r\n"); + } + + if (gcReadZoneData(&sInfo.gc.data, 0, _scrub) == 0) { + if (gcWriteDataPages(&sInfo.gc.data, _scrub) == 0) { + if (sInfo.gc.data.dataPagesPerSublk <= + sInfo.gc.data.btocIdx) { + gcSanityCheckValid(&sInfo.gc.data); + } + + return 0; + } else { + sInfo.gc.data.state = 4; + } + } else if (sInfo.gc.data.state == 1) { + sInfo.gc.data.state = 0; + } + } + } else if (sInfo.gc.data.state == 2) { + // Claim this as a free block. + uint32_t chosenBlock = sInfo.gc.data.chosenBlock; + + gcSanityCheckValid(&sInfo.gc.data); + sInfo.blockArray[chosenBlock].unkn5 = 2; + sInfo.blockArray[chosenBlock].readCount = 0; + sInfo.blockArray[chosenBlock].validPagesINo = 0; + sInfo.blockArray[chosenBlock].validPagesDNo = 0; + sInfo.blockArray[chosenBlock].status = BLOCKSTATUS_FREE; + ++sInfo.blockStats.numFree; + ++sInfo.blockStats.numAvailable; + --sInfo.blockStats.numAllocated; + ++sStats.freeBlocks; + + sInfo.gc.data.state = 0; + if (sInfo.gc.data.list.head == sInfo.gc.data.list.tail) + return 0; + } else if (sInfo.gc.data.state == 3) { + while (sInfo.gc.index.list.head != sInfo.gc.index.list.tail) + gcFreeIndexPages(0xFFFFFFFF, _scrub); + + sInfo.gc.data.state = 0; + } else if (sInfo.gc.data.state == 4) { + sInfo.gc.data.state = 0; + } else { + return 0; + } + } + + return 0; +} + +static void setupIndexSpares(SpareData* _pSpare, size_t _count) +{ + uint32_t i; + + if (sInfo.lastWrittenBlock != sInfo.latestIndexBlk.blockNum) { + sInfo.lastWrittenBlock = sInfo.latestIndexBlk.blockNum; + ++sInfo.maxIndexUsn; + } + + for (i = 0; i < _count; ++i) { + _pSpare[i].usn = sInfo.maxIndexUsn; + _pSpare[i].type = PAGETYPE_INDEX; + } +} + +static error_t gcWriteIndexPages(GCData* _data) +{ + SpareData* spareArray = sInfo.gc.index.spareArray; + uint32_t block = sInfo.latestIndexBlk.blockNum; + uint32_t leftToWrite = _data->curZoneSize; + uint32_t pageOffset = 0; + uint32_t i; + + while (leftToWrite > 0) { + uint32_t usablePages = sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock; + + if (sInfo.latestIndexBlk.usedPages < usablePages) { + // Can't just close the block, because it isn't full. Bummer. + uint32_t toWrite = MIN(leftToWrite, + usablePages - sInfo.latestIndexBlk.usedPages); + uint8_t* buffer = &sInfo.gc.index.pageBuffer2[pageOffset * + sGeometry.bytesPerPage]; + + setupIndexSpares(&spareArray[pageOffset], toWrite); + if (YAFTL_writeMultiPages(block, sInfo.latestIndexBlk.usedPages, + toWrite, buffer, &spareArray[pageOffset], 0)) { + // Write failure. + gcListPushBack(&_data->list, _data->chosenBlock); + gcListPushBack(&_data->list, block); + YAFTL_allocateNewBlock(0); + return EIO; + } + + for (i = 0; i < toWrite; ++i) { + uint32_t pageInBlk = sInfo.latestIndexBlk.usedPages + i; + + sInfo.latestIndexBlk.tocBuffer[pageInBlk] = + spareArray[i].lpn; + sInfo.tocArray[spareArray[i].lpn].indexPage = + block * sGeometry.pagesPerSublk + pageInBlk; + } + + sInfo.blockArray[block].validPagesINo += toWrite; + sInfo.blockStats.numValidIPages += toWrite; + sStats.indexPages += toWrite; + sInfo.latestIndexBlk.usedPages += toWrite; + pageOffset += toWrite; + leftToWrite -= toWrite; + + if (leftToWrite == 0) + return 0; + + if (sInfo.latestIndexBlk.usedPages + toWrite < + sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock) { + continue; + } + } + + YAFTL_closeLatestBlock(0); + YAFTL_allocateNewBlock(0); + block = sInfo.latestIndexBlk.blockNum; + + if (sInfo.latestIndexBlk.usedPages) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteIndexPages got a non-empty block " + "%d\r\n", block); + } + } + + return 0; +} + +/* Externals */ + +error_t gcInit() +{ + uint32_t i; + for (i = sGeometry.total_banks_ftl; i <= 0xF; i <<= 1); + + gcResetReadCache(&sInfo.gc.data.read_c); + sInfo.gcPages = i; + sInfo.gc.data.state = 0; + sInfo.gc.data.victim = 0xFFFFFFFF; + sInfo.gc.index.victim = 0xFFFFFFFF; + + // Allocate buffers + bufzone_init(&sInfo.gcBufZone); + + sInfo.gc.data.btoc = (uint32_t*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.tocPagesPerBlock * sGeometry.bytesPerPage); + sInfo.gc.data.pageBuffer1 = (uint8_t*)bufzone_alloc(&sInfo.gcBufZone, + sGeometry.bytesPerPage); + sInfo.gc.index.btoc = (uint32_t*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.tocPagesPerBlock * sGeometry.bytesPerPage); + sInfo.gc.index.pageBuffer1 = (uint8_t*)bufzone_alloc(&sInfo.gcBufZone, + sGeometry.bytesPerPage); + // TODO: Verify that this is working. They use something else. + sInfo.gcSpareBuffer = (SpareData*)bufzone_alloc(&sInfo.gcBufZone, + sizeof(SpareData)); + sInfo.gc.data.pageBuffer2 = (uint8_t*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.gcPages * sGeometry.bytesPerPage); + sInfo.gc.index.pageBuffer2 = (uint8_t*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.gcPages * sGeometry.bytesPerPage); + sInfo.gc.data.spareArray = (SpareData*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.gcPages * sizeof(SpareData)); + sInfo.gc.index.spareArray = (SpareData*)bufzone_alloc(&sInfo.gcBufZone, + sInfo.gcPages * sizeof(SpareData)); + + bufzone_finished_allocs(&sInfo.gcBufZone); + + sInfo.gc.data.btoc = (uint32_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.data.btoc); + sInfo.gc.data.pageBuffer1 = (uint8_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.data.pageBuffer1); + sInfo.gc.index.btoc = (uint32_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.index.btoc); + sInfo.gc.index.pageBuffer1 = (uint8_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.index.pageBuffer1); + sInfo.gcSpareBuffer = (SpareData*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gcSpareBuffer); + sInfo.gc.data.pageBuffer2 = (uint8_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.data.pageBuffer2); + sInfo.gc.index.pageBuffer2 = (uint8_t*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.index.pageBuffer2); + sInfo.gc.data.spareArray = (SpareData*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.data.spareArray); + sInfo.gc.index.spareArray = (SpareData*)bufzone_rebase(&sInfo.gcBufZone, + sInfo.gc.index.spareArray); + + bufzone_finished_rebases(&sInfo.gcBufZone); + + sInfo.gc.data.zone = (uint32_t*)yaftl_alloc(sInfo.gcPages + * sizeof(uint32_t)); + sInfo.gc.index.zone = (uint32_t*)yaftl_alloc(sInfo.gcPages + * sizeof(uint32_t)); + + if (sInfo.gc.data.btoc && sInfo.gc.data.pageBuffer1 && sInfo.gc.index.btoc + && sInfo.gc.index.pageBuffer1 && sInfo.gcSpareBuffer + && sInfo.gc.data.pageBuffer2 && sInfo.gc.index.pageBuffer2 + && sInfo.gc.data.spareArray && sInfo.gc.index.spareArray + && sInfo.gc.data.zone && sInfo.gc.index.zone) { + return 0; + } else { + return ENOMEM; + } +} + +void gcResetReadCache(GCReadC* _readC) +{ + _readC->field_14 = 0xFFFFFFFF; + _readC->node = NULL; + _readC->span = 0; +} + +void gcListPushBack(GCList* _list, uint32_t _block) +{ + uint32_t curr = _list->head; + + // Search if the block already exists. + while (curr != _list->tail) { + if (_list->block[curr] == _block) + return; + + ++curr; + if (curr > GCLIST_MAX) + curr = 0; + } + + _list->block[_list->tail] = _block; + ++_list->tail; + + if (_list->tail > GCLIST_MAX) + _list->tail = 0; + + if (_list->tail == _list->head) + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcListPushBack -- list is full\r\n"); +} + +void gcFreeBlock(uint32_t _block, uint8_t _scrub) +{ + uint32_t chosenBlock = sInfo.gc.data.chosenBlock; + uint32_t currBlock = _block; + + if (sInfo.gc.data.state && chosenBlock != 0xFFFFFFFF && + sInfo.blockArray[chosenBlock].status == BLOCKSTATUS_CURRENT) { + sInfo.blockArray[chosenBlock].status = BLOCKSTATUS_ALLOCATED; + } + + sInfo.gc.data.state = 0; + + while (1) { + sInfo.gc.data.victim = currBlock; + while (gcFreeDataPages(0, _scrub) == 0) { + if (!sInfo.gc.data.state) { + if (sInfo.gc.data.list.head == sInfo.gc.data.list.tail) + return; + + currBlock = gcListPopFront(&sInfo.gc.data.list); + } + } + } +} + +void gcPrepareToWrite(uint32_t _numPages) +{ + uint32_t numBlocks = _numPages + / (sGeometry.pagesPerSublk - sInfo.tocPagesPerBlock); + + sInfo.gc.data.victim = 0xFFFFFFFF; + + while (sInfo.blockStats.numAvailable <= numBlocks + 5) { + if (sInfo.blockStats.numIAvailable <= 1) + gcFreeIndexPages(0xFFFFFFFF, 1); + + gcFreeBlock(0xFFFFFFFF, 1); + } + + if (sInfo.blockStats.numAvailable <= numBlocks + 10 + || sInfo.gc.data.state != 0) + { + gcFreeDataPages(_numPages, 1); + } +} + +void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub) +{ + uint32_t i; + + sInfo.gc.index.victim = _victim; + ++sStats.freeIndexOps; + + while (1) { + uint8_t failure = 0; + uint32_t block; + + gcChooseBlock(&sInfo.gc.index, BLOCKSTATUS_I_ALLOCATED); + block = sInfo.gc.index.chosenBlock; + + if (sInfo.gc.index.totalValidPages == 0) { + ++sStats.field_38; + } else { + gcPopulateBTOC(&sInfo.gc.index, _scrub, sInfo.tocArrayLength); + sInfo.gc.index.numInvalidatedPages = 0; + sInfo.gc.index.curZoneSize = 0; + sInfo.gc.index.uECC = 0; + sInfo.blockArray[sInfo.gc.index.chosenBlock].status = 0x80; + + for (i = 0; i < sInfo.gc.index.dataPagesPerSublk + && sInfo.gc.index.numInvalidatedPages < + sInfo.gc.index.totalValidPages; ++i) { + uint32_t btocEntry = sInfo.gc.index.btoc[i]; + TOCStruct* toc; + TOCCache* tocCache = NULL; + + if(btocEntry == 0xFFFFFFFF) + continue; + + toc = &sInfo.tocArray[btocEntry]; + + // If there's no index page, or it's not our page, skip. + if ((toc->indexPage == 0xFFFFFFFF && toc->cacheNum == 0xFFFF) + || (toc->indexPage != 0xFFFFFFF && toc->indexPage != + block * sGeometry.pagesPerSublk + i)) { + continue; + } + + // If the data is already cached, used it. + if (toc->cacheNum != 0xFFFF) { + TOCCache* cache = &sInfo.tocCaches[toc->cacheNum]; + + if (cache->state == CACHESTATE_CLEAN) { + // Should invalidate. + cache->state = CACHESTATE_DIRTY; + + if (toc->indexPage == block * sGeometry.pagesPerSublk + + i) { + if (sInfo.blockArray[block].validPagesINo == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages tried to" + " invalidate in block %d, with no " + "valid pages\r\n", block); + } + + --sInfo.blockArray[block].validPagesINo; + --sInfo.blockStats.numValidIPages; + --sStats.indexPages; + ++sInfo.gc.index.numInvalidatedPages; + toc->indexPage = 0xFFFFFFFF; + } + } + + continue; + } + + // Data is not cached. First, validate everything. + if (toc->indexPage == 0xFFFFFFFF) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages found a TOC which " + "is not cached nor available %d\r\n", block); + } + + if (toc->indexPage != block * sGeometry.pagesPerSublk + i) + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages can't be here\r\n"); + + // Find a free cache. + toc->cacheNum = YAFTL_findFreeTOCCache(); + if (toc->cacheNum != 0xFFFF) { + // Found one. + tocCache = &sInfo.tocCaches[toc->cacheNum]; + if (YAFTL_readPage(block * sGeometry.pagesPerSublk + i, + (uint8_t*)tocCache->buffer, + sInfo.spareBuffer18, 0, 1, _scrub) + != 0) { + // Read failure. + failure = 1; + break; + } + + tocCache->state = CACHESTATE_DIRTY; + tocCache->page = btocEntry; + toc->indexPage = 0xFFFFFFFF; + + if (sInfo.blockArray[block].validPagesINo == 0) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages tried to " + "invalidate an index in block %d but it has " + "no valid indexes\r\n", block); + } + + --sInfo.blockArray[block].validPagesINo; + --sInfo.blockStats.numValidIPages; + --sStats.indexPages; + ++sInfo.gc.index.numInvalidatedPages; + continue; + } + + // No free cache :( Must free manually. + if (sInfo.blockArray[block].validPagesINo < + sInfo.gc.index.curZoneSize) { + printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages can't invalidate " + "more pages than available (%d < %d)\r\n", + sInfo.blockArray[block].validPagesINo, + sInfo.gc.index.curZoneSize); + } + + if (sInfo.gcPages - sInfo.latestIndexBlk.usedPages + % sGeometry.total_banks_ftl + <= sInfo.gc.index.curZoneSize) { + // There are pages to free. + if (gcReadZoneData(&sInfo.gc.index, 1, _scrub) != 0 || + gcWriteIndexPages(&sInfo.gc.index) != 0) { + // Read / write failure. + failure = 1; + break; + } + + sInfo.blockArray[block].validPagesINo -= + sInfo.gc.index.curZoneSize; + sInfo.blockStats.numValidIPages -= + sInfo.gc.index.curZoneSize; + sStats.indexPages -= sInfo.gc.index.curZoneSize; + sInfo.gc.index.numInvalidatedPages += + sInfo.gc.index.curZoneSize; + sInfo.gc.index.curZoneSize = 0; + } + + sInfo.gc.index.zone[sInfo.gc.index.curZoneSize++] = + block * sGeometry.pagesPerSublk + i; + } + } + + if (!failure) { + if (sInfo.gc.index.curZoneSize != 0 + && sInfo.gc.index.totalValidPages != 0) { + // There are pages to free. + if (gcReadZoneData(&sInfo.gc.index, 1, _scrub) != 0 || + gcWriteIndexPages(&sInfo.gc.index) != 0) { + // Read / write failure. + continue; + } + + sInfo.blockStats.numValidIPages -= sInfo.gc.index.curZoneSize; + sInfo.blockArray[block].validPagesINo -= + sInfo.gc.index.curZoneSize; + sStats.indexPages -= sInfo.gc.index.curZoneSize; + sInfo.gc.index.numInvalidatedPages += + sInfo.gc.index.curZoneSize; + } + + // Claim as a free block. + gcSanityCheckValid(&sInfo.gc.index); + sInfo.blockArray[block].unkn5 = 4; + sInfo.blockArray[block].readCount = 0; + sInfo.blockArray[block].validPagesINo = 0; + sInfo.blockArray[block].validPagesDNo = 0; + sInfo.blockArray[block].status = BLOCKSTATUS_FREE; + ++sInfo.blockStats.numFree; + ++sInfo.blockStats.numIAvailable; + --sInfo.blockStats.numIAllocated; + ++sStats.freeBlocks; + + if (sInfo.gc.index.list.head == sInfo.gc.index.list.tail) + return; + } + } +} + +void gcPrepareToFlush() +{ + uint32_t i; + + while (sInfo.numIBlocks < sInfo.blockStats.numIAllocated + 2) + gcFreeIndexPages(0xFFFFFFFF, 1); + + for (i = 0; i < sInfo.numCaches; ++i) + YAFTL_clearEntryInCache(i); +} diff --git a/drivers/block/apple/yaftl/yaftl_gc.h b/drivers/block/apple/yaftl/yaftl_gc.h new file mode 100644 index 00000000000..99058db456c --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_gc.h @@ -0,0 +1,20 @@ +#ifndef FTL_YAFTL_GC_H +#define FTL_YAFTL_GC_H + +#include "yaftl_common.h" + +error_t gcInit(void); + +void gcResetReadCache(GCReadC* _readC); + +void gcListPushBack(GCList* _gcList, uint32_t _block); + +void gcFreeBlock(uint32_t _block, uint8_t _scrub); + +void gcPrepareToWrite(uint32_t _numPages); + +void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub); + +void gcPrepareToFlush(void); + +#endif // FTL_YAFTL_GC_H diff --git a/drivers/block/apple/yaftl/yaftl_mem.c b/drivers/block/apple/yaftl/yaftl_mem.c new file mode 100644 index 00000000000..7fce5df2a91 --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_mem.c @@ -0,0 +1,88 @@ +#include +#include "yaftl_mem.h" + +#define ROUND_UP(a, b) ((((a) + (b) - 1)/(b)) * (b)) + +void* yaftl_alloc(size_t size) +{ +#ifdef YUSTAS_FIXME + void* buffer = memalign(0x40, size); +#else + void* buffer = kzalloc(size, GFP_KERNEL); +#endif + + if (!buffer) + printk(KERN_DEBUG "PANIC!!!: yaftl_alloc failed\r\n"); + +#ifdef YUSTAS_FIXME + memset(buffer, 0, size); +#endif + return buffer; +} + +void bufzone_init(bufzone_t* _zone) +{ + _zone->buffer = 0; + _zone->endOfBuffer = 0; + _zone->size = 0; + _zone->numAllocs = 0; + _zone->numRebases = 0; + _zone->paddingsSize = 0; + _zone->state = 1; +} + +// returns the sub-buffer offset +void* bufzone_alloc(bufzone_t* _zone, size_t size) +{ + size_t oldSizeRounded; + + if (_zone->state != 1) + printk(KERN_DEBUG "PANIC!!!: bufzone_alloc: bad state\r\n"); + + oldSizeRounded = ROUND_UP(_zone->size, 64); + _zone->paddingsSize = _zone->paddingsSize + (oldSizeRounded - _zone->size); + _zone->size = oldSizeRounded + size; + _zone->numAllocs++; + + return (void*)oldSizeRounded; +} + +error_t bufzone_finished_allocs(bufzone_t* _zone) +{ + uint8_t* buff; + + if (_zone->state != 1) { + printk(KERN_ERR "bufzone_finished_allocs: bad state\r\n"); + return EINVAL; + } + + _zone->size = ROUND_UP(_zone->size, 64); + buff = yaftl_alloc(_zone->size); + + if (!buff) { + printk(KERN_ERR "bufzone_finished_alloc: No buffer.\r\n"); + return EINVAL; + } + + _zone->buffer = (uint32_t)buff; + _zone->endOfBuffer = (uint32_t)(buff + _zone->size); + _zone->state = 2; + + return 0; +} + +void* bufzone_rebase(bufzone_t* _zone, void* buf) +{ + _zone->numRebases++; + return buf + _zone->buffer; +} + +error_t bufzone_finished_rebases(bufzone_t* _zone) +{ + if (_zone->numAllocs != _zone->numRebases) { + printk(KERN_ERR "WMR_BufZone_FinishedRebases: _zone->numAllocs != _zone->numRebases\r\n"); + return EINVAL; + } + + return 0; +} diff --git a/drivers/block/apple/yaftl/yaftl_mem.h b/drivers/block/apple/yaftl/yaftl_mem.h new file mode 100644 index 00000000000..dd35582750c --- /dev/null +++ b/drivers/block/apple/yaftl/yaftl_mem.h @@ -0,0 +1,25 @@ +#ifndef FTL_YAFTL_MEM_H +#define FTL_YAFTL_MEM_H +#include +#include "../vfl.h" + +typedef struct +{ + uint32_t buffer; + uint32_t endOfBuffer; + uint32_t size; + uint32_t numAllocs; + uint32_t numRebases; + uint32_t paddingsSize; + uint32_t state; +} bufzone_t; + +// TODO: documentation +void* yaftl_alloc(size_t size); +void bufzone_init(bufzone_t* _zone); +void* bufzone_alloc(bufzone_t* _zone, size_t size); +error_t bufzone_finished_allocs(bufzone_t* _zone); +void* bufzone_rebase(bufzone_t* _zone, void* buf); +error_t bufzone_finished_rebases(bufzone_t* _zone); + +#endif // FTL_YAFTL_MEM_H From 1d6f23555cd5a5f2a0eb8ce203734cef7e3e83d8 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Thu, 3 May 2012 17:18:50 +0400 Subject: [PATCH 08/11] Addition to previous commit. --- arch/arm/mach-s5l8930/cpu.c | 8 + arch/arm/mach-s5l8930/include/mach/map.h | 4 + drivers/block/apple/Makefile | 9 +- drivers/block/apple/api.c | 67 +- drivers/block/apple/blk_dev.c | 370 +++++++ drivers/block/apple/h2fmi.c | 78 +- drivers/block/apple/vfl.c | 171 +++ drivers/block/apple/vsvfl.c | 1216 ++++++++++++++++++++++ include/linux/apple_flash.h | 24 +- 9 files changed, 1897 insertions(+), 50 deletions(-) diff --git a/arch/arm/mach-s5l8930/cpu.c b/arch/arm/mach-s5l8930/cpu.c index e894b32ec6e..d91399b843e 100644 --- a/arch/arm/mach-s5l8930/cpu.c +++ b/arch/arm/mach-s5l8930/cpu.c @@ -134,6 +134,12 @@ static struct map_desc s5l8930_iodesc[] __initdata = { .length = SZ_PWM, .type = MT_DEVICE, }, + { + .virtual = (unsigned long)VA_CHIPID, + .pfn = __phys_to_pfn(PA_CHIPID), + .length = SZ_CHIPID, + .type = MT_DEVICE, + }, }; static __init void s5l8930_cpu_map_io(void) @@ -295,6 +301,8 @@ EXPORT_SYMBOL(s3c_setup_uart_cfg_gpio); void __init s5l8930_map_io(void) { +// unsigned long idcode = __raw_readl(VA_CHIPID); +// printk(KERN_ERR "%s:%d IDCODE=%x\n", __FUNCTION__, __LINE__, idcode); s3c_init_cpu(0x8930, cpu_id, ARRAY_SIZE(cpu_id)); s3c24xx_init_clocks(24000000); s3c24xx_init_uarts(s5l8930_uartcfgs, ARRAY_SIZE(s5l8930_uartcfgs)); diff --git a/arch/arm/mach-s5l8930/include/mach/map.h b/arch/arm/mach-s5l8930/include/mach/map.h index 48682d8cb0a..072471b6968 100644 --- a/arch/arm/mach-s5l8930/include/mach/map.h +++ b/arch/arm/mach-s5l8930/include/mach/map.h @@ -191,6 +191,10 @@ #define PA_DISPLAYPORT 0x84900000 #define SZ_DISPLAYPORT 0x2000 +#define PA_CHIPID 0xBF500000 +#define SZ_CHIPID 0x1000 +#define VA_CHIPID (S3C_ADDR_CPU(0x900000)) + // S3C mappings #define S3C_PA_USB_HSOTG PA_USB diff --git a/drivers/block/apple/Makefile b/drivers/block/apple/Makefile index b96768db897..49e65c5478c 100644 --- a/drivers/block/apple/Makefile +++ b/drivers/block/apple/Makefile @@ -1,9 +1,10 @@ blk_dev_apple-y += blk_dev.o api.o +obj-$(CONFIG_BLK_DEV_H2FMI) += h2fmi.o obj-$(CONFIG_BLK_DEV_APPLE) += blk_dev_apple.o -obj-$(CONFIG_BLK_DEV_APPLE_VSVFL) += vsvfl.o -#obj-$(CONFIG_BLK_DEV_APPLE_YAFTL) += yaftl/ -obj-$(CONFIG_BLK_DEV_APPLE_LEGACY_VFL) += vfl.o #obj-$(CONFIG_BLK_DEV_APPLE_LEGACY_FTL) += ftl/ -obj-$(CONFIG_BLK_DEV_H2FMI) += h2fmi.o +obj-$(CONFIG_BLK_DEV_APPLE_LEGACY_FTL) += ftl.o +obj-$(CONFIG_BLK_DEV_APPLE_YAFTL) += yaftl/ +obj-$(CONFIG_BLK_DEV_APPLE_LEGACY_VFL) += vfl.o +obj-$(CONFIG_BLK_DEV_APPLE_VSVFL) += vsvfl.o diff --git a/drivers/block/apple/api.c b/drivers/block/apple/api.c index cee406dad2a..e6e8b95d42a 100644 --- a/drivers/block/apple/api.c +++ b/drivers/block/apple/api.c @@ -5,9 +5,39 @@ // // NAND // +#define TOTAL_CHIPS_COUNT 2 +struct apple_nand *chips[TOTAL_CHIPS_COUNT]; +int total_chips = 0; + +struct apple_nand* get_apple_nand(u16 ce) +{ + if (ce >= total_chips) { + printk(KERN_ERR "%s: wrong chip number.\n", __FUNCTION__); + return NULL; + } + return chips[ce]; +} + +int apple_nand_set_data_whitening(int whitening) +{ + int i; + for (i = 0; i < total_chips; i++) + { + struct apple_nand *nand = get_apple_nand(i); + if (nand->set_whitening(nand, whitening)) + return -ENOENT; + } + return 0; +} +EXPORT_SYMBOL_GPL(apple_nand_set_data_whitening); int register_apple_nand(struct apple_nand *_nd) { + /* TODO: for now each bus has only one chip !*/ + if (total_chips == TOTAL_CHIPS_COUNT) + return -ENOMEM; + + chips[total_chips++] = _nd; return 0; } EXPORT_SYMBOL_GPL(register_apple_nand); @@ -17,9 +47,23 @@ void remove_apple_nand(struct apple_nand *_nd) } EXPORT_SYMBOL_GPL(remove_apple_nand); -int apple_nand_special_page(struct apple_nand *_nd, u16 _ce, char _page[16], +int apple_nand_get_num_ce(void) +{ + return total_chips; +} +EXPORT_SYMBOL_GPL(apple_nand_get_num_ce); + +int apple_nand_get_info(int info) +{ + struct apple_nand *nand = get_apple_nand(0); + return nand->get(nand, info); +} +EXPORT_SYMBOL_GPL(apple_nand_get_info); + +int apple_nand_special_page(u16 _ce, char _page[16], uint8_t* _buffer, size_t _amt) { + struct apple_nand *_nd = get_apple_nand(_ce); int pagesz = _nd->get(_nd, NAND_PAGE_SIZE); int bpce = _nd->get(_nd, NAND_BLOCKS_PER_CE); int bpb = _nd->get(_nd, NAND_BLOCKS_PER_BANK); @@ -42,8 +86,8 @@ int apple_nand_special_page(struct apple_nand *_nd, u16 _ce, char _page[16], if(badCount > 2) break; - ret = apple_nand_read_page(_nd, _ce, - (realBlock * ppb) + page, buf, 0); + ret = apple_nand_read_page(_ce, + (realBlock * ppb) + page, buf, 0, 0); if(ret < 0) { if(ret != -ENOENT) @@ -75,10 +119,12 @@ int apple_nand_special_page(struct apple_nand *_nd, u16 _ce, char _page[16], } EXPORT_SYMBOL_GPL(apple_nand_special_page); -int apple_nand_read_page(struct apple_nand *_nd, u16 _ce, page_t _page, - uint8_t *_data, uint8_t *_oob) +int apple_nand_read_page(u16 _ce, page_t _page, + uint8_t *_data, uint8_t *_oob, int disable_aes) { struct scatterlist sg_buf, sg_oob; + struct apple_nand *_nd = get_apple_nand(_ce); + u16 chip = 0; if(!_nd->read) return -EPERM; @@ -86,14 +132,16 @@ int apple_nand_read_page(struct apple_nand *_nd, u16 _ce, page_t _page, sg_init_one(&sg_buf, _data, _nd->get(_nd, NAND_PAGE_SIZE)); sg_init_one(&sg_oob, _oob, _nd->get(_nd, NAND_OOB_ALLOC)); - return _nd->read(_nd, 1, &_ce, &_page, &sg_buf, 1, &sg_oob, 1); + return _nd->read(_nd, 1, &chip, &_page, &sg_buf, 1, &sg_oob, 1, disable_aes); } EXPORT_SYMBOL_GPL(apple_nand_read_page); -int apple_nand_write_page(struct apple_nand *_nd, u16 _ce, page_t _page, +int apple_nand_write_page(u16 _ce, page_t _page, const uint8_t *_data, const uint8_t *_oob) { struct scatterlist sg_buf, sg_oob; + struct apple_nand *_nd = get_apple_nand(_ce); + u16 chip = 0; if(!_nd->write) return -EPERM; @@ -101,12 +149,13 @@ int apple_nand_write_page(struct apple_nand *_nd, u16 _ce, page_t _page, sg_init_one(&sg_buf, _data, _nd->get(_nd, NAND_PAGE_SIZE)); sg_init_one(&sg_oob, _oob, _nd->get(_nd, NAND_OOB_ALLOC)); - return _nd->write(_nd, 1, &_ce, &_page, &sg_buf, 1, &sg_oob, 1); + return _nd->write(_nd, 1, &chip, &_page, &sg_buf, 1, &sg_oob, 1); } EXPORT_SYMBOL_GPL(apple_nand_write_page); -int apple_nand_erase_block(struct apple_nand *_nd, u16 _ce, page_t _page) +int apple_nand_erase_block(u16 _ce, page_t _page) { + struct apple_nand *_nd = get_apple_nand(_ce); if(!_nd->erase) return -EPERM; diff --git a/drivers/block/apple/blk_dev.c b/drivers/block/apple/blk_dev.c index e69de29bb2d..8d4a0406a5d 100644 --- a/drivers/block/apple/blk_dev.c +++ b/drivers/block/apple/blk_dev.c @@ -0,0 +1,370 @@ +#include +#include +#include +#include +#include +#include +#include +#include "vfl.h" +#include "ftl.h" + +// 2^9 = 512 +#define SECTOR_SHIFT 9 + +#ifdef YUSTAS_FIXME +extern NANDData* NANDGeometry; +#endif + +static struct +{ + vfl_device_t *h2fmi_vfl_device; + ftl_device_t *h2fmi_ftl_device; + + spinlock_t lock; + struct gendisk* gd; + struct block_device* bdev; + struct request_queue* queue; + int sectorSize; + int pageShift; + int majorNum; + + struct request* req; + bool processing; + u8* bounceBuffer; +} apple_a4_block_device; + +static void ftl_workqueue_handler(struct work_struct* work); + +DECLARE_WORK(ftl_workqueue, &ftl_workqueue_handler); +static struct workqueue_struct* ftl_wq; + +static void apple_a4_block_scatter_gather(struct request* req, bool gather) +{ + unsigned int offset = 0; + struct req_iterator iter; + struct bio_vec *bvec; + unsigned int i = 0; + size_t size; + void *buf; + + rq_for_each_segment(bvec, req, iter) { + unsigned long flags; + //printk("%s:%u: bio %u: %u segs %u sectors from %lu\n", + // __func__, __LINE__, i, bio_segments(iter.bio), + // bio_sectors(iter.bio), (unsigned long) iter.bio->bi_sector); + + size = bvec->bv_len; + buf = bvec_kmap_irq(bvec, &flags); + if (gather) + memcpy(apple_a4_block_device.bounceBuffer + offset, buf, size); + else + memcpy(buf, apple_a4_block_device.bounceBuffer + offset, size); + offset += size; + flush_kernel_dcache_page(bvec->bv_page); + bvec_kunmap_irq(buf, &flags); + i++; + } + + //printk("scatter_gather total: %d / %d\n", offset, NANDGeometry->pagesPerSuBlk * NANDGeometry->bytesPerPage); + +} + +static void ftl_workqueue_handler(struct work_struct* work) +{ + unsigned long flags; + bool dir_out; + int ret; + + //printk("ftl_workqueue_handler enter\n"); + + while(true) + { + u32 lpn; + u32 numPages; + u32 remainder; + + //printk("ftl_workqueue_handler loop\n"); + + spin_lock_irqsave(&apple_a4_block_device.lock, flags); + if(apple_a4_block_device.req == NULL || apple_a4_block_device.processing) + { + spin_unlock_irqrestore(&apple_a4_block_device.lock, flags); + //printk("ftl_workqueue_handler exit\n"); + return; + } + + apple_a4_block_device.processing = true; + spin_unlock_irqrestore(&apple_a4_block_device.lock, flags); + + if(apple_a4_block_device.req->cmd_type == REQ_TYPE_FS) + { + lpn = blk_rq_pos(apple_a4_block_device.req) >> (apple_a4_block_device.pageShift - SECTOR_SHIFT); + numPages = blk_rq_bytes(apple_a4_block_device.req) / apple_a4_block_device.sectorSize; + remainder = numPages * apple_a4_block_device.sectorSize - blk_rq_bytes(apple_a4_block_device.req); + + if(remainder) + { + printk("apple_a4_block: requested not page aligned number of bytes (%d bytes)\n", blk_rq_bytes(apple_a4_block_device.req)); + blk_end_request_all(apple_a4_block_device.req, -EINVAL); + } else + { + if(rq_data_dir(apple_a4_block_device.req)) + { + dir_out = true; + apple_a4_block_scatter_gather(apple_a4_block_device.req, true); + } else + { + dir_out = false; + } + + + if(dir_out) + { + //printk("FTL_Write enter: %p\n", apple_a4_block_device.req); +#ifdef YUSTAS_FIXME + ret = FTL_Write(lpn, numPages, apple_a4_block_device.bounceBuffer); +#endif + //printk("FTL_Write exit: %p\n", apple_a4_block_device.req); + } else + { + //printk("FTL_Read enter: %p\n", apple_a4_block_device.req); +#ifdef YUSTAS_FIXME + ret = FTL_Read(lpn, numPages, apple_a4_block_device.bounceBuffer); +#endif + //printk("FTL_Read exit: %p\n", apple_a4_block_device.req); + } + + if(!dir_out) + { + apple_a4_block_scatter_gather(apple_a4_block_device.req, false); + } + + blk_end_request_all(apple_a4_block_device.req, ret); + } + } else + { + blk_end_request_all(apple_a4_block_device.req, -EINVAL); + } + spin_lock_irqsave(&apple_a4_block_device.lock, flags); + apple_a4_block_device.processing = false; + apple_a4_block_device.req = blk_fetch_request(apple_a4_block_device.queue); + spin_unlock_irqrestore(&apple_a4_block_device.lock, flags); + } +} + +static int apple_a4_block_busy(struct request_queue *q) +{ + int ret = (apple_a4_block_device.req == NULL) ? 0 : 1; + printk("apple_a4_block_busy: %d\n", ret); + return ret; +} + +static int apple_a4_block_getgeo(struct block_device* bdev, struct hd_geometry* geo) +{ +#ifdef YUSTAS_FIXME + long size = (NANDGeometry->pagesPerSuBlk * NANDGeometry->userSuBlksTotal) * (apple_a4_block_device.sectorSize >> SECTOR_SHIFT); + + geo->heads = 64; + geo->sectors = 32; + geo->cylinders = size / (geo->heads * geo->sectors); +#endif + + return 0; +} + +static int apple_a4_block_open(struct block_device* bdev, fmode_t mode) +{ + return 0; +} + +static int apple_a4_block_release(struct gendisk *disk, fmode_t mode) +{ +#ifdef YUSTAS_FIXME + ftl_sync(); +#endif + return 0; +} + +static int apple_a4_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) +{ + switch(cmd) + { + case BLKFLSBUF: +#ifdef YUSTAS_FIXME + ftl_sync(); +#endif + return 0; + default: + return -ENOTTY; + } +} + +static void apple_a4_block_request(struct request_queue* q) +{ + if(apple_a4_block_device.req) + { + //printk("not queueing request due to busy\n"); + return; + } + + apple_a4_block_device.req = blk_fetch_request(q); + + //printk("scheduling work: %p\n", apple_a4_block_device.req); + queue_work(ftl_wq, &ftl_workqueue); +} + +static struct block_device_operations apple_a4_block_fops = +{ + .owner = THIS_MODULE, + .getgeo = apple_a4_block_getgeo, + .open = apple_a4_block_open, + .release = apple_a4_block_release, + .ioctl = apple_a4_block_ioctl +}; + +static int apple_a4_block_probe(struct platform_device *pdev) +{ + int i; + + ftl_wq = create_workqueue("apple_a4_ftl_worker"); + + if (vfl_detect(&apple_a4_block_device.h2fmi_vfl_device)) { + printk("apple_a4-block: failed to open VFL\n"); + return -ENODEV; + } + if (ftl_detect(&apple_a4_block_device.h2fmi_ftl_device, + apple_a4_block_device.h2fmi_vfl_device)) { + printk("apple_a4-block: failed to open FTL\n"); + return -ENODEV; + } +#ifdef YUSTAS_FIXME + for(i = 0; i < 31; ++i) + { + if((1 << i) == NANDGeometry->bytesPerPage) + { + apple_a4_block_device.pageShift = i; + break; + } + } + + spin_lock_init(&apple_a4_block_device.lock); + + apple_a4_block_device.processing = false; + apple_a4_block_device.req = NULL; + + apple_a4_block_device.bounceBuffer = (u8*) kmalloc(NANDGeometry->pagesPerSuBlk * NANDGeometry->bytesPerPage, GFP_KERNEL | GFP_DMA); + if(!apple_a4_block_device.bounceBuffer) + return -EIO; + + apple_a4_block_device.sectorSize = NANDGeometry->bytesPerPage; + apple_a4_block_device.majorNum = register_blkdev(0, "nand"); + + apple_a4_block_device.gd = alloc_disk(5); + if(!apple_a4_block_device.gd) + goto out_unregister; + + apple_a4_block_device.gd->major = apple_a4_block_device.majorNum; + apple_a4_block_device.gd->first_minor = 0; + apple_a4_block_device.gd->fops = &apple_a4_block_fops; + apple_a4_block_device.gd->private_data = &apple_a4_block_device; + strcpy(apple_a4_block_device.gd->disk_name, "nand0"); + + apple_a4_block_device.queue = blk_init_queue(apple_a4_block_request, &apple_a4_block_device.lock); + if(!apple_a4_block_device.queue) + goto out_put_disk; + + blk_queue_lld_busy(apple_a4_block_device.queue, apple_a4_block_busy); + blk_queue_bounce_limit(apple_a4_block_device.queue, BLK_BOUNCE_ANY); + blk_queue_max_hw_sectors(apple_a4_block_device.queue, NANDGeometry->pagesPerSuBlk * (apple_a4_block_device.sectorSize >> SECTOR_SHIFT)); + blk_queue_max_segment_size(apple_a4_block_device.queue, NANDGeometry->pagesPerSuBlk * apple_a4_block_device.sectorSize); + blk_queue_physical_block_size(apple_a4_block_device.queue, apple_a4_block_device.sectorSize); + blk_queue_logical_block_size(apple_a4_block_device.queue, apple_a4_block_device.sectorSize); + apple_a4_block_device.gd->queue = apple_a4_block_device.queue; + + set_capacity(apple_a4_block_device.gd, (NANDGeometry->pagesPerSuBlk * NANDGeometry->userSuBlksTotal) * (apple_a4_block_device.sectorSize >> SECTOR_SHIFT)); + add_disk(apple_a4_block_device.gd); + +#endif + printk("apple_a4-block: block device registered with major num %d\n", apple_a4_block_device.majorNum); + + return 0; + +out_put_disk: + put_disk(apple_a4_block_device.gd); + +out_unregister: + unregister_blkdev(apple_a4_block_device.majorNum, "nand"); + kfree(apple_a4_block_device.bounceBuffer); + + return -ENOMEM; +} + +static int apple_a4_block_remove(struct platform_device *pdev) +{ + del_gendisk(apple_a4_block_device.gd); + put_disk(apple_a4_block_device.gd); + blk_cleanup_queue(apple_a4_block_device.queue); + unregister_blkdev(apple_a4_block_device.majorNum, "nand"); + flush_workqueue(ftl_wq); + kfree(apple_a4_block_device.bounceBuffer); +#ifdef YUSTAS_FIXME + ftl_sync(); +#endif + printk("apple_a4-block: block device unregistered\n"); + return 0; +} + +static void apple_a4_block_shutdown(struct platform_device *pdev) +{ +#ifdef YUSTAS_FIXME + ftl_sync(); +#endif +} + +static struct platform_driver apple_a4_block_driver = { + .probe = apple_a4_block_probe, + .remove = apple_a4_block_remove, + .suspend = NULL, /* optional but recommended */ + .resume = NULL, /* optional but recommended */ + .shutdown = apple_a4_block_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "apple_a4-block", + }, +}; + +static struct platform_device apple_a4_block_dev = { + .name = "apple_a4-block", + .id = -1, +}; + + /* + * Setup + */ + +static int __init apple_a4_block_init(void) +{ + int ret; + + ret = platform_driver_register(&apple_a4_block_driver); + + if (!ret) { + ret = platform_device_register(&apple_a4_block_dev); + + if (ret != 0) { + platform_driver_unregister(&apple_a4_block_driver); + } + } + + return ret; +} + +static void __exit apple_a4_block_exit(void) +{ + platform_device_unregister(&apple_a4_block_dev); + platform_driver_unregister(&apple_a4_block_driver); +} + +module_init(apple_a4_block_init); +module_exit(apple_a4_block_exit); + diff --git a/drivers/block/apple/h2fmi.c b/drivers/block/apple/h2fmi.c index 645b033bb7a..74677850324 100644 --- a/drivers/block/apple/h2fmi.c +++ b/drivers/block/apple/h2fmi.c @@ -151,7 +151,7 @@ struct h2fmi_geometry uint64_t chip_size; - u32 oob_size, oob_alloc_size, oobsize; + u32 oob_size, oob_alloc_size/*, oobsize*/; }; struct h2fmi_timing_setup @@ -209,13 +209,13 @@ struct h2fmi_state int num_chips; unsigned int bitmap; int chip_map[H2FMI_MAX_CHIPS]; - u8 *bbt[H2FMI_MAX_CHIPS]; +// u8 *bbt[H2FMI_MAX_CHIPS]; uint32_t timing; int ecc_step_shift; - unsigned whitening_disabled: 1; + int whitening; u32 page_fmt; u32 ecc_fmt; @@ -435,7 +435,7 @@ static void h2fmi_read(struct h2fmi_state *_state, void *_buff, size_t _sz) return; } - +#if 0 static void h2fmi_write(struct h2fmi_state *_state, const void *_buffer, size_t _sz) { const u8 *u8b; @@ -458,6 +458,7 @@ static void h2fmi_write(struct h2fmi_state *_state, const void *_buffer, size_t left |= (u8b[i] << (i*8)); writel(left, _state->base_regs + H2FMI_DATA0); } +#endif static inline void h2fmi_clear_ecc_sts(struct h2fmi_state *_state) { @@ -600,12 +601,14 @@ static int h2fmi_transfer_ready(struct h2fmi_state *_state) } } +#if 0 static irqreturn_t h2fmi_irq_handler(int _irq, void *_dev) { struct h2fmi_state *state = _dev; dev_info(&state->dev->dev, "irq!\n"); return IRQ_HANDLED; } +#endif static u8 h2fmi_calculate_ecc_bits(struct h2fmi_state *_state) { @@ -665,7 +668,7 @@ static int h2fmi_setup_timing(struct h2fmi_timing_setup *_timing, u8 *_buffer) _buffer[3] = var_28; _buffer[4] = h2fmi_round_down(smth, max(_timing->f + _timing->r, var_2C)); - //_buffer[2] = 2; // TODO: This is a hack because the above calculation is somehow broken. + _buffer[2] = 2; // TODO: This is a hack because the above calculation is somehow broken. return 0; } @@ -711,7 +714,7 @@ static void h2fmi_setup_aes(struct h2fmi_state *_state, int _enabled, int _encry else cdma_aes(_state->pdata->dma0, NULL); } - +/* static int h2fmi_is_block_bad(struct h2fmi_state *_state, int _ce, int _block) { @@ -733,7 +736,7 @@ static void h2fmi_mark_block_bad(struct h2fmi_state *_state, int _ce, if(_state->bbt[_ce]) _state->bbt[_ce][bb] |= (1 << bi); } - +*/ static int h2fmi_rw_large_page(struct h2fmi_state *_state) { int ret; @@ -997,6 +1000,8 @@ static int h2fmi_read_pages(struct h2fmi_state *_state, int _count, for(i = 0; i < _count; i++) { + u8 *ptr; + u32 *p; u8 *sg_ptr = sg_virt(sg); if(!sg_ptr) { @@ -1006,14 +1011,14 @@ static int h2fmi_read_pages(struct h2fmi_state *_state, int _count, panic("Not enough SGs for metadata! Not caught earlier!\n"); } - u8 *ptr = sg_ptr + sg_off; - u32 *p = (u32*)ptr; + ptr = sg_ptr + sg_off; + p = (u32*)ptr; if(sg->length - sg_off < _state->geo.oob_alloc_size) panic("SG too small for metadata whitening. %d-%d.\n", sg->length, sg_off); - if(!_state->whitening_disabled) - for(j = 0; j < 4; j++) + if(_state->whitening) + for(j = 0; j < _state->geo.oob_alloc_size >> 2; j++) p[i] ^= h2fmi_hash_table[(j + _pages[i]) % ARRAY_SIZE(h2fmi_hash_table)]; @@ -1023,14 +1028,15 @@ static int h2fmi_read_pages(struct h2fmi_state *_state, int _count, sg_off += _state->geo.oob_alloc_size; if(sg_off >= sg->length) { - if(count == 0 || !(sg = sg_next(sg))) + sg = sg_next(sg); + sg_off = 0; + count--; + if(count && !sg) { printk(KERN_ERR "h2fmi: not enough SGs for metadata!\n"); break; } - sg_off = 0; - count--; } } } @@ -1501,6 +1507,7 @@ static int h2fmi_erase_blocks(struct h2fmi_state *_state, return 0; } +#if 0 static int h2fmi_read_single_page(struct h2fmi_state *_state, u16 _ce, int _page, u8 *_buffer, int _raw) { @@ -1519,16 +1526,19 @@ static int h2fmi_read_single_page(struct h2fmi_state *_state, u16 _ce, int _page kfree(oobbuf); return ret; } +#endif // NAND interface implementation static int h2fmi_nand_read(struct apple_nand *_nd, int _count, u16 *_chips, page_t *_pages, struct scatterlist *_sg_data, size_t _sg_num_data, - struct scatterlist *_sg_oob, size_t _sg_num_oob) + struct scatterlist *_sg_oob, size_t _sg_num_oob, + int disable_aes) { struct h2fmi_state *state = container_of(_nd, struct h2fmi_state, nand); - h2fmi_setup_aes(state, 1, 0); // TODO: FTL-mode + int enable_aes = !disable_aes; + h2fmi_setup_aes(state, enable_aes, 0); // TODO: FTL-mode return h2fmi_read_pages(state, _count, _chips, _pages, _sg_data, _sg_num_data, _sg_oob, _sg_num_oob, NULL, NULL); } @@ -1557,8 +1567,8 @@ static int h2fmi_nand_get(struct apple_nand *_nd, int _info) switch(_info) { - case NAND_NUM_CE: - return state->geo.num_ce; +// case NAND_NUM_CE: +// return state->geo.num_ce; case NAND_BLOCKS_PER_CE: return state->geo.blocks_per_ce; @@ -1593,7 +1603,7 @@ static int h2fmi_nand_get(struct apple_nand *_nd, int _info) case NAND_PAGE_SIZE: return state->geo.bytes_per_page; - case NAND_OOB_SIZE: + case NAND_OOB_SIZE: /* ecc_bytes */ return state->geo.oob_size; case NAND_OOB_ALLOC: @@ -1619,6 +1629,13 @@ static int h2fmi_nand_set(struct apple_nand *_nd, int _info, int _val) } } +static int h2fmi_nand_set_whitening(struct apple_nand *_nd, int whitening) +{ + struct h2fmi_state *state = container_of(_nd, struct h2fmi_state, nand); + state->whitening = whitening; + return 0; +} +/* static int h2fmi_nand_is_bad(struct apple_nand *_nd, u16 _ce, page_t _page) { struct h2fmi_state *state = container_of(_nd, struct h2fmi_state, nand); @@ -1639,7 +1656,7 @@ static int h2fmi_scan_bbt(struct h2fmi_state *_state) for(i = 0; i < _state->num_chips; i++) { _state->bbt[i] = kzalloc(sz, GFP_KERNEL); - ret = apple_nand_special_page(&_state->nand, i, + ret = apple_nand_special_page(i, "DEVICEINFOBBT\0\0\0", _state->bbt[i], sz); if(ret) return ret; @@ -1647,7 +1664,7 @@ static int h2fmi_scan_bbt(struct h2fmi_state *_state) return 0; } - +*/ static int h2fmi_detect_nand(struct h2fmi_state *_state) { char goodbuf[8]; @@ -1735,9 +1752,9 @@ static int h2fmi_detect_nand(struct h2fmi_state *_state) _state->geo.bytes_per_spare = _state->chip_info->bytes_per_spare; _state->geo.banks_per_ce = _state->chip_info->banks_per_ce; _state->geo.blocks_per_bank = _state->geo.blocks_per_ce / _state->geo.banks_per_ce; - _state->geo.oobsize = _state->geo.bytes_per_spare; +// _state->geo.oobsize = _state->geo.bytes_per_spare; _state->geo.oob_alloc_size = 0xC; - _state->geo.oob_size = 0xA; + _state->geo.oob_size = 0xA; /* ecc_bytes */ _state->geo.chip_size = _state->geo.bytes_per_page * _state->geo.pages_per_block @@ -1818,9 +1835,9 @@ static int h2fmi_detect_nand(struct h2fmi_state *_state) writel(_state->timing, _state->flash_regs + H2FMI_TIMING); dev_info(&_state->dev->dev, "timing 0x%08x.\n", _state->timing); - ret = h2fmi_scan_bbt(_state); - if(ret) - return ret; +// ret = h2fmi_scan_bbt(_state); +// if(ret) +// return ret; return 0; } @@ -1840,6 +1857,7 @@ int h2fmi_probe(struct platform_device *_dev) state->dev = _dev; state->pdata = _dev->dev.platform_data; + state->whitening = 1; /* enable by default */ // Setup interface { @@ -1848,8 +1866,9 @@ int h2fmi_probe(struct platform_device *_dev) state->nand.erase = h2fmi_nand_erase; state->nand.get = h2fmi_nand_get; state->nand.set = h2fmi_nand_set; - state->nand.is_bad = h2fmi_nand_is_bad; - state->nand.set_bad = h2fmi_nand_set_bad; +// state->nand.is_bad = h2fmi_nand_is_bad; +// state->nand.set_bad = h2fmi_nand_set_bad; + state->nand.set_whitening = h2fmi_nand_set_whitening; } state->clk = clk_get(&_dev->dev, "fmi"); @@ -1933,6 +1952,9 @@ int h2fmi_probe(struct platform_device *_dev) platform_set_drvdata(_dev, state); ret = h2fmi_detect_nand(state); + if (!ret) + register_apple_nand(&state->nand); + goto done; err_ecc_regs: diff --git a/drivers/block/apple/vfl.c b/drivers/block/apple/vfl.c index e69de29bb2d..2620d1b9230 100644 --- a/drivers/block/apple/vfl.c +++ b/drivers/block/apple/vfl.c @@ -0,0 +1,171 @@ +/* + * vfl.c + * + * Copyright 2010 iDroid Project + * + * This file is part of iDroid. An android distribution for Apple products. + * For more information, please visit http://www.idroidproject.org/. + * + * 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 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include "vfl.h" +#include "vsvfl.h" + +void vfl_init(vfl_device_t *_vfl) +{ + memset(_vfl, 0, sizeof(*_vfl)); +} + +void vfl_cleanup(vfl_device_t *_vfl) +{ +} + +vfl_device_t *vfl_allocate(void) +{ + vfl_device_t *ret = kmalloc(sizeof(*ret), GFP_KERNEL); + vfl_init(ret); + return ret; +} + +error_t vfl_open(vfl_device_t *_vfl) +{ + if(!_vfl->open) + return -ENOENT; + + return _vfl->open(_vfl); +} + +void vfl_close(vfl_device_t *_vfl) +{ + if(!_vfl->close) + return; + + _vfl->close(_vfl); +} + +error_t vfl_read_single_page(vfl_device_t *_vfl, uint32_t _page, uint8_t* buffer, uint8_t* spare, + int empty_ok, int* refresh_page, uint32_t disable_aes) +{ + if(!_vfl->read_single_page) + return -ENOENT; + + return _vfl->read_single_page(_vfl, _page, buffer, spare, empty_ok, refresh_page, disable_aes); +} + +error_t vfl_write_single_page(vfl_device_t *_vfl, uint32_t _page, uint8_t* buffer, uint8_t* spare, + int _scrub) +{ + if(!_vfl->write_single_page) + return -ENOENT; + + return _vfl->write_single_page(_vfl, _page, buffer, spare, _scrub); +} + +error_t vfl_erase_single_block(vfl_device_t *_vfl, uint32_t _block, int _replace_bad_block) +{ + if(!_vfl->erase_single_block) + return -ENOENT; + + return _vfl->erase_single_block(_vfl, _block, _replace_bad_block); +} + +error_t vfl_write_context(vfl_device_t *_vfl, uint16_t *_control_block) +{ + if(!_vfl->write_context) + return -ENOENT; + + return _vfl->write_context(_vfl, _control_block); +} + +uint16_t *vfl_get_ftl_ctrl_block(vfl_device_t *_vfl) +{ + if(!_vfl->get_ftl_ctrl_block) { + return NULL; + } + + return _vfl->get_ftl_ctrl_block(_vfl); +} + +error_t vfl_get_info(vfl_device_t *_vfl, vfl_info_t _item, void *_result, size_t _sz) +{ + if(!_vfl->get_info) { + return -ENOENT; + } + + return _vfl->get_info(_vfl, _item, _result, _sz); +} + +uint32_t chipid_get_nand_epoch(void) +{ + uint32_t ret = (__raw_readl(VA_CHIPID) >> 9) & 0x7f; + return ret ? ret : 1; +} + +error_t vfl_detect(vfl_device_t **_vfl) +{ + uint8_t sigbuf[264]; + uint32_t flags; + + error_t ret; + + ret = apple_nand_special_page(0, "NANDDRIVERSIGN\0\0", sigbuf, sizeof(sigbuf)); + if(ret) + return ret; + + + // Starting from iOS5 there's a change in behaviour at chipid_get_nand_epich(). + if((!chipid_get_nand_epoch() && sigbuf[0] != '1' && sigbuf[0] != '2') || sigbuf[3] != 'C' + || sigbuf[1] > '1' || sigbuf[2] > '1' + || sigbuf[4] > 6) + { + printk(KERN_ERR "vfl: Incompatible signature.\n"); + return -ENOENT; + } + + flags = *(uint32_t*)&sigbuf[4]; + + if(sigbuf[1] == '1') + { + int whitening; + printk(KERN_INFO "vfl: Detected VSVFL.\n"); + *_vfl = &vfl_vsvfl_device_allocate()->vfl; + if(!*_vfl) + return -ENOENT; + + whitening = flags & 0x10000; + if(apple_nand_set_data_whitening(whitening) + && whitening) { + printk(KERN_ERR "vfl: Failed to enable data whitening!\n"); + return -ENOENT; + } + } + else if(sigbuf[1] == '0') + { + printk(KERN_ERR "vfl: Detected old-style VFL.\r\n"); + printk(KERN_ERR "vfl: Standard VFL support not included!\r\n"); + return -ENOENT; + } + else + { + printk(KERN_ERR "vfl: No valid VFL signature found!\r\n"); + return -ENOENT; + } + + return vfl_open(*_vfl); +} diff --git a/drivers/block/apple/vsvfl.c b/drivers/block/apple/vsvfl.c index e69de29bb2d..069ec0bc450 100644 --- a/drivers/block/apple/vsvfl.c +++ b/drivers/block/apple/vsvfl.c @@ -0,0 +1,1216 @@ +#include +#include +#include "yaftl/yaftl_common.h" +#include "vsvfl.h" + +typedef struct _vfl_vsvfl_context +{ + uint32_t usn_inc; // 0x000 + uint32_t usn_dec; // 0x004 + uint32_t ftl_type; // 0x008 + uint16_t usn_block; // 0x00C // current block idx + uint16_t usn_page; // 0x00E // used pages + uint16_t active_context_block; // 0x010 + uint16_t write_failure_count; // 0x012 + uint16_t bad_block_count; // 0x014 + uint8_t replaced_block_count[4]; // 0x016 + uint16_t num_reserved_blocks; // 0x01A + uint16_t field_1C; // 0x01C + uint16_t total_reserved_blocks; // 0x01E + uint8_t field_20[6]; // 0x020 + uint16_t reserved_block_pool_map[820]; // 0x026 + uint16_t vfl_context_block[4]; // 0x68E + uint16_t usable_blocks_per_bank; // 0x696 + uint16_t reserved_block_pool_start; // 0x698 + uint16_t control_block[3]; // 0x69A + uint16_t scrub_list_length; // 0x6A0 + uint16_t scrub_list[20]; // 0x6A2 + uint32_t field_6CA[4]; // 0x6CA + uint32_t vendor_type; // 0x6DA + uint8_t field_6DE[204]; // 0x6DE + uint16_t remapping_schedule_start; // 0x7AA + uint8_t unk3[0x48]; // 0x7AC + uint32_t version; // 0x7F4 + uint32_t checksum1; // 0x7F8 + uint32_t checksum2; // 0x7FC +} __attribute__((packed)) vfl_vsvfl_context_t; + +typedef struct _vfl_vsvfl_spare_data +{ + union + { + struct + { + uint32_t logicalPageNumber; + uint32_t usn; + } __attribute__ ((packed)) user; + + struct + { + uint32_t usnDec; + uint16_t idx; + uint8_t field_6; + uint8_t field_7; + } __attribute__ ((packed)) meta; + }; + + uint8_t type2; + uint8_t type1; + uint8_t eccMark; + uint8_t field_B; +} __attribute__ ((packed)) vfl_vsvfl_spare_data_t; + +static void virtual_to_physical_10001(vfl_vsvfl_device_t *_vfl, uint32_t _vBank, uint32_t _vPage, uint32_t *_pCE, uint32_t *_pPage) +{ + *_pCE = _vBank; + *_pPage = _vPage; +} + +static void physical_to_virtual_10001(vfl_vsvfl_device_t *_vfl, uint32_t _pCE, uint32_t _pPage, uint32_t *_vBank, uint32_t *_vPage) +{ + *_vBank = _pCE; + *_vPage = _pPage; +} + +static void virtual_to_physical_100014(vfl_vsvfl_device_t *_vfl, uint32_t _vBank, uint32_t _vPage, uint32_t *_pCE, uint32_t *_pPage) +{ + uint32_t pBank, pPage; + + pBank = _vBank / _vfl->geometry.num_ce; + pPage = ((_vfl->geometry.pages_per_block - 1) & _vPage) | (2 * (~(_vfl->geometry.pages_per_block - 1) & _vPage)); + if (pBank & 1) + pPage |= _vfl->geometry.pages_per_block; + + *_pCE = _vBank % _vfl->geometry.num_ce; + *_pPage = pPage; +} + +static void physical_to_virtual_100014(vfl_vsvfl_device_t *_vfl, uint32_t _pCE, uint32_t _pPage, uint32_t *_vBank, uint32_t *_vPage) +{ + uint32_t vBank, vPage; + vBank = _vfl->geometry.pages_per_block & _pPage; + vPage = ((_vfl->geometry.pages_per_block - 1) & _pPage) | (((_vfl->geometry.pages_per_block * -2) & _pPage) / 2); + if(vBank) + vBank = _vfl->geometry.num_ce; + + *_vBank = _pCE + vBank; + *_vPage = vPage; +} + +static void virtual_to_physical_150011(vfl_vsvfl_device_t *_vfl, uint32_t _vBank, uint32_t _vPage, uint32_t *_pCE, uint32_t *_pPage) +{ + uint32_t pBlock; + + pBlock = 2 * (_vPage / _vfl->geometry.pages_per_block); + if(_vBank % (2 * _vfl->geometry.num_ce) >= _vfl->geometry.num_ce) + pBlock++; + + *_pCE = _vBank % _vfl->geometry.num_ce; + *_pPage = (_vfl->geometry.pages_per_block * pBlock) | (_vPage % 128); +} + +static void physical_to_virtual_150011(vfl_vsvfl_device_t *_vfl, uint32_t _pCE, uint32_t _pPage, uint32_t *_vBank, uint32_t *_vPage) +{ + uint32_t pBlock; + + *_vBank = _pCE; + pBlock = _pPage / _vfl->geometry.pages_per_block; + if(pBlock % 2) { + pBlock--; + *_vBank = _vfl->geometry.num_ce + _pCE; + } + *_vPage = (_vfl->geometry.pages_per_block * (pBlock / 2)) | (_pPage % 128); +} + +static error_t virtual_block_to_physical_block(vfl_vsvfl_device_t *_vfl, uint32_t _vBank, uint32_t _vBlock, uint32_t *_pBlock) +{ + uint32_t pCE, pPage; + + if(!_vfl->virtual_to_physical) { + printk(KERN_ERR "vsvfl: virtual_to_physical hasn't been initialized yet!\r\n"); + return EINVAL; + } + + _vfl->virtual_to_physical(_vfl, _vBank, _vfl->geometry.pages_per_block * _vBlock, &pCE, &pPage); + *_pBlock = pPage / _vfl->geometry.pages_per_block; + + return 0; +} + +static error_t physical_block_to_virtual_block(vfl_vsvfl_device_t *_vfl, uint32_t _pBlock, uint32_t *_vBank, uint32_t *_vBlock) +{ + uint32_t vBank, vPage; + + if(!_vfl->physical_to_virtual) { + printk(KERN_ERR "vsvfl: physical_to_virtual hasn't been initialized yet!\r\n"); + return EINVAL; + } + + _vfl->physical_to_virtual(_vfl, 0, _vfl->geometry.pages_per_block * _pBlock, &vBank, &vPage); + *_vBank = vBank / _vfl->geometry.num_ce; + *_vBlock = vPage / _vfl->geometry.pages_per_block; + + return 0; +} + + +static int vfl_is_good_block(uint8_t* badBlockTable, uint32_t block) { + return (badBlockTable[block / 8] & (1 << (block % 8))) != 0; +} + +static uint32_t remap_block(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _block, uint32_t *_isGood) +{ + int pwDesPbn; + printk(KERN_DEBUG "vsvfl: remap_block: CE %d, block %d\r\n", _ce, _block); + + if(vfl_is_good_block(_vfl->bbt[_ce], _block)) + return _block; + + printk(KERN_DEBUG "vsvfl: remapping block...\r\n"); + + if(_isGood) + _isGood = 0; + + for(pwDesPbn = 0; pwDesPbn < _vfl->geometry.blocks_per_ce - _vfl->contexts[_ce].reserved_block_pool_start * _vfl->geometry.banks_per_ce; pwDesPbn++) + { + if(_vfl->contexts[_ce].reserved_block_pool_map[pwDesPbn] == _block) + { + uint32_t vBank, vBlock, pBlock; + + /* + if(pwDesPbn >= _vfl->geometry.blocks_per_ce) + printk(KERN_ERR "ftl: Destination physical block for remapping is greater than number of blocks per CE!"); + */ + + vBank = _ce + _vfl->geometry.num_ce * (pwDesPbn / (_vfl->geometry.blocks_per_bank_vfl - _vfl->contexts[_ce].reserved_block_pool_start)); + vBlock = _vfl->contexts[_ce].reserved_block_pool_start + (pwDesPbn % (_vfl->geometry.blocks_per_bank_vfl - _vfl->contexts[_ce].reserved_block_pool_start)); + + if(virtual_block_to_physical_block(_vfl, vBank, vBlock, &pBlock)) + printk(KERN_DEBUG "PANIC!!!: vfl: failed to convert virtual reserved block to physical\r\n"); + + return pBlock; + } + } + + printk(KERN_ERR "vfl: failed to remap CE %d block 0x%04x\r\n", _ce, _block); + return _block; +} + +static error_t virtual_page_number_to_physical(vfl_vsvfl_device_t *_vfl, uint32_t _vpNum, uint32_t* _ce, uint32_t* _page) { + uint32_t ce, vBank, ret, bank_offset, pBlock; + + vBank = _vpNum % _vfl->geometry.banks_total; + ce = vBank % _vfl->geometry.num_ce; + + ret = virtual_block_to_physical_block(_vfl, vBank, _vpNum / _vfl->geometry.pages_per_sublk, &pBlock); + + if(ret) + return ret; + + pBlock = remap_block(_vfl, ce, pBlock, 0); + + bank_offset = _vfl->geometry.bank_address_space * (pBlock / _vfl->geometry.blocks_per_bank); + + *_ce = ce; + *_page = _vfl->geometry.pages_per_block_2 * (bank_offset + (pBlock % _vfl->geometry.blocks_per_bank)) + + ((_vpNum % _vfl->geometry.pages_per_sublk) / _vfl->geometry.banks_total); + + return 0; +} + +static void vfl_checksum(void* data, int size, uint32_t* a, uint32_t* b) +{ + int i; + uint32_t* buffer = (uint32_t*) data; + uint32_t x = 0; + uint32_t y = 0; + for(i = 0; i < (size / 4); i++) { + x += buffer[i]; + y ^= buffer[i]; + } + + *a = x + 0xAABBCCDD; + *b = y ^ 0xAABBCCDD; +} + +static int vfl_gen_checksum(vfl_vsvfl_device_t *_vfl, int ce) +{ + vfl_checksum(&_vfl->contexts[ce], (uint32_t)&_vfl->contexts[ce].checksum1 - (uint32_t)&_vfl->contexts[ce], + &_vfl->contexts[ce].checksum1, &_vfl->contexts[ce].checksum2); + return 0; +} + +static int vfl_check_checksum(vfl_vsvfl_device_t *_vfl, int ce) +{ + static int counter = 0; + uint32_t checksum1; + uint32_t checksum2; + + counter++; + + vfl_checksum(&_vfl->contexts[ce], (uint32_t)&_vfl->contexts[ce].checksum1 - (uint32_t)&_vfl->contexts[ce], + &checksum1, &checksum2); + + // Yeah, this looks fail, but this is actually the logic they use + if(checksum1 == _vfl->contexts[ce].checksum1) + return 1; + + if(checksum2 != _vfl->contexts[ce].checksum2) + return 1; + + return 0; +} + +static error_t vsvfl_store_vfl_cxt(vfl_vsvfl_device_t *_vfl, uint32_t _ce); +static int is_block_in_scrub_list(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _block) { + uint32_t i; + + for (i = 0; i < _vfl->contexts[_ce].scrub_list_length; i++) { + if (_vfl->contexts[_ce].scrub_list[i] == _block) + return 1; + } + + return 0; +} + +static int add_block_to_scrub_list(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _block) { + if(is_block_in_scrub_list(_vfl, _ce, _block)) + return 0; + + if(_vfl->contexts[_ce].scrub_list_length > 0x13) { + printk(KERN_ERR "vfl: too many scrubs!\r\n"); + return 0; + } + + if(!vfl_check_checksum(_vfl, _ce)) + printk(KERN_DEBUG "PANIC!!!: vfl_add_block_to_scrub_list: failed checksum\r\n"); + + _vfl->contexts[_ce].scrub_list[_vfl->contexts[_ce].scrub_list_length++] = _block; + vfl_gen_checksum(_vfl, _ce); + return vsvfl_store_vfl_cxt(_vfl, _ce); +} + +static error_t vfl_vsvfl_write_single_page(vfl_device_t *_vfl, uint32_t dwVpn, uint8_t* buffer, uint8_t* spare, int _scrub) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + + uint32_t pCE = 0, pPage = 0; + int ret; + + ret = virtual_page_number_to_physical(vfl, dwVpn, &pCE, &pPage); + + if(ret) { + printk(KERN_ERR "vfl_vsvfl_write_single_page: virtual_page_number_to_physical returned an error (dwVpn %d)!\r\n", dwVpn); + return ret; + } + + ret = apple_nand_write_page(pCE, pPage, buffer, spare); + + if(ret) { + if(!vfl_check_checksum(vfl, pCE)) + printk(KERN_DEBUG "PANIC!!!: vfl_vsfl_write_single_page: failed checksum\r\n"); + + vfl->contexts[pCE].write_failure_count++; + vfl_gen_checksum(vfl, pCE); + + // TODO: add block map support + // vsvfl_mark_page_as_bad(pCE, pPage, ret); + + if(_scrub) + add_block_to_scrub_list(vfl, pCE, pPage / vfl->geometry.pages_per_block); // Something like that, I think + + return ret; + } + + return 0; +} + +static error_t vfl_vsvfl_read_single_page(vfl_device_t *_vfl, uint32_t dwVpn, uint8_t* buffer, uint8_t* spare, int empty_ok, int* refresh_page, uint32_t disable_aes) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + uint32_t pCE = 0, pPage = 0; + int ret; + + + if(refresh_page) + *refresh_page = 0; + + //VFLData1.field_8++; + //VFLData1.field_20++; + + ret = virtual_page_number_to_physical(vfl, dwVpn, &pCE, &pPage); + + if(ret) { + printk(KERN_ERR "vfl_vsvfl_read_single_page: virtual_page_number_to_physical returned an error (dwVpn %d)!\r\n", dwVpn); + return ret; + } + + // Hack to get reading by absolute page number. + ret = apple_nand_read_page(pCE, pPage, buffer, spare, disable_aes); + + if(!empty_ok && ret == ENOENT) + ret = EIO; + else if(empty_ok && ret == ENOENT) { + if(spare) + memset(spare, 0xFF, vfl->geometry.bytes_per_spare); + + return 1; + } + + if(ret == EINVAL || ret == EIO) + { + ret = apple_nand_read_page(pCE, pPage, buffer, spare, disable_aes); + if(!empty_ok && ret == ENOENT) + return EIO; + + if(ret == EINVAL || ret == EIO) + return ret; + } + + if(ret == ENOENT && spare) + memset(spare, 0xFF, vfl->geometry.bytes_per_spare); + + return ret; +} + +static error_t vsvfl_write_vfl_cxt_to_flash(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { + int fails = 0; + int i; + vfl_vsvfl_context_t *curVFLCxt; + uint32_t curPage; + uint8_t* pageBuffer; + uint8_t* spareBuffer; + + if(_ce >= _vfl->geometry.num_ce) + return EINVAL; + + if(!vfl_check_checksum(_vfl, _ce)) + printk(KERN_DEBUG "PANIC!!!: vsvfl_write_vfl_cxt_to_flash: failed checksum\r\n"); + +// posix_memalign(&pageBuffer, 0x40, _vfl->geometry.bytes_per_page); +// posix_memalign(&spareBuffer, 0x40, _vfl->geometry.bytes_per_spare); + pageBuffer = kzalloc(_vfl->geometry.bytes_per_page, GFP_KERNEL); + spareBuffer = kmalloc(_vfl->geometry.bytes_per_spare, GFP_KERNEL); + if(pageBuffer == NULL || spareBuffer == NULL) { + printk(KERN_ERR "vfl: cannot allocate page and spare buffer\r\n"); + return ENOMEM; + } +// memset(pageBuffer, 0x0, _vfl->geometry.bytes_per_page); + + curVFLCxt = &_vfl->contexts[_ce]; + curVFLCxt->usn_inc = _vfl->current_version++; + curPage = curVFLCxt->usn_page; + curVFLCxt->usn_page += 8; + curVFLCxt->usn_dec -= 1; + vfl_gen_checksum(_vfl, _ce); + + memcpy(pageBuffer, curVFLCxt, 0x800); + for (i = 0; i < 8; i++) { + uint32_t bankStart; + uint32_t blockOffset; + int status; + memset(spareBuffer, 0xFF, _vfl->geometry.bytes_per_spare); + ((uint32_t*)spareBuffer)[0] = curVFLCxt->usn_dec; + spareBuffer[8] = 0; + spareBuffer[9] = 0x80; + bankStart = (curVFLCxt->vfl_context_block[curVFLCxt->usn_block] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; + blockOffset = curVFLCxt->vfl_context_block[curVFLCxt->usn_block] % _vfl->geometry.blocks_per_bank; + status = apple_nand_write_page(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, pageBuffer, spareBuffer); + if(status) { + printk(KERN_ERR "vfl_write_vfl_cxt_to_flash: Failed write\r\n"); + kfree(pageBuffer); + kfree(spareBuffer); + // vsvfl_mark_page_as_bad(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, status); + return EIO; + } + } + for (i = 0; i < 8; i++) { + uint32_t bankStart = (curVFLCxt->vfl_context_block[curVFLCxt->usn_block] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; + uint32_t blockOffset = curVFLCxt->vfl_context_block[curVFLCxt->usn_block] % _vfl->geometry.blocks_per_bank; + if(apple_nand_read_page(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i, pageBuffer, spareBuffer, 0)) { + //vsvfl_store_block_map_single_page(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block_2 + curPage + i); + fails++; + continue; + } + if(memcmp(pageBuffer, curVFLCxt, 0x6E0) || ((uint32_t*)spareBuffer)[0] != curVFLCxt->usn_dec || spareBuffer[8] || spareBuffer[9] != 0x80) + fails++; + } + kfree(pageBuffer); + kfree(spareBuffer); + if(fails > 3) + return EIO; + else + return 0; +} + +static error_t vfl_vsvfl_write_context(vfl_device_t *_vfl, uint16_t *_control_block) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + uint32_t ce = vfl->current_version % vfl->geometry.num_ce; + uint32_t i; + + // check and update cxt of each CE + for(i = 0; i < vfl->geometry.num_ce; i++) { + if(vfl_check_checksum(vfl, i) == 0) + printk(KERN_DEBUG "PANIC!!!: vsvfl: VFLCxt has bad checksum.\r\n"); + memmove(vfl->contexts[i].control_block, _control_block, 6); + vfl_gen_checksum(vfl, i); + } + + // write cxt on the ce with the oldest cxt + if(vsvfl_store_vfl_cxt(vfl, ce)) { + printk(KERN_ERR "vsvfl: context write fail!\r\n"); + return EIO; + } + + return 0; +} + +static error_t vsvfl_store_vfl_cxt(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { + vfl_vsvfl_context_t *curVFLCxt; + if(_ce >= _vfl->geometry.num_ce) + printk(KERN_DEBUG "PANIC!!!: vfl: Can't store VFLCxt on non-existent CE\r\n"); + + curVFLCxt = &_vfl->contexts[_ce]; + if(curVFLCxt->usn_page + 8 > _vfl->geometry.pages_per_block || vsvfl_write_vfl_cxt_to_flash(_vfl, _ce)) { + int startBlock = curVFLCxt->usn_block; + int nextBlock = (curVFLCxt->usn_block + 1) % 4; + while(startBlock != nextBlock) { + if(curVFLCxt->vfl_context_block[nextBlock] != 0xFFFF) { + int fail = 0; + int i; + for (i = 0; i < 4; i++) { + uint32_t bankStart = (curVFLCxt->vfl_context_block[nextBlock] / _vfl->geometry.blocks_per_bank) * _vfl->geometry.bank_address_space; + uint32_t blockOffset = curVFLCxt->vfl_context_block[nextBlock] % _vfl->geometry.blocks_per_bank; + int status = apple_nand_erase_block(_ce, (bankStart + blockOffset) * _vfl->geometry.pages_per_block); + if(!status) + break; + //vsvfl_mark_bad_vfl_block(_vfl, _ce, curVFLCxt->vfl_context_block[nextBlock], status); + if(i == 3) + fail = 1; + } + if(!fail) { + int result; + if(!vfl_check_checksum(_vfl, _ce)) + printk(KERN_DEBUG "PANIC!!!: vsvfl_store_vfl_cxt: failed checksum\r\n"); + curVFLCxt->usn_block = nextBlock; + curVFLCxt->usn_page = 0; + vfl_gen_checksum(_vfl, _ce); + result = vsvfl_write_vfl_cxt_to_flash(_vfl, _ce); + if(!result) + return result; + } + } + nextBlock = (nextBlock + 1) % 4; + } + return EIO; + } + return 0; +} + +static error_t vsvfl_replace_bad_block(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _block){ + int i; + uint32_t vBank = 0, vBlock; + uint16_t drbc[16]; // dynamic replaced block count + vfl_vsvfl_context_t *curVFLCxt = &_vfl->contexts[_ce]; + uint32_t reserved_blocks = _vfl->geometry.blocks_per_ce - (curVFLCxt->reserved_block_pool_start * _vfl->geometry.banks_per_ce); + + _vfl->bbt[_ce][_block >> 3] &= ~(1 << (_block & 7)); + + for (i = 0; i < reserved_blocks; i++) { + uint32_t reserved_blocks_per_bank; + uint32_t bank; + uint32_t block_number; + uint32_t pBlock; + if(curVFLCxt->reserved_block_pool_map[i] != _block) + continue; + + reserved_blocks_per_bank = _vfl->geometry.blocks_per_bank - curVFLCxt->reserved_block_pool_start; + bank = _ce + _vfl->geometry.num_ce * (i / reserved_blocks_per_bank); + block_number = curVFLCxt->reserved_block_pool_start + (i % reserved_blocks_per_bank); + virtual_block_to_physical_block(_vfl, bank, block_number, &pBlock); + _vfl->bbt[_ce][pBlock] &= ~(1 << (pBlock & 7)); + } + + physical_block_to_virtual_block(_vfl, _block, &vBank, &vBlock); + while(curVFLCxt->replaced_block_count[vBank] < (_vfl->geometry.blocks_per_bank_vfl - curVFLCxt->reserved_block_pool_start)) { + uint32_t weirdBlock = curVFLCxt->replaced_block_count[vBank] + (_vfl->geometry.blocks_per_bank_vfl - curVFLCxt->reserved_block_pool_start) * vBank; + if(curVFLCxt->reserved_block_pool_map[weirdBlock] == 0xFFF0) { + curVFLCxt->reserved_block_pool_map[weirdBlock] = _block; + curVFLCxt->replaced_block_count[vBank]++; + return 0; + } + vBank++; + } + + for (i = 0; i < _vfl->geometry.banks_per_ce; i++) { + drbc[i] = i; + } + if(_vfl->geometry.banks_per_ce != 1) { + for(i = 0; i < (_vfl->geometry.banks_per_ce - 1); i++) { + int j; + for (j = i + 1; j < _vfl->geometry.banks_per_ce; j++) { + if(curVFLCxt->replaced_block_count[drbc[j]] < curVFLCxt->replaced_block_count[i]) { + drbc[j] = i; + drbc[i] = j; + } + } + } + } + for (i = 0; i < _vfl->geometry.banks_per_ce; i++) { + while(curVFLCxt->replaced_block_count[drbc[i]] < (_vfl->geometry.blocks_per_bank_vfl - curVFLCxt->reserved_block_pool_start)) { + uint32_t weirdBlock = curVFLCxt->replaced_block_count[drbc[i]] + (_vfl->geometry.blocks_per_bank_vfl - curVFLCxt->reserved_block_pool_start) * vBank; + if(curVFLCxt->reserved_block_pool_map[weirdBlock] == 0xFFF0) { + curVFLCxt->reserved_block_pool_map[weirdBlock] = _block; + curVFLCxt->replaced_block_count[drbc[i]]++; + return 0; + } + i++; + } + } + printk(KERN_DEBUG "PANIC!!!: vsvfl_replace_bad_block: Failed to replace block\r\n"); + return EIO; +} + +static error_t vfl_vsvfl_erase_single_block(vfl_device_t *_vfl, uint32_t _vbn, int _replaceBadBlock) { + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + uint32_t bank; + uint32_t status = EINVAL; + + // In order to erase a single virtual block, we have to erase the matching + // blocks across all banks. + for (bank = 0; bank < vfl->geometry.banks_total; bank++) { + uint32_t pBlock, pCE, blockRemapped; + + // Find the physical block before bad-block remapping. + virtual_block_to_physical_block(vfl, bank, _vbn, &pBlock); + pCE = bank % vfl->geometry.num_ce; + vfl->blockBuffer[bank] = pBlock; + + if (is_block_in_scrub_list(vfl, pCE, pBlock)) { + vfl_vsvfl_context_t *curVFLCxt; + vsvfl_replace_bad_block(vfl, pCE, pBlock); + vfl_gen_checksum(vfl, pCE); + curVFLCxt = &vfl->contexts[pCE]; + if(is_block_in_scrub_list(vfl, pCE, vfl->blockBuffer[bank])) { + int i; + for (i = 0; i < curVFLCxt->scrub_list_length; i++) { + if(curVFLCxt->scrub_list[i] != vfl->blockBuffer[bank]) + continue; + if(!vfl_check_checksum(vfl, pCE)) + printk(KERN_DEBUG "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); + curVFLCxt->scrub_list[i] = 0; + curVFLCxt->scrub_list_length--; + if(i != curVFLCxt->scrub_list_length && curVFLCxt->scrub_list_length != 0) + curVFLCxt->scrub_list[i] = curVFLCxt->scrub_list[curVFLCxt->scrub_list_length]; + vfl_gen_checksum(vfl, pCE); + vsvfl_store_vfl_cxt(vfl, pCE); + break; + } + } else + printk(KERN_ERR "vfl_erase_single_block: Failed checking for block in scrub list\r\n"); + } + + // Remap the block and calculate its physical number (considering bank address space). + blockRemapped = remap_block(vfl, pCE, pBlock, 0); + vfl->blockBuffer[bank] = blockRemapped % vfl->geometry.blocks_per_bank + + (blockRemapped / vfl->geometry.blocks_per_bank) * vfl->geometry.bank_address_space; + } + + // TODO: H2FMI erase multiple blocks. Currently we erase the blocks one by one. + // Actually, the block buffer is used for erase multiple blocks, so we won't use it here. + for (bank = 0; bank < vfl->geometry.banks_total; bank++) { + uint32_t pBlock, pCE, tries; + + virtual_block_to_physical_block(vfl, bank, _vbn, &pBlock); + pCE = bank % vfl->geometry.num_ce; + + // Try to erase each block at most 3 times. + for (tries = 0; tries < 3; tries++) { + uint32_t blockRemapped, bankStart, blockOffset; + + blockRemapped = remap_block(vfl, pCE, pBlock, 0); + bankStart = (blockRemapped / vfl->geometry.blocks_per_bank) * vfl->geometry.bank_address_space; + blockOffset = blockRemapped % vfl->geometry.blocks_per_bank; + + status = apple_nand_erase_block(pCE, (bankStart + blockOffset) * vfl->geometry.pages_per_block); + if (status == 0) + break; + + // TODO: add block map support. + //mark_bad_block(vfl, pCE, pBlock, status); + printk(KERN_ERR "vfl: failed erasing physical block %d on bank %d. status: 0x%08x\r\n", + blockRemapped, bank, status); + + if (!_replaceBadBlock) + return EINVAL; + + // Bad block management at erasing should actually be like this (improvised \o/) + vsvfl_replace_bad_block(vfl, pCE, bankStart + blockOffset); + if(!vfl_check_checksum(vfl, pCE)) + printk(KERN_DEBUG "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); + vfl->contexts[pCE].bad_block_count++; + vfl_gen_checksum(vfl, pCE); + vsvfl_store_vfl_cxt(vfl, pCE); + } + } + + if (status) + printk(KERN_DEBUG "PANIC!!!: vfl: failed to erase virtual block %d!\r\n", _vbn); + + return 0; +} + +static vfl_vsvfl_context_t* get_most_updated_context(vfl_vsvfl_device_t *vfl) { + int ce = 0; + int max = 0; + vfl_vsvfl_context_t* cxt = NULL; + + for(ce = 0; ce < vfl->geometry.num_ce; ce++) + { + int cur = vfl->contexts[ce].usn_inc; + if(max <= cur) + { + max = cur; + cxt = &vfl->contexts[ce]; + } + } + + return cxt; +} + +static uint16_t* VFL_get_FTLCtrlBlock(vfl_device_t *_vfl) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + + vfl_vsvfl_context_t *cxt = get_most_updated_context(vfl); + + if(cxt) + return cxt->control_block; + else + return NULL; +} + +static uint32_t next_power_of_two(uint32_t n) +{ + uint32_t val = 1 << (31 - __builtin_clz(n)); + if (n % val) + val *= 2; + return val; +} + +static inline error_t vfl_vsvfl_setup_geometry(vfl_vsvfl_device_t *_vfl) +{ +#define nand_load(what, where) _vfl->geometry.where = apple_nand_get_info(what) + +// uint32_t val = 1; + uint16_t z; + uint32_t mag = 1; + uint16_t a; + + nand_load(NAND_BLOCKS_PER_CE, blocks_per_ce); + nand_load(NAND_BLOCKS_PER_BANK, blocks_per_bank); + nand_load(NAND_BANKS_PER_CE, banks_per_ce); + nand_load(NAND_PAGE_SIZE, bytes_per_page); + nand_load(NAND_BANK_ADDRESS_SPACE, bank_address_space); + _vfl->geometry.num_ce = apple_nand_get_num_ce(); + nand_load(NAND_PAGES_PER_BLOCK, pages_per_block); + _vfl->geometry.pages_per_block_2 = next_power_of_two(_vfl->geometry.pages_per_block); +#ifdef YUSTAS_FIXME + nand_load(, ecc_bits); +#endif + + printk(KERN_DEBUG "blocks per ce: 0x%08x\r\n", _vfl->geometry.blocks_per_ce); + printk(KERN_DEBUG "blocks per bank: 0x%0x\r\n", _vfl->geometry.blocks_per_bank); + printk(KERN_DEBUG "banks per ce: 0x%08x\r\n", _vfl->geometry.banks_per_ce); + printk(KERN_DEBUG "bytes per page: 0x%08x\r\n", _vfl->geometry.bytes_per_page); + printk(KERN_DEBUG "bank address space: 0x%08x\r\n", _vfl->geometry.bank_address_space); + printk(KERN_DEBUG "num ce: 0x%08x\r\n", _vfl->geometry.num_ce); + printk(KERN_DEBUG "pages per block: 0x%08x\r\n", _vfl->geometry.pages_per_block); + printk(KERN_DEBUG "pages per block2: 0x%08x\r\n", _vfl->geometry.pages_per_block_2); +#ifdef YUSTAS_FIXME + printk(KERN_ERR "ecc bits: 0x%08x\r\n", _vfl->geometry.ecc_bits); +#endif + + z = _vfl->geometry.blocks_per_ce; + mag = 1; + while(z != 0 && mag < z) mag <<= 1; + mag >>= 10; + + a = (mag << 7) - (mag << 3) + mag; + _vfl->geometry.some_page_mask = a; + printk(KERN_DEBUG "some_page_mask: 0x%08x\r\n", _vfl->geometry.some_page_mask); + + _vfl->geometry.pages_total = z * _vfl->geometry.pages_per_block * _vfl->geometry.num_ce; + printk(KERN_DEBUG "pages_total: 0x%08x\r\n", _vfl->geometry.pages_total); + + _vfl->geometry.pages_per_sublk = _vfl->geometry.pages_per_block * _vfl->geometry.banks_per_ce * _vfl->geometry.num_ce; + printk(KERN_DEBUG "pages_per_sublk: 0x%08x\r\n", _vfl->geometry.pages_per_sublk); + + _vfl->geometry.some_sublk_mask = + _vfl->geometry.some_page_mask * _vfl->geometry.pages_per_sublk; + printk(KERN_DEBUG "some_sublk_mask: 0x%08x\r\n", _vfl->geometry.some_sublk_mask); + + _vfl->geometry.banks_total = _vfl->geometry.num_ce * _vfl->geometry.banks_per_ce; + printk(KERN_DEBUG "banks_total: 0x%08x\r\n", _vfl->geometry.banks_total); + +#ifdef YUSTAS_FIXME + ret = nand_load(NAND_OOB_SIZE, num_ecc_bytes); + if(ret) + return EINVAL; + + printk(KERN_ERR "num_ecc_bytes: 0x%08x\r\n", _vfl->geometry.num_ecc_bytes); +#endif + + nand_load(NAND_OOB_ALLOC, bytes_per_spare); + printk(KERN_DEBUG "bytes_per_spare: 0x%08x\r\n", _vfl->geometry.bytes_per_spare); + +#ifdef YUSTAS_FIXME + ret = nand_load(diReturnOne, one); + if(ret) + return EINVAL; + + printk(KERN_ERR "one: 0x%08x\r\n", _vfl->geometry.one); +#endif + + if(_vfl->geometry.num_ce != 1) + { + _vfl->geometry.some_crazy_val = _vfl->geometry.blocks_per_ce + - 27 - _vfl->geometry.reserved_blocks - _vfl->geometry.some_page_mask; + } + else + { + _vfl->geometry.some_crazy_val = _vfl->geometry.blocks_per_ce - 27 + - _vfl->geometry.some_page_mask - (_vfl->geometry.reserved_blocks & 0xFFFF); + } + + printk(KERN_DEBUG "some_crazy_val: 0x%08x\r\n", _vfl->geometry.some_crazy_val); + + _vfl->geometry.vfl_blocks = _vfl->geometry.some_crazy_val + 4; + printk(KERN_DEBUG "vfl_blocks: 0x%08x\r\n", _vfl->geometry.vfl_blocks); + + _vfl->geometry.fs_start_block = _vfl->geometry.vfl_blocks + _vfl->geometry.reserved_blocks; + printk(KERN_DEBUG "fs_start_block: 0x%08x\r\n", _vfl->geometry.fs_start_block); + +#ifdef YUSTAS_FIXME + nand->set(nand, diVendorType, val); + + val = 0x10001; + nand->set(nand, NAND_BANKS_PER_CE_VFL, val); +#endif + +#undef nand_load + + return 0; +} + +static error_t vfl_vsvfl_open(vfl_device_t *_vfl) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + uint32_t ce = 0; + uint32_t vendorType; + uint32_t banksPerCE; + uint32_t bank, i; + uint32_t num_reserved; + uint32_t num_non_reserved; + vfl_vsvfl_context_t *latestCxt; + int ret; + + ret = vfl_vsvfl_setup_geometry(vfl); + if(ret) + return ret; + + vfl->contexts = kzalloc(vfl->geometry.num_ce * sizeof(vfl_vsvfl_context_t), GFP_KERNEL); + + vfl->pageBuffer = (uint32_t*) kmalloc(vfl->geometry.pages_per_block * sizeof(uint32_t), GFP_KERNEL); + vfl->chipBuffer = (uint16_t*) kmalloc(vfl->geometry.pages_per_block * sizeof(uint16_t), GFP_KERNEL); + vfl->blockBuffer = (uint16_t*) kmalloc(vfl->geometry.banks_total * sizeof(uint16_t), GFP_KERNEL); + vfl->stats = kzalloc(sizeof(VSVFLStats), GFP_KERNEL); + + for(ce = 0; ce < vfl->geometry.num_ce; ce++) { + vfl_vsvfl_context_t *curVFLCxt; + uint8_t* pageBuffer; + uint8_t* spareBuffer; + int i; + int minUsn; + int VFLCxtIdx; + int page; + int last; + +// vfl->bbt[ce] = (uint8_t*) kmalloc(CEIL_DIVIDE(vfl->geometry.blocks_per_ce, 8), GFP_KERNEL); + vfl->bbt[ce] = (uint8_t*) kmalloc((vfl->geometry.blocks_per_ce + 7) / 8, GFP_KERNEL); + + printk(KERN_ERR "vsvfl: Checking CE %d.\r\n", ce); + + if(apple_nand_special_page(ce, "DEVICEINFOBBT\0\0\0", + vfl->bbt[ce], (vfl->geometry.blocks_per_ce + 7) / 8)) + { + printk(KERN_ERR "vsvfl: Failed to find DEVICEINFOBBT!\r\n"); + return EIO; + } + + if(ce >= vfl->geometry.num_ce) + return EIO; + + curVFLCxt = &vfl->contexts[ce]; + pageBuffer = kmalloc(vfl->geometry.bytes_per_page, GFP_KERNEL); + spareBuffer = kmalloc(vfl->geometry.bytes_per_spare, GFP_KERNEL); + if(pageBuffer == NULL || spareBuffer == NULL) { + printk(KERN_ERR "ftl: cannot allocate page and spare buffer\r\n"); + return ENOMEM; + } + + // Any VFLCxt page will contain an up-to-date list of all blocks used to store VFLCxt pages. Find any such + // page in the system area. + + for(i = vfl->geometry.reserved_blocks; i < vfl->geometry.fs_start_block; i++) { + // so pstBBTArea is a bit array of some sort + if(!(vfl->bbt[ce][i / 8] & (1 << (i & 0x7)))) + continue; + + if(!apple_nand_read_page(ce, i * vfl->geometry.pages_per_block, pageBuffer, spareBuffer, 0)) + { + /* + * YUSTAS_FIXME: type of VFL cxt is 0x91 + * Is that correct? + */ +// SpareData* spareData = (SpareData*) spareBuffer; +// if(spareData->field_8 != 0x94 && spareData->type == 0x91) { + memcpy(curVFLCxt->vfl_context_block, ((vfl_vsvfl_context_t*)pageBuffer)->vfl_context_block, + sizeof(curVFLCxt->vfl_context_block)); + break; +// } + } + } + +// print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_OFFSET, 32, +// 1, pageBuffer, vfl->geometry.bytes_per_page, true); +// print_hex_dump(KERN_INFO, "spare: ", DUMP_PREFIX_OFFSET, 32, +// 1, spareBuffer, vfl->geometry.bytes_per_spare, true); + if(i == vfl->geometry.fs_start_block) { + printk(KERN_ERR "vsvfl: cannot find readable VFLCxtBlock\r\n"); + kfree(pageBuffer); + kfree(spareBuffer); + return EIO; + } + + // Since VFLCxtBlock is a ringbuffer, if blockA.page0.spare.usnDec < blockB.page0.usnDec, then for any page a + // in blockA and any page b in blockB, a.spare.usNDec < b.spare.usnDec. Therefore, to begin finding the + // page/VFLCxt with the lowest usnDec, we should just look at the first page of each block in the ring. + minUsn = 0xFFFFFFFF; + VFLCxtIdx = 4; + for(i = 0; i < 4; i++) { + uint16_t block = curVFLCxt->vfl_context_block[i]; + vfl_vsvfl_spare_data_t *spareData; + if(block == 0xFFFF) + continue; + + if(apple_nand_read_page(ce, block * vfl->geometry.pages_per_block, pageBuffer, spareBuffer, 0)) + continue; + + spareData = (vfl_vsvfl_spare_data_t*)spareBuffer; + + if(spareData->meta.usnDec > 0 && spareData->meta.usnDec <= minUsn) { + minUsn = spareData->meta.usnDec; + VFLCxtIdx = i; + } + } + + if(VFLCxtIdx == 4) { + printk(KERN_ERR "vsvfl: cannot find readable VFLCxtBlock index in spares\r\n"); + kfree(pageBuffer); + kfree(spareBuffer); + return EIO; + } + + // VFLCxts are stored in the block such that they are duplicated 8 times. Therefore, we only need to + // read every 8th page, and nand_readvfl_cxt_page will try the 7 subsequent pages if the first was + // no good. The last non-blank page will have the lowest spare.usnDec and highest usnInc for VFLCxt + // in all the land (and is the newest). + page = 8; + last = 0; + for(page = 8; page < vfl->geometry.pages_per_block; page += 8) { + if(apple_nand_read_page(ce, curVFLCxt->vfl_context_block[VFLCxtIdx] * vfl->geometry.pages_per_block + page, pageBuffer, spareBuffer, 0) != 0) { + break; + } + + last = page; + } + + if(apple_nand_read_page(ce, curVFLCxt->vfl_context_block[VFLCxtIdx] * vfl->geometry.pages_per_block + last, pageBuffer, spareBuffer, 0) != 0) { + printk(KERN_ERR "vsvfl: cannot find readable VFLCxt\n"); + kfree(pageBuffer); + kfree(spareBuffer); + return -1; + } + + // Aha, so the upshot is that this finds the VFLCxt and copies it into vfl->contexts + memcpy(&vfl->contexts[ce], pageBuffer, sizeof(vfl_vsvfl_context_t)); + + // This is the newest VFLCxt across all CEs + if(curVFLCxt->usn_inc >= vfl->current_version) { + vfl->current_version = curVFLCxt->usn_inc; + } + + kfree(pageBuffer); + kfree(spareBuffer); + + // Verify the checksum + if(vfl_check_checksum(vfl, ce) == 0) + { + printk(KERN_ERR "vsvfl: VFLCxt has bad checksum.\r\n"); + return EIO; + } + } + + // retrieve some global parameters from the latest VFL across all CEs. + latestCxt = get_most_updated_context(vfl); + + // Then we update the VFLCxts on every ce with that information. + for(ce = 0; ce < vfl->geometry.num_ce; ce++) { + // Don't copy over own data. + if(&vfl->contexts[ce] != latestCxt) { + // Copy the data, and generate the new checksum. + memcpy(vfl->contexts[ce].control_block, latestCxt->control_block, sizeof(latestCxt->control_block)); + vfl->contexts[ce].usable_blocks_per_bank = latestCxt->usable_blocks_per_bank; + vfl->contexts[ce].reserved_block_pool_start = latestCxt->reserved_block_pool_start; + vfl->contexts[ce].ftl_type = latestCxt->ftl_type; + memcpy(vfl->contexts[ce].field_6CA, latestCxt->field_6CA, sizeof(latestCxt->field_6CA)); + + vfl_gen_checksum(vfl, ce); + } + } + + // Vendor-specific virtual-from/to-physical functions. + // Note: support for some vendors is still missing. + vendorType = vfl->contexts[0].vendor_type; + + if(!vendorType) { +#ifdef YUSTAS_FIXME + if(nand_device_get_info(_nand, diVendorType, &vendorType, sizeof(vendorType))) + return EIO; +#else + vendorType = 0x10001; +#endif + } + + switch(vendorType) { + case 0x10001: + vfl->geometry.banks_per_ce = 1; + vfl->virtual_to_physical = virtual_to_physical_10001; + vfl->physical_to_virtual = physical_to_virtual_10001; + break; + + case 0x100010: + case 0x100014: + case 0x120014: + vfl->geometry.banks_per_ce = 2; + vfl->virtual_to_physical = virtual_to_physical_100014; + vfl->physical_to_virtual = physical_to_virtual_100014; + break; + + case 0x150011: + vfl->geometry.banks_per_ce = 2; + vfl->virtual_to_physical = virtual_to_physical_150011; + vfl->physical_to_virtual = physical_to_virtual_150011; + break; + + default: + printk(KERN_ERR "vsvfl: unsupported vendor 0x%06x\r\n", vendorType); + return EIO; + } + +#ifdef YUSTAS_FIXME + if(nand_device_set_info(_nand, diVendorType, &vendorType, sizeof(vendorType))) + return EIO; +#endif + + vfl->geometry.pages_per_sublk = vfl->geometry.pages_per_block * vfl->geometry.banks_per_ce * vfl->geometry.num_ce; + vfl->geometry.banks_total = vfl->geometry.num_ce * vfl->geometry.banks_per_ce; + vfl->geometry.blocks_per_bank_vfl = vfl->geometry.blocks_per_ce / vfl->geometry.banks_per_ce; + + banksPerCE = vfl->geometry.banks_per_ce; +// _nand->set(_nand, NAND_BANKS_PER_CE_VFL, banksPerCE); + + printk(KERN_ERR "vsvfl: detected chip vendor 0x%06x\r\n", vendorType); + + // Now, discard the old scfg bad-block table, and set it using the VFL context's reserved block pool map. + num_reserved = vfl->contexts[0].reserved_block_pool_start; + num_non_reserved = vfl->geometry.blocks_per_bank_vfl - num_reserved; + + for(ce = 0; ce < vfl->geometry.num_ce; ce++) { +// memset(vfl->bbt[ce], 0xFF, CEIL_DIVIDE(vfl->geometry.blocks_per_ce, 8)); + memset(vfl->bbt[ce], 0xFF, (vfl->geometry.blocks_per_ce + 7) / 8); + + for(bank = 0; bank < banksPerCE; bank++) { + for(i = 0; i < num_non_reserved; i++) { + uint16_t mapEntry = vfl->contexts[ce].reserved_block_pool_map[bank * num_non_reserved + i]; + uint32_t pBlock; + + if(mapEntry == 0xFFF0) + continue; + + if(mapEntry < vfl->geometry.blocks_per_ce) { + pBlock = mapEntry; + } else if(mapEntry > 0xFFF0) { + virtual_block_to_physical_block(vfl, ce + bank * vfl->geometry.num_ce, num_reserved + i, &pBlock); + } else { + printk(KERN_DEBUG "PANIC!!!: vsvfl: bad map table: CE %d, entry %d, value 0x%08x\r\n", + ce, bank * num_non_reserved + i, mapEntry); + } + + vfl->bbt[ce][pBlock / 8] &= ~(1 << (pBlock % 8)); + } + } + } + + printk(KERN_ERR "vsvfl: VFL successfully opened!\r\n"); + + return 0; +} + +inline void auto_store(void *_ptr, size_t _sz, uint32_t _val) +{ + switch(_sz) + { + case 0: + return; + + case 1: + *((uint8_t*)_ptr) = _val; + return; + + case 2: + *((uint16_t*)_ptr) = _val; + return; + + case 4: + *((uint32_t*)_ptr) = _val; + return; + } +} + +static error_t vfl_vsvfl_get_info(vfl_device_t *_vfl, vfl_info_t _item, void *_result, size_t _sz) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + + if(_sz > 4 || _sz == 3) { + return EINVAL; + } + + switch(_item) { + case diPagesPerBlockTotalBanks: + auto_store(_result, _sz, vfl->geometry.pages_per_sublk); + return 0; + + case diSomeThingFromVFLCXT: + auto_store(_result, _sz, vfl->contexts[0].usable_blocks_per_bank); + return 0; + + case diFTLType: + auto_store(_result, _sz, vfl->contexts[0].ftl_type); + return 0; + + case diBytesPerPageFTL: + auto_store(_result, _sz, apple_nand_get_info(NAND_PAGE_SIZE)); + return 0; + + case diMetaBytes0xC: + auto_store(_result, _sz, 0xC); + return 0; + + case diUnkn20_1: + auto_store(_result, _sz, 1); + return 0; + + case diTotalBanks: + auto_store(_result, _sz, apple_nand_get_info(NAND_BANKS_PER_CE/*_VFL*/) + * apple_nand_get_num_ce()); + return 0; + + default: + return ENOENT; + } +} + +#if 0 +static struct apple_nand *vfl_vsvfl_get_device(vfl_device_t *_vfl) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + return vfl->device; +} +#endif +static void* *vfl_vsvfl_get_stats(vfl_device_t *_vfl, uint32_t *size) +{ + vfl_vsvfl_device_t *vfl = container_of(_vfl, vfl_vsvfl_device_t, vfl); + if(size) + *size = sizeof(VSVFLStats); + return (void*)vfl->stats; +} + +error_t vfl_vsvfl_device_init(vfl_vsvfl_device_t *_vfl) +{ + memset(_vfl, 0, sizeof(*_vfl)); + + //memset(&VFLData1, 0, sizeof(VFLData1)); + + _vfl->current_version = 0; + _vfl->vfl.open = vfl_vsvfl_open; + +// _vfl->vfl.get_device = vfl_vsvfl_get_device; + _vfl->vfl.get_stats = vfl_vsvfl_get_stats; + + _vfl->vfl.read_single_page = vfl_vsvfl_read_single_page; + + _vfl->vfl.write_single_page = vfl_vsvfl_write_single_page; + + _vfl->vfl.erase_single_block = vfl_vsvfl_erase_single_block; + + _vfl->vfl.write_context = vfl_vsvfl_write_context; + + _vfl->vfl.get_ftl_ctrl_block = VFL_get_FTLCtrlBlock; + + _vfl->vfl.get_info = vfl_vsvfl_get_info; + + memset(&_vfl->geometry, 0, sizeof(_vfl->geometry)); + +#if defined(CONFIG_CPU_S5L8930) && !defined(CONFIG_MACH_IPAD_1G) + _vfl->geometry.reserved_blocks = 16; +#else + _vfl->geometry.reserved_blocks = 1; +#endif + + return 0; +} + +void vfl_vsvfl_device_cleanup(vfl_vsvfl_device_t *_vfl) +{ + uint32_t i; + + if(_vfl->contexts) + kfree(_vfl->contexts); + + for (i = 0; i < sizeof(_vfl->bbt) / sizeof(void*); i++) { + if(_vfl->bbt[i]) + kfree(_vfl->bbt[i]); + } + + if(_vfl->pageBuffer) + kfree(_vfl->pageBuffer); + + if(_vfl->chipBuffer) + kfree(_vfl->chipBuffer); + + if(_vfl->blockBuffer) + kfree(_vfl->blockBuffer); + + memset(_vfl, 0, sizeof(*_vfl)); +} + +vfl_vsvfl_device_t *vfl_vsvfl_device_allocate() +{ + vfl_vsvfl_device_t *ret = kzalloc(sizeof(*ret), GFP_KERNEL); + vfl_vsvfl_device_init(ret); + return ret; +} + diff --git a/include/linux/apple_flash.h b/include/linux/apple_flash.h index d69fb130b92..89c9ab40b77 100644 --- a/include/linux/apple_flash.h +++ b/include/linux/apple_flash.h @@ -7,7 +7,7 @@ typedef uint32_t page_t; enum apple_nand_info { - NAND_NUM_CE, +/* NAND_NUM_CE,*/ NAND_BLOCKS_PER_CE, NAND_PAGES_PER_CE, NAND_PAGES_PER_BLOCK, @@ -29,7 +29,8 @@ struct apple_nand int (*read)(struct apple_nand *, int _count, u16 *_chips, page_t *_pages, struct scatterlist *_sg_data, size_t _sg_num_data, - struct scatterlist *_sg_oob, size_t _sg_num_oob); + struct scatterlist *_sg_oob, size_t _sg_num_oob, + int disable_aes); int (*write)(struct apple_nand *, int _count, u16 *_chips, page_t *_pages, @@ -42,20 +43,25 @@ struct apple_nand int (*get)(struct apple_nand *, int _info); int (*set)(struct apple_nand *, int _info, int _val); - int (*is_bad)(struct apple_nand*, u16 _ce, page_t _page); - void (*set_bad)(struct apple_nand*, u16 _ce, page_t _page); +// int (*is_bad)(struct apple_nand*, u16 _ce, page_t _page); +// void (*set_bad)(struct apple_nand*, u16 _ce, page_t _page); + + int (*set_whitening)(struct apple_nand *, int whitening); }; int register_apple_nand(struct apple_nand*); void remove_apple_nand(struct apple_nand*); -int apple_nand_special_page(struct apple_nand*, u16 _ce, char _page[16], +int apple_nand_set_data_whitening(int whitening); +int apple_nand_get_num_ce(void); +int apple_nand_get_info(int info); +int apple_nand_special_page(u16 _ce, char _page[16], uint8_t* _buffer, size_t _amt); -int apple_nand_read_page(struct apple_nand*, u16 _ce, page_t _page, - uint8_t *_data, uint8_t *_oob); -int apple_nand_write_page(struct apple_nand*, u16 _ce, page_t _page, +int apple_nand_read_page(u16 _ce, page_t _page, + uint8_t *_data, uint8_t *_oob, int disable_aes); +int apple_nand_write_page(u16 _ce, page_t _page, const uint8_t *_data, const uint8_t *_oob); -int apple_nand_erase_block(struct apple_nand*, u16 _ce, page_t _page); +int apple_nand_erase_block(u16 _ce, page_t _page); struct apple_vfl { From 81e573aad9fa7b3f56bf6efc31e4ed65d131711c Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sat, 5 May 2012 05:45:59 +0400 Subject: [PATCH 09/11] Some fixup. Ecc checking still failed :( --- drivers/block/apple/api.c | 40 +++++++++++++---- drivers/block/apple/h2fmi.c | 21 ++++++--- drivers/block/apple/vsvfl.c | 55 ++++++++++------------- drivers/block/apple/yaftl/l2v.c | 34 +++++++------- drivers/block/apple/yaftl/yaftl.c | 38 ++++++++-------- drivers/block/apple/yaftl/yaftl_common.c | 18 ++++---- drivers/block/apple/yaftl/yaftl_gc.c | 56 ++++++++++++------------ drivers/block/apple/yaftl/yaftl_mem.c | 9 ++-- include/linux/apple_flash.h | 5 ++- 9 files changed, 153 insertions(+), 123 deletions(-) diff --git a/drivers/block/apple/api.c b/drivers/block/apple/api.c index e6e8b95d42a..4043764ca33 100644 --- a/drivers/block/apple/api.c +++ b/drivers/block/apple/api.c @@ -8,6 +8,8 @@ #define TOTAL_CHIPS_COUNT 2 struct apple_nand *chips[TOTAL_CHIPS_COUNT]; int total_chips = 0; +int banks_per_ce_vfl; +int vendor_type; struct apple_nand* get_apple_nand(u16 ce) { @@ -47,19 +49,40 @@ void remove_apple_nand(struct apple_nand *_nd) } EXPORT_SYMBOL_GPL(remove_apple_nand); -int apple_nand_get_num_ce(void) -{ - return total_chips; -} -EXPORT_SYMBOL_GPL(apple_nand_get_num_ce); - int apple_nand_get_info(int info) { struct apple_nand *nand = get_apple_nand(0); - return nand->get(nand, info); + + switch(info) + { + case NAND_NUM_CE: + return total_chips; + case NAND_BANKS_PER_CE_VFL: + return banks_per_ce_vfl; + case NAND_VENDOR_TYPE: + return vendor_type; + default: + return nand->get(nand, info); + } } EXPORT_SYMBOL_GPL(apple_nand_get_info); +int apple_nand_set_info(int info, int val) +{ + switch(info) + { + case NAND_BANKS_PER_CE_VFL: + banks_per_ce_vfl = val; + return 0; + case NAND_VENDOR_TYPE: + vendor_type = val; + return 0; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(apple_nand_set_info); + int apple_nand_special_page(u16 _ce, char _page[16], uint8_t* _buffer, size_t _amt) { @@ -156,10 +179,11 @@ EXPORT_SYMBOL_GPL(apple_nand_write_page); int apple_nand_erase_block(u16 _ce, page_t _page) { struct apple_nand *_nd = get_apple_nand(_ce); + u16 chip = 0; if(!_nd->erase) return -EPERM; - return _nd->erase(_nd, 1, &_ce, &_page); + return _nd->erase(_nd, 1, &chip, &_page); } EXPORT_SYMBOL_GPL(apple_nand_erase_block); diff --git a/drivers/block/apple/h2fmi.c b/drivers/block/apple/h2fmi.c index 74677850324..22c71e5edf3 100644 --- a/drivers/block/apple/h2fmi.c +++ b/drivers/block/apple/h2fmi.c @@ -947,8 +947,10 @@ static int h2fmi_read_pages(struct h2fmi_state *_state, int _count, { int i, j; - if(_state->state != H2FMI_IDLE) + if(_state->state != H2FMI_IDLE) { + printk(KERN_ERR "%s:%d Implement waiting\n", __FUNCTION__, __LINE__); return -EBUSY; + } memset(&_state->transaction, 0, sizeof(_state->transaction)); @@ -1041,17 +1043,25 @@ static int h2fmi_read_pages(struct h2fmi_state *_state, int _count, } } - if(_state->transaction.result) + if(_state->transaction.result) { + printk(KERN_ERR "%s:%d not success!\n", __FUNCTION__, __LINE__); return _state->transaction.result; + } - if(_state->transaction.num_failed) + if(_state->transaction.num_failed) { + printk(KERN_ERR "%s:%d not success!\n", __FUNCTION__, __LINE__); return -EIO; + } - if(_state->transaction.num_ecc) + if(_state->transaction.num_ecc) { + printk(KERN_ERR "%s:%d not success!\n", __FUNCTION__, __LINE__); return -EUCLEAN; + } - if(_state->transaction.num_empty) + if(_state->transaction.num_empty) { + printk(KERN_ERR "%s:%d not success!\n", __FUNCTION__, __LINE__); return -ENOENT; + } return 0; } @@ -1804,6 +1814,7 @@ static int h2fmi_detect_nand(struct h2fmi_state *_state) return PTR_ERR(clk); timing_setup.freq = clk_get_rate(clk); + printk(KERN_ERR "freq: %lld\n", timing_setup.freq); clk_put(clk); diff --git a/drivers/block/apple/vsvfl.c b/drivers/block/apple/vsvfl.c index 069ec0bc450..37f594c1d77 100644 --- a/drivers/block/apple/vsvfl.c +++ b/drivers/block/apple/vsvfl.c @@ -186,7 +186,7 @@ static uint32_t remap_block(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint32_t _bl vBlock = _vfl->contexts[_ce].reserved_block_pool_start + (pwDesPbn % (_vfl->geometry.blocks_per_bank_vfl - _vfl->contexts[_ce].reserved_block_pool_start)); if(virtual_block_to_physical_block(_vfl, vBank, vBlock, &pBlock)) - printk(KERN_DEBUG "PANIC!!!: vfl: failed to convert virtual reserved block to physical\r\n"); + printk(KERN_ERR "PANIC!!!: vfl: failed to convert virtual reserved block to physical\r\n"); return pBlock; } @@ -283,7 +283,7 @@ static int add_block_to_scrub_list(vfl_vsvfl_device_t *_vfl, uint32_t _ce, uint3 } if(!vfl_check_checksum(_vfl, _ce)) - printk(KERN_DEBUG "PANIC!!!: vfl_add_block_to_scrub_list: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vfl_add_block_to_scrub_list: failed checksum\r\n"); _vfl->contexts[_ce].scrub_list[_vfl->contexts[_ce].scrub_list_length++] = _block; vfl_gen_checksum(_vfl, _ce); @@ -308,7 +308,7 @@ static error_t vfl_vsvfl_write_single_page(vfl_device_t *_vfl, uint32_t dwVpn, u if(ret) { if(!vfl_check_checksum(vfl, pCE)) - printk(KERN_DEBUG "PANIC!!!: vfl_vsfl_write_single_page: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vfl_vsfl_write_single_page: failed checksum\r\n"); vfl->contexts[pCE].write_failure_count++; vfl_gen_checksum(vfl, pCE); @@ -385,7 +385,7 @@ static error_t vsvfl_write_vfl_cxt_to_flash(vfl_vsvfl_device_t *_vfl, uint32_t _ return EINVAL; if(!vfl_check_checksum(_vfl, _ce)) - printk(KERN_DEBUG "PANIC!!!: vsvfl_write_vfl_cxt_to_flash: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vsvfl_write_vfl_cxt_to_flash: failed checksum\r\n"); // posix_memalign(&pageBuffer, 0x40, _vfl->geometry.bytes_per_page); // posix_memalign(&spareBuffer, 0x40, _vfl->geometry.bytes_per_spare); @@ -452,7 +452,7 @@ static error_t vfl_vsvfl_write_context(vfl_device_t *_vfl, uint16_t *_control_bl // check and update cxt of each CE for(i = 0; i < vfl->geometry.num_ce; i++) { if(vfl_check_checksum(vfl, i) == 0) - printk(KERN_DEBUG "PANIC!!!: vsvfl: VFLCxt has bad checksum.\r\n"); + printk(KERN_ERR "PANIC!!!: vsvfl: VFLCxt has bad checksum.\r\n"); memmove(vfl->contexts[i].control_block, _control_block, 6); vfl_gen_checksum(vfl, i); } @@ -469,7 +469,7 @@ static error_t vfl_vsvfl_write_context(vfl_device_t *_vfl, uint16_t *_control_bl static error_t vsvfl_store_vfl_cxt(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { vfl_vsvfl_context_t *curVFLCxt; if(_ce >= _vfl->geometry.num_ce) - printk(KERN_DEBUG "PANIC!!!: vfl: Can't store VFLCxt on non-existent CE\r\n"); + printk(KERN_ERR "PANIC!!!: vfl: Can't store VFLCxt on non-existent CE\r\n"); curVFLCxt = &_vfl->contexts[_ce]; if(curVFLCxt->usn_page + 8 > _vfl->geometry.pages_per_block || vsvfl_write_vfl_cxt_to_flash(_vfl, _ce)) { @@ -492,7 +492,7 @@ static error_t vsvfl_store_vfl_cxt(vfl_vsvfl_device_t *_vfl, uint32_t _ce) { if(!fail) { int result; if(!vfl_check_checksum(_vfl, _ce)) - printk(KERN_DEBUG "PANIC!!!: vsvfl_store_vfl_cxt: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vsvfl_store_vfl_cxt: failed checksum\r\n"); curVFLCxt->usn_block = nextBlock; curVFLCxt->usn_page = 0; vfl_gen_checksum(_vfl, _ce); @@ -568,7 +568,7 @@ static error_t vsvfl_replace_bad_block(vfl_vsvfl_device_t *_vfl, uint32_t _ce, u i++; } } - printk(KERN_DEBUG "PANIC!!!: vsvfl_replace_bad_block: Failed to replace block\r\n"); + printk(KERN_ERR "PANIC!!!: vsvfl_replace_bad_block: Failed to replace block\r\n"); return EIO; } @@ -598,7 +598,7 @@ static error_t vfl_vsvfl_erase_single_block(vfl_device_t *_vfl, uint32_t _vbn, i if(curVFLCxt->scrub_list[i] != vfl->blockBuffer[bank]) continue; if(!vfl_check_checksum(vfl, pCE)) - printk(KERN_DEBUG "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); curVFLCxt->scrub_list[i] = 0; curVFLCxt->scrub_list_length--; if(i != curVFLCxt->scrub_list_length && curVFLCxt->scrub_list_length != 0) @@ -648,7 +648,7 @@ static error_t vfl_vsvfl_erase_single_block(vfl_device_t *_vfl, uint32_t _vbn, i // Bad block management at erasing should actually be like this (improvised \o/) vsvfl_replace_bad_block(vfl, pCE, bankStart + blockOffset); if(!vfl_check_checksum(vfl, pCE)) - printk(KERN_DEBUG "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); + printk(KERN_ERR "PANIC!!!: vfl_erase_single_block: failed checksum\r\n"); vfl->contexts[pCE].bad_block_count++; vfl_gen_checksum(vfl, pCE); vsvfl_store_vfl_cxt(vfl, pCE); @@ -656,7 +656,7 @@ static error_t vfl_vsvfl_erase_single_block(vfl_device_t *_vfl, uint32_t _vbn, i } if (status) - printk(KERN_DEBUG "PANIC!!!: vfl: failed to erase virtual block %d!\r\n", _vbn); + printk(KERN_ERR "PANIC!!!: vfl: failed to erase virtual block %d!\r\n", _vbn); return 0; } @@ -703,7 +703,6 @@ static inline error_t vfl_vsvfl_setup_geometry(vfl_vsvfl_device_t *_vfl) { #define nand_load(what, where) _vfl->geometry.where = apple_nand_get_info(what) -// uint32_t val = 1; uint16_t z; uint32_t mag = 1; uint16_t a; @@ -713,7 +712,7 @@ static inline error_t vfl_vsvfl_setup_geometry(vfl_vsvfl_device_t *_vfl) nand_load(NAND_BANKS_PER_CE, banks_per_ce); nand_load(NAND_PAGE_SIZE, bytes_per_page); nand_load(NAND_BANK_ADDRESS_SPACE, bank_address_space); - _vfl->geometry.num_ce = apple_nand_get_num_ce(); + nand_load(NAND_NUM_CE, num_ce); nand_load(NAND_PAGES_PER_BLOCK, pages_per_block); _vfl->geometry.pages_per_block_2 = next_power_of_two(_vfl->geometry.pages_per_block); #ifdef YUSTAS_FIXME @@ -792,12 +791,9 @@ static inline error_t vfl_vsvfl_setup_geometry(vfl_vsvfl_device_t *_vfl) _vfl->geometry.fs_start_block = _vfl->geometry.vfl_blocks + _vfl->geometry.reserved_blocks; printk(KERN_DEBUG "fs_start_block: 0x%08x\r\n", _vfl->geometry.fs_start_block); -#ifdef YUSTAS_FIXME - nand->set(nand, diVendorType, val); - - val = 0x10001; - nand->set(nand, NAND_BANKS_PER_CE_VFL, val); -#endif + /* Set default values */ + apple_nand_set_info(NAND_VENDOR_TYPE, 0x10001); + apple_nand_set_info(NAND_BANKS_PER_CE_VFL, 1); #undef nand_load @@ -868,8 +864,7 @@ static error_t vfl_vsvfl_open(vfl_device_t *_vfl) if(!(vfl->bbt[ce][i / 8] & (1 << (i & 0x7)))) continue; - if(!apple_nand_read_page(ce, i * vfl->geometry.pages_per_block, pageBuffer, spareBuffer, 0)) - { + if(! apple_nand_read_page(ce, i * vfl->geometry.pages_per_block, pageBuffer, spareBuffer, 0)) { /* * YUSTAS_FIXME: type of VFL cxt is 0x91 * Is that correct? @@ -881,6 +876,7 @@ static error_t vfl_vsvfl_open(vfl_device_t *_vfl) break; // } } + } // print_hex_dump(KERN_INFO, "data: ", DUMP_PREFIX_OFFSET, 32, @@ -986,12 +982,9 @@ static error_t vfl_vsvfl_open(vfl_device_t *_vfl) vendorType = vfl->contexts[0].vendor_type; if(!vendorType) { -#ifdef YUSTAS_FIXME - if(nand_device_get_info(_nand, diVendorType, &vendorType, sizeof(vendorType))) - return EIO; -#else - vendorType = 0x10001; -#endif + vendorType = apple_nand_get_info(NAND_VENDOR_TYPE); + if (vendorType < 0) + return -EIO; } switch(vendorType) { @@ -1030,7 +1023,7 @@ static error_t vfl_vsvfl_open(vfl_device_t *_vfl) vfl->geometry.blocks_per_bank_vfl = vfl->geometry.blocks_per_ce / vfl->geometry.banks_per_ce; banksPerCE = vfl->geometry.banks_per_ce; -// _nand->set(_nand, NAND_BANKS_PER_CE_VFL, banksPerCE); + apple_nand_set_info(NAND_BANKS_PER_CE_VFL, banksPerCE); printk(KERN_ERR "vsvfl: detected chip vendor 0x%06x\r\n", vendorType); @@ -1055,7 +1048,7 @@ static error_t vfl_vsvfl_open(vfl_device_t *_vfl) } else if(mapEntry > 0xFFF0) { virtual_block_to_physical_block(vfl, ce + bank * vfl->geometry.num_ce, num_reserved + i, &pBlock); } else { - printk(KERN_DEBUG "PANIC!!!: vsvfl: bad map table: CE %d, entry %d, value 0x%08x\r\n", + printk(KERN_ERR "PANIC!!!: vsvfl: bad map table: CE %d, entry %d, value 0x%08x\r\n", ce, bank * num_non_reserved + i, mapEntry); } @@ -1124,8 +1117,8 @@ static error_t vfl_vsvfl_get_info(vfl_device_t *_vfl, vfl_info_t _item, void *_r return 0; case diTotalBanks: - auto_store(_result, _sz, apple_nand_get_info(NAND_BANKS_PER_CE/*_VFL*/) - * apple_nand_get_num_ce()); + auto_store(_result, _sz, apple_nand_get_info(NAND_BANKS_PER_CE_VFL) + * apple_nand_get_info(NAND_NUM_CE)); return 0; default: diff --git a/drivers/block/apple/yaftl/l2v.c b/drivers/block/apple/yaftl/l2v.c index 4668bed21e8..34369640180 100644 --- a/drivers/block/apple/yaftl/l2v.c +++ b/drivers/block/apple/yaftl/l2v.c @@ -11,7 +11,7 @@ error_t L2V_Init(uint32_t totalPages, uint32_t numBlocks, uint32_t pagesPerSublk int32_t i; if (numBlocks * pagesPerSublk > 0x1FF0001) - printk(KERN_DEBUG "PANIC!!!: L2V: Tree bitspace not sufficient for geometry %dx%d\r\n", numBlocks, pagesPerSublk); + printk(KERN_ERR "PANIC!!!: L2V: Tree bitspace not sufficient for geometry %dx%d\r\n", numBlocks, pagesPerSublk); L2V.numBlocks = numBlocks; L2V.pagesPerSublk = pagesPerSublk; @@ -20,15 +20,15 @@ error_t L2V_Init(uint32_t totalPages, uint32_t numBlocks, uint32_t pagesPerSublk L2V.Tree = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); if (!L2V.Tree) - printk(KERN_DEBUG "PANIC!!!: L2V: tree allocation has failed\r\n"); + printk(KERN_ERR "PANIC!!!: L2V: tree allocation has failed\r\n"); L2V.TreeNodes = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); if (!L2V.TreeNodes) - printk(KERN_DEBUG "PANIC!!!: L2V: tree nodes allocation has failed\r\n"); + printk(KERN_ERR "PANIC!!!: L2V: tree nodes allocation has failed\r\n"); L2V.UpdatesSinceRepack = (uint32_t*)yaftl_alloc(L2V.numRoots * sizeof(uint32_t)); if (!L2V.UpdatesSinceRepack) - printk(KERN_DEBUG "PANIC!!!: L2V: updates since repack allocation has failed\r\n"); + printk(KERN_ERR "PANIC!!!: L2V: updates since repack allocation has failed\r\n"); for (i = 0; i < L2V.numRoots; ++i) L2V.Tree[i] = 0x7FC000Cu; @@ -134,7 +134,7 @@ void L2V_UpdateFromTOC(uint32_t _tocIdx, uint32_t* _tocBuffer) static void L2V_SetFirstNode(L2VNode* node) { if (node < &L2V.Pool[0] || node > &L2V.Pool[0x10000]) - printk(KERN_DEBUG "PANIC!!!: L2V: new first node must be in pool range\r\n"); + printk(KERN_ERR "PANIC!!!: L2V: new first node must be in pool range\r\n"); node->next = L2V.firstNode; L2V.firstNode = node; @@ -145,7 +145,7 @@ L2VNode* L2V_EraseFirstNode(void) { L2VNode* node; if (!L2V.firstNode) - printk(KERN_DEBUG "PANIC!!!: L2V: currentNode not set\r\n"); + printk(KERN_ERR "PANIC!!!: L2V: currentNode not set\r\n"); node = L2V.firstNode; L2V.firstNode = node->next; @@ -195,11 +195,11 @@ void L2V_Search(GCReadC* _c) span += *(uint16_t*)((uint8_t*)&_c->node->next + _c->nodeSize) << bits; // high bits for span if ((_c->next_nOfs) > (_c->nodeSize - 6)) //sizeof(lPtr_t) = 6 - printk(KERN_DEBUG "PANIC!!!: _c->next_nOfs) <= _c->nodeSize - sizeof(lPtr_t)"); + printk(KERN_ERR "PANIC!!!: _c->next_nOfs) <= _c->nodeSize - sizeof(lPtr_t)"); } if (span == 0) - printk(KERN_DEBUG "PANIC!!!: L2V: span mustn't be zero!"); + printk(KERN_ERR "PANIC!!!: L2V: span mustn't be zero!"); if (!setting) { @@ -222,7 +222,7 @@ void L2V_Search(GCReadC* _c) uint32_t targTofs = pageNum % 0x8000; uint32_t targTree = pageNum / 0x8000; // root number if(L2V.numRoots <= targTree) - printk(KERN_DEBUG "PANIC!!!: L2V: targTree has to be less than L2V.numRoots"); + printk(KERN_ERR "PANIC!!!: L2V: targTree has to be less than L2V.numRoots"); if (L2V.Tree[targTree] & 1) { @@ -242,7 +242,7 @@ void L2V_Search(GCReadC* _c) while (1) { if (_c->field_20 > 31) - printk(KERN_DEBUG "PANIC!!!: L2V_Search() fail!\r\n"); + printk(KERN_ERR "PANIC!!!: L2V_Search() fail!\r\n"); if (value == 0) { @@ -269,7 +269,7 @@ void L2V_Search(GCReadC* _c) { node = (uint32_t*)((uint8_t*)&pool->next + nOfs); if (*node == 0xFFFFFFFF) - printk(KERN_DEBUG "PANIC!!!: NAND index: bad tree\n"); + printk(KERN_ERR "PANIC!!!: NAND index: bad tree\n"); if (*node & 1) { bits = 14; @@ -288,18 +288,18 @@ void L2V_Search(GCReadC* _c) nodeSize -= 2; Span += *(uint16_t*)((uint8_t*)&pool->next + nodeSize) << bits; if (nOfs > (nodeSize - 4)) //sizeof(lPtr_t) = 6 - printk(KERN_DEBUG "PANIC!!!: _c->next_nOfs) <= c->nodeSize - sizeof(lPtr_t)\n"); + printk(KERN_ERR "PANIC!!!: _c->next_nOfs) <= c->nodeSize - sizeof(lPtr_t)\n"); } if (Span == 0); - printk(KERN_DEBUG "PANIC!!!: L2V_Search fail!!\r\n"); + printk(KERN_ERR "PANIC!!!: L2V_Search fail!!\r\n"); if (targTofs >= (Span + nextSpan)) { nOfs += sizeof(uint16_t); nextSpan += Span; if(nOfs > nodeSize - 6) - printk(KERN_DEBUG "PANIC!!!: NAND index: bad tree\n"); + printk(KERN_ERR "PANIC!!!: NAND index: bad tree\n"); continue; } else { _c->next_nOfs = nOfs + 4; @@ -375,7 +375,7 @@ void L2V_Repack(uint32_t rootNum) } if (L2VS.span[i] == 0) - printk(KERN_DEBUG "PANIC!!!: ((cu).span) != (0)\n"); + printk(KERN_ERR "PANIC!!!: ((cu).span) != (0)\n"); // [0:1]=config, [2:17] = vpn, [18:31] = span-lo *(uint32_t*)(&node1.Ofs + nOfs)= (L2VS.span[i] << 18) | (((&L2VS.Node[i] - &L2V.Pool[0]) / 0x40) & 0xFFFF) << 2 | 3;//repacking @@ -400,7 +400,7 @@ void L2V_Repack(uint32_t rootNum) } if (nodes == 0 && spanCount != 0x8000) - printk(KERN_DEBUG "PANIC!!!: (thisSpan) == ((1 << 15)) \n"); + printk(KERN_ERR "PANIC!!!: (thisSpan) == ((1 << 15)) \n"); L2VS.bottomIdx = nodes; } @@ -408,7 +408,7 @@ void L2V_Repack(uint32_t rootNum) L2V.TreeNodes[rootNum] = index; if (L2VS.bottomIdx != 0) - printk(KERN_DEBUG "PANIC!!!: (r->bottomIdx) == (0)\n"); + printk(KERN_ERR "PANIC!!!: (r->bottomIdx) == (0)\n"); L2V.Tree[rootNum] = (((&L2VS.Node[0] - &L2V.Pool[0]) / 0x40) & 0xFFFF) << 2 | 1; // span = 0 L2V.UpdatesSinceRepack[rootNum] = 0; diff --git a/drivers/block/apple/yaftl/yaftl.c b/drivers/block/apple/yaftl/yaftl.c index b2d162531d5..88f3d960d77 100644 --- a/drivers/block/apple/yaftl/yaftl.c +++ b/drivers/block/apple/yaftl/yaftl.c @@ -94,7 +94,7 @@ static uint32_t YAFTL_Init(void) error_t error = 0; if (yaftl_inited) - printk(KERN_DEBUG "PANIC!!!: Oh shit, yaftl already initialized!\r\n"); + printk(KERN_ERR "PANIC!!!: Oh shit, yaftl already initialized!\r\n"); memset(&sInfo, 0, sizeof(sInfo)); memset(&sFTLStats, 0, sizeof(sFTLStats)); @@ -120,10 +120,10 @@ static uint32_t YAFTL_Init(void) sGeometry.pagesPerSublk * sGeometry.numBlocks; if (error) - printk(KERN_DEBUG "PANIC!!!: yaftl: vfl get info failed!\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: vfl get info failed!\r\n"); if (sGeometry.spareDataSize != 0xC) - printk(KERN_DEBUG "PANIC!!!: yaftl: spareDataSize isn't 0xC!\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: spareDataSize isn't 0xC!\r\n"); printk(KERN_ERR "yaftl: got information from VFL.\r\n"); printk(KERN_ERR "pagesPerSublk: %d\r\n", sGeometry.pagesPerSublk); @@ -169,7 +169,7 @@ static uint32_t YAFTL_Init(void) * sizeof(SpareData)); if (bufzone_finished_allocs(&sInfo.zone)) - printk(KERN_DEBUG "PANIC!!!: YAFTL_Init: bufzone_finished_allocs failed!"); + printk(KERN_ERR "PANIC!!!: YAFTL_Init: bufzone_finished_allocs failed!"); sInfo.pageBuffer = bufzone_rebase(&sInfo.zone, sInfo.pageBuffer); sInfo.tocPageBuffer = bufzone_rebase(&sInfo.zone, sInfo.tocPageBuffer); @@ -213,15 +213,15 @@ static uint32_t YAFTL_Init(void) sInfo.tocArray = yaftl_alloc(sInfo.tocArrayLength * sizeof(TOCStruct)); if (!sInfo.tocArray) - printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate tocArray\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: failed to allocate tocArray\r\n"); sInfo.blockArray = yaftl_alloc(sGeometry.numBlocks * sizeof(BlockStruct)); if (!sInfo.blockArray) - printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate blockArray\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: failed to allocate blockArray\r\n"); sInfo.unknBuffer3_ftl = yaftl_alloc(sGeometry.pagesPerSublk << 2); if (!sInfo.unknBuffer3_ftl) - printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to allocate unknBuffer3_ftl\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: failed to allocate unknBuffer3_ftl\r\n"); // Initialize number of pages in a context slot. sInfo.nPagesTocPageIndices = CEIL_DIVIDE(sInfo.tocArrayLength @@ -257,17 +257,17 @@ static uint32_t YAFTL_Init(void) gcResetReadCache(&sInfo.readc); if (gcInit()) - printk(KERN_DEBUG "PANIC!!!: YAFTL: GC initialization has failed\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: GC initialization has failed\r\n"); if (BTOC_Init()) - printk(KERN_DEBUG "PANIC!!!: YAFTL: BTOC initialization has failed\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: BTOC initialization has failed\r\n"); sInfo.latestUserBlk.tocBuffer = YAFTL_allocBTOC(-3); sInfo.latestIndexBlk.tocBuffer = YAFTL_allocBTOC(-2); if (L2V_Init(sInfo.totalPages, sGeometry.numBlocks, sGeometry.pagesPerSublk)) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: L2V initialization has failed\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: L2V initialization has failed\r\n"); } yaftl_inited = 1; @@ -534,7 +534,7 @@ static int flushQuick(void) if (!sInfo.field_79) { if (vfl_erase_single_block(vfl, block, 1)) - printk(KERN_DEBUG "PANIC!!!: yaftl: erase block %d failed\r\n", block); + printk(KERN_ERR "PANIC!!!: yaftl: erase block %d failed\r\n", block); ++sInfo.blockArray[block].eraseCount; ++sInfo.field_DC[3]; @@ -669,7 +669,7 @@ void YAFTL_Flush(void) if (sInfo.blockArray[block].unkn5 != 0) { // Need to erase. if (vfl_erase_single_block(vfl, block, 1)) - printk(KERN_DEBUG "PANIC!!!: yaftl: erase block %d failed\r\n", block); + printk(KERN_ERR "PANIC!!!: yaftl: erase block %d failed\r\n", block); sInfo.blockArray[block].unkn5 = 0; } @@ -1327,7 +1327,7 @@ static uint32_t YAFTL_Restore(uint8_t ftlCtrlBlockPresent) // TODO - printk(KERN_DEBUG "PANIC!!!: yaftl: sorry, no restore + write support yet :(\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: sorry, no restore + write support yet :(\r\n"); printk(KERN_ERR "yaftl: context is invalid. performing a read-only restore...\r\n"); @@ -1386,10 +1386,10 @@ static uint32_t YAFTL_Restore(uint8_t ftlCtrlBlockPresent) // User or index page. Determine which. if (type & PAGETYPE_INDEX) { if (!(indexListHead = addBlockToList(indexListHead, i, sInfo.spareBuffer10->usn))) - printk(KERN_DEBUG "PANIC!!!: yaftl: out of memory.\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: out of memory.\r\n"); } else if (type & PAGETYPE_LBN) { if (!(userListHead = addBlockToList(userListHead, i, sInfo.spareBuffer10->usn))) - printk(KERN_DEBUG "PANIC!!!: yaftl: out of memory.\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: out of memory.\r\n"); } else { printk(KERN_ERR "yaftl: warning - block %d doesn't belong to anything (type 0x%02x).\r\n", i, type); if(!vfl_erase_single_block(vfl, i, 1)) @@ -1707,7 +1707,7 @@ static uint32_t YAFTL_Open(uint32_t signature_bit) } if (!sInfo.pageBuffer) { - printk(KERN_DEBUG "PANIC!!!: yaftl: This can't happen. This shouldn't happen. Whatever, it's doing something else then.\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: This can't happen. This shouldn't happen. Whatever, it's doing something else then.\r\n"); return -1; } @@ -2122,7 +2122,7 @@ static uint32_t* refreshTOCCaches(uint32_t _page, uint32_t* _pCacheEntry) kfreeCacheSlot = YAFTL_clearEntryInCache(0xFFFF); if (kfreeCacheSlot == 0xFFFF) - printk(KERN_DEBUG "PANIC!!!: YAFTL: refreshTOCCaches is out of caches\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: refreshTOCCaches is out of caches\r\n"); } } @@ -2145,7 +2145,7 @@ static uint32_t* refreshTOCCaches(uint32_t _page, uint32_t* _pCacheEntry) sInfo.field_78 = 0; } - printk(KERN_DEBUG "PANIC!!!: YAFTL: refreshTOCCaches failed to read an index" + printk(KERN_ERR "PANIC!!!: YAFTL: refreshTOCCaches failed to read an index" " page\r\n"); } } @@ -2186,7 +2186,7 @@ static void invalidatePages(uint32_t _start, uint32_t _count) --sInfo.blockStats.numValidDPages; if (sInfo.blockArray[block].validPagesDNo == 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: tried to invalidate pages in block %d, " + printk(KERN_ERR "PANIC!!!: YAFTL: tried to invalidate pages in block %d, " "but it had no valid pages at all!\r\n", block); } diff --git a/drivers/block/apple/yaftl/yaftl_common.c b/drivers/block/apple/yaftl/yaftl_common.c index ea11664d4a2..9eae97db9d9 100644 --- a/drivers/block/apple/yaftl/yaftl_common.c +++ b/drivers/block/apple/yaftl/yaftl_common.c @@ -22,7 +22,7 @@ static void deallocBTOC(uint32_t* btoc) } } - printk(KERN_DEBUG "PANIC!!!: YAFTL: couldn't deallocate BTOC %p\r\n", btoc); + printk(KERN_ERR "PANIC!!!: YAFTL: couldn't deallocate BTOC %p\r\n", btoc); } static void setupIndexSpare(SpareData* _pSpare, uint32_t _lpn) @@ -50,7 +50,7 @@ static int writeIndexPage(void* _pBuf, SpareData* _pSpare) YAFTL_allocateNewBlock(0); if (sInfo.latestIndexBlk.usedPages != 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: writeIndexPage expected a fresh index " + printk(KERN_ERR "PANIC!!!: YAFTL: writeIndexPage expected a fresh index " "block, but %d of its pages are used\r\n", sInfo.latestIndexBlk.usedPages); } @@ -237,7 +237,7 @@ void YAFTL_allocateNewBlock(uint8_t isUserBlock) } if (vfl_erase_single_block(vfl, currBlk, 1)) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock failed to erase block %d\r\n", + printk(KERN_ERR "PANIC!!!: YAFTL: YAFTL_allocateNewBlock failed to erase block %d\r\n", currBlk); } @@ -253,12 +253,12 @@ void YAFTL_allocateNewBlock(uint8_t isUserBlock) // Candidate found. if (sInfo.blockArray[currBlk].eraseCount < minEraseCnt) { if (sInfo.blockArray[currBlk].validPagesINo) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" + printk(KERN_ERR "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" " (%d) with validPagesINo != 0\r\n", currBlk); } if (sInfo.blockArray[currBlk].validPagesDNo) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" + printk(KERN_ERR "PANIC!!!: YAFTL: YAFTL_allocateNewBlock found an empty block" " (%d) with validPagesDNo != 0\r\n", currBlk); } @@ -269,7 +269,7 @@ void YAFTL_allocateNewBlock(uint8_t isUserBlock) } if (bestBlk == 0xFFFFFFFF) - printk(KERN_DEBUG "PANIC!!!: YAFTL: YAFTL_allocateNewBlock is out of blocks\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: YAFTL_allocateNewBlock is out of blocks\r\n"); if (isUserBlock) { deallocBTOC(sInfo.latestUserBlk.tocBuffer); @@ -389,7 +389,7 @@ uint32_t* YAFTL_allocBTOC(uint32_t _block) } if (found == -1) - printk(KERN_DEBUG "PANIC!!!: yaftl: couldn't allocate a BTOC\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl: couldn't allocate a BTOC\r\n"); sInfo.btocCacheMissing &= ~(1<head == _list->tail) - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcListPopFront was called but list is empty\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: gcListPopFront was called but list is empty\r\n"); block = _list->block[_list->head++]; if (_list->head > GCLIST_MAX) @@ -72,7 +72,7 @@ static int gcFillIndex(uint32_t _lpn, uint32_t* _pBuf) L2V_Search(&sInfo.gc.index.read_c); if (sInfo.gc.index.read_c.span == 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFillIndex -- no such page %d\r\n", + printk(KERN_ERR "PANIC!!!: YAFTL: gcFillIndex -- no such page %d\r\n", basePage + i); } } @@ -91,7 +91,7 @@ static int gcFillIndex(uint32_t _lpn, uint32_t* _pBuf) sInfo.gc.index.read_c.vpn += count; } else { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFillIndex got an invalid vpn %x\r\n", vpn); + printk(KERN_ERR "PANIC!!!: YAFTL: gcFillIndex got an invalid vpn %x\r\n", vpn); } i += count; @@ -119,7 +119,7 @@ static error_t gcReadZoneData(GCData* _data, uint8_t _isIndex, uint8_t _scrub) uint32_t entry = _data->btoc[zone[i] % sGeometry.pagesPerSublk]; spareArray[i].lpn = entry; if (entry == 0xFFFFFFFF) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcReadZoneData read an invalid LPN " + printk(KERN_ERR "PANIC!!!: YAFTL: gcReadZoneData read an invalid LPN " "%d\r\n", entry); } @@ -141,7 +141,7 @@ static error_t gcReadZoneData(GCData* _data, uint8_t _isIndex, uint8_t _scrub) if (YAFTL_readMultiPages(zone, _data->curZoneSize, pageBuffer, spareArray, !_isIndex, _scrub) != 1) { // TODO: Don't panic, manually read the pages. - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcReadZoneData couldn't read multi pages\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: gcReadZoneData couldn't read multi pages\r\n"); } return 0; @@ -156,7 +156,7 @@ static int gcHandleVpnMiss(GCData* _data, uint8_t _scrub) SpareData* pSpare = sInfo.gcSpareBuffer; if (indexPageNo >= sInfo.tocArrayLength) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss got an out-of-range index page " + printk(KERN_ERR "PANIC!!!: YAFTL: gcHandleVpnMiss got an out-of-range index page " "%d\r\n", indexPageNo); } @@ -189,7 +189,7 @@ static int gcHandleVpnMiss(GCData* _data, uint8_t _scrub) sInfo.field_78 = 1; } - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss Index UECC page 0x%08x status" + printk(KERN_ERR "PANIC!!!: YAFTL: gcHandleVpnMiss Index UECC page 0x%08x status" " %08x\r\n", page, status); } else { if (!(pSpare->type & PAGETYPE_INDEX)) { @@ -198,7 +198,7 @@ static int gcHandleVpnMiss(GCData* _data, uint8_t _scrub) sInfo.field_78 = 1; } - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcHandleVpnMiss Invalid index metadata " + printk(KERN_ERR "PANIC!!!: YAFTL: gcHandleVpnMiss Invalid index metadata " "0x%02x\r\n", pSpare->type); } else { --sInfo.numFreeCaches; @@ -249,12 +249,12 @@ static void gcChooseBlock(GCData* _data, uint8_t _filter) if (_filter == BLOCKSTATUS_ALLOCATED && status != BLOCKSTATUS_CURRENT && status != BLOCKSTATUS_GC) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock chose a block which doesn't" + printk(KERN_ERR "PANIC!!!: YAFTL: gcChooseBlock chose a block which doesn't" " match the filter -- status %02x\r\n", status); } else if (_filter == BLOCKSTATUS_I_ALLOCATED && status != BLOCKSTATUS_I_CURRENT && status != BLOCKSTATUS_I_GC) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock chose an index block which " + printk(KERN_ERR "PANIC!!!: YAFTL: gcChooseBlock chose an index block which " "doesn't match the filter -- status %02x\r\n", status); } } @@ -277,7 +277,7 @@ static void gcChooseBlock(GCData* _data, uint8_t _filter) if (best == 0xFFFFFFFF || bestValid == 0xFFFFFFFF || bestErases == 0xFFFFFFFF) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcChooseBlock couldn't find a block\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: gcChooseBlock couldn't find a block\r\n"); } _data->chosenBlock = best; @@ -295,7 +295,7 @@ static void gcSanityCheckValid(GCData* _data) blk->validPagesINo, blk->validPagesDNo); if (_data->uECC < blk->validPagesINo + blk->validPagesDNo) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: non-zero validity counter; block %d, " + printk(KERN_ERR "PANIC!!!: YAFTL: non-zero validity counter; block %d, " "uECC %d\r\n", _data->chosenBlock, _data->uECC); } else { printk(KERN_ERR "YAFTL: %d uECCs caused it. fixing up counters\r\n", @@ -409,7 +409,7 @@ static int gcWriteDataPages(GCData* _data, uint8_t _scrub) cache = toc->cacheNum; if (toc->indexPage == 0xFFFFFFFF && cache == 0xFFFF) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteDataPages failed to find TOC %d\r\n", + printk(KERN_ERR "PANIC!!!: YAFTL: gcWriteDataPages failed to find TOC %d\r\n", indexPageNo); } @@ -421,7 +421,7 @@ static int gcWriteDataPages(GCData* _data, uint8_t _scrub) cache = YAFTL_clearEntryInCache(0xFFFF); if (cache == 0xFFFF) - printk(KERN_DEBUG "PANIC!!!: YAFTL: failed to find a TOC cache\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: failed to find a TOC cache\r\n"); status = YAFTL_readPage( toc->indexPage, (uint8_t*)sInfo.tocCaches[cache].buffer, spare, @@ -433,7 +433,7 @@ static int gcWriteDataPages(GCData* _data, uint8_t _scrub) sInfo.field_78 = 0; } - printk(KERN_DEBUG "PANIC!!!: YAFTL: uecc toc page 0x%08x status 0x%08x\r\n", + printk(KERN_ERR "PANIC!!!: YAFTL: uecc toc page 0x%08x status 0x%08x\r\n", toc->indexPage, status); return status; } @@ -453,7 +453,7 @@ static int gcWriteDataPages(GCData* _data, uint8_t _scrub) sInfo.field_78 = 0; } - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteDataPages tried to move a page from" + printk(KERN_ERR "PANIC!!!: YAFTL: gcWriteDataPages tried to move a page from" " a block with no valid data pages %d\r\n", blk); } @@ -514,7 +514,7 @@ static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) if (sInfo.blockArray[sInfo.gc.data.chosenBlock].validPagesINo != 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages chose a block with no" + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeDataPages chose a block with no" " valid index pages\r\n"); } @@ -554,7 +554,7 @@ static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) sInfo.gc.data.read_c.pageIndex = btocEntry; L2V_Search(&sInfo.gc.data.read_c); if (sInfo.gc.data.read_c.span == 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages has called " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeDataPages has called " "L2V_Search but span is still 0\r\n"); } @@ -577,7 +577,7 @@ static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) break; } } else if (vpn != L2V_VPN_SPECIAL) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages doesn't know " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeDataPages doesn't know " "what vpn %X is\r\n", vpn); } @@ -600,7 +600,7 @@ static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) <= sInfo.gc.data.curZoneSize) { if (sInfo.blockArray[sInfo.gc.data.chosenBlock]. validPagesDNo < sInfo.gc.data.curZoneSize) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages tried to free " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeDataPages tried to free " "more pages than it should\r\n"); } @@ -631,7 +631,7 @@ static int gcFreeDataPages(int32_t _numPages, uint8_t _scrub) if (sInfo.gc.data.curZoneSize > 0) { if (sInfo.blockArray[sInfo.gc.data.chosenBlock].validPagesDNo < sInfo.gc.data.curZoneSize) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeDataPages had pages left in the" + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeDataPages had pages left in the" " zone, but there aren't valid pages at all\r\n"); } @@ -756,7 +756,7 @@ static error_t gcWriteIndexPages(GCData* _data) block = sInfo.latestIndexBlk.blockNum; if (sInfo.latestIndexBlk.usedPages) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcWriteIndexPages got a non-empty block " + printk(KERN_ERR "PANIC!!!: YAFTL: gcWriteIndexPages got a non-empty block " "%d\r\n", block); } } @@ -867,7 +867,7 @@ void gcListPushBack(GCList* _list, uint32_t _block) _list->tail = 0; if (_list->tail == _list->head) - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcListPushBack -- list is full\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: gcListPushBack -- list is full\r\n"); } void gcFreeBlock(uint32_t _block, uint8_t _scrub) @@ -969,7 +969,7 @@ void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub) if (toc->indexPage == block * sGeometry.pagesPerSublk + i) { if (sInfo.blockArray[block].validPagesINo == 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages tried to" + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeIndexPages tried to" " invalidate in block %d, with no " "valid pages\r\n", block); } @@ -987,12 +987,12 @@ void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub) // Data is not cached. First, validate everything. if (toc->indexPage == 0xFFFFFFFF) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages found a TOC which " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeIndexPages found a TOC which " "is not cached nor available %d\r\n", block); } if (toc->indexPage != block * sGeometry.pagesPerSublk + i) - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages can't be here\r\n"); + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeIndexPages can't be here\r\n"); // Find a free cache. toc->cacheNum = YAFTL_findFreeTOCCache(); @@ -1013,7 +1013,7 @@ void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub) toc->indexPage = 0xFFFFFFFF; if (sInfo.blockArray[block].validPagesINo == 0) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages tried to " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeIndexPages tried to " "invalidate an index in block %d but it has " "no valid indexes\r\n", block); } @@ -1028,7 +1028,7 @@ void gcFreeIndexPages(uint32_t _victim, uint8_t _scrub) // No free cache :( Must free manually. if (sInfo.blockArray[block].validPagesINo < sInfo.gc.index.curZoneSize) { - printk(KERN_DEBUG "PANIC!!!: YAFTL: gcFreeIndexPages can't invalidate " + printk(KERN_ERR "PANIC!!!: YAFTL: gcFreeIndexPages can't invalidate " "more pages than available (%d < %d)\r\n", sInfo.blockArray[block].validPagesINo, sInfo.gc.index.curZoneSize); diff --git a/drivers/block/apple/yaftl/yaftl_mem.c b/drivers/block/apple/yaftl/yaftl_mem.c index 7fce5df2a91..081b671d888 100644 --- a/drivers/block/apple/yaftl/yaftl_mem.c +++ b/drivers/block/apple/yaftl/yaftl_mem.c @@ -12,7 +12,7 @@ void* yaftl_alloc(size_t size) #endif if (!buffer) - printk(KERN_DEBUG "PANIC!!!: yaftl_alloc failed\r\n"); + printk(KERN_ERR "PANIC!!!: yaftl_alloc failed\r\n"); #ifdef YUSTAS_FIXME memset(buffer, 0, size); @@ -37,7 +37,7 @@ void* bufzone_alloc(bufzone_t* _zone, size_t size) size_t oldSizeRounded; if (_zone->state != 1) - printk(KERN_DEBUG "PANIC!!!: bufzone_alloc: bad state\r\n"); + printk(KERN_ERR "PANIC!!!: bufzone_alloc: bad state\r\n"); oldSizeRounded = ROUND_UP(_zone->size, 64); _zone->paddingsSize = _zone->paddingsSize + (oldSizeRounded - _zone->size); @@ -52,15 +52,16 @@ error_t bufzone_finished_allocs(bufzone_t* _zone) uint8_t* buff; if (_zone->state != 1) { - printk(KERN_ERR "bufzone_finished_allocs: bad state\r\n"); + printk(KERN_ERR "bufzone_finished_allocs: bad state\n"); return EINVAL; } _zone->size = ROUND_UP(_zone->size, 64); + printk(KERN_ERR "%s:%d size: %d\n", __FUNCTION__, __LINE__, _zone->size); buff = yaftl_alloc(_zone->size); if (!buff) { - printk(KERN_ERR "bufzone_finished_alloc: No buffer.\r\n"); + printk(KERN_ERR "bufzone_finished_alloc: No buffer.\n"); return EINVAL; } diff --git a/include/linux/apple_flash.h b/include/linux/apple_flash.h index 89c9ab40b77..daa7ed7fd08 100644 --- a/include/linux/apple_flash.h +++ b/include/linux/apple_flash.h @@ -7,7 +7,8 @@ typedef uint32_t page_t; enum apple_nand_info { -/* NAND_NUM_CE,*/ + NAND_VENDOR_TYPE, + NAND_NUM_CE, NAND_BLOCKS_PER_CE, NAND_PAGES_PER_CE, NAND_PAGES_PER_BLOCK, @@ -53,7 +54,7 @@ int register_apple_nand(struct apple_nand*); void remove_apple_nand(struct apple_nand*); int apple_nand_set_data_whitening(int whitening); -int apple_nand_get_num_ce(void); +int apple_nand_set_info(int info, int val); int apple_nand_get_info(int info); int apple_nand_special_page(u16 _ce, char _page[16], uint8_t* _buffer, size_t _amt); From 95627694aed0310f2fe5d8fe37a63dc75e876d5c Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sat, 5 May 2012 06:00:21 +0400 Subject: [PATCH 10/11] Warnings cleanup. --- arch/arm/mach-s5l8930/cpu.c | 2 +- arch/arm/mach-s5l8930/dev-clcd.c | 2 +- arch/arm/mach-s5l8930/dev-h2fmi.c | 6 ++++-- arch/arm/mach-s5l8930/mach-ipodtouch4g.c | 2 ++ arch/arm/mach-s5l8930/time.c | 2 +- arch/arm/plat-samsung/dev-i2c1.c | 1 - drivers/spi/spi_s5l89xx.c | 2 +- drivers/video/s5l_clcd.c | 4 ++-- drivers/video/s5l_display_pipe.c | 2 ++ 9 files changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-s5l8930/cpu.c b/arch/arm/mach-s5l8930/cpu.c index d91399b843e..b784683996d 100644 --- a/arch/arm/mach-s5l8930/cpu.c +++ b/arch/arm/mach-s5l8930/cpu.c @@ -186,7 +186,7 @@ static struct platform_device cdma_dev = { .num_resources = ARRAY_SIZE(cdma_res), .dev = { - .coherent_dma_mask = DMA_32BIT_MASK, + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; diff --git a/arch/arm/mach-s5l8930/dev-clcd.c b/arch/arm/mach-s5l8930/dev-clcd.c index cfe3e6bfe19..6da35a657aa 100644 --- a/arch/arm/mach-s5l8930/dev-clcd.c +++ b/arch/arm/mach-s5l8930/dev-clcd.c @@ -52,7 +52,7 @@ static struct platform_device clcd_dev = { .dev = { .platform_data = &dp_info, - .coherent_dma_mask = DMA_32BIT_MASK, + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; diff --git a/arch/arm/mach-s5l8930/dev-h2fmi.c b/arch/arm/mach-s5l8930/dev-h2fmi.c index 40e255adb07..f14795416a2 100644 --- a/arch/arm/mach-s5l8930/dev-h2fmi.c +++ b/arch/arm/mach-s5l8930/dev-h2fmi.c @@ -77,6 +77,7 @@ static struct h2fmi_smth h2fmi_smth_ipt4g = { { 0, 0, 0, 5, 5, 5, 5, 0 }, { 0x33 static struct h2fmi_smth h2fmi_smth_atv2g = { { 0, 0, 0, 3, 3, 3, 4, 0 }, { 0x3333, 0xCCCC, 0 } }; #endif +#if 0 static int count_bits(u32 _val) { int ret = 0; @@ -91,6 +92,7 @@ static int count_bits(u32 _val) return ret; } +#endif static struct h2fmi_platform_data pdata0 = { .ecc_step_shift = 10, @@ -121,7 +123,7 @@ static struct platform_device h2fmi0 = { .dev = { .platform_data = &pdata0, - .coherent_dma_mask = DMA_32BIT_MASK, + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; @@ -134,7 +136,7 @@ static struct platform_device h2fmi1 = { .dev = { .platform_data = &pdata1, - .coherent_dma_mask = DMA_32BIT_MASK, + .coherent_dma_mask = DMA_BIT_MASK(32), }, }; diff --git a/arch/arm/mach-s5l8930/mach-ipodtouch4g.c b/arch/arm/mach-s5l8930/mach-ipodtouch4g.c index 21ac7985a22..2eec7593f9e 100644 --- a/arch/arm/mach-s5l8930/mach-ipodtouch4g.c +++ b/arch/arm/mach-s5l8930/mach-ipodtouch4g.c @@ -58,6 +58,7 @@ static struct fb_videomode video_mode = { .flag = 0xD, }; +#if 0 static struct gpio_keys_button buttons[] = { [0] = { .type = EV_KEY, @@ -110,6 +111,7 @@ static struct spi_board_info __initdata spidevs[] = { .platform_data = &z2_data, }, }; +#endif static void __init ipt4g_init(void) { diff --git a/arch/arm/mach-s5l8930/time.c b/arch/arm/mach-s5l8930/time.c index 9f93edadba8..c4e8b48384a 100644 --- a/arch/arm/mach-s5l8930/time.c +++ b/arch/arm/mach-s5l8930/time.c @@ -5,7 +5,7 @@ #include #include -static void timer_disable() +static void timer_disable(void) { //printk("%s\n", __func__); writel(2, S5L_TIMER0_CTRL); diff --git a/arch/arm/plat-samsung/dev-i2c1.c b/arch/arm/plat-samsung/dev-i2c1.c index 3a1a85fd8e1..0361cef4c1e 100644 --- a/arch/arm/plat-samsung/dev-i2c1.c +++ b/arch/arm/plat-samsung/dev-i2c1.c @@ -74,7 +74,6 @@ void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd) void s3c_i2c1_force_stop() { - struct resource *ioarea; void __iomem *regs; struct clk *clk; unsigned long iicstat; diff --git a/drivers/spi/spi_s5l89xx.c b/drivers/spi/spi_s5l89xx.c index 5ad10114307..fd08aef5cb1 100644 --- a/drivers/spi/spi_s5l89xx.c +++ b/drivers/spi/spi_s5l89xx.c @@ -173,7 +173,7 @@ static int s5l89xx_spi_update_state(struct spi_device *spi, if (div > 255) div = 255; - dev_dbg(&spi->dev, "pre-scaler=%d src=%u (wanted %d, got %ld)\n", + dev_dbg(&spi->dev, "pre-scaler=%d src=%lu (wanted %d, got %ld)\n", div, clk, hz, clk / (2 * (div + 1))); cs->hz = hz; diff --git a/drivers/video/s5l_clcd.c b/drivers/video/s5l_clcd.c index 926ebed5488..37c897d546f 100644 --- a/drivers/video/s5l_clcd.c +++ b/drivers/video/s5l_clcd.c @@ -107,11 +107,11 @@ static int s5l_clcd_dev_probe(struct mipi_dsim_lcd_device *_dev) printk("CLCD regs:\n"); for(i = 0; i < 25; i++) - printk("0x%08x: 0x%08x\n", state->regs + (i*4), readl(state->regs + (i*4))); + printk("0x%08x: 0x%08x\n", (unsigned int)(state->regs + (i*4)), readl(state->regs + (i*4))); printk("MIPI DSIM regs:\n"); for(i = 0; i < 24; i++) - printk("0x%08x: 0x%08x\n", dsim->reg_base + (i*4), readl(dsim->reg_base + (i*4))); + printk("0x%08x: 0x%08x\n", (unsigned int)(dsim->reg_base + (i*4)), readl(dsim->reg_base + (i*4))); } return 0; diff --git a/drivers/video/s5l_display_pipe.c b/drivers/video/s5l_display_pipe.c index 7bb3ccd72d1..60bf0a928ac 100644 --- a/drivers/video/s5l_display_pipe.c +++ b/drivers/video/s5l_display_pipe.c @@ -301,8 +301,10 @@ static int display_pipe_probe(struct platform_device *_dev) goto exit; +#if 0 fail_drv: platform_set_drvdata(_dev, NULL); +#endif fail_clk: clk_put(state->clk); From a964aa2ae03143aeacf80212930959d156ac7301 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov Date: Sat, 12 May 2012 15:13:17 +0400 Subject: [PATCH 11/11] h2fmi.c: fixed timings setup. --- arch/arm/mach-s5l8930/clock.c | 5 +++-- arch/arm/plat-samsung/clock-clksrc.c | 2 ++ drivers/block/apple/h2fmi.c | 2 -- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-s5l8930/clock.c b/arch/arm/mach-s5l8930/clock.c index 664f0692ad2..933d7a6d874 100644 --- a/arch/arm/mach-s5l8930/clock.c +++ b/arch/arm/mach-s5l8930/clock.c @@ -82,7 +82,7 @@ static unsigned long s5l_pll_get_rate(struct s5l_pll *_pll) return 0; base = (((uint64_t)PLLCON0_P(conf)) << PLLCON0_S(conf)); - freq = (PLLCON0_M(conf) * (uint64_t)CLOCK_BASE_HZ); + freq = ((uint64_t)PLLCON0_M(conf) * (uint64_t)CLOCK_BASE_HZ); do_div(freq, base); return freq; } @@ -298,7 +298,8 @@ static struct clksrc_clk clk_system1 = { .clk = { .name = "system1", .id = -1, - .parent = &clk_system0.clk, +// .parent = &clk_system0.clk, + .parent = &clk_system_source.clk, }, .reg_div = { .reg = VA_PMGR0 + 0x40, .shift = SHIFT_DIV2, .size = SIZE_DIV }, diff --git a/arch/arm/plat-samsung/clock-clksrc.c b/arch/arm/plat-samsung/clock-clksrc.c index ae8b8507663..1f9520e9c19 100644 --- a/arch/arm/plat-samsung/clock-clksrc.c +++ b/arch/arm/plat-samsung/clock-clksrc.c @@ -44,7 +44,9 @@ static unsigned long s3c_getrate_clksrc(struct clk *clk) clkdiv &= mask; clkdiv >>= sclk->reg_div.shift; +#ifndef CONFIG_CPU_S5L8930 clkdiv++; +#endif rate /= clkdiv; return rate; diff --git a/drivers/block/apple/h2fmi.c b/drivers/block/apple/h2fmi.c index 22c71e5edf3..a1e3ab82d12 100644 --- a/drivers/block/apple/h2fmi.c +++ b/drivers/block/apple/h2fmi.c @@ -668,7 +668,6 @@ static int h2fmi_setup_timing(struct h2fmi_timing_setup *_timing, u8 *_buffer) _buffer[3] = var_28; _buffer[4] = h2fmi_round_down(smth, max(_timing->f + _timing->r, var_2C)); - _buffer[2] = 2; // TODO: This is a hack because the above calculation is somehow broken. return 0; } @@ -1814,7 +1813,6 @@ static int h2fmi_detect_nand(struct h2fmi_state *_state) return PTR_ERR(clk); timing_setup.freq = clk_get_rate(clk); - printk(KERN_ERR "freq: %lld\n", timing_setup.freq); clk_put(clk);