From 2e7d7c880279b45957ce49fa116daebe3c6c5974 Mon Sep 17 00:00:00 2001 From: Marc Fiuczynski Date: Wed, 15 Sep 2004 17:11:56 +0000 Subject: [PATCH] Fedora Core 2 - 1.494 --- CREDITS | 4 + arch/alpha/kernel/entry.S | 18 + arch/alpha/kernel/process.c | 15 +- arch/alpha/kernel/signal.c | 5 +- arch/ppc/Kconfig | 52 +- arch/ppc/boot/common/misc-common.c | 15 +- arch/ppc/boot/simple/Makefile | 6 +- arch/ppc/boot/simple/head.S | 6 +- arch/ppc/boot/simple/misc-ev64260.S | 80 +- arch/ppc/boot/simple/misc.c | 4 +- arch/ppc/configs/ev64260_defconfig | 328 ++--- arch/ppc/platforms/85xx/sbc8560.c | 2 + arch/ppc/platforms/85xx/sbc8560.h | 6 +- arch/ppc/platforms/85xx/sbc85xx.c | 64 +- arch/ppc/platforms/Makefile | 3 +- arch/ppc/platforms/ev64260.h | 103 +- arch/ppc/syslib/Makefile | 7 +- arch/ppc/syslib/gt64260_pic.c | 64 +- arch/ppc/syslib/open_pic.c | 76 +- configs/kernel-2.6.7-i586-smp.config | 25 +- configs/kernel-2.6.7-i586.config | 25 +- configs/kernel-2.6.7-i686-smp.config | 25 +- configs/kernel-2.6.7-i686.config | 25 +- drivers/mtd/Kconfig | 20 +- drivers/mtd/Makefile | 18 +- drivers/mtd/afs.c | 2 +- drivers/mtd/chips/Kconfig | 83 +- drivers/mtd/chips/Makefile | 5 +- drivers/mtd/chips/amd_flash.c | 2 +- drivers/mtd/chips/cfi_cmdset_0001.c | 697 ++++++----- drivers/mtd/chips/cfi_cmdset_0002.c | 1711 +++++++++++++++----------- drivers/mtd/chips/cfi_cmdset_0020.c | 256 ++-- drivers/mtd/chips/cfi_probe.c | 97 +- drivers/mtd/chips/chipreg.c | 2 +- drivers/mtd/chips/gen_probe.c | 223 +--- drivers/mtd/chips/jedec.c | 2 +- drivers/mtd/chips/jedec_probe.c | 379 ++++-- drivers/mtd/chips/map_ram.c | 11 +- drivers/mtd/chips/map_rom.c | 2 +- drivers/mtd/chips/sharp.c | 2 +- drivers/mtd/cmdlinepart.c | 11 +- drivers/mtd/devices/Kconfig | 34 +- drivers/mtd/devices/Makefile | 6 +- drivers/mtd/devices/blkmtd.c | 4 +- drivers/mtd/devices/doc2000.c | 624 ++++++---- drivers/mtd/devices/doc2001.c | 22 +- drivers/mtd/devices/doc2001plus.c | 80 +- drivers/mtd/devices/docprobe.c | 28 +- drivers/mtd/devices/lart.c | 2 +- drivers/mtd/devices/ms02-nv.c | 28 +- drivers/mtd/devices/ms02-nv.h | 82 +- drivers/mtd/devices/pmc551.c | 2 +- drivers/mtd/ftl.c | 4 +- drivers/mtd/inftlcore.c | 46 +- drivers/mtd/inftlmount.c | 152 ++- drivers/mtd/maps/Kconfig | 96 +- drivers/mtd/maps/Makefile | 11 +- drivers/mtd/maps/amd76xrom.c | 234 ++-- drivers/mtd/maps/arctic-mtd.c | 4 +- drivers/mtd/maps/autcpu12-nvram.c | 4 +- drivers/mtd/maps/beech-mtd.c | 4 +- drivers/mtd/maps/cdb89712.c | 8 +- drivers/mtd/maps/ceiva.c | 4 +- drivers/mtd/maps/cfi_flagadm.c | 4 +- drivers/mtd/maps/cstm_mips_ixx.c | 10 +- drivers/mtd/maps/dbox2-flash.c | 17 +- drivers/mtd/maps/dc21285.c | 223 ++-- drivers/mtd/maps/dilnetpc.c | 4 +- drivers/mtd/maps/ebony.c | 10 +- drivers/mtd/maps/edb7312.c | 8 +- drivers/mtd/maps/elan-104nc.c | 60 +- drivers/mtd/maps/epxa10db-flash.c | 4 +- drivers/mtd/maps/fortunet.c | 24 +- drivers/mtd/maps/h720x-flash.c | 12 +- drivers/mtd/maps/impa7.c | 12 +- drivers/mtd/maps/integrator-flash.c | 4 +- drivers/mtd/maps/iq80310.c | 4 +- drivers/mtd/maps/ixp4xx.c | 28 +- drivers/mtd/maps/l440gx.c | 4 +- drivers/mtd/maps/lasat.c | 80 +- drivers/mtd/maps/lubbock-flash.c | 24 +- drivers/mtd/maps/map_funcs.c | 74 +- drivers/mtd/maps/mbx860.c | 4 +- drivers/mtd/maps/netsc520.c | 4 +- drivers/mtd/maps/nettel.c | 6 +- drivers/mtd/maps/ocelot.c | 6 +- drivers/mtd/maps/octagon-5066.c | 68 +- drivers/mtd/maps/pb1xxx-flash.c | 160 ++- drivers/mtd/maps/pci.c | 148 +-- drivers/mtd/maps/pcmciamtd.c | 108 +- drivers/mtd/maps/physmap.c | 66 +- drivers/mtd/maps/pnc2000.c | 4 +- drivers/mtd/maps/redwood.c | 15 +- drivers/mtd/maps/rpxlite.c | 4 +- drivers/mtd/maps/sa1100-flash.c | 8 +- drivers/mtd/maps/sbc_gxx.c | 58 +- drivers/mtd/maps/sc520cdp.c | 8 +- drivers/mtd/maps/scb2_flash.c | 4 +- drivers/mtd/maps/scx200_docflash.c | 6 +- drivers/mtd/maps/solutionengine.c | 8 +- drivers/mtd/maps/sun_uflash.c | 4 +- drivers/mtd/maps/tqm8xxl.c | 4 +- drivers/mtd/maps/tsunami_flash.c | 20 +- drivers/mtd/maps/uclinux.c | 4 +- drivers/mtd/maps/vmax301.c | 68 +- drivers/mtd/maps/wr_sbc82xx_flash.c | 68 +- drivers/mtd/mtd_blkdevs.c | 7 +- drivers/mtd/mtdblock.c | 2 +- drivers/mtd/mtdchar.c | 125 +- drivers/mtd/mtdconcat.c | 17 +- drivers/mtd/mtdcore.c | 44 +- drivers/mtd/mtdpart.c | 37 +- drivers/mtd/nand/Kconfig | 71 +- drivers/mtd/nand/Makefile | 21 +- drivers/mtd/nand/autcpu12.c | 95 +- drivers/mtd/nand/edb7312.c | 26 +- drivers/mtd/nand/nand_ecc.c | 67 +- drivers/mtd/nand/nand_ids.c | 113 +- drivers/mtd/nand/spia.c | 21 +- drivers/mtd/nftlcore.c | 42 +- drivers/mtd/nftlmount.c | 59 +- drivers/mtd/redboot.c | 50 +- drivers/net/Kconfig | 11 + drivers/net/Makefile | 1 + drivers/serial/Kconfig | 14 + drivers/serial/Makefile | 2 + fs/Kconfig | 82 +- fs/jffs2/Makefile | 11 +- fs/jffs2/background.c | 42 +- fs/jffs2/build.c | 157 +-- fs/jffs2/compr.c | 508 ++++++-- fs/jffs2/compr_rtime.c | 34 +- fs/jffs2/compr_rubin.c | 66 +- fs/jffs2/compr_zlib.c | 58 +- fs/jffs2/erase.c | 103 +- fs/jffs2/file.c | 19 +- fs/jffs2/fs.c | 186 ++- fs/jffs2/gc.c | 316 +++-- fs/jffs2/ioctl.c | 6 +- fs/jffs2/malloc.c | 2 +- fs/jffs2/nodelist.c | 35 +- fs/jffs2/nodelist.h | 125 +- fs/jffs2/nodemgmt.c | 127 +- fs/jffs2/os-linux.h | 43 +- fs/jffs2/read.c | 13 +- fs/jffs2/readinode.c | 100 +- fs/jffs2/scan.c | 78 +- fs/jffs2/super.c | 32 +- fs/jffs2/symlink.c | 2 +- fs/jffs2/wbuf.c | 295 ++--- fs/jffs2/write.c | 129 +- fs/jffs2/writev.c | 2 +- include/asm-ppc/ocp_ids.h | 12 +- include/asm-ppc/serial.h | 2 + include/linux/cpumask.h | 5 + include/linux/jffs2.h | 37 +- include/linux/jffs2_fs_i.h | 4 +- include/linux/libata.h | 1 + include/linux/mtd/cfi.h | 511 +++----- include/linux/mtd/doc2000.h | 33 +- include/linux/mtd/flashchip.h | 15 +- include/linux/mtd/ftl.h | 2 +- include/linux/mtd/gen_probe.h | 6 +- include/linux/mtd/inftl.h | 90 +- include/linux/mtd/map.h | 364 +++++- include/linux/mtd/mtd.h | 119 +- include/linux/mtd/nand.h | 397 ++++-- include/linux/mtd/nand_ecc.h | 16 +- include/linux/mtd/nftl.h | 71 +- include/linux/mtd/partitions.h | 4 +- include/linux/serialP.h | 7 + include/linux/serial_core.h | 3 + mm/shmem.c | 3 +- 173 files changed, 7405 insertions(+), 5112 deletions(-) diff --git a/CREDITS b/CREDITS index 518c5bd87..cc0cffd96 100644 --- a/CREDITS +++ b/CREDITS @@ -1090,6 +1090,10 @@ E: jbglaw@lug-owl.de D: SRM environment driver (for Alpha systems) P: 1024D/8399E1BB 250D 3BCF 7127 0D8C A444 A961 1DBD 5E75 8399 E1BB +N: Thomas Gleixner +E: tglx@linutronix.de +D: NAND flash hardware support, JFFS2 on NAND flash + N: Richard E. Gooch E: rgooch@atnf.csiro.au D: parent process death signal to children diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index be1731104..f0927ee53 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -924,6 +924,24 @@ sys_ptrace: jmp $31, do_sys_ptrace .end sys_ptrace + .align 4 + .globl sys_execve + .ent sys_execve +sys_execve: + .prologue 0 + mov $sp, $19 + jmp $31, do_sys_execve +.end sys_execve + + .align 4 + .globl osf_sigprocmask + .ent osf_sigprocmask +osf_sigprocmask: + .prologue 0 + mov $sp, $18 + jmp $31, do_osf_sigprocmask +.end osf_sigprocmask + .align 4 .globl alpha_ni_syscall .ent alpha_ni_syscall diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 06646a20d..6fd854655 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -456,19 +456,10 @@ dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task) /* * sys_execve() executes a new program. - * - * This works due to the alpha calling sequence: the first 6 args - * are gotten from registers, while the rest is on the stack, so - * we get a0-a5 for free, and then magically find "struct pt_regs" - * on the stack for us.. - * - * Don't do this at home. */ asmlinkage int -sys_execve(char __user *ufilename, char __user * __user *argv, - char __user * __user *envp, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) +do_sys_execve(char __user *ufilename, char __user * __user *argv, + char __user * __user *envp, struct pt_regs *regs) { int error; char *filename; @@ -477,7 +468,7 @@ sys_execve(char __user *ufilename, char __user * __user *argv, error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, argv, envp, ®s); + error = do_execve(filename, argv, envp, regs); putname(filename); out: return error; diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index 0797b46b7..dd1d9195c 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -53,8 +53,7 @@ static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *, * operation, as all of this is local to this thread. */ asmlinkage unsigned long -osf_sigprocmask(int how, unsigned long newmask, long a2, long a3, - long a4, long a5, struct pt_regs regs) +do_osf_sigprocmask(int how, unsigned long newmask, struct pt_regs *regs) { unsigned long oldmask = -EINVAL; @@ -78,7 +77,7 @@ osf_sigprocmask(int how, unsigned long newmask, long a2, long a3, recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - (®s)->r0 = 0; /* special no error return */ + regs->r0 = 0; /* special no error return */ } return oldmask; } diff --git a/arch/ppc/Kconfig b/arch/ppc/Kconfig index 8ce1cca29..4502dc418 100644 --- a/arch/ppc/Kconfig +++ b/arch/ppc/Kconfig @@ -507,6 +507,12 @@ config APUS More information is available at: . +config KATANA + bool "Artesyn-Katana" + +config DMV182 + bool "Dy-4 SVME/DMV-182" + config WILLOW bool "Cogent-Willow" @@ -517,7 +523,10 @@ config POWERPMC250 bool "Force-PowerPMC250" config EV64260 - bool "Galileo-EV-64260-BP" + bool "Marvell-EV64260BP" + help + Select EV64260 if configuring of a Marvell (formerly Galileo) + EV64260BP Evaluation platofm. config SPRUCE bool "IBM-Spruce" @@ -671,9 +680,29 @@ config PPC_OF depends on PPC_PMAC || PPC_CHRP default y +menu "Set bridge options" + depends on MV64X60 + +config MV64X60_BASE + hex "Set bridge base used by firmware" + default "0xf1000000" + help + A firmware can leave the base address of the bridge's registers at + a non-standard location. If so, set this value to reflect the + address of that non-standard location. + +config MV64X60_NEW_BASE + hex "Set bridge base used by kernel" + default "0xf1000000" + help + If the current base address of the bridge's registers is not where + you want it, set this value to the address that you want it moved to. + +endmenu + config PPC_GEN550 bool - depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800 + depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800 || (EV64260 && !MV64X60_MPSC) || DMV182 default y config FORCE @@ -686,6 +715,16 @@ config GT64260 depends on EV64260 default y +config MV64360 + bool + depends on KATANA || DMV182 + default y + +config MV64X60 + bool + depends on (GT64260 || MV64360) + default y + config NONMONARCH_SUPPORT bool "Enable Non-Monarch Support" depends on PRPMC800 @@ -744,11 +783,6 @@ config SERIAL_CONSOLE depends on 8xx default y -config SERIAL_CONSOLE_BAUD - int - depends on EV64260 - default "115200" - config PPCBUG_NVRAM bool "Enable reading PPCBUG NVRAM during boot" if PPLUS || LOPEC default y if PPC_PREP @@ -1363,11 +1397,11 @@ config BOOTX_TEXT config SERIAL_TEXT_DEBUG bool "Support for early boot texts over serial port" - depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550 + depends on 4xx || LOPEC || MV64X60 || PPLUS || PRPMC800 || PPC_GEN550 config PPC_OCP bool - depends on IBM_OCP || FSL_OCP + depends on IBM_OCP || FSL_OCP || MV64X60 default y endmenu diff --git a/arch/ppc/boot/common/misc-common.c b/arch/ppc/boot/common/misc-common.c index fadc77100..4404fe8cb 100644 --- a/arch/ppc/boot/common/misc-common.c +++ b/arch/ppc/boot/common/misc-common.c @@ -59,7 +59,8 @@ static int _cvt(unsigned long val, char *buf, long radix, char *digits); void _vprintk(void(*putc)(const char), const char *fmt0, va_list ap); unsigned char *ISA_io = NULL; -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \ + defined(CONFIG_SERIAL_MPSC_CONSOLE) extern unsigned long com_port; extern int serial_tstc(unsigned long com_port); @@ -80,7 +81,8 @@ void exit(void) int tstc(void) { -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \ + defined(CONFIG_SERIAL_MPSC_CONSOLE) if(keyb_present) return (CRT_tstc() || serial_tstc(com_port)); else @@ -93,7 +95,8 @@ int tstc(void) int getc(void) { while (1) { -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \ + defined(CONFIG_SERIAL_MPSC_CONSOLE) if (serial_tstc(com_port)) return (serial_getc(com_port)); #endif /* serial console */ @@ -108,7 +111,8 @@ putc(const char c) { int x,y; -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \ + defined(CONFIG_SERIAL_MPSC_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); @@ -155,7 +159,8 @@ void puts(const char *s) y = orig_y; while ( ( c = *s++ ) != '\0' ) { -#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) +#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE) || \ + defined(CONFIG_SERIAL_MPSC_CONSOLE) serial_putc(com_port, c); if ( c == '\n' ) serial_putc(com_port, '\r'); #endif /* serial console */ diff --git a/arch/ppc/boot/simple/Makefile b/arch/ppc/boot/simple/Makefile index d497a9366..2bae1ea90 100644 --- a/arch/ppc/boot/simple/Makefile +++ b/arch/ppc/boot/simple/Makefile @@ -65,7 +65,7 @@ zimageinitrd-$(CONFIG_OCOTEA) := zImage.initrd-TREE end-$(CONFIG_OCOTEA) := ocotea entrypoint-$(CONFIG_OCOTEA) := 0x01000000 - extra.o-$(CONFIG_EV64260) := direct.o misc-ev64260.o + extra.o-$(CONFIG_EV64260) := misc-ev64260.o end-$(CONFIG_EV64260) := ev64260 cacheflag-$(CONFIG_EV64260) := -include $(clear_L2_L3) @@ -136,14 +136,16 @@ boot-$(CONFIG_8xx) += embed_config.o boot-$(CONFIG_8260) += embed_config.o boot-$(CONFIG_BSEIP) += iic.o boot-$(CONFIG_MBX) += iic.o pci.o qspan_pci.o +boot-$(CONFIG_MV64X60) += misc-mv64x60.o +boot-$(CONFIG_DMV182) += mv64x60_stub.o boot-$(CONFIG_RPXCLASSIC) += iic.o pci.o qspan_pci.o boot-$(CONFIG_RPXLITE) += iic.o # Different boards need different serial implementations. ifeq ($(CONFIG_SERIAL_CONSOLE),y) boot-$(CONFIG_8xx) += m8xx_tty.o boot-$(CONFIG_8260) += m8260_tty.o -boot-$(CONFIG_GT64260_CONSOLE) += gt64260_tty.o endif +boot-$(CONFIG_SERIAL_MPSC_CONSOLE) += mv64x60_tty.o LIBS := $(common)/lib.a $(bootlib)/lib.a ifeq ($(CONFIG_PPC_PREP),y) diff --git a/arch/ppc/boot/simple/head.S b/arch/ppc/boot/simple/head.S index cb95484fe..e80c7d89f 100644 --- a/arch/ppc/boot/simple/head.S +++ b/arch/ppc/boot/simple/head.S @@ -135,9 +135,9 @@ haveOF: */ #endif -#ifdef CONFIG_EV64260 - /* Move 64260's base regs & CS window for external UART */ - bl ev64260_init +#ifdef CONFIG_MV64X60 + /* mv64x60 specific hook to do things like moving register base, etc. */ + bl mv64x60_init #endif /* Get the load address. diff --git a/arch/ppc/boot/simple/misc-ev64260.S b/arch/ppc/boot/simple/misc-ev64260.S index 7ce68158d..957e0f683 100644 --- a/arch/ppc/boot/simple/misc-ev64260.S +++ b/arch/ppc/boot/simple/misc-ev64260.S @@ -1,60 +1,70 @@ /* * arch/ppc/boot/simple/misc-ev64260.S - * + * * Host bridge init code for the Marvell/Galileo EV-64260-BP evaluation board * with a GT64260 onboard. * * Author: Mark Greer * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. */ #include +#include #include -#include - +#include #include - .globl ev64260_init -ev64260_init: - li r20,0 + .globl mv64x60_board_init +mv64x60_board_init: + /* DINK doesn't enable 745x timebase, so enable here (Adrian Cox) */ + mfspr r25,PVR + srwi r25,r25,16 + cmplwi r25,(PVR_7450 >> 16) + bne 1f + mfspr r25,HID0 + oris r25,r25,(HID0_TBEN >> 16) + mtspr HID0,r25 +1: +#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE) li r23,20 - /* Relocate galileo's regs */ - addis r25,0,GT64260_INTERNAL_SPACE_DEFAULT_ADDR@h - ori r25,r25,GT64260_INTERNAL_SPACE_DECODE - lwbrx r26,0,(r25) - lis r24,0xffff - and r26,r26,r24 - addis r24,0,EV64260_BRIDGE_REG_BASE@h - srw r24,r24,r23 - or r26,r26,r24 - stwbrx r26,0,(r25) - sync - - /* Wait for write to take effect */ - addis r25,0,EV64260_BRIDGE_REG_BASE@h - ori r25,r25,GT64260_INTERNAL_SPACE_DECODE -1: lwbrx r24,0,(r25) - cmpw r24,r26 - bne 1b - - /* Change CS2 (UARTS on device module) window */ - addis r25,0,EV64260_BRIDGE_REG_BASE@h - ori r25,r25,GT64260_CPU_CS_DECODE_2_BOT + /* + * Change the CS2 window for the UART so that the bootloader + * can do I/O thru the UARTs. + */ + addis r25,0,CONFIG_MV64X60_NEW_BASE@h + ori r25,r25,MV64x60_CPU2DEV_2_BASE addis r26,0,EV64260_UART_BASE@h srw r26,r26,r23 stwbrx r26,0,(r25) sync - addis r25,0,EV64260_BRIDGE_REG_BASE@h - ori r25,r25,GT64260_CPU_CS_DECODE_2_TOP + addis r25,0,CONFIG_MV64X60_NEW_BASE@h + ori r25,r25,MV64x60_CPU2DEV_2_SIZE addis r26,0,EV64260_UART_END@h srw r26,r26,r23 stwbrx r26,0,(r25) sync - +#endif blr + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) +.data + .globl mv64x60_console_baud +mv64x60_console_baud: +.long EV64260_DEFAULT_BAUD + + .globl mv64x60_mpsc_clk_src +mv64x60_mpsc_clk_src: +.long EV64260_MPSC_CLK_SRC + + .globl mv64x60_mpsc_clk_freq +mv64x60_mpsc_clk_freq: +.long EV64260_MPSC_CLK_FREQ +#endif diff --git a/arch/ppc/boot/simple/misc.c b/arch/ppc/boot/simple/misc.c index dd4bdde71..412658888 100644 --- a/arch/ppc/boot/simple/misc.c +++ b/arch/ppc/boot/simple/misc.c @@ -50,7 +50,7 @@ * user to edit the cmdline or not. */ #if (defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_VGA_CONSOLE)) \ - && !defined(CONFIG_GEMINI) + && !defined(CONFIG_GEMINI) || defined(CONFIG_SERIAL_MPSC_CONSOLE) #define INTERACTIVE_CONSOLE 1 #endif @@ -98,7 +98,7 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) unsigned long initrd_loc, TotalMemory = 0; serial_fixups(); -#ifdef CONFIG_SERIAL_8250_CONSOLE +#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_MPSC_CONSOLE) com_port = serial_init(0, NULL); #endif diff --git a/arch/ppc/configs/ev64260_defconfig b/arch/ppc/configs/ev64260_defconfig index c5bfc64d7..b0f304c0c 100644 --- a/arch/ppc/configs/ev64260_defconfig +++ b/arch/ppc/configs/ev64260_defconfig @@ -4,23 +4,39 @@ CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y CONFIG_HAVE_DEC_LOCK=y +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_GENERIC_NVRAM=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_STANDALONE=y +CONFIG_BROKEN_ON_SMP=y # # General setup # CONFIG_SWAP=y CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y CONFIG_FUTEX=y CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # # Loadable module support @@ -33,24 +49,26 @@ CONFIG_OBSOLETE_MODPARM=y CONFIG_KMOD=y # -# Platform support +# Processor # -CONFIG_PPC=y -CONFIG_PPC32=y CONFIG_6xx=y # CONFIG_40x is not set +# CONFIG_44x is not set # CONFIG_POWER3 is not set +# CONFIG_POWER4 is not set # CONFIG_8xx is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set +# CONFIG_CPU_FREQ is not set +CONFIG_PPC_STD_MMU=y # -# IBM 4xx options +# Platform options # -# CONFIG_8260 is not set -CONFIG_GENERIC_ISA_DMA=y -CONFIG_PPC_STD_MMU=y # CONFIG_PPC_MULTIPLATFORM is not set # CONFIG_APUS is not set -# CONFIG_WILLOW_2 is not set +# CONFIG_KATANA is not set +# CONFIG_WILLOW is not set # CONFIG_PCORE is not set # CONFIG_POWERPMC250 is not set CONFIG_EV64260=y @@ -66,37 +84,37 @@ CONFIG_EV64260=y # CONFIG_K2 is not set # CONFIG_PAL4 is not set # CONFIG_GEMINI is not set +# CONFIG_EST8260 is not set +# CONFIG_SBS8260 is not set +# CONFIG_RPX6 is not set +# CONFIG_TQM8260 is not set + +# +# Set bridge base address +# +CONFIG_MV64X60_BASE=0xf1000000 +CONFIG_MV64X60_NEW_BASE=0xfbe00000 +CONFIG_PPC_GEN550=y CONFIG_GT64260=y +CONFIG_MV64X60=y CONFIG_SERIAL_CONSOLE_BAUD=115200 # CONFIG_SMP is not set # CONFIG_PREEMPT is not set -CONFIG_ALTIVEC=y -CONFIG_TAU=y -# CONFIG_TAU_INT is not set -# CONFIG_TAU_AVERAGE is not set -# CONFIG_CPU_FREQ is not set +# CONFIG_HIGHMEM is not set +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0,115200 ip=on" # -# General setup +# Bus options # -# CONFIG_HIGHMEM is not set +CONFIG_GENERIC_ISA_DMA=y CONFIG_PCI=y CONFIG_PCI_DOMAINS=y -CONFIG_KCORE_ELF=y -CONFIG_BINFMT_ELF=y -CONFIG_KERNEL_ELF=y -CONFIG_BINFMT_MISC=y CONFIG_PCI_LEGACY_PROC=y CONFIG_PCI_NAMES=y -# CONFIG_HOTPLUG is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set -# CONFIG_PPC601_SYNC_FIX is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="console=ttyS0,115200 ip=on" # # Advanced setup @@ -112,15 +130,27 @@ CONFIG_KERNEL_START=0xc0000000 CONFIG_TASK_SIZE=0x80000000 CONFIG_BOOT_LOAD=0x00800000 +# +# Device Drivers +# + +# +# Generic Driver Options +# + # # Memory Technology Devices (MTD) # # CONFIG_MTD is not set +# +# Parallel port support +# +# CONFIG_PARPORT is not set + # # Plug and Play support # -# CONFIG_PNP is not set # # Block devices @@ -131,32 +161,35 @@ CONFIG_BOOT_LOAD=0x00800000 # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_UMEM is not set CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_CARMEL is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y +# CONFIG_LBD is not set # -# Multi-device support (RAID and LVM) +# ATA/ATAPI/MFM/RLL support # -# CONFIG_MD is not set +# CONFIG_IDE is not set # -# ATA/IDE/MFM/RLL support +# SCSI device support # -# CONFIG_IDE is not set +# CONFIG_SCSI is not set # -# SCSI support +# Multi-device support (RAID and LVM) # -# CONFIG_SCSI is not set +# CONFIG_MD is not set # # Fusion MPT device support # # -# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set @@ -165,6 +198,10 @@ CONFIG_BLK_DEV_INITRD=y # # CONFIG_I2O is not set +# +# Macintosh device drivers +# + # # Networking support # @@ -176,8 +213,6 @@ CONFIG_NET=y CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set # CONFIG_NETLINK_DEV is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y @@ -191,34 +226,24 @@ CONFIG_IP_PNP_DHCP=y # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set -# CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set - -# -# IP: Netfilter Configuration -# -# CONFIG_IP_NF_CONNTRACK is not set -# CONFIG_IP_NF_QUEUE is not set -# CONFIG_IP_NF_IPTABLES is not set -# CONFIG_IP_NF_ARPTABLES is not set -# CONFIG_IP_NF_COMPAT_IPCHAINS is not set -# CONFIG_IP_NF_COMPAT_IPFWADM is not set # CONFIG_IPV6 is not set -# CONFIG_XFRM_USER is not set +# CONFIG_NETFILTER is not set # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set +# CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set -# CONFIG_LLC is not set # CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_NET_DIVERT is not set @@ -236,23 +261,27 @@ CONFIG_IPV6_SCTP__=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set # # ARCnet devices # # CONFIG_ARCNET is not set -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set -# CONFIG_ETHERTAP is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y -# CONFIG_MII is not set +CONFIG_MII=y # CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set @@ -261,13 +290,22 @@ CONFIG_NET_ETHERNET=y # # Tulip family network device support # -# CONFIG_NET_TULIP is not set +CONFIG_NET_TULIP=y +# CONFIG_DE2104X is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_TULIP_NAPI is not set +# CONFIG_DE4X5 is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_DM9102 is not set # CONFIG_HP100 is not set CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set # CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set # CONFIG_DGRS is not set CONFIG_EEPRO100=y # CONFIG_EEPRO100_PIO is not set @@ -300,60 +338,55 @@ CONFIG_EEPRO100=y # Ethernet (10000 Mbit) # # CONFIG_IXGB is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set +# CONFIG_S2IO is not set # -# Wireless LAN (non-hamradio) +# Token Ring devices # -# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set # -# Token Ring devices (depends on LLC=y) +# Wireless LAN (non-hamradio) # -# CONFIG_RCPCI is not set -# CONFIG_SHAPER is not set +# CONFIG_NET_RADIO is not set # # Wan interfaces # # CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support -# -# CONFIG_IRDA is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # # ISDN subsystem # -# CONFIG_ISDN_BOOL is not set - -# -# Graphics support -# -# CONFIG_FB is not set +# CONFIG_ISDN is not set # -# Old CD-ROM drivers (not SCSI, not IDE) +# Telephony Support # -# CONFIG_CD_NO_IDESCSI is not set +# CONFIG_PHONE is not set # # Input device support # -# CONFIG_INPUT is not set +CONFIG_INPUT=y # # Userland interfaces # +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set # # Input I/O drivers @@ -361,18 +394,23 @@ CONFIG_EEPRO100=y # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set +# CONFIG_SERIO_I8042 is not set # # Input Device Drivers # - -# -# Macintosh device drivers -# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set # # Character devices # +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y # CONFIG_SERIAL_NONSTANDARD is not set # @@ -380,50 +418,18 @@ CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 # CONFIG_SERIAL_8250_EXTENDED is not set # # Non-8250 serial port support # +# CONFIG_SERIAL_MPSC is not set CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y -CONFIG_UNIX98_PTY_COUNT=256 - -# -# I2C support -# -CONFIG_I2C=m -# CONFIG_I2C_ALGOBIT is not set -# CONFIG_I2C_ALGOPCF is not set -CONFIG_I2C_CHARDEV=m - -# -# I2C Hardware Sensors Mainboard support -# -# CONFIG_I2C_ALI15X3 is not set -# CONFIG_I2C_AMD756 is not set -# CONFIG_I2C_AMD8111 is not set -# CONFIG_I2C_I801 is not set -# CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_SIS96X is not set -# CONFIG_I2C_VIAPRO is not set - -# -# I2C Hardware Sensors Chip support -# -# CONFIG_SENSORS_ADM1021 is not set -# CONFIG_SENSORS_IT87 is not set -# CONFIG_SENSORS_LM75 is not set -# CONFIG_SENSORS_LM85 is not set -# CONFIG_SENSORS_VIA686A is not set -# CONFIG_SENSORS_W83781D is not set -# CONFIG_I2C_SENSOR is not set - -# -# Mice -# -# CONFIG_BUSMOUSE is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_QIC02_TAPE is not set # @@ -449,7 +455,15 @@ CONFIG_GEN_RTC=y # CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set -# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Misc devices +# # # Multimedia devices @@ -461,6 +475,33 @@ CONFIG_GEN_RTC=y # # CONFIG_DVB is not set +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + # # File systems # @@ -493,12 +534,14 @@ CONFIG_EXT2_FS=y # Pseudo filesystems # CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y CONFIG_DEVFS_FS=y # CONFIG_DEVFS_MOUNT is not set # CONFIG_DEVFS_DEBUG is not set -CONFIG_DEVPTS_FS=y # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # @@ -507,6 +550,7 @@ CONFIG_RAMFS=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 @@ -523,18 +567,18 @@ CONFIG_RAMFS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y # CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set CONFIG_ROOT_NFS=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y # CONFIG_EXPORTFS is not set CONFIG_SUNRPC=y -# CONFIG_SUNRPC_GSS is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set -# CONFIG_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -544,32 +588,22 @@ CONFIG_SUNRPC=y CONFIG_MSDOS_PARTITION=y # -# Sound +# Native Language Support # -# CONFIG_SOUND is not set - -# -# USB support -# -# CONFIG_USB is not set -# CONFIG_USB_GADGET is not set - -# -# Bluetooth support -# -# CONFIG_BT is not set +# CONFIG_NLS is not set # # Library routines # -# CONFIG_CRC32 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set # # Kernel hacking # # CONFIG_DEBUG_KERNEL is not set -# CONFIG_KALLSYMS is not set # CONFIG_SERIAL_TEXT_DEBUG is not set +CONFIG_PPC_OCP=y # # Security options diff --git a/arch/ppc/platforms/85xx/sbc8560.c b/arch/ppc/platforms/85xx/sbc8560.c index c32d662fe..2dddc0097 100644 --- a/arch/ppc/platforms/85xx/sbc8560.c +++ b/arch/ppc/platforms/85xx/sbc8560.c @@ -31,6 +31,7 @@ #include /* for linux/serial_core.h */ #include #include +#include #include #include @@ -92,6 +93,7 @@ sbc8560_early_serial_map(void) uart_req.iotype = SERIAL_IO_MEM; uart_req.mapbase = UARTA_ADDR; uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE); + uart_req.type = PORT_16650; #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) gen550_init(0, &uart_req); diff --git a/arch/ppc/platforms/85xx/sbc8560.h b/arch/ppc/platforms/85xx/sbc8560.h index 5c86187f8..6475d3079 100644 --- a/arch/ppc/platforms/85xx/sbc8560.h +++ b/arch/ppc/platforms/85xx/sbc8560.h @@ -29,11 +29,11 @@ #define BASE_BAUD ( 1843200 / 16 ) #ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#define STD_COM_FLAGS (ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) #else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#define STD_COM_FLAGS (ASYNC_SKIP_TEST) #endif - + #define STD_SERIAL_PORT_DFNS \ { 0, BASE_BAUD, UARTA_ADDR, MPC85xx_IRQ_EXT9, STD_COM_FLAGS, /* ttyS0 */ \ iomem_base: (u8 *)UARTA_ADDR, \ diff --git a/arch/ppc/platforms/85xx/sbc85xx.c b/arch/ppc/platforms/85xx/sbc85xx.c index a7a33fdff..8cc3323a6 100644 --- a/arch/ppc/platforms/85xx/sbc85xx.c +++ b/arch/ppc/platforms/85xx/sbc85xx.c @@ -61,38 +61,38 @@ extern unsigned long total_memory; /* in mm/init */ /* Internal interrupts are all Level Sensitive, and Positive Polarity */ static u_char sbc8560_openpic_initsenses[] __initdata = { - (IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ - (IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ - (IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ - (IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ - (IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ - (IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ - (IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ - (IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ - (IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ - (IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ - (IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ - (IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ - (IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ - (IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ - (IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ - (IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ - (IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ - (IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ - (IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ - (IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ - (IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ - (IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ - (IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ - (IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ - (IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ - (IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */ 0x0, /* External 0: */ 0x0, /* External 1: */ #if defined(CONFIG_PCI) diff --git a/arch/ppc/platforms/Makefile b/arch/ppc/platforms/Makefile index 42273c052..3a1c3c03b 100644 --- a/arch/ppc/platforms/Makefile +++ b/arch/ppc/platforms/Makefile @@ -32,7 +32,8 @@ obj-$(CONFIG_ADIR) += adir_setup.o adir_pic.o adir_pci.o obj-$(CONFIG_EST8260) += est8260_setup.o obj-$(CONFIG_PQ2ADS) += pq2ads_setup.o obj-$(CONFIG_TQM8260) += tqm8260_setup.o -obj-$(CONFIG_EV64260) += ev64260_setup.o +obj-$(CONFIG_EV64260) += ev64260.o +obj-$(CONFIG_DMV182) += dmv182.o obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o obj-$(CONFIG_K2) += k2.o obj-$(CONFIG_LOPEC) += lopec_setup.o lopec_pci.o diff --git a/arch/ppc/platforms/ev64260.h b/arch/ppc/platforms/ev64260.h index bc826d62e..156bfa890 100644 --- a/arch/ppc/platforms/ev64260.h +++ b/arch/ppc/platforms/ev64260.h @@ -1,42 +1,100 @@ /* * arch/ppc/platforms/ev64260.h - * + * * Definitions for Marvell/Galileo EV-64260-BP Evaluation Board. * * Author: Mark A. Greer * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */ /* - * The GT64260 has 2 PCI buses each with 1 window from the CPU bus to + * The MV64x60 has 2 PCI buses each with 1 window from the CPU bus to * PCI I/O space and 4 windows from the CPU bus to PCI MEM space. * We'll only use one PCI MEM window on each PCI bus. + * + * This is the CPU physical memory map (windows must be at least 1MB and start + * on a boundary that is a multiple of the window size): + * + * 0xfc000000-0xffffffff - External FLASH on device module + * 0xfbf00000-0xfbffffff - Embedded (on board) FLASH + * 0xfbe00000-0xfbefffff - GT64260 Registers (preferably) + * but really a config option + * 0xfbd00000-0xfbdfffff - External SRAM on device module + * 0xfbc00000-0xfbcfffff - TODC chip on device module + * 0xfbb00000-0xfbbfffff - External UART on device module + * 0xa2000000-0xfbafffff - + * 0xa1000000-0xa1ffffff - PCI 1 I/O (defined in gt64260.h) + * 0xa0000000-0xa0ffffff - PCI 0 I/O (defined in gt64260.h) + * 0x90000000-0x9fffffff - PCI 1 MEM (defined in gt64260.h) + * 0x80000000-0x8fffffff - PCI 0 MEM (defined in gt64260.h) */ #ifndef __PPC_PLATFORMS_EV64260_H #define __PPC_PLATFORMS_EV64260_H -#define EV64260_BRIDGE_REG_BASE 0xf8000000 -#define EV64260_BRIDGE_REG_BASE_TO_TOP 0x08000000U +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/* + * CPU Physical Memory Map setup. + */ +#define EV64260_EXT_FLASH_BASE 0xfc000000 +#define EV64260_EMB_FLASH_BASE 0xfbf00000 +#define EV64260_EXT_SRAM_BASE 0xfbd00000 +#define EV64260_TODC_BASE 0xfbc00000 +#define EV64260_UART_BASE 0xfbb00000 -#define EV64260_TODC_BASE 0xfc800000 -#define EV64260_TODC_LEN 0x00800000 -#define EV64260_TODC_END (EV64260_TODC_BASE + \ - EV64260_TODC_LEN - 1) +#define EV64260_EXT_FLASH_SIZE_ACTUAL 0x04000000 /* <= 64MB Extern FLASH */ +#define EV64260_EMB_FLASH_SIZE_ACTUAL 0x00080000 /* 512KB of Embed FLASH */ +#define EV64260_EXT_SRAM_SIZE_ACTUAL 0x00100000 /* 1MB SDRAM */ +#define EV64260_TODC_SIZE_ACTUAL 0x00000020 /* 32 bytes for TODC */ +#define EV64260_UART_SIZE_ACTUAL 0x00000040 /* 64 bytes for DUART */ + +#define EV64260_EXT_FLASH_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EXT_FLASH_SIZE_ACTUAL) +#define EV64260_EMB_FLASH_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EMB_FLASH_SIZE_ACTUAL) +#define EV64260_EXT_SRAM_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \ + EV64260_EXT_SRAM_SIZE_ACTUAL) +#define EV64260_TODC_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \ + EV64260_TODC_SIZE_ACTUAL) +#if 0 /* XXXX blows up assembler in bootloader */ +#define EV64260_UART_SIZE MAX(GT64260_WINDOW_SIZE_MIN, \ + EV64260_UART_SIZE_ACTUAL) +#else +#define EV64260_UART_SIZE GT64260_WINDOW_SIZE_MIN +#endif +#define EV64260_UART_END ((EV64260_UART_BASE + \ + EV64260_UART_SIZE - 1) & 0xfff00000) + +/* + * Board-specific IRQ info + */ +#define EV64260_UART_0_IRQ 85 +#define EV64260_UART_1_IRQ 86 +#define EV64260_PCI_0_IRQ 91 +#define EV64260_PCI_1_IRQ 93 -#define EV64260_UART_BASE 0xfd000000 -#define EV64260_UART_LEN 0x00800000 -#define EV64260_UART_END (EV64260_UART_BASE + \ - EV64260_UART_LEN - 1) -/* Serial driver setup. */ +/* + * Serial port setup. + */ +#define EV64260_DEFAULT_BAUD 115200 + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) +#define SERIAL_PORT_DFNS + +#define EV64260_MPSC_CLK_SRC 8 /* TCLK */ +#define EV64260_MPSC_CLK_FREQ 100000000 /* 100MHz clk */ +#else #define EV64260_SERIAL_0 (EV64260_UART_BASE + 0x20) #define EV64260_SERIAL_1 EV64260_UART_BASE -#define BASE_BAUD ( 3686400 / 16 ) +#define BASE_BAUD (EV64260_DEFAULT_BAUD * 2) #ifdef CONFIG_SERIAL_MANY_PORTS #define RS_TABLE_SIZE 64 @@ -50,18 +108,21 @@ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) #endif -#if !defined(CONFIG_GT64260_CONSOLE) /* Required for bootloader's ns16550.c code */ #define STD_SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, EV64260_SERIAL_0, 85, STD_COM_FLAGS, /* ttyS0 */\ - iomem_base: (u8 *)EV64260_SERIAL_0, \ + { 0, BASE_BAUD, EV64260_SERIAL_0, EV64260_UART_0_IRQ, STD_COM_FLAGS, \ + iomem_base: (u8 *)EV64260_SERIAL_0, /* ttyS0 */ \ + iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM }, + +#if 0 + { 1, BASE_BAUD, EV64260_SERIAL_1, EV64260_UART_1_IRQ, STD_COM_FLAGS, \ + iomem_base: (u8 *)EV64260_SERIAL_1, /* ttyS1 */ \ iomem_reg_shift: 2, \ io_type: SERIAL_IO_MEM }, +#endif #define SERIAL_PORT_DFNS \ STD_SERIAL_PORT_DFNS -#else -#define SERIAL_PORT_DFNS #endif - #endif /* __PPC_PLATFORMS_EV64260_H */ diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile index 313fe208e..fde6c4e72 100644 --- a/arch/ppc/syslib/Makefile +++ b/arch/ppc/syslib/Makefile @@ -43,9 +43,10 @@ obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o todc_time.o obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ todc_time.o obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o -obj-$(CONFIG_EV64260) += gt64260_common.o gt64260_pic.o \ - indirect_pci.o todc_time.o pci_auto.o +obj-$(CONFIG_EV64260) += indirect_pci.o todc_time.o pci_auto.o +obj-$(CONFIG_DMV182) += indirect_pci.o todc_time.o pci_auto.o obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o +obj-$(CONFIG_GT64260) += gt64260_pic.o obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \ pci_auto.o obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o @@ -53,6 +54,8 @@ obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \ open_pic.o i8259.o hawk_common.o obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \ pci_auto.o indirect_pci.o +obj-$(CONFIG_MV64360) += mv64360_pic.o +obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_ocp.o obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \ i8259.o pci_auto.o hawk_common.o obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o diff --git a/arch/ppc/syslib/gt64260_pic.c b/arch/ppc/syslib/gt64260_pic.c index 70cb5772d..e6f680a16 100644 --- a/arch/ppc/syslib/gt64260_pic.c +++ b/arch/ppc/syslib/gt64260_pic.c @@ -3,7 +3,7 @@ * * Interrupt controller support for Galileo's GT64260. * - * Author: Chris Zankel + * Author: Chris Zankel * Modified by: Mark A. Greer * * Based on sources from Rabeeh Khoury / Galileo Technology @@ -43,7 +43,8 @@ #include #include #include -#include +#include +#include /* ========================== forward declaration ========================== */ @@ -66,6 +67,9 @@ struct hw_interrupt_type gt64260_pic = { u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */ +static mv64x60_handle_t base_bh; +static mv64x60_handle_t ic_bh; + /* gt64260_init_irq() * * This function initializes the interrupt controller. It assigns @@ -87,19 +91,38 @@ u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */ __init void gt64260_init_irq(void) { + struct ocp_def *def; int i; +/* XXXX extract reg base, irq base from ocp */ +/* XXXX rewrite read/write macros to not use 'bh'?? */ +/* XXXX Have to use ocp b/c can pass arg to this routine */ + if ( ppc_md.progress ) ppc_md.progress("gt64260_init_irq: enter", 0x0); + if ((def = ocp_get_one_device(OCP_VENDOR_MARVELL, OCP_FUNC_HB, + OCP_ANY_INDEX)) == NULL) { + /* XXXX SCREAM */ + return; + } + base_bh.v_base = (u32)ioremap(def->paddr, 0x10000); /* XXXX */ + + if ((def = ocp_get_one_device(OCP_VENDOR_MARVELL, OCP_FUNC_PIC, + OCP_ANY_INDEX)) == NULL) { + /* XXXX SCREAM */ + return; + } + ic_bh.v_base = (u32)ioremap(def->paddr, 0x1000); /* XXXX */ + ppc_cached_irq_mask[0] = 0; ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ ppc_cached_irq_mask[2] = 0; /* disable all interrupts and clear current interrupts */ - gt_write(GT64260_GPP_INTR_MASK, ppc_cached_irq_mask[2]); - gt_write(GT64260_GPP_INTR_CAUSE,0); - gt_write(GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]); - gt_write(GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]); + mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]); + mv64x60_write(&base_bh, MV64x60_GPP_INTR_CAUSE,0); + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]); + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]); /* use the gt64260 for all (possible) interrupt sources */ for( i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++ ) { @@ -110,7 +133,8 @@ gt64260_init_irq(void) } -/* gt64260_get_irq() +/* + * gt64260_get_irq() * * This function returns the lowest interrupt number of all interrupts that * are currently asserted. @@ -131,18 +155,18 @@ gt64260_get_irq(struct pt_regs *regs) int irq; int irq_gpp; - irq = gt_read(GT64260_IC_MAIN_CAUSE_LO); + irq = mv64x60_read(&ic_bh, GT64260_IC_MAIN_CAUSE_LO); irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); if (irq == -1) { - irq = gt_read(GT64260_IC_MAIN_CAUSE_HI); + irq = mv64x60_read(&ic_bh, GT64260_IC_MAIN_CAUSE_HI); irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]); if (irq == -1) { irq = -2; /* bogus interrupt, should never happen */ } else { if (irq >= 24) { - irq_gpp = gt_read(GT64260_GPP_INTR_CAUSE); + irq_gpp = mv64x60_read(&base_bh, MV64x60_GPP_INTR_CAUSE); irq_gpp = __ilog2(irq_gpp & ppc_cached_irq_mask[2]); @@ -150,7 +174,7 @@ gt64260_get_irq(struct pt_regs *regs) irq = -2; } else { irq = irq_gpp + 64; - gt_write(GT64260_GPP_INTR_CAUSE, ~(1<<(irq-64))); + mv64x60_write(&base_bh, MV64x60_GPP_INTR_CAUSE, ~(1<<(irq-64))); } } else { irq += 32; @@ -182,20 +206,23 @@ gt64260_get_irq(struct pt_regs *regs) static void gt64260_unmask_irq(unsigned int irq) { + /* XXXX + printk("XXXX: *** unmask irq: %d\n", irq); + */ irq -= gt64260_irq_base; if (irq > 31) { if (irq > 63) { /* unmask GPP irq */ - gt_write(GT64260_GPP_INTR_MASK, + mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2] |= (1<<(irq-64))); } else { /* mask high interrupt register */ - gt_write(GT64260_IC_CPU_INTR_MASK_HI, + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1] |= (1<<(irq-32))); } } else { /* mask low interrupt register */ - gt_write(GT64260_IC_CPU_INTR_MASK_LO, + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0] |= (1< 31) { if (irq > 63) { /* mask GPP irq */ - gt_write(GT64260_GPP_INTR_MASK, + mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2] &= ~(1<<(irq-64))); } else { /* mask high interrupt register */ - gt_write(GT64260_IC_CPU_INTR_MASK_HI, + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1] &= ~(1<<(irq-32))); } } else { /* mask low interrupt register */ - gt_write(GT64260_IC_CPU_INTR_MASK_LO, + mv64x60_write(&ic_bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0] &= ~(1<>= 1) - if (cpu_online(i)) - mask |= (cpumask & 1) << smp_hw_index[i]; return mask; } #else @@ -607,12 +605,12 @@ void __devinit do_openpic_setup_cpu(void) { #ifdef CONFIG_IRQ_ALL_CPUS int i; - u32 msk; + cpumask_t msk = CPU_MASK_NONE; #endif spin_lock(&openpic_setup_lock); #ifdef CONFIG_IRQ_ALL_CPUS - msk = 1 << smp_hw_index[smp_processor_id()]; + cpu_set(smp_hw_index[smp_processor_id()], mask); /* let the openpic know we want intrs. default affinity * is 0xffffffff until changed via /proc @@ -621,7 +619,7 @@ void __devinit do_openpic_setup_cpu(void) * in irq.c. */ for (i = 0; i < NumSources; i++) - openpic_mapirq(i, msk, ~0U); + openpic_mapirq(i, msk, CPU_MASK_ALL); #endif /* CONFIG_IRQ_ALL_CPUS */ openpic_set_priority(0); @@ -649,11 +647,12 @@ static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) /* * Map a timer interrupt to one or more CPUs */ -static void __init openpic_maptimer(u_int timer, u_int cpumask) +static void __init openpic_maptimer(u_int timer, cpumask_t cpumask) { + cpumask_t phys = physmask(cpumask); check_arg_timer(timer); openpic_write(&OpenPIC->Global.Timer[timer].Destination, - physmask(cpumask)); + cpus_addr(phys)[0]); } /* @@ -770,13 +769,16 @@ openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) /* * Map an interrupt source to one or more CPUs */ -static void openpic_mapirq(u_int irq, u_int physmask, u_int keepmask) +static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask) { if (ISR[irq] == 0) return; - if (keepmask != 0) - physmask |= openpic_read(&ISR[irq]->Destination) & keepmask; - openpic_write(&ISR[irq]->Destination, physmask); + if (!cpus_empty(keepmask)) { + cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) }; + cpus_and(irqdest, irqdest, keepmask); + cpus_or(physmask, physmask, irqdest); + } + openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]); } #ifdef notused @@ -820,9 +822,9 @@ static void openpic_end_irq(unsigned int irq_nr) #endif } -static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) +static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask) { - openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), 0); + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE); } #ifdef CONFIG_SMP diff --git a/configs/kernel-2.6.7-i586-smp.config b/configs/kernel-2.6.7-i586-smp.config index 3e9e0b91e..01e6cf2ef 100644 --- a/configs/kernel-2.6.7-i586-smp.config +++ b/configs/kernel-2.6.7-i586-smp.config @@ -284,7 +284,8 @@ CONFIG_MTD=m CONFIG_MTD_PARTITIONS=m CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m -CONFIG_MTD_CMDLINE_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set # # User Modules And Translation Layers @@ -304,9 +305,21 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m @@ -324,7 +337,7 @@ CONFIG_MTD_SBC_GXX=m CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m CONFIG_MTD_AMD76XROM=m -CONFIG_MTD_ICH2ROM=m +# CONFIG_MTD_ICHXROM is not set CONFIG_MTD_SCB2_FLASH=m # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set @@ -338,6 +351,7 @@ CONFIG_MTD_PMC551=m # CONFIG_MTD_PMC551_BUGFIX is not set # CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -350,6 +364,7 @@ CONFIG_MTD_DOC2000=m # CONFIG_MTD_DOC2001 is not set CONFIG_MTD_DOC2001PLUS=m CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m # CONFIG_MTD_DOCPROBE_ADVANCED is not set CONFIG_MTD_DOCPROBE_ADDRESS=0 @@ -359,6 +374,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set # # Parallel port support @@ -2275,6 +2291,11 @@ CONFIG_EFS_FS=m CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_PROC is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set diff --git a/configs/kernel-2.6.7-i586.config b/configs/kernel-2.6.7-i586.config index f3994f04a..e54092da7 100644 --- a/configs/kernel-2.6.7-i586.config +++ b/configs/kernel-2.6.7-i586.config @@ -276,7 +276,8 @@ CONFIG_MTD=m CONFIG_MTD_PARTITIONS=m CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m -CONFIG_MTD_CMDLINE_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set # # User Modules And Translation Layers @@ -296,9 +297,21 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m @@ -316,7 +329,7 @@ CONFIG_MTD_SBC_GXX=m CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m CONFIG_MTD_AMD76XROM=m -CONFIG_MTD_ICH2ROM=m +# CONFIG_MTD_ICHXROM is not set CONFIG_MTD_SCB2_FLASH=m # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set @@ -330,6 +343,7 @@ CONFIG_MTD_PMC551=m # CONFIG_MTD_PMC551_BUGFIX is not set # CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -342,6 +356,7 @@ CONFIG_MTD_DOC2000=m # CONFIG_MTD_DOC2001 is not set CONFIG_MTD_DOC2001PLUS=m CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m # CONFIG_MTD_DOCPROBE_ADVANCED is not set CONFIG_MTD_DOCPROBE_ADDRESS=0 @@ -351,6 +366,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set # # Parallel port support @@ -2298,6 +2314,11 @@ CONFIG_EFS_FS=m CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_PROC is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set diff --git a/configs/kernel-2.6.7-i686-smp.config b/configs/kernel-2.6.7-i686-smp.config index 6551b56cf..33f49bfb2 100644 --- a/configs/kernel-2.6.7-i686-smp.config +++ b/configs/kernel-2.6.7-i686-smp.config @@ -288,7 +288,8 @@ CONFIG_MTD=m CONFIG_MTD_PARTITIONS=m CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m -CONFIG_MTD_CMDLINE_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set # # User Modules And Translation Layers @@ -308,9 +309,21 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m @@ -328,7 +341,7 @@ CONFIG_MTD_SBC_GXX=m CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m CONFIG_MTD_AMD76XROM=m -CONFIG_MTD_ICH2ROM=m +# CONFIG_MTD_ICHXROM is not set CONFIG_MTD_SCB2_FLASH=m # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set @@ -342,6 +355,7 @@ CONFIG_MTD_PMC551=m # CONFIG_MTD_PMC551_BUGFIX is not set # CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -354,6 +368,7 @@ CONFIG_MTD_DOC2000=m # CONFIG_MTD_DOC2001 is not set CONFIG_MTD_DOC2001PLUS=m CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m # CONFIG_MTD_DOCPROBE_ADVANCED is not set CONFIG_MTD_DOCPROBE_ADDRESS=0 @@ -363,6 +378,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set # # Parallel port support @@ -2278,6 +2294,11 @@ CONFIG_EFS_FS=m CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_PROC is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set diff --git a/configs/kernel-2.6.7-i686.config b/configs/kernel-2.6.7-i686.config index e7007e9e4..9c13093bb 100644 --- a/configs/kernel-2.6.7-i686.config +++ b/configs/kernel-2.6.7-i686.config @@ -277,7 +277,8 @@ CONFIG_MTD=m CONFIG_MTD_PARTITIONS=m CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m -CONFIG_MTD_CMDLINE_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set # # User Modules And Translation Layers @@ -297,9 +298,21 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m @@ -317,7 +330,7 @@ CONFIG_MTD_SBC_GXX=m CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m CONFIG_MTD_AMD76XROM=m -CONFIG_MTD_ICH2ROM=m +# CONFIG_MTD_ICHXROM is not set CONFIG_MTD_SCB2_FLASH=m # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set @@ -331,6 +344,7 @@ CONFIG_MTD_PMC551=m # CONFIG_MTD_PMC551_BUGFIX is not set # CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -343,6 +357,7 @@ CONFIG_MTD_DOC2000=m # CONFIG_MTD_DOC2001 is not set CONFIG_MTD_DOC2001PLUS=m CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m # CONFIG_MTD_DOCPROBE_ADVANCED is not set CONFIG_MTD_DOCPROBE_ADDRESS=0 @@ -352,6 +367,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set # # Parallel port support @@ -2299,6 +2315,11 @@ CONFIG_EFS_FS=m CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_PROC is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5fb011c37..2839166ac 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,4 +1,4 @@ -# $Id: Kconfig,v 1.3 2003/05/28 11:02:23 dwmw2 Exp $ +# $Id: Kconfig,v 1.5 2004/06/04 15:59:32 gleixner Exp $ menu "Memory Technology Devices (MTD)" @@ -68,9 +68,23 @@ config MTD_REDBOOT_PARTS SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. +config MTD_REDBOOT_PARTS_UNALLOCATED + bool " Include unallocated flash regions" + depends on MTD_REDBOOT_PARTS + help + If you need to register each unallocated flash region as a MTD + 'partition', enable this option. + +config MTD_REDBOOT_PARTS_READONLY + bool " Force read-only for RedBoot system images" + depends on MTD_REDBOOT_PARTS + help + If you need to force read-only for 'RedBoot', 'RedBoot Config' and + 'FIS directory' images, enable this option. + config MTD_CMDLINE_PARTS - tristate "Command line partition table parsing" - depends on MTD_PARTITIONS + bool "Command line partition table parsing" + depends on MTD_PARTITIONS = "y" ---help--- Allow generic configuration of the MTD paritition tables via the kernel command line. Multiple flash resources are supported for hardware where diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 2915a535d..85239c969 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -1,23 +1,7 @@ # # Makefile for the memory technology device drivers. # -# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $ - -# *** BIG UGLY NOTE *** -# -# The shiny new inter_module_xxx has introduced yet another ugly link -# order dependency, which I'd previously taken great care to avoid. -# We now have to ensure that the chip drivers are initialised before the -# map drivers, and that the doc200[01] drivers are initialised before -# docprobe. -# -# We'll hopefully merge the doc200[01] drivers and docprobe back into -# a single driver some time soon, but the CFI drivers are going to have -# to stay like that. -# -# Urgh. -# -# dwmw2 21/11/0 +# $Id: Makefile.common,v 1.3 2004/07/12 16:07:30 dwmw2 Exp $ # Core functionality. obj-$(CONFIG_MTD) += mtdcore.o diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 4ea08f5e9..801e6c7d0 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -21,7 +21,7 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: afs.c,v 1.12 2003/06/13 15:31:06 rmk Exp $ + $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $ ======================================================================*/ diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index e1c40ec77..1247669ac 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.3 2003/05/28 15:13:24 dwmw2 Exp $ +# $Id: Kconfig,v 1.8 2004/07/13 22:32:02 dwmw2 Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -85,59 +85,72 @@ config MTD_CFI_GEOMETRY arrangements of CFI chips. If unsure, say 'N' and all options which are supported by the current code will be enabled. -config MTD_CFI_B1 - bool "Support 8-bit buswidth" - depends on MTD_CFI_GEOMETRY +config MTD_MAP_BANK_WIDTH_1 + bool "Support 8-bit buswidth" if MTD_CFI_GEOMETRY + default y help If you wish to support CFI devices on a physical bus which is 8 bits wide, say 'Y'. -config MTD_CFI_B2 - bool "Support 16-bit buswidth" - depends on MTD_CFI_GEOMETRY +config MTD_MAP_BANK_WIDTH_2 + bool "Support 16-bit buswidth" if MTD_CFI_GEOMETRY + default y help If you wish to support CFI devices on a physical bus which is 16 bits wide, say 'Y'. -config MTD_CFI_B4 - bool "Support 32-bit buswidth" - depends on MTD_CFI_GEOMETRY +config MTD_MAP_BANK_WIDTH_4 + bool "Support 32-bit buswidth" if MTD_CFI_GEOMETRY + default y help If you wish to support CFI devices on a physical bus which is 32 bits wide, say 'Y'. -config MTD_CFI_B8 - bool "Support 64-bit buswidth" - depends on MTD_CFI_GEOMETRY +config MTD_MAP_BANK_WIDTH_8 + bool "Support 64-bit buswidth" if MTD_CFI_GEOMETRY + default n help If you wish to support CFI devices on a physical bus which is 64 bits wide, say 'Y'. +config MTD_MAP_BANK_WIDTH_16 + bool "Support 128-bit buswidth" if MTD_CFI_GEOMETRY + default n + help + If you wish to support CFI devices on a physical bus which is + 128 bits wide, say 'Y'. + +config MTD_MAP_BANK_WIDTH_32 + bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY + default n + help + If you wish to support CFI devices on a physical bus which is + 256 bits wide, say 'Y'. + config MTD_CFI_I1 - bool "Support 1-chip flash interleave" if !MTD_CFI_B1 - depends on MTD_CFI_GEOMETRY - default y if MTD_CFI_B1 + bool "Support 1-chip flash interleave" if MTD_CFI_GEOMETRY + default y help If your flash chips are not interleaved - i.e. you only have one flash chip addressed by each bus cycle, then say 'Y'. config MTD_CFI_I2 - bool "Support 2-chip flash interleave" - depends on MTD_CFI_GEOMETRY + bool "Support 2-chip flash interleave" if MTD_CFI_GEOMETRY + default y help If your flash chips are interleaved in pairs - i.e. you have two flash chips addressed by each bus cycle, then say 'Y'. config MTD_CFI_I4 - bool "Support 4-chip flash interleave" - depends on MTD_CFI_GEOMETRY + bool "Support 4-chip flash interleave" if MTD_CFI_GEOMETRY + default n help If your flash chips are interleaved in fours - i.e. you have four flash chips addressed by each bus cycle, then say 'Y'. config MTD_CFI_I8 - bool "Support 8-chip flash interleave" - depends on MTD_CFI_GEOMETRY + bool "Support 8-chip flash interleave" if MTD_CFI_GEOMETRY + default n help If your flash chips are interleaved in eights - i.e. you have eight flash chips addressed by each bus cycle, then say 'Y'. @@ -160,6 +173,27 @@ config MTD_CFI_AMDSTD provides support for one of those command sets, used on chips including the AMD Am29LV320. +config MTD_CFI_AMDSTD_RETRY + int "Retry failed commands (erase/program)" + depends on MTD_CFI_AMDSTD + default "0" + help + Some chips, when attached to a shared bus, don't properly filter + bus traffic that is destined to other devices. This broken + behavior causes erase and program sequences to be aborted when + the sequences are mixed with traffic for other devices. + + SST49LF040 (and related) chips are know to be broken. + +config MTD_CFI_AMDSTD_RETRY_MAX + int "Max retries of failed commands (erase/program)" + depends on MTD_CFI_AMDSTD_RETRY + default "0" + help + If you have an SST49LF040 (or related chip) then this value should + be set to at least 1. This can also be adjusted at driver load + time with the retry_cmd_max module parameter. + config MTD_CFI_STAA tristate "Support for ST (Advanced Architecture) flash chips" depends on MTD_GEN_PROBE @@ -168,6 +202,11 @@ config MTD_CFI_STAA sets which a CFI-compliant chip may claim to implement. This code provides support for one of those command sets. +config MTD_CFI_UTIL + tristate + default y if MTD_CFI_INTELEXT=y || MTD_CFI_AMDSTD=y || MTD_CFI_STAA=y + default m if MTD_CFI_INTELEXT=m || MTD_CFI_AMDSTD=m || MTD_CFI_STAA=m + config MTD_RAM tristate "Support for RAM chips in bus mapping" depends on MTD diff --git a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile index 7293717af..683048982 100644 --- a/drivers/mtd/chips/Makefile +++ b/drivers/mtd/chips/Makefile @@ -1,18 +1,19 @@ # # linux/drivers/chips/Makefile # -# $Id: Makefile.common,v 1.1 2003/05/21 15:00:01 dwmw2 Exp $ +# $Id: Makefile.common,v 1.4 2004/07/12 16:07:30 dwmw2 Exp $ # *** BIG UGLY NOTE *** # # The removal of get_module_symbol() and replacement with # inter_module_register() et al has introduced a link order dependency # here where previously there was none. We now have to ensure that -# the CFI command set drivers are linked before cfi_probe.o +# the CFI command set drivers are linked before gen_probe.o obj-$(CONFIG_MTD) += chipreg.o obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o obj-$(CONFIG_MTD_CFI) += cfi_probe.o +obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index fc7d0f259..ca710c78c 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg * - * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.24 2004/07/12 13:34:30 dwmw2 Exp $ * * Copyright (c) 2001 Axis Communications AB * diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 25a40ec7e..a36168e84 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.126 2003/06/23 07:45:48 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.153 2004/07/12 21:52:20 dwmw2 Exp $ * * * 10/10/2000 Nicolas Pitre @@ -34,12 +34,14 @@ #include #include +/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ + // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); @@ -54,6 +56,7 @@ static void cfi_intelext_destroy(struct mtd_info *); struct mtd_info *cfi_cmdset_0001(struct map_info *, int); static struct mtd_info *cfi_intelext_setup (struct map_info *); +static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **); static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); @@ -79,17 +82,18 @@ static struct mtd_chip_driver cfi_intelext_chipdrv = { static void cfi_tell_features(struct cfi_pri_intelext *extp) { int i; - printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); - printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); - printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); - printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); - printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); - printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); - printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); - printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); - printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); - printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); - for (i=9; i<32; i++) { + printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); + printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); + printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); + printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); + printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); + printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); + printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); + printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); + printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); + printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); + printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported"); + for (i=10; i<32; i++) { if (extp->FeatureSupport & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); + extp->VccOptimal >> 4, extp->VccOptimal & 0xf); if (extp->VppOptimal) printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", - extp->VppOptimal >> 8, extp->VppOptimal & 0xf); + extp->VppOptimal >> 4, extp->VppOptimal & 0xf); +} +#endif + +#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ +static void fixup_intel_strataflash(struct map_info *map, void* param) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + + printk(KERN_WARNING "cfi_cmdset_0001: Suspend " + "erase on write disabled.\n"); + extp->SuspendCmdSupport &= ~1; +} +#endif + +static void fixup_st_m28w320ct(struct map_info *map, void* param) +{ + struct cfi_private *cfi = map->fldrv_priv; + + cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ + cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ } + +static void fixup_st_m28w320cb(struct map_info *map, void* param) +{ + struct cfi_private *cfi = map->fldrv_priv; + + /* Note this is done after the region info is endian swapped */ + cfi->cfiq->EraseRegionInfo[1] = + (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; +}; + +static struct cfi_fixup fixup_table[] = { +#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE + { + CFI_MFR_ANY, CFI_ID_ANY, + fixup_intel_strataflash, NULL + }, #endif + { + 0x0020, /* STMicroelectronics */ + 0x00ba, /* M28W320CT */ + fixup_st_m28w320ct, NULL + }, { + 0x0020, /* STMicroelectronics */ + 0x00bb, /* M28W320CB */ + fixup_st_m28w320cb, NULL + }, { + 0, 0, NULL, NULL + } +}; /* This routine is made available to other mtd code via * inter_module_register. It must only be accessed through @@ -128,7 +182,6 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; int i; - __u32 base = cfi->chips[0].start; if (cfi->cfi_mode == CFI_MODE_CFI) { /* @@ -138,40 +191,20 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_intelext *extp; - int ofs_factor = cfi->interleave * cfi->device_type; - //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); - if (!adr) + extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp"); + if (!extp) return NULL; - - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - - extp = kmalloc(sizeof(*extp), GFP_KERNEL); - if (!extp) { - printk(KERN_ERR "Failed to allocate memory\n"); - return NULL; - } - - /* Read in the Extended Query Table */ - for (i=0; iMajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { - printk(KERN_WARNING " Unknown IntelExt Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - return NULL; - } /* Do some byteswapping if necessary */ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + + /* Install our own private info structure */ + cfi->cmdset_priv = extp; + + cfi_fixup(map, fixup_table); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ @@ -179,18 +212,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) #endif if(extp->SuspendCmdSupport & 1) { -//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE -#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE -/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ - printk(KERN_WARNING "cfi_cmdset_0001: Suspend " - "erase on write disabled.\n"); - extp->SuspendCmdSupport &= ~1; -#else printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); -#endif } - /* Install our own private info structure */ - cfi->cmdset_priv = extp; } for (i=0; i< cfi->numchips; i++) { @@ -202,8 +225,6 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) map->fldrv = &cfi_intelext_chipdrv; - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_intelext_setup(map); } @@ -281,8 +302,10 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) printk(KERN_INFO "Using word write method\n" ); mtd->write = cfi_intelext_write_words; } +#if 0 mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; +#endif mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; @@ -291,6 +314,12 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) mtd->flags = MTD_CAP_NORFLASH; map->fldrv = &cfi_intelext_chipdrv; mtd->name = map->name; + + /* This function has the potential to distort the reality + a bit and therefore should be called last. */ + if (cfi_intelext_partition_fixup(map, &cfi) != 0) + goto setup_err; + __module_get(THIS_MODULE); return mtd; @@ -301,10 +330,87 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) kfree(mtd); } kfree(cfi->cmdset_priv); - kfree(cfi->cfiq); return NULL; } +static int cfi_intelext_partition_fixup(struct map_info *map, + struct cfi_private **pcfi) +{ + struct cfi_private *cfi = *pcfi; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + + /* + * Probing of multi-partition flash ships. + * + * This is extremely crude at the moment and should probably be + * extracted entirely from the Intel extended query data instead. + * Right now a L18 flash is assumed if multiple operations is + * detected. + * + * To support multiple partitions when available, we simply arrange + * for each of them to have their own flchip structure even if they + * are on the same physical chip. This means completely recreating + * a new cfi_private structure right here which is a blatent code + * layering violation, but this is still the least intrusive + * arrangement at this point. This can be rearranged in the future + * if someone feels motivated enough. --nico + */ + if (extp && extp->FeatureSupport & (1 << 9)) { + struct cfi_private *newcfi; + struct flchip *chip; + struct flchip_shared *shared; + int numparts, partshift, numvirtchips, i, j; + + /* + * The L18 flash memory array is divided + * into multiple 8-Mbit partitions. + */ + numparts = 1 << (cfi->cfiq->DevSize - 20); + partshift = 20 + __ffs(cfi->interleave); + numvirtchips = cfi->numchips * numparts; + + newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); + if (!newcfi) + return -ENOMEM; + shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL); + if (!shared) { + kfree(newcfi); + return -ENOMEM; + } + memcpy(newcfi, cfi, sizeof(struct cfi_private)); + newcfi->numchips = numvirtchips; + newcfi->chipshift = partshift; + + chip = &newcfi->chips[0]; + for (i = 0; i < cfi->numchips; i++) { + shared[i].writing = shared[i].erasing = NULL; + spin_lock_init(&shared[i].lock); + for (j = 0; j < numparts; j++) { + *chip = cfi->chips[i]; + chip->start += j << partshift; + chip->priv = &shared[i]; + /* those should be reset too since + they create memory references. */ + init_waitqueue_head(&chip->wq); + spin_lock_init(&chip->_spinlock); + chip->mutex = &chip->_spinlock; + chip++; + } + } + + printk(KERN_DEBUG "%s: %d sets of %d interleaved chips " + "--> %d partitions of %#x bytes\n", + map->name, cfi->numchips, cfi->interleave, + newcfi->numchips, 1<chipshift); + + map->fldrv_priv = newcfi; + *pcfi = newcfi; + kfree(cfi); + } + + return 0; +} + /* * *********** CHIP ACCESS FUNCTIONS *********** */ @@ -313,25 +419,87 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr { DECLARE_WAITQUEUE(wait, current); struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK = CMD(0x80); + map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01); unsigned long timeo; - struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; resettime: timeo = jiffies + HZ; retry: + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) { + /* + * OK. We have possibility for contension on the write/erase + * operations which are global to the real chip and not per + * partition. So let's fight it over in the partition which + * currently has authority on the operation. + * + * The rules are as follows: + * + * - any write operation must own shared->writing. + * + * - any erase operation must own _both_ shared->writing and + * shared->erasing. + * + * - contension arbitration is handled in the owner's context. + * + * The 'shared' struct can be read when its lock is taken. + * However any writes to it can only be made when the current + * owner's lock is also held. + */ + struct flchip_shared *shared = chip->priv; + struct flchip *contender; + spin_lock(&shared->lock); + contender = shared->writing; + if (contender && contender != chip) { + /* + * The engine to perform desired operation on this + * partition is already in use by someone else. + * Let's fight over it in the context of the chip + * currently using it. If it is possible to suspend, + * that other partition will do just that, otherwise + * it'll happily send us to sleep. In any case, when + * get_chip returns success we're clear to go ahead. + */ + int ret = spin_trylock(contender->mutex); + spin_unlock(&shared->lock); + if (!ret) + goto retry; + spin_unlock(chip->mutex); + ret = get_chip(map, contender, contender->start, mode); + spin_lock(chip->mutex); + if (ret) { + spin_unlock(contender->mutex); + return ret; + } + timeo = jiffies + HZ; + spin_lock(&shared->lock); + } + + /* We now own it */ + shared->writing = chip; + if (mode == FL_ERASING) + shared->erasing = chip; + if (contender && contender != chip) + spin_unlock(contender->mutex); + spin_unlock(&shared->lock); + } + switch (chip->state) { case FL_STATUS: for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) + break; + + /* At this point we're fine with write operations + in other partitions as they don't conflict. */ + if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) break; if (time_after(jiffies, timeo)) { - printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", - (long long)status); - spin_unlock(chip->mutex); + printk(KERN_ERR "Waiting for chip to be ready timed out. Status %lx\n", + status.x[0]); return -EIO; } spin_unlock(chip->mutex); @@ -354,31 +522,31 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr /* Erase suspend */ - cfi_write(map, CMD(0xB0), adr); + map_write(map, CMD(0xB0), adr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to * 'read' mode. Make sure that we switch to 'read status' * mode so we get the right data. --rmk */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; if (time_after(jiffies, timeo)) { /* Urgh. Resume and pretend we weren't here. */ - cfi_write(map, CMD(0xd0), adr); + map_write(map, CMD(0xd0), adr); /* Make sure we're in 'read status' mode if it had finished */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_ERASING; chip->oldstate = FL_READY; printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%llx\n", status); + "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } @@ -412,6 +580,32 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad { struct cfi_private *cfi = map->fldrv_priv; + if (chip->priv) { + struct flchip_shared *shared = chip->priv; + spin_lock(&shared->lock); + if (shared->writing == chip) { + /* We own the ability to write, but we're done */ + shared->writing = shared->erasing; + if (shared->writing && shared->writing != chip) { + /* give back ownership to who we loaned it from */ + struct flchip *loaner = shared->writing; + spin_lock(loaner->mutex); + spin_unlock(&shared->lock); + spin_unlock(chip->mutex); + put_chip(map, loaner, loaner->start); + spin_lock(chip->mutex); + spin_unlock(loaner->mutex); + } else { + if (chip->oldstate != FL_ERASING) { + shared->erasing = NULL; + if (chip->oldstate != FL_WRITING) + shared->writing = NULL; + } + spin_unlock(&shared->lock); + } + } + } + switch(chip->oldstate) { case FL_ERASING: chip->state = chip->oldstate; @@ -424,14 +618,15 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad sending the 0x70 (Read Status) command to an erasing chip and expecting it to be ignored, that's what we do. */ - cfi_write(map, CMD(0xd0), adr); - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), adr); chip->oldstate = FL_READY; chip->state = FL_ERASING; break; case FL_READY: case FL_STATUS: + case FL_JEDEC_QUERY: /* We should really make set_vpp() count, rather than doing this */ DISABLE_VPP(map); break; @@ -450,7 +645,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a adr += chip->start; /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + cmd_addr = adr & ~(map_bankwidth(map)-1); spin_lock(chip->mutex); @@ -458,7 +653,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a if (!ret) { if (chip->state != FL_POINT && chip->state != FL_READY) - cfi_write(map, CMD(0xff), cmd_addr); + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_POINT; chip->ref_point_counter++; @@ -476,12 +671,10 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si int chipnum; int ret = 0; - if (from + len > mtd->size) + if (!map->virt || (from + len > mtd->size)) return -EINVAL; *mtdbuf = (void *)map->virt + from; - if(*mtdbuf == NULL) - return -EINVAL; /* can not point this region */ *retlen = 0; /* Now lock the chip(s) to POINT state */ @@ -566,7 +759,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof adr += chip->start; /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + cmd_addr = adr & ~(map_bankwidth(map)-1); spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); @@ -576,7 +769,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof } if (chip->state != FL_POINT && chip->state != FL_READY) { - cfi_write(map, CMD(0xff), cmd_addr); + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; } @@ -627,7 +820,7 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz } return ret; } - +#if 0 static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) { struct map_info *map = mtd->priv; @@ -658,7 +851,7 @@ static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t } if (chip->state != FL_JEDEC_QUERY) { - cfi_write(map, CMD(0x90), chip->start); + map_write(map, CMD(0x90), chip->start); chip->state = FL_JEDEC_QUERY; } @@ -718,12 +911,12 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, s return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); } +#endif - -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum) +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long timeo; int z, ret=0; @@ -740,11 +933,12 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned } ENABLE_VPP(map); - cfi_write(map, CMD(0x40), adr); - cfi_write(map, datum, adr); + map_write(map, CMD(0x40), adr); + map_write(map, datum, adr); chip->state = FL_WRITING; spin_unlock(chip->mutex); + INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); cfi_udelay(chip->word_write_time); spin_lock(chip->mutex); @@ -765,8 +959,8 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned continue; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ @@ -794,11 +988,11 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned /* Done and happy. */ chip->state = FL_STATUS; /* check for lock bit */ - if (status & CMD(0x02)) { + if (map_word_bitsset(map, status, CMD(0x02))) { /* clear status */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); ret = -EROFS; } out: @@ -825,35 +1019,22 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ofs = to - (chipnum << cfi->chipshift); /* If it's not bus-aligned, do the first byte write */ - if (ofs & (CFIDEV_BUSWIDTH-1)) { - unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + if (ofs & (map_bankwidth(map)-1)) { + unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); int gap = ofs - bus_ofs; - int i = 0, n = 0; - u_char tmp_buf[8]; - cfi_word datum; - - while (gap--) - tmp_buf[i++] = 0xff; - while (len && i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = buf[n++], len--; - while (i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = 0xff; - - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else if (cfi_buswidth_is_8()) { - datum = *(__u64*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } + int n; + map_word datum; + + n = min_t(int, len, map_bankwidth(map)-gap); + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, gap, n); ret = do_write_oneword(map, &cfi->chips[chipnum], bus_ofs, datum); if (ret) return ret; - + + len -= n; ofs += n; buf += n; (*retlen) += n; @@ -866,30 +1047,18 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le } } - while(len >= CFIDEV_BUSWIDTH) { - cfi_word datum; - - if (cfi_buswidth_is_1()) { - datum = *(__u8*)buf; - } else if (cfi_buswidth_is_2()) { - datum = *(__u16*)buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)buf; - } else if (cfi_buswidth_is_8()) { - datum = *(__u64*)buf; - } else { - return -EINVAL; - } + while(len >= map_bankwidth(map)) { + map_word datum = map_word_load(map, buf); ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); if (ret) return ret; - ofs += CFIDEV_BUSWIDTH; - buf += CFIDEV_BUSWIDTH; - (*retlen) += CFIDEV_BUSWIDTH; - len -= CFIDEV_BUSWIDTH; + ofs += map_bankwidth(map); + buf += map_bankwidth(map); + (*retlen) += map_bankwidth(map); + len -= map_bankwidth(map); if (ofs >> cfi->chipshift) { chipnum ++; @@ -899,32 +1068,18 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le } } - if (len & (CFIDEV_BUSWIDTH-1)) { - int i = 0, n = 0; - u_char tmp_buf[8]; - cfi_word datum; - - while (len--) - tmp_buf[i++] = buf[n++]; - while (i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = 0xff; - - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else if (cfi_buswidth_is_8()) { - datum = *(__u64*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ - } + if (len & (map_bankwidth(map)-1)) { + map_word datum; + + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, 0, len); ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); if (ret) return ret; - (*retlen) += n; + (*retlen) += len; } return 0; @@ -935,11 +1090,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long cmd_adr, timeo; int wbufsize, z, ret=0, bytes, words; - wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); @@ -953,29 +1108,28 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, return ret; } - if (chip->state != FL_STATUS) - cfi_write(map, CMD(0x70), cmd_adr); - - status = cfi_read(map, cmd_adr); - /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise we're just pissing in the wind */ - if (status & CMD(0x30)) { - printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %llx). Clearing.\n", status); - cfi_write(map, CMD(0x50), cmd_adr); - cfi_write(map, CMD(0x70), cmd_adr); + if (chip->state != FL_STATUS) + map_write(map, CMD(0x70), cmd_adr); + status = map_read(map, cmd_adr); + if (map_word_bitsset(map, status, CMD(0x30))) { + printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); + map_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); } + ENABLE_VPP(map); chip->state = FL_WRITING_TO_BUFFER; z = 0; for (;;) { - cfi_write(map, CMD(0xe8), cmd_adr); + map_write(map, CMD(0xe8), cmd_adr); - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; spin_unlock(chip->mutex); @@ -984,84 +1138,47 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, if (++z > 20) { /* Argh. Not ready for write to buffer */ - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr)); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx, status = %lx\n", + status.x[0], map_read(map, cmd_adr).x[0]); /* Odd. Clear status bits */ - cfi_write(map, CMD(0x50), cmd_adr); - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); ret = -EIO; goto out; } } /* Write length of data to come */ - bytes = len & (CFIDEV_BUSWIDTH-1); - words = len / CFIDEV_BUSWIDTH; - cfi_write(map, CMD(words - !bytes), cmd_adr ); + bytes = len & (map_bankwidth(map)-1); + words = len / map_bankwidth(map); + map_write(map, CMD(words - !bytes), cmd_adr ); /* Write data */ z = 0; - while(z < words * CFIDEV_BUSWIDTH) { - if (cfi_buswidth_is_1()) { - u8 *b = (u8 *)buf; - - map_write8 (map, *b++, adr+z); - buf = (const u_char *)b; - } else if (cfi_buswidth_is_2()) { - u16 *b = (u16 *)buf; - - map_write16 (map, *b++, adr+z); - buf = (const u_char *)b; - } else if (cfi_buswidth_is_4()) { - u32 *b = (u32 *)buf; - - map_write32 (map, *b++, adr+z); - buf = (const u_char *)b; - } else if (cfi_buswidth_is_8()) { - u64 *b = (u64 *)buf; - - map_write64 (map, *b++, adr+z); - buf = (const u_char *)b; - } else { - ret = -EINVAL; - goto out; - } - z += CFIDEV_BUSWIDTH; + while(z < words * map_bankwidth(map)) { + map_word datum = map_word_load(map, buf); + map_write(map, datum, adr+z); + + z += map_bankwidth(map); + buf += map_bankwidth(map); } + if (bytes) { - int i = 0, n = 0; - u_char tmp_buf[8], *tmp_p = tmp_buf; - - while (bytes--) - tmp_buf[i++] = buf[n++]; - while (i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = 0xff; - if (cfi_buswidth_is_2()) { - u16 *b = (u16 *)tmp_p; - - map_write16 (map, *b++, adr+z); - tmp_p = (u_char *)b; - } else if (cfi_buswidth_is_4()) { - u32 *b = (u32 *)tmp_p; - - map_write32 (map, *b++, adr+z); - tmp_p = (u_char *)b; - } else if (cfi_buswidth_is_8()) { - u64 *b = (u64 *)tmp_p; - - map_write64 (map, *b++, adr+z); - tmp_p = (u_char *)b; - } else { - ret = -EINVAL; - goto out; - } + map_word datum; + + datum = map_word_ff(map); + datum = map_word_load_partial(map, datum, buf, 0, bytes); + map_write(map, datum, adr+z); } + /* GO GO GO */ - cfi_write(map, CMD(0xd0), cmd_adr); + map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; spin_unlock(chip->mutex); + INVALIDATE_CACHED_RANGE(map, adr, len); cfi_udelay(chip->buffer_write_time); spin_lock(chip->mutex); @@ -1081,8 +1198,8 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, continue; } - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ @@ -1111,11 +1228,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, chip->state = FL_STATUS; /* check for lock bit */ - if (status & CMD(0x02)) { + if (map_word_bitsset(map, status, CMD(0x02))) { /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); ret = -EROFS; } @@ -1130,7 +1247,7 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; @@ -1143,8 +1260,8 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, ofs = to - (chipnum << cfi->chipshift); /* If it's not bus-aligned, do the first word write */ - if (ofs & (CFIDEV_BUSWIDTH-1)) { - size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); + if (ofs & (map_bankwidth(map)-1)) { + size_t local_len = (-ofs)&(map_bankwidth(map)-1); if (local_len > len) local_len = len; ret = cfi_intelext_write_words(mtd, to, local_len, @@ -1163,7 +1280,6 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, } } - /* Write buffer is worth it only if more than one word to write... */ while(len) { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); @@ -1191,7 +1307,7 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, } typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, void *thunk); + unsigned long adr, int len, void *thunk); static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, loff_t ofs, size_t len, void *thunk) @@ -1258,15 +1374,19 @@ static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, i=first; while(len) { - ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); + unsigned long chipmask; + int size = regions[i].erasesize; + + ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); if (ret) return ret; - adr += regions[i].erasesize; - len -= regions[i].erasesize; + adr += size; + len -= size; - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + chipmask = (1 << cfi->chipshift) - 1; + if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) i++; if (adr >> cfi->chipshift) { @@ -1282,10 +1402,11 @@ static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, } -static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int do_erase_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); @@ -1306,15 +1427,16 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned ENABLE_VPP(map); /* Clear the status register first */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* Now erase */ - cfi_write(map, CMD(0x20), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x20), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; chip->erase_suspended = 0; spin_unlock(chip->mutex); + INVALIDATE_CACHED_RANGE(map, adr, len); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((chip->erase_time*HZ)/(2*1000)); spin_lock(chip->mutex); @@ -1341,19 +1463,19 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned chip->erase_suspended = 0; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n", - adr, (__u64)status, (__u64)cfi_read(map, adr)); + printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %lx, status = %lx.\n", + adr, status.x[0], map_read(map, adr).x[0]); /* Clear status bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); DISABLE_VPP(map); spin_unlock(chip->mutex); return -EIO; @@ -1370,43 +1492,46 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned ret = 0; /* We've broken this before. It doesn't hurt to be safe */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - status = cfi_read(map, adr); + status = map_read(map, adr); /* check for lock bit */ - if (status & CMD(0x3a)) { - unsigned char chipstatus = status; - if (status != CMD(status & 0xff)) { - int i; - for (i = 1; i> (cfi->device_type * 8); + if (map_word_bitsset(map, status, CMD(0x3a))) { + unsigned char chipstatus = status.x[0]; + if (!map_word_equal(map, status, CMD(chipstatus))) { + int i, w; + for (w=0; w> (cfi->device_type * 8); + } } - printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus); + printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", + status.x[0], chipstatus); } /* Reset the error bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ - printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%llx\n", (__u64)status); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; chip->state = FL_STATUS; spin_unlock(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); ret = -EIO; } } @@ -1476,7 +1601,8 @@ static void cfi_intelext_sync (struct mtd_info *mtd) } #ifdef DEBUG_LOCK_BITS -static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; int ofs_factor = cfi->interleave * cfi->device_type; @@ -1484,8 +1610,7 @@ static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", adr, cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - + chip->state = FL_JEDEC_QUERY; return 0; } #endif @@ -1493,10 +1618,11 @@ static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - cfi_word status, status_OK; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; int ret; @@ -1513,13 +1639,13 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigne } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); + map_write(map, CMD(0x60), adr); if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { - cfi_write(map, CMD(0x01), adr); + map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; } else BUG(); @@ -1534,15 +1660,16 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigne timeo = jiffies + (HZ*20); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr)); + printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", + status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock(chip->mutex); return -EIO; @@ -1576,8 +1703,8 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); #ifdef DEBUG_LOCK_BITS - printk(KERN_DEBUG - "%s: lock status after, ret=%d\n", __FUNCTION__, ret); + printk(KERN_DEBUG "%s: lock status after, ret=%d\n", + __FUNCTION__, ret); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif @@ -1600,7 +1727,8 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); #ifdef DEBUG_LOCK_BITS - printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); + printk(KERN_DEBUG "%s: lock status after, ret=%d\n", + __FUNCTION__, ret); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif @@ -1680,7 +1808,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { - cfi_write(map, CMD(0xFF), 0); + map_write(map, CMD(0xFF), cfi->chips[i].start); chip->state = FL_READY; wake_up(&chip->wq); } @@ -1695,6 +1823,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd) struct cfi_private *cfi = map->fldrv_priv; kfree(cfi->cmdset_priv); kfree(cfi->cfiq); + kfree(cfi->chips[0].priv); kfree(cfi); kfree(mtd->eraseregions); } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index cb5fcfcac..d7b54d8f6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -3,15 +3,21 @@ * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) * * Copyright (C) 2000 Crossnet Co. + * Copyright (C) 2004 Arcom Control Systems Ltd * * 2_by_8 routines added by Simon Munton * + * 4_by_16 work by Carolyn J. Smith + * + * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.74 2003/05/28 12:51:48 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.103 2004/07/14 16:24:03 dwmw2 Exp $ * */ +#include #include #include #include @@ -24,18 +30,23 @@ #include #include #include +#include #include #include #include -#include #define AMD_BOOTLOC_BUG +#define FORCE_WORD_WRITE 0 + +#define MAX_WORD_RETRIES 3 static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); -static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); +static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t); +static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); @@ -55,50 +66,129 @@ static struct mtd_chip_driver cfi_amdstd_chipdrv = { }; +/* #define DEBUG_LOCK_BITS */ +/* #define DEBUG_CFI_FEATURES */ + + +#ifdef DEBUG_CFI_FEATURES +static void cfi_tell_features(struct cfi_pri_amdstd *extp) +{ + const char* erase_suspend[3] = { + "Not supported", "Read only", "Read/write" + }; + const char* top_bottom[6] = { + "No WP", "8x8KiB sectors at top & bottom, no WP", + "Bottom boot", "Top boot", + "Uniform, Bottom WP", "Uniform, Top WP" + }; + + printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); + printk(" Address sensitive unlock: %s\n", + (extp->SiliconRevision & 1) ? "Not required" : "Required"); + + if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) + printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); + else + printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); + + if (extp->BlkProt == 0) + printk(" Block protection: Not supported\n"); + else + printk(" Block protection: %d sectors per group\n", extp->BlkProt); + + + printk(" Temporary block unprotect: %s\n", + extp->TmpBlkUnprotect ? "Supported" : "Not supported"); + printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); + printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); + printk(" Burst mode: %s\n", + extp->BurstMode ? "Supported" : "Not supported"); + if (extp->PageMode == 0) + printk(" Page mode: Not supported\n"); + else + printk(" Page mode: %d word page\n", extp->PageMode << 2); + + printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", + extp->VppMin >> 4, extp->VppMin & 0xf); + printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", + extp->VppMax >> 4, extp->VppMax & 0xf); + + if (extp->TopBottom < ARRAY_SIZE(top_bottom)) + printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); + else + printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); +} +#endif + +#ifdef AMD_BOOTLOC_BUG +/* Wheee. Bring me the head of someone at AMD. */ +static void fixup_amd_bootblock(struct map_info *map, void* param) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + __u8 major = extp->MajorVersion; + __u8 minor = extp->MinorVersion; + + if (((major << 8) | minor) < 0x3131) { + /* CFI version 1.0 => don't trust bootloc */ + if (cfi->id & 0x80) { + printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); + extp->TopBottom = 3; /* top boot */ + } else { + extp->TopBottom = 2; /* bottom boot */ + } + } +} +#endif + +static struct cfi_fixup fixup_table[] = { +#ifdef AMD_BOOTLOC_BUG + { + 0x0001, /* AMD */ + CFI_ID_ANY, + fixup_amd_bootblock, NULL + }, +#endif + { 0, 0, NULL, NULL } +}; + + struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; unsigned char bootloc; - int ofs_factor = cfi->interleave * cfi->device_type; int i; - __u8 major, minor; - __u32 base = cfi->chips[0].start; if (cfi->cfi_mode==CFI_MODE_CFI){ + /* + * It's a real CFI chip, not one for which the probe + * routine faked a CFI structure. So we read the feature + * table from it. + */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_amdstd *extp; + + extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); + if (!extp) + return NULL; + + /* Install our own private info structure */ + cfi->cmdset_priv = extp; + + cfi_fixup(map, fixup_table); + +#ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); +#endif + + bootloc = extp->TopBottom; + if ((bootloc != 2) && (bootloc != 3)) { + printk(KERN_WARNING "%s: CFI does not contain boot " + "bank location. Assuming top.\n", map->name); + bootloc = 2; + } - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - - major = cfi_read_query(map, base + (adr+3)*ofs_factor); - minor = cfi_read_query(map, base + (adr+4)*ofs_factor); - - printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", - major, minor, adr); - cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); - - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - /* FIXME - should have a delay before continuing */ - cfi->mfr = cfi_read_query(map, base); - cfi->id = cfi_read_query(map, base + ofs_factor); - - /* Wheee. Bring me the head of someone at AMD. */ -#ifdef AMD_BOOTLOC_BUG - if (((major << 8) | minor) < 0x3131) { - /* CFI version 1.0 => don't trust bootloc */ - if (cfi->id & 0x80) { - printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); - bootloc = 3; /* top boot */ - } else { - bootloc = 2; /* bottom boot */ - } - } else -#endif - { - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); - } if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); @@ -112,31 +202,41 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) } } /* - * FIXME - These might already be setup (more correctly) - * buy jedec_probe.c. + * These might already be setup (more correctly) by + * jedec_probe.c - still need it for cfi_probe.c path. */ - switch (cfi->device_type) { - case CFI_DEVICETYPE_X8: - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; - break; - case CFI_DEVICETYPE_X16: - cfi->addr_unlock1 = 0xaaa; - if (map->buswidth == cfi->interleave) { - /* X16 chip(s) in X8 mode */ - cfi->addr_unlock2 = 0x555; - } else { - cfi->addr_unlock2 = 0x554; + if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) { + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + break; + case CFI_DEVICETYPE_X16: + cfi->addr_unlock1 = 0xaaa; + if (map_bankwidth(map) == cfi_interleave(cfi)) { + /* X16 chip(s) in X8 mode */ + cfi->addr_unlock2 = 0x555; + } else { + cfi->addr_unlock2 = 0x554; + } + break; + case CFI_DEVICETYPE_X32: + cfi->addr_unlock1 = 0x1554; + if (map_bankwidth(map) == cfi_interleave(cfi)*2) { + /* X32 chip(s) in X16 mode */ + cfi->addr_unlock1 = 0xaaa; + } else { + cfi->addr_unlock2 = 0xaa8; + } + break; + default: + printk(KERN_WARNING + "MTD %s(): Unsupported device type %d\n", + __func__, cfi->device_type); + return NULL; } - break; - case CFI_DEVICETYPE_X32: - cfi->addr_unlock1 = 0x1555; - cfi->addr_unlock2 = 0xaaa; - break; - default: - printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type); - return NULL; } + } /* CFI mode */ for (i=0; i< cfi->numchips; i++) { @@ -147,23 +247,25 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) map->fldrv = &cfi_amdstd_chipdrv; - cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_amdstd_setup(map); } + static struct mtd_info *cfi_amdstd_setup(struct map_info *map) { struct cfi_private *cfi = map->fldrv_priv; struct mtd_info *mtd; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; + unsigned long offset = 0; + int i,j; mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); printk(KERN_NOTICE "number of %s chips: %d\n", - (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); + (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); if (!mtd) { - printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); - goto setup_err; + printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); + goto setup_err; } memset(mtd, 0, sizeof(*mtd)); @@ -171,86 +273,69 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) mtd->type = MTD_NORFLASH; /* Also select the correct geometry setup too */ mtd->size = devsize * cfi->numchips; - - if (cfi->cfiq->NumEraseRegions == 1) { - /* No need to muck about with multiple erase sizes */ - mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; - } else { - unsigned long offset = 0; - int i,j; - - mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; - mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { - printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); - goto setup_err; - } + + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) + * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); + goto setup_err; + } - for (i=0; icfiq->NumEraseRegions; i++) { - unsigned long ernum, ersize; - ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; - ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; + for (i=0; icfiq->NumEraseRegions; i++) { + unsigned long ernum, ersize; + ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; + ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; - if (mtd->erasesize < ersize) { - mtd->erasesize = ersize; - } - for (j=0; jnumchips; j++) { - mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; - mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; - mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; - } - offset += (ersize * ernum); + if (mtd->erasesize < ersize) { + mtd->erasesize = ersize; } - if (offset != devsize) { - /* Argh */ - printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); - goto setup_err; + for (j=0; jnumchips; j++) { + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; } + offset += (ersize * ernum); + } + if (offset != devsize) { + /* Argh */ + printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + goto setup_err; + } #if 0 - // debug - for (i=0; inumeraseregions;i++){ - printk("%d: offset=0x%x,size=0x%x,blocks=%d\n", - i,mtd->eraseregions[i].offset, - mtd->eraseregions[i].erasesize, - mtd->eraseregions[i].numblocks); - } -#endif + // debug + for (i=0; inumeraseregions;i++){ + printk("%d: offset=0x%x,size=0x%x,blocks=%d\n", + i,mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); } - - switch (CFIDEV_BUSWIDTH) - { - case 1: - case 2: - case 4: -#if 1 - if (mtd->numeraseregions > 1) - mtd->erase = cfi_amdstd_erase_varsize; - else #endif - if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) - mtd->erase = cfi_amdstd_erase_chip; - else - mtd->erase = cfi_amdstd_erase_onesize; - mtd->read = cfi_amdstd_read; - mtd->write = cfi_amdstd_write; - break; - default: - printk(KERN_WARNING "Unsupported buswidth\n"); - goto setup_err; - break; + if (mtd->numeraseregions == 1 + && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) { + mtd->erase = cfi_amdstd_erase_chip; + } else { + mtd->erase = cfi_amdstd_erase_varsize; + mtd->lock = cfi_amdstd_lock_varsize; + mtd->unlock = cfi_amdstd_unlock_varsize; } - if (cfi->fast_prog) { - /* In cfi_amdstd_write() we frob the protection stuff - without paying any attention to the state machine. - This upsets in-progress erases. So we turn this flag - off for now till the code gets fixed. */ - printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); - cfi->fast_prog = 0; + + if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { + DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); + mtd->write = cfi_amdstd_write_buffers; + } else { + DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" ); + mtd->write = cfi_amdstd_write_words; } + mtd->read = cfi_amdstd_read; - /* does this chip have a secsi area? */ + /* FIXME: erase-suspend-program is broken. See + http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ + printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); + + /* does this chip have a secsi area? */ if(cfi->mfr==1){ switch(cfi->id){ @@ -289,46 +374,181 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) return NULL; } -static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +/* + * Return true if the chip is ready. + * + * Ready is one of: read mode, query mode, erase-suspend-read mode (in any + * non-suspended sector) and is indicated by no toggle bits toggling. + * + * Note that anything more complicated than checking if no bits are toggling + * (including checking DQ5 for an error status) is tricky to get working + * correctly and is therefore not done (particulary with interleaved chips + * as each chip must be checked independantly of the others). + */ +static int chip_ready(struct map_info *map, unsigned long addr) +{ + map_word d, t; + + d = map_read(map, addr); + t = map_read(map, addr); + + return map_word_equal(map, d, t); +} + +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) { DECLARE_WAITQUEUE(wait, current); - unsigned long timeo = jiffies + HZ; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long timeo; + struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; + resettime: + timeo = jiffies + HZ; retry: - cfi_spin_lock(chip->mutex); + switch (chip->state) { - if (chip->state != FL_READY){ -#if 0 - printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); -#endif + case FL_STATUS: + for (;;) { + if (chip_ready(map, adr)) + break; + + if (time_after(jiffies, timeo)) { + printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); + cfi_spin_unlock(chip->mutex); + return -EIO; + } + cfi_spin_unlock(chip->mutex); + cfi_udelay(1); + cfi_spin_lock(chip->mutex); + /* Someone else might have been playing with it. */ + goto retry; + } + + case FL_READY: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + return 0; + + case FL_ERASING: + if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ + goto sleep; + + if (!(mode == FL_READY || mode == FL_POINT + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) + || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) + goto sleep; + + /* We could check to see if we're trying to access the sector + * that is currently being erased. However, no user will try + * anything like that so we just wait for the timeout. */ + + /* Erase suspend */ + /* It's harmless to issue the Erase-Suspend and Erase-Resume + * commands when the erase algorithm isn't in progress. */ + map_write(map, CMD(0xB0), chip->in_progress_block_addr); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; + chip->erase_suspended = 1; + for (;;) { + if (chip_ready(map, adr)) + break; + + if (time_after(jiffies, timeo)) { + /* Should have suspended the erase by now. + * Send an Erase-Resume command as either + * there was an error (so leave the erase + * routine to recover from it) or we trying to + * use the erase-in-progress sector. */ + map_write(map, CMD(0x30), chip->in_progress_block_addr); + chip->state = FL_ERASING; + chip->oldstate = FL_READY; + printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); + return -EIO; + } + + cfi_spin_unlock(chip->mutex); + cfi_udelay(1); + cfi_spin_lock(chip->mutex); + /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. + So we can just loop here. */ + } + chip->state = FL_READY; + return 0; + + case FL_POINT: + /* Only if there's no operation suspended... */ + if (mode == FL_READY && chip->oldstate == FL_READY) + return 0; + + default: + sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); - schedule(); remove_wait_queue(&chip->wq, &wait); -#if 0 - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; + cfi_spin_lock(chip->mutex); + goto resettime; + } +} - goto retry; - } + +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + + switch(chip->oldstate) { + case FL_ERASING: + chip->state = chip->oldstate; + map_write(map, CMD(0x30), chip->in_progress_block_addr); + chip->oldstate = FL_READY; + chip->state = FL_ERASING; + break; + + case FL_READY: + case FL_STATUS: + /* We should really make set_vpp() count, rather than doing this */ + DISABLE_VPP(map); + break; + default: + printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); + } + wake_up(&chip->wq); +} + + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; + int ret; adr += chip->start; - chip->state = FL_READY; + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(map_bankwidth(map)-1); + + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, cmd_addr, FL_READY); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xf0), cmd_addr); + chip->state = FL_READY; + } map_copy_from(map, buf, adr, len); - wake_up(&chip->wq); - cfi_spin_unlock(chip->mutex); + put_chip(map, chip, cmd_addr); + cfi_spin_unlock(chip->mutex); return 0; } + static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct map_info *map = mtd->priv; @@ -370,6 +590,7 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_ return ret; } + static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) { DECLARE_WAITQUEUE(wait, current); @@ -381,11 +602,11 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi if (chip->state != FL_READY){ #if 0 - printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); + printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); #endif set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - + cfi_spin_unlock(chip->mutex); schedule(); @@ -402,13 +623,15 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi adr += chip->start; chip->state = FL_READY; - + + /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); map_copy_from(map, buf, adr, len); + /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -463,215 +686,136 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, return ret; } -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum, int fast) + +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) { - unsigned long timeo = jiffies + HZ; - unsigned int oldstatus, status, prev_oldstatus, prev_status; - unsigned int dq6; struct cfi_private *cfi = map->fldrv_priv; - /* We use a 1ms + 1 jiffies generic timeout for writes (most devices have - a max write time of a few hundreds usec). However, we should use the - maximum timeout value given by the chip at probe time instead. - Unfortunately, struct flchip does have a field for maximum timeout, - only for typical which can be far too short depending of the conditions. - The ' + 1' is to avoid having a timeout of 0 jiffies if HZ is smaller - than 1000. Using a static variable allows makes us save the costly - divide operation at each word write.*/ - static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; - DECLARE_WAITQUEUE(wait, current); + unsigned long timeo = jiffies + HZ; + /* + * We use a 1ms + 1 jiffies generic timeout for writes (most devices + * have a max write time of a few hundreds usec). However, we should + * use the maximum timeout value given by the chip at probe time + * instead. Unfortunately, struct flchip does have a field for + * maximum timeout, only for typical which can be far too short + * depending of the conditions. The ' + 1' is to avoid having a + * timeout of 0 jiffies if HZ is smaller than 1000. + */ + unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; - int ta = 0; + map_word oldd, curd; + int retry_cnt = 0; - retry: - cfi_spin_lock(chip->mutex); + adr += chip->start; - if (chip->state != FL_READY) { -#if 0 - printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); -#endif - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { cfi_spin_unlock(chip->mutex); + return ret; + } - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - printk(KERN_DEBUG "Wake up to write:\n"); - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; - - goto retry; - } - - chip->state = FL_WRITING; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", + __func__, adr, datum.x[0] ); - adr += chip->start; - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8llx)\n", - __func__, adr, datum ); + /* + * Check for a NOP for the case when the datum to write is already + * present - it saves time and works around buggy chips that corrupt + * data at other locations when 0xff is written to a location that + * already contains 0xff. + */ + oldd = map_read(map, adr); + if (map_word_equal(map, oldd, datum)) { + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n", + __func__); + goto op_done; + } ENABLE_VPP(map); - if (fast) { /* Unlock bypass */ - cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); - } - else { - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - } - cfi_write(map, datum, adr); + retry: + /* + * The CFI_DEVICETYPE_X8 argument is needed even when + * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for + * command sequences don't scale even when the device is + * wider. This is the case for many of the cfi_send_gen_cmd() + * below. I'm not sure, however, why some use + * cfi->device_type. + */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + map_write(map, datum, adr); + chip->state = FL_WRITING; cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); cfi_spin_lock(chip->mutex); - /* - * Polling toggle bits instead of reading back many times - * This ensures that write operation is really completed, - * or tells us why it failed. - * - * It appears tha the polling and decoding of error state might - * be simplified. Don't do it unless you really know what you - * are doing. You must remember that JESD21-C 3.5.3 states that - * the status must be read back an _additional_ two times before - * a failure is determined. This is because these devices have - * internal state machines that are asynchronous to the external - * data bus. During an erase or write the read-back status of the - * polling bits might be transitioning internaly when the external - * read-back occurs. This means that the bits aren't in the final - * state and they might appear to report an error as they transition - * and are in a weird state. This will produce infrequent errors - * that will usually disappear the next time an erase or write - * happens (Try tracking those errors down!). To ensure that - * the bits are not in transition the location must be read-back - * two more times and compared against what was written - BOTH reads - * MUST match what was written - don't think this can be simplified - * to only the last read matching. If the comparison fails, error - * state can then be decoded. - * - * - Thayne Harbaugh - */ - dq6 = CMD(1<<6); /* See comment above for timeout value. */ timeo = jiffies + uWriteTimeout; - - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - - /* - * This only checks if dq6 is still toggling and that our - * timer hasn't expired. We purposefully ignore the chips - * internal timer that will assert dq5 and leave dq6 toggling. - * This is done for a variety of reasons: - * 1) Not all chips support dq5. - * 2) Dealing with asynchronous status bit and data updates - * and reading a device two more times creates _messy_ - * logic when trying to deal with interleaved devices - - * some may be changing while others are still busy. - * 3) Checking dq5 only helps to optimize an error case that - * should at worst be infrequent and at best non-existent. - * - * If our timeout occurs _then_ we will check dq5 to see - * if the device also had an internal timeout. - */ - while( ( ( status ^ oldstatus ) & dq6 ) - && ! ( ta = time_after(jiffies, timeo) ) ) { + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); - if (need_resched()) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); - yield(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ cfi_spin_lock(chip->mutex); - } else - udelay(1); - - oldstatus = cfi_read( map, adr ); - status = cfi_read( map, adr ); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - } + continue; + } - /* - * Something kicked us out of the read-back loop. We'll - * check success befor checking failure. - * Even though dq6 might be true data, it is unkown if - * all of the other bits have changed to true data due to - * the asynchronous nature of the internal state machine. - * We will read two more times and use this to either - * verify that the write completed successfully or - * that something really went wrong. BOTH reads - * must match what was written - this certifies that - * bits aren't still changing and that the status - * bits erroneously match the datum that was written. - */ - prev_oldstatus = oldstatus; - prev_status = status; - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - - if ( oldstatus == datum && status == datum ) { - /* success - do nothing */ - goto write_done; - } + /* Test to see if toggling has stopped. */ + oldd = map_read(map, adr); + curd = map_read(map, adr); + if (map_word_equal(map, curd, oldd)) { + /* Do we have the correct value? */ + if (map_word_equal(map, curd, datum)) { + goto op_done; + } + /* Nope something has gone wrong. */ + break; + } - if ( ta ) { - int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; - if ( status & dq5mask ) { - /* dq5 asserted - decode interleave chips */ - printk( KERN_WARNING - "MTD %s(): FLASH internal timeout: 0x%.8x\n", - __func__, - status & dq5mask ); - } else { - printk( KERN_WARNING - "MTD %s(): Software timed out during write.\n", + if (time_after(jiffies, timeo)) { + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__ ); + break; } - goto write_failed; - } - - /* - * If we get to here then it means that something - * is wrong and it's not a timeout. Something - * is seriously wacky! Dump some debug info. - */ - printk(KERN_WARNING - "MTD %s(): Wacky! Unable to decode failure status\n", - __func__ ); - printk(KERN_WARNING - "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", - __func__, adr, datum, - prev_oldstatus, prev_status, - oldstatus, status); + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + cfi_udelay(1); + cfi_spin_lock(chip->mutex); + } - write_failed: - ret = -EIO; /* reset on all failures. */ - cfi_write( map, CMD(0xF0), chip->start ); + map_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ + if (++retry_cnt <= MAX_WORD_RETRIES) + goto retry; - write_done: - DISABLE_VPP(map); + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); return ret; } -static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) + +static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int ret = 0; int chipnum; unsigned long ofs, chipstart; + DECLARE_WAITQUEUE(wait, current); *retlen = 0; if (!len) @@ -682,33 +826,52 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ chipstart = cfi->chips[chipnum].start; /* If it's not bus-aligned, do the first byte write */ - if (ofs & (CFIDEV_BUSWIDTH-1)) { - unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + if (ofs & (map_bankwidth(map)-1)) { + unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); int i = ofs - bus_ofs; int n = 0; - u_char tmp_buf[8]; - cfi_word datum; + map_word tmp_buf; - map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); - while (len && i < CFIDEV_BUSWIDTH) - tmp_buf[i++] = buf[n++], len--; + retry: + cfi_spin_lock(cfi->chips[chipnum].mutex); - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ + if (cfi->chips[chipnum].state != FL_READY) { +#if 0 + printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&cfi->chips[chipnum].wq, &wait); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + schedule(); + remove_wait_queue(&cfi->chips[chipnum].wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + goto retry; } + /* Load 'tmp_buf' with old contents of flash */ + tmp_buf = map_read(map, bus_ofs+chipstart); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + /* Number of bytes to copy from buffer */ + n = min_t(int, len, map_bankwidth(map)-i); + + tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n); + ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum, 0); + bus_ofs, tmp_buf); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; + len -= n; if (ofs >> cfi->chipshift) { chipnum ++; @@ -718,315 +881,286 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ } } - if (cfi->fast_prog) { - /* Go into unlock bypass mode */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - } - /* We are now aligned, write as much as possible */ - while(len >= CFIDEV_BUSWIDTH) { - cfi_word datum; - - if (cfi_buswidth_is_1()) { - datum = *(__u8*)buf; - } else if (cfi_buswidth_is_2()) { - datum = *(__u16*)buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)buf; - } else { - return -EINVAL; - } - ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum, cfi->fast_prog); - if (ret) { - if (cfi->fast_prog){ - /* Get out of unlock bypass mode */ - cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); - } - return ret; - } + while(len >= map_bankwidth(map)) { + map_word datum; - ofs += CFIDEV_BUSWIDTH; - buf += CFIDEV_BUSWIDTH; - (*retlen) += CFIDEV_BUSWIDTH; - len -= CFIDEV_BUSWIDTH; + datum = map_word_load(map, buf); - if (ofs >> cfi->chipshift) { - if (cfi->fast_prog){ - /* Get out of unlock bypass mode */ - cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); - } + ret = do_write_oneword(map, &cfi->chips[chipnum], + ofs, datum); + if (ret) + return ret; + + ofs += map_bankwidth(map); + buf += map_bankwidth(map); + (*retlen) += map_bankwidth(map); + len -= map_bankwidth(map); + if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; chipstart = cfi->chips[chipnum].start; - if (cfi->fast_prog){ - /* Go into unlock bypass mode for next set of chips */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - } } } - if (cfi->fast_prog){ - /* Get out of unlock bypass mode */ - cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); - } - /* Write the trailing bytes if any */ - if (len & (CFIDEV_BUSWIDTH-1)) { - int i = 0, n = 0; - u_char tmp_buf[8]; - cfi_word datum; - - map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); - while (len--) - tmp_buf[i++] = buf[n++]; - - if (cfi_buswidth_is_2()) { - datum = *(__u16*)tmp_buf; - } else if (cfi_buswidth_is_4()) { - datum = *(__u32*)tmp_buf; - } else { - return -EINVAL; /* should never happen, but be safe */ + if (len & (map_bankwidth(map)-1)) { + map_word tmp_buf; + + retry1: + cfi_spin_lock(cfi->chips[chipnum].mutex); + + if (cfi->chips[chipnum].state != FL_READY) { +#if 0 + printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); +#endif + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&cfi->chips[chipnum].wq, &wait); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + schedule(); + remove_wait_queue(&cfi->chips[chipnum].wq, &wait); +#if 0 + if(signal_pending(current)) + return -EINTR; +#endif + goto retry1; } + tmp_buf = map_read(map, ofs + chipstart); + + cfi_spin_unlock(cfi->chips[chipnum].mutex); + + tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); + ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum, 0); + ofs, tmp_buf); if (ret) return ret; - (*retlen) += n; + (*retlen) += len; } return 0; } -static inline int do_erase_chip(struct map_info *map, struct flchip *chip) + +/* + * FIXME: interleaved mode not tested, and probably not supported! + */ +static inline int do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) { - unsigned int oldstatus, status, prev_oldstatus, prev_status; - unsigned int dq6; - unsigned long timeo = jiffies + HZ; - unsigned long int adr; struct cfi_private *cfi = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); - int ret = 0; - int ta = 0; - cfi_word ones = 0; + unsigned long timeo = jiffies + HZ; + /* see comments in do_write_oneword() regarding uWriteTimeo. */ + static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + int ret = -EIO; + unsigned long cmd_adr; + int z, words; + map_word datum; - retry: - cfi_spin_lock(chip->mutex); + adr += chip->start; + cmd_adr = adr; - if (chip->state != FL_READY){ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { cfi_spin_unlock(chip->mutex); + return ret; + } - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; + datum = map_word_load(map, buf); - goto retry; - } + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", + __func__, adr, datum.x[0] ); - chip->state = FL_ERASING; - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", - __func__, chip->start ); - - /* Handle devices with one erase region, that only implement - * the chip erase command. - */ ENABLE_VPP(map); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - timeo = jiffies + (HZ*20); - adr = cfi->addr_unlock1; + //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - /* Wait for the end of programing/erasure by using the toggle method. - * As long as there is a programming procedure going on, bit 6 - * is toggling it's state with each consecutive read. - * The toggling stops as soon as the procedure is completed. - * - * If the process has gone on for too long on the chip bit 5 gets. - * After bit5 is set you can kill the operation by sending a reset - * command to the chip. - */ - /* see comments in do_write_oneword */ - dq6 = CMD(1<<6); + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); + chip->state = FL_WRITING_TO_BUFFER; - while( ( ( status ^ oldstatus ) & dq6 ) - && ! ( ta = time_after(jiffies, timeo) ) ) { - int wait_reps; + /* Write length of data to come */ + words = len / map_bankwidth(map); + map_write(map, CMD(words - 1), cmd_adr); + /* Write data */ + z = 0; + while(z < words * map_bankwidth(map)) { + datum = map_word_load(map, buf); + map_write(map, datum, adr + z); - /* an initial short sleep */ - cfi_spin_unlock(chip->mutex); - schedule_timeout(HZ/100); - cfi_spin_lock(chip->mutex); + z += map_bankwidth(map); + buf += map_bankwidth(map); + } + z -= map_bankwidth(map); + + adr += z; + + /* Write Buffer Program Confirm: GO GO GO */ + map_write(map, CMD(0x29), cmd_adr); + chip->state = FL_WRITING; + + cfi_spin_unlock(chip->mutex); + cfi_udelay(chip->buffer_write_time); + cfi_spin_lock(chip->mutex); + + timeo = jiffies + uWriteTimeout; - if (chip->state != FL_ERASING) { - /* Someone's suspended the erase. Sleep */ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); - printk("erase suspended. Sleeping\n"); - schedule(); remove_wait_queue(&chip->wq, &wait); -#if 0 - if (signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + (HZ*2); /* FIXME */ + timeo = jiffies + (HZ / 2); /* FIXME */ cfi_spin_lock(chip->mutex); continue; } - /* Busy wait for 1/10 of a milisecond */ - for(wait_reps = 0; - (wait_reps < 100) - && ( ( status ^ oldstatus ) & dq6 ); - wait_reps++) { - - /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - - cfi_udelay(1); - - cfi_spin_lock(chip->mutex); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - } - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - } - - prev_oldstatus = oldstatus; - prev_status = status; - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - - if ( cfi_buswidth_is_1() ) { - ones = (__u8)~0; - } else if ( cfi_buswidth_is_2() ) { - ones = (__u16)~0; - } else if ( cfi_buswidth_is_4() ) { - ones = (__u32)~0; - } else { - printk(KERN_WARNING "Unsupported buswidth\n"); - goto erase_failed; - } - - if ( oldstatus == ones && status == ones ) { - /* success - do nothing */ - goto erase_done; - } + if (chip_ready(map, adr)) + goto op_done; + + if( time_after(jiffies, timeo)) + break; - if ( ta ) { - int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; - if ( status & dq5mask ) { - /* dq5 asserted - decode interleave chips */ - printk( KERN_WARNING - "MTD %s(): FLASH internal timeout: 0x%.8x\n", - __func__, - status & dq5mask ); - } else { - printk( KERN_WARNING - "MTD %s(): Software timed out during write.\n", - __func__ ); - } - goto erase_failed; + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + cfi_udelay(1); + cfi_spin_lock(chip->mutex); } - printk(KERN_WARNING - "MTD %s(): Wacky! Unable to decode failure status\n", + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__ ); - printk(KERN_WARNING - "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", - __func__, adr, ones, - prev_oldstatus, prev_status, - oldstatus, status); - - erase_failed: - ret = -EIO; /* reset on all failures. */ - cfi_write( map, CMD(0xF0), chip->start ); + map_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ - erase_done: - DISABLE_VPP(map); + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); + return ret; } -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - unsigned int oldstatus, status, prev_oldstatus, prev_status; - unsigned int dq6; - unsigned long timeo = jiffies + HZ; + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - DECLARE_WAITQUEUE(wait, current); + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; - int ta = 0; - cfi_word ones = 0; + int chipnum; + unsigned long ofs; - retry: - cfi_spin_lock(chip->mutex); + *retlen = 0; + if (!len) + return 0; - if (chip->state != FL_READY){ - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&chip->wq, &wait); - - cfi_spin_unlock(chip->mutex); + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); - schedule(); - remove_wait_queue(&chip->wq, &wait); -#if 0 - if(signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + HZ; + /* If it's not bus-aligned, do the first word write */ + if (ofs & (map_bankwidth(map)-1)) { + size_t local_len = (-ofs)&(map_bankwidth(map)-1); + if (local_len > len) + local_len = len; + ret = cfi_amdstd_write_words(mtd, to, local_len, + retlen, buf); + if (ret) + return ret; + ofs += local_len; + buf += local_len; + len -= local_len; - goto retry; - } + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } - chip->state = FL_ERASING; + /* Write buffer is worth it only if more than one word to write... */ + while (len >= map_bankwidth(map) * 2) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) + size = len; + if (size % map_bankwidth(map)) + size -= size % map_bankwidth(map); + + ret = do_write_buffer(map, &cfi->chips[chipnum], + ofs, buf, size); + if (ret) + return ret; + + ofs += size; + buf += size; + (*retlen) += size; + len -= size; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + if (len) { + size_t retlen_dregs = 0; + + ret = cfi_amdstd_write_words(mtd, to, len, &retlen_dregs, buf); + + *retlen += retlen_dregs; + return ret; + } + + return 0; +} + + +/* + * Handle devices with one erase region, that only implement + * the chip erase command. + */ +static inline int do_erase_chip(struct map_info *map, struct flchip *chip) +{ + struct cfi_private *cfi = map->fldrv_priv; + unsigned long timeo = jiffies + HZ; + unsigned long int adr; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + adr = cfi->addr_unlock1; + + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } - adr += chip->start; DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", - __func__, adr ); + __func__, chip->start ); ENABLE_VPP(map); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); @@ -1034,155 +1168,85 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_write(map, CMD(0x30), adr); - - timeo = jiffies + (HZ*20); - - /* Wait for the end of programing/erasure by using the toggle method. - * As long as there is a programming procedure going on, bit 6 - * is toggling it's state with each consecutive read. - * The toggling stops as soon as the procedure is completed. - * - * If the process has gone on for too long on the chip bit 5 gets. - * After bit5 is set you can kill the operation by sending a reset - * command to the chip. - */ - /* see comments in do_write_oneword */ - dq6 = CMD(1<<6); + chip->state = FL_ERASING; + chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); + cfi_spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((chip->erase_time*HZ)/(2*1000)); + cfi_spin_lock(chip->mutex); - while( ( ( status ^ oldstatus ) & dq6 ) - && ! ( ta = time_after(jiffies, timeo) ) ) { - int wait_reps; + timeo = jiffies + (HZ*20); - /* an initial short sleep */ - cfi_spin_unlock(chip->mutex); - schedule_timeout(HZ/100); - cfi_spin_lock(chip->mutex); - + for (;;) { if (chip->state != FL_ERASING) { /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); - printk(KERN_DEBUG "erase suspended. Sleeping\n"); - schedule(); remove_wait_queue(&chip->wq, &wait); -#if 0 - if (signal_pending(current)) - return -EINTR; -#endif - timeo = jiffies + (HZ*2); /* FIXME */ cfi_spin_lock(chip->mutex); continue; } - - /* Busy wait for 1/10 of a milisecond */ - for(wait_reps = 0; - (wait_reps < 100) - && ( ( status ^ oldstatus ) & dq6 ); - wait_reps++) { - - /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - - cfi_udelay(1); - - cfi_spin_lock(chip->mutex); - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); + if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ + chip->erase_suspended = 0; } - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - } - prev_oldstatus = oldstatus; - prev_status = status; - oldstatus = cfi_read(map, adr); - status = cfi_read(map, adr); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", - __func__, oldstatus, status ); - - if ( cfi_buswidth_is_1() ) { - ones = (__u8)~0; - } else if ( cfi_buswidth_is_2() ) { - ones = (__u16)~0; - } else if ( cfi_buswidth_is_4() ) { - ones = (__u32)~0; - } else { - printk(KERN_WARNING "Unsupported buswidth\n"); - goto erase_failed; - } + if (chip_ready(map, adr)) + goto op_done; - if ( oldstatus == ones && status == ones ) { - /* success - do nothing */ - goto erase_done; - } + if (time_after(jiffies, timeo)) + break; - if ( ta ) { - int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; - if ( status & dq5mask ) { - /* dq5 asserted - decode interleave chips */ - printk( KERN_WARNING - "MTD %s(): FLASH internal timeout: 0x%.8x\n", - __func__, - status & dq5mask ); - } else { - printk( KERN_WARNING - "MTD %s(): Software timed out during write.\n", - __func__ ); - } - goto erase_failed; + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + cfi_spin_lock(chip->mutex); } - printk(KERN_WARNING - "MTD %s(): Wacky! Unable to decode failure status\n", + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__ ); - printk(KERN_WARNING - "MTD %s(): 0x%.8lx(0x%.8llx): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", - __func__, adr, ones, - prev_oldstatus, prev_status, - oldstatus, status); - - erase_failed: - ret = -EIO; /* reset on all failures. */ - cfi_write( map, CMD(0xF0), chip->start ); + map_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ - erase_done: - DISABLE_VPP(map); + ret = -EIO; + op_done: chip->state = FL_READY; - wake_up(&chip->wq); + put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); + return ret; } -static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) + +typedef int (*frob_t)(struct map_info *map, struct flchip *chip, + unsigned long adr, void *thunk); + + +static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, + loff_t ofs, size_t len, void *thunk) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; + unsigned long adr; int chipnum, ret = 0; int i, first; struct mtd_erase_region_info *regions = mtd->eraseregions; - if (instr->addr > mtd->size) + if (ofs > mtd->size) return -EINVAL; - if ((instr->len + instr->addr) > mtd->size) + if ((len + ofs) > mtd->size) return -EINVAL; /* Check that both start and end of the requested erase are @@ -1197,7 +1261,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *ins start of the requested erase, and then go back one. */ - while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) + while (i < mtd->numeraseregions && ofs >= regions[i].offset) i++; i--; @@ -1207,7 +1271,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *ins effect here. */ - if (instr->addr & (regions[i].erasesize-1)) + if (ofs & (regions[i].erasesize-1)) return -EINVAL; /* Remember the erase region we start on */ @@ -1217,7 +1281,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *ins * with the erase region at that address. */ - while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) + while (inumeraseregions && (ofs + len) >= regions[i].offset) i++; /* As before, drop back one to point at the region in which @@ -1225,18 +1289,17 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *ins */ i--; - if ((instr->addr + instr->len) & (regions[i].erasesize-1)) + if ((ofs + len) & (regions[i].erasesize-1)) return -EINVAL; - - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); - len = instr->len; - i=first; + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + i=first; + while (len) { + ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); + if (ret) return ret; @@ -1255,51 +1318,107 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *ins } } - instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); - return 0; } -static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) { - struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; - int chipnum, ret = 0; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; - if (instr->addr & (mtd->erasesize - 1)) - return -EINVAL; + adr += chip->start; - if (instr->len & (mtd->erasesize -1)) - return -EINVAL; + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_ERASING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } - if ((instr->len + instr->addr) > mtd->size) - return -EINVAL; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", + __func__, adr ); - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); - len = instr->len; + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + map_write(map, CMD(0x30), adr); + + chip->state = FL_ERASING; + chip->erase_suspended = 0; + chip->in_progress_block_addr = adr; + + cfi_spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((chip->erase_time*HZ)/(2*1000)); + cfi_spin_lock(chip->mutex); - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + timeo = jiffies + (HZ*20); - if (ret) - return ret; + for (;;) { + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + cfi_spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + cfi_spin_lock(chip->mutex); + continue; + } + if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ + chip->erase_suspended = 0; + } - adr += mtd->erasesize; - len -= mtd->erasesize; + if (chip_ready(map, adr)) + goto op_done; - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) + if (time_after(jiffies, timeo)) break; - } + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + cfi_spin_lock(chip->mutex); } - + + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); + + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + ret = -EIO; + op_done: + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + return ret; +} + + +int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ + unsigned long ofs, len; + int ret; + + ofs = instr->addr; + len = instr->len; + + ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); + if (ret) + return ret; + instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); @@ -1307,6 +1426,7 @@ static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *ins return 0; } + static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) { struct map_info *map = mtd->priv; @@ -1330,6 +1450,7 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) return 0; } + static void cfi_amdstd_sync (struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1368,7 +1489,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) schedule(); - remove_wait_queue(&chip->wq, &wait); + remove_wait_queue(&chip->wq, &wait); goto retry; } @@ -1427,7 +1548,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) /* Unlock the chips again */ if (ret) { - for (i--; i >=0; i--) { + for (i--; i >=0; i--) { chip = &cfi->chips[i]; cfi_spin_lock(chip->mutex); @@ -1443,6 +1564,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) return ret; } + static void cfi_amdstd_resume(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1458,7 +1580,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; - cfi_write(map, CMD(0xF0), chip->start); + map_write(map, CMD(0xF0), chip->start); wake_up(&chip->wq); } else @@ -1468,6 +1590,137 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) } } + +#ifdef DEBUG_LOCK_BITS + +static int do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + void *thunk) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ofs_factor = cfi->interleave * cfi->device_type; + + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", + adr, cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + + return 0; +} + + +#define debug_dump_locks(mtd, frob, ofs, len, thunk) \ + cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk)) + +#else + +#define debug_dump_locks(...) + +#endif /* DEBUG_LOCK_BITS */ + + +struct xxlock_thunk { + uint8_t val; + flstate_t state; +}; + + +#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING}) +#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING}) + + +/* + * FIXME - this is *very* specific to a particular chip. It likely won't + * work for all chips that require unlock. It also hasn't been tested + * with interleaved chips. + */ +static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk; + int ret; + + /* + * This is easy because these are writes to registers and not writes + * to flash memory - that means that we don't have to check status + * and timeout. + */ + + adr += chip->start; + /* + * lock block registers: + * - on 64k boundariesand + * - bit 1 set high + * - block lock registers are 4MiB lower - overflow subtract (danger) + */ + adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff; + + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + chip->state = xxlt->state; + map_write(map, CMD(xxlt->val), adr); + + /* Done and happy. */ + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + return 0; +} + + +static int cfi_amdstd_lock_varsize(struct mtd_info *mtd, + loff_t ofs, + size_t len) +{ + int ret; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __func__, ofs, len); + debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); + + ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, + (void *)&DO_XXLOCK_ONEBLOCK_LOCK); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s: lock status after, ret=%d\n", + __func__, ret); + + debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); + + return ret; +} + + +static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd, + loff_t ofs, + size_t len) +{ + int ret; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __func__, ofs, len); + debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); + + ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, + (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s: lock status after, ret=%d\n", + __func__, ret); + debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); + + return ret; +} + + static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -1480,21 +1733,23 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) static char im_name[]="cfi_cmdset_0002"; + int __init cfi_amdstd_init(void) { inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); return 0; } + static void __exit cfi_amdstd_exit(void) { inter_module_unregister(im_name); } + module_init(cfi_amdstd_init); module_exit(cfi_amdstd_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); - diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index f1fd12188..98817c2d9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,6 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * + * $Id: cfi_cmdset_0020.c,v 1.13 2004/07/12 21:52:50 dwmw2 Exp $ * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and @@ -116,7 +117,6 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; int i; - __u32 base = cfi->chips[0].start; if (cfi->cfi_mode) { /* @@ -126,36 +126,11 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_intelext *extp; - int ofs_factor = cfi->interleave * cfi->device_type; - printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); - if (!adr) + extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); + if (!extp) return NULL; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - - extp = kmalloc(sizeof(*extp), GFP_KERNEL); - if (!extp) { - printk(KERN_ERR "Failed to allocate memory\n"); - return NULL; - } - - /* Read in the Extended Query Table */ - for (i=0; iMajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { - printk(KERN_WARNING " Unknown staa Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - return NULL; - } - /* Do some byteswapping if necessary */ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); @@ -175,8 +150,6 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) cfi->chips[i].erase_time = 1024; } - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_staa_setup(map); } @@ -266,7 +239,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) { - __u32 status, status_OK; + map_word status, status_OK; unsigned long timeo; DECLARE_WAITQUEUE(wait, current); int suspended = 0; @@ -276,7 +249,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof adr += chip->start; /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + cmd_addr = adr & ~(map_bankwidth(map)-1); /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); @@ -290,33 +263,33 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof */ switch (chip->state) { case FL_ERASING: - if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) + if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) goto sleep; /* We don't support erase suspend */ - cfi_write (map, CMD(0xb0), cmd_addr); + map_write (map, CMD(0xb0), cmd_addr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to * 'read' mode. Make sure that we switch to 'read status' * mode so we get the right data. --rmk */ - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; // printk("Erase suspending at 0x%lx\n", cmd_addr); for (;;) { - status = cfi_read(map, cmd_addr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_addr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; if (time_after(jiffies, timeo)) { /* Urgh */ - cfi_write(map, CMD(0xd0), cmd_addr); + map_write(map, CMD(0xd0), cmd_addr); /* make sure we're in 'read status' mode */ - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); printk(KERN_ERR "Chip not ready after erase " - "suspended: status = 0x%x\n", status); + "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } @@ -326,7 +299,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof } suspended = 1; - cfi_write(map, CMD(0xff), cmd_addr); + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; @@ -340,13 +313,13 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof case FL_CFI_QUERY: case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, cmd_addr); - if ((status & status_OK) == status_OK) { - cfi_write(map, CMD(0xff), cmd_addr); + status = map_read(map, cmd_addr); + if (map_word_andequal(map, status, status_OK, status_OK)) { + map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; } @@ -354,7 +327,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status); + printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO; } @@ -389,8 +362,8 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof sending the 0x70 (Read Status) command to an erasing chip and expecting it to be ignored, that's what we do. */ - cfi_write(map, CMD(0xd0), cmd_addr); - cfi_write(map, CMD(0x70), cmd_addr); + map_write(map, CMD(0xd0), cmd_addr); + map_write(map, CMD(0x70), cmd_addr); } wake_up(&chip->wq); @@ -441,16 +414,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + map_word status, status_OK; unsigned long cmd_adr, timeo; DECLARE_WAITQUEUE(wait, current); int wbufsize, z; /* M58LW064A requires bus alignment for buffer wriets -- saw */ - if (adr & (CFIDEV_BUSWIDTH-1)) + if (adr & (map_bankwidth(map)-1)) return -EINVAL; - wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); @@ -476,21 +449,21 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, case FL_CFI_QUERY: case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; #ifdef DEBUG_CFI_FEATURES - printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr)); + printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); #endif case FL_STATUS: - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %llx\n", - status, cfi_read(map, cmd_adr)); + printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", + status.x[0], map_read(map, cmd_adr).x[0]); return -EIO; } @@ -512,13 +485,13 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, } ENABLE_VPP(map); - cfi_write(map, CMD(0xe8), cmd_adr); + map_write(map, CMD(0xe8), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; z = 0; for (;;) { - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; spin_unlock_bh(chip->mutex); @@ -528,41 +501,26 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, if (++z > 100) { /* Argh. Not ready for write to buffer */ DISABLE_VPP(map); - cfi_write(map, CMD(0x70), cmd_adr); + map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; spin_unlock_bh(chip->mutex); - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO; } } /* Write length of data to come */ - cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); + map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr ); /* Write data */ - for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { - if (cfi_buswidth_is_1()) { - u8 *b = (u8 *)buf; - - map_write8 (map, *b++, adr+z); - buf = (const u_char *)b; - } else if (cfi_buswidth_is_2()) { - u16 *b = (u16 *)buf; - - map_write16 (map, *b++, adr+z); - buf = (const u_char *)b; - } else if (cfi_buswidth_is_4()) { - u32 *b = (u32 *)buf; - - map_write32 (map, *b++, adr+z); - buf = (const u_char *)b; - } else { - DISABLE_VPP(map); - return -EINVAL; - } + for (z = 0; z < len; + z += map_bankwidth(map), buf += map_bankwidth(map)) { + map_word d; + d = map_word_load(map, buf); + map_write(map, d, adr+z); } /* GO GO GO */ - cfi_write(map, CMD(0xd0), cmd_adr); + map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; spin_unlock_bh(chip->mutex); @@ -584,16 +542,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, continue; } - status = cfi_read(map, cmd_adr); - if ((status & status_OK) == status_OK) + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); + map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; DISABLE_VPP(map); spin_unlock_bh(chip->mutex); @@ -620,19 +578,18 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, chip->state = FL_STATUS; /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ - if ((status & CMD(0x02)) || (status & CMD(0x08)) || - (status & CMD(0x10)) || (status & CMD(0x20))) { + if (map_word_bitsset(map, status, CMD(0x3a))) { #ifdef DEBUG_CFI_FEATURES - printk("%s: 2 status[%x]\n", __FUNCTION__, status); + printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); #endif - /* clear status */ - cfi_write(map, CMD(0x50), cmd_adr); - /* put back into read status register mode */ - cfi_write(map, CMD(0x70), adr); - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - return (status & CMD(0x02)) ? -EROFS : -EIO; - } + /* clear status */ + map_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ + map_write(map, CMD(0x70), adr); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; + } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); @@ -644,7 +601,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; @@ -657,7 +614,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, ofs = to - (chipnum << cfi->chipshift); #ifdef DEBUG_CFI_FEATURES - printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH); + printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); #endif @@ -769,7 +726,7 @@ write_error: static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + map_word status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); @@ -789,12 +746,12 @@ retry: case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -823,11 +780,11 @@ retry: ENABLE_VPP(map); /* Clear the status register first */ - cfi_write(map, CMD(0x50), adr); + map_write(map, CMD(0x50), adr); /* Now erase */ - cfi_write(map, CMD(0x20), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x20), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); @@ -851,15 +808,15 @@ retry: continue; } - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %llx.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -875,43 +832,46 @@ retry: ret = 0; /* We've broken this before. It doesn't hurt to be safe */ - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - status = cfi_read(map, adr); + status = map_read(map, adr); /* check for lock bit */ - if (status & CMD(0x3a)) { - unsigned char chipstatus = status; - if (status != CMD(status & 0xff)) { - int i; - for (i = 1; i> (cfi->device_type * 8); + if (map_word_bitsset(map, status, CMD(0x3a))) { + unsigned char chipstatus = status.x[0]; + if (!map_word_equal(map, status, CMD(chipstatus))) { + int i, w; + for (w=0; w> (cfi->device_type * 8); + } } - printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); + printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", + status.x[0], chipstatus); } /* Reset the error bits */ - cfi_write(map, CMD(0x50), adr); - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x50), adr); + map_write(map, CMD(0x70), adr); if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ - printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; chip->state = FL_STATUS; spin_unlock_bh(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); ret = -EIO; } } @@ -1072,7 +1032,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); @@ -1090,12 +1050,12 @@ retry: case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -1123,8 +1083,8 @@ retry: } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0x01), adr); + map_write(map, CMD(0x60), adr); + map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; spin_unlock_bh(chip->mutex); @@ -1137,15 +1097,15 @@ retry: timeo = jiffies + (HZ*2); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %llx.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -1221,7 +1181,7 @@ static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len) static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + map_word status, status_OK; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); @@ -1239,12 +1199,12 @@ retry: case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ @@ -1272,8 +1232,8 @@ retry: } ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0xD0), adr); + map_write(map, CMD(0x60), adr); + map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; spin_unlock_bh(chip->mutex); @@ -1286,15 +1246,15 @@ retry: timeo = jiffies + (HZ*2); for (;;) { - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + status = map_read(map, adr); + if (map_word_andequal(map, status, status_OK, status_OK)) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); + map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %llx.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; @@ -1423,7 +1383,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { - cfi_write(map, CMD(0xFF), 0); + map_write(map, CMD(0xFF), 0); chip->state = FL_READY; wake_up(&chip->wq); } diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index fba4ddf0f..071be4220 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.71 2003/05/28 12:51:48 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.77 2004/07/14 08:38:44 dwmw2 Exp $ */ #include @@ -26,7 +26,7 @@ static void print_cfi_ident(struct cfi_ident *); #endif static int cfi_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi); + unsigned long *chip_map, struct cfi_private *cfi); static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); struct mtd_info *cfi_probe(struct map_info *map); @@ -35,21 +35,36 @@ struct mtd_info *cfi_probe(struct map_info *map); in: interleave,type,mode ret: table index, <0 for error */ -static inline int qry_present(struct map_info *map, __u32 base, +static int qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor + map_word val; + map_word qry; - if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) && - cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) && - cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi)) - return 1; // ok ! + qry = cfi_build_cmd('Q', map, cfi); + val = map_read(map, base + osf*0x10); - return 0; // nothing found + if (!map_word_equal(map, qry, val)) + return 0; + + qry = cfi_build_cmd('R', map, cfi); + val = map_read(map, base + osf*0x11); + + if (!map_word_equal(map, qry, val)) + return 0; + + qry = cfi_build_cmd('Y', map, cfi); + val = map_read(map, base + osf*0x12); + + if (!map_word_equal(map, qry, val)) + return 0; + + return 1; // nothing found } static int cfi_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi) + unsigned long *chip_map, struct cfi_private *cfi) { int i; @@ -66,6 +81,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, return 0; } cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); if (!qry_present(map,base,cfi)) @@ -78,18 +94,25 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, } /* Check each previous chip to see if it's an alias */ - for (i=0; inumchips; i++) { + for (i=0; i < (base >> cfi->chipshift); i++) { + unsigned long start; + if(!test_bit(i, chip_map)) { + /* Skip location; no valid chip at this address */ + continue; + } + start = i << cfi->chipshift; /* This chip should be in read mode if it's one we've already touched. */ - if (qry_present(map,chips[i].start,cfi)) { + if (qry_present(map, start, cfi)) { /* Eep. This chip also had the QRY marker. * Is it an alias for the new one? */ - cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); /* If the QRY marker goes away, it's an alias */ - if (!qry_present(map, chips[i].start, cfi)) { + if (!qry_present(map, start, cfi)) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } /* Yes, it's actually got QRY for data. Most @@ -97,10 +120,11 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, * too and if it's the same, assume it's an alias. */ /* FIXME: Use other modes to do a proper check */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); if (qry_present(map, base, cfi)) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } } @@ -108,21 +132,16 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, /* OK, if we got to here, then none of the previous chips appear to be aliases for the current one. */ - if (cfi->numchips == MAX_CFI_CHIPS) { - printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); - /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ - return -1; - } - chips[cfi->numchips].start = base; - chips[cfi->numchips].state = FL_READY; + set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ cfi->numchips++; /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, - map->buswidth*8); + map->bankwidth*8); return 1; } @@ -150,7 +169,6 @@ static int cfi_chip_setup(struct map_info *map, memset(cfi->cfiq,0,sizeof(struct cfi_ident)); cfi->cfi_mode = CFI_MODE_CFI; - cfi->fast_prog=1; /* CFI supports fast programming */ /* Read the CFI info structure */ for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { @@ -180,8 +198,29 @@ static int cfi_chip_setup(struct map_info *map, (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1); #endif } + + /* Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* ... even if it's an Intel chip */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", + map->name, cfi->interleave, cfi->device_type*8, base, + map->bankwidth*8); return 1; } @@ -241,11 +280,11 @@ static void print_cfi_ident(struct cfi_ident *cfip) printk("No Alternate Algorithm Table\n"); - printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); - printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); + printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); if (cfip->VppMin) { - printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); - printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); + printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); } else printk("No Vpp line\n"); diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index 3bc9199d2..1e45df074 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -1,5 +1,5 @@ /* - * $Id: chipreg.c,v 1.15 2003/05/21 15:15:05 dwmw2 Exp $ + * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $ * * Registration for chip drivers * diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index bae2fb569..3615fd815 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,7 +2,7 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.13 2003/06/25 11:50:37 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.19 2004/07/13 22:33:32 dwmw2 Exp $ */ #include @@ -50,16 +50,15 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) EXPORT_SYMBOL(mtd_do_chip_probe); -struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) +static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) { - unsigned long base=0; struct cfi_private cfi; struct cfi_private *retcfi; - struct flchip chip[MAX_CFI_CHIPS]; - int i; + unsigned long *chip_map; + int i, j, mapsize; + int max_chips; memset(&cfi, 0, sizeof(cfi)); - memset(&chip[0], 0, sizeof(chip)); /* Call the probetype-specific code with all permutations of interleave and device type, etc. */ @@ -80,46 +79,47 @@ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe return NULL; } #endif - chip[0].start = 0; - chip[0].state = FL_READY; cfi.chipshift = cfi.cfiq->DevSize; - switch(cfi.interleave) { -#ifdef CFIDEV_INTERLEAVE_1 - case 1: - break; -#endif -#ifdef CFIDEV_INTERLEAVE_2 - case 2: + if (cfi_interleave_is_1(&cfi)) { + ; + } else if (cfi_interleave_is_2(&cfi)) { cfi.chipshift++; - break; -#endif -#ifdef CFIDEV_INTERLEAVE_4 - case 4: - cfi.chipshift+=2; - break; -#endif - default: + } else if (cfi_interleave_is_4((&cfi))) { + cfi.chipshift += 2; + } else if (cfi_interleave_is_8(&cfi)) { + cfi.chipshift += 3; + } else { BUG(); } cfi.numchips = 1; + /* + * Allocate memory for bitmap of valid chips. + * Align bitmap storage size to full byte. + */ + max_chips = map->size >> cfi.chipshift; + mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0); + chip_map = kmalloc(mapsize, GFP_KERNEL); + if (!chip_map) { + printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); + kfree(cfi.cfiq); + return NULL; + } + memset (chip_map, 0, mapsize); + + set_bit(0, chip_map); /* Mark first chip valid */ + /* * Now probe for other chips, checking sensibly for aliases while * we're at it. The new_chip probe above should have let the first * chip in read mode. - * - * NOTE: Here, we're checking if there is room for another chip - * the same size within the mapping. Therefore, - * base + chipsize <= map->size is the correct thing to do, - * because, base + chipsize would be the _first_ byte of the - * next chip, not the one we're currently pondering. */ - for (base = (1<size; - base += (1<probe_chip(map, base, &chip[0], &cfi); + for (i = 1; i < max_chips; i++) { + cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); + } /* * Now allocate the space for the structures we need to return to @@ -131,19 +131,26 @@ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe if (!retcfi) { printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); kfree(cfi.cfiq); + kfree(chip_map); return NULL; } memcpy(retcfi, &cfi, sizeof(cfi)); - memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); - - /* Fix up the stuff that breaks when you move it */ - for (i=0; i< retcfi->numchips; i++) { - init_waitqueue_head(&retcfi->chips[i].wq); - spin_lock_init(&retcfi->chips[i]._spinlock); - retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock; + memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); + + for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { + if(test_bit(i, chip_map)) { + struct flchip *pchip = &retcfi->chips[j++]; + + pchip->start = (i << cfi.chipshift); + pchip->state = FL_READY; + init_waitqueue_head(&pchip->wq); + spin_lock_init(&pchip->_spinlock); + pchip->mutex = &pchip->_spinlock; + } } + kfree(chip_map); return retcfi; } @@ -151,131 +158,27 @@ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, struct cfi_private *cfi) { - switch (map->buswidth) { -#ifdef CFIDEV_BUSWIDTH_1 - case CFIDEV_BUSWIDTH_1: - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - break; -#endif /* CFIDEV_BUSWITDH_1 */ - -#ifdef CFIDEV_BUSWIDTH_2 - case CFIDEV_BUSWIDTH_2: -#ifdef CFIDEV_INTERLEAVE_1 - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_1 */ -#ifdef CFIDEV_INTERLEAVE_2 - cfi->interleave = CFIDEV_INTERLEAVE_2; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ - break; -#endif /* CFIDEV_BUSWIDTH_2 */ - -#ifdef CFIDEV_BUSWIDTH_4 - case CFIDEV_BUSWIDTH_4: -#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE) - cfi->interleave = CFIDEV_INTERLEAVE_1; - - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_1 */ -#ifdef CFIDEV_INTERLEAVE_2 - cfi->interleave = CFIDEV_INTERLEAVE_2; - -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ -#ifdef CFIDEV_INTERLEAVE_4 - cfi->interleave = CFIDEV_INTERLEAVE_4; - -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_4 */ - break; -#endif /* CFIDEV_BUSWIDTH_4 */ - -#ifdef CFIDEV_BUSWIDTH_8 - case CFIDEV_BUSWIDTH_8: -#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE) - cfi->interleave = CFIDEV_INTERLEAVE_2; - - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_2 */ -#ifdef CFIDEV_INTERLEAVE_4 - cfi->interleave = CFIDEV_INTERLEAVE_4; - -#ifdef SOMEONE_ACTUALLY_MAKES_THESE - cfi->device_type = CFI_DEVICETYPE_X32; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_4 */ -#ifdef CFIDEV_INTERLEAVE_8 - cfi->interleave = CFIDEV_INTERLEAVE_8; - - cfi->device_type = CFI_DEVICETYPE_X16; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; - - cfi->device_type = CFI_DEVICETYPE_X8; - if (cp->probe_chip(map, 0, NULL, cfi)) - return 1; -#endif /* CFIDEV_INTERLEAVE_8 */ - break; -#endif /* CFIDEV_BUSWIDTH_8 */ - - default: - printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth); - return 0; + int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ + int max_chips = map_bankwidth(map); /* And minimum 1 */ + int nr_chips, type; + + for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) { + + if (!cfi_interleave_supported(nr_chips)) + continue; + + cfi->interleave = nr_chips; + + for (type = 0; type < 3; type++) { + cfi->device_type = 1<probe_chip(map, 0, NULL, cfi)) + return 1; + } } return 0; } - typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); extern cfi_cmdset_fn_t cfi_cmdset_0001; diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c index f0362facf..0b6e96f7c 100644 --- a/drivers/mtd/chips/jedec.c +++ b/drivers/mtd/chips/jedec.c @@ -11,7 +11,7 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.19 2003/05/29 09:25:23 dwmw2 Exp $ + * $Id: jedec.c,v 1.20 2004/07/12 14:03:01 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index cbef6f1ef..daf554c00 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,9 +1,11 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.29 2003/05/28 13:57:46 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.51 2004/07/14 14:44:30 thayne Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. + + Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com */ #include @@ -16,6 +18,7 @@ #include #include #include +#include #include #include @@ -36,8 +39,13 @@ /* AMD */ +#define AM29DL800BB 0x22C8 +#define AM29DL800BT 0x224A + #define AM29F800BB 0x2258 #define AM29F800BT 0x22D6 +#define AM29LV400BB 0x22BA +#define AM29LV400BT 0x22B9 #define AM29LV800BB 0x225B #define AM29LV800BT 0x22DA #define AM29LV160DT 0x22C4 @@ -58,6 +66,7 @@ #define AT49BV32XT 0x00C9 /* Fujitsu */ +#define MBM29F040C 0x00A4 #define MBM29LV650UE 0x22D7 #define MBM29LV320TE 0x22F6 #define MBM29LV320BE 0x22F9 @@ -65,6 +74,9 @@ #define MBM29LV160BE 0x2249 #define MBM29LV800BA 0x225B #define MBM29LV800TA 0x22DA +#define MBM29LV400TC 0x22B9 +#define MBM29LV400BC 0x22BA + /* Intel */ #define I28F004B3T 0x00d4 @@ -113,6 +125,8 @@ #define M50FW016 0x002E /* SST */ +#define SST29EE020 0x0010 +#define SST29LE020 0x0012 #define SST29EE512 0x005d #define SST29LE512 0x003d #define SST39LF800 0x2781 @@ -123,6 +137,8 @@ #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 +#define SST49LF004B 0x0060 +#define SST49LF008A 0x005a #define SST49LF030A 0x001C #define SST49LF040A 0x0051 #define SST49LF080A 0x005B @@ -213,11 +229,10 @@ struct amd_flash_info { const __u16 dev_id; const char *name; const int DevSize; - const int InterfaceDesc; const int NumEraseRegions; const int CmdSet; - const __u8 uaddr[3]; /* unlock addrs for 8, 16, 32 modes */ - const ulong regions[4]; + const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ + const ulong regions[6]; }; #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) @@ -285,6 +300,40 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x08000,1), ERASEINFO(0x10000,31) } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV400BB, + .name = "AMD AM29LV400BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7) + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29LV400BT, + .name = "AMD AM29LV400BT", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } }, { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV800BB, @@ -303,6 +352,45 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { +/* add DL */ + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29DL800BB, + .name = "AMD AM29DL800BB", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 6, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,4), + ERASEINFO(0x08000,1), + ERASEINFO(0x04000,1), + ERASEINFO(0x10000,14) + } + }, { + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29DL800BT, + .name = "AMD AM29DL800BT", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 6, + .regions = { + ERASEINFO(0x10000,14), + ERASEINFO(0x04000,1), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,4), + ERASEINFO(0x08000,1), + ERASEINFO(0x04000,1) + } + }, { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F800BB, .name = "AMD AM29F800BB", @@ -505,6 +593,19 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) } + }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29F040C, + .name = "Fujitsu MBM29F040C", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x10000,8) + } }, { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV650UE, @@ -616,6 +717,40 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,2), ERASEINFO(0x04000,1) } + }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV400BC, + .name = "Fujitsu MBM29LV400BC", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,7) + } + }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29LV400TC, + .name = "Fujitsu MBM29LV400TC", + .uaddr = { + [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ + [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } }, { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F004B3B, @@ -1065,6 +1200,30 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST29EE020, + .name = "SST 29EE020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_SST_PAGE, + .NumEraseRegions= 1, + regions: {ERASEINFO(0x01000,64), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST29LE020, + .name = "SST 29LE020", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_SST_PAGE, + .NumEraseRegions= 1, + regions: {ERASEINFO(0x01000,64), + } + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF020, .name = "SST 39LF020", @@ -1116,6 +1275,32 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,64), } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF004B, + .name = "SST 49LF004B", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST49LF008A, + .name = "SST 49LF008A", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ + }, + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x01000,256), + } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF030A, @@ -1392,37 +1577,50 @@ static const struct amd_flash_info jedec_table[] = { static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); static int jedec_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi); + unsigned long *chip_map, struct cfi_private *cfi); struct mtd_info *jedec_probe(struct map_info *map); static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, struct cfi_private *cfi) { - u32 result, mask; + map_word result; + unsigned long mask; mask = (1 << (cfi->device_type * 8)) -1; - result = cfi_read(map, base); - result &= mask; - return result; + result = map_read(map, base); + return result.x[0] & mask; } static inline u32 jedec_read_id(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf; - u32 result, mask; + map_word result; + unsigned long mask; osf = cfi->interleave *cfi->device_type; mask = (1 << (cfi->device_type * 8)) -1; - result = cfi_read(map, base + osf); - result &= mask; - return result; + result = map_read(map, base + osf); + return result.x[0] & mask; } static inline void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi) { /* Reset */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + + /* after checking the datasheets for SST, MACRONIX and ATMEL + * (oh and incidentaly the jedec spec - 3.5.3.3) the reset + * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at + * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips + * as they will ignore the writes and dont care what address + * the F0 is written to */ + if(cfi->addr_unlock1) { + /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/ + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + + cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* Some misdesigned intel chips do not respond for 0xF0 for a reset, * so ensure we're in read mode. Send both the Intel and the AMD command * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so @@ -1450,6 +1648,12 @@ static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_ty uaddr = finfo->uaddr[uaddr_idx]; + if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { + /* ASSERT("The unlock addresses for non-8-bit mode + are bollocks. We don't really need an array."); */ + uaddr = finfo->uaddr[0]; + } + uaddr_done: return uaddr; } @@ -1458,6 +1662,7 @@ static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_ty static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; + unsigned long mask; __u8 uaddr; printk("Found: %s\n",jedec_table[index].name); @@ -1487,12 +1692,15 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) p_cfi->id = jedec_table[index].dev_id; uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); - if ( MTD_UADDR_NOT_SUPPORTED ) { + if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { kfree( p_cfi->cfiq ); return 0; } - p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; - p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; + + /* Mask out address bits which are smaller than the device type */ + mask = ~(p_cfi->device_type-1); + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask; return 1; /* ok */ } @@ -1513,9 +1721,37 @@ static inline int jedec_match( __u32 base, int rc = 0; /* failure until all tests pass */ u32 mfr, id; __u8 uaddr; + unsigned long mask; - /* The ID's must match */ - if ( cfi->mfr != finfo->mfr_id || cfi->id != finfo->dev_id ) { + /* + * The IDs must match. For X16 and X32 devices operating in + * a lower width ( X8 or X16 ), the device ID's are usually just + * the lower byte(s) of the larger device ID for wider mode. If + * a part is found that doesn't fit this assumption (device id for + * smaller width mode is completely unrealated to full-width mode) + * then the jedec_table[] will have to be augmented with the IDs + * for different widths. + */ + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: + mfr = (__u8)finfo->mfr_id; + id = (__u8)finfo->dev_id; + break; + case CFI_DEVICETYPE_X16: + mfr = (__u16)finfo->mfr_id; + id = (__u16)finfo->dev_id; + break; + case CFI_DEVICETYPE_X32: + mfr = (__u16)finfo->mfr_id; + id = (__u32)finfo->dev_id; + break; + default: + printk(KERN_WARNING + "MTD %s(): Unsupported device type %d\n", + __func__, cfi->device_type); + goto match_done; + } + if ( cfi->mfr != mfr || cfi->id != id ) { goto match_done; } @@ -1523,7 +1759,7 @@ static inline int jedec_match( __u32 base, DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); - if ( base + ( 1 << finfo->DevSize ) > map->size ) { + if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", __func__, finfo->mfr_id, finfo->dev_id, @@ -1532,20 +1768,22 @@ static inline int jedec_match( __u32 base, } uaddr = finfo_uaddr(finfo, cfi->device_type); - if ( MTD_UADDR_NOT_SUPPORTED ) { + if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { goto match_done; } + mask = ~(cfi->device_type-1); + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr - && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 - || unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { + && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 || + (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) { DEBUG( MTD_DEBUG_LEVEL3, - "MTD %s(): 0x%.4x 0x%.4x did not match\n", + "MTD %s(): 0x%.4lx 0x%.4lx did not match\n", __func__, - unlock_addrs[uaddr].addr1, - unlock_addrs[uaddr].addr2 ); + unlock_addrs[uaddr].addr1 & mask, + unlock_addrs[uaddr].addr2 & mask); goto match_done; } @@ -1593,41 +1831,25 @@ static inline int jedec_match( __u32 base, static int jedec_probe_chip(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi) + unsigned long *chip_map, struct cfi_private *cfi) { int i; - int unlockpass = 0; + enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; - /* - * FIXME - eventually replace these unlock address seeds with - * information from unlock_addrs[]. - */ + retry: if (!cfi->numchips) { - switch (cfi->device_type) { - case CFI_DEVICETYPE_X8: - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; - break; - case CFI_DEVICETYPE_X16: - cfi->addr_unlock1 = 0xaaa; - if (map->buswidth == cfi->interleave) { - /* X16 chip(s) in X8 mode */ - cfi->addr_unlock2 = 0x555; - } else { - cfi->addr_unlock2 = 0x554; - } - break; - case CFI_DEVICETYPE_X32: - cfi->addr_unlock1 = 0x1555; - cfi->addr_unlock2 = 0xaaa; - break; - default: - printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type); - return 0; - } + unsigned long mask = ~(cfi->device_type-1); + + uaddr_idx++; + + if (MTD_UADDR_UNNECESSARY == uaddr_idx) + return 0; + + /* Mask out address bits which are smaller than the device type */ + cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask; + cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask; } - retry: /* Make certain we aren't probing past the end of map */ if (base >= map->size) { printk(KERN_NOTICE @@ -1668,7 +1890,8 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, cfi->mfr = jedec_read_mfr(map, base, cfi); cfi->id = jedec_read_id(map, base, cfi); - printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", + DEBUG(MTD_DEBUG_LEVEL3, + "Search for id:(%02x %02x) interleave(%d) type(%d)\n", cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); for (i=0; iaddr_unlock1 |= cfi->addr_unlock1 << 4; - cfi->addr_unlock2 |= cfi->addr_unlock2 << 4; - goto retry; - case 1: - cfi->addr_unlock1 = cfi->addr_unlock2 = 0; - goto retry; - } - return 0; + goto retry; } else { __u16 mfr; __u16 id; @@ -1707,21 +1921,24 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } } - /* Check each previous chip to see if it's an alias */ - for (i=0; inumchips; i++) { - /* This chip should be in read mode if it's one - we've already touched. */ - if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && - jedec_read_id(map, chips[i].start, cfi) == cfi->id) { + /* Check each previous chip locations to see if it's an alias */ + for (i=0; i < (base >> cfi->chipshift); i++) { + unsigned long start; + if(!test_bit(i, chip_map)) { + continue; /* Skip location; no valid chip at this address */ + } + start = i << cfi->chipshift; + if (jedec_read_mfr(map, start, cfi) == cfi->mfr && + jedec_read_id(map, start, cfi) == cfi->id) { /* Eep. This chip also looks like it's in autoselect mode. Is it an alias for the new one? */ - jedec_reset(chips[i].start, map, cfi); + jedec_reset(start, map, cfi); /* If the device IDs go away, it's an alias */ if (jedec_read_mfr(map, base, cfi) != cfi->mfr || jedec_read_id(map, base, cfi) != cfi->id) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } @@ -1733,7 +1950,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, if (jedec_read_mfr(map, base, cfi) == cfi->mfr && jedec_read_id(map, base, cfi) == cfi->id) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", - map->name, base, chips[i].start); + map->name, base, start); return 0; } } @@ -1741,22 +1958,16 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, /* OK, if we got to here, then none of the previous chips appear to be aliases for the current one. */ - if (cfi->numchips == MAX_CFI_CHIPS) { - printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); - /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ - return -1; - } - chips[cfi->numchips].start = base; - chips[cfi->numchips].state = FL_READY; + set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ cfi->numchips++; ok_out: /* Put it back into Read Mode */ jedec_reset(base, map, cfi); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, - map->buswidth*8); + map->bankwidth*8); return 1; } diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 7e17b452e..db6f6c40f 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $ + * $Id: map_ram.c,v 1.19 2004/07/12 21:58:44 dwmw2 Exp $ */ #include @@ -104,10 +104,15 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr) /* Yeah, it's inefficient. Who cares? It's faster than a _real_ flash erase. */ struct map_info *map = (struct map_info *)mtd->priv; + map_word allff; unsigned long i; - for (i=0; ilen; i++) - map_write8(map, 0xFF, instr->addr + i); + allff = map_word_ff(map); + + for (i=0; ilen; i += map_bankwidth(map)) + map_write(map, allff, instr->addr + i); + + instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 8ccf91753..db2e2c6a1 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $ + * $Id: map_rom.c,v 1.21 2004/07/12 14:06:01 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c index bd0ed8905..1313c7029 100644 --- a/drivers/mtd/chips/sharp.c +++ b/drivers/mtd/chips/sharp.c @@ -4,7 +4,7 @@ * Copyright 2000,2001 David A. Schleef * 2000,2001 Lineo, Inc. * - * $Id: sharp.c,v 1.12 2003/05/28 15:39:52 dwmw2 Exp $ + * $Id: sharp.c,v 1.13 2004/07/12 14:06:34 dwmw2 Exp $ * * Devices supported: * LH28F016SCT Symmetrical block flash memory, 2Mx8 diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index a66e5a665..4e0f1a7a0 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.9 2003/05/16 17:08:24 dwmw2 Exp $ + * $Id: cmdlinepart.c,v 1.14 2004/07/12 12:34:23 dwmw2 Exp $ * * Read flash partition table from command line * @@ -10,7 +10,7 @@ * mtdparts=[; := :[,] * := [@offset][][ro] - * := unique id used in mapping driver/device + * := unique name used in mapping driver/device (mtd->name) * := standard linux memsize OR "-" to denote all remaining space * := '(' NAME ')' * @@ -358,14 +358,7 @@ static int __init cmdline_parser_init(void) return register_mtd_parser(&cmdline_parser); } -static void __exit cmdline_parser_exit(void) -{ - deregister_mtd_parser(&cmdline_parser); -} - module_init(cmdline_parser_init); -module_exit(cmdline_parser_exit); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marius Groeger "); diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index d1df46206..6ecd5ec9e 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.4 2003/05/28 15:18:54 dwmw2 Exp $ +# $Id: Kconfig,v 1.10 2004/07/15 00:34:49 dwmw2 Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n @@ -9,9 +9,9 @@ config MTD_PMC551 depends on MTD && PCI ---help--- This provides a MTD device driver for the Ramix PMC551 RAM PCI card - from Ramix Inc. . These devices come - in memory configurations from 32M - 1G. If you have one, you - probably want to enable this. + from Ramix Inc. . + These devices come in memory configurations from 32M - 1G. If you + have one, you probably want to enable this. If this driver is compiled as a module you get the ability to select the size of the aperture window pointing into the devices memory. @@ -40,9 +40,12 @@ config MTD_PMC551_DEBUG config MTD_MS02NV tristate "DEC MS02-NV NVRAM module support" - depends on CONFIG_MACH_DECSTATION + depends on MTD && MACH_DECSTATION help - Support for NVRAM module on DECstation. + This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery + backed-up NVRAM module. The module was originally meant as an NFS + accelerator. Say Y here if you have a DECstation 5000/2x0 or a + DECsystem 5900 equipped with such a module. config MTD_SLRAM tristate "Uncached system RAM" @@ -52,6 +55,16 @@ config MTD_SLRAM you can still use it for storage or swap by using this driver to present it to the system as a Memory Technology Device. +config MTD_PHRAM + tristate "Physical system RAM" + depends on MTD + help + This is a re-implementation of the slram driver above. + + Use this driver to access physical memory that the kernel proper + doesn't have access to, memory beyond the mem=xxx limit, nvram, + memory on the video card, etc... + config MTD_LART tristate "28F160xx flash driver for LART" depends on SA1100_LART && MTD @@ -161,11 +174,18 @@ config MTD_DOC2001PLUS config MTD_DOCPROBE tristate - default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MOD_DOC2001PLUS=m) + default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m) default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y help This isn't a real config option, it's derived. +config MTD_DOCECC + tristate + default m if MTD_DOCPROBE!=y && MTD_NAND_DISKONCHIP!=y && (MTD_DOCPROBE=m || MTD_NAND_DISKONCHIP=m) + default y if MTD_DOCPROBE=y || MTD_NAND_DISKONCHIP=y + help + This isn't a real config option, it's derived. + config MTD_DOCPROBE_ADVANCED bool "Advanced detection options for DiskOnChip" depends on MTD_DOCPROBE diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 8ab580b91..3bb872eda 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.3 2003/05/28 10:54:23 dwmw2 Exp $ +# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $ # *** BIG UGLY NOTE *** # @@ -13,8 +13,10 @@ obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o -obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o +obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o +obj-$(CONFIG_MTD_DOCECC) += docecc.o obj-$(CONFIG_MTD_SLRAM) += slram.o +obj-$(CONFIG_MTD_PHRAM) += phram.o obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o diff --git a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c index 4bd5d3219..a7e3a9e4b 100644 --- a/drivers/mtd/devices/blkmtd.c +++ b/drivers/mtd/devices/blkmtd.c @@ -1,5 +1,5 @@ /* - * $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $ + * $Id: blkmtd-25.c,v 1.6 2004/07/15 15:09:15 dwmw2 Exp $ * * blkmtd.c - use a block device as a fake MTD * @@ -39,7 +39,7 @@ /* Default erase size in K, always make it a multiple of PAGE_SIZE */ #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ -#define VERSION "$Revision: 1.5 $" +#define VERSION "$Revision: 1.6 $" /* Info for the block device */ struct blkmtd_dev { diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index fe708695f..1a0c28e4e 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2000.c,v 1.53 2003/06/11 09:45:19 dwmw2 Exp $ + * $Id: doc2000.c,v 1.60 2004/04/07 08:30:04 gleixner Exp $ */ #include @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include #define DOC_SUPPORT_2000 +#define DOC_SUPPORT_2000TSOP #define DOC_SUPPORT_MILLENNIUM #ifdef DOC_SUPPORT_2000 @@ -33,7 +35,7 @@ #define DoC_is_2000(doc) (0) #endif -#ifdef DOC_SUPPORT_MILLENNIUM +#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) #else #define DoC_is_Millennium(doc) (0) @@ -53,11 +55,12 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused); + size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused); + size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); +static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen, + u_char *eccbuf, struct nand_oobinfo *oobsel); static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, @@ -94,6 +97,10 @@ static int _DoC_WaitReady(struct DiskOnChip *doc) /* Out-of-line routine to wait for chip response */ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + /* issue 2 read from NOP register after reading from CDSNControl register + see Software Requirement 11.4 item 2. */ + DoC_Delay(doc, 2); + if (time_after(jiffies, timeo)) { DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); return -EIO; @@ -147,6 +154,8 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, /* Send the command */ WriteDOC_(command, docptr, doc->ioreg); + if (DoC_is_Millennium(doc)) + WriteDOC(command, docptr, WritePipeTerm); /* Lower the CLE line */ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); @@ -208,6 +217,9 @@ static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, } } + if (DoC_is_Millennium(doc)) + WriteDOC(ofs & 0xff, docptr, WritePipeTerm); + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ /* FIXME: The SlowIO's for millennium could be replaced by @@ -346,15 +358,25 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) /* Read the manufacturer and device id codes from the device */ - /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ - dummy = ReadDOC(doc->virtadr, CDSNSlowIO); - DoC_Delay(doc, 2); - mfr = ReadDOC_(doc->virtadr, doc->ioreg); + if (DoC_is_Millennium(doc)) { + DoC_Delay(doc, 2); + dummy = ReadDOC(doc->virtadr, ReadPipeInit); + mfr = ReadDOC(doc->virtadr, LastDataRead); - /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ - dummy = ReadDOC(doc->virtadr, CDSNSlowIO); - DoC_Delay(doc, 2); - id = ReadDOC_(doc->virtadr, doc->ioreg); + DoC_Delay(doc, 2); + dummy = ReadDOC(doc->virtadr, ReadPipeInit); + id = ReadDOC(doc->virtadr, LastDataRead); + } else { + /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + + /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); + } /* No response - return failure */ if (mfr == 0xff || mfr == 0) @@ -388,11 +410,10 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) if (!doc->mfr) { doc->mfr = mfr; doc->id = id; - doc->chipshift = - nand_flash_ids[i].chipshift; - doc->page256 = nand_flash_ids[i].page256; - doc->pageadrlen = - nand_flash_ids[i].chipshift > 25 ? 3 : 2; + doc->chipshift = + ffs((nand_flash_ids[i].chipsize << 20)) - 1; + doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0; + doc->pageadrlen = doc->chipshift > 25 ? 3 : 2; doc->erasesize = nand_flash_ids[i].erasesize; return 1; @@ -412,20 +433,16 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ -static void DoC_ScanChips(struct DiskOnChip *this) +static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) { int floor, chip; int numchips[MAX_FLOORS]; - int maxchips = MAX_CHIPS; int ret = 1; this->numchips = 0; this->mfr = 0; this->id = 0; - if (DoC_is_Millennium(this)) - maxchips = MAX_CHIPS_MIL; - /* For each floor, find the number of valid chips it contains */ for (floor = 0; floor < MAX_FLOORS; floor++) { ret = 1; @@ -517,6 +534,7 @@ static void DoC2k_init(struct mtd_info *mtd) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; struct DiskOnChip *old = NULL; + int maxchips; /* We must avoid being called twice for the same device. */ @@ -540,14 +558,28 @@ static void DoC2k_init(struct mtd_info *mtd) switch (this->ChipID) { + case DOC_ChipID_Doc2kTSOP: + mtd->name = "DiskOnChip 2000 TSOP"; + this->ioreg = DoC_Mil_CDSN_IO; + /* Pretend it's a Millennium */ + this->ChipID = DOC_ChipID_DocMil; + maxchips = MAX_CHIPS; + break; case DOC_ChipID_Doc2k: mtd->name = "DiskOnChip 2000"; this->ioreg = DoC_2k_CDSN_IO; + maxchips = MAX_CHIPS; break; case DOC_ChipID_DocMil: mtd->name = "DiskOnChip Millennium"; this->ioreg = DoC_Mil_CDSN_IO; + maxchips = MAX_CHIPS_MIL; break; + default: + printk("Unknown ChipID 0x%02x\n", this->ChipID); + kfree(mtd); + iounmap((void *) this->virtadr); + return; } printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, @@ -568,6 +600,7 @@ static void DoC2k_init(struct mtd_info *mtd) mtd->write = doc_write; mtd->read_ecc = doc_read_ecc; mtd->write_ecc = doc_write_ecc; + mtd->writev_ecc = doc_writev_ecc; mtd->read_oob = doc_read_oob; mtd->write_oob = doc_write_oob; mtd->sync = NULL; @@ -580,7 +613,7 @@ static void DoC2k_init(struct mtd_info *mtd) init_MUTEX(&this->lock); /* Ident all the chips present. */ - DoC_ScanChips(this); + DoC_ScanChips(this, maxchips); if (!this->totlen) { kfree(mtd); @@ -599,12 +632,11 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { /* Just a special case of doc_read_ecc */ - return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); } static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * eccbuf, - struct nand_oobinfo *unused) + size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; unsigned long docptr; @@ -612,6 +644,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, unsigned char syndrome[6]; volatile char dummy; int i, len256 = 0, ret=0; + size_t left = len; docptr = this->virtadr; @@ -621,122 +654,131 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, down(&this->lock); - /* Don't allow a single read to cross a 512-byte block boundary */ - if (from + len > ((from | 0x1ff) + 1)) - len = ((from | 0x1ff) + 1) - from; - - /* The ECC will not be calculated correctly if less than 512 is read */ - if (len != 0x200 && eccbuf) - printk(KERN_WARNING - "ECC needs a full sector read (adr: %lx size %lx)\n", - (long) from, (long) len); - - /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */ + *retlen = 0; + while (left) { + len = left; + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; - /* Find the chip which is to be used and select it */ - mychip = &this->chips[from >> (this->chipshift)]; + /* The ECC will not be calculated correctly if less than 512 is read */ + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector read (adr: %lx size %lx)\n", + (long) from, (long) len); - if (this->curfloor != mychip->floor) { - DoC_SelectFloor(this, mychip->floor); - DoC_SelectChip(this, mychip->chip); - } else if (this->curchip != mychip->chip) { - DoC_SelectChip(this, mychip->chip); - } + /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */ - this->curfloor = mychip->floor; - this->curchip = mychip->chip; - DoC_Command(this, - (!this->page256 - && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, - CDSN_CTRL_WP); - DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP, - CDSN_CTRL_ECC_IO); - - if (eccbuf) { - /* Prime the ECC engine */ - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_EN, docptr, ECCConf); - } else { - /* disable the ECC engine */ - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_DIS, docptr, ECCConf); - } + /* Find the chip which is to be used and select it */ + mychip = &this->chips[from >> (this->chipshift)]; - /* treat crossing 256-byte sector for 2M x 8bits devices */ - if (this->page256 && from + len > (from | 0xff) + 1) { - len256 = (from | 0xff) + 1 - from; - DoC_ReadBuf(this, buf, len256); + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } - DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); - DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, - CDSN_CTRL_WP, CDSN_CTRL_ECC_IO); - } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; - DoC_ReadBuf(this, &buf[len256], len - len256); + DoC_Command(this, + (!this->page256 + && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP, + CDSN_CTRL_ECC_IO); - /* Let the caller know we completed it */ - *retlen = len; + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + } - if (eccbuf) { - /* Read the ECC data through the DiskOnChip ECC logic */ - /* Note: this will work even with 2M x 8bit devices as */ - /* they have 8 bytes of OOB per 256 page. mf. */ - DoC_ReadBuf(this, eccbuf, 6); + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && from + len > (from | 0xff) + 1) { + len256 = (from | 0xff) + 1 - from; + DoC_ReadBuf(this, buf, len256); - /* Flush the pipeline */ - if (DoC_is_Millennium(this)) { - dummy = ReadDOC(docptr, ECCConf); - dummy = ReadDOC(docptr, ECCConf); - i = ReadDOC(docptr, ECCConf); - } else { - dummy = ReadDOC(docptr, 2k_ECCStatus); - dummy = ReadDOC(docptr, 2k_ECCStatus); - i = ReadDOC(docptr, 2k_ECCStatus); + DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); + DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, + CDSN_CTRL_WP, CDSN_CTRL_ECC_IO); } - /* Check the ECC Status */ - if (i & 0x80) { - int nb_errors; - /* There was an ECC error */ + DoC_ReadBuf(this, &buf[len256], len - len256); + + /* Let the caller know we completed it */ + *retlen += len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ + /* Note: this will work even with 2M x 8bit devices as */ + /* they have 8 bytes of OOB per 256 page. mf. */ + DoC_ReadBuf(this, eccbuf, 6); + + /* Flush the pipeline */ + if (DoC_is_Millennium(this)) { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + i = ReadDOC(docptr, ECCConf); + } else { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + i = ReadDOC(docptr, 2k_ECCStatus); + } + + /* Check the ECC Status */ + if (i & 0x80) { + int nb_errors; + /* There was an ECC error */ #ifdef ECC_DEBUG - printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from); + printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from); #endif - /* Read the ECC syndrom through the DiskOnChip ECC logic. - These syndrome will be all ZERO when there is no error */ - for (i = 0; i < 6; i++) { - syndrome[i] = - ReadDOC(docptr, ECCSyndrome0 + i); - } - nb_errors = doc_decode_ecc(buf, syndrome); + /* Read the ECC syndrom through the DiskOnChip ECC logic. + These syndrome will be all ZERO when there is no error */ + for (i = 0; i < 6; i++) { + syndrome[i] = + ReadDOC(docptr, ECCSyndrome0 + i); + } + nb_errors = doc_decode_ecc(buf, syndrome); #ifdef ECC_DEBUG - printk(KERN_ERR "Errors corrected: %x\n", nb_errors); + printk(KERN_ERR "Errors corrected: %x\n", nb_errors); #endif - if (nb_errors < 0) { - /* We return error, but have actually done the read. Not that - this can be told to user-space, via sys_read(), but at least - MTD-aware stuff can know about it by checking *retlen */ - ret = -EIO; - } - } + if (nb_errors < 0) { + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + ret = -EIO; + } + } #ifdef PSYCHO_DEBUG - printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - (long)from, eccbuf[0], eccbuf[1], eccbuf[2], - eccbuf[3], eccbuf[4], eccbuf[5]); + printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5]); #endif - /* disable the ECC engine */ - WriteDOC(DOC_ECC_DIS, docptr , ECCConf); - } + /* disable the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr , ECCConf); + } - /* according to 11.4.1, we need to wait for the busy line - * drop if we read to the end of the page. */ - if(0 == ((from + *retlen) & 0x1ff)) - { - DoC_WaitReady(this); + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ + if(0 == ((from + len) & 0x1ff)) + { + DoC_WaitReady(this); + } + + from += len; + left -= len; + buf += len; } up(&this->lock); @@ -748,13 +790,12 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) { char eccbuf[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); } static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf, - u_char * eccbuf, - struct nand_oobinfo *unused) + u_char * eccbuf, struct nand_oobinfo *oobsel) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ @@ -762,6 +803,8 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, volatile char dummy; int len256 = 0; struct Nand *mychip; + size_t left = len; + int status; docptr = this->virtadr; @@ -771,65 +814,133 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, down(&this->lock); - /* Don't allow a single write to cross a 512-byte block boundary */ - if (to + len > ((to | 0x1ff) + 1)) - len = ((to | 0x1ff) + 1) - to; + *retlen = 0; + while (left) { + len = left; - /* The ECC will not be calculated correctly if less than 512 is written */ - if (len != 0x200 && eccbuf) - printk(KERN_WARNING - "ECC needs a full sector write (adr: %lx size %lx)\n", - (long) to, (long) len); + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ((to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; - /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ + /* The ECC will not be calculated correctly if less than 512 is written */ +/* DBB- + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); + -DBB */ - /* Find the chip which is to be used and select it */ - mychip = &this->chips[to >> (this->chipshift)]; + /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ - if (this->curfloor != mychip->floor) { - DoC_SelectFloor(this, mychip->floor); - DoC_SelectChip(this, mychip->chip); - } else if (this->curchip != mychip->chip) { - DoC_SelectChip(this, mychip->chip); - } + /* Find the chip which is to be used and select it */ + mychip = &this->chips[to >> (this->chipshift)]; - this->curfloor = mychip->floor; - this->curchip = mychip->chip; + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(this, mychip->floor); + DoC_SelectChip(this, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(this, mychip->chip); + } - /* Set device to main plane of flash */ - DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); - DoC_Command(this, - (!this->page256 - && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, - CDSN_CTRL_WP); + this->curfloor = mychip->floor; + this->curchip = mychip->chip; - DoC_Command(this, NAND_CMD_SEQIN, 0); - DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); + /* Set device to main plane of flash */ + DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(this, + (!this->page256 + && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0, + CDSN_CTRL_WP); - if (eccbuf) { - /* Prime the ECC engine */ - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); - } else { - /* disable the ECC engine */ - WriteDOC(DOC_ECC_RESET, docptr, ECCConf); - WriteDOC(DOC_ECC_DIS, docptr, ECCConf); - } + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); - /* treat crossing 256-byte sector for 2M x 8bits devices */ - if (this->page256 && to + len > (to | 0xff) + 1) { - len256 = (to | 0xff) + 1 - to; - DoC_WriteBuf(this, buf, len256); + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } else { + /* disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + } + + /* treat crossing 256-byte sector for 2M x 8bits devices */ + if (this->page256 && to + len > (to | 0xff) + 1) { + len256 = (to | 0xff) + 1 - to; + DoC_WriteBuf(this, buf, len256); + + DoC_Command(this, NAND_CMD_PAGEPROG, 0); + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + + if (ReadDOC_(docptr, this->ioreg) & 1) { + printk(KERN_ERR "Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + up(&this->lock); + return -EIO; + } + + DoC_Command(this, NAND_CMD_SEQIN, 0); + DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, + CDSN_CTRL_ECC_IO); + } + + DoC_WriteBuf(this, &buf[len256], len - len256); + + if (eccbuf) { + WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, + CDSNControl); + + if (DoC_is_Millennium(this)) { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } else { + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + WriteDOC_(0, docptr, this->ioreg); + } + + WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, + CDSNControl); + + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di = 0; di < 6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); + } + + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + +#ifdef PSYCHO_DEBUG + printk + ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], + eccbuf[4], eccbuf[5]); +#endif + } DoC_Command(this, NAND_CMD_PAGEPROG, 0); DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); /* There's an implicit DoC_WaitReady() in DoC_Command */ - dummy = ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(this, 2); + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming flash\n"); /* Error in programming */ *retlen = 0; @@ -837,82 +948,97 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return -EIO; } - DoC_Command(this, NAND_CMD_SEQIN, 0); - DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, - CDSN_CTRL_ECC_IO); + /* Let the caller know we completed it */ + *retlen += len; + + if (eccbuf) { + unsigned char x[8]; + size_t dummy; + int ret; + + /* Write the ECC data to flash */ + for (di=0; di<6; di++) + x[di] = eccbuf[di]; + + x[6]=0x55; + x[7]=0x55; + + ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); + if (ret) { + up(&this->lock); + return ret; + } + } + + to += len; + left -= len; + buf += len; } - DoC_WriteBuf(this, &buf[len256], len - len256); + up(&this->lock); + return 0; +} - if (eccbuf) { - WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, - CDSNControl); +static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen, + u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + static char static_buf[512]; + static DECLARE_MUTEX(writev_buf_sem); - if (DoC_is_Millennium(this)) { - WriteDOC(0, docptr, NOP); - WriteDOC(0, docptr, NOP); - WriteDOC(0, docptr, NOP); - } else { - WriteDOC_(0, docptr, this->ioreg); - WriteDOC_(0, docptr, this->ioreg); - WriteDOC_(0, docptr, this->ioreg); - } + size_t totretlen = 0; + size_t thisvecofs = 0; + int ret= 0; - /* Read the ECC data through the DiskOnChip ECC logic */ - for (di = 0; di < 6; di++) { - eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); - } + down(&writev_buf_sem); - /* Reset the ECC engine */ - WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + while(count) { + size_t thislen, thisretlen; + unsigned char *buf; -#ifdef PSYCHO_DEBUG - printk - ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], - eccbuf[4], eccbuf[5]); -#endif - } + buf = vecs->iov_base + thisvecofs; + thislen = vecs->iov_len - thisvecofs; - DoC_Command(this, NAND_CMD_PAGEPROG, 0); - DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); - /* There's an implicit DoC_WaitReady() in DoC_Command */ + if (thislen >= 512) { + thislen = thislen & ~(512-1); + thisvecofs += thislen; + } else { + /* Not enough to fill a page. Copy into buf */ + memcpy(static_buf, buf, thislen); + buf = &static_buf[thislen]; + + while(count && thislen < 512) { + vecs++; + count--; + thisvecofs = min((512-thislen), vecs->iov_len); + memcpy(buf, vecs->iov_base, thisvecofs); + thislen += thisvecofs; + buf += thisvecofs; + } + buf = static_buf; + } + if (count && thisvecofs == vecs->iov_len) { + thisvecofs = 0; + vecs++; + count--; + } + ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); - dummy = ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(this, 2); + totretlen += thisretlen; - if (ReadDOC_(docptr, this->ioreg) & 1) { - printk(KERN_ERR "Error programming flash\n"); - /* Error in programming */ - *retlen = 0; - up(&this->lock); - return -EIO; - } + if (ret || thisretlen != thislen) + break; - /* Let the caller know we completed it */ - *retlen = len; - - if (eccbuf) { - unsigned char x[8]; - size_t dummy; - int ret; - - /* Write the ECC data to flash */ - for (di=0; di<6; di++) - x[di] = eccbuf[di]; - - x[6]=0x55; - x[7]=0x55; - - ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); - up(&this->lock); - return ret; - } - up(&this->lock); - return 0; + to += thislen; + } + + up(&writev_buf_sem); + *retlen = totretlen; + return ret; } + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, u_char * buf) { @@ -982,6 +1108,7 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, unsigned long docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; volatile int dummy; + int status; // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); @@ -1030,10 +1157,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_Command(this, NAND_CMD_STATUS, 0); /* DoC_WaitReady() is implicit in DoC_Command */ - dummy = ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(this, 2); + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming oob data\n"); /* There was an error */ *retlen = 0; @@ -1049,10 +1182,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, DoC_Command(this, NAND_CMD_STATUS, 0); /* DoC_WaitReady() is implicit in DoC_Command */ - dummy = ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(this, 2); + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (status & 1) { printk(KERN_ERR "Error programming oob data\n"); /* There was an error */ *retlen = 0; @@ -1085,6 +1224,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) volatile int dummy; unsigned long docptr; struct Nand *mychip; + int status; down(&this->lock); @@ -1116,10 +1256,16 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); - dummy = ReadDOC(docptr, CDSNSlowIO); - DoC_Delay(this, 2); - - if (ReadDOC_(docptr, this->ioreg) & 1) { + if (DoC_is_Millennium(this)) { + ReadDOC(docptr, ReadPipeInit); + status = ReadDOC(docptr, LastDataRead); + } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); + status = ReadDOC_(docptr, this->ioreg); + } + + if (status & 1) { printk(KERN_ERR "Error erasing at 0x%x\n", ofs); /* There was an error */ instr->state = MTD_ERASE_FAILED; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 6d5d8218b..77ed2c409 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001.c,v 1.41 2003/06/11 09:45:19 dwmw2 Exp $ + * $Id: doc2001.c,v 1.42 2004/04/04 12:36:45 gleixner Exp $ */ #include @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -37,12 +38,9 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused); + size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused); - + size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, @@ -229,7 +227,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name); doc->mfr = mfr; doc->id = id; - doc->chipshift = nand_flash_ids[i].chipshift; + doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; break; } } @@ -406,12 +404,11 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { /* Just a special case of doc_read_ecc */ - return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); } static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused) + size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) { int i, ret; volatile char dummy; @@ -533,12 +530,11 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { char eccbuf[6]; - return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); } static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf, u_char *eccbuf, - struct nand_oobinfo *unused) + size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel) { int i,ret = 0; volatile char dummy; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index e5e5fdaeb..da4275cb7 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -6,7 +6,9 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001plus.c,v 1.5 2003/06/11 09:45:19 dwmw2 Exp $ + * $Id: doc2001plus.c,v 1.8 2004/04/04 12:36:45 gleixner Exp $ + * + * Released under GPL */ #include @@ -21,6 +23,7 @@ #include #include #include +#include #include #include @@ -183,24 +186,35 @@ static int DoC_SelectFloor(unsigned long docptr, int floor) * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | * +-----------+-------+-------+-------+--------------+---------+-----------+ */ +/* FIXME: This lives in INFTL not here. Other users of flash devices + may not want it */ static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) { - unsigned int ofs = *from & 0x3ff; - unsigned int cmd; + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; - if (ofs < 512) { - cmd = NAND_CMD_READ0; - ofs &= 0x1ff; - } else if (ofs < 1014) { - cmd = NAND_CMD_READ1; - ofs = (ofs & 0x1ff) + 10; + if (this->interleave) { + unsigned int ofs = *from & 0x3ff; + unsigned int cmd; + + if (ofs < 512) { + cmd = NAND_CMD_READ0; + ofs &= 0x1ff; + } else if (ofs < 1014) { + cmd = NAND_CMD_READ1; + ofs = (ofs & 0x1ff) + 10; + } else { + cmd = NAND_CMD_READOOB; + ofs = ofs - 1014; + } + + *from = (*from & ~0x3ff) | ofs; + return cmd; } else { - cmd = NAND_CMD_READOOB; - ofs = ofs - 1014; + /* No interleave */ + if ((*from) & 0x100) + return NAND_CMD_READ1; + return NAND_CMD_READ0; } - - *from = (*from & ~0x3ff) | ofs; - return cmd; } static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) @@ -294,10 +308,12 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) dummy = ReadDOC(docptr, Mplus_ReadPipeInit); mfr = ReadDOC(docptr, Mil_CDSN_IO); - dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ + if (doc->interleave) + dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ id = ReadDOC(docptr, Mil_CDSN_IO); - dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ + if (doc->interleave) + dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ dummy = ReadDOC(docptr, Mplus_LastDataRead); dummy = ReadDOC(docptr, Mplus_LastDataRead); @@ -321,10 +337,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) nand_manuf_ids[j].name, nand_flash_ids[i].name); doc->mfr = mfr; doc->id = id; - doc->interleave = 0; - if (doc->ChipID == DOC_ChipID_DocMilPlus32) - doc->interleave = 1; - doc->chipshift = nand_flash_ids[i].chipshift; + doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; break; } @@ -346,6 +359,21 @@ static void DoC_ScanChips(struct DiskOnChip *this) this->mfr = 0; this->id = 0; + /* Work out the intended interleave setting */ + this->interleave = 0; + if (this->ChipID == DOC_ChipID_DocMilPlus32) + this->interleave = 1; + + /* Check the ASIC agrees */ + if ( (this->interleave << 2) != + (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { + u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); + printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", + this->interleave?"on (16-bit)":"off (8-bit)"); + conf ^= 4; + WriteDOC(this->virtadr, conf, Mplus_Configuration); + } + /* For each floor, find the number of valid chips it contains */ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { numchips[floor] = 0; @@ -739,7 +767,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, return -EINVAL; /* Determine position of OOB flags, before or after data */ - before = to & 0x200; + before = (this->interleave && (to & 0x200)); DoC_CheckASIC(docptr); @@ -886,7 +914,10 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Figure out which region we are accessing... */ fofs = ofs; base = ofs & 0xf; - if (base < 6) { + if (!this->interleave) { + DoC_Command(docptr, NAND_CMD_READOOB, 0); + size = 16 - base; + } else if (base < 6) { DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); size = 6 - base; } else if (base < 8) { @@ -963,7 +994,10 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, /* Figure out which region we are accessing... */ fofs = ofs; base = ofs & 0x0f; - if (base < 6) { + if (!this->interleave) { + WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); + size = 16 - base; + } else if (base < 6) { WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); size = 6 - base; } else if (base < 8) { diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index f7a91fa86..cb8b8a51e 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -4,7 +4,7 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.36 2003/05/23 11:29:34 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */ @@ -135,6 +135,9 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr window, DOCControl); #endif /* !DOC_PASSIVE_PROBE */ + /* We need to read the ChipID register four times. For some + newer DiskOnChip 2000 units, the first three reads will + return the DiskOnChip Millennium ident. Don't ask. */ ChipID = ReadDOC(window, ChipID); switch (ChipID) { @@ -148,6 +151,12 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr break; case DOC_ChipID_DocMil: + /* Check for the new 2000 with Millennium ASIC */ + ReadDOC(window, ChipID); + ReadDOC(window, ChipID); + if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) + ChipID = DOC_ChipID_Doc2kTSOP; + /* Check the TOGGLE bit in the ECC register */ tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; @@ -191,7 +200,6 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; if (tmp != tmpb && tmp == tmpc) return ChipID; - break; default: break; } @@ -199,8 +207,8 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr default: -#ifndef CONFIG_MTD_DOCPROBE_55AA - printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", +#ifdef CONFIG_MTD_DOCPROBE_55AA + printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ChipID, physadr); #endif #ifndef DOC_PASSIVE_PROBE @@ -241,6 +249,12 @@ static void __init DoC_Probe(unsigned long physadr) return; if ((ChipID = doccheck(docptr, physadr))) { + if (ChipID == DOC_ChipID_Doc2kTSOP) { + /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ + printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); + iounmap((void *)docptr); + return; + } docfound = 1; mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); @@ -262,6 +276,12 @@ static void __init DoC_Probe(unsigned long physadr) sprintf(namebuf, "with ChipID %2.2X", ChipID); switch(ChipID) { + case DOC_ChipID_Doc2kTSOP: + name="2000 TSOP"; + im_funcname = "DoC2k_init"; + im_modname = "doc2000"; + break; + case DOC_ChipID_Doc2k: name="2000"; im_funcname = "DoC2k_init"; diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index f60195ec5..d67ca4cb2 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -2,7 +2,7 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $ + * $Id: lart.c,v 1.6 2004/07/14 17:21:38 dwmw2 Exp $ * * Author: Abraham vd Merwe * diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index e7f49a5ca..3a331aa0b 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -1,12 +1,12 @@ /* - * Copyright (c) 2001 Maciej W. Rozycki + * Copyright (c) 2001 Maciej W. Rozycki * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. * - * $Id: ms02-nv.c,v 1.4 2003/05/20 21:03:07 dwmw2 Exp $ + * $Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $ */ #include @@ -29,7 +29,7 @@ static char version[] __initdata = - "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; + "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; MODULE_AUTHOR("Maciej W. Rozycki "); MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); @@ -38,9 +38,9 @@ MODULE_LICENSE("GPL"); /* * Addresses we probe for an MS02-NV at. Modules may be located - * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB - * boundary within a 0MB up to 448MB range. We don't support a module - * at 0MB, though. + * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB + * boundary within a 0MiB up to 448MiB range. We don't support a module + * at 0MiB, though. */ static ulong ms02nv_addrs[] __initdata = { 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000, @@ -130,7 +130,7 @@ static int __init ms02nv_init_one(ulong addr) int ret = -ENODEV; - /* The module decodes 8MB of address space. */ + /* The module decodes 8MiB of address space. */ mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); if (!mod_res) return -ENOMEM; @@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr) goto err_out_csr_res; } - printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n", + printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n", mtd->index, ms02nv_name, addr, size >> 20); mp->next = root_ms02nv_mtd; @@ -293,12 +293,12 @@ static int __init ms02nv_init(void) switch (mips_machtype) { case MACH_DS5000_200: - csr = (volatile u32 *)KN02_CSR_ADDR; + csr = (volatile u32 *)KN02_CSR_BASE; if (*csr & KN02_CSR_BNK32M) stride = 2; break; case MACH_DS5000_2X0: - case MACH_DS5000: + case MACH_DS5900: csr = (volatile u32 *)KN03_MCR_BASE; if (*csr & KN03_MCR_BNK32M) stride = 2; diff --git a/drivers/mtd/devices/ms02-nv.h b/drivers/mtd/devices/ms02-nv.h index fb1ec1056..8a6eef7cf 100644 --- a/drivers/mtd/devices/ms02-nv.h +++ b/drivers/mtd/devices/ms02-nv.h @@ -1,34 +1,96 @@ /* - * Copyright (c) 2001 Maciej W. Rozycki + * Copyright (c) 2001, 2003 Maciej W. Rozycki * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for + * DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260 + * systems. * - * $Id: ms02-nv.h,v 1.1 2002/09/13 13:46:55 dwmw2 Exp $ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $ */ #include #include +/* + * Addresses are decoded as follows: + * + * 0x000000 - 0x3fffff SRAM + * 0x400000 - 0x7fffff CSR + * + * Within the SRAM area the following ranges are forced by the system + * firmware: + * + * 0x000000 - 0x0003ff diagnostic area, destroyed upon a reboot + * 0x000400 - ENDofRAM storage area, available to operating systems + * + * but we can't really use the available area right from 0x000400 as + * the first word is used by the firmware as a status flag passed + * from an operating system. If anything but the valid data magic + * ID value is found, the firmware considers the SRAM clean, i.e. + * containing no valid data, and disables the battery resulting in + * data being erased as soon as power is switched off. So the choice + * for the start address of the user-available is 0x001000 which is + * nicely page aligned. The area between 0x000404 and 0x000fff may + * be used by the driver for own needs. + * + * The diagnostic area defines two status words to be read by an + * operating system, a magic ID to distinguish a MS02-NV board from + * anything else and a status information providing results of tests + * as well as the size of SRAM available, which can be 1MiB or 2MiB + * (that's what the firmware handles; no idea if 2MiB modules ever + * existed). + * + * The firmware only handles the MS02-NV board if installed in the + * last (15th) slot, so for any other location the status information + * stored in the SRAM cannot be relied upon. But from the hardware + * point of view there is no problem using up to 14 such boards in a + * system -- only the 1st slot needs to be filled with a DRAM module. + * The MS02-NV board is ECC-protected, like other MS02 memory boards. + * + * The state of the battery as provided by the CSR is reflected on + * the two onboard LEDs. When facing the battery side of the board, + * with the LEDs at the top left and the battery at the bottom right + * (i.e. looking from the back side of the system box), their meaning + * is as follows (the system has to be powered on): + * + * left LED battery disable status: lit = enabled + * right LED battery condition status: lit = OK + */ + /* MS02-NV iomem register offsets. */ #define MS02NV_CSR 0x400000 /* control & status register */ +/* MS02-NV CSR status bits. */ +#define MS02NV_CSR_BATT_OK 0x01 /* battery OK */ +#define MS02NV_CSR_BATT_OFF 0x02 /* battery disabled */ + + /* MS02-NV memory offsets. */ #define MS02NV_DIAG 0x0003f8 /* diagnostic status */ #define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */ -#define MS02NV_RAM 0x000400 /* general-purpose RAM start */ +#define MS02NV_VALID 0x000400 /* valid data magic ID */ +#define MS02NV_RAM 0x001000 /* user-exposed RAM start */ -/* MS02-NV diagnostic status constants. */ -#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */ -#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */ +/* MS02-NV diagnostic status bits. */ +#define MS02NV_DIAG_TEST 0x01 /* SRAM test done (?) */ +#define MS02NV_DIAG_RO 0x02 /* SRAM r/o test done */ +#define MS02NV_DIAG_RW 0x04 /* SRAM r/w test done */ +#define MS02NV_DIAG_FAIL 0x08 /* SRAM test failed */ +#define MS02NV_DIAG_SIZE_MASK 0xf0 /* SRAM size mask */ +#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* SRAM size shift (left) */ /* MS02-NV general constants. */ #define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */ +#define MS02NV_VALID_ID 0xbd100248 /* valid data magic ID value */ #define MS02NV_SLOT_SIZE 0x800000 /* size of the address space decoded by the module */ + typedef volatile u32 ms02nv_uint; struct ms02nv_private { diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 14308850a..80d428756 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.24 2003/05/20 21:03:08 dwmw2 Exp $ + * $Id: pmc551.c,v 1.26 2004/07/14 17:25:07 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 4e2d14ce4..b9892acfe 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $ + * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -1093,7 +1093,7 @@ struct mtd_blktrans_ops ftl_tr = { int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $\n"); + DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n"); return register_mtd_blktrans(&ftl_tr); } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 2417fba1b..bc7e2bf56 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -7,7 +7,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse * - * $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $ + * $Id: inftlcore.c,v 1.16 2004/07/12 12:34:58 dwmw2 Exp $ * * 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 @@ -55,9 +55,19 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct INFTLrecord *inftl; unsigned long temp; - if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) + if (mtd->type != MTD_NANDFLASH) + return; + /* OK, this is moderately ugly. But probably safe. Alternatives? */ + if (memcmp(mtd->name, "DiskOnChip", 10)) return; + if (!mtd->block_isbad) { + printk(KERN_ERR +"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" +"Please use the new diskonchip driver under the NAND subsystem.\n"); + return; + } + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); @@ -72,6 +82,8 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) inftl->mbd.devnum = -1; inftl->mbd.blksize = 512; inftl->mbd.tr = tr; + memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); + inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; if (INFTL_mount(inftl) < 0) { printk(KERN_WARNING "INFTL: could not mount device\n"); @@ -284,21 +296,22 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if (BlockMap[block] == BLOCK_NIL) continue; - ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * + ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, - &retlen, movebuf, (char *)&oob, NULL); + &retlen, movebuf); if (ret < 0) { - ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * + ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), - SECTORSIZE, &retlen, movebuf, (char *)&oob, - NULL); + SECTORSIZE, &retlen, movebuf); if (ret != -EIO) DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " "away on retry?\n"); } + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + (block * SECTORSIZE), SECTORSIZE, &retlen, - movebuf, (char *)&oob, NULL); + movebuf, (char *)&oob, &inftl->oobinfo); } /* @@ -326,7 +339,6 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. - * FixMe: Update Bad Unit Table on disk. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { @@ -668,7 +680,6 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) if (INFTL_formatblock(inftl, thisEUN) < 0) { /* * Could not erase : mark block as reserved. - * FixMe: Update Bad Unit Table on medium. */ inftl->PUtable[thisEUN] = BLOCK_RESERVED; } else { @@ -754,7 +765,7 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, unsigned int writeEUN; unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); size_t retlen; - u8 eccbuf[6]; + struct inftl_oob oob; char *p, *pend; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%ld," @@ -778,11 +789,13 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, return 1; } + memset(&oob, 0xff, sizeof(struct inftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + blockofs, SECTORSIZE, &retlen, (char *)buffer, - (char *)eccbuf, NULL); + (char *)&oob, &inftl->oobinfo); /* - * No need to write SECTOR_USED flags since they are written + * need to write SECTOR_USED flags since they are not written * in mtd_writeecc */ } else { @@ -846,9 +859,8 @@ foundit: } else { size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - u_char eccbuf[6]; - if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, - buffer, eccbuf, NULL)) + if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, + buffer)) return -EIO; } return 0; @@ -881,7 +893,7 @@ extern char inftlmountrev[]; int __init init_inftl(void) { - printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, " + printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.16 $, " "inftlmount.c %s\n", inftlmountrev); return register_mtd_blktrans(&inftl_tr); diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 042c4eead..a7075c2a2 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,7 +8,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 Exp $ + * $Id: inftlmount.c,v 1.13 2004/06/28 16:06:36 dbrown Exp $ * * 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 @@ -41,7 +41,7 @@ #include #include -char inftlmountrev[]="$Revision: 1.11 $"; +char inftlmountrev[]="$Revision: 1.13 $"; /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -54,7 +54,7 @@ static int find_boot_record(struct INFTLrecord *inftl) { struct inftl_unittail h1; //struct inftl_oob oob; - unsigned int i, block, boot_record_count = 0; + unsigned int i, block; u8 buf[SECTORSIZE]; struct INFTLMediaHeader *mh = &inftl->MediaHdr; struct INFTLPartition *ip; @@ -72,7 +72,6 @@ static int find_boot_record(struct INFTLrecord *inftl) inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->MediaUnit = BLOCK_NIL; - inftl->SpareMediaUnit = BLOCK_NIL; /* Search for a valid boot record */ for (block = 0; block < inftl->nb_blocks; block++) { @@ -82,8 +81,11 @@ static int find_boot_record(struct INFTLrecord *inftl) * Check for BNAND header first. Then whinge if it's found * but later checks fail. */ - if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, - SECTORSIZE, &retlen, buf))) { + ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, + SECTORSIZE, &retlen, buf); + /* We ignore ret in case the ECC of the MediaHeader is invalid + (which is apparently acceptable) */ + if (retlen != SECTORSIZE) { static int warncount = 5; if (warncount) { @@ -114,36 +116,28 @@ static int find_boot_record(struct INFTLrecord *inftl) continue; } - if (boot_record_count) { - /* - * We've already processed one. So we just check if - * this one is the same as the first one we found. - */ - if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { - printk(KERN_WARNING "INFTL: Media Headers at " - "0x%x and 0x%x disagree.\n", - inftl->MediaUnit * inftl->EraseSize, - block * inftl->EraseSize); - return -1; - } - if (boot_record_count == 1) - inftl->SpareMediaUnit = block; - - /* - * Mark this boot record (INFTL MediaHeader) block as - * reserved. - */ - inftl->PUtable[block] = BLOCK_RESERVED; - - boot_record_count++; - continue; - } /* * This is the first we've seen. * Copy the media header structure into place. */ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); + + /* Read the spare media header at offset 4096 */ + MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, + SECTORSIZE, &retlen, buf); + if (retlen != SECTORSIZE) { + printk(KERN_WARNING "INFTL: Unable to read spare " + "Media Header\n"); + return -1; + } + /* Check if this one is the same as the first one we found. */ + if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { + printk(KERN_WARNING "INFTL: Primary and spare Media " + "Headers disagree.\n"); + return -1; + } + mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); @@ -197,8 +191,9 @@ static int find_boot_record(struct INFTLrecord *inftl) "UnitSizeFactor 0x%02x is experimental\n", mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << - (0xff - mh->BlockMultiplierBits); + mh->BlockMultiplierBits; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + block >>= mh->BlockMultiplierBits; } /* Scan the partitions */ @@ -317,34 +312,23 @@ static int find_boot_record(struct INFTLrecord *inftl) /* Mark this boot record (NFTL MediaHeader) block as reserved */ inftl->PUtable[block] = BLOCK_RESERVED; -#if 0 /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ for (i = 0; i < inftl->nb_blocks; i++) { - if ((i & (SECTORSIZE - 1)) == 0) { - /* read one sector for every SECTORSIZE of blocks */ - if ((ret = MTD_READECC(inftl->mbd.mtd, - block * inftl->EraseSize + i + SECTORSIZE, - SECTORSIZE, &retlen, buf, - (char *)&oob, NULL)) < 0) { - printk(KERN_WARNING "INFTL: read of " - "bad sector table failed " - "(err %d)\n", ret); - kfree(inftl->VUtable); - kfree(inftl->PUtable); - return -1; - } + int physblock; + /* If any of the physical eraseblocks are bad, don't + use the unit. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) { + if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock)) + inftl->PUtable[i] = BLOCK_RESERVED; } - /* Mark the Bad Erase Unit as RESERVED in PUtable */ - if (buf[i & (SECTORSIZE - 1)] != 0xff) - inftl->PUtable[i] = BLOCK_RESERVED; } -#endif inftl->MediaUnit = block; - boot_record_count++; + return 0; } - - return boot_record_count ? 0 : -1; + + /* Not found. */ + return -1; } static int memcmpb(void *a, int c, int n) @@ -365,27 +349,20 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, int len, int check_oob) { int i, retlen; - u8 buf[SECTORSIZE]; + u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize]; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, address, len, check_oob); for (i = 0; i < len; i += SECTORSIZE) { - /* - * We want to read the sector without ECC check here since a - * free sector does not have ECC syndrome on it yet. - */ - if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (MTD_READOOB(inftl->mbd.mtd, address, - inftl->mbd.mtd->oobsize, &retlen, buf) < 0) - return -1; - if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0) + if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0) return -1; } address += SECTORSIZE; @@ -402,52 +379,62 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? - * 2. UnitSizeFactor != 0xFF */ int INFTL_formatblock(struct INFTLrecord *inftl, int block) { int retlen; struct inftl_unittail uci; struct erase_info *instr = &inftl->instr; + int physblock; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," "block=%d)\n", (int)inftl, block); memset(instr, 0, sizeof(struct erase_info)); + /* FIXME: Shouldn't we be setting the 'discarded' flag to zero + _first_? */ + /* Use async erase interface, test return code */ instr->addr = block * inftl->EraseSize; - instr->len = inftl->EraseSize; - MTD_ERASE(inftl->mbd.mtd, instr); + instr->len = inftl->mbd.mtd->erasesize; + /* Erase one physical eraseblock at a time, even though the NAND api + allows us to group them. This way we if we have a failure, we can + mark only the failed block in the bbt. */ + for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { + MTD_ERASE(inftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + printk(KERN_WARNING "INFTL: error while formatting block %d\n", + block); + goto fail; + } - if (instr->state == MTD_ERASE_FAILED) { /* - * Could not format, FixMe: We should update the BadUnitTable - * both in memory and on disk. - */ - printk(KERN_WARNING "INFTL: error while formatting block %d\n", - block); - return -1; + * Check the "freeness" of Erase Unit before updating metadata. + * FixMe: is this check really necessary? Since we have check the + * return code after the erase operation. + */ + if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0) + goto fail; } - /* - * Check the "freeness" of Erase Unit before updating metadata. - * FixMe: is this check really necessary? Since we have check the - * return code after the erase operation. - */ - if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) - return -1; - uci.EraseMark = cpu_to_le16(ERASE_MARK); uci.EraseMark1 = cpu_to_le16(ERASE_MARK); uci.Reserved[0] = 0; uci.Reserved[1] = 0; uci.Reserved[2] = 0; uci.Reserved[3] = 0; - if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + + instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; + if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) - return -1; + goto fail; return 0; +fail: + /* could not format, update the bad block table (caller is responsible + for setting the PUtable to BLOCK_RESERVED on failure) */ + inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr); + return -1; } /* @@ -472,7 +459,6 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) if (INFTL_formatblock(inftl, block) < 0) { /* * Cannot format !!!! Mark it as Bad Unit, - * FixMe: update the BadUnitTable on disk. */ inftl->PUtable[block] = BLOCK_RESERVED; } else { diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 03d39d703..b1ad6567b 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.12 2003/06/23 07:38:11 dwmw2 Exp $ +# $Id: Kconfig,v 1.29 2004/07/15 15:29:17 dwmw2 Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -151,11 +151,11 @@ config MTD_AMD76XROM BE VERY CAREFUL. -config MTD_ICH2ROM - tristate "BIOS flash chip on Intel Hub Controller 2" +config MTD_ICHXROM + tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5" depends on X86 && MTD_JEDECPROBE && MTD_COMPLEX_MAPPINGS help - Support for treating the BIOS flash chip on ICH2 motherboards + Support for treating the BIOS flash chip on ICHX motherboards as an MTD device - with this you can reprogram your BIOS. BE VERY CAREFUL. @@ -177,7 +177,7 @@ config MTD_TSUNAMI config MTD_LASAT tristate "Flash chips on LASAT board" - depends on LASAT && MTD_CFI + depends on LASAT help Support for the flash chips on the Lasat 100 and 200 boards. @@ -210,6 +210,52 @@ config MTD_PB1XXX_USER You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use both banks. +config MTD_PB1550 + tristate "Flash devices on Alchemy PB1550 board" + depends on MIPS && MIPS_PB1550 + help + Flash memory access on Alchemy Pb1550 board + +config MTD_PB1550_BOOT + bool "PB1550 boot flash device" + depends on MTD_PB1550 + help + Use the first of the two 64MiB flash banks on Pb1550 board. + You can say 'Y' to both this and 'MTD_PB1550_USER' below, to use + both banks. + +config MTD_PB1550_USER + bool "PB1550 user flash device" + depends on MTD_PB1550 + default y if MTD_PB1550_BOOT = n + help + Use the second of the two 64MiB flash banks on Pb1550 board. + You can say 'Y' to both this and 'MTD_PB1550_BOOT' above, to use + both banks. + +config MTD_DB1550 + tristate "Flash devices on Alchemy DB1550 board" + depends on MIPS && MIPS_DB1550 + help + Flash memory access on Alchemy Db1550 board + +config MTD_DB1550_BOOT + bool "DB1550 boot flash device" + depends on MTD_DB1550 + help + Use the first of the two 64MiB flash banks on Db1550 board. + You can say 'Y' to both this and 'MTD_DB1550_USER' below, to use + both banks. + +config MTD_DB1550_USER + bool "DB1550 user flash device" + depends on MTD_DB1550 + default y if MTD_DB1550_BOOT = n + help + Use the second of the two 64MiB flash banks on Db1550 board. + You can say 'Y' to both this and 'MTD_DB1550_BOOT' above, to use + both banks. + config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT @@ -235,6 +281,13 @@ config MTD_L440GX BE VERY CAREFUL. +config MTD_SBC8240 + tristate "Flash device on SBC8240" + depends on PPC32 && MTD_JEDECPROBE && 6xx && 8260 + help + Flash access on the SBC8240 board from Wind River. See + + config MTD_TQM8XXL tristate "CFI Flash device mapped on TQM8XXL" depends on MTD_CFI && PPC32 && 8xx && TQM8xxL @@ -265,7 +318,7 @@ config MTD_MBX860 config MTD_DBOX2 tristate "CFI Flash device mapped on D-Box2" - depends on PPC32 && 8xx && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD + depends on PPC32 && 8xx && DBOX2 && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD help This enables access routines for the flash chips on the Nokia/Sagem D-Box 2 board. If you have one of these boards and would like to use @@ -457,6 +510,13 @@ config MTD_CEIVA PhotoMax Digital Picture Frame. If you have such a device, say 'Y'. +config MTD_NOR_TOTO + tristate "NOR Flash device on TOTO board" + depends on ARM && ARCH_OMAP && OMAP_TOTO + help + This enables access to the NOR flash on the Texas Instruments + TOTO board. + config MTD_H720X tristate "Hynix evaluation board mappings" depends on ARM && MTD_CFI && ( ARCH_H7201 || ARCH_H7202 ) @@ -464,6 +524,13 @@ config MTD_H720X This enables access to the flash chips on the Hynix evaluation boards. If you have such a board, say 'Y'. +config MTD_MPC1211 + tristate "CFI Flash device mapped on Interface MPC-1211" + depends on SUPERH && SH_MPC1211 && MTD_CFI + help + This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02). + If you have such a board, say 'Y'. + # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" @@ -491,11 +558,26 @@ config MTD_UCLINUX config MTD_WRSBC8260 tristate "Map driver for WindRiver PowerQUICC II MPC82xx board" - depends on MTD_PARTITIONS && SBC82xx + depends on (SBC82xx || SBC8560) + select MTD_PARTITIONS + select MTD_MAP_BANK_WIDTH_4 + select MTD_MAP_BANK_WIDTH_1 + select MTD_CFI_I1 + select MTD_CFI_I4 help Map driver for WindRiver PowerQUICC II MPC82xx board. Drives all three flash regions on CS0, CS1 and CS6 if they are configured correctly by the boot loader. +config MTD_DMV182 + tristate "Map driver for Dy-4 SVME/DMV-182 board." + depends on DMV182 + select MTD_PARTITIONS + select MTD_MAP_BANK_WIDTH_32 + select MTD_CFI_I8 + select MTD_CFI_AMDSTD + help + Map driver for Dy-4 SVME/DMV-182 board. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 929d66861..505178f36 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.2 2003/05/28 10:48:41 dwmw2 Exp $ +# $Id: Makefile.common,v 1.14 2004/07/12 16:07:31 dwmw2 Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -19,7 +19,7 @@ obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o -obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o +obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o obj-$(CONFIG_MTD_MBX860) += mbx860.o @@ -42,6 +42,9 @@ obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o +obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o +obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o +obj-$(CONFIG_MTD_DB1550) += db1550-flash.o obj-$(CONFIG_MTD_LASAT) += lasat.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o @@ -55,5 +58,9 @@ obj-$(CONFIG_MTD_EBONY) += ebony.o obj-$(CONFIG_MTD_BEECH) += beech-mtd.o obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o +obj-$(CONFIG_MTD_SBC8240) += sbc8240.o +obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o +obj-$(CONFIG_MTD_MPC1211) += mpc1211.o obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o +obj-$(CONFIG_MTD_DMV182) += dmv182.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 39fdeae24..7f3ab3768 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -2,7 +2,7 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.8 2003/05/28 15:44:28 dwmw2 Exp $ + * $Id: amd76xrom.c,v 1.12 2004/07/14 14:44:31 thayne Exp $ */ #include @@ -17,25 +17,60 @@ #include +#define xstr(s) str(s) +#define str(s) #s +#define MOD_NAME xstr(KBUILD_BASENAME) + +#define MTD_DEV_NAME_LENGTH 16 + struct amd76xrom_map_info { struct map_info map; struct mtd_info *mtd; unsigned long window_addr; u32 window_start, window_size; struct pci_dev *pdev; + struct resource window_rsrc; + struct resource rom_rsrc; + char mtd_name[MTD_DEV_NAME_LENGTH]; }; static struct amd76xrom_map_info amd76xrom_map = { .map = { - .name = "AMD76X rom", + .name = MOD_NAME, .size = 0, - .buswidth = 1, - }, - .mtd = NULL, - .window_addr = 0, + .bankwidth = 1, + } + /* remaining fields of structure are initialized to 0 */ }; + +static void amd76xrom_cleanup(struct amd76xrom_map_info *info) +{ + u8 byte; + + /* Disable writes through the rom window */ + pci_read_config_byte(info->pdev, 0x40, &byte); + pci_write_config_byte(info->pdev, 0x40, byte & ~1); + + if (info->mtd) { + del_mtd_device(info->mtd); + map_destroy(info->mtd); + info->mtd = NULL; + info->map.virt = 0; + } + if (info->rom_rsrc.parent) + release_resource(&info->rom_rsrc); + if (info->window_rsrc.parent) + release_resource(&info->window_rsrc); + + if (info->window_addr) { + iounmap((void *)(info->window_addr)); + info->window_addr = 0; + } +} + + static int __devinit amd76xrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -45,6 +80,10 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, u8 segen_bits; }; static struct rom_window rom_window[] = { + /* + * Need the 5MiB window for chips that have block lock/unlock + * registers located below 4MiB window. + */ { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, { 0xffc00000, 4*1024*1024, (1<<7), }, { 0xffff0000, 64*1024, 0 }, @@ -60,80 +99,134 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, int i; u32 rom_size; + info->pdev = pdev; window = &rom_window[0]; - /* disabled because it fights with BIOS reserved regions */ -#define REQUEST_MEM_REGION 0 -#if REQUEST_MEM_REGION - while(window->size) { - if (request_mem_region(window->start, window->size, "amd76xrom")) { - break; + while (window->size) { + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to a fragment of the window being + * "reseved" by the BIOS. In the case that the + * request_mem_region() fails then once the rom size is + * discovered we will try to reserve the unreserved fragment. + */ + info->window_rsrc.name = MOD_NAME; + info->window_rsrc.start = window->start; + info->window_rsrc.end = window->start + window->size - 1; + info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &info->window_rsrc)) { + info->window_rsrc.parent = NULL; + printk(KERN_ERR MOD_NAME + " %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + info->window_rsrc.start, info->window_rsrc.end); } - window++; - } - if (!window->size) { - printk(KERN_ERR "amd76xrom: cannot reserve rom window\n"); - goto err_out_none; - } -#endif /* REQUEST_MEM_REGION */ - /* Enable the selected rom window */ - pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); - /* Enable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte | 1); + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, 0x40, &byte); + pci_write_config_byte(pdev, 0x40, byte | 1); - /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ - printk(KERN_NOTICE "amd76xrom window : %x at %x\n", - window->size, window->start); - /* For write accesses caches are useless */ - info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size); + printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", + window->size, window->start); + /* For write accesses caches are useless */ + info->window_addr = + (unsigned long)ioremap_nocache(window->start, + window->size); - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); - goto err_out_free_mmio_region; - } - info->mtd = NULL; - for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { - char **chip_type; - if (rom_size > window->size) { + if (!info->window_addr) { + printk(KERN_ERR "Failed to ioremap\n"); continue; } - info->map.phys = window->start + window->size - rom_size; - info->map.virt = - info->window_addr + window->size - rom_size; - info->map.size = rom_size; - simple_map_init(&info->map); - chip_type = rom_probe_types; - for(; !info->mtd && *chip_type; chip_type++) { - info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); - } - if (info->mtd) { - break; + + info->mtd = NULL; + + for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { + char **chip_type; + if (rom_size > window->size) { + continue; + } + info->map.phys = window->start + window->size - rom_size; + info->map.virt = + info->window_addr + window->size - rom_size; + info->map.size = rom_size; + simple_map_init(&info->map); + chip_type = rom_probe_types; + for(; !info->mtd && *chip_type; chip_type++) { + info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); + } + if (info->mtd) goto found_mtd; } + iounmap((void *)(info->window_addr)); + info->window_addr = 0; + + /* Disable writes through the rom window */ + pci_read_config_byte(pdev, 0x40, &byte); + pci_write_config_byte(pdev, 0x40, byte & ~1); + + window++; } - if (!info->mtd) { - goto err_out_iounmap; - } - printk(KERN_NOTICE "amd76xrom chip at offset: 0x%x\n", + goto failed; + + found_mtd: + printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n", window->size - rom_size); - + info->mtd->owner = THIS_MODULE; + + if (!info->window_rsrc.parent) { + /* failed to reserve entire window - try fragments */ + info->window_rsrc.name = MOD_NAME; + info->window_rsrc.start = window->start; + info->window_rsrc.end = window->start + window->size - rom_size - 1; + info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &info->window_rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve window resource fragment\n"); +#if 0 + /* + * The BIOS e820 usually reserves this so it isn't + * usually an error. + */ + goto failed; +#endif + } + } + add_mtd_device(info->mtd); info->window_start = window->start; info->window_size = window->size; + + if (info->window_rsrc.parent) { + /* + * Registering the MTD device in iomem may not be possible + * if there is a BIOS "reserved" and BUSY range. If this + * fails then continue anyway. + */ + snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, + "mtd%d", info->mtd->index); + + info->rom_rsrc.name = info->mtd_name; + info->rom_rsrc.start = window->start + window->size - rom_size; + info->rom_rsrc.end = window->start + window->size - 1; + info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { + printk(KERN_ERR MOD_NAME + ": cannot reserve MTD resource\n"); + info->rom_rsrc.parent = NULL; + } + } + return 0; -err_out_iounmap: - iounmap((void *)(info->window_addr)); -err_out_free_mmio_region: -#if REQUEST_MEM_REGION - release_mem_region(window->start, window->size); -err_out_none: -#endif /* REQUEST_MEM_REGION */ + failed: + amd76xrom_cleanup(info); return -ENODEV; } @@ -141,23 +234,8 @@ err_out_none: static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) { struct amd76xrom_map_info *info = &amd76xrom_map; - u8 byte; - - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.virt = 0; - - iounmap((void *)(info->window_addr)); - info->window_addr = 0; - - /* Disable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte & ~1); -#if REQUEST_MEM_REGION - release_mem_region(info->window_start, info->window_size); -#endif /* REQUEST_MEM_REGION */ + amd76xrom_cleanup(info); } static struct pci_device_id amd76xrom_pci_tbl[] = { @@ -173,7 +251,7 @@ MODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl); #if 0 static struct pci_driver amd76xrom_driver = { - .name = "amd76xrom", + .name = MOD_NAME, .id_table = amd76xrom_pci_tbl, .probe = amd76xrom_init_one, .remove = amd76xrom_remove_one, diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c index c111b2d1f..2acf5b0b8 100644 --- a/drivers/mtd/maps/arctic-mtd.c +++ b/drivers/mtd/maps/arctic-mtd.c @@ -1,5 +1,5 @@ /* - * $Id: arctic-mtd.c,v 1.10 2003/06/02 16:37:59 trini Exp $ + * $Id: arctic-mtd.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $ * * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for * IBM 405LP Arctic boards. @@ -72,7 +72,7 @@ static struct map_info arctic_mtd_map = { .name = NAME, .size = SIZE, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, .phys = PADDR, }; diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index f4c27c415..31e9df662 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -2,7 +2,7 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: autcpu12-nvram.c,v 1.6 2004/07/12 21:59:43 dwmw2 Exp $ * * 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 @@ -39,7 +39,7 @@ static struct mtd_info *sram_mtd; struct map_info autcpu12_sram_map = { .name = "SRAM", .size = 32768, - .buswidth = 4, + .bankwidth = 4, .phys = 0x12000000, }; diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c index 61e21970d..d8f737aa7 100644 --- a/drivers/mtd/maps/beech-mtd.c +++ b/drivers/mtd/maps/beech-mtd.c @@ -1,5 +1,5 @@ /* - * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: beech-mtd.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $ * * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for * IBM 405LP Beech boards. @@ -51,7 +51,7 @@ static struct map_info beech_mtd_map = { .name = NAME, .size = SIZE, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, .phys = PADDR }; diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 119968944..e8c984ee1 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -1,7 +1,7 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: cdb89712.c,v 1.8 2004/07/12 21:59:43 dwmw2 Exp $ */ #include @@ -23,7 +23,7 @@ static struct mtd_info *flash_mtd; struct map_info cdb89712_flash_map = { .name = "flash", .size = FLASH_SIZE, - .buswidth = FLASH_WIDTH, + .bankwidth = FLASH_WIDTH, .phys = FLASH_START, }; @@ -93,7 +93,7 @@ static struct mtd_info *sram_mtd; struct map_info cdb89712_sram_map = { .name = "SRAM", .size = SRAM_SIZE, - .buswidth = SRAM_WIDTH, + .bankwidth = SRAM_WIDTH, .phys = SRAM_START, }; @@ -161,7 +161,7 @@ static struct mtd_info *bootrom_mtd; struct map_info cdb89712_bootrom_map = { .name = "BootROM", .size = BOOTROM_SIZE, - .buswidth = BOOTROM_WIDTH, + .bankwidth = BOOTROM_WIDTH, .phys = BOOTROM_START, }; diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 01b867c1b..8475505f0 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -11,7 +11,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: ceiva.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $ */ #include @@ -151,7 +151,7 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info } clps[i].map->virt = (unsigned long)clps[i].vbase; - clps[i].map->buswidth = clps[i].width; + clps[i].map->bankwidth = clps[i].width; clps[i].map->size = clps[i].size; simple_map_init(&clps[i].map); diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index ff08d851f..28d59ff77 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,7 +1,7 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson * - * $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: cfi_flagadm.c,v 1.12 2004/07/12 21:59:43 dwmw2 Exp $ * * 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 @@ -60,7 +60,7 @@ struct map_info flagadm_map = { .name = "FlagaDM flash device", .size = FLASH_SIZE, - .buswidth = 2, + .bankwidth = 2, }; struct mtd_partition flagadm_parts[] = { diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index 38f7e1183..773b1ba9a 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -1,5 +1,5 @@ /* - * $Id: cstm_mips_ixx.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: cstm_mips_ixx.c,v 1.10 2004/07/12 21:59:43 dwmw2 Exp $ * * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. * Config with both CFI and JEDEC device support. @@ -104,7 +104,7 @@ struct cstm_mips_ixx_info { char *name; unsigned long window_addr; unsigned long window_size; - int buswidth; + int bankwidth; int num_partitions; }; @@ -116,7 +116,7 @@ const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = "big flash", // name 0x08000000, // window_addr 0x02000000, // window_size - 4, // buswidth + 4, // bankwidth 1, // num_partitions } @@ -138,7 +138,7 @@ const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = "MTD flash", // name CONFIG_MTD_CSTM_MIPS_IXX_START, // window_addr CONFIG_MTD_CSTM_MIPS_IXX_LEN, // window_size - CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // buswidth + CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH, // bankwidth 1, // num_partitions }, @@ -177,7 +177,7 @@ int __init init_cstm_mips_ixx(void) } cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size; - cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth; + cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth; #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp; #endif diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 6565afb17..7c4de43c0 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -1,5 +1,5 @@ /* - * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: dbox2-flash.c,v 1.11 2004/07/12 21:59:43 dwmw2 Exp $ * * D-Box 2 flash driver */ @@ -13,6 +13,7 @@ #include #include #include +#include /* partition_info gives details on the logical partitions that the split the * single flash device into. If the size if zero we use up to the end of the @@ -25,31 +26,31 @@ static struct mtd_partition partition_info[]= { .mask_flags = MTD_WRITEABLE }, { - .name = "flfs (ppcboot)", + .name = "FLFS (U-Boot)", .size = 128 * 1024, .offset = MTDPART_OFS_APPEND, .mask_flags = 0 }, { - .name = "root (cramfs)", + .name = "Root (SquashFS)", .size = 7040 * 1024, .offset = MTDPART_OFS_APPEND, .mask_flags = 0 }, { - .name = "var (jffs2)", + .name = "var (JFFS2)", .size = 896 * 1024, .offset = MTDPART_OFS_APPEND, .mask_flags = 0 }, { - .name = "flash without bootloader", + .name = "Flash without bootloader", .size = MTDPART_SIZ_FULL, .offset = 128 * 1024, .mask_flags = 0 }, { - .name = "complete flash", + .name = "Complete Flash", .size = MTDPART_SIZ_FULL, .offset = 0, .mask_flags = MTD_WRITEABLE @@ -67,7 +68,7 @@ static struct mtd_info *mymtd; struct map_info dbox2_flash_map = { .name = "D-Box 2 flash memory", .size = WINDOW_SIZE, - .buswidth = 4, + .bankwidth = 4, .phys = WINDOW_ADDR, }; @@ -86,7 +87,7 @@ int __init init_dbox2_flash(void) mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); if (!mymtd) { // Probe for single Intel 28F640 - dbox2_flash_map.buswidth = 2; + dbox2_flash_map.bankwidth = 2; mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); } diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index 8230cc5cd..fc72bb5c5 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -5,13 +5,14 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.15 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: dc21285.c,v 1.20 2004/07/12 22:38:29 dwmw2 Exp $ */ #include #include #include #include #include +#include #include #include @@ -19,91 +20,117 @@ #include #include +#include -static struct mtd_info *mymtd; +static struct mtd_info *dc21285_mtd; -__u8 dc21285_read8(struct map_info *map, unsigned long ofs) +#ifdef CONFIG_ARCH_NETWINDER +/* + * This is really ugly, but it seams to be the only + * realiable way to do it, as the cpld state machine + * is unpredictible. So we have a 25us penalty per + * write access. + */ +static void nw_en_write(void) { + extern spinlock_t gpio_lock; + unsigned long flags; + + /* + * we want to write a bit pattern XXX1 to Xilinx to enable + * the write gate, which will be open for about the next 2ms. + */ + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(1, 1); + spin_unlock_irqrestore(&gpio_lock, flags); + + /* + * let the ISA bus to catch on... + */ + udelay(25); +} +#else +#define nw_en_write() do { } while (0) +#endif + +static map_word dc21285_read8(struct map_info *map, unsigned long ofs) { - return *(__u8*)(map->map_priv_1 + ofs); + return *(uint8_t*)(map->map_priv_1 + ofs); } -__u16 dc21285_read16(struct map_info *map, unsigned long ofs) +static map_word dc21285_read16(struct map_info *map, unsigned long ofs) { - return *(__u16*)(map->map_priv_1 + ofs); + return *(uint16_t*)(map->map_priv_1 + ofs); } -__u32 dc21285_read32(struct map_info *map, unsigned long ofs) +static map_word dc21285_read32(struct map_info *map, unsigned long ofs) { - return *(__u32*)(map->map_priv_1 + ofs); + return *(uint32_t*)(map->map_priv_1 + ofs); } -void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { memcpy(to, (void*)(map->map_priv_1 + from), len); } -void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) +static void dc21285_write(struct map_info *map, map_word d, unsigned long adr) { + if (machine_is_netwinder()) + nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(__u8*)(map->map_priv_1 + adr) = d; + *(uint8_t*)(map->map_priv_1 + adr) = d.x[0]; } -void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) +static void dc21285_write16(struct map_info *map, map_word d, unsigned long adr) { + if (machine_is_netwinder()) + nw_en_write(); *CSR_ROMWRITEREG = adr & 3; adr &= ~3; - *(__u16*)(map->map_priv_1 + adr) = d; + *(uint16_t*)(map->map_priv_1 + adr) = d.x[0]; } -void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) +static void dc21285_write32(struct map_info *map, map_word d, unsigned long adr) { - *(__u32*)(map->map_priv_1 + adr) = d; + if (machine_is_netwinder()) + nw_en_write(); + *(uint32_t*)(map->map_priv_1 + adr) = d.x[0]; } -void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len) { - switch (map->buswidth) { - case 4: - while (len > 0) { - __u32 d = *((__u32*)from)++; - dc21285_write32(map, d, to); - to += 4; - len -= 4; - } - break; - case 2: - while (len > 0) { - __u16 d = *((__u16*)from)++; - dc21285_write16(map, d, to); - to += 2; - len -= 2; - } - break; - case 1: - while (len > 0) { - __u8 d = *((__u8*)from)++; - dc21285_write8(map, d, to); - to++; - len--; - } - break; + while (len > 0) { + uint32_t d = *((uint32_t*)from)++; + dc21285_write32(map, d, to); + to += 4; + len -= 4; + } +} + +static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while (len > 0) { + uint16_t d = *((uint16_t*)from)++; + dc21285_write16(map, d, to); + to += 2; + len -= 2; } } -struct map_info dc21285_map = { +static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + uint8_t d = *((uint8_t*)from)++; + dc21285_write8(map, d, to); + to++; + len--; +} + +static struct map_info dc21285_map = { .name = "DC21285 flash", .phys = NO_XIP, .size = 16*1024*1024, - .read8 = dc21285_read8, - .read16 = dc21285_read16, - .read32 = dc21285_read32, .copy_from = dc21285_copy_from, - .write8 = dc21285_write8, - .write16 = dc21285_write16, - .write32 = dc21285_write32, - .copy_to = dc21285_copy_to }; @@ -113,39 +140,39 @@ static struct mtd_partition *dc21285_parts; static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif -int __init init_dc21285(void) +static int __init init_dc21285(void) { - /* - * Flash timing is determined with bits 19-16 of the - * CSR_SA110_CNTL. The value is the number of wait cycles, or - * 0 for 16 cycles (the default). Cycles are 20 ns. - * Here we use 7 for 140 ns flash chips. - */ - /* access time */ - *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); - /* burst time */ - *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); - /* tristate time */ - *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); - - /* Determine buswidth */ +#ifdef CONFIG_MTD_PARTITIONS + int nrparts; +#endif + + /* Determine bankwidth */ switch (*CSR_SA110_CNTL & (3<<14)) { case SA110_CNTL_ROMWIDTH_8: - dc21285_map.buswidth = 1; + dc21285_map.bankwidth = 1; + dc21285_map.read = dc21285_read8; + dc21285_map.write = dc21285_write8; + dc21285_map.copy_to = dc21285_copy_to_8; break; case SA110_CNTL_ROMWIDTH_16: - dc21285_map.buswidth = 2; + dc21285_map.bankwidth = 2; + dc21285_map.read = dc21285_read16; + dc21285_map.write = dc21285_write16; + dc21285_map.copy_to = dc21285_copy_to_16; break; case SA110_CNTL_ROMWIDTH_32: - dc21285_map.buswidth = 4; + dc21285_map.bankwidth = 4; break; + dc21285_map.read = dc21285_read32; + dc21285_map.write = dc21285_write32; + dc21285_map.copy_to = dc21285_copy_to_32; default: - printk (KERN_ERR "DC21285 flash: undefined buswidth\n"); + printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); return -ENXIO; } - printk (KERN_NOTICE "DC21285 flash support (%d-bit buswidth)\n", - dc21285_map.buswidth*8); + printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n", + dc21285_map.bankwidth*8); /* Let's map the flash area */ dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); @@ -154,40 +181,56 @@ int __init init_dc21285(void) return -EIO; } - mymtd = do_map_probe("cfi_probe", &dc21285_map); - if (mymtd) { - int nrparts = 0; + if (machine_is_ebsa285()) { + dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); + } else { + dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); + } - mymtd->owner = THIS_MODULE; - - /* partition fixup */ + if (!dc21285_mtd) { + iounmap((void *)dc21285_map.map_priv_1); + return -ENXIO; + } + + dc21285_mtd->owner = THIS_MODULE; #ifdef CONFIG_MTD_PARTITIONS - nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0); - if (nrparts > 0) { - add_mtd_partitions(mymtd, dc21285_parts, nrparts); - return 0; - } -#endif - add_mtd_device(mymtd); - return 0; + nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, (void *)0); + if (nrparts > 0) + add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts); + else +#endif + add_mtd_device(dc21285_mtd); + + if(machine_is_ebsa285()) { + /* + * Flash timing is determined with bits 19-16 of the + * CSR_SA110_CNTL. The value is the number of wait cycles, or + * 0 for 16 cycles (the default). Cycles are 20 ns. + * Here we use 7 for 140 ns flash chips. + */ + /* access time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); + /* burst time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); + /* tristate time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); } - - iounmap((void *)dc21285_map.map_priv_1); - return -ENXIO; + + return 0; } static void __exit cleanup_dc21285(void) { #ifdef CONFIG_MTD_PARTITIONS if (dc21285_parts) { - del_mtd_partitions(mymtd); + del_mtd_partitions(dc21285_mtd); kfree(dc21285_parts); } else #endif - del_mtd_device(mymtd); + del_mtd_device(dc21285_mtd); - map_destroy(mymtd); + map_destroy(dc21285_mtd); iounmap((void *)dc21285_map.map_priv_1); } diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index d7521b39c..feb38ba14 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -14,7 +14,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.12 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: dilnetpc.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this @@ -252,7 +252,7 @@ static void adnp_set_vpp(struct map_info *not_used, int on) static struct map_info dnpc_map = { .name = "ADNP Flash Bank", .size = ADNP_WINDOW_SIZE, - .buswidth = 1, + .bankwidth = 1, .set_vpp = adnp_set_vpp, .phys = WINDOW_ADDR }; diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index 00e87e211..7add7b8f2 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -1,5 +1,5 @@ /* - * $Id: ebony.c,v 1.8 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: ebony.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $ * * Mapping for Ebony user flash * @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include static struct mtd_info *flash; @@ -30,13 +30,13 @@ static struct mtd_info *flash; static struct map_info ebony_small_map = { .name = "Ebony small flash", .size = EBONY_SMALL_FLASH_SIZE, - .buswidth = 1, + .bankwidth = 1, }; static struct map_info ebony_large_map = { .name = "Ebony large flash", .size = EBONY_LARGE_FLASH_SIZE, - .buswidth = 1, + .bankwidth = 1, }; static struct mtd_partition ebony_small_partitions[] = { @@ -71,7 +71,7 @@ int __init init_ebony(void) return -ENOMEM; fpga0_reg = readb(fpga0_adr); - iounmap64(fpga0_adr); + iounmap(fpga0_adr); if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && !EBONY_FLASH_SEL(fpga0_reg)) diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 0ecac2071..842f1b046 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -1,5 +1,5 @@ /* - * $Id: edb7312.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: edb7312.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $ * * Handle mapping of the NOR flash on Cogent EDB7312 boards * @@ -28,8 +28,8 @@ #define BUSWIDTH 2 #define FLASH_BLOCKSIZE_MAIN 0x20000 #define FLASH_NUMBLOCKS_MAIN 128 -/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */ -#define PROBETYPES { "cfi_probe", 0 } +/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */ +#define PROBETYPES { "cfi_probe", NULL } #define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ #define MTDID "edb7312-nor" /* for mtdparts= partitioning */ @@ -39,7 +39,7 @@ static struct mtd_info *mymtd; struct map_info edb7312nor_map = { .name = "NOR flash on EDB7312", .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, .phys = WINDOW_ADDR, }; diff --git a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c index 326e9cc66..9410e1fb2 100644 --- a/drivers/mtd/maps/elan-104nc.c +++ b/drivers/mtd/maps/elan-104nc.c @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: elan-104nc.c,v 1.18 2003/06/23 07:37:02 dwmw2 Exp $ + $Id: elan-104nc.c,v 1.21 2004/07/12 22:38:29 dwmw2 Exp $ The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. @@ -107,39 +107,19 @@ static inline void elan_104nc_page(struct map_info *map, unsigned long ofs) } -static __u8 elan_104nc_read8(struct map_info *map, unsigned long ofs) +static map_word elan_104nc_read16(struct map_info *map, unsigned long ofs) { - __u8 ret; + map_word ret; spin_lock(&elan_104nc_spin); elan_104nc_page(map, ofs); - ret = readb(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); - return ret; -} - -static __u16 elan_104nc_read16(struct map_info *map, unsigned long ofs) -{ - __u16 ret; - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, ofs); - ret = readw(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); - return ret; -} - -static __u32 elan_104nc_read32(struct map_info *map, unsigned long ofs) -{ - __u32 ret; - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, ofs); - ret = readl(iomapadr + (ofs & WINDOW_MASK)); + ret.x[0] = readw(iomapadr + (ofs & WINDOW_MASK)); spin_unlock(&elan_104nc_spin); return ret; } static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - while(len) { + while (len) { unsigned long thislen = len; if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) thislen = WINDOW_LENGTH-(from & WINDOW_MASK); @@ -154,27 +134,11 @@ static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long f } } -static void elan_104nc_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, adr); - writeb(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); -} - -static void elan_104nc_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, adr); - writew(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); -} - -static void elan_104nc_write32(struct map_info *map, __u32 d, unsigned long adr) +static void elan_104nc_write16(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&elan_104nc_spin); elan_104nc_page(map, adr); - writel(d, iomapadr + (adr & WINDOW_MASK)); + writew(d.x[0], iomapadr + (adr & WINDOW_MASK)); spin_unlock(&elan_104nc_spin); } @@ -201,14 +165,10 @@ static struct map_info elan_104nc_map = { .size = 8*1024*1024, /* this must be set to a maximum possible amount of flash so the cfi probe routines find all the chips */ - .buswidth = 2, - .read8 = elan_104nc_read8, - .read16 = elan_104nc_read16, - .read32 = elan_104nc_read32, + .bankwidth = 2, + .read = elan_104nc_read16, .copy_from = elan_104nc_copy_from, - .write8 = elan_104nc_write8, - .write16 = elan_104nc_write16, - .write32 = elan_104nc_write32, + .write = elan_104nc_write16, .copy_to = elan_104nc_copy_to }; diff --git a/drivers/mtd/maps/epxa10db-flash.c b/drivers/mtd/maps/epxa10db-flash.c index 5b83d5fd8..545a398c4 100644 --- a/drivers/mtd/maps/epxa10db-flash.c +++ b/drivers/mtd/maps/epxa10db-flash.c @@ -5,7 +5,7 @@ * Copyright (C) 2001 Altera Corporation * Copyright (C) 2001 Red Hat, Inc. * - * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: epxa10db-flash.c,v 1.11 2004/07/12 21:59:44 dwmw2 Exp $ * * 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 @@ -50,7 +50,7 @@ static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition static struct map_info epxa_map = { .name = "EPXA flash", .size = FLASH_SIZE, - .buswidth = 2, + .bankwidth = 2, .phys = FLASH_START, }; diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 0cc00cdd2..50409efda 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -1,6 +1,6 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $ + * $Id: fortunet.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $ */ #include @@ -25,7 +25,7 @@ struct map_region { int window_addr_physical; - int altbuswidth; + int altbankwidth; struct map_info map_info; struct mtd_info *mymtd; struct mtd_partition parts[MAX_NUM_PARTITIONS]; @@ -41,7 +41,7 @@ static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; struct map_info default_map = { .size = DEF_WINDOW_SIZE, - .buswidth = 4, + .bankwidth = 4, }; static char * __init get_string_option(char *dest,int dest_size,char *sor) @@ -102,7 +102,7 @@ static int __init MTD_New_Region(char *line) if(params[0]<1) { printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " - " name,region-number[,base,size,buswidth,altbuswidth]\n"); + " name,region-number[,base,size,bankwidth,altbankwidth]\n"); return 1; } if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) @@ -116,7 +116,7 @@ static int __init MTD_New_Region(char *line) &default_map,sizeof(map_regions[params[1]].map_info)); map_regions_set[params[1]] = 1; map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; - map_regions[params[1]].altbuswidth = 2; + map_regions[params[1]].altbankwidth = 2; map_regions[params[1]].mymtd = NULL; map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; strcpy(map_regions[params[1]].map_info.name,string); @@ -130,11 +130,11 @@ static int __init MTD_New_Region(char *line) } if(params[0]>3) { - map_regions[params[1]].map_info.buswidth = params[4]; + map_regions[params[1]].map_info.bankwidth = params[4]; } if(params[0]>4) { - map_regions[params[1]].altbuswidth = params[5]; + map_regions[params[1]].altbankwidth = params[5]; } return 1; } @@ -193,7 +193,7 @@ int __init init_fortunet(void) sizeof(map_regions[ix].map_info)); map_regions_set[ix] = 1; map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; - map_regions[ix].altbuswidth = 2; + map_regions[ix].altbankwidth = 2; map_regions[ix].mymtd = NULL; map_regions[ix].map_info.name = map_regions[ix].map_name; strcpy(map_regions[ix].map_info.name,"FORTUNET"); @@ -227,13 +227,13 @@ int __init init_fortunet(void) map_regions[ix].mymtd = do_map_probe("cfi_probe", &map_regions[ix].map_info); if((!map_regions[ix].mymtd)&&( - map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth)) + map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth)) { - printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth " + printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth " "for %s flash.\n", map_regions[ix].map_info.name); - map_regions[ix].map_info.buswidth = - map_regions[ix].altbuswidth; + map_regions[ix].map_info.bankwidth = + map_regions[ix].altbankwidth; map_regions[ix].mymtd = do_map_probe("cfi_probe", &map_regions[ix].map_info); } diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 3a0c58db2..e7cd7b022 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -2,9 +2,11 @@ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based * evaluation boards * + * $Id: h720x-flash.c,v 1.9 2004/07/14 17:45:40 dwmw2 Exp $ + * * (C) 2002 Jungjun Kim * 2003 Thomas Gleixner -*/ + */ #include #include @@ -24,7 +26,7 @@ static struct mtd_info *mymtd; static struct map_info h720x_map = { .name = "H720X", - .buswidth = 4, + .bankwidth = 4, .size = FLASH_SIZE, .phys = FLASH_PHYS, }; @@ -80,13 +82,13 @@ int __init h720x_mtd_init(void) simple_map_init(&h720x_map); - // Probe for flash buswidth 4 + // Probe for flash bankwidth 4 printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); mymtd = do_map_probe("cfi_probe", &h720x_map); if (!mymtd) { printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); - // Probe for buswidth 2 - h720x_map.buswidth = 2; + // Probe for bankwidth 2 + h720x_map.bankwidth = 2; mymtd = do_map_probe("cfi_probe", &h720x_map); } diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index fdf0dca3f..a05fc01b9 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -1,5 +1,5 @@ /* - * $Id: impa7.c,v 1.9 2003/06/23 11:47:43 dwmw2 Exp $ + * $Id: impa7.c,v 1.11 2004/07/14 09:52:55 dwmw2 Exp $ * * Handle mapping of the NOR flash on implementa A7 boards * @@ -30,25 +30,25 @@ #define NUM_FLASHBANKS 2 #define BUSWIDTH 4 -/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */ -#define PROBETYPES { "jedec_probe", 0 } +/* can be { "cfi_probe", "jedec_probe", "map_rom", NULL } */ +#define PROBETYPES { "jedec_probe", NULL } #define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ #define MTDID "impa7-%d" /* for mtdparts= partitioning */ -static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; +static struct mtd_info *impa7_mtd[NUM_FLASHBANKS]; static struct map_info impa7_map[NUM_FLASHBANKS] = { { .name = "impA7 NOR Flash Bank #0", .size = WINDOW_SIZE0, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, }, { .name = "impA7 NOR Flash Bank #1", .size = WINDOW_SIZE1, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, }, }; diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index b05dda1b3..1f23ab1cd 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -22,7 +22,7 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.15 2004/02/27 22:37:39 rmk Exp $ + $Id: integrator-flash.c,v 1.16 2004/07/12 21:59:44 dwmw2 Exp $ ======================================================================*/ @@ -108,7 +108,7 @@ static int armflash_probe(struct device *_dev) * look for CFI based flash parts fitted to this board */ info->map.size = size; - info->map.buswidth = plat->width; + info->map.bankwidth = plat->width; info->map.phys = res->start; info->map.virt = (unsigned long) base; info->map.name = dev->dev.bus_id; diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c index f0b7b4dab..17d7c7759 100644 --- a/drivers/mtd/maps/iq80310.c +++ b/drivers/mtd/maps/iq80310.c @@ -1,5 +1,5 @@ /* - * $Id: iq80310.c,v 1.17 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: iq80310.c,v 1.18 2004/07/12 21:59:44 dwmw2 Exp $ * * Mapping for the Intel XScale IQ80310 evaluation board * @@ -31,7 +31,7 @@ static struct mtd_info *mymtd; static struct map_info iq80310_map = { .name = "IQ80310 flash", .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, .phys = WINDOW_ADDR }; diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index a10f92126..7ebc8cc5b 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -1,5 +1,5 @@ /* - * $Id: ixp4xx.c,v 1.1 2004/05/13 22:21:26 dsaxena Exp $ + * $Id: ixp4xx.c,v 1.3 2004/07/12 22:38:29 dwmw2 Exp $ * * drivers/mtd/maps/ixp4xx.c * @@ -39,10 +39,11 @@ #define BYTE1(h) ((h) & 0xFF) #endif -static __u16 -ixp4xx_read16(struct map_info *map, unsigned long ofs) +static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs) { - return *(__u16 *) (map->map_priv_1 + ofs); + map_word val; + val.x[0] = *(__u16 *) (map->map_priv_1 + ofs); + return val; } /* @@ -50,9 +51,8 @@ ixp4xx_read16(struct map_info *map, unsigned long ofs) * when attached to a 16-bit wide device (such as the 28F128J3A), * so we can't just memcpy_fromio(). */ -static void -ixp4xx_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) +static void ixp4xx_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) { int i; u8 *dest = (u8 *) to; @@ -69,10 +69,9 @@ ixp4xx_copy_from(struct map_info *map, void *to, dest[len - 1] = BYTE0(src[i]); } -static void -ixp4xx_write16(struct map_info *map, __u16 d, unsigned long adr) +static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) { - *(__u16 *) (map->map_priv_1 + adr) = d; + *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; } struct ixp4xx_flash_info { @@ -84,8 +83,7 @@ struct ixp4xx_flash_info { static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; -static int -ixp4xx_flash_remove(struct device *_dev) +static int ixp4xx_flash_remove(struct device *_dev) { struct platform_device *dev = to_platform_device(_dev); struct flash_platform_data *plat = dev->dev.platform_data; @@ -168,10 +166,10 @@ static int ixp4xx_flash_probe(struct device *_dev) * any board use 8-bit access, we'll fixup the driver to * handle that. */ - info->map.buswidth = 2; + info->map.bankwidth = 2; info->map.name = dev->dev.bus_id; - info->map.read16 = ixp4xx_read16, - info->map.write16 = ixp4xx_write16, + info->map.read = ixp4xx_read16, + info->map.write = ixp4xx_write16, info->map.copy_from = ixp4xx_copy_from, info->res = request_mem_region(dev->resource->start, diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 2e410ef55..046f7efbd 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,5 +1,5 @@ /* - * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: l440gx.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. * @@ -46,7 +46,7 @@ void l440gx_set_vpp(struct map_info *map, int vpp) struct map_info l440gx_map = { .name = "L440GX BIOS", .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, + .bankwidth = BUSWIDTH, .phys = WINDOW_ADDR, #if 0 /* FIXME verify that this is the diff --git a/drivers/mtd/maps/lasat.c b/drivers/mtd/maps/lasat.c index 36a14d6a4..2a2efaa42 100644 --- a/drivers/mtd/maps/lasat.c +++ b/drivers/mtd/maps/lasat.c @@ -1,14 +1,13 @@ /* - * Flash device on lasat 100 and 200 boards + * Flash device on Lasat 100 and 200 boards * - * Presumably (C) 2002 Brian Murphy or whoever he - * works for. + * (C) 2002 Brian Murphy * * 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. * - * $Id: lasat.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: lasat.c,v 1.7 2004/07/12 21:59:44 dwmw2 Exp $ * */ @@ -22,44 +21,53 @@ #include #include #include -#include -static struct mtd_info *mymtd; - -static struct map_info sp_map = { - .name = "SP flash", - .buswidth = 4, -}; +static struct mtd_info *lasat_mtd; static struct mtd_partition partition_info[LASAT_MTD_LAST]; static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; -static int __init init_sp(void) +static void lasat_set_vpp(struct map_info *map, int vpp) { - int i; - /* this does not play well with the old flash code which - * protects and uprotects the flash when necessary */ - /* FIXME: Implement set_vpp() */ - printk(KERN_NOTICE "Unprotecting flash\n"); - *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; + if (vpp) + *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; + else + *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit); +} + +static struct map_info lasat_map = { + .name = "LASAT flash", + .bankwidth = 4, + .set_vpp = lasat_set_vpp +}; - sp_map.virt = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); - sp_map.phys = virt_to_phys(sp_map.virt); - sp_map.size = lasat_board_info.li_flash_size; +static int __init init_lasat(void) +{ + int i; + /* since we use AMD chips and set_vpp is not implimented + * for these (yet) we still have to permanently enable flash write */ + printk(KERN_NOTICE "Unprotecting flash\n"); + ENABLE_VPP((&lasat_map)); - simple_map_init(&sp_map); + lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); + lasat_map.virt = (unsigned long)ioremap_nocache( + lasat_map.phys, lasat_board_info.li_flash_size); + lasat_map.size = lasat_board_info.li_flash_size; - printk(KERN_NOTICE "sp flash device: %lx at %lx\n", - sp_map.size, sp_map.phys); + simple_map_init(&lasat_map); for (i=0; i < LASAT_MTD_LAST; i++) partition_info[i].name = lasat_mtd_partnames[i]; - mymtd = do_map_probe("cfi_probe", &sp_map); - if (mymtd) { + lasat_mtd = do_map_probe("cfi_probe", &lasat_map); + + if (!lasat_mtd) + lasat_mtd = do_map_probe("jedec_probe", &lasat_map); + + if (lasat_mtd) { u32 size, offset = 0; - mymtd->owner = THIS_MODULE; + lasat_mtd->owner = THIS_MODULE; for (i=0; i < LASAT_MTD_LAST; i++) { size = lasat_flash_partition_size(i); @@ -68,26 +76,26 @@ static int __init init_sp(void) offset += size; } - add_mtd_partitions( mymtd, partition_info, LASAT_MTD_LAST ); + add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST ); return 0; } return -ENXIO; } -static void __exit cleanup_sp(void) +static void __exit cleanup_lasat(void) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); + if (lasat_mtd) { + del_mtd_partitions(lasat_mtd); + map_destroy(lasat_mtd); } - if (sp_map.virt) { - sp_map.virt = 0; + if (lasat_map.virt) { + lasat_map.virt = 0; } } -module_init(init_sp); -module_exit(cleanup_sp); +module_init(init_lasat); +module_exit(cleanup_lasat); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Brian Murphy "); diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c index 5f7bdd883..53e708b2e 100644 --- a/drivers/mtd/maps/lubbock-flash.c +++ b/drivers/mtd/maps/lubbock-flash.c @@ -1,5 +1,5 @@ /* - * $Id: lubbock-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: lubbock-flash.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $ * * Map driver for the Lubbock developer platform. * @@ -28,12 +28,19 @@ #define WINDOW_SIZE 64*1024*1024 +static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) +{ + consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); +} + static struct map_info lubbock_maps[2] = { { .size = WINDOW_SIZE, .phys = 0x00000000, + .inval_cache = lubbock_map_inval_cache, }, { .size = WINDOW_SIZE, .phys = 0x04000000, + .inval_cache = lubbock_map_inval_cache, } }; static struct mtd_partition lubbock_partitions[] = { @@ -64,7 +71,7 @@ static int __init init_lubbock(void) int flashboot = (LUB_CONF_SWITCHES & 1); int ret = 0, i; - lubbock_maps[0].buswidth = lubbock_maps[1].buswidth = + lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth = (BOOT_DEF & 1) ? 2 : 4; /* Compensate for the nROMBT switch which swaps the flash banks */ @@ -82,16 +89,23 @@ static int __init init_lubbock(void) ret = -ENOMEM; continue; } + lubbock_maps[i].cached = __ioremap(lubbock_maps[i].phys, + WINDOW_SIZE, + L_PTE_CACHEABLE, 1); + if (!lubbock_maps[i].cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); simple_map_init(&lubbock_maps[i]); - printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n", + printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", lubbock_maps[i].name, lubbock_maps[i].phys, - lubbock_maps[i].buswidth * 8); + lubbock_maps[i].bankwidth * 8); mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); if (!mymtds[i]) { iounmap((void *)lubbock_maps[i].virt); + if (lubbock_maps[i].cached) + iounmap(lubbock_maps[i].cached); if (!ret) ret = -EIO; continue; @@ -138,6 +152,8 @@ static void __exit cleanup_lubbock(void) map_destroy(mymtds[i]); iounmap((void *)lubbock_maps[i].virt); + if (lubbock_maps[i].cached) + iounmap(lubbock_maps[i].cached); if (parsed_parts[i]) kfree(parsed_parts[i]); diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 4bb4af6c8..38f6a7af5 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,5 +1,5 @@ /* - * $Id: map_funcs.c,v 1.2 2003/05/21 15:15:07 dwmw2 Exp $ + * $Id: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 Exp $ * * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS * is enabled. @@ -7,87 +7,35 @@ #include #include -#include -#include -#include -#include #include -#include -static u8 simple_map_read8(struct map_info *map, unsigned long ofs) +static map_word simple_map_read(struct map_info *map, unsigned long ofs) { - return __raw_readb(map->virt + ofs); + return inline_map_read(map, ofs); } -static u16 simple_map_read16(struct map_info *map, unsigned long ofs) +static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs) { - return __raw_readw(map->virt + ofs); -} - -static u32 simple_map_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->virt + ofs); -} - -static u64 simple_map_read64(struct map_info *map, unsigned long ofs) -{ -#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ - BUG(); - return 0; -#else - return __raw_readll(map->virt + ofs); -#endif -} - -static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs) -{ - __raw_writeb(datum, map->virt + ofs); - mb(); -} - -static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs) -{ - __raw_writew(datum, map->virt + ofs); - mb(); -} - -static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs) -{ - __raw_writel(datum, map->virt + ofs); - mb(); -} - -static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs) -{ -#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ - BUG(); -#else - __raw_writell(datum, map->virt + ofs); - mb(); -#endif /* CFI_B8 */ + inline_map_write(map, datum, ofs); } static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - memcpy_fromio(to, map->virt + from, len); + inline_map_copy_from(map, to, from, len); } static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { - memcpy_toio(map->virt + to, from, len); + inline_map_copy_to(map, to, from, len); } void simple_map_init(struct map_info *map) { - map->read8 = simple_map_read8; - map->read16 = simple_map_read16; - map->read32 = simple_map_read32; - map->read64 = simple_map_read64; - map->write8 = simple_map_write8; - map->write16 = simple_map_write16; - map->write32 = simple_map_write32; - map->write64 = simple_map_write64; + BUG_ON(!map_bankwidth_supported(map->bankwidth)); + + map->read = simple_map_read; + map->write = simple_map_write; map->copy_from = simple_map_copy_from; map->copy_to = simple_map_copy_to; } diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 5adbec880..4c0f9e967 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -1,5 +1,5 @@ /* - * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: mbx860.c,v 1.6 2004/07/12 21:59:44 dwmw2 Exp $ * * Handle mapping of the flash on MBX860 boards * @@ -54,7 +54,7 @@ struct map_info mbx_map = { .name = "MBX flash", .size = WINDOW_SIZE, .phys = WINDOW_ADDR, - .buswidth = 4, + .bankwidth = 4, }; int __init init_mbx(void) diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index caaac71cc..afdf6589b 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: netsc520.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $ * * 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 @@ -84,7 +84,7 @@ static struct mtd_partition partition_info[]={ static struct map_info netsc520_map = { .name = "netsc520 Flash Bank", .size = WINDOW_SIZE, - .buswidth = 4, + .bankwidth = 4, .phys = WINDOW_ADDR, }; diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 53a9934fb..e06b07351 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -6,7 +6,7 @@ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) * - * $Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $ + * $Id: nettel.c,v 1.5 2004/07/12 21:59:44 dwmw2 Exp $ */ /****************************************************************************/ @@ -65,7 +65,7 @@ static struct mtd_info *amd_mtd; static struct map_info nettel_intel_map = { .name = "SnapGear Intel", .size = 0, - .buswidth = INTEL_BUSWIDTH, + .bankwidth = INTEL_BUSWIDTH, }; static struct mtd_partition nettel_intel_partitions[] = { @@ -103,7 +103,7 @@ static struct mtd_partition nettel_intel_partitions[] = { static struct map_info nettel_amd_map = { .name = "SnapGear AMD", .size = AMD_WINDOW_MAXSIZE, - .buswidth = AMD_BUSWIDTH, + .bankwidth = AMD_BUSWIDTH, }; static struct mtd_partition nettel_amd_partitions[] = { diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c index 201f67a40..40c524d62 100644 --- a/drivers/mtd/maps/ocelot.c +++ b/drivers/mtd/maps/ocelot.c @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: ocelot.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -49,14 +49,14 @@ static struct mtd_partition *parsed_parts; struct map_info ocelot_flash_map = { .name = "Ocelot boot flash", .size = FLASH_WINDOW_SIZE, - .buswidth = FLASH_BUSWIDTH, + .bankwidth = FLASH_BUSWIDTH, .phys = FLASH_WINDOW_ADDR, }; struct map_info ocelot_nvram_map = { .name = "Ocelot NVRAM", .size = NVRAM_WINDOW_SIZE, - .buswidth = NVRAM_BUSWIDTH, + .bankwidth = NVRAM_BUSWIDTH, .phys = NVRAM_WINDOW_ADDR, }; diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index 176890b9b..10bd8901e 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.26 2004/07/12 22:38:29 dwmw2 Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -62,32 +62,12 @@ static inline void oct5066_page(struct map_info *map, unsigned long ofs) } -static __u8 oct5066_read8(struct map_info *map, unsigned long ofs) +static map_word oct5066_read8(struct map_info *map, unsigned long ofs) { - __u8 ret; + map_word ret; spin_lock(&oct5066_spin); oct5066_page(map, ofs); - ret = readb(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&oct5066_spin); - return ret; -} - -static __u16 oct5066_read16(struct map_info *map, unsigned long ofs) -{ - __u16 ret; - spin_lock(&oct5066_spin); - oct5066_page(map, ofs); - ret = readw(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&oct5066_spin); - return ret; -} - -static __u32 oct5066_read32(struct map_info *map, unsigned long ofs) -{ - __u32 ret; - spin_lock(&oct5066_spin); - oct5066_page(map, ofs); - ret = readl(iomapadr + (ofs & WINDOW_MASK)); + ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); spin_unlock(&oct5066_spin); return ret; } @@ -109,27 +89,11 @@ static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from } } -static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - spin_lock(&oct5066_spin); - oct5066_page(map, adr); - writeb(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&oct5066_spin); -} - -static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - spin_lock(&oct5066_spin); - oct5066_page(map, adr); - writew(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&oct5066_spin); -} - -static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr) +static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&oct5066_spin); oct5066_page(map, adr); - writel(d, iomapadr + (adr & WINDOW_MASK)); + writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); spin_unlock(&oct5066_spin); } @@ -155,14 +119,10 @@ static struct map_info oct5066_map[2] = { .name = "Octagon 5066 Socket", .phys = NO_XIP, .size = 512 * 1024, - .buswidth = 1, - .read8 = oct5066_read8, - .read16 = oct5066_read16, - .read32 = oct5066_read32, + .bankwidth = 1, + .read = oct5066_read8, .copy_from = oct5066_copy_from, - .write8 = oct5066_write8, - .write16 = oct5066_write16, - .write32 = oct5066_write32, + .write = oct5066_write8, .copy_to = oct5066_copy_to, .map_priv_1 = 1<<6 }, @@ -170,14 +130,10 @@ static struct map_info oct5066_map[2] = { .name = "Octagon 5066 Internal Flash", .phys = NO_XIP, .size = 2 * 1024 * 1024, - .buswidth = 1, - .read8 = oct5066_read8, - .read16 = oct5066_read16, - .read32 = oct5066_read32, + .bankwidth = 1, + .read = oct5066_read8, .copy_from = oct5066_copy_from, - .write8 = oct5066_write8, - .write16 = oct5066_write16, - .write32 = oct5066_write32, + .write = oct5066_write8, .copy_to = oct5066_copy_to, .map_priv_1 = 2<<6 } diff --git a/drivers/mtd/maps/pb1xxx-flash.c b/drivers/mtd/maps/pb1xxx-flash.c index 713c52b0e..b6b8ccf48 100644 --- a/drivers/mtd/maps/pb1xxx-flash.c +++ b/drivers/mtd/maps/pb1xxx-flash.c @@ -3,14 +3,14 @@ * * (C) 2001 Pete Popov * - * $Id: pb1xxx-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: pb1xxx-flash.c,v 1.11 2004/07/12 21:59:44 dwmw2 Exp $ */ #include #include #include -#include #include +#include #include #include @@ -26,102 +26,87 @@ #endif #ifdef CONFIG_MIPS_PB1000 + #define WINDOW_ADDR 0x1F800000 #define WINDOW_SIZE 0x800000 -#endif - - -static struct map_info pb1xxx_map = { - .name = "Pb1xxx flash", -}; - - -#ifdef CONFIG_MIPS_PB1000 -static unsigned long flash_size = 0x00800000; -static unsigned char flash_buswidth = 4; static struct mtd_partition pb1xxx_partitions[] = { { - .name = "yamon env", - .size = 0x00020000, - .offset = 0, - .mask_flags = MTD_WRITEABLE - },{ - .name = "User FS", - .size = 0x003e0000, - .offset = 0x20000, - },{ - .name = "boot code", - .size = 0x100000, - .offset = 0x400000, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw/kernel", - .size = 0x300000, - .offset = 0x500000 - } + .name = "yamon env", + .size = 0x00020000, + .offset = 0, + .mask_flags = MTD_WRITEABLE}, + { + .name = "User FS", + .size = 0x003e0000, + .offset = 0x20000,}, + { + .name = "boot code", + .size = 0x100000, + .offset = 0x400000, + .mask_flags = MTD_WRITEABLE}, + { + .name = "raw/kernel", + .size = 0x300000, + .offset = 0x500000} }; #elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) -static unsigned char flash_buswidth = 4; #if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) -/* both 32MiB banks will be used. Combine the first 32MiB bank and the - * first 28MiB of the second bank together into a single jffs/jffs2 +/* both 32MB banks will be used. Combine the first 32MB bank and the + * first 28MB of the second bank together into a single jffs/jffs2 * partition. */ -static unsigned long flash_size = 0x04000000; #define WINDOW_ADDR 0x1C000000 #define WINDOW_SIZE 0x4000000 static struct mtd_partition pb1xxx_partitions[] = { { - .name = "User FS", - .size = 0x3c00000, - .offset = 0x0000000 + .name = "User FS", + .size = 0x3c00000, + .offset = 0x0000000 },{ - .name = "yamon", - .size = 0x0100000, - .offset = 0x3c00000, - .mask_flags = MTD_WRITEABLE + .name = "yamon", + .size = 0x0100000, + .offset = 0x3c00000, + .mask_flags = MTD_WRITEABLE },{ - .name = "raw kernel", - .size = 0x02c0000, - .offset = 0x3d00000 + .name = "raw kernel", + .size = 0x02c0000, + .offset = 0x3d00000 } }; #elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER) -static unsigned long flash_size = 0x02000000; #define WINDOW_ADDR 0x1E000000 #define WINDOW_SIZE 0x2000000 static struct mtd_partition pb1xxx_partitions[] = { { - .name = "User FS", - .size = 0x1c00000, - .offset = 0x0000000 + .name = "User FS", + .size = 0x1c00000, + .offset = 0x0000000 },{ - .name = "yamon", - .size = 0x0100000, - .offset = 0x1c00000, - .mask_flags = MTD_WRITEABLE + .name = "yamon", + .size = 0x0100000, + .offset = 0x1c00000, + .mask_flags = MTD_WRITEABLE },{ - .name = "raw kernel", - .size = 0x02c0000, - .offset = 0x1d00000 + .name = "raw kernel", + .size = 0x02c0000, + .offset = 0x1d00000 } }; #elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) -static unsigned long flash_size = 0x02000000; #define WINDOW_ADDR 0x1C000000 #define WINDOW_SIZE 0x2000000 static struct mtd_partition pb1xxx_partitions[] = { { - .name = "User FS", - .size = 0x1e00000, - .offset = 0x0000000 + .name = "User FS", + .size = 0x1e00000, + .offset = 0x0000000 },{ - .name = "raw kernel", - .size = 0x0200000, - .offset = 0x1e00000, + .name = "raw kernel", + .size = 0x0200000, + .offset = 0x1e00000, } }; #else @@ -131,8 +116,20 @@ static struct mtd_partition pb1xxx_partitions[] = { #error Unsupported board #endif -static struct mtd_partition *parsed_parts; -static struct mtd_info *mymtd; +#define NAME "Pb1x00 Linux Flash" +#define PADDR WINDOW_ADDR +#define BUSWIDTH 4 +#define SIZE WINDOW_SIZE +#define PARTITIONS 4 + +static struct map_info pb1xxx_mtd_map = { + .name = NAME, + .size = SIZE, + .bankwidth = BUSWIDTH, + .phys = PADDR, +}; + +static struct mtd_info *pb1xxx_mtd; int __init pb1xxx_mtd_init(void) { @@ -140,49 +137,38 @@ int __init pb1xxx_mtd_init(void) int nb_parts = 0; char *part_type; - /* Default flash buswidth */ - pb1xxx_map.buswidth = flash_buswidth; - /* * Static partition definition selection */ part_type = "static"; parts = pb1xxx_partitions; nb_parts = ARRAY_SIZE(pb1xxx_partitions); - pb1xxx_map.size = flash_size; /* * Now let's probe for the actual flash. Do it here since * specific machine settings might have been set above. */ printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", - pb1xxx_map.buswidth*8); - pb1xxx_map.phys = WINDOW_ADDR; - pb1xxx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + BUSWIDTH*8); + pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); - simple_map_init(&pb1xxx_map); + simple_map_init(&pb1xxx_mtd_map); - mymtd = do_map_probe("cfi_probe", &pb1xxx_map); - if (!mymtd) { - iounmap(pb1xxx_map.virt); - return -ENXIO; - } - mymtd->owner = THIS_MODULE; + pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map); + if (!pb1xxx_mtd) return -ENXIO; + pb1xxx_mtd->owner = THIS_MODULE; - add_mtd_partitions(mymtd, parts, nb_parts); + add_mtd_partitions(pb1xxx_mtd, parts, nb_parts); return 0; } static void __exit pb1xxx_mtd_cleanup(void) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - if (parsed_parts) - kfree(parsed_parts); + if (pb1xxx_mtd) { + del_mtd_partitions(pb1xxx_mtd); + map_destroy(pb1xxx_mtd); + iounmap((void *) pb1xxx_mtd_map.virt); } - if (pb1xxx_map.virt) - iounmap(pb1xxx_map.virt); } module_init(pb1xxx_mtd_init); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 8baca24cb..8b5d1ff45 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.5 2003/05/20 20:59:31 dwmw2 Exp $ + * $Id: pci.c,v 1.8 2004/07/12 22:38:29 dwmw2 Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. @@ -39,6 +39,74 @@ struct map_pci_info { struct pci_dev *dev; }; +static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0]= readb(map->base + map->translate(map, ofs)); +// printk("read8 : %08lx => %02x\n", ofs, val.x[0]); + return val; +} + +#if 0 +static map_word mtd_pci_read16(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0] = readw(map->base + map->translate(map, ofs)); +// printk("read16: %08lx => %04x\n", ofs, val.x[0]); + return val; +} +#endif +static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + map_word val; + val.x[0] = readl(map->base + map->translate(map, ofs)); +// printk("read32: %08lx => %08x\n", ofs, val.x[0]); + return val; +} + +static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_fromio(to, map->base + map->translate(map, from), len); +} + +static void mtd_pci_write8(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write8 : %08lx <= %02x\n", ofs, val.x[0]); + writeb(val.x[0], map->base + map->translate(map, ofs)); +} + +#if 0 +static void mtd_pci_write16(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write16: %08lx <= %04x\n", ofs, val.x[0]); + writew(val.x[0], map->base + map->translate(map, ofs)); +} +#endif +static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write32: %08lx <= %08x\n", ofs, val.x[0]); + writel(val.x[0], map->base + map->translate(map, ofs)); +} + +static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_toio(map->base + map->translate(map, to), from, len); +} + +static struct map_info mtd_pci_map = { + .phys = NO_XIP, + .copy_from = mtd_pci_copyfrom, + .copy_to = mtd_pci_copyto, +}; + /* * Intel IOP80310 Flash driver */ @@ -48,7 +116,10 @@ intel_iq80310_init(struct pci_dev *dev, struct map_pci_info *map) { u32 win_base; - map->map.buswidth = 1; + map->map.bankwidth = 1; + map->map.read = mtd_pci_read8, + map->map.write = mtd_pci_write8, + map->map.size = 0x00800000; map->base = ioremap_nocache(pci_resource_start(dev, 0), pci_resource_len(dev, 0)); @@ -147,7 +218,9 @@ intel_dc21285_init(struct pci_dev *dev, struct map_pci_info *map) if (!len || !base) return -ENXIO; - map->map.buswidth = 4; + map->map.bankwidth = 4; + map->map.read = mtd_pci_read32, + map->map.write = mtd_pci_write32, map->map.size = len; map->base = ioremap_nocache(base, len); @@ -215,75 +288,6 @@ static struct pci_device_id mtd_pci_ids[] = { * Generic code follows. */ -static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u8 val = readb(map->base + map->translate(map, ofs)); -// printk("read8 : %08lx => %02x\n", ofs, val); - return val; -} - -static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u16 val = readw(map->base + map->translate(map, ofs)); -// printk("read16: %08lx => %04x\n", ofs, val); - return val; -} - -static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - u32 val = readl(map->base + map->translate(map, ofs)); -// printk("read32: %08lx => %08x\n", ofs, val); - return val; -} - -static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - memcpy_fromio(to, map->base + map->translate(map, from), len); -} - -static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write8 : %08lx <= %02x\n", ofs, val); - writeb(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write16: %08lx <= %04x\n", ofs, val); - writew(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; -// printk("write32: %08lx <= %08x\n", ofs, val); - writel(val, map->base + map->translate(map, ofs)); -} - -static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) -{ - struct map_pci_info *map = (struct map_pci_info *)_map; - memcpy_toio(map->base + map->translate(map, to), from, len); -} - -static struct map_info mtd_pci_map = { - .phys = NO_XIP, - .read8 = mtd_pci_read8, - .read16 = mtd_pci_read16, - .read32 = mtd_pci_read32, - .copy_from = mtd_pci_copyfrom, - .write8 = mtd_pci_write8, - .write16 = mtd_pci_write16, - .write32 = mtd_pci_write32, - .copy_to = mtd_pci_copyto, -}; - static int __devinit mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 4453a2707..ec63e3b6f 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -1,5 +1,5 @@ /* - * $Id: pcmciamtd.c,v 1.48 2003/06/24 07:14:38 spse Exp $ + * $Id: pcmciamtd.c,v 1.51 2004/07/12 22:38:29 dwmw2 Exp $ * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * @@ -49,7 +49,7 @@ static const int debug = 0; #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.48 $" +#define DRIVER_VERSION "$Revision: 1.51 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -73,7 +73,7 @@ static dev_link_t *dev_list; /* Module parameters */ /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ -static int buswidth = 2; +static int bankwidth = 2; /* Speed of memory accesses, in ns */ static int mem_speed; @@ -93,8 +93,8 @@ static int mem_type; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_PARM(buswidth, "i"); -MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)"); +MODULE_PARM(bankwidth, "i"); +MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)"); MODULE_PARM(mem_speed, "i"); MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); MODULE_PARM(force_size, "i"); @@ -135,32 +135,32 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) } -static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) { caddr_t addr; - u8 d; + map_word d = {{0}}; addr = remap_window(map, ofs); if(!addr) - return 0; + return d; - d = readb(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d); + d.x[0] = readb(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); return d; } -static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) { caddr_t addr; - u16 d; + map_word d = {{0}}; addr = remap_window(map, ofs); if(!addr) - return 0; + return d; - d = readw(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d); + d.x[0] = readw(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); return d; } @@ -191,26 +191,26 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long } -static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr) +static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr) { caddr_t addr = remap_window(map, adr); if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d); - writeb(d, addr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); + writeb(d.x[0], addr); } -static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr) +static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr) { caddr_t addr = remap_window(map, adr); if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d); - writew(d, addr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); + writew(d.x[0], addr); } @@ -244,30 +244,30 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v #define DEV_REMOVED(x) (!(*(u_int *)x->map_priv_1 & DEV_PRESENT)) -static u8 pcmcia_read8(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) { caddr_t win_base = (caddr_t)map->map_priv_2; - u8 d; + map_word d = {{0}}; if(DEV_REMOVED(map)) - return 0; + return d; - d = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d); + d.x[0] = readb(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); return d; } -static u16 pcmcia_read16(struct map_info *map, unsigned long ofs) +static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) { caddr_t win_base = (caddr_t)map->map_priv_2; - u16 d; + map_word d = {{0}}; if(DEV_REMOVED(map)) - return 0; + return d; - d = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d); + d.x[0] = readw(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); return d; } @@ -439,9 +439,9 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ case CISTPL_DEVICE_GEO: { cistpl_device_geo_t *t = &parse.device_geo; int i; - dev->pcmcia_map.buswidth = t->geo[0].buswidth; + dev->pcmcia_map.bankwidth = t->geo[0].buswidth; for(i = 0; i < t->ngeo; i++) { - DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth); DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); @@ -460,17 +460,17 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ if(!dev->pcmcia_map.size) dev->pcmcia_map.size = MAX_PCMCIA_ADDR; - if(!dev->pcmcia_map.buswidth) - dev->pcmcia_map.buswidth = 2; + if(!dev->pcmcia_map.bankwidth) + dev->pcmcia_map.bankwidth = 2; if(force_size) { dev->pcmcia_map.size = force_size << 20; DEBUG(2, "size forced to %dM", force_size); } - if(buswidth) { - dev->pcmcia_map.buswidth = buswidth; - DEBUG(2, "buswidth forced to %d", buswidth); + if(bankwidth) { + dev->pcmcia_map.bankwidth = bankwidth; + DEBUG(2, "bankwidth forced to %d", bankwidth); } dev->pcmcia_map.name = dev->mtd_name; @@ -480,7 +480,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ } DEBUG(1, "Device: Size: %lu Width:%d Name: %s", - dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name); + dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); } @@ -522,12 +522,15 @@ static void pcmciamtd_config(dev_link_t *link) card_settings(dev, link, &new_name); dev->pcmcia_map.phys = NO_XIP; - dev->pcmcia_map.read8 = pcmcia_read8_remap; - dev->pcmcia_map.read16 = pcmcia_read16_remap; dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; - dev->pcmcia_map.write8 = pcmcia_write8_remap; - dev->pcmcia_map.write16 = pcmcia_write16_remap; dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; + if (dev->pcmcia_map.bankwidth == 1) { + dev->pcmcia_map.read = pcmcia_read8_remap; + dev->pcmcia_map.write = pcmcia_write8_remap; + } else { + dev->pcmcia_map.read = pcmcia_read16_remap; + dev->pcmcia_map.write = pcmcia_write16_remap; + } if(setvpp == 1) dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; @@ -536,7 +539,7 @@ static void pcmciamtd_config(dev_link_t *link) whole card - otherwise we try smaller windows until we succeed */ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; - req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; req.Base = 0; req.AccessSpeed = mem_speed; link->win = (window_handle_t)link->handle; @@ -657,11 +660,14 @@ static void pcmciamtd_config(dev_link_t *link) DEBUG(1, "Using non remapping memory functions"); dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state); dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; - dev->pcmcia_map.read8 = pcmcia_read8; - dev->pcmcia_map.read16 = pcmcia_read16; + if (dev->pcmcia_map.bankwidth == 1) { + dev->pcmcia_map.read = pcmcia_read8; + dev->pcmcia_map.write = pcmcia_write8; + } else { + dev->pcmcia_map.read = pcmcia_read16; + dev->pcmcia_map.write = pcmcia_write16; + } dev->pcmcia_map.copy_from = pcmcia_copy_from; - dev->pcmcia_map.write8 = pcmcia_write8; - dev->pcmcia_map.write16 = pcmcia_write16; dev->pcmcia_map.copy_to = pcmcia_copy_to; } @@ -828,9 +834,9 @@ static int __init init_pcmciamtd(void) { info(DRIVER_DESC " " DRIVER_VERSION); - if(buswidth && buswidth != 1 && buswidth != 2) { - info("bad buswidth (%d), using default", buswidth); - buswidth = 2; + if(bankwidth && bankwidth != 1 && bankwidth != 2) { + info("bad bankwidth (%d), using default", bankwidth); + bankwidth = 2; } if(force_size && (force_size < 1 || force_size > 64)) { info("bad force_size (%d), using default", force_size); diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 279d60127..8dfd0b233 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,7 +1,12 @@ /* - * $Id: physmap.c,v 1.29 2003/05/29 09:24:10 dwmw2 Exp $ + * $Id: physmap.c,v 1.33 2004/07/12 14:37:24 dwmw2 Exp $ * * Normal mappings of chips in physical memory + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 031022 - [jsun] add run-time configure and partition setup */ #include @@ -15,62 +20,33 @@ #include #include -#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START -#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN -#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH - static struct mtd_info *mymtd; - -struct map_info physmap_map = { - .name = "Physically mapped flash", - .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, - .phys = WINDOW_ADDR, -}; +struct map_info physmap_map = {.name = "phys_mapped_flash"}; #ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition *mtd_parts; static int mtd_parts_nb; -static struct mtd_partition physmap_partitions[] = { -#if 0 -/* Put your own partition definitions here */ - { - .name = "bootROM", - .size = 0x80000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "zImage", - .size = 0x100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "ramdisk.gz", - .size = 0x300000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "User FS", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -#endif -}; +static int num_physmap_partitions; +static struct mtd_partition *physmap_partitions; -#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) -const char *part_probes[] = {"cmdlinepart", "RedBoot", NULL}; +static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; +void physmap_set_partitions(struct mtd_partition *parts, int num_parts) +{ + physmap_partitions=parts; + num_physmap_partitions=num_parts; +} #endif /* CONFIG_MTD_PARTITIONS */ -int __init init_physmap(void) +static int __init init_physmap(void) { static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; const char **type; - printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - physmap_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); + physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size); if (!physmap_map.virt) { printk("Failed to ioremap\n"); @@ -97,11 +73,11 @@ int __init init_physmap(void) return 0; } - if (NUM_PARTITIONS != 0) + if (num_physmap_partitions != 0) { printk(KERN_NOTICE "Using physmap partition definition\n"); - add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); + add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); return 0; } @@ -121,7 +97,7 @@ static void __exit cleanup_physmap(void) if (mtd_parts_nb) { del_mtd_partitions(mymtd); kfree(mtd_parts); - } else if (NUM_PARTITIONS) { + } else if (num_physmap_partitions) { del_mtd_partitions(mymtd); } else { del_mtd_device(mymtd); diff --git a/drivers/mtd/maps/pnc2000.c b/drivers/mtd/maps/pnc2000.c index dcf68f2aa..b204786d5 100644 --- a/drivers/mtd/maps/pnc2000.c +++ b/drivers/mtd/maps/pnc2000.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $ */ #include @@ -29,7 +29,7 @@ struct map_info pnc_map = { .name = "PNC-2000", .size = WINDOW_SIZE, - .buswidth = 4, + .bankwidth = 4, .phys = 0xFFFFFFFF, .virt = WINDOW_ADDR, }; diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 4ece1c883..acc3af90a 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -1,14 +1,13 @@ /* - * $Id: redwood.c,v 1.6 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: redwood.c,v 1.8 2004/07/12 21:59:44 dwmw2 Exp $ * * drivers/mtd/maps/redwood.c * * FLASH map for the IBM Redwood 4/5/6 boards. * + * Author: MontaVista Software, Inc. * - * Author: Armin Kuster - * - * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. @@ -89,7 +88,7 @@ static struct mtd_partition redwood_flash_partitions[] = { static struct mtd_partition redwood_flash_partitions[] = { { - .name = "Redwood kernel", + .name = "Redwood filesystem", .offset = RW_PART0_OF, .size = RW_PART0_SZ }, @@ -100,7 +99,7 @@ static struct mtd_partition redwood_flash_partitions[] = { .mask_flags = MTD_WRITEABLE /* force read-only */ }, { - .name = "Redwood filesystem", + .name = "Redwood kernel", .offset = RW_PART2_OF, .size = RW_PART2_SZ }, @@ -117,7 +116,7 @@ static struct mtd_partition redwood_flash_partitions[] = { struct map_info redwood_flash_map = { .name = "IBM Redwood", .size = WINDOW_SIZE, - .buswidth = 2, + .bankwidth = 2, .phys = WINDOW_ADDR, }; @@ -167,5 +166,5 @@ module_init(init_redwood_flash); module_exit(cleanup_redwood_flash); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Armin Kuster "); +MODULE_AUTHOR("MontaVista Software "); MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 9c3e7da44..837089a8f 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,5 +1,5 @@ /* - * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.20 2004/07/12 21:59:44 dwmw2 Exp $ * * Handle mapping of the flash on the RPX Lite and CLLF boards */ @@ -21,7 +21,7 @@ static struct mtd_info *mymtd; static struct map_info rpxlite_map = { .name = "RPX", .size = WINDOW_SIZE, - .buswidth = 4, + .bankwidth = 4, .phys = WINDOW_ADDR, }; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 3ffedfa6a..c42ca6f96 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -3,7 +3,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: sa1100-flash.c,v 1.36 2003/05/29 08:59:35 dwmw2 Exp $ + * $Id: sa1100-flash.c,v 1.39 2004/07/12 21:59:44 dwmw2 Exp $ */ #include @@ -935,7 +935,7 @@ static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info * sa[i].map->virt = (unsigned long)sa[i].vbase; sa[i].map->phys = sa[i].base; sa[i].map->set_vpp = sa[i].set_vpp; - sa[i].map->buswidth = sa[i].width; + sa[i].map->bankwidth = sa[i].width; sa[i].map->size = sa[i].size; simple_map_init(sa[i].map); @@ -1066,7 +1066,7 @@ static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) return; } - sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4; + sa1100_probe_map.bankwidth = msc & MSC_RBW ? 2 : 4; sa1100_probe_map.size = SZ_1M; sa1100_probe_map.phys = phys; sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M); @@ -1253,7 +1253,7 @@ static int __init sa1100_locate_flash(void) return nr; /* - * Retrieve the buswidth from the MSC registers. + * Retrieve the bankwidth from the MSC registers. * We currently only implement CS0 and CS1 here. */ for (i = 0; i < nr; i++) { diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 4a1477f12..7a9cb1d45 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.26 2003/05/26 08:50:36 dwmw2 Exp $ + $Id: sbc_gxx.c,v 1.29 2004/07/12 22:38:29 dwmw2 Exp $ The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. @@ -114,32 +114,12 @@ static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) } -static __u8 sbc_gxx_read8(struct map_info *map, unsigned long ofs) +static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) { - __u8 ret; + map_word ret; spin_lock(&sbc_gxx_spin); sbc_gxx_page(map, ofs); - ret = readb(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&sbc_gxx_spin); - return ret; -} - -static __u16 sbc_gxx_read16(struct map_info *map, unsigned long ofs) -{ - __u16 ret; - spin_lock(&sbc_gxx_spin); - sbc_gxx_page(map, ofs); - ret = readw(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&sbc_gxx_spin); - return ret; -} - -static __u32 sbc_gxx_read32(struct map_info *map, unsigned long ofs) -{ - __u32 ret; - spin_lock(&sbc_gxx_spin); - sbc_gxx_page(map, ofs); - ret = readl(iomapadr + (ofs & WINDOW_MASK)); + ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); spin_unlock(&sbc_gxx_spin); return ret; } @@ -161,27 +141,11 @@ static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from } } -static void sbc_gxx_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - spin_lock(&sbc_gxx_spin); - sbc_gxx_page(map, adr); - writeb(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&sbc_gxx_spin); -} - -static void sbc_gxx_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - spin_lock(&sbc_gxx_spin); - sbc_gxx_page(map, adr); - writew(d, iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&sbc_gxx_spin); -} - -static void sbc_gxx_write32(struct map_info *map, __u32 d, unsigned long adr) +static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&sbc_gxx_spin); sbc_gxx_page(map, adr); - writel(d, iomapadr + (adr & WINDOW_MASK)); + writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); spin_unlock(&sbc_gxx_spin); } @@ -208,14 +172,10 @@ static struct map_info sbc_gxx_map = { .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount of flash so the cfi probe routines find all the chips */ - .buswidth = 1, - .read8 = sbc_gxx_read8, - .read16 = sbc_gxx_read16, - .read32 = sbc_gxx_read32, + .bankwidth = 1, + .read = sbc_gxx_read8, .copy_from = sbc_gxx_copy_from, - .write8 = sbc_gxx_write8, - .write16 = sbc_gxx_write16, - .write32 = sbc_gxx_write32, + .write = sbc_gxx_write8, .copy_to = sbc_gxx_copy_to }; diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index d446d55ed..8269b6029 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.15 2003/05/21 12:45:20 dwmw2 Exp $ + * $Id: sc520cdp.c,v 1.16 2004/07/12 21:59:45 dwmw2 Exp $ * * * The SC520CDP is an evaluation board for the Elan SC520 processor available @@ -90,19 +90,19 @@ static struct map_info sc520cdp_map[] = { { .name = "SC520CDP Flash Bank #0", .size = WINDOW_SIZE_0, - .buswidth = 4, + .bankwidth = 4, .phys = WINDOW_ADDR_0 }, { .name = "SC520CDP Flash Bank #1", .size = WINDOW_SIZE_1, - .buswidth = 4, + .bankwidth = 4, .phys = WINDOW_ADDR_1 }, { .name = "SC520CDP DIL Flash", .size = WINDOW_SIZE_2, - .buswidth = 1, + .bankwidth = 1, .phys = WINDOW_ADDR_2 }, }; diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index d6d175f49..ff2328dbb 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,6 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.8 2004/07/12 21:59:45 dwmw2 Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * @@ -67,7 +67,7 @@ static struct mtd_info *scb2_mtd; struct map_info scb2_map = { .name = "SCB2 BIOS Flash", .size = 0, - .buswidth = 1, + .bankwidth = 1, }; static int region_fail; diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index c909f4bda..58855a15e 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,7 +2,7 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $ + $Id: scx200_docflash.c,v 1.6 2004/07/12 21:59:45 dwmw2 Exp $ National Semiconductor SCx200 flash mapped with DOCCS */ @@ -173,9 +173,9 @@ int __init init_scx200_docflash(void) scx200_docflash_map.size = size; if (width == 8) - scx200_docflash_map.buswidth = 1; + scx200_docflash_map.bankwidth = 1; else - scx200_docflash_map.buswidth = 2; + scx200_docflash_map.bankwidth = 2; simple_map_init(&scx200_docflash_map); diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index ea2b7af95..9b2eebe1b 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -1,5 +1,5 @@ /* - * $Id: solutionengine.c,v 1.10 2003/05/21 12:45:20 dwmw2 Exp $ + * $Id: solutionengine.c,v 1.13 2004/07/12 21:59:45 dwmw2 Exp $ * * Flash and EPROM on Hitachi Solution Engine and similar boards. * @@ -17,7 +17,7 @@ #include #include #include - +#include static struct mtd_info *flash_mtd; static struct mtd_info *eprom_mtd; @@ -27,13 +27,13 @@ static struct mtd_partition *parsed_parts; struct map_info soleng_eprom_map = { .name = "Solution Engine EPROM", .size = 0x400000, - .buswidth = 4, + .bankwidth = 4, }; struct map_info soleng_flash_map = { .name = "Solution Engine FLASH", .size = 0x400000, - .buswidth = 4, + .bankwidth = 4, }; static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index b676e0554..2a9932c50 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.7 2003/05/20 20:59:32 dwmw2 Exp $ +/* $Id: sun_uflash.c,v 1.9 2004/07/12 21:59:45 dwmw2 Exp $ * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. @@ -51,7 +51,7 @@ struct uflash_dev { struct map_info uflash_map_templ = { .name = "SUNW,???-????", .size = UFLASH_WINDOW_SIZE, - .buswidth = UFLASH_BUSWIDTH, + .bankwidth = UFLASH_BUSWIDTH, }; int uflash_devinit(struct linux_ebus_device* edev) diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 8ba73f107..496c9857f 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -2,7 +2,7 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * $Id: tqm8xxl.c,v 1.11 2004/07/12 21:59:45 dwmw2 Exp $ * * based on rpxlite.c * @@ -151,7 +151,7 @@ int __init init_tqm_mtd(void) sprintf(map_banks[idx]->name, "TQM8xxL%d", idx); map_banks[idx]->size = flash_size; - map_banks[idx]->buswidth = 4; + map_banks[idx]->bankwidth = 4; simple_map_init(map_banks[idx]); diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c index d4c9d28e2..170d71239 100644 --- a/drivers/mtd/maps/tsunami_flash.c +++ b/drivers/mtd/maps/tsunami_flash.c @@ -2,7 +2,7 @@ * tsunami_flash.c * * flash chip on alpha ds10... - * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $ + * $Id: tsunami_flash.c,v 1.9 2004/07/14 09:52:55 dwmw2 Exp $ */ #include #include @@ -15,14 +15,16 @@ #define FLASH_DISABLE_BYTE 0x00 #define MAX_TIG_FLASH_SIZE (12*1024*1024) -static inline __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset) +static inline map_word tsunami_flash_read8(struct map_info *map, unsigned long offset) { - return tsunami_tig_readb(offset); + map_word val; + val.x[0] = tsunami_tig_readb(offset); + return val; } -static void tsunami_flash_write8(struct map_info *map, __u8 value, unsigned long offset) +static void tsunami_flash_write8(struct map_info *map, map_word value, unsigned long offset) { - tsunami_tig_writeb(value, offset); + tsunami_tig_writeb(value.x[0], offset); } static void tsunami_flash_copy_from( @@ -61,10 +63,10 @@ static struct map_info tsunami_flash_map = { .name = "flash chip on the Tsunami TIG bus", .size = MAX_TIG_FLASH_SIZE, .phys = NO_XIP; - .buswidth = 1, - .read8 = tsunami_flash_read8, + .bankwidth = 1, + .read = tsunami_flash_read8, .copy_from = tsunami_flash_copy_from, - .write8 = tsunami_flash_write8, + .write = tsunami_flash_write8, .copy_to = tsunami_flash_copy_to, }; @@ -84,7 +86,7 @@ static void __exit cleanup_tsunami_flash(void) static int __init init_tsunami_flash(void) { - static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; char **type; tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT); diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 44d13f561..c5cd70bdb 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -5,7 +5,7 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $ + * $Id: uclinux.c,v 1.7 2004/07/12 21:59:45 dwmw2 Exp $ */ /****************************************************************************/ @@ -64,7 +64,7 @@ int __init uclinux_mtd_init(void) mapp = &uclinux_ram_map; mapp->phys = (unsigned long) &_ebss; mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); - mapp->buswidth = 4; + mapp->bankwidth = 4; printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", (int) mapp->map_priv_2, (int) mapp->size); diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index a649609fd..9cf2629b8 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $ +// $Id: vmax301.c,v 1.30 2004/07/12 22:38:29 dwmw2 Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -54,32 +54,12 @@ static inline void vmax301_page(struct map_info *map, __vmax301_page(map, page); } -static __u8 vmax301_read8(struct map_info *map, unsigned long ofs) +static map_word vmax301_read8(struct map_info *map, unsigned long ofs) { - __u8 ret; + map_word ret; spin_lock(&vmax301_spin); vmax301_page(map, ofs); - ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); - spin_unlock(&vmax301_spin); - return ret; -} - -static __u16 vmax301_read16(struct map_info *map, unsigned long ofs) -{ - __u16 ret; - spin_lock(&vmax301_spin); - vmax301_page(map, ofs); - ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK)); - spin_unlock(&vmax301_spin); - return ret; -} - -static __u32 vmax301_read32(struct map_info *map, unsigned long ofs) -{ - __u32 ret; - spin_lock(&vmax301_spin); - vmax301_page(map, ofs); - ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK)); + ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); spin_unlock(&vmax301_spin); return ret; } @@ -100,27 +80,11 @@ static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from } } -static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - spin_lock(&vmax301_spin); - vmax301_page(map, adr); - writeb(d, map->map_priv_2 + (adr & WINDOW_MASK)); - spin_unlock(&vmax301_spin); -} - -static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - spin_lock(&vmax301_spin); - vmax301_page(map, adr); - writew(d, map->map_priv_2 + (adr & WINDOW_MASK)); - spin_unlock(&vmax301_spin); -} - -static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr) +static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr) { spin_lock(&vmax301_spin); vmax301_page(map, adr); - writel(d, map->map_priv_2 + (adr & WINDOW_MASK)); + writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK)); spin_unlock(&vmax301_spin); } @@ -146,14 +110,10 @@ static struct map_info vmax_map[2] = { .name = "VMAX301 Internal Flash", .phys = NO_XIP, .size = 3*2*1024*1024, - .buswidth = 1, - .read8 = vmax301_read8, - .read16 = vmax301_read16, - .read32 = vmax301_read32, + .bankwidth = 1, + .read = vmax301_read8, .copy_from = vmax301_copy_from, - .write8 = vmax301_write8, - .write16 = vmax301_write16, - .write32 = vmax301_write32, + .write = vmax301_write8, .copy_to = vmax301_copy_to, .map_priv_1 = WINDOW_START + WINDOW_LENGTH, .map_priv_2 = 0xFFFFFFFF @@ -162,14 +122,10 @@ static struct map_info vmax_map[2] = { .name = "VMAX301 Socket", .phys = NO_XIP, .size = 0, - .buswidth = 1, - .read8 = vmax301_read8, - .read16 = vmax301_read16, - .read32 = vmax301_read32, + .bankwidth = 1, + .read = vmax301_read8, .copy_from = vmax301_copy_from, - .write8 = vmax301_write8, - .write16 = vmax301_write16, - .write32 = vmax301_write32, + .write = vmax301_write8, .copy_to = vmax301_copy_to, .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), .map_priv_2 = 0xFFFFFFFF diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 47216dad2..b7fdece51 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -1,5 +1,5 @@ /* - * $Id: wr_sbc82xx_flash.c,v 1.1 2004/06/07 10:21:32 dwmw2 Exp $ + * $Id: wr_sbc82xx_flash.c,v 1.5 2004/07/15 14:52:02 dwmw2 Exp $ * * Map for flash chips on Wind River PowerQUICC II SBC82xx board. * @@ -46,55 +46,69 @@ static struct mtd_partition smallflash_parts[] = { static struct mtd_partition bigflash_parts[] = { { .name = "bootloader", - .size = 0x80000, + .size = 0x00100000, .offset = 0, }, { .name = "file system", - .size = MTDPART_SIZ_FULL, + .size = 0x01f00000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "boot config", + .size = 0x00100000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "space", + .size = 0x01f00000, .offset = MTDPART_OFS_APPEND, } }; static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; +#define init_sbc82xx_one_flash(map, br, or) \ +do { \ + (map).phys = (br & 1) ? (br & 0xffff8000) : 0; \ + (map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0; \ + switch (br & 0x00001800) { \ + case 0x00000000: \ + case 0x00000800: (map).bankwidth = 1; break; \ + case 0x00001000: (map).bankwidth = 2; break; \ + case 0x00001800: (map).bankwidth = 4; break; \ + } \ +} while (0); + int __init init_sbc82xx_flash(void) { - volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; + volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; int bigflash; int i; - /* First, register the boot flash, whichever we're booting from */ - if ((mc->memc_br0 & 0x00001800) == 0x00001800) { - bigflash = 0; - } else if ((mc->memc_br0 & 0x00001800) == 0x00000800) { - bigflash = 1; - } else { - printk(KERN_WARNING "Bus Controller register BR0 is %08x. Cannot determine flash configuration\n", mc->memc_br0); - return 1; - } +#ifdef CONFIG_SBC8560 + mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t)); +#else + mc = &cpm2_immr->im_memctl; +#endif - /* Set parameters for the big flash chip (CS6 or CS0) */ - sbc82xx_flash_map[bigflash].buswidth = 4; - sbc82xx_flash_map[bigflash].size = 0x4000000; - - /* Set parameters for the small flash chip (CS0 or CS6) */ - sbc82xx_flash_map[!bigflash].buswidth = 1; - sbc82xx_flash_map[!bigflash].size = 0x200000; + bigflash = 1; + if ((mc->memc_br0 & 0x00001800) == 0x00001800) + bigflash = 0; - /* Set parameters for the user flash chip (CS1) */ - sbc82xx_flash_map[2].buswidth = 4; - sbc82xx_flash_map[2].size = 0x4000000; + init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0); + init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6); + init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1); - sbc82xx_flash_map[0].phys = mc->memc_br0 & 0xffff8000; - sbc82xx_flash_map[1].phys = mc->memc_br6 & 0xffff8000; - sbc82xx_flash_map[2].phys = mc->memc_br1 & 0xffff8000; +#ifdef CONFIG_SBC8560 + iounmap((void *) mc); +#endif for (i=0; i<3; i++) { int8_t flashcs[3] = { 0, 6, 1 }; int nr_parts; printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d", - sbc82xx_flash_map[i].name, sbc82xx_flash_map[i].size >> 20, flashcs[i]); + sbc82xx_flash_map[i].name, + (sbc82xx_flash_map[i].size >> 20), + flashcs[i]); if (!sbc82xx_flash_map[i].phys) { /* We know it can't be at zero. */ printk("): disabled by bootloader.\n"); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index e45b3fc99..b134315ab 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -1,5 +1,5 @@ /* - * $Id: mtd_blkdevs.c,v 1.16 2003/06/23 13:34:43 dwmw2 Exp $ + * $Id: mtd_blkdevs.c,v 1.22 2004/07/12 12:35:28 dwmw2 Exp $ * * (C) 2003 David Woodhouse * @@ -295,7 +295,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) snprintf(gd->devfs_name, sizeof(gd->devfs_name), "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); - set_capacity(gd, new->size); + /* 2.5 has capacity in units of 512 bytes while still + having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ + set_capacity(gd, (new->size * new->blksize) >> 9); + gd->private_data = new; new->blkcore_priv = gd; gd->queue = tr->blkcore_priv->rq; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index fd096eaea..c91d9701e 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,7 +1,7 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.63 2003/06/23 12:00:08 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $ * * (C) 2000-2003 Nicolas Pitre * (C) 1999-2003 David Woodhouse diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b1a9257b9..02b93f9c2 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.54 2003/05/21 10:50:43 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.62 2004/07/14 13:20:42 dwmw2 Exp $ * * Character-device access to raw MTD devices. * @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -16,14 +17,46 @@ #ifdef CONFIG_DEVFS_FS #include -static void mtd_notify_add(struct mtd_info* mtd); -static void mtd_notify_remove(struct mtd_info* mtd); + +static void mtd_notify_add(struct mtd_info* mtd) +{ + if (!mtd) + return; + + devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); + + devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); +} + +static void mtd_notify_remove(struct mtd_info* mtd) +{ + if (!mtd) + return; + devfs_remove("mtd/%d", mtd->index); + devfs_remove("mtd/%dro", mtd->index); +} static struct mtd_notifier notifier = { .add = mtd_notify_add, .remove = mtd_notify_remove, }; +static inline void mtdchar_devfs_init(void) +{ + devfs_mk_dir("mtd"); + register_mtd_user(¬ifier); +} + +static inline void mtdchar_devfs_exit(void) +{ + unregister_mtd_user(¬ifier); + devfs_remove("mtd"); +} +#else /* !DEVFS */ +#define mtdchar_devfs_init() do { } while(0) +#define mtdchar_devfs_exit() do { } while(0) #endif static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) @@ -298,7 +331,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, memset (erase,0,sizeof(struct erase_info)); if (copy_from_user(&erase->addr, argp, - 2 * sizeof(u_long))) { + sizeof(struct erase_info_user))) { kfree(erase); return -EFAULT; } @@ -366,7 +399,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user(argp + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) ret = -EFAULT; kfree(databuf); @@ -400,7 +433,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); - if (copy_to_user(argp + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) + if (put_user(retlen, (uint32_t __user *)argp)) ret = -EFAULT; else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) ret = -EFAULT; @@ -411,29 +444,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs, argp, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else - ret = mtd->lock(mtd, adrs[0], adrs[1]); + ret = mtd->lock(mtd, info.start, info.length); break; } case MEMUNLOCK: { - unsigned long adrs[2]; + struct erase_info_user info; - if (copy_from_user(adrs, argp, 2* sizeof(unsigned long))) + if (copy_from_user(&info, argp, sizeof(info))) return -EFAULT; if (!mtd->unlock) ret = -EOPNOTSUPP; else - ret = mtd->unlock(mtd, adrs[0], adrs[1]); + ret = mtd->unlock(mtd, info.start, info.length); break; } @@ -443,7 +476,40 @@ static int mtd_ioctl(struct inode *inode, struct file *file, return -EFAULT; break; } + + case MEMGETOOBSEL: + { + if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) + return -EFAULT; + break; + } + + case MEMGETBADBLOCK: + { + loff_t offs; + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_isbad) + ret = -EOPNOTSUPP; + else + return mtd->block_isbad(mtd, offs); + break; + } + + case MEMSETBADBLOCK: + { + loff_t offs; + + if (copy_from_user(&offs, argp, sizeof(loff_t))) + return -EFAULT; + if (!mtd->block_markbad) + ret = -EOPNOTSUPP; + else + return mtd->block_markbad(mtd, offs); + break; + } + default: DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); ret = -ENOTTY; @@ -462,30 +528,6 @@ static struct file_operations mtd_fops = { .release = mtd_close, }; - -#ifdef CONFIG_DEVFS_FS -/* Notification that a new device has been added. Create the devfs entry for - * it. */ - -static void mtd_notify_add(struct mtd_info* mtd) -{ - if (!mtd) - return; - devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), - S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); - devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), - S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%dro", mtd->index); -} - -static void mtd_notify_remove(struct mtd_info* mtd) -{ - if (!mtd) - return; - devfs_remove("mtd/%d", mtd->index); - devfs_remove("mtd/%dro", mtd->index); -} -#endif - static int __init init_mtdchar(void) { if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { @@ -494,20 +536,13 @@ static int __init init_mtdchar(void) return -EAGAIN; } -#ifdef CONFIG_DEVFS_FS - devfs_mk_dir("mtd"); - - register_mtd_user(¬ifier); -#endif + mtdchar_devfs_init(); return 0; } static void __exit cleanup_mtdchar(void) { -#ifdef CONFIG_DEVFS_FS - unregister_mtd_user(¬ifier); - devfs_remove("mtd"); -#endif + mtdchar_devfs_exit(); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 5a4f02cc1..8f66d093c 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -7,7 +7,7 @@ * * This code is GPL * - * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $ + * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $ */ #include @@ -391,7 +391,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) struct mtd_concat *concat = CONCAT(mtd); struct mtd_info *subdev; int i, err; - u_int32_t length; + u_int32_t length, offset = 0; struct erase_info *erase; if (!(mtd->flags & MTD_WRITEABLE)) @@ -450,6 +450,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) return -EINVAL; } + instr->fail_addr = 0xffffffff; + /* make a local copy of instr to avoid modifying the caller's struct */ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); @@ -465,10 +467,12 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) */ for (i = 0; i < concat->num_subdev; i++) { subdev = concat->subdev[i]; - if (subdev->size <= erase->addr) + if (subdev->size <= erase->addr) { erase->addr -= subdev->size; - else + offset += subdev->size; + } else { break; + } } /* must never happen since size limit has been verified above */ @@ -497,6 +501,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) * block alignment has been checked above */ if (err == -EINVAL) BUG(); + if (erase->fail_addr != 0xffffffff) + instr->fail_addr = erase->fail_addr + offset; break; } /* @@ -508,12 +514,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) * current subdevice, i.e. at offset zero. */ erase->addr = 0; + offset += subdev->size; } + instr->state = erase->state; kfree(erase); if (err) return err; - instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); return 0; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 12775563f..9c5af1dd7 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.42 2004/07/13 10:21:13 dwmw2 Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -334,10 +334,7 @@ static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) /* Support for /proc/mtd */ #ifdef CONFIG_PROC_FS - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static struct proc_dir_entry *proc_mtd; -#endif static inline int mtd_proc_info (char *buf, int i) { @@ -350,13 +347,8 @@ static inline int mtd_proc_info (char *buf, int i) this->erasesize, this->name); } -static int mtd_read_proc ( char *page, char **start, off_t off,int count -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - ,int *eof, void *data_unused -#else - ,int unused -#endif - ) +static int mtd_read_proc (char *page, char **start, off_t off, int count, + int *eof, void *data_unused) { int len, l, i; off_t begin = 0; @@ -376,9 +368,7 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count } } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) *eof = 1; -#endif done: up(&mtd_table_mutex); @@ -388,18 +378,6 @@ done: return ((count < begin+len-off) ? count : begin+len-off); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) -struct proc_dir_entry mtd_proc_entry = { - 0, /* low_ino: the inode -- dynamic */ - 3, "mtd", /* len of name and name */ - S_IFREG | S_IRUGO, /* mode */ - 1, 0, 0, /* nlinks, owner, group */ - 0, NULL, /* size - unused; operations -- use default */ - &mtd_read_proc, /* function used to read data */ - /* nothing more */ - }; -#endif - #endif /* CONFIG_PROC_FS */ /*====================================================================*/ @@ -408,16 +386,8 @@ struct proc_dir_entry mtd_proc_entry = { int __init init_mtd(void) { #ifdef CONFIG_PROC_FS -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) - proc_mtd->read_proc = mtd_read_proc; -#else - proc_register_dynamic(&proc_root,&mtd_proc_entry); -#endif -#endif - -#if LINUX_VERSION_CODE < 0x20212 - init_mtd_devices(); + proc_mtd->read_proc = mtd_read_proc; #endif #ifdef CONFIG_PM @@ -436,12 +406,8 @@ static void __exit cleanup_mtd(void) #endif #ifdef CONFIG_PROC_FS -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) if (proc_mtd) - remove_proc_entry( "mtd", NULL); -#else - proc_unregister(&proc_root,mtd_proc_entry.low_ino); -#endif + remove_proc_entry( "mtd", NULL); #endif } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index e90064e85..f007d0740 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.41 2003/06/18 14:53:02 dwmw2 Exp $ + * $Id: mtdpart.c,v 1.46 2004/07/12 13:28:07 dwmw2 Exp $ * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob @@ -239,12 +239,16 @@ static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, static int part_erase (struct mtd_info *mtd, struct erase_info *instr) { struct mtd_part *part = PART(mtd); + int ret; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (instr->addr >= mtd->size) return -EINVAL; instr->addr += part->offset; - return part->master->erase(part->master, instr); + ret = part->master->erase(part->master, instr); + if (instr->fail_addr != 0xffffffff) + instr->fail_addr -= part->offset; + return ret; } static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) @@ -281,6 +285,26 @@ static void part_resume(struct mtd_info *mtd) part->master->resume(part->master); } +static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + return part->master->block_isbad(part->master, ofs); +} + +static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + return part->master->block_markbad(part->master, ofs); +} + /* * This function unregisters and destroy all slave MTD objects which are * attached to the given master MTD object. @@ -316,7 +340,7 @@ int del_mtd_partitions(struct mtd_info *master) */ int add_mtd_partitions(struct mtd_info *master, - struct mtd_partition *parts, + const struct mtd_partition *parts, int nbparts) { struct mtd_part *slave; @@ -391,6 +415,10 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; + if (master->block_isbad) + slave->mtd.block_isbad = part_block_isbad; + if (master->block_markbad) + slave->mtd.block_markbad = part_block_markbad; slave->mtd.erase = part_erase; slave->master = master; slave->offset = parts[i].offset; @@ -461,6 +489,9 @@ int add_mtd_partitions(struct mtd_info *master, parts[i].name); } + /* copy oobinfo from master */ + memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo)); + if(parts[i].mtdp) { /* store the object pointer (caller may or may not register it */ *parts[i].mtdp = &slave->mtd; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f6ab47241..ddd29306d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.4 2003/05/28 10:04:23 dwmw2 Exp $ +# $Id: Kconfig,v 1.14 2004/07/13 00:14:35 dbrown Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -9,7 +9,7 @@ config MTD_NAND depends on MTD help This enables support for accessing all type of NAND flash - devices with an 8-bit data bus interface. For further information see + devices. For further information see . config MTD_NAND_VERIFY_WRITE @@ -42,10 +42,73 @@ config MTD_NAND_SPIA help If you had to ask, you don't have one. Say 'N'. +config MTD_NAND_TOTO + tristate "NAND Flash device on TOTO board" + depends on ARM && ARCH_OMAP && MTD_NAND + help + Support for NAND flash on Texas Instruments Toto platform. + config MTD_NAND_IDS tristate default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m - -endmenu +config MTD_NAND_TX4925NDFMC + tristate "SmartMedia Card on Toshiba RBTX4925 reference board" + depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND + help + This enables the driver for the NAND flash device found on the + Toshiba RBTX4925 reference board, which is a SmartMediaCard. + +config MTD_NAND_TX4938NDFMC + tristate "NAND Flash device on Toshiba RBTX4938 reference board" + depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND + help + This enables the driver for the NAND flash device found on the + Toshiba RBTX4938 reference board. + +config MTD_NAND_AU1550 + tristate "Au1550 NAND support" + depends on SOC_AU1550 && MTD_NAND + help + This enables the driver for the NAND flash controller on the + AMD/Alchemy 1550 SOC. + +config MTD_NAND_PPCHAMELEONEVB + tristate "NAND Flash device on PPChameleonEVB board" + depends on PPCHAMELEONEVB && MTD_NAND + help + This enables the NAND flash driver on the PPChameleon EVB Board. + +config MTD_NAND_DISKONCHIP + tristate "DiskOnChip 2000 and Millennium (NAND reimplementation) (EXPERIMENTAL)" + depends on MTD_NAND && EXPERIMENTAL + help + This is a reimplementation of M-Systems DiskOnChip 2000 and + Millennium as a standard NAND device driver, as opposed to the + earlier self-contained MTD device drivers. + This should enable, among other things, proper JFFS2 operation on + these devices. + +config MTD_NAND_DISKONCHIP_BBTWRITE + bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" + depends on MTD_NAND_DISKONCHIP + help + On DiskOnChip devices shipped with the INFTL filesystem (Millennium + and 2000 TSOP/Alon), Linux reserves some space at the end of the + device for the Bad Block Table (BBT). If you have existing INFTL + data on your device (created by non-Linux tools such as M-Systems' + DOS drivers), your data might overlap the area Linux wants to use for + the BBT. If this is a concern for you, leave this option disabled and + Linux will not write BBT data into this area. + The downside of leaving this option disabled is that if bad blocks + are detected by Linux, they will not be recorded in the BBT, which + could cause future problems. + Once you enable this option, new filesystems (INFTL or others, created + in Linux or other operating systems) will not use the reserved area. + The only reason not to enable this option is to prevent damage to + preexisting filesystems. + Even if you leave this disabled, you can enable BBT writes at module + load time (assuming you build diskonchip as a module) with the module + parameter "inftl_bbt_write=1". +endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index dfc4c1f98..db1939939 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,10 +1,19 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.2 2003/05/28 11:38:54 dwmw2 Exp $ +# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $ -obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o -obj-$(CONFIG_MTD_NAND_SPIA) += spia.o -obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o -obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o -obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o + +obj-$(CONFIG_MTD_NAND_SPIA) += spia.o +obj-$(CONFIG_MTD_NAND_TOTO) += toto.o +obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o +obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o +obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o +obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o +obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o +obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o + +nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dc6df46ad..6d8ecf695 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -6,7 +6,7 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.11 2003/06/04 17:04:09 gleixner Exp $ + * $Id: autcpu12.c,v 1.19 2004/07/12 15:02:15 dwmw2 Exp $ * * 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 @@ -15,7 +15,7 @@ * Overview: * This is a device driver for the NAND flash device found on the * autronix autcpu12 board, which is a SmartMediaCard. It supports - * 16MB, 32MB and 64MB cards. + * 16MiB, 32MiB and 64MiB cards. * * * 02-12-2002 TG Cleanup of module params @@ -71,42 +71,40 @@ __setup("autcpu12_pedr=",autcpu12_pedr); /* * Define partitions for flash devices */ -extern struct nand_oobinfo jffs2_oobinfo; - static struct mtd_partition partition_info16k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 8 * SZ_1M, + .size = 8 * SZ_1M }, }; static struct mtd_partition partition_info32k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 8 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 8 * SZ_1M, - .size = 24 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 8 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 8 * SZ_1M, + .size = 24 * SZ_1M }, }; static struct mtd_partition partition_info64k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 48 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 16 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 16 * SZ_1M, + .size = 48 * SZ_1M }, }; static struct mtd_partition partition_info128k[] = { - { .name = "AUTCPU12 flash partition 1", - .offset = 0, - .size = 16 * SZ_1M }, - { .name = "AUTCPU12 flash partition 2", - .offset = 16 * SZ_1M, - .size = 112 * SZ_1M }, + { .name = "AUTCPU12 flash partition 1", + .offset = 0, + .size = 16 * SZ_1M }, + { .name = "AUTCPU12 flash partition 2", + .offset = 16 * SZ_1M, + .size = 112 * SZ_1M }, }; #define NUM_PARTITIONS16K 2 @@ -116,7 +114,7 @@ static struct mtd_partition partition_info128k[] = { /* * hardware specific access to control-lines */ -void autcpu12_hwcontrol(int cmd) +static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) { switch(cmd){ @@ -135,12 +133,13 @@ void autcpu12_hwcontrol(int cmd) /* * read device ready pin */ -int autcpu12_device_ready(void) +int autcpu12_device_ready(struct mtd_info *mtd) { return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; } + /* * Main initialization routine */ @@ -185,20 +184,18 @@ int __init autcpu12_init (void) this->chip_delay = 20; this->eccmode = NAND_ECC_SOFT; + /* Enable the following for a flash based bad block table */ + /* + this->options = NAND_USE_FLASH_BBT; + */ + this->options = NAND_USE_FLASH_BBT; + /* Scan to find existance of the device */ - if (nand_scan (autcpu12_mtd)) { + if (nand_scan (autcpu12_mtd, 1)) { err = -ENXIO; goto out_ior; } - - /* Allocate memory for internal data buffer */ - this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); - if (!this->data_buf) { - printk ("Unable to allocate NAND data buffer for AUTCPU12.\n"); - err = -ENOMEM; - goto out_ior; - } - + /* Register the partitions */ switch(autcpu12_mtd->size){ case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; @@ -208,13 +205,11 @@ int __init autcpu12_init (void) default: { printk ("Unsupported SmartMedia device\n"); err = -ENXIO; - goto out_buf; + goto out_ior; } } goto out; -out_buf: - kfree (this->data_buf); out_ior: iounmap((void *)autcpu12_fio_base); out_mtd: @@ -231,20 +226,12 @@ module_init(autcpu12_init); #ifdef MODULE static void __exit autcpu12_cleanup (void) { - struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1]; - - /* Unregister partitions */ - del_mtd_partitions(autcpu12_mtd); - - /* Unregister the device */ - del_mtd_device (autcpu12_mtd); - - /* Free internal data buffers */ - kfree (this->data_buf); + /* Release resources, unregister device */ + nand_release (autcpu12_mtd); /* unmap physical adress */ iounmap((void *)autcpu12_fio_base); - + /* Free the MTD device structure */ kfree (autcpu12_mtd); } diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index db678bfb4..797794069 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.5 2003/04/20 07:24:40 gleixner Exp $ + * $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 Exp $ * * 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 @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -83,7 +84,7 @@ static struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines */ -static void ep7312_hwcontrol(int cmd) +static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) { switch(cmd) { @@ -113,10 +114,13 @@ static void ep7312_hwcontrol(int cmd) /* * read device ready pin */ -static int ep7312_device_ready(void) +static int ep7312_device_ready(struct mtd_info *mtd) { return 1; } +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif /* * Main initialization routine @@ -171,7 +175,7 @@ static int __init ep7312_init (void) this->chip_delay = 15; /* Scan to find existence of the device */ - if (nand_scan (ep7312_mtd)) { + if (nand_scan (ep7312_mtd, 1)) { iounmap((void *)ep7312_fio_base); kfree (ep7312_mtd); return -ENXIO; @@ -186,16 +190,16 @@ static int __init ep7312_init (void) return -ENOMEM; } -#ifdef CONFIG_MTD_CMDLINE_PARTS - mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, - "edb7312-nand"); +#ifdef CONFIG_PARTITIONS + ep7312_mtd->name = "edb7312-nand"; + mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, + &mtd_parts, 0); if (mtd_parts_nb > 0) - part_type = "command line"; + part_type = "command line"; else - mtd_parts_nb = 0; + mtd_parts_nb = 0; #endif - if (mtd_parts_nb == 0) - { + if (mtd_parts_nb == 0) { mtd_parts = partition_info; mtd_parts_nb = NUM_PARTITIONS; part_type = "static"; diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index bb57398b3..2e341b754 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -1,22 +1,44 @@ /* - * drivers/mtd/nand_ecc.c + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. * - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * Toshiba America Electronics Components, Inc. + * drivers/mtd/nand/nand_ecc.c * - * $Id: nand_ecc.c,v 1.9 2003/02/20 13:34:19 sjhill Exp $ + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 2.1 as published by the Free Software Foundation. + * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ * - * This file contains an ECC algorithm from Toshiba that detects and - * corrects 1 bit errors in a 256 byte block of data. + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. */ #include #include #include +#include /* * Pre-calculated 256-way 1 byte column parity @@ -41,7 +63,12 @@ static const u_char nand_ecc_precalc_table[] = { }; -/* +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * * Creates non-inverted ECC code from line parity */ static void nand_trans_result(u_char reg2, u_char reg3, @@ -81,10 +108,13 @@ static void nand_trans_result(u_char reg2, u_char reg3, ecc_code[1] = tmp2; } -/* - * Calculate 3 byte ECC code for 256 byte block +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC */ -void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { u_char idx, reg1, reg2, reg3; int j; @@ -114,12 +144,19 @@ void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) ecc_code[0] = ~ecc_code[0]; ecc_code[1] = ~ecc_code[1]; ecc_code[2] = ((~reg1) << 2) | 0x03; + return 0; } -/* +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * * Detect and correct a 1 bit error for 256 byte block */ -int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { u_char a, b, c, d1, d2, d3, add, bit, i; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 5a5518fe3..2d8c43212 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -2,9 +2,8 @@ * drivers/mtd/nandids.c * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) - * - * - * $Id: nand_ids.c,v 1.4 2003/05/21 15:15:08 dwmw2 Exp $ + * + * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ * * 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 @@ -13,26 +12,99 @@ */ #include #include - /* * Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { - {"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1}, - {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1}, - {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0}, - {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1}, - {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1}, - {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1}, - {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0}, - {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0}, - {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0}, - {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0}, - {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0}, - {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0}, - {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0}, - {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0}, - {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0}, + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0}, + + /* These are the new chips with large page size. The pagesize + * and the erasesize is determined from the extended id bytes + */ + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! + * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes + * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 + * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go + * There are more speed improvements for reads and writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + {NULL,} }; @@ -44,10 +116,11 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_SAMSUNG, "Samsung"}, {NAND_MFR_FUJITSU, "Fujitsu"}, {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, {0x0, "Unknown"} }; - EXPORT_SYMBOL (nand_manuf_ids); EXPORT_SYMBOL (nand_flash_ids); diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index e5e25eb36..acf6fc808 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -8,7 +8,7 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.19 2003/04/20 07:24:40 gleixner Exp $ + * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $ * * 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 @@ -20,6 +20,8 @@ * a 64Mibit (8MiB x 8 bits) NAND flash device. */ +#include +#include #include #include #include @@ -35,14 +37,14 @@ static struct mtd_info *spia_mtd = NULL; /* * Values specific to the SPIA board (used with EP7212 processor) */ -#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ -#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ -#define SPIA_PEDR = 0x0080 /* +#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ +#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ +#define SPIA_PEDR 0x0080 /* * IO offset to Port E data register * where the CLE, ALE and NCE pins * are wired to. */ -#define SPIA_PEDDR = 0x00c0 /* +#define SPIA_PEDDR 0x00c0 /* * IO offset to Port E data direction * register so we can control the IO * lines. @@ -62,11 +64,6 @@ MODULE_PARM(spia_fio_base, "i"); MODULE_PARM(spia_pedr, "i"); MODULE_PARM(spia_peddr, "i"); -__setup("spia_io_base=",spia_io_base); -__setup("spia_fio_base=",spia_fio_base); -__setup("spia_pedr=",spia_pedr); -__setup("spia_peddr=",spia_peddr); - /* * Define partitions for flash device */ @@ -88,7 +85,7 @@ const static struct mtd_partition partition_info[] = { /* * hardware specific access to control-lines */ -void spia_hwcontrol(int cmd){ +static void spia_hwcontrol(struct mtd_info *mtd, int cmd){ switch(cmd){ @@ -143,7 +140,7 @@ int __init spia_init (void) this->chip_delay = 15; /* Scan to find existence of the device */ - if (nand_scan (spia_mtd)) { + if (nand_scan (spia_mtd, 1)) { kfree (spia_mtd); return -ENXIO; } diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index ea709d7cf..4102a6797 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,7 +1,7 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */ +/* $Id: nftlcore.c,v 1.96 2004/06/28 13:52:55 dbrown Exp $ */ /* The contents of this file are distributed under the GNU General @@ -43,9 +43,19 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct NFTLrecord *nftl; unsigned long temp; - if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) + if (mtd->type != MTD_NANDFLASH) + return; + /* OK, this is moderately ugly. But probably safe. Alternatives? */ + if (memcmp(mtd->name, "DiskOnChip", 10)) return; + if (!mtd->block_isbad) { + printk(KERN_ERR +"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" +"Please use the new diskonchip driver under the NAND subsystem.\n"); + return; + } + DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); @@ -60,6 +70,8 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) nftl->mbd.devnum = -1; nftl->mbd.blksize = 512; nftl->mbd.tr = tr; + memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); + nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; if (NFTL_mount(nftl) < 0) { printk(KERN_WARNING "NFTL: could not mount device\n"); @@ -350,17 +362,19 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p if (BlockMap[block] == BLOCK_NIL) continue; - ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), - 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf); if (ret < 0) { - ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), 512, &retlen, - movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + movebuf); if (ret != -EIO) printk("Error went away on retry.\n"); } + memset(&oob, 0xff, sizeof(struct nftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), - 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo); } /* add the header so that it is now a valid chain */ @@ -390,7 +404,6 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p if (NFTL_formatblock(nftl, thisEUN) < 0) { /* could not erase : mark block as reserved - * FixMe: Update Bad Unit Table on disk */ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; } else { @@ -617,7 +630,7 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, u16 writeEUN; unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); size_t retlen; - u8 eccbuf[6]; + struct nftl_oob oob; writeEUN = NFTL_findwriteunit(nftl, block); @@ -628,9 +641,11 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, return 1; } + memset(&oob, 0xff, sizeof(struct nftl_oob)); + oob.b.Status = oob.b.Status1 = SECTOR_USED; MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); - /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ + 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo); + /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */ return 0; } @@ -692,8 +707,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, } else { loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; - u_char eccbuf[6]; - if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) + if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer)) return -EIO; } return 0; @@ -735,7 +749,7 @@ extern char nftlmountrev[]; int __init init_nftl(void) { - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev); + printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.96 $, nftlmount.c %s\n", nftlmountrev); return register_mtd_blktrans(&nftl_tr); } diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 394a0a115..a8a4e1ac8 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $ + * $Id: nftlmount.c,v 1.36 2004/06/28 13:52:55 dbrown Exp $ * * 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 @@ -31,7 +31,7 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.34 $"; +char nftlmountrev[]="$Revision: 1.36 $"; /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update @@ -50,6 +50,10 @@ static int find_boot_record(struct NFTLrecord *nftl) /* Assume logical EraseSize == physical erasesize for starting the scan. We'll sort it out later if we find a MediaHeader which says otherwise */ + /* Actually, we won't. The new DiskOnChip driver has already scanned + the MediaHeader and adjusted the virtual erasesize it presents in + the mtd device accordingly. We could even get rid of + nftl->EraseSize if there were any point in doing so. */ nftl->EraseSize = nftl->mbd.mtd->erasesize; nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; @@ -62,7 +66,10 @@ static int find_boot_record(struct NFTLrecord *nftl) /* Check for ANAND header first. Then can whinge if it's found but later checks fail */ - if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { + ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf); + /* We ignore ret in case the ECC of the MediaHeader is invalid + (which is apparently acceptable) */ + if (retlen != SECTORSIZE) { static int warncount = 5; if (warncount) { @@ -104,7 +111,7 @@ static int find_boot_record(struct NFTLrecord *nftl) /* Finally reread to check ECC */ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) { + &retlen, buf, (char *)&oob, NULL) < 0)) { printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", block * nftl->EraseSize, nftl->mbd.mtd->index, ret); continue; @@ -149,6 +156,10 @@ static int find_boot_record(struct NFTLrecord *nftl) memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); /* Do some sanity checks on it */ +#if 0 +The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual +erasesize based on UnitSizeFactor. So the erasesize we read from the mtd +device is already correct. if (mh->UnitSizeFactor == 0) { printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); } else if (mh->UnitSizeFactor < 0xfc) { @@ -161,6 +172,7 @@ static int find_boot_record(struct NFTLrecord *nftl) nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; } +#endif nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); @@ -213,11 +225,13 @@ static int find_boot_record(struct NFTLrecord *nftl) /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ for (i = 0; i < nftl->nb_blocks; i++) { +#if 0 +The new DiskOnChip driver already scanned the bad block table. Just query it. if ((i & (SECTORSIZE - 1)) == 0) { /* read one sector for every SECTORSIZE of blocks */ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + i + SECTORSIZE, SECTORSIZE, &retlen, buf, - (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { + (char *)&oob, NULL)) < 0) { printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", ret); kfree(nftl->ReplUnitTable); @@ -228,6 +242,9 @@ static int find_boot_record(struct NFTLrecord *nftl) /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ if (buf[i & (SECTORSIZE - 1)] != 0xff) nftl->ReplUnitTable[i] = BLOCK_RESERVED; +#endif + if (nftl->mbd.mtd->block_isbad(nftl->mbd.mtd, i * nftl->EraseSize)) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; } nftl->MediaUnit = block; @@ -253,21 +270,16 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int int check_oob) { int i, retlen; - u8 buf[SECTORSIZE]; + u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; for (i = 0; i < len; i += SECTORSIZE) { - /* we want to read the sector without ECC check here since a free - sector does not have ECC syndrome on it yet */ - if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0) return -1; if (memcmpb(buf, 0xff, SECTORSIZE) != 0) return -1; if (check_oob) { - if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize, - &retlen, buf) < 0) - return -1; - if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0) + if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0) return -1; } address += SECTORSIZE; @@ -282,7 +294,6 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? - * 2. UnitSizeFactor != 0xFF */ int NFTL_formatblock(struct NFTLrecord *nftl, int block) { @@ -312,11 +323,10 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) MTD_ERASE(nftl->mbd.mtd, instr); if (instr->state == MTD_ERASE_FAILED) { - /* could not format, FixMe: We should update the BadUnitTable - both in memory and on disk */ printk("Error while formatting block %d\n", block); - return -1; - } else { + goto fail; + } + /* increase and write Wear-Leveling info */ nb_erases = le32_to_cpu(uci.WearInfo); nb_erases++; @@ -329,14 +339,18 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) * FixMe: is this check really necessary ? since we have check the * return code after the erase operation. */ if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) - return -1; + goto fail; uci.WearInfo = le32_to_cpu(nb_erases); if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) - return -1; + goto fail; return 0; - } +fail: + /* could not format, update the bad block table (caller is responsible + for setting the ReplUnitTable to BLOCK_RESERVED on failure) */ + nftl->mbd.mtd->block_markbad(nftl->mbd.mtd, instr->addr); + return -1; } /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. @@ -441,8 +455,7 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block) printk("Formatting block %d\n", block); if (NFTL_formatblock(nftl, block) < 0) { - /* cannot format !!!! Mark it as Bad Unit, - FixMe: update the BadUnitTable on disk */ + /* cannot format !!!! Mark it as Bad Unit */ nftl->ReplUnitTable[block] = BLOCK_RESERVED; } else { nftl->ReplUnitTable[block] = BLOCK_FREE; diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 3e279992f..626ad368e 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.11 2003/05/21 10:39:26 dwmw2 Exp $ + * $Id: redboot.c,v 1.13 2004/04/01 10:17:40 gthomas Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -48,21 +48,24 @@ static int parse_redboot_partitions(struct mtd_info *master, char *names; char *nullname; int namelen = 0; + int nulllen = 0; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; +#endif - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + buf = kmalloc(master->erasesize, GFP_KERNEL); if (!buf) return -ENOMEM; /* Read the start of the last erase block */ ret = master->read(master, master->size - master->erasesize, - PAGE_SIZE, &retlen, (void *)buf); + master->erasesize, &retlen, (void *)buf); if (ret) goto out; - if (retlen != PAGE_SIZE) { + if (retlen != master->erasesize) { ret = -EIO; goto out; } @@ -80,7 +83,7 @@ static int parse_redboot_partitions(struct mtd_info *master, goto out; } - for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) @@ -112,48 +115,69 @@ static int parse_redboot_partitions(struct mtd_info *master, nrparts++; } - if (fl->img->flash_base) +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->img->flash_base) { nrparts++; + nulllen = sizeof(nullstring); + } for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { - if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { nrparts++; + nulllen = sizeof(nullstring); + } } - parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL); +#endif + parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); if (!parts) { ret = -ENOMEM; goto out; } - memset(parts, 0, sizeof(*parts)*nrparts + namelen); + memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen); - /* FIXME: Include nullname only if it's used */ nullname = (char *)&parts[nrparts]; - sprintf(nullname, nullstring); - names = nullname + sizeof(nullstring); +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (nulllen > 0) { + strcpy(nullname, nullstring); + } +#endif + names = nullname + nulllen; i=0; +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (fl->img->flash_base) { parts[0].name = nullname; parts[0].size = fl->img->flash_base; parts[0].offset = 0; + i++; } +#endif for ( ; iimg->size; parts[i].offset = fl->img->flash_base; parts[i].name = names; strcpy(names, fl->img->name); +#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY + if (!memcmp(names, "RedBoot", 8) || + !memcmp(names, "RedBoot config", 15) || + !memcmp(names, "FIS directory", 14)) { + parts[i].mask_flags = MTD_WRITEABLE; + } +#endif names += strlen(names)+1; - if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { +#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { i++; parts[i].offset = parts[i-1].size + parts[i-1].offset; parts[i].size = fl->next->img->flash_base - parts[i].offset; parts[i].name = nullname; } +#endif tmp_fl = fl; fl = fl->next; kfree(tmp_fl); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c73345b5d..d67975b90 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2130,6 +2130,17 @@ config TIGON3 To compile this driver as a module, choose M here: the module will be called tg3. This is recommended. +config GIANFAR + tristate "Gianfar Ethernet" + depends on 85xx + help + This driver supports the Gigabit TSEC on the MPC85xx + family of chips, and the FEC on the 8540 + +config GFAR_NAPI + bool "NAPI Support" + depends on GIANFAR + endmenu # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3a7b01d4b..76d3af56e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IXGB) += ixgb/ obj-$(CONFIG_BONDING) += bonding/ +obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o # # link order important here diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 070031dcd..30bc80a51 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -337,6 +337,20 @@ config SERIAL_UART00_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_MPSC + bool "Marvell MPSC serial port support" + depends on PPC32 && MV64X60 + select SERIAL_CORE + help + Say Y here if you want to use the Marvell MPSC serial controller. + +config SERIAL_MPSC_CONSOLE + bool "Support for console on Marvell MPSC serial port" + depends on SERIAL_MPSC + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on a Marvell MPSC. + config SERIAL_PXA bool "PXA serial port support" depends on ARM && ARCH_PXA diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 5780ae751..f7d357773 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -4,6 +4,8 @@ # $Id: Makefile,v 1.8 2002/07/21 21:32:30 rmk Exp $ # +obj-$(CONFIG_SERIAL_MPSC) += mpsc/ + serial-8250-y := serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o serial-8250-$(CONFIG_GSC) += 8250_gsc.o diff --git a/fs/Kconfig b/fs/Kconfig index a4a93a02a..2c35a14cc 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1097,17 +1097,15 @@ config JFFS_FS_VERBOSE config JFFS_PROC_FS bool "JFFS stats available in /proc filesystem" - depends on JFFS_FS && PROC + depends on JFFS_FS && PROC_FS help Enabling this option will cause statistics from mounted JFFS file systems to be made available to the user in the /proc/fs/jffs/ directory. config JFFS2_FS tristate "Journalling Flash File System v2 (JFFS2) support" - depends on MTD select CRC32 - select ZLIB_INFLATE - select ZLIB_DEFLATE + depends on MTD help JFFS2 is the second generation of the Journalling Flash File System for use on diskless embedded devices. It provides improved wear @@ -1151,6 +1149,82 @@ config JFFS2_FS_NAND Say 'N' unless you have NAND flash and you are willing to test and develop JFFS2 support for it. +config JFFS2_COMPRESSION_OPTIONS + bool "Advanced compression options for JFFS2" + default n + help + Enabling this option allows you to explicitly choose which + compression modules, if any, are enabled in JFFS2. Removing + compressors and mean you cannot read existing file systems, + and enabling experimental compressors can mean that you + write a file system which cannot be read by a standard kernel. + + If unsure, you should _definitely_ say 'N'. + +config JFFS2_ZLIB + bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS + select ZLIB_INFLATE + select ZLIB_DEFLATE + depends on JFFS2_FS + default y + help + Zlib is designed to be a free, general-purpose, legally unencumbered, + lossless data-compression library for use on virtually any computer + hardware and operating system. See http://www.gzip.org/zlib/ for + further information. + + Say 'Y' if unsure. + +config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. + +config JFFS2_RUBIN + bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default n + help + RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. + +choice + prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + default JFFS2_CMODE_PRIORITY + depends on JFFS2_FS + help + You can set here the default compression mode of JFFS2 from + the avaiable compression modes. Don't touch if unsure. + +config JFFS2_CMODE_NONE + bool "no compression" + help + Uses no compression. + +config JFFS2_CMODE_PRIORITY + bool "priority" + help + Tries the compressors in a predefinied order and chooses the first + successful one. + +config JFFS2_CMODE_SIZE + bool "size (EXPERIMENTAL)" + help + Tries all compressors and chooses the one which has the smallest + result. + +endchoice + +config JFFS2_PROC + bool "JFFS2 proc interface support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS && PROC_FS + default n + help + You can read some statistics and set the compression mode and + compressor priorities with this interface. + + config CRAMFS tristate "Compressed ROM file system support" select ZLIB_INFLATE diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 1a47874c7..73ee11016 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -1,15 +1,18 @@ # -# Makefile for the linux Journalling Flash FileSystem (JFFS) routines. +# Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile,v 1.34 2002/03/08 11:27:59 dwmw2 Exp $ +# $Id: Makefile.common,v 1.5 2004/07/15 16:06:41 dwmw2 Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o -jffs2-y := compr.o compr_rubin.o compr_rtime.o compr_zlib.o -jffs2-y += dir.o file.o ioctl.o nodelist.o malloc.o +jffs2-y := compr.o dir.o file.o ioctl.o nodelist.o malloc.o jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o +jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o +jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o +jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o +jffs2-$(CONFIG_JFFS2_PROC) += proc.o diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 621231064..ac59ee35d 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.44 2003/10/08 13:29:55 dwmw2 Exp $ + * $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $ * */ @@ -20,12 +20,11 @@ static int jffs2_garbage_collect_thread(void *); -static int thread_should_wake(struct jffs2_sb_info *c); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { spin_lock(&c->erase_completion_lock); - if (c->gc_task && thread_should_wake(c)) + if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); spin_unlock(&c->erase_completion_lock); } @@ -84,11 +83,11 @@ static int jffs2_garbage_collect_thread(void *_c) for (;;) { allow_signal(SIGHUP); - if (!thread_should_wake(c)) { + if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); - /* Yes, there's a race here; we checked thread_should_wake() before - setting current->state to TASK_INTERRUPTIBLE. But it doesn't + /* Yes, there's a race here; we checked jffs2_thread_should_wake() + before setting current->state to TASK_INTERRUPTIBLE. But it doesn't matter - We don't care if we miss a wakeup, because the GC thread is only an optimisation anyway. */ schedule(); @@ -144,34 +143,3 @@ static int jffs2_garbage_collect_thread(void *_c) spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); } - -static int thread_should_wake(struct jffs2_sb_info *c) -{ - int ret = 0; - uint32_t dirty; - - if (c->unchecked_size) { - D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n", - c->unchecked_size, c->checked_ino)); - return 1; - } - - /* dirty_size contains blocks on erase_pending_list - * those blocks are counted in c->nr_erasing_blocks. - * If one block is actually erased, it is not longer counted as dirty_space - * but it is counted in c->nr_erasing_blocks, so we add it and subtract it - * with c->nr_erasing_blocks * c->sector_size again. - * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks - * This helps us to force gc and pick eventually a clean block to spread the load. - */ - dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; - - if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && - (dirty > c->nospc_dirty_size)) - ret = 1; - - D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", - c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); - - return ret; -} diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index c05f64b5a..9b6e1d8e4 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.52 2003/10/09 00:38:38 dwmw2 Exp $ + * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ * */ @@ -16,8 +16,7 @@ #include #include "nodelist.h" -int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); -int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *); +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); static inline struct jffs2_inode_cache * first_inode_chain(int *i, struct jffs2_sb_info *c) @@ -44,6 +43,41 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c) ic; \ ic = next_inode(&i, ic, (c))) + +static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_full_dirent *fd; + + D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino)); + + /* For each child, increase nlink */ + for(fd = ic->scan_dents; fd; fd = fd->next) { + struct jffs2_inode_cache *child_ic; + if (!fd->ino) + continue; + + /* XXX: Can get high latency here with huge directories */ + + child_ic = jffs2_get_ino_cache(c, fd->ino); + if (!child_ic) { + printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", + fd->name, fd->ino, ic->ino); + continue; + } + + if (child_ic->nlink++ && fd->type == DT_DIR) { + printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); + if (fd->ino == 1 && ic->ino == 1) { + printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); + printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); + } + /* What do we do about it? */ + } + D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); + /* Can't free them. We might need them in pass 2 */ + } +} + /* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go - Scan directory tree from top down, setting nlink in inocaches @@ -54,6 +88,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *dead_fds = NULL; /* First, scan the medium and build all the inode caches with lists of physical nodes */ @@ -71,47 +106,51 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); - ret = jffs2_build_inode_pass1(c, ic); - if (ret) { - D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret)); - return ret; + + D1(BUG_ON(ic->ino > c->highest_ino)); + + if (ic->scan_dents) { + jffs2_build_inode_pass1(c, ic); + cond_resched(); } - cond_resched(); } D1(printk(KERN_DEBUG "Pass 1 complete\n")); - D1(jffs2_dump_block_lists(c)); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ - do { - D1(printk(KERN_DEBUG "Pass 2 (re)starting\n")); - ret = 0; - for_each_inode(i, c, ic) { - D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); - if (ic->nlink) - continue; + D1(printk(KERN_DEBUG "Pass 2 starting\n")); + + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); + if (ic->nlink) + continue; - /* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */ + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + cond_resched(); + } - ret = jffs2_build_remove_unlinked_inode(c, ic); - if (ret) - break; - /* -EAGAIN means the inode's nlink was zero, so we deleted it, - and furthermore that it had children and their nlink has now - gone to zero too. So we have to restart the scan. */ - } - D1(jffs2_dump_block_lists(c)); + D1(printk(KERN_DEBUG "Pass 2a starting\n")); - cond_resched(); - - } while(ret == -EAGAIN); + while (dead_fds) { + struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd = dead_fds; + + dead_fds = fd->next; + + ic = jffs2_get_ino_cache(c, fd->ino); + D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); + + if (ic) + jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); + jffs2_free_full_dirent(fd); + } D1(printk(KERN_DEBUG "Pass 2 complete\n")); - /* Finally, we can scan again and free the dirent nodes and scan_info structs */ + /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { struct jffs2_full_dirent *fd; D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); @@ -133,49 +172,10 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) return ret; } -int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) -{ - struct jffs2_full_dirent *fd; - - D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino)); - - if (ic->ino > c->highest_ino) - c->highest_ino = ic->ino; - - /* For each child, increase nlink */ - for(fd=ic->scan_dents; fd; fd = fd->next) { - struct jffs2_inode_cache *child_ic; - if (!fd->ino) - continue; - - /* XXX: Can get high latency here with huge directories */ - - child_ic = jffs2_get_ino_cache(c, fd->ino); - if (!child_ic) { - printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", - fd->name, fd->ino, ic->ino); - continue; - } - - if (child_ic->nlink++ && fd->type == DT_DIR) { - printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); - if (fd->ino == 1 && ic->ino == 1) { - printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); - printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); - } - /* What do we do about it? */ - } - D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); - /* Can't free them. We might need them in pass 2 */ - } - return 0; -} - -int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; - int ret = 0; D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); @@ -214,18 +214,29 @@ int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inod jffs2_free_full_dirent(fd); continue; } - jffs2_free_full_dirent(fd); + + /* Reduce nlink of the child. If it's now zero, stick it on the + dead_fds list to be cleaned up later. Else just free the fd */ + child_ic->nlink--; + + if (!child_ic->nlink) { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", + fd->ino, fd->name)); + fd->next = *dead_fds; + *dead_fds = fd; + } else { + D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", + fd->ino, fd->name, child_ic->nlink)); + jffs2_free_full_dirent(fd); + } } - ret = -EAGAIN; } /* We don't delete the inocache from the hash list and free it yet. The erase code will do that, when all the nodes are completely gone. */ - - return ret; } static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 471ec55e6..9e5138a64 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -2,137 +2,473 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. - * * Created by Arjan van de Ven * + * Copyright (C) 2004 Ferenc Havasi , + * University of Szeged, Hungary + * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $ * */ -#if defined(__KERNEL__) || defined (__ECOS) -#include -#include -#include -#include -#else -#define KERN_DEBUG -#define KERN_NOTICE -#define KERN_WARNING -#define printk printf -#include -#include -#include -#endif +#include "compr.h" + +static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED; + +/* Available compressors are on this list */ +static LIST_HEAD(jffs2_compressor_list); + +/* Actual compression mode */ +static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; -#include +void jffs2_set_compression_mode(int mode) +{ + jffs2_compression_mode = mode; +} -int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); -int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); -void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); +int jffs2_get_compression_mode(void) +{ + return jffs2_compression_mode; +} +/* Statistics for blocks stored without compression */ +static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; /* jffs2_compress: * @data: Pointer to uncompressed data - * @cdata: Pointer to buffer for compressed data + * @cdata: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed * data. On exit, expected to hold the actual size of the compressed * data. * - * Returns: Byte to be stored with data indicating compression type used. + * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. + * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ -unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *datalen, uint32_t *cdatalen) +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen) { - int ret; + int ret = JFFS2_COMPR_NONE; + int compr_ret; + struct jffs2_compressor *this, *best=NULL; + unsigned char *output_buf = NULL, *tmp_buf; + uint32_t orig_slen, orig_dlen; + uint32_t best_slen=0, best_dlen=0; - ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen); - if (!ret) { - return JFFS2_COMPR_ZLIB; - } -#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ - ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen); - if (!ret) { - return JFFS2_COMPR_DYNRUBIN; - } -#endif -#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */ - ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen); - if (!ret) { - return JFFS2_COMPR_RUBINMIPS; - } -#endif - /* rtime does manage to recompress already-compressed data */ - ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen); - if (!ret) { - return JFFS2_COMPR_RTIME; - } -#if 0 - /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */ - /* If we get here, no compression is going to work */ - /* But we might want to use the fragmentation part -- Arjan */ - memcpy(cpage_out,data_in,min(*datalen,*cdatalen)); - if (*datalen > *cdatalen) - *datalen = *cdatalen; -#endif - return JFFS2_COMPR_NONE; /* We failed to compress */ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + break; + case JFFS2_COMPR_MODE_PRIORITY: + output_buf = kmalloc(*cdatalen,GFP_KERNEL); + if (!output_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); + goto out; + } + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + ret = this->compr; + this->stat_compr_blocks++; + this->stat_compr_orig_size += *datalen; + this->stat_compr_new_size += *cdatalen; + break; + } + } + spin_unlock(&jffs2_compressor_list_lock); + if (ret == JFFS2_COMPR_NONE) kfree(output_buf); + break; + case JFFS2_COMPR_MODE_SIZE: + orig_slen = *datalen; + orig_dlen = *cdatalen; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + /* Skip decompress-only backwards-compatibility and disabled modules */ + if ((!this->compress)||(this->disabled)) + continue; + /* Allocating memory for output buffer if necessary */ + if ((this->compr_buf_sizecompr_buf)) { + spin_unlock(&jffs2_compressor_list_lock); + kfree(this->compr_buf); + spin_lock(&jffs2_compressor_list_lock); + this->compr_buf_size=0; + this->compr_buf=NULL; + } + if (!this->compr_buf) { + spin_unlock(&jffs2_compressor_list_lock); + tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); + spin_lock(&jffs2_compressor_list_lock); + if (!tmp_buf) { + printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); + continue; + } + else { + this->compr_buf = tmp_buf; + this->compr_buf_size = orig_dlen; + } + } + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + *datalen = orig_slen; + *cdatalen = orig_dlen; + compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + this->usecount--; + if (!compr_ret) { + if ((!best_dlen)||(best_dlen>*cdatalen)) { + best_dlen = *cdatalen; + best_slen = *datalen; + best = this; + } + } + } + if (best_dlen) { + *cdatalen = best_dlen; + *datalen = best_slen; + output_buf = best->compr_buf; + best->compr_buf = NULL; + best->compr_buf_size = 0; + best->stat_compr_blocks++; + best->stat_compr_orig_size += best_slen; + best->stat_compr_new_size += best_dlen; + ret = best->compr; + } + spin_unlock(&jffs2_compressor_list_lock); + break; + default: + printk(KERN_ERR "JFFS2: unknow compression mode.\n"); + } + out: + if (ret == JFFS2_COMPR_NONE) { + *cpage_out = data_in; + *datalen = *cdatalen; + none_stat_compr_blocks++; + none_stat_compr_size += *datalen; + } + else { + *cpage_out = output_buf; + } + return ret; } - -int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { - switch (comprtype) { + struct jffs2_compressor *this; + int ret; + + switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); + none_stat_decompr_blocks++; break; - case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; + default: + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (comprtype == this->compr) { + this->usecount++; + spin_unlock(&jffs2_compressor_list_lock); + ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); + spin_lock(&jffs2_compressor_list_lock); + if (ret) { + printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); + } + else { + this->stat_decompr_blocks++; + } + this->usecount--; + spin_unlock(&jffs2_compressor_list_lock); + return ret; + } + } + printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype); + spin_unlock(&jffs2_compressor_list_lock); + return -EIO; + } + return 0; +} - case JFFS2_COMPR_ZLIB: - jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen); - break; +int jffs2_register_compressor(struct jffs2_compressor *comp) +{ + struct jffs2_compressor *this; - case JFFS2_COMPR_RTIME: - jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen); - break; + if (!comp->name) { + printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); + return -1; + } + comp->compr_buf_size=0; + comp->compr_buf=NULL; + comp->usecount=0; + comp->stat_compr_orig_size=0; + comp->stat_compr_new_size=0; + comp->stat_compr_blocks=0; + comp->stat_decompr_blocks=0; + D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + goto out; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); +out: + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + + spin_unlock(&jffs2_compressor_list_lock); + + return 0; +} + +int jffs2_unregister_compressor(struct jffs2_compressor *comp) +{ + D2(struct jffs2_compressor *this;) + + D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); + + spin_lock(&jffs2_compressor_list_lock); + + if (comp->usecount) { + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); + return -1; + } + list_del(&comp->list); + + D2(list_for_each_entry(this, &jffs2_compressor_list, list) { + printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); + }) + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} + +#ifdef CONFIG_JFFS2_PROC + +#define JFFS2_STAT_BUF_SIZE 16000 + +char *jffs2_list_compressors(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"disabled"); + else + act_buf += sprintf(act_buf,"enabled"); + act_buf += sprintf(act_buf,"\n"); + } + return buf; +} + +char *jffs2_stats(void) +{ + struct jffs2_compressor *this; + char *buf, *act_buf; + + act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL); + + act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n"); + act_buf += sprintf(act_buf,"%10s ","none"); + act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks, + none_stat_compr_size, none_stat_decompr_blocks); + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + act_buf += sprintf(act_buf,"%10s ",this->name); + if ((this->disabled)||(!this->compress)) + act_buf += sprintf(act_buf,"- "); + else + act_buf += sprintf(act_buf,"+ "); + act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks, + this->stat_compr_new_size, this->stat_compr_orig_size, + this->stat_decompr_blocks); + act_buf += sprintf(act_buf,"\n"); + } + spin_unlock(&jffs2_compressor_list_lock); + + return buf; +} + +char *jffs2_get_compression_mode_name(void) +{ + switch (jffs2_compression_mode) { + case JFFS2_COMPR_MODE_NONE: + return "none"; + case JFFS2_COMPR_MODE_PRIORITY: + return "priority"; + case JFFS2_COMPR_MODE_SIZE: + return "size"; + } + return "unkown"; +} + +int jffs2_set_compression_mode_name(const char *name) +{ + if (!strcmp("none",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + return 0; + } + if (!strcmp("priority",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; + return 0; + } + if (!strcmp("size",name)) { + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + return 0; + } + return 1; +} + +static int jffs2_compressor_Xable(const char *name, int disabled) +{ + struct jffs2_compressor *this; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->disabled = disabled; + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +} + +int jffs2_enable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 0); +} + +int jffs2_disable_compressor_name(const char *name) +{ + return jffs2_compressor_Xable(name, 1); +} + +int jffs2_set_compressor_priority(const char *name, int priority) +{ + struct jffs2_compressor *this,*comp; + spin_lock(&jffs2_compressor_list_lock); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (!strcmp(this->name, name)) { + this->priority = priority; + comp = this; + goto reinsert; + } + } + spin_unlock(&jffs2_compressor_list_lock); + printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name); + return 1; +reinsert: + /* list is sorted in the order of priority, so if + we change it we have to reinsert it into the + good place */ + list_del(&comp->list); + list_for_each_entry(this, &jffs2_compressor_list, list) { + if (this->priority < comp->priority) { + list_add(&comp->list, this->list.prev); + spin_unlock(&jffs2_compressor_list_lock); + return 0; + } + } + list_add_tail(&comp->list, &jffs2_compressor_list); + spin_unlock(&jffs2_compressor_list_lock); + return 0; +} - case JFFS2_COMPR_RUBINMIPS: -#if 0 /* Disabled 23/9/1 */ - jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); -#else - printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n"); #endif - break; - case JFFS2_COMPR_DYNRUBIN: -#if 1 /* Phase this one out */ - jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) +{ + if (orig != comprbuf) + kfree(comprbuf); +} + +int jffs2_compressors_init(void) +{ +/* Registering compressors */ +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_init(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_init(); +#endif +#ifdef CONFIG_JFFS2_RUBIN + jffs2_rubinmips_init(); + jffs2_dynrubin_init(); +#endif +#ifdef CONFIG_JFFS2_LZARI + jffs2_lzari_init(); +#endif +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); +#endif +/* Setting default compression mode */ +#ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; + D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) #else - printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n"); +#ifdef CONFIG_JFFS2_CMODE_SIZE + jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; + D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) +#else + D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) #endif - break; +#endif + return 0; +} - default: - printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype); - return -EIO; - } - return 0; +int jffs2_compressors_exit(void) +{ +/* Unregistering compressors */ +#ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); +#endif +#ifdef CONFIG_JFFS2_LZARI + jffs2_lzari_exit(); +#endif +#ifdef CONFIG_JFFS2_RUBIN + jffs2_dynrubin_exit(); + jffs2_rubinmips_exit(); +#endif +#ifdef CONFIG_JFFS2_RTIME + jffs2_rtime_exit(); +#endif +#ifdef CONFIG_JFFS2_ZLIB + jffs2_zlib_exit(); +#endif + return 0; } diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index a72ce670c..3bfe27773 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr_rtime.c,v 1.14 2004/06/23 16:34:40 havasi Exp $ * * * Very simple lz77-ish encoder. @@ -25,10 +25,12 @@ #include #include #include +#include +#include "compr.h" /* _compress returns the compressed size, -1 if bigger */ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { short positions[256]; int outpos = 0; @@ -67,8 +69,8 @@ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, } -void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) +int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) { short positions[256]; int outpos = 0; @@ -98,7 +100,29 @@ void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, outpos+=repeat; } } - } + } + return 0; } +static struct jffs2_compressor jffs2_rtime_comp = { + .priority = JFFS2_RTIME_PRIORITY, + .name = "rtime", + .compr = JFFS2_COMPR_RTIME, + .compress = &jffs2_rtime_compress, + .decompress = &jffs2_rtime_decompress, +#ifdef JFFS2_RTIME_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rtime_init(void) +{ + return jffs2_register_compressor(&jffs2_rtime_comp); +} +void jffs2_rtime_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rtime_comp); +} diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index be8e8d501..450d66241 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -7,17 +7,17 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: compr_rubin.c,v 1.20 2004/06/23 16:34:40 havasi Exp $ * */ #include #include +#include #include "compr_rubin.h" #include "histo_mips.h" - - +#include "compr.h" static void init_rubin(struct rubin_state *rs, int div, int *bits) { @@ -223,13 +223,13 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, #if 0 /* _compress returns the compressed size, -1 if bigger */ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); } #endif int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { int bits[8]; unsigned char histo[256]; @@ -306,14 +306,15 @@ static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata } -void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen) +int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, void *model) { rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + return 0; } -void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t sourcelen, uint32_t dstlen) +int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t sourcelen, uint32_t dstlen, void *model) { int bits[8]; int c; @@ -322,4 +323,51 @@ void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, bits[c] = data_in[c]; rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); + return 0; +} + +static struct jffs2_compressor jffs2_rubinmips_comp = { + .priority = JFFS2_RUBINMIPS_PRIORITY, + .name = "rubinmips", + .compr = JFFS2_COMPR_DYNRUBIN, + .compress = NULL, /*&jffs2_rubinmips_compress,*/ + .decompress = &jffs2_rubinmips_decompress, +#ifdef JFFS2_RUBINMIPS_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_rubinmips_init(void) +{ + return jffs2_register_compressor(&jffs2_rubinmips_comp); +} + +void jffs2_rubinmips_exit(void) +{ + jffs2_unregister_compressor(&jffs2_rubinmips_comp); +} + +static struct jffs2_compressor jffs2_dynrubin_comp = { + .priority = JFFS2_DYNRUBIN_PRIORITY, + .name = "dynrubin", + .compr = JFFS2_COMPR_RUBINMIPS, + .compress = jffs2_dynrubin_compress, + .decompress = &jffs2_dynrubin_decompress, +#ifdef JFFS2_DYNRUBIN_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_dynrubin_init(void) +{ + return jffs2_register_compressor(&jffs2_dynrubin_comp); +} + +void jffs2_dynrubin_exit(void) +{ + jffs2_unregister_compressor(&jffs2_dynrubin_comp); } diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index c05e65f4c..f3b0bc97e 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ * */ @@ -17,13 +17,12 @@ #include #include -#include -#include #include #include #include #include #include "nodelist.h" +#include "compr.h" /* Plan: call deflate() with avail_in == *sourcelen, avail_out = *dstlen - 12 and flush == Z_FINISH. @@ -39,7 +38,10 @@ static DECLARE_MUTEX(inflate_sem); static z_stream inf_strm, def_strm; #ifdef __KERNEL__ /* Linux-only */ -int __init jffs2_zlib_init(void) +#include +#include + +static int __init alloc_workspaces(void) { def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); if (!def_strm.workspace) { @@ -57,15 +59,18 @@ int __init jffs2_zlib_init(void) return 0; } -void jffs2_zlib_exit(void) +static void free_workspaces(void) { vfree(def_strm.workspace); vfree(inf_strm.workspace); } +#else +#define alloc_workspaces() (0) +#define free_workspaces() do { } while(0) #endif /* __KERNEL__ */ int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *sourcelen, uint32_t *dstlen) + uint32_t *sourcelen, uint32_t *dstlen, void *model) { int ret; @@ -130,8 +135,8 @@ int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, return ret; } -void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t srclen, uint32_t destlen) +int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, + uint32_t srclen, uint32_t destlen, void *model) { int ret; int wbits = MAX_WBITS; @@ -165,7 +170,7 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed\n"); up(&inflate_sem); - return; + return 1; } while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) @@ -175,4 +180,39 @@ void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, } zlib_inflateEnd(&inf_strm); up(&inflate_sem); + return 0; +} + +static struct jffs2_compressor jffs2_zlib_comp = { + .priority = JFFS2_ZLIB_PRIORITY, + .name = "zlib", + .compr = JFFS2_COMPR_ZLIB, + .compress = &jffs2_zlib_compress, + .decompress = &jffs2_zlib_decompress, +#ifdef JFFS2_ZLIB_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int __init jffs2_zlib_init(void) +{ + int ret; + + ret = alloc_workspaces(); + if (ret) + return ret; + + ret = jffs2_register_compressor(&jffs2_zlib_comp); + if (ret) + free_workspaces(); + + return ret; +} + +void jffs2_zlib_exit(void) +{ + jffs2_unregister_compressor(&jffs2_zlib_comp); + free_workspaces(); } diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index f7a940962..4856f369c 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.53 2003/10/08 17:22:54 dwmw2 Exp $ + * $Id: erase.c,v 1.60 2004/06/30 17:26:15 dbrown Exp $ * */ @@ -28,7 +28,7 @@ struct erase_priv_struct { #ifndef __ECOS static void jffs2_erase_callback(struct erase_info *); #endif -static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); @@ -36,6 +36,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; + uint32_t bad_offset; #ifdef __ECOS ret = jffs2_flash_erase(c, jeb); if (!ret) { @@ -65,18 +66,16 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) instr->len = c->sector_size; instr->callback = jffs2_erase_callback; instr->priv = (unsigned long)(&instr[1]); + instr->fail_addr = 0xffffffff; ((struct erase_priv_struct *)instr->priv)->jeb = jeb; ((struct erase_priv_struct *)instr->priv)->c = c; - /* NAND , read out the fail counter, if possible */ - if (!jffs2_can_mark_obsolete(c)) - jffs2_nand_read_failcnt(c,jeb); - ret = c->mtd->erase(c->mtd, instr); if (!ret) return; + bad_offset = instr->fail_addr; kfree(instr); #endif /* __ECOS */ @@ -98,10 +97,10 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); - jffs2_erase_failed(c, jeb); + jffs2_erase_failed(c, jeb, bad_offset); } -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; @@ -118,6 +117,11 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) spin_unlock(&c->erase_completion_lock); jffs2_mark_erased_block(c, jeb); + if (!--count) { + D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); + goto done; + } + } else if (!list_empty(&c->erase_pending_list)) { jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); @@ -144,6 +148,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) } spin_unlock(&c->erase_completion_lock); + done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); @@ -160,16 +165,34 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo jffs2_erase_pending_trigger(c); } -static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { - spin_lock(&c->erase_completion_lock); - c->erasing_size -= c->sector_size; - c->bad_size += c->sector_size; - list_del(&jeb->list); - list_add(&jeb->list, &c->bad_list); - c->nr_erasing_blocks--; - spin_unlock(&c->erase_completion_lock); - wake_up(&c->erase_wait); + /* For NAND, if the failure did not occur at the device level for a + specific physical page, don't bother updating the bad block table. */ + if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) { + /* We had a device-level failure to erase. Let's see if we've + failed too many times. */ + if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { + /* We'd like to give this block another try. */ + spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; + c->dirty_size += c->sector_size; + jeb->dirty_size = c->sector_size; + spin_unlock(&c->erase_completion_lock); + return; + } + } + + spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; + list_del(&jeb->list); + list_add(&jeb->list, &c->bad_list); + c->nr_erasing_blocks--; + spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); } #ifndef __ECOS @@ -179,7 +202,7 @@ static void jffs2_erase_callback(struct erase_info *instr) if(instr->state != MTD_ERASE_DONE) { printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); - jffs2_erase_failed(priv->c, priv->jeb); + jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); } else { jffs2_erase_succeeded(priv->c, priv->jeb); } @@ -277,17 +300,13 @@ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_erase jeb->last_node = NULL; } -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) -{ - OFNI_BS_2SFFJ(c)->s_dirt = 1; -} - static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_raw_node_ref *marker_ref = NULL; unsigned char *ebuf; size_t retlen; int ret; + uint32_t bad_offset; if (!jffs2_cleanmarker_oob(c)) { marker_ref = jffs2_alloc_raw_node_ref(); @@ -312,6 +331,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); int i; + bad_offset = ofs; + ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); if (ret) { printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); @@ -325,22 +346,21 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb /* It's OK. We know it's properly aligned */ unsigned long datum = *(unsigned long *)(&ebuf[i]); if (datum + 1) { - printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i); + bad_offset += i; + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset); bad: if (!jffs2_cleanmarker_oob(c)) jffs2_free_raw_node_ref(marker_ref); - else - jffs2_write_nand_badblock( c ,jeb ); kfree(ebuf); bad2: spin_lock(&c->erase_completion_lock); - c->erasing_size -= c->sector_size; - c->bad_size += c->sector_size; - - list_add_tail(&jeb->list, &c->bad_list); - c->nr_erasing_blocks--; + /* Stick it on a list (any list) so + erase_failed can take it right off + again. Silly, but shouldn't happen + often. */ + list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); - wake_up(&c->erase_wait); + jffs2_erase_failed(c, jeb, bad_offset); return; } } @@ -349,7 +369,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb } kfree(ebuf); } - + + bad_offset = jeb->offset; + /* Write the erase complete marker */ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); if (jffs2_cleanmarker_oob(c)) { @@ -370,29 +392,30 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb .totlen = cpu_to_je32(c->cleanmarker_size) }; - marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4)); + marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker); + /* We only write the header; the rest was noise or padding anyway */ + ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); goto bad2; } - if (retlen != je32_to_cpu(marker.totlen)) { + if (retlen != sizeof(marker)) { printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", - jeb->offset, je32_to_cpu(marker.totlen), retlen); + jeb->offset, sizeof(marker), retlen); goto bad2; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->totlen = PAD(je32_to_cpu(marker.totlen)); + marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; - jeb->free_size = c->sector_size - marker_ref->totlen; - jeb->used_size = marker_ref->totlen; + jeb->free_size = c->sector_size - c->cleanmarker_size; + jeb->used_size = c->cleanmarker_size; jeb->dirty_size = 0; jeb->wasted_size = 0; } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 8b3f8e7e5..4b141011f 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.96 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $ * */ @@ -72,7 +72,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) unsigned char *pg_buf; int ret; - D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); + D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); if (!PageLocked(pg)) PAGE_BUG(pg); @@ -93,7 +93,7 @@ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) flush_dcache_page(pg); kunmap(pg); - D1(printk(KERN_DEBUG "readpage finished\n")); + D2(printk(KERN_DEBUG "readpage finished\n")); return 0; } @@ -207,6 +207,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; + unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; @@ -240,9 +241,9 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); - ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start, - (pg->index << PAGE_CACHE_SHIFT) + start, - end - start, &writtenlen); + ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, + (pg->index << PAGE_CACHE_SHIFT) + aligned_start, + end - aligned_start, &writtenlen); kunmap(pg); @@ -250,6 +251,12 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi /* There was an error writing. */ SetPageError(pg); } + + /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ + if (writtenlen < (start&3)) + writtenlen = 0; + else + writtenlen -= (start&3); if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 1f9e5787f..76d7279c8 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.32 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $ * */ @@ -58,7 +58,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) return -ENOMEM; - ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); + ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { kfree(mdata); return ret; @@ -145,10 +145,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) old_metadata = f->metadata; - if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { - vmtruncate(inode, iattr->ia_size); + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size); - } if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); @@ -166,6 +164,14 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) up(&f->sem); jffs2_complete_reservation(c); + /* We have to do the vmtruncate() without f->sem held, since + some pages may be locked and waiting for it in readpage(). + We are protected from a simultaneous write() extending i_size + back past iattr->ia_size, because do_truncate() holds the + generic inode semaphore. */ + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) + vmtruncate(inode, iattr->ia_size); + return 0; } @@ -287,7 +293,7 @@ void jffs2_read_inode (struct inode *inode) case S_IFCHR: /* Read the device numbers from the media */ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); - if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { + if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); @@ -317,7 +323,7 @@ void jffs2_dirty_inode(struct inode *inode) struct iattr iattr; if (!(inode->i_state & I_DIRTY_DATASYNC)) { - D1(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); + D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); return; } @@ -343,9 +349,14 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) /* We stop if it was running, then restart if it needs to. This also catches the case where it was stopped and this - is just a remount to restart it */ - if (!(sb->s_flags & MS_RDONLY)) + is just a remount to restart it. + Flush the writebuffer, if neccecary, else we loose it */ + if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); + jffs2_flush_wbuf_pad(c); + up(&c->alloc_sem); + } if (!(*flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); @@ -365,7 +376,7 @@ void jffs2_write_super (struct super_block *sb) D1(printk(KERN_DEBUG "jffs2_write_super()\n")); jffs2_garbage_collect_trigger(c); - jffs2_erase_pending_blocks(c); + jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } @@ -437,17 +448,35 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) c = JFFS2_SB_INFO(sb); +#ifndef CONFIG_JFFS2_FS_NAND + if (c->mtd->type == MTD_NANDFLASH) { + printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); + return -EINVAL; + } +#endif + c->flash_size = c->mtd->size; /* * Check, if we have to concatenate physical blocks to larger virtual blocks * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation) */ - blocks = c->flash_size / c->mtd->erasesize; - while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) + c->sector_size = c->mtd->erasesize; + blocks = c->flash_size / c->sector_size; + while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { blocks >>= 1; + c->sector_size <<= 1; + } - c->sector_size = c->flash_size / blocks; + /* + * Size alignment check + */ + if ((c->sector_size * blocks) != c->flash_size) { + c->flash_size = c->sector_size * blocks; + printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n", + c->flash_size / 1024); + } + if (c->sector_size != c->mtd->erasesize) printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", c->mtd->erasesize / 1024, c->sector_size / 1024); @@ -460,12 +489,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) c->cleanmarker_size = sizeof(struct jffs2_unknown_node); /* Joern -- stick alignment for weird 8-byte-page flash here */ - if (jffs2_cleanmarker_oob(c)) { - /* NAND (or other bizarre) flash... do setup accordingly */ - ret = jffs2_nand_flash_setup(c); - if (ret) - return ret; - } + /* NAND (or other bizarre) flash... do setup accordingly */ + ret = jffs2_flash_setup(c); + if (ret) + return ret; c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); if (!c->inocache_list) { @@ -510,7 +537,126 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) out_inohash: kfree(c->inocache_list); out_wbuf: - jffs2_nand_flash_cleanup(c); + jffs2_flash_cleanup(c); + + return ret; +} + +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f) +{ + iput(OFNI_EDONI_2SFFJ(f)); +} + +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink) +{ + struct inode *inode; + struct jffs2_inode_cache *ic; + if (!nlink) { + /* The inode has zero nlink but its nodes weren't yet marked + obsolete. This has to be because we're still waiting for + the final (close() and) iput() to happen. + + There's a possibility that the final iput() could have + happened while we were contemplating. In order to ensure + that we don't cause a new read_inode() (which would fail) + for the inode in question, we use ilookup() in this case + instead of iget(). + + The nlink can't _become_ zero at this point because we're + holding the alloc_sem, and jffs2_do_unlink() would also + need that while decrementing nlink on any inode. + */ + inode = ilookup(OFNI_BS_2SFFJ(c), inum); + if (!inode) { + D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", + inum)); + + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + if (!ic) { + D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); + spin_unlock(&c->inocache_lock); + return NULL; + } + if (ic->state != INO_STATE_CHECKEDABSENT) { + /* Wait for progress. Don't just loop */ + D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", + ic->ino, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + } else { + spin_unlock(&c->inocache_lock); + } + + return NULL; + } + } else { + /* Inode has links to it still; they're not going away because + jffs2_do_unlink() would need the alloc_sem and we have it. + Just iget() it, and if read_inode() is necessary that's OK. + */ + inode = iget(OFNI_BS_2SFFJ(c), inum); + if (!inode) + return ERR_PTR(-ENOMEM); + } + if (is_bad_inode(inode)) { + printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", + inum, nlink); + /* NB. This will happen again. We need to do something appropriate here. */ + iput(inode); + return ERR_PTR(-EIO); + } + + return JFFS2_INODE_INFO(inode); +} + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv) +{ + struct inode *inode = OFNI_EDONI_2SFFJ(f); + struct page *pg; + + pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, + (void *)jffs2_do_readpage_unlock, inode); + if (IS_ERR(pg)) + return (void *)pg; + + *priv = (unsigned long)pg; + return kmap(pg); +} + +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *ptr, + unsigned long *priv) +{ + struct page *pg = (void *)*priv; + + kunmap(pg); + page_cache_release(pg); +} + +int jffs2_flash_setup(struct jffs2_sb_info *c) { + int ret = 0; + + if (jffs2_cleanmarker_oob(c)) { + /* NAND flash... do setup accordingly */ + ret = jffs2_nand_flash_setup(c); + if (ret) + return ret; + } + /* add setups for other bizarre flashes here... */ return ret; } + +void jffs2_flash_cleanup(struct jffs2_sb_info *c) { + + if (jffs2_cleanmarker_oob(c)) { + jffs2_nand_flash_cleanup(c); + } + + /* add cleanups for other bizarre flashes here... */ +} diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 1cc6bfc8f..f4d16ad9c 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.114 2003/10/09 13:53:35 dwmw2 Exp $ + * $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $ * */ @@ -19,6 +19,7 @@ #include #include #include "nodelist.h" +#include "compr.h" static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, @@ -36,7 +37,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, uint32_t start, uint32_t end); static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic); + struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) @@ -80,7 +81,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) nextlist = &c->erasable_list; } else { /* Eep. All were empty */ - printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); + D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n")); return NULL; } @@ -112,11 +113,11 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { + struct jffs2_inode_info *f; struct jffs2_inode_cache *ic; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; - uint32_t inum; - int ret = 0; + int ret = 0, inum, nlink; if (down_interruptible(&c->alloc_sem)) return -EINTR; @@ -186,7 +187,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ic->state = INO_STATE_CHECKING; spin_unlock(&c->inocache_lock); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino)); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ret = jffs2_do_crccheck_inode(c, ic); if (ret) @@ -204,7 +205,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) jeb = jffs2_find_gc_block(c); if (!jeb) { - printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"); + D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n")); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -EIO; @@ -223,17 +224,21 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); - jeb->gc_node = raw = raw->next_phys; - if (!raw) { + raw = raw->next_phys; + if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); + jeb->gc_node = raw; spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } } + jeb->gc_node = raw; + D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); + if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ /* FIXME: If it's something that needs to be copied, including something @@ -243,13 +248,17 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) up(&c->alloc_sem); goto eraseit_lock; } - - inum = jffs2_raw_ref_to_inum(raw); - D1(printk(KERN_DEBUG "Inode number is #%u\n", inum)); + + ic = jffs2_raw_ref_to_ic(raw); + + /* We need to hold the inocache. Either the erase_completion_lock or + the inocache_lock are sufficient; we trade down since the inocache_lock + causes less contention. */ + spin_lock(&c->inocache_lock); spin_unlock(&c->erase_completion_lock); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum)); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); /* Three possibilities: 1. Inode is already in-core. We must iget it and do proper @@ -259,11 +268,6 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 3. Inode is not in-core, node is not pristine. We must iget() and take the slow path. */ - spin_lock(&c->inocache_lock); - ic = jffs2_get_ino_cache(c, inum); - - /* This should never fail unless I'm particularly stupid. - So we don't check before dereferencing it */ switch(ic->state) { case INO_STATE_CHECKEDABSENT: @@ -275,28 +279,28 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) ic->state = INO_STATE_GC; else { D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", - inum)); + ic->ino)); } break; case INO_STATE_PRESENT: - case INO_STATE_UNCHECKED: - /* It's in-core or hasn't been checked. GC must iget() it. */ + /* It's in-core. GC must iget() it. */ break; + case INO_STATE_UNCHECKED: case INO_STATE_CHECKING: + case INO_STATE_GC: /* Should never happen. We should have finished checking - by the time we actually start doing any GC. */ + by the time we actually start doing any GC, and since + we're holding the alloc_sem, no other garbage collection + can happen. + */ + printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", + ic->ino, ic->state); + up(&c->alloc_sem); + spin_unlock(&c->inocache_lock); BUG(); - - case INO_STATE_GC: - /* Should never happen. We are holding the alloc_sem, - no other garbage collection can happen. Note that we - do depend on this later when deciding to do a simple - node copy */ - BUG(); - case INO_STATE_READING: /* Someone's currently trying to read it. We must wait for them to finish and then go through the full iget() route @@ -306,7 +310,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) up(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", - inum, ic->state)); + ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); /* And because we dropped the alloc_sem we must start again from the beginning. Ponder chance of livelock here -- we're returning success @@ -319,27 +323,50 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) A: Small enough that I don't care :) */ return 0; - } - spin_unlock(&c->inocache_lock); - /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the node intact, and we don't have to muck about with the fragtree etc. because we know it's not in-core. If it _was_ in-core, we go through all the iget() crap anyway */ if (ic->state == INO_STATE_GC) { + spin_unlock(&c->inocache_lock); + ret = jffs2_garbage_collect_pristine(c, ic, raw); - jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); - if (ret != -EBADFD) + spin_lock(&c->inocache_lock); + ic->state = INO_STATE_CHECKEDABSENT; + wake_up(&c->inocache_wq); + + if (ret != -EBADFD) { + spin_unlock(&c->inocache_lock); goto release_sem; + } - /* Fall through if it wanted us to */ + /* Fall through if it wanted us to, with inocache_lock held */ } - ret = jffs2_garbage_collect_live(c, jeb, raw, ic); + /* Prevent the fairly unlikely race where the gcblock is + entirely obsoleted by the final close of a file which had + the only valid nodes in the block, followed by erasure, + followed by freeing of the ic because the erased block(s) + held _all_ the nodes of that inode.... never been seen but + it's vaguely possible. */ + + inum = ic->ino; + nlink = ic->nlink; + spin_unlock(&c->inocache_lock); + + f = jffs2_gc_fetch_inode(c, inum, nlink); + if (IS_ERR(f)) + return PTR_ERR(f); + if (!f) + return 0; + + ret = jffs2_garbage_collect_live(c, jeb, raw, f); + + jffs2_gc_release_inode(c, f); release_sem: up(&c->alloc_sem); @@ -362,38 +389,35 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) return ret; } - static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic) + struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f) { - struct jffs2_inode_info *f; struct jffs2_node_frag *frag; struct jffs2_full_dnode *fn = NULL; struct jffs2_full_dirent *fd; uint32_t start = 0, end = 0, nrfrags = 0; - struct inode *inode; int ret = 0; - inode = iget(OFNI_BS_2SFFJ(c), ic->ino); - if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); - /* NB. This will happen again. We need to do something appropriate here. */ - up(&c->alloc_sem); - iput(inode); - return -EIO; - } - - f = JFFS2_INODE_INFO(inode); down(&f->sem); /* Now we have the lock for this inode. Check that it's still the one at the head of the list. */ + spin_lock(&c->erase_completion_lock); + + if (c->gcblock != jeb) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); + goto upnout; + } if (ref_obsolete(raw)) { + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); /* They'll call again */ goto upnout; } + spin_unlock(&c->erase_completion_lock); + /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ if (f->metadata && f->metadata->raw == raw) { fn = f->metadata; @@ -406,30 +430,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era if (frag->node && frag->node->raw == raw) { fn = frag->node; end = frag->ofs + frag->size; -#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ - if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) { - if (fn->frags > 1) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags); - mark_ref_normal(raw); - } - /* A hole node which isn't multi-page should be garbage-collected - and merged anyway, so we just check for the frag size here, - rather than mucking around with actually reading the node - and checking the compression type, which is the real way - to tell a hole node. */ - if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", - ref_offset(raw)); - mark_ref_normal(raw); - } - - if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) { - printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", - ref_offset(raw), frag->ofs, frag->ofs+frag->size); - mark_ref_normal(raw); - } - } -#endif if (!nrfrags++) start = frag->ofs; if (nrfrags == frag->node->frags) @@ -438,10 +438,10 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } if (fn) { if (ref_flags(raw) == REF_PRISTINE) { - ret = jffs2_garbage_collect_pristine(c, ic, raw); + ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); if (!ret) { /* Urgh. Return it sensibly. */ - frag->node->raw = ic->nodes; + frag->node->raw = f->inocache->nodes; } if (ret != -EBADFD) goto upnout; @@ -478,7 +478,6 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_era } upnout: up(&f->sem); - iput(inode); return ret; } @@ -492,30 +491,32 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, size_t retlen; int ret; uint32_t phys_ofs, alloclen; - uint32_t crc; + uint32_t crc, rawlen; int retried = 0; D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); + rawlen = ref_totlen(c, c->gcblock, raw); + /* Ask for a small amount of space (or the totlen if smaller) because we don't want to force wastage of the end of a block if splitting would work. */ - ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen), - &phys_ofs, &alloclen); + ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, + rawlen), &phys_ofs, &alloclen); if (ret) return ret; - if (alloclen < raw->totlen) { + if (alloclen < rawlen) { /* Doesn't fit untouched. We'll go the old route and split it */ return -EBADFD; } - node = kmalloc(raw->totlen, GFP_KERNEL); + node = kmalloc(rawlen, GFP_KERNEL); if (!node) return -ENOMEM; - ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node); - if (!ret && retlen != raw->totlen) + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); + if (!ret && retlen != rawlen) ret = -EIO; if (ret) goto out_node; @@ -578,14 +579,14 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, /* OK, all the CRCs are good; this node can just be copied as-is. */ retry: nraw->flash_offset = phys_ofs; - nraw->totlen = raw->totlen; + nraw->__totlen = rawlen; nraw->next_phys = NULL; - ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); + ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); - if (ret || (retlen != raw->totlen)) { + if (ret || (retlen != rawlen)) { printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", - raw->totlen, phys_ofs, ret, retlen); + rawlen, phys_ofs, ret, retlen); if (retlen) { /* Doesn't belong to any inode */ nraw->next_in_ino = NULL; @@ -609,7 +610,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); - ret = jffs2_reserve_space_gc(c, raw->totlen, &phys_ofs, &dummy); + ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); if (!ret) { D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); @@ -674,7 +675,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n"); return -ENOMEM; } - ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen); + ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen); if (ret) { printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret); kfree(mdata); @@ -779,13 +780,17 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct delete a 'real' dirent with the same name that's still somewhere else on the flash. */ if (!jffs2_can_mark_obsolete(c)) { - struct jffs2_raw_dirent rd; + struct jffs2_raw_dirent *rd; struct jffs2_raw_node_ref *raw; int ret; size_t retlen; int name_len = strlen(fd->name); uint32_t name_crc = crc32(0, fd->name, name_len); - char *namebuf = NULL; + uint32_t rawlen = ref_totlen(c, jeb, fd->raw); + + rd = kmalloc(rawlen, GFP_KERNEL); + if (!rd) + return -ENOMEM; /* Prevent the erase code from nicking the obsolete node refs while we're looking at them. I really don't like this extra lock but @@ -793,91 +798,66 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct down(&c->erase_free_sem); for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { + /* We only care about obsolete ones */ if (!(ref_obsolete(raw))) continue; + /* Any dirent with the same name is going to have the same length... */ + if (ref_totlen(c, NULL, raw) != rawlen) + continue; + /* Doesn't matter if there's one in the same erase block. We're going to delete it too at the same time. */ if ((raw->flash_offset & ~(c->sector_size-1)) == (fd->raw->flash_offset & ~(c->sector_size-1))) continue; - /* This is an obsolete node belonging to the same directory */ - ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd); + D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); + + /* This is an obsolete node belonging to the same directory, and it's of the right + length. We need to take a closer look...*/ + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw)); + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); /* If we can't read it, we don't need to continue to obsolete it. Continue */ continue; } - if (retlen != sizeof(struct jffs2_unknown_node)) { + if (retlen != rawlen) { printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", - retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw)); + retlen, rawlen, ref_offset(raw)); continue; } - if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT || - PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len)) - continue; - /* OK, it's a dirent node, it's the right length. We have to take a - closer look at it... */ - ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd); - if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw)); - /* If we can't read it, we don't need to continune to obsolete it. Continue */ + if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) continue; - } - if (retlen != sizeof(rd)) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n", - retlen, sizeof(rd), ref_offset(raw)); - continue; - } /* If the name CRC doesn't match, skip */ - if (je32_to_cpu(rd.name_crc) != name_crc) + if (je32_to_cpu(rd->name_crc) != name_crc) continue; + /* If the name length doesn't match, or it's another deletion dirent, skip */ - if (rd.nsize != name_len || !je32_to_cpu(rd.ino)) + if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) continue; /* OK, check the actual name now */ - if (!namebuf) { - namebuf = kmalloc(name_len + 1, GFP_KERNEL); - if (!namebuf) { - up(&c->erase_free_sem); - return -ENOMEM; - } - } - /* We read the extra byte before it so it's a word-aligned read */ - ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf); - if (ret) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw)); - /* If we can't read it, we don't need to continune to obsolete it. Continue */ - continue; - } - if (retlen != name_len+1) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n", - retlen, name_len+1, ref_offset(raw)); - continue; - } - if (memcmp(namebuf+1, fd->name, name_len)) + if (memcmp(rd->name, fd->name, name_len)) continue; /* OK. The name really does match. There really is still an older node on the flash which our deletion dirent obsoletes. So we have to write out a new deletion dirent to replace it */ - - if (namebuf) - kfree(namebuf); - up(&c->erase_free_sem); + + D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", + ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); + kfree(rd); + return jffs2_garbage_collect_dirent(c, jeb, f, fd); } up(&c->erase_free_sem); - - if (namebuf) - kfree(namebuf); + kfree(rd); } /* No need for it any more. Just mark it obsolete and remove it from the list */ @@ -1008,6 +988,9 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras je32_to_cpu(ri.ino)); }); + /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ + mark_ref_normal(new_fn->raw); + for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); frag; frag = frag_next(frag)) { if (frag->ofs > fn->size + fn->ofs) @@ -1042,10 +1025,9 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; - struct page *pg; + unsigned long pg; unsigned char *pg_ptr; - /* FIXME: */ struct inode *inode = OFNI_EDONI_2SFFJ(f); - + memset(&ri, 0, sizeof(ri)); D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", @@ -1184,23 +1166,18 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ -#ifdef __ECOS - pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); -#else - pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); -#endif - if (IS_ERR(pg)) { - printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); - return PTR_ERR(pg); + pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + + if (IS_ERR(pg_ptr)) { + printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); + return PTR_ERR(pg_ptr); } - pg_ptr = (char *)kmap(pg); - comprbuf = kmalloc(end - start, GFP_KERNEL); offset = start; while(offset < orig_end) { uint32_t datalen; uint32_t cdatalen; - char comprtype = JFFS2_COMPR_NONE; + uint16_t comprtype = JFFS2_COMPR_NONE; ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); @@ -1214,14 +1191,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); - if (comprbuf) { - comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen); - } - if (comprtype) { - writebuf = comprbuf; - } else { - datalen = cdatalen; - } + comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen); + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); @@ -1239,11 +1210,14 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ri.offset = cpu_to_je32(offset); ri.csize = cpu_to_je32(cdatalen); ri.dsize = cpu_to_je32(datalen); - ri.compr = comprtype; + ri.compr = comprtype & 0xff; + ri.usercompr = (comprtype >> 8) & 0xff; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); - ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen)); + ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, ALLOC_GC); + new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + + jffs2_free_comprbuf(comprbuf, writebuf); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); @@ -1258,12 +1232,8 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era f->metadata = NULL; } } - if (comprbuf) kfree(comprbuf); - kunmap(pg); - /* XXX: Does the page get freed automatically? */ - /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */ - page_cache_release(pg); + jffs2_gc_release_page(c, pg_ptr, &pg); return ret; } diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 1c0bebc6b..1f8fb486c 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -1,13 +1,13 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001-2003 Red Hat, Inc. * * Created by David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.7 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ * */ @@ -18,6 +18,6 @@ int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, { /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which will include compression support etc. */ - return -EINVAL; + return -ENOTTY; } diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index d81680fe6..b0bbe8a01 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.25 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ * */ diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 6c94c3699..b359f8a14 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.80 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ * */ @@ -58,7 +58,7 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new /* Put a new tmp_dnode_info into the list, keeping the list in order of increasing version */ -void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) +static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) { struct jffs2_tmp_dnode_info **prev = list; @@ -133,7 +133,9 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode cond_resched(); /* FIXME: point() */ - err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node); + err = jffs2_flash_read(c, (ref_offset(ref)), + min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), + &retlen, (void *)&node); if (err) { printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); goto free_out; @@ -141,7 +143,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* Check we've managed to read at least the common node header */ - if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) { + if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; @@ -246,7 +248,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* If we've never checked the CRCs on this node, check them now. */ if (ref_flags(ref) == REF_UNCHECKED) { - uint32_t crc; + uint32_t crc, len; struct jffs2_eraseblock *jeb; crc = crc32(0, &node, sizeof(node.i)-8); @@ -321,10 +323,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* Mark the node as having been checked and fix the accounting accordingly */ spin_lock(&c->erase_completion_lock); jeb = &c->blocks[ref->flash_offset / c->sector_size]; - jeb->used_size += ref->totlen; - jeb->unchecked_size -= ref->totlen; - c->used_size += ref->totlen; - c->unchecked_size -= ref->totlen; + len = ref_totlen(c, jeb, ref); + + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; /* If node covers at least a whole page, or if it starts at the beginning of a page and runs to the end of the file, or if @@ -377,6 +381,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode default: if (ref_flags(ref) == REF_UNCHECKED) { struct jffs2_eraseblock *jeb; + uint32_t len; printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); @@ -384,10 +389,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* Mark the node as having been checked and fix the accounting accordingly */ spin_lock(&c->erase_completion_lock); jeb = &c->blocks[ref->flash_offset / c->sector_size]; - jeb->used_size += ref->totlen; - jeb->unchecked_size -= ref->totlen; - c->used_size += ref->totlen; - c->unchecked_size -= ref->totlen; + len = ref_totlen(c, jeb, ref); + + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; mark_ref_normal(ref); spin_unlock(&c->erase_completion_lock); @@ -631,6 +638,8 @@ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) jffs2_free_node_frag(frag); frag = parent; + + cond_resched(); } } diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index d36f29aa5..cea0c1390 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.104 2003/10/08 11:45:11 dwmw2 Exp $ + * $Id: nodelist.h,v 1.119 2004/05/26 12:28:12 gleixner Exp $ * */ @@ -44,6 +44,39 @@ #define D2(x) #endif +#define JFFS2_NATIVE_ENDIAN + +/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from + whatever OS we're actually running on here too. */ + +#if defined(JFFS2_NATIVE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){x}) +#define cpu_to_je32(x) ((jint32_t){x}) +#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) + +#define je16_to_cpu(x) ((x).v16) +#define je32_to_cpu(x) ((x).v32) +#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) +#elif defined(JFFS2_BIG_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (be16_to_cpu(x.v16)) +#define je32_to_cpu(x) (be32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) +#elif defined(JFFS2_LITTLE_ENDIAN) +#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) +#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) +#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) + +#define je16_to_cpu(x) (le16_to_cpu(x.v16)) +#define je32_to_cpu(x) (le32_to_cpu(x.v32)) +#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) +#else +#error wibble +#endif + /* This is all we need to keep in-core for each raw node during normal operation. As and when we do read_inode on a particular inode, we can @@ -59,13 +92,12 @@ struct jffs2_raw_node_ref word so you know when you've got there :) */ struct jffs2_raw_node_ref *next_phys; uint32_t flash_offset; - uint32_t totlen; - + uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ +}; + /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits - to play with. So we set the least significant bit to 1 to - signify that the node is obsoleted by later nodes. - */ + to play with, which indicate the node's status; see below: */ #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ #define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ #define REF_PRISTINE 2 /* Completely clean. GC without looking */ @@ -74,7 +106,6 @@ struct jffs2_raw_node_ref #define ref_offset(ref) ((ref)->flash_offset & ~3) #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) -}; /* Used for keeping track of deletion nodes &c, which can only be marked @@ -246,9 +277,9 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) BUG(); \ } \ if (ref_flags(ref2) == REF_UNCHECKED) \ - my_unchecked_size += ref2->totlen; \ + my_unchecked_size += ref_totlen(c, jeb, ref2); \ else if (!ref_obsolete(ref2)) \ - my_used_size += ref2->totlen; \ + my_used_size += ref_totlen(c, jeb, ref2); \ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ @@ -268,6 +299,57 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) } \ } while(0) +/* Calculate totlen from surrounding nodes or eraseblock */ +static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ref_end; + + if (ref->next_phys) + ref_end = ref_offset(ref->next_phys); + else { + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + + /* Last node in block. Use free_space */ + BUG_ON(ref != jeb->last_node); + ref_end = jeb->offset + c->sector_size - jeb->free_size; + } + return ref_end - ref_offset(ref); +} + +static inline uint32_t ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ret; + + D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { + printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", + jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); + BUG(); + }) + +#if 1 + ret = ref->__totlen; +#else + /* This doesn't actually work yet */ + ret = __ref_totlen(c, jeb, ref); + if (ret != ref->__totlen) { + printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", + ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, + ret, ref->__totlen); + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + paranoia_failed_dump(jeb); + BUG(); + } +#endif + return ret; +} + + #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ @@ -281,13 +363,13 @@ static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) #define PAD(x) (((x)+3)&~3) -static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw) +static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) { while(raw->next_in_ino) { raw = raw->next_in_ino; } - return ((struct jffs2_inode_cache *)raw)->ino; + return ((struct jffs2_inode_cache *)raw); } static inline struct jffs2_node_frag *frag_first(struct rb_root *root) @@ -311,7 +393,6 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root) /* nodelist.c */ D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); -void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, @@ -330,6 +411,7 @@ struct rb_node *rb_prev(struct rb_node *); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); /* nodemgmt.c */ +int jffs2_thread_should_wake(struct jffs2_sb_info *c); int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio); int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); @@ -383,18 +465,13 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *); int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); /* read.c */ -int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); +int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_full_dnode *fd, unsigned char *buf, + int ofs, int len); int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len); char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); - -/* compr.c */ -unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, - uint32_t *datalen, uint32_t *cdatalen); -int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, - unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); - /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); @@ -404,8 +481,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c); -void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); +void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); #ifdef CONFIG_JFFS2_FS_NAND /* wbuf.c */ @@ -413,11 +489,6 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #endif -/* compr_zlib.c */ -int jffs2_zlib_init(void); -void jffs2_zlib_exit(void); - #endif /* __JFFS2_NODELIST_H__ */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 980de2e22..5043d1a6f 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ * */ @@ -209,8 +209,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui if (list_empty(&c->free_list)) { - DECLARE_WAITQUEUE(wait, current); - if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; @@ -243,30 +241,12 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } - /* Make sure this can't deadlock. Someone has to start the erases - of erase_pending blocks */ -#ifdef __ECOS - /* In eCos, we don't have a handy kernel thread doing the erases for - us. We do them ourselves right now. */ - jffs2_erase_pending_blocks(c); -#else - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&c->erase_wait, &wait); - D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", - c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no", - list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no")); - if (!list_empty(&c->erase_pending_list)) { - D1(printk(KERN_DEBUG "Triggering pending erases\n")); - jffs2_erase_pending_trigger(c); - } + spin_unlock(&c->erase_completion_lock); - schedule(); - remove_wait_queue(&c->erase_wait, &wait); + /* Don't wait for it; just erase one right now */ + jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); - if (signal_pending(current)) { - return -EINTR; - } -#endif + /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ @@ -321,9 +301,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) { struct jffs2_eraseblock *jeb; - uint32_t len = new->totlen; + uint32_t len; jeb = &c->blocks[new->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, new); + D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); #if 1 if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { @@ -420,31 +402,31 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { - D1(if (unlikely(jeb->unchecked_size < ref->totlen)) { + D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", - ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen)); - jeb->unchecked_size -= ref->totlen; - c->unchecked_size -= ref->totlen; + D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); + jeb->unchecked_size -= ref_totlen(c, jeb, ref); + c->unchecked_size -= ref_totlen(c, jeb, ref); } else { - D1(if (unlikely(jeb->used_size < ref->totlen)) { + D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", - ref->totlen, blocknr, ref->flash_offset, jeb->used_size); + ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen)); - jeb->used_size -= ref->totlen; - c->used_size -= ref->totlen; + D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); + jeb->used_size -= ref_totlen(c, jeb, ref); + c->used_size -= ref_totlen(c, jeb, ref); } // Take care, that wasted size is taken into concern - if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) { + if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { D1(printk("Dirtying\n")); - addedsize = ref->totlen; - jeb->dirty_size += ref->totlen; - c->dirty_size += ref->totlen; + addedsize = ref_totlen(c, jeb, ref); + jeb->dirty_size += ref_totlen(c, jeb, ref); + c->dirty_size += ref_totlen(c, jeb, ref); /* Convert wasted space to dirty, if not a bad block */ if (jeb->wasted_size) { @@ -465,8 +447,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } else { D1(printk("Wasting\n")); addedsize = 0; - jeb->wasted_size += ref->totlen; - c->wasted_size += ref->totlen; + jeb->wasted_size += ref_totlen(c, jeb, ref); + c->wasted_size += ref_totlen(c, jeb, ref); } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; @@ -497,30 +479,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref if (jffs2_wbuf_dirty(c)) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); -#if 0 /* This check was added to allow us to find places where we added nodes to the lists - after dropping the alloc_sem, and it did that just fine. But it also caused us to - lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise - have needed to. So I suspect it's outlived its usefulness. Thomas? */ - - /* We've changed the rules slightly. After - writing a node you now mustn't drop the - alloc_sem before you've finished all the - list management - this is so that when we - get here, we know that no other nodes have - been written, and the above check on wbuf - is valid - wbuf_len is nonzero IFF the node - which obsoletes this node is still in the - wbuf. - - So we BUG() if that new rule is broken, to - make sure we catch it and fix it. - */ - if (!down_trylock(&c->alloc_sem)) { - up(&c->alloc_sem); - printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n"); - BUG(); - } -#endif } else { if (jiffies & 127) { /* Most of the time, we just erase it immediately. Otherwise we @@ -572,12 +530,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); return; } - if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) { - printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen); + if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { + printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); return; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { - D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype))); + D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); return; } /* XXX FIXME: This is ugly now */ @@ -750,3 +708,34 @@ void jffs2_dump_block_lists(struct jffs2_sb_info *c) } } #endif /* CONFIG_JFFS2_FS_DEBUG */ + +int jffs2_thread_should_wake(struct jffs2_sb_info *c) +{ + int ret = 0; + uint32_t dirty; + + if (c->unchecked_size) { + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", + c->unchecked_size, c->checked_ino)); + return 1; + } + + /* dirty_size contains blocks on erase_pending_list + * those blocks are counted in c->nr_erasing_blocks. + * If one block is actually erased, it is not longer counted as dirty_space + * but it is counted in c->nr_erasing_blocks, so we add it and subtract it + * with c->nr_erasing_blocks * c->sector_size again. + * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks + * This helps us to force gc and pick eventually a clean block to spread the load. + */ + dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; + + if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && + (dirty > c->nospc_dirty_size)) + ret = 1; + + D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", + c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); + + return ret; +} diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 11f3a5a35..9a7e01ca2 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.37 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $ * */ @@ -23,6 +23,9 @@ #define kstatfs statfs #endif +struct kstatfs; +struct kvec; + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) @@ -69,14 +72,6 @@ #define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) #endif -/* Hmmm. P'raps generic code should only ever see versions of signal - functions which do the locking automatically? */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) && !defined(__rh_config_h__) -#define current_sig_lock current->sigmask_lock -#else -#define current_sig_lock current->sighand->siglock -#endif - #define sleep_on_spinunlock(wq, s) \ do { \ DECLARE_WAITQUEUE(__wait, current); \ @@ -113,8 +108,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; }) #define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; }) -#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) -#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0) +#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1) #define jffs2_nand_flash_setup(c) (0) #define jffs2_nand_flash_cleanup(c) do {} while(0) #define jffs2_wbuf_dirty(c) (0) @@ -130,9 +124,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) -struct kstatfs; -struct kvec; /* wbuf.c */ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); @@ -140,13 +132,19 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); #endif /* NAND */ +/* erase.c */ +static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +{ + OFNI_BS_2SFFJ(c)->s_dirt = 1; +} + /* background.c */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); @@ -184,13 +182,26 @@ int jffs2_statfs (struct super_block *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f); +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink); + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv); +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *pg, + unsigned long *priv); +int jffs2_flash_setup(struct jffs2_sb_info *c); +void jffs2_flash_cleanup(struct jffs2_sb_info *c); + /* writev.c */ int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); -/* super.c */ - #endif /* __JFFS2_OS_LINUX_H__ */ diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index adeeeb19a..37375950f 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ * */ @@ -18,8 +18,11 @@ #include #include #include "nodelist.h" +#include "compr.h" -int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) +int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_full_dnode *fd, unsigned char *buf, + int ofs, int len) { struct jffs2_raw_inode *ri; size_t readlen; @@ -127,7 +130,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig if (ri->compr != JFFS2_COMPR_NONE) { D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); - ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); + ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); if (ret) { printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; @@ -195,7 +198,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", frag->ofs+fragofs, frag->ofs+fragofs+readlen, ref_offset(frag->node->raw), ref_flags(frag->node->raw))); - ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); + ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); D2(printk(KERN_DEBUG "node read done\n")); if (ret) { D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); @@ -231,7 +234,7 @@ char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) } buf[f->metadata->size]=0; - ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size); + ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size); up(&f->sem); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index ea4bbf7f2..54a5d1ac3 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.107 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ * */ @@ -56,6 +56,66 @@ void jffs2_print_frag_list(struct jffs2_inode_info *f) printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); } } + +static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) +{ + struct jffs2_node_frag *frag; + int bitched = 0; + + for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + + struct jffs2_full_dnode *fn = frag->node; + if (!fn || !fn->raw) + continue; + + if (ref_flags(fn->raw) == REF_PRISTINE) { + + if (fn->frags > 1) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags); + bitched = 1; + } + /* A hole node which isn't multi-page should be garbage-collected + and merged anyway, so we just check for the frag size here, + rather than mucking around with actually reading the node + and checking the compression type, which is the real way + to tell a hole node. */ + if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", + ref_offset(fn->raw)); + bitched = 1; + } + + if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { + printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", + ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); + bitched = 1; + } + } + } + + if (bitched) { + struct jffs2_node_frag *thisfrag; + + printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino); + thisfrag = frag_first(&f->fragtree); + while (thisfrag) { + if (!thisfrag->node) { + printk("Frag @0x%x-0x%x; node-less hole\n", + thisfrag->ofs, thisfrag->size + thisfrag->ofs); + } else if (!thisfrag->node->raw) { + printk("Frag @0x%x-0x%x; raw-less hole\n", + thisfrag->ofs, thisfrag->size + thisfrag->ofs); + } else { + printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n", + thisfrag->ofs, thisfrag->size + thisfrag->ofs, + ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw), + thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size); + } + thisfrag = frag_next(thisfrag); + } + } + return bitched; +} #endif /* D1 */ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) @@ -130,6 +190,11 @@ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_in mark_ref_normal(next->node->raw); } } + D2(if (jffs2_sanitycheck_fragtree(f)) { + printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n", + fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); + return 0; + }) D2(jffs2_print_frag_list(f)); return 0; } @@ -384,6 +449,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, } } spin_unlock(&c->inocache_lock); + if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); @@ -460,7 +526,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, fn = tn->fn; if (f->metadata) { - if (tn->version > mdata_ver) { + if (likely(tn->version >= mdata_ver)) { D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw))); jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); @@ -468,10 +534,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, mdata_ver = 0; } else { - D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n", - ref_offset(f->metadata->raw), tn->version, mdata_ver)); + /* This should never happen. */ + printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", + ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); + /* Fill in latest_node from the metadata, not this one we're about to free... */ + fn = f->metadata; goto next_tn; } } @@ -488,6 +557,8 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, tn_list = tn->next; jffs2_free_tmp_dnode_info(tn); } + D1(jffs2_sanitycheck_fragtree(f)); + if (!fn) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { @@ -594,24 +665,10 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) { struct jffs2_full_dirent *fd, *fds; - /* I don't think we care about the potential race due to reading this - without f->sem. It can never get undeleted. */ - int deleted = f->inocache && !f->inocache->nlink; - - /* If it's a deleted inode, grab the alloc_sem. This prevents - jffs2_garbage_collect_pass() from deciding that it wants to - garbage collect one of the nodes we're just about to mark - obsolete -- by the time we drop alloc_sem and return, all - the nodes are marked obsolete, and jffs2_g_c_pass() won't - call iget() for the inode in question. - - We also used to do this to keep the temporary BUG() in - jffs2_mark_node_obsolete() from triggering. - */ - if(deleted) - down(&c->alloc_sem); + int deleted; down(&f->sem); + deleted = f->inocache && !f->inocache->nlink; if (f->metadata) { if (deleted) @@ -633,7 +690,4 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); up(&f->sem); - - if(deleted) - up(&c->alloc_sem); } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 6e59cad2d..67a1d1bce 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.104 2003/10/11 14:52:48 dwmw2 Exp $ + * $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $ * */ #include @@ -285,8 +285,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; - int wasempty = 0; - uint32_t empty_start = 0; #ifdef CONFIG_JFFS2_FS_NAND int cleanmarkerfound = 0; #endif @@ -339,8 +337,6 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; - case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */ - case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } @@ -359,6 +355,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo noise = 10; +scan_more: while(ofs < jeb->offset + c->sector_size) { D1(ACCT_PARANOIA_CHECK(jeb)); @@ -398,42 +395,52 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { - uint32_t inbuf_ofs = ofs - buf_ofs + 4; - uint32_t scanend; + uint32_t inbuf_ofs; + uint32_t empty_start; empty_start = ofs; ofs += 4; - /* If scanning empty space after only a cleanmarker, don't - bother scanning the whole block */ - if (unlikely(empty_start == jeb->offset + c->cleanmarker_size && - jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len)) - scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs; - else - scanend = buf_len; - D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); - while (inbuf_ofs < scanend) { - if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) - goto emptyends; + more_empty: + inbuf_ofs = ofs - buf_ofs; + while (inbuf_ofs < buf_len) { + if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { + printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", + empty_start, ofs); + DIRTY_SPACE(ofs-empty_start); + goto scan_more; + } inbuf_ofs+=4; ofs += 4; } /* Ran off end. */ - D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); + D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); - if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size) + /* If we're only checking the beginning of a block with a cleanmarker, + bail now */ + if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && + c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) { + D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE)); return BLK_STATE_CLEANMARKER; - wasempty = 1; - continue; - } else if (wasempty) { - emptyends: - printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); - DIRTY_SPACE(ofs-empty_start); - wasempty = 0; - continue; + } + + /* See how much more there is to read in this eraseblock... */ + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + if (!buf_len) { + /* No more to read. Break out of main loop without marking + this range of empty space as dirty (because it's not) */ + D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", + empty_start)); + break; + } + D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + goto more_empty; } if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { @@ -554,7 +561,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = ofs | REF_NORMAL; - marker_ref->totlen = c->cleanmarker_size; + marker_ref->__totlen = c->cleanmarker_size; jeb->first_node = jeb->last_node = marker_ref; USED_SPACE(PAD(c->cleanmarker_size)); @@ -610,7 +617,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || jeb->first_node->next_in_ino) ) + && (!jeb->first_node || !jeb->first_node->next_in_ino) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ @@ -634,6 +641,9 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info if (ic) return ic; + if (ino > c->highest_ino) + c->highest_ino = ino; + ic = jffs2_alloc_inode_cache(); if (!ic) { printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); @@ -645,7 +655,7 @@ static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info ic->nodes = (void *)ic; jffs2_add_ino_cache(c, ic); if (ino == 1) - ic->nlink=1; + ic->nlink = 1; return ic; } @@ -698,7 +708,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc /* Wheee. It worked */ raw->flash_offset = ofs | REF_UNCHECKED; - raw->totlen = PAD(je32_to_cpu(ri->totlen)); + raw->__totlen = PAD(je32_to_cpu(ri->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; @@ -775,7 +785,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo return -ENOMEM; } - raw->totlen = PAD(je32_to_cpu(rd->totlen)); + raw->__totlen = PAD(je32_to_cpu(rd->totlen)); raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index b657c55d5..23dad4785 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $ + * $Id: super.c,v 1.96 2004/07/13 08:57:30 dwmw2 Exp $ * */ @@ -24,6 +24,7 @@ #include #include #include +#include "compr.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); @@ -266,7 +267,7 @@ static void jffs2_put_super (struct super_block *sb) jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); kfree(c->blocks); - jffs2_nand_flash_cleanup(c); + jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) c->mtd->sync(c->mtd); @@ -294,7 +295,7 @@ static int __init init_jffs2_fs(void) int ret; printk(KERN_INFO "JFFS2 version 2.2." -#ifdef CONFIG_FS_JFFS2_NAND +#ifdef CONFIG_JFFS2_FS_NAND " (NAND)" #endif " (C) 2001-2003 Red Hat, Inc.\n"); @@ -307,15 +308,22 @@ static int __init init_jffs2_fs(void) printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); return -ENOMEM; } - ret = jffs2_zlib_init(); +#ifdef CONFIG_JFFS2_PROC + ret = jffs2_proc_init(); if (ret) { - printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); + printk(KERN_ERR "JFFS2 error: Failed to initialise proc interface\n"); + goto out; + } +#endif + ret = jffs2_compressors_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); goto out; } ret = jffs2_create_slab_caches(); if (ret) { printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); - goto out_zlib; + goto out_compressors; } ret = register_filesystem(&jffs2_fs_type); if (ret) { @@ -326,8 +334,11 @@ static int __init init_jffs2_fs(void) out_slab: jffs2_destroy_slab_caches(); - out_zlib: - jffs2_zlib_exit(); + out_compressors: + jffs2_compressors_exit(); +#ifdef CONFIG_JFFS2_PROC + jffs2_proc_exit(); +#endif out: return ret; } @@ -336,7 +347,10 @@ static void __exit exit_jffs2_fs(void) { unregister_filesystem(&jffs2_fs_type); jffs2_destroy_slab_caches(); - jffs2_zlib_exit(); + jffs2_compressors_exit(); +#ifdef CONFIG_JFFS2_PROC + jffs2_proc_exit(); +#endif kmem_cache_destroy(jffs2_inode_cachep); } diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index d0adaf6fd..958cafb90 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $ + * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $ * */ diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 956f98d8d..2470eef95 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -2,12 +2,14 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright (C) 2004 Thomas Gleixner * * Created by David Woodhouse + * Modified debugged and enhanced by Thomas Gleixner * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.53 2003/10/11 11:46:09 dwmw2 Exp $ + * $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $ * */ @@ -27,7 +29,7 @@ static unsigned char *brokenbuf; #endif /* max. erase failures before we mark a block bad */ -#define MAX_ERASE_FAILURES 5 +#define MAX_ERASE_FAILURES 2 /* two seconds timeout for timed wbuf-flushing */ #define WBUF_FLUSH_TIMEOUT 2 * HZ @@ -179,10 +181,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) first_raw = &jeb->first_node; while (*first_raw && (ref_obsolete(*first_raw) || - (ref_offset(*first_raw) + (*first_raw)->totlen) < c->wbuf_ofs)) { + (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ref_offset(*first_raw), ref_flags(*first_raw), - (ref_offset(*first_raw) + (*first_raw)->totlen), + (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), c->wbuf_ofs)); first_raw = &(*first_raw)->next_phys; } @@ -195,13 +197,13 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) } start = ref_offset(*first_raw); - end = ref_offset(*first_raw) + (*first_raw)->totlen; + end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); /* Find the last node to be recovered */ raw = first_raw; while ((*raw)) { if (!ref_obsolete(*raw)) - end = ref_offset(*raw) + (*raw)->totlen; + end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); raw = &(*raw)->next_phys; } @@ -295,7 +297,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) return; raw2->flash_offset = ofs | REF_OBSOLETE; - raw2->totlen = (*first_raw)->totlen; + raw2->__totlen = ref_totlen(c, jeb, *first_raw); raw2->next_phys = NULL; raw2->next_in_ino = NULL; @@ -336,24 +338,26 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) raw = first_raw; while (*raw) { + uint32_t rawlen = ref_totlen(c, jeb, *raw); + D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", - (*raw)->totlen, ref_offset(*raw), ref_flags(*raw), ofs)); + rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); if (ref_obsolete(*raw)) { /* Shouldn't really happen much */ - new_jeb->dirty_size += (*raw)->totlen; - new_jeb->free_size -= (*raw)->totlen; - c->dirty_size += (*raw)->totlen; + new_jeb->dirty_size += rawlen; + new_jeb->free_size -= rawlen; + c->dirty_size += rawlen; } else { - new_jeb->used_size += (*raw)->totlen; - new_jeb->free_size -= (*raw)->totlen; - jeb->dirty_size += (*raw)->totlen; - jeb->used_size -= (*raw)->totlen; - c->dirty_size += (*raw)->totlen; + new_jeb->used_size += rawlen; + new_jeb->free_size -= rawlen; + jeb->dirty_size += rawlen; + jeb->used_size -= rawlen; + c->dirty_size += rawlen; } - c->free_size -= (*raw)->totlen; + c->free_size -= rawlen; (*raw)->flash_offset = ofs | ref_flags(*raw); - ofs += (*raw)->totlen; + ofs += rawlen; new_jeb->last_node = *raw; raw = &(*raw)->next_phys; @@ -422,6 +426,9 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); + } else { + /* Pad with JFFS2_DIRTY_BITMASK */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); } } /* else jffs2_flash_writev has actually filled in the rest of the @@ -454,31 +461,34 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) return ret; } - /* Adjusting free size of next block only, if it's called from fsync ! */ - if (pad == 2) { - D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n")); - spin_lock(&c->erase_completion_lock); - if (!c->nextblock) - BUG(); + spin_lock(&c->erase_completion_lock); + + /* Adjust free size of the block if we padded. */ + if (pad) { + struct jffs2_eraseblock *jeb; + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", + (jeb==c->nextblock)?"next":"", jeb->offset)); + /* wbuf_pagesize - wbuf_len is the amount of space that's to be padded. If there is less free space in the block than that, something screwed up */ - if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) { + if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) { printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", - c->nextblock->offset, c->nextblock->free_size); + jeb->offset, jeb->free_size); BUG(); } - c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len); + jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); c->free_size -= (c->wbuf_pagesize - c->wbuf_len); - c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len); + jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); - spin_unlock(&c->erase_completion_lock); } /* Stick any now-obsoleted blocks on the erase_pending_list */ - spin_lock(&c->erase_completion_lock); jffs2_refile_wbuf_blocks(c); jffs2_clear_wbuf_ino_list(c); spin_unlock(&c->erase_completion_lock); @@ -512,8 +522,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) old_wbuf_ofs = c->wbuf_ofs; old_wbuf_len = c->wbuf_len; - while (old_wbuf_len && - old_wbuf_ofs == c->wbuf_ofs) { + if (c->unchecked_size) { + /* GC won't make any progress for a while */ + D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); + ret = __jffs2_flush_wbuf(c, 2); + } else while (old_wbuf_len && + old_wbuf_ofs == c->wbuf_ofs) { up(&c->alloc_sem); @@ -835,9 +849,8 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb size_t retlen; int oob_size; - oob_size = c->mtd->oobsize; - /* allocate a buffer for all oob data in this sector */ + oob_size = c->mtd->oobsize; len = 4 * oob_size; buf = kmalloc(len, GFP_KERNEL); if (!buf) { @@ -861,35 +874,23 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb goto out; } - /* Special check for first two pages */ - for (page = 0; page < 2 * oob_size; page += oob_size) { - /* Check for bad block marker */ - if (buf[page+c->badblock_pos] != 0xff) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset)); - /* Return 2 for bad and 3 for failed block - bad goes to list_bad and failed to list_erase */ - ret = (!page) ? 2 : 3; + /* Special check for first page */ + for(i = 0; i < oob_size ; i++) { + /* Yeah, we know about the cleanmarker. */ + if (mode && i >= c->fsdata_pos && + i < c->fsdata_pos + c->fsdata_len) + continue; + + if (buf[i] != 0xFF) { + D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", + buf[page+i], page+i, jeb->offset)); + ret = 1; goto out; } - for(i = 0; i < oob_size ; i++) { - /* Yeah, we know about the cleanmarker. */ - if (mode && i >= c->fsdata_pos && - i < c->fsdata_pos+c->fsdata_len) - continue; - - if (buf[page+i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[page+i], page+i, jeb->offset)); - ret = 1; - goto out; - } - } - /* only the first page can contain a cleanmarker !*/ - mode = 0; - } + } /* we know, we are aligned :) */ - for (; page < len; page += sizeof(long)) { + for (page = oob_size; page < len; page += sizeof(long)) { unsigned long dat = *(unsigned long *)(&buf[page]); if(dat != -1) { ret = 1; @@ -912,7 +913,7 @@ out: int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; - unsigned char buf[32]; + unsigned char buf[2 * NAND_MAX_OOBSIZE]; unsigned char *p; int ret, i, cnt, retval = 0; size_t retlen, offset; @@ -923,6 +924,11 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo /* Loop through the physical blocks */ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { + /* Check first if the block is bad. */ + if (c->mtd->block_isbad (c->mtd, offset)) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); + return 2; + } /* * We read oob data from page 0 and 1 of the block. * page 0 contains cleanmarker and badblock info @@ -939,19 +945,6 @@ int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblo return -EIO; } - /* Check for bad block marker */ - if (buf[c->badblock_pos] != 0xff) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n", - jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos)); - return 2; - } - - /* Check for failure counter in the second page */ - if (buf[c->badblock_pos + oob_size] != 0xff) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size])); - return 3; - } - /* Check cleanmarker only on the first physical block */ if (!cnt) { n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); @@ -1002,136 +995,100 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc } /* - * We try to get the failure count of this block. - */ -int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - - unsigned char buf[16]; - int ret; - size_t retlen; - int oob_size; - - oob_size = c->mtd->oobsize; - - ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf); - - if (ret) { - D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - return ret; - } - - if (retlen < oob_size) { - D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); - return -EIO; - } - - jeb->bad_count = buf[c->badblock_pos]; - return 0; -} - -/* - * On NAND we try to mark this block bad. We try to write how often - * the block was erased and mark it finaly bad, if the count - * is > MAX_ERASE_FAILURES. We read this information on mount ! - * jeb->bad_count contains the count before this erase. + * On NAND we try to mark this block bad. If the block was erased more + * than MAX_ERASE_FAILURES we mark it finaly bad. * Don't care about failures. This block remains on the erase-pending * or badblock list as long as nobody manipulates the flash with * a bootloader or something like that. */ -int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) { - unsigned char buf = 0x0; int ret; - size_t retlen; /* if the count is < max, we try to write the counter to the 2nd page oob area */ - if( ++jeb->bad_count < MAX_ERASE_FAILURES) { - buf = (unsigned char)jeb->bad_count; - c->badblock_pos += c->mtd->oobblock; - } - - ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf); + if( ++jeb->bad_count < MAX_ERASE_FAILURES) + return 0; + + if (!c->mtd->block_markbad) + return 1; // What else can we do? + + D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset)); + ret = c->mtd->block_markbad(c->mtd, bad_offset); if (ret) { D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); return ret; } - if (retlen != 1) { - D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen)); - return ret; - } - return 0; + return 1; } -#define JFFS2_OOB_ECCPOS0 0 -#define JFFS2_OOB_ECCPOS1 1 -#define JFFS2_OOB_ECCPOS2 2 -#define JFFS2_OOB_ECCPOS3 3 -#define JFFS2_OOB_ECCPOS4 6 -#define JFFS2_OOB_ECCPOS5 7 - -#define NAND_JFFS2_OOB8_FSDAPOS 6 -#define NAND_JFFS2_OOB16_FSDAPOS 8 -#define NAND_JFFS2_OOB8_FSDALEN 2 #define NAND_JFFS2_OOB16_FSDALEN 8 -static struct nand_oobinfo jffs2_oobinfo_swecc = { - .useecc = 1, - .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, - JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} -}; - static struct nand_oobinfo jffs2_oobinfo_docecc = { - .useecc = 1, + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, .eccpos = {0,1,2,3,4,5} }; - -int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { + struct nand_oobinfo *oinfo = &c->mtd->oobinfo; + + /* Do this only, if we have an oob buffer */ + if (!c->mtd->oobsize) + return 0; + /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; - /* Initialise write buffer */ - c->wbuf_pagesize = c->mtd->oobblock; - c->wbuf_ofs = 0xFFFFFFFF; - - /* FIXME: If we had a generic way of describing the hardware's - use of OOB area, we could perhaps make this generic too. */ - switch(c->mtd->ecctype) { - case MTD_ECC_SW: - D1(printk(KERN_DEBUG "JFFS2 using software ECC\n")); - c->oobinfo = &jffs2_oobinfo_swecc; - if (c->mtd->oobsize == 8) { - c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS; - c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN; - } else { - c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS; + /* Should we use autoplacement ? */ + if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { + D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); + /* Get the position of the free bytes */ + if (!oinfo->oobfree[0][0]) { + printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); + return -ENOSPC; + } + c->fsdata_pos = oinfo->oobfree[0][0]; + c->fsdata_len = oinfo->oobfree[0][1]; + if (c->fsdata_len > 8) + c->fsdata_len = 8; + } else { + /* This is just a legacy fallback and should go away soon */ + switch(c->mtd->ecctype) { + case MTD_ECC_RS_DiskOnChip: + printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); + c->oobinfo = &jffs2_oobinfo_docecc; + c->fsdata_pos = 6; c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; + c->badblock_pos = 15; + break; + + default: + D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + return -EINVAL; } - c->badblock_pos = NAND_BADBLOCK_POS; - break; - - case MTD_ECC_RS_DiskOnChip: - D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n")); - c->oobinfo = &jffs2_oobinfo_docecc; - c->fsdata_pos = 6; - c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; - c->badblock_pos = 15; - break; - - default: - printk("JFFS2 doesn't yet know how to handle ECC type %d\n", - c->mtd->ecctype); - return -EINVAL; } + return 0; +} +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) +{ + int res; + + /* Initialise write buffer */ + c->wbuf_pagesize = c->mtd->oobblock; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; + res = jffs2_nand_set_oobinfo(c); + #ifdef BREAKME if (!brokenbuf) brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); @@ -1141,7 +1098,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) } memset(brokenbuf, 0xdb, c->wbuf_pagesize); #endif - return 0; + return res; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 11c761045..1253c48da 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.75 2003/10/08 11:45:11 dwmw2 Exp $ + * $Id: write.c,v 1.85 2004/07/13 08:58:25 dwmw2 Exp $ * */ @@ -18,6 +18,7 @@ #include #include #include "nodelist.h" +#include "compr.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) @@ -31,7 +32,6 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint memset(ic, 0, sizeof(*ic)); - init_MUTEX_LOCKED(&f->sem); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; @@ -133,7 +133,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 fn->raw = raw; raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*ri)+datalen); + raw->__totlen = PAD(sizeof(*ri)+datalen); raw->next_phys = NULL; ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, @@ -275,11 +275,11 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff fd->raw = raw; raw->flash_offset = flash_ofs; - raw->totlen = PAD(sizeof(*rd)+namelen); + raw->__totlen = PAD(sizeof(*rd)+namelen); raw->next_phys = NULL; ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, - (alloc_mode==ALLOC_GC)?0:fd->ino); + (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); if (ret || (retlen != sizeof(*rd) + namelen)) { printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", sizeof(*rd)+namelen, flash_ofs, ret, retlen); @@ -359,7 +359,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, while(writelen) { struct jffs2_full_dnode *fn; unsigned char *comprbuf = NULL; - unsigned char comprtype = JFFS2_COMPR_NONE; + uint16_t comprtype = JFFS2_COMPR_NONE; uint32_t phys_ofs, alloclen; uint32_t datalen, cdatalen; int retried = 0; @@ -373,24 +373,10 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, break; } down(&f->sem); - datalen = writelen; - cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); + datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); - comprbuf = kmalloc(cdatalen, GFP_KERNEL); - if (comprbuf) { - comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen); - } - if (comprtype == JFFS2_COMPR_NONE) { - /* Either compression failed, or the allocation of comprbuf failed */ - if (comprbuf) - kfree(comprbuf); - comprbuf = buf; - datalen = cdatalen; - } - /* Now comprbuf points to the data to be written, be it compressed or not. - comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means - that the comprbuf doesn't need to be kfree()d. - */ + comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); @@ -403,14 +389,14 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ri->offset = cpu_to_je32(offset); ri->csize = cpu_to_je32(cdatalen); ri->dsize = cpu_to_je32(datalen); - ri->compr = comprtype; + ri->compr = comprtype & 0xff; + ri->usercompr = (comprtype >> 8 ) & 0xff; ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); - if (comprtype != JFFS2_COMPR_NONE) - kfree(comprbuf); + jffs2_free_comprbuf(comprbuf, buf); if (IS_ERR(fn)) { ret = PTR_ERR(fn); @@ -559,48 +545,75 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t alloclen, phys_ofs; int ret; - rd = jffs2_alloc_raw_dirent(); - if (!rd) - return -ENOMEM; + if (1 /* alternative branch needs testing */ || + !jffs2_can_mark_obsolete(c)) { + /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */ - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); - if (ret) { + rd = jffs2_alloc_raw_dirent(); + if (!rd) + return -ENOMEM; + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); + if (ret) { + jffs2_free_raw_dirent(rd); + return ret; + } + + down(&dir_f->sem); + + /* Build a deletion node */ + rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); + + rd->pino = cpu_to_je32(dir_f->inocache->ino); + rd->version = cpu_to_je32(++dir_f->highest_version); + rd->ino = cpu_to_je32(0); + rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_UNKNOWN; + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); + rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); + + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); + jffs2_free_raw_dirent(rd); - return ret; - } - down(&dir_f->sem); + if (IS_ERR(fd)) { + jffs2_complete_reservation(c); + up(&dir_f->sem); + return PTR_ERR(fd); + } - /* Build a deletion node */ - rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); - rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); - rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); + /* File it. This will mark the old one obsolete. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); + up(&dir_f->sem); + } else { + struct jffs2_full_dirent **prev = &dir_f->dents; + uint32_t nhash = full_name_hash(name, namelen); - rd->pino = cpu_to_je32(dir_f->inocache->ino); - rd->version = cpu_to_je32(++dir_f->highest_version); - rd->ino = cpu_to_je32(0); - rd->mctime = cpu_to_je32(get_seconds()); - rd->nsize = namelen; - rd->type = DT_UNKNOWN; - rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); - rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); + down(&dir_f->sem); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); - - jffs2_free_raw_dirent(rd); + while ((*prev) && (*prev)->nhash <= nhash) { + if ((*prev)->nhash == nhash && + !memcmp((*prev)->name, name, namelen) && + !(*prev)->name[namelen]) { + struct jffs2_full_dirent *this = *prev; - if (IS_ERR(fd)) { - jffs2_complete_reservation(c); + D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", + this->ino, ref_offset(this->raw))); + + *prev = this->next; + jffs2_mark_node_obsolete(c, (this->raw)); + jffs2_free_full_dirent(this); + break; + } + prev = &((*prev)->next); + } up(&dir_f->sem); - return PTR_ERR(fd); } - /* File it. This will mark the old one obsolete. */ - jffs2_add_fd_to_list(c, fd, &dir_f->dents); - - up(&dir_f->sem); - /* dead_f is NULL if this was a rename not a real unlink */ /* Also catch the !f->inocache case, where there was a dirent pointing to an inode which didn't exist. */ diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index c88bf7336..7e46e952d 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $ + * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $ * */ diff --git a/include/asm-ppc/ocp_ids.h b/include/asm-ppc/ocp_ids.h index 5d4ad8a22..666796c2b 100644 --- a/include/asm-ppc/ocp_ids.h +++ b/include/asm-ppc/ocp_ids.h @@ -25,6 +25,7 @@ #define OCP_VENDOR_ARM 0x0004 #define OCP_VENDOR_FREESCALE 0x1057 #define OCP_VENDOR_IBM 0x1014 +#define OCP_VENDOR_MARVELL 0x11ab #define OCP_VENDOR_MOTOROLA OCP_VENDOR_FREESCALE #define OCP_VENDOR_XILINX 0x10ee #define OCP_VENDOR_UNKNOWN 0xFFFF @@ -42,6 +43,9 @@ #define OCP_FUNC_16550 0x0031 #define OCP_FUNC_IIC 0x0032 #define OCP_FUNC_USB 0x0033 +#define OCP_FUNC_MPSC 0x0034 +#define OCP_FUNC_COMM_MPSC 0x0035 +#define OCP_FUNC_SDMA 0x0036 /* Memory devices 0x0090 - 0x009F */ #define OCP_FUNC_MAL 0x0090 @@ -60,12 +64,18 @@ #define OCP_FUNC_PERFMON 0x00D2 /* Performance Monitor */ #define OCP_FUNC_RGMII 0x00D3 #define OCP_FUNC_TAH 0x00D4 +#define OCP_FUNC_I2C 0x00D5 /* I2C Controller */ +#define OCP_FUNC_BRG 0x00D6 /* Baud Rate Generator */ +#define OCP_FUNC_PIC 0x00D7 /* Programmable Interrupt Controller */ /* Network 0x0200 - 0x02FF */ #define OCP_FUNC_EMAC 0x0200 -#define OCP_FUNC_GFAR 0x0201 /* TSEC & FEC */ +#define OCP_FUNC_ENET 0x0201 /* TSEC & FEC */ +#define OCP_FUNC_COMM_EMAC 0x0202 +#define OCP_FUNC_GFAR 0x0203 /* TSEC & FEC */ /* Bridge devices 0xE00 - 0xEFF */ #define OCP_FUNC_OPB 0x0E00 +#define OCP_FUNC_HB 0x0E01 /* Host bridge */ #define OCP_FUNC_UNKNOWN 0xFFFF diff --git a/include/asm-ppc/serial.h b/include/asm-ppc/serial.h index 310206e28..c77cfb42a 100644 --- a/include/asm-ppc/serial.h +++ b/include/asm-ppc/serial.h @@ -10,6 +10,8 @@ #if defined(CONFIG_EV64260) #include +#elif defined(CONFIG_DMV182) +#include #elif defined(CONFIG_GEMINI) #include #elif defined(CONFIG_POWERPMC250) diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index cd51da3a0..7109aa6e5 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -252,6 +252,11 @@ static inline int __next_cpu(int n, const cpumask_t *srcp, int nbits) [0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \ } }) +#define CPU_MASK_CPU0 \ +((cpumask_t) { { \ + [0] = 1UL \ +} }) + #define cpus_addr(src) ((src).bits) #define cpumask_scnprintf(buf, len, src) \ diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index e1a49dca4..162f06ef5 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -8,7 +8,7 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $ + * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ * */ @@ -43,6 +43,8 @@ #define JFFS2_COMPR_COPY 0x04 #define JFFS2_COMPR_DYNRUBIN 0x05 #define JFFS2_COMPR_ZLIB 0x06 +#define JFFS2_COMPR_LZO 0x07 +#define JFFS2_COMPR_LZARI 0x08 /* Compatibility flags. */ #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ #define JFFS2_NODE_ACCURATE 0x2000 @@ -87,39 +89,6 @@ typedef struct { uint16_t v16; } __attribute__((packed)) jint16_t; -#define JFFS2_NATIVE_ENDIAN - -/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from - whatever OS we're actually running on here too. */ - -#if defined(JFFS2_NATIVE_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){x}) -#define cpu_to_je32(x) ((jint32_t){x}) -#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) - -#define je16_to_cpu(x) ((x).v16) -#define je32_to_cpu(x) ((x).v32) -#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) -#elif defined(JFFS2_BIG_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) -#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) -#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) - -#define je16_to_cpu(x) (be16_to_cpu(x.v16)) -#define je32_to_cpu(x) (be32_to_cpu(x.v32)) -#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) -#elif defined(JFFS2_LITTLE_ENDIAN) -#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) -#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) -#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) - -#define je16_to_cpu(x) (le16_to_cpu(x.v16)) -#define je32_to_cpu(x) (le32_to_cpu(x.v32)) -#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) -#else -#error wibble -#endif - struct jffs2_unknown_node { /* All start like this */ diff --git a/include/linux/jffs2_fs_i.h b/include/linux/jffs2_fs_i.h index db91aa6c0..14743de6c 100644 --- a/include/linux/jffs2_fs_i.h +++ b/include/linux/jffs2_fs_i.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_i.h,v 1.15 2002/11/12 09:42:49 dwmw2 Exp $ */ +/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ #ifndef _JFFS2_FS_I #define _JFFS2_FS_I @@ -36,9 +36,11 @@ struct jffs2_inode_info { uint16_t flags; uint8_t usercompr; +#if !defined (__ECOS) #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) struct inode vfs_inode; #endif +#endif }; #endif /* _JFFS2_FS_I */ diff --git a/include/linux/libata.h b/include/linux/libata.h index b646a4834..acbd71969 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -69,6 +69,7 @@ /* defines only for the constants which don't work well as enums */ #define ATA_TAG_POISON 0xfafbfcfdU +#undef PORT_UNKNOWN enum { /* various global constants */ LIBATA_MAX_PRD = ATA_MAX_PRD / 2, diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index fa32bf254..12781628c 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.35 2003/05/28 15:37:32 dwmw2 Exp $ + * $Id: cfi.h,v 1.44 2004/07/13 22:32:52 dwmw2 Exp $ */ #ifndef __MTD_CFI_H__ @@ -13,200 +13,74 @@ #include #include #include +#include #include -/* - * You can optimize the code size and performance by defining only - * the geometry(ies) available on your hardware. - * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width) - * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes) - * - * By default, all (known) geometries are supported. - */ - -#ifndef CONFIG_MTD_CFI_GEOMETRY - -/* The default case - support all but 64-bit, which has - a performance penalty */ - -#define CFIDEV_INTERLEAVE_1 (1) -#define CFIDEV_INTERLEAVE_2 (2) -#define CFIDEV_INTERLEAVE_4 (4) - -#define CFIDEV_BUSWIDTH_1 (1) -#define CFIDEV_BUSWIDTH_2 (2) -#define CFIDEV_BUSWIDTH_4 (4) - -typedef __u32 cfi_word; - -#else - -/* Explicitly configured buswidth/interleave support */ - #ifdef CONFIG_MTD_CFI_I1 -#define CFIDEV_INTERLEAVE_1 (1) -#endif -#ifdef CONFIG_MTD_CFI_I2 -#define CFIDEV_INTERLEAVE_2 (2) -#endif -#ifdef CONFIG_MTD_CFI_I4 -#define CFIDEV_INTERLEAVE_4 (4) -#endif -#ifdef CONFIG_MTD_CFI_I8 -#define CFIDEV_INTERLEAVE_8 (8) -#endif - -#ifdef CONFIG_MTD_CFI_B1 -#define CFIDEV_BUSWIDTH_1 (1) -#endif -#ifdef CONFIG_MTD_CFI_B2 -#define CFIDEV_BUSWIDTH_2 (2) -#endif -#ifdef CONFIG_MTD_CFI_B4 -#define CFIDEV_BUSWIDTH_4 (4) -#endif -#ifdef CONFIG_MTD_CFI_B8 -#define CFIDEV_BUSWIDTH_8 (8) -#endif - -/* pick the largest necessary */ -#ifdef CONFIG_MTD_CFI_B8 -typedef __u64 cfi_word; - -/* This only works if asm/io.h is included first */ -#ifndef __raw_readll -#define __raw_readll(addr) (*(volatile __u64 *)(addr)) -#endif -#ifndef __raw_writell -#define __raw_writell(v, addr) (*(volatile __u64 *)(addr) = (v)) -#endif -#define CFI_WORD_64 -#else /* CONFIG_MTD_CFI_B8 */ -/* All others can use 32-bits. It's probably more efficient than - the smaller types anyway */ -typedef __u32 cfi_word; -#endif /* CONFIG_MTD_CFI_B8 */ - -#endif - -/* - * The following macros are used to select the code to execute: - * cfi_buswidth_is_*() - * cfi_interleave_is_*() - * [where * is either 1, 2, 4, or 8] - * Those macros should be used with 'if' statements. If only one of few - * geometry arrangements are selected, they expand to constants thus allowing - * the compiler (most of them being 0) to optimize away all the unneeded code, - * while still validating the syntax (which is not possible with embedded - * #if ... #endif constructs). - * The exception to this is the 64-bit versions, which need an extension - * to the cfi_word type, and cause compiler warnings about shifts being - * out of range. - */ - -#ifdef CFIDEV_INTERLEAVE_1 -# ifdef CFIDEV_INTERLEAVE -# undef CFIDEV_INTERLEAVE -# define CFIDEV_INTERLEAVE (cfi->interleave) -# else -# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1 -# endif -# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1) +#define cfi_interleave(cfi) 1 +#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1) #else -# define cfi_interleave_is_1() (0) +#define cfi_interleave_is_1(cfi) (0) #endif -#ifdef CFIDEV_INTERLEAVE_2 -# ifdef CFIDEV_INTERLEAVE -# undef CFIDEV_INTERLEAVE -# define CFIDEV_INTERLEAVE (cfi->interleave) +#ifdef CONFIG_MTD_CFI_I2 +# ifdef cfi_interleave +# undef cfi_interleave +# define cfi_interleave(cfi) ((cfi)->interleave) # else -# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2 +# define cfi_interleave(cfi) 2 # endif -# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2) +#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2) #else -# define cfi_interleave_is_2() (0) +#define cfi_interleave_is_2(cfi) (0) #endif -#ifdef CFIDEV_INTERLEAVE_4 -# ifdef CFIDEV_INTERLEAVE -# undef CFIDEV_INTERLEAVE -# define CFIDEV_INTERLEAVE (cfi->interleave) +#ifdef CONFIG_MTD_CFI_I4 +# ifdef cfi_interleave +# undef cfi_interleave +# define cfi_interleave(cfi) ((cfi)->interleave) # else -# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4 +# define cfi_interleave(cfi) 4 # endif -# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4) +#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4) #else -# define cfi_interleave_is_4() (0) +#define cfi_interleave_is_4(cfi) (0) #endif -#ifdef CFIDEV_INTERLEAVE_8 -# ifdef CFIDEV_INTERLEAVE -# undef CFIDEV_INTERLEAVE -# define CFIDEV_INTERLEAVE (cfi->interleave) +#ifdef CONFIG_MTD_CFI_I8 +# ifdef cfi_interleave +# undef cfi_interleave +# define cfi_interleave(cfi) ((cfi)->interleave) # else -# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8 +# define cfi_interleave(cfi) 8 # endif -# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8) +#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8) #else -# define cfi_interleave_is_8() (0) +#define cfi_interleave_is_8(cfi) (0) #endif -#ifndef CFIDEV_INTERLEAVE -#error You must define at least one interleave to support! +static inline int cfi_interleave_supported(int i) +{ + switch (i) { +#ifdef CONFIG_MTD_CFI_I1 + case 1: #endif - -#ifdef CFIDEV_BUSWIDTH_1 -# ifdef CFIDEV_BUSWIDTH -# undef CFIDEV_BUSWIDTH -# define CFIDEV_BUSWIDTH (map->buswidth) -# else -# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1 -# endif -# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1) -#else -# define cfi_buswidth_is_1() (0) +#ifdef CONFIG_MTD_CFI_I2 + case 2: #endif - -#ifdef CFIDEV_BUSWIDTH_2 -# ifdef CFIDEV_BUSWIDTH -# undef CFIDEV_BUSWIDTH -# define CFIDEV_BUSWIDTH (map->buswidth) -# else -# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2 -# endif -# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2) -#else -# define cfi_buswidth_is_2() (0) +#ifdef CONFIG_MTD_CFI_I4 + case 4: #endif - -#ifdef CFIDEV_BUSWIDTH_4 -# ifdef CFIDEV_BUSWIDTH -# undef CFIDEV_BUSWIDTH -# define CFIDEV_BUSWIDTH (map->buswidth) -# else -# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4 -# endif -# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4) -#else -# define cfi_buswidth_is_4() (0) +#ifdef CONFIG_MTD_CFI_I8 + case 8: #endif + return 1; -#ifdef CFIDEV_BUSWIDTH_8 -# ifdef CFIDEV_BUSWIDTH -# undef CFIDEV_BUSWIDTH -# define CFIDEV_BUSWIDTH (map->buswidth) -# else -# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8 -# endif -# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8) -#else -# define cfi_buswidth_is_8() (0) -#endif + default: + return 0; + } +} -#ifndef CFIDEV_BUSWIDTH -#error You must define at least one bus width to support! -#endif /* NB: these values must represents the number of bytes needed to meet the * device type (x8, x16, x32). Eg. a 32 bit device is 4 x 8 bytes. @@ -223,64 +97,84 @@ typedef __u32 cfi_word; /* Basic Query Structure */ struct cfi_ident { - __u8 qry[3]; - __u16 P_ID; - __u16 P_ADR; - __u16 A_ID; - __u16 A_ADR; - __u8 VccMin; - __u8 VccMax; - __u8 VppMin; - __u8 VppMax; - __u8 WordWriteTimeoutTyp; - __u8 BufWriteTimeoutTyp; - __u8 BlockEraseTimeoutTyp; - __u8 ChipEraseTimeoutTyp; - __u8 WordWriteTimeoutMax; - __u8 BufWriteTimeoutMax; - __u8 BlockEraseTimeoutMax; - __u8 ChipEraseTimeoutMax; - __u8 DevSize; - __u16 InterfaceDesc; - __u16 MaxBufWriteSize; - __u8 NumEraseRegions; - __u32 EraseRegionInfo[0]; /* Not host ordered */ + uint8_t qry[3]; + uint16_t P_ID; + uint16_t P_ADR; + uint16_t A_ID; + uint16_t A_ADR; + uint8_t VccMin; + uint8_t VccMax; + uint8_t VppMin; + uint8_t VppMax; + uint8_t WordWriteTimeoutTyp; + uint8_t BufWriteTimeoutTyp; + uint8_t BlockEraseTimeoutTyp; + uint8_t ChipEraseTimeoutTyp; + uint8_t WordWriteTimeoutMax; + uint8_t BufWriteTimeoutMax; + uint8_t BlockEraseTimeoutMax; + uint8_t ChipEraseTimeoutMax; + uint8_t DevSize; + uint16_t InterfaceDesc; + uint16_t MaxBufWriteSize; + uint8_t NumEraseRegions; + uint32_t EraseRegionInfo[0]; /* Not host ordered */ } __attribute__((packed)); /* Extended Query Structure for both PRI and ALT */ struct cfi_extquery { - __u8 pri[3]; - __u8 MajorVersion; - __u8 MinorVersion; + uint8_t pri[3]; + uint8_t MajorVersion; + uint8_t MinorVersion; } __attribute__((packed)); /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */ struct cfi_pri_intelext { - __u8 pri[3]; - __u8 MajorVersion; - __u8 MinorVersion; - __u32 FeatureSupport; - __u8 SuspendCmdSupport; - __u16 BlkStatusRegMask; - __u8 VccOptimal; - __u8 VppOptimal; - __u8 NumProtectionFields; - __u16 ProtRegAddr; - __u8 FactProtRegSize; - __u8 UserProtRegSize; + uint8_t pri[3]; + uint8_t MajorVersion; + uint8_t MinorVersion; + uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature + block follows - FIXME - not currently supported */ + uint8_t SuspendCmdSupport; + uint16_t BlkStatusRegMask; + uint8_t VccOptimal; + uint8_t VppOptimal; + uint8_t NumProtectionFields; + uint16_t ProtRegAddr; + uint8_t FactProtRegSize; + uint8_t UserProtRegSize; +} __attribute__((packed)); + +/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */ + +struct cfi_pri_amdstd { + uint8_t pri[3]; + uint8_t MajorVersion; + uint8_t MinorVersion; + uint8_t SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ + uint8_t EraseSuspend; + uint8_t BlkProt; + uint8_t TmpBlkUnprotect; + uint8_t BlkProtUnprot; + uint8_t SimultaneousOps; + uint8_t BurstMode; + uint8_t PageMode; + uint8_t VppMin; + uint8_t VppMax; + uint8_t TopBottom; } __attribute__((packed)); struct cfi_pri_query { - __u8 NumFields; - __u32 ProtField[1]; /* Not host ordered */ + uint8_t NumFields; + uint32_t ProtField[1]; /* Not host ordered */ } __attribute__((packed)); struct cfi_bri_query { - __u8 PageModeReadCap; - __u8 NumFields; - __u32 ConfField[1]; /* Not host ordered */ + uint8_t PageModeReadCap; + uint8_t NumFields; + uint32_t ConfField[1]; /* Not host ordered */ } __attribute__((packed)); #define P_ID_NONE 0 @@ -288,8 +182,10 @@ struct cfi_bri_query { #define P_ID_AMD_STD 2 #define P_ID_INTEL_STD 3 #define P_ID_AMD_EXT 4 +#define P_ID_ST_ADV 32 #define P_ID_MITSUBISHI_STD 256 #define P_ID_MITSUBISHI_EXT 257 +#define P_ID_SST_PAGE 258 #define P_ID_RESERVED 65535 @@ -297,14 +193,13 @@ struct cfi_bri_query { #define CFI_MODE_JEDEC 0 struct cfi_private { - __u16 cmdset; + uint16_t cmdset; void *cmdset_priv; int interleave; int device_type; int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */ int addr_unlock1; int addr_unlock2; - int fast_prog; struct mtd_info *(*cmdset_setup)(struct map_info *); struct cfi_ident *cfiq; /* For now only one. We insist that all devs must be of the same type. */ @@ -315,107 +210,81 @@ struct cfi_private { struct flchip chips[0]; /* per-chip data structure for each chip */ }; -#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */ - /* * Returns the command address according to the given geometry. */ -static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type) +static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type) { return (cmd_ofs * type) * interleave; } /* - * Transforms the CFI command for the given geometry (bus width & interleave. + * Transforms the CFI command for the given geometry (bus width & interleave). + * It looks too long to be inline, but in the common case it should almost all + * get optimised away. */ -static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) +static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) { - cfi_word val = 0; - - if (cfi_buswidth_is_1()) { - /* 1 x8 device */ - val = cmd; - } else if (cfi_buswidth_is_2()) { - if (cfi_interleave_is_1()) { - /* 1 x16 device in x16 mode */ - val = cpu_to_cfi16(cmd); - } else if (cfi_interleave_is_2()) { - /* 2 (x8, x16 or x32) devices in x8 mode */ - val = cpu_to_cfi16((cmd << 8) | cmd); - } - } else if (cfi_buswidth_is_4()) { - if (cfi_interleave_is_1()) { - /* 1 x32 device in x32 mode */ - val = cpu_to_cfi32(cmd); - } else if (cfi_interleave_is_2()) { - /* 2 x16 device in x16 mode */ - val = cpu_to_cfi32((cmd << 16) | cmd); - } else if (cfi_interleave_is_4()) { - /* 4 (x8, x16 or x32) devices in x8 mode */ - val = (cmd << 16) | cmd; - val = cpu_to_cfi32((val << 8) | val); - } -#ifdef CFI_WORD_64 - } else if (cfi_buswidth_is_8()) { - if (cfi_interleave_is_1()) { - /* 1 x64 device in x64 mode */ - val = cpu_to_cfi64(cmd); - } else if (cfi_interleave_is_2()) { - /* 2 x32 device in x32 mode */ - val = cmd; - val = cpu_to_cfi64((val << 32) | val); - } else if (cfi_interleave_is_4()) { - /* 4 (x16, x32 or x64) devices in x16 mode */ - val = (cmd << 16) | cmd; - val = cpu_to_cfi64((val << 32) | val); - } else if (cfi_interleave_is_8()) { - /* 8 (x8, x16 or x32) devices in x8 mode */ - val = (cmd << 8) | cmd; - val = (val << 16) | val; - val = (val << 32) | val; - val = cpu_to_cfi64(val); - } -#endif /* CFI_WORD_64 */ - } - return val; -} -#define CMD(x) cfi_build_cmd((x), map, cfi) - -/* - * Read a value according to the bus width. - */ - -static inline cfi_word cfi_read(struct map_info *map, __u32 addr) -{ - if (cfi_buswidth_is_1()) { - return map_read8(map, addr); - } else if (cfi_buswidth_is_2()) { - return map_read16(map, addr); - } else if (cfi_buswidth_is_4()) { - return map_read32(map, addr); - } else if (cfi_buswidth_is_8()) { - return map_read64(map, addr); + map_word val = { {0} }; + int wordwidth, words_per_bus, chip_mode, chips_per_word; + unsigned long onecmd; + int i; + + /* We do it this way to give the compiler a fighting chance + of optimising away all the crap for 'bankwidth' larger than + an unsigned long, in the common case where that support is + disabled */ + if (map_bankwidth_is_large(map)) { + wordwidth = sizeof(unsigned long); + words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1 } else { - return 0; + wordwidth = map_bankwidth(map); + words_per_bus = 1; + } + + chip_mode = map_bankwidth(map) / cfi_interleave(cfi); + chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map); + + /* First, determine what the bit-pattern should be for a single + device, according to chip mode and endianness... */ + switch (chip_mode) { + default: BUG(); + case 1: + onecmd = cmd; + break; + case 2: + onecmd = cpu_to_cfi16(cmd); + break; + case 4: + onecmd = cpu_to_cfi32(cmd); + break; } -} -/* - * Write a value according to the bus width. - */ + /* Now replicate it across the size of an unsigned long, or + just to the bus width as appropriate */ + switch (chips_per_word) { + default: BUG(); +#if BITS_PER_LONG >= 64 + case 8: + onecmd |= (onecmd << (chip_mode * 32)); +#endif + case 4: + onecmd |= (onecmd << (chip_mode * 16)); + case 2: + onecmd |= (onecmd << (chip_mode * 8)); + case 1: + ; + } -static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr) -{ - if (cfi_buswidth_is_1()) { - map_write8(map, val, addr); - } else if (cfi_buswidth_is_2()) { - map_write16(map, val, addr); - } else if (cfi_buswidth_is_4()) { - map_write32(map, val, addr); - } else if (cfi_buswidth_is_8()) { - map_write64(map, val, addr); + /* And finally, for the multi-word case, replicate it + in all words in the structure */ + for (i=0; i < words_per_bus; i++) { + val.x[i] = onecmd; } + + return val; } +#define CMD(x) cfi_build_cmd((x), map, cfi) /* * Sends a CFI command to a bank of flash for the given geometry. @@ -424,35 +293,36 @@ static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr) * If prev_val is non-null, it will be set to the value at the command address, * before the command was written. */ -static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base, +static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base, struct map_info *map, struct cfi_private *cfi, - int type, cfi_word *prev_val) + int type, map_word *prev_val) { - cfi_word val; - __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type); + map_word val; + uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type); val = cfi_build_cmd(cmd, map, cfi); if (prev_val) - *prev_val = cfi_read(map, addr); + *prev_val = map_read(map, addr); - cfi_write(map, val, addr); + map_write(map, val, addr); return addr - base; } -static inline __u8 cfi_read_query(struct map_info *map, __u32 addr) +static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr) { - if (cfi_buswidth_is_1()) { - return map_read8(map, addr); - } else if (cfi_buswidth_is_2()) { - return cfi16_to_cpu(map_read16(map, addr)); - } else if (cfi_buswidth_is_4()) { - return cfi32_to_cpu(map_read32(map, addr)); - } else if (cfi_buswidth_is_8()) { - return cfi64_to_cpu(map_read64(map, addr)); + map_word val = map_read(map, addr); + + if (map_bankwidth_is_1(map)) { + return val.x[0]; + } else if (map_bankwidth_is_2(map)) { + return cfi16_to_cpu(val.x[0]); } else { - return 0; + /* No point in a 64-bit byteswap since that would just be + swapping the responses from different chips, and we are + only interested in one chip (a representative sample) */ + return cfi32_to_cpu(val.x[0]); } } @@ -480,4 +350,19 @@ static inline void cfi_spin_unlock(spinlock_t *mutex) spin_unlock_bh(mutex); } +struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size, + const char* name); + +struct cfi_fixup { + uint16_t mfr; + uint16_t id; + void (*fixup)(struct map_info *map, void* param); + void* param; +}; + +#define CFI_MFR_ANY 0xffff +#define CFI_ID_ANY 0xffff + +void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups); + #endif /* __MTD_CFI_H__ */ diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 64465a963..0fd8f49c6 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -1,13 +1,21 @@ - -/* Linux driver for Disk-On-Chip 2000 */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse */ -/* $Id: doc2000.h,v 1.17 2003/06/12 01:20:46 gerg Exp $ */ +/* + * Linux driver for Disk-On-Chip devices + * + * Copyright (C) 1999 Machine Vision Holdings, Inc. + * Copyright (C) 2001-2003 David Woodhouse + * Copyright (C) 2002-2003 Greg Ungerer + * Copyright (C) 2002-2003 SnapGear Inc + * + * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $ + * + * Released under GPL + */ #ifndef __MTD_DOC2000_H__ #define __MTD_DOC2000_H__ #include +#include #define DoC_Sig1 0 #define DoC_Sig2 1 @@ -73,12 +81,12 @@ * Others use readb/writeb */ #if defined(__arm__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2)))) -#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) +#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)))) +#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) #define DOC_IOREMAP_LEN 0x8000 #elif defined(__ppc__) -#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1)))) -#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) +#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)))) +#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) #define DOC_IOREMAP_LEN 0x4000 #else #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) @@ -106,6 +114,7 @@ #define DOC_MODE_MDWREN 0x04 #define DOC_ChipID_Doc2k 0x20 +#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */ #define DOC_ChipID_DocMil 0x30 #define DOC_ChipID_DocMilPlus32 0x40 #define DOC_ChipID_DocMilPlus16 0x41 @@ -147,10 +156,10 @@ struct Nand { #define MAX_FLOORS 4 #define MAX_CHIPS 4 -#define MAX_FLOORS_MIL 4 +#define MAX_FLOORS_MIL 1 #define MAX_CHIPS_MIL 1 -#define MAX_FLOORS_MPLUS 1 +#define MAX_FLOORS_MPLUS 2 #define MAX_CHIPS_MPLUS 1 #define ADDR_COLUMN 1 @@ -161,7 +170,7 @@ struct DiskOnChip { unsigned long physadr; unsigned long virtadr; unsigned long totlen; - char ChipID; /* Type of DiskOnChip */ + unsigned char ChipID; /* Type of DiskOnChip */ int ioreg; unsigned long mfr; /* Flash IDs - only one type of flash per device */ diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index 7e042bf5f..c3ac4df72 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.9 2003/04/30 11:15:22 dwmw2 Exp $ + * $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $ * */ @@ -43,7 +43,8 @@ typedef enum { /* NOTE: confusingly, this can be used to refer to more than one chip at a time, - if they're interleaved. */ + if they're interleaved. This can even refer to individual partitions on + the same physical chip when present. */ struct flchip { unsigned long start; /* Offset within the map */ @@ -61,6 +62,7 @@ struct flchip { int write_suspended:1; int erase_suspended:1; + unsigned long in_progress_block_addr; spinlock_t *mutex; spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */ @@ -69,8 +71,17 @@ struct flchip { int word_write_time; int buffer_write_time; int erase_time; + + void *priv; }; +/* This is used to handle contention on write/erase operations + between partitions of the same physical chip. */ +struct flchip_shared { + spinlock_t lock; + struct flchip *writing; + struct flchip *erasing; +}; #endif /* __MTD_FLASHCHIP_H__ */ diff --git a/include/linux/mtd/ftl.h b/include/linux/mtd/ftl.h index ae017efb9..3678459b4 100644 --- a/include/linux/mtd/ftl.h +++ b/include/linux/mtd/ftl.h @@ -1,5 +1,5 @@ /* - * $Id: ftl.h,v 1.5 2001/06/02 20:35:51 dwmw2 Exp $ + * $Id: ftl.h,v 1.6 2003/01/24 13:20:04 dwmw2 Exp $ * * Derived from (and probably identical to): * ftl.h 1.7 1999/10/25 20:23:17 diff --git a/include/linux/mtd/gen_probe.h b/include/linux/mtd/gen_probe.h index 43c3a08a4..2d66b33f8 100644 --- a/include/linux/mtd/gen_probe.h +++ b/include/linux/mtd/gen_probe.h @@ -1,7 +1,7 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $ + * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ @@ -10,12 +10,12 @@ #include #include #include +#include struct chip_probe { char *name; int (*probe_chip)(struct map_info *map, __u32 base, - struct flchip *chips, struct cfi_private *cfi); - + unsigned long *chip_map, struct cfi_private *cfi); }; struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp); diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index 5cc052da9..b52c8cbd2 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -3,105 +3,32 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: inftl.h,v 1.3 2003/05/23 11:35:34 dwmw2 Exp $ + * $Id: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown Exp $ */ #ifndef __MTD_INFTL_H__ #define __MTD_INFTL_H__ +#ifndef __KERNEL__ +#error This is a kernel header. Perhaps include nftl-user.h instead? +#endif + #include #include #include -#define OSAK_VERSION 0x5120 -#define PERCENTUSED 98 - -#define SECTORSIZE 512 +#include #ifndef INFTL_MAJOR -#define INFTL_MAJOR 93 /* FIXME */ +#define INFTL_MAJOR 94 #endif #define INFTL_PARTN_BITS 4 -/* Block Control Information */ - -struct inftl_bci { - __u8 ECCsig[6]; - __u8 Status; - __u8 Status1; -} __attribute__((packed)); - -struct inftl_unithead1 { - __u16 virtualUnitNo; - __u16 prevUnitNo; - __u8 ANAC; - __u8 NACs; - __u8 parityPerField; - __u8 discarded; -} __attribute__((packed)); - -struct inftl_unithead2 { - __u8 parityPerField; - __u8 ANAC; - __u16 prevUnitNo; - __u16 virtualUnitNo; - __u8 NACs; - __u8 discarded; -} __attribute__((packed)); - -struct inftl_unittail { - __u8 Reserved[4]; - __u16 EraseMark; - __u16 EraseMark1; -} __attribute__((packed)); - -union inftl_uci { - struct inftl_unithead1 a; - struct inftl_unithead2 b; - struct inftl_unittail c; -}; - -struct inftl_oob { - struct inftl_bci b; - union inftl_uci u; -}; - - -/* INFTL Media Header */ - -struct INFTLPartition { - __u32 virtualUnits; - __u32 firstUnit; - __u32 lastUnit; - __u32 flags; - __u32 spareUnits; - __u32 Reserved0; - __u32 Reserved1; -} __attribute__((packed)); - -struct INFTLMediaHeader { - char bootRecordID[8]; - __u32 NoOfBootImageBlocks; - __u32 NoOfBinaryPartitions; - __u32 NoOfBDTLPartitions; - __u32 BlockMultiplierBits; - __u32 FormatFlags; - __u32 OsakVersion; - __u32 PercentUsed; - struct INFTLPartition Partitions[4]; -} __attribute__((packed)); - -/* Partition flag types */ -#define INFTL_BINARY 0x20000000 -#define INFTL_BDTL 0x40000000 -#define INFTL_LAST 0x80000000 - - #ifdef __KERNEL__ struct INFTLrecord { struct mtd_blktrans_dev mbd; - __u16 MediaUnit, SpareMediaUnit; + __u16 MediaUnit; __u32 EraseSize; struct INFTLMediaHeader MediaHdr; int usecount; @@ -119,6 +46,7 @@ struct INFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; + struct nand_oobinfo oobinfo; }; int INFTL_mount(struct INFTLrecord *s); diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 0c933f9bf..ed41daec7 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.34 2003/05/28 12:42:22 dwmw2 Exp $ */ +/* $Id: map.h,v 1.43 2004/07/14 13:30:27 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -8,17 +8,163 @@ #include #include #include +#include #include #include +#include + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1 +#define map_bankwidth(map) 1 +#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1) +#define map_bankwidth_is_large(map) (0) +#define map_words(map) (1) +#define MAX_MAP_BANKWIDTH 1 +#else +#define map_bankwidth_is_1(map) (0) +#endif + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2 +# ifdef map_bankwidth +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# else +# define map_bankwidth(map) 2 +# define map_bankwidth_is_large(map) (0) +# define map_words(map) (1) +# endif +#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2) +#undef MAX_MAP_BANKWIDTH +#define MAX_MAP_BANKWIDTH 2 +#else +#define map_bankwidth_is_2(map) (0) +#endif + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4 +# ifdef map_bankwidth +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# else +# define map_bankwidth(map) 4 +# define map_bankwidth_is_large(map) (0) +# define map_words(map) (1) +# endif +#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4) +#undef MAX_MAP_BANKWIDTH +#define MAX_MAP_BANKWIDTH 4 +#else +#define map_bankwidth_is_4(map) (0) +#endif + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8 +# ifdef map_bankwidth +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# if BITS_PER_LONG < 64 +# undef map_bankwidth_is_large +# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) +# undef map_words +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# endif +# else +# define map_bankwidth(map) 8 +# define map_bankwidth_is_large(map) (BITS_PER_LONG < 64) +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# endif +#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8) +#undef MAX_MAP_BANKWIDTH +#define MAX_MAP_BANKWIDTH 8 +#else +#define map_bankwidth_is_8(map) (0) +#endif + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16 +# ifdef map_bankwidth +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# undef map_bankwidth_is_large +# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) +# undef map_words +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# else +# define map_bankwidth(map) 16 +# define map_bankwidth_is_large(map) (1) +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# endif +#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16) +#undef MAX_MAP_BANKWIDTH +#define MAX_MAP_BANKWIDTH 16 +#else +#define map_bankwidth_is_16(map) (0) +#endif + +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32 +# ifdef map_bankwidth +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# undef map_bankwidth_is_large +# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) +# undef map_words +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# else +# define map_bankwidth(map) 32 +# define map_bankwidth_is_large(map) (1) +# define map_words(map) (map_bankwidth(map) / sizeof(unsigned long)) +# endif +#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32) +#undef MAX_MAP_BANKWIDTH +#define MAX_MAP_BANKWIDTH 32 +#else +#define map_bankwidth_is_32(map) (0) +#endif + +#ifndef map_bankwidth +#error "No bus width supported. What's the point?" +#endif + +static inline int map_bankwidth_supported(int w) +{ + switch (w) { +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1 + case 1: +#endif +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2 + case 2: +#endif +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4 + case 4: +#endif +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8 + case 8: +#endif +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16 + case 16: +#endif +#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32 + case 32: +#endif + return 1; + + default: + return 0; + } +} + +#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG ) + +typedef union { + unsigned long x[MAX_MAP_LONGS]; +} map_word; /* The map stuff is very simple. You fill in your struct map_info with a handful of routines for accessing the device, making sure they handle paging etc. correctly if your device needs it. Then you pass it off - to a chip driver which deals with a mapped device - generally either - do_cfi_probe() or do_ram_probe(), either of which will return a - struct mtd_info if they liked what they saw. At which point, you - fill in the mtd->module with your own module address, and register - it. + to a chip probe routine -- either JEDEC or CFI probe or both -- via + do_map_probe(). If a chip is recognised, the probe code will invoke the + appropriate chip driver (if present) and return a struct mtd_info. + At which point, you fill in the mtd->module with your own module + address, and register it with the MTD core code. Or you could partition + it and register the partitions instead, or keep it for your own private + use; whatever. The mtd->priv field will point to the struct map_info, and any further private data required by the chip driver is linked from the @@ -36,28 +182,29 @@ struct map_info { unsigned long virt; void *cached; - int buswidth; /* in octets */ + int bankwidth; /* in octets. This isn't necessarily the width + of actual bus cycles -- it's the repeat interval + in bytes, before you are talking to the first chip again. + */ #ifdef CONFIG_MTD_COMPLEX_MAPPINGS - u8 (*read8)(struct map_info *, unsigned long); - u16 (*read16)(struct map_info *, unsigned long); - u32 (*read32)(struct map_info *, unsigned long); - u64 (*read64)(struct map_info *, unsigned long); - /* If it returned a 'long' I'd call it readl. - * It doesn't. - * I won't. - * dwmw2 */ - + map_word (*read)(struct map_info *, unsigned long); void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t); - void (*write8)(struct map_info *, u8, unsigned long); - void (*write16)(struct map_info *, u16, unsigned long); - void (*write32)(struct map_info *, u32, unsigned long); - void (*write64)(struct map_info *, u64, unsigned long); + + void (*write)(struct map_info *, const map_word, unsigned long); void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t); /* We can perhaps put in 'point' and 'unpoint' methods, if we really want to enable XIP for non-linear mappings. Not yet though. */ #endif + /* It's possible for the map driver to use cached memory in its + copy_from implementation (and _only_ with copy_from). However, + when the chip driver knows some flash area has changed contents, + it will signal it to the map driver through this routine to let + the map driver invalidate the corresponding cache as needed. + If there is no cache to care about this can be set to NULL. */ + void (*inval_cache)(struct map_info *, unsigned long, ssize_t); + /* set_vpp() must handle being reentered -- enable, enable, disable must leave it enabled. */ void (*set_vpp)(struct map_info *, int); @@ -85,86 +232,173 @@ void map_destroy(struct mtd_info *mtd); #define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) #define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) -#ifdef CONFIG_MTD_COMPLEX_MAPPINGS -#define map_read8(map, ofs) (map)->read8(map, ofs) -#define map_read16(map, ofs) (map)->read16(map, ofs) -#define map_read32(map, ofs) (map)->read32(map, ofs) -#define map_read64(map, ofs) (map)->read64(map, ofs) -#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len) -#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs) -#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs) -#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs) -#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs) -#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len) +#define INVALIDATE_CACHED_RANGE(map, from, size) \ + do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) -extern void simple_map_init(struct map_info *); -#define map_is_linear(map) (map->phys != NO_XIP) -#else -static inline u8 map_read8(struct map_info *map, unsigned long ofs) +static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2) { - return __raw_readb(map->virt + ofs); + int i; + for (i=0; ivirt + ofs); + map_word r; + int i; + + for (i=0; ivirt + ofs); + map_word r; + int i; + + for (i=0; ivirt + ofs); -#endif } -static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs) +static inline map_word map_word_load(struct map_info *map, const void *ptr) { - __raw_writeb(datum, map->virt + ofs); - mb(); + map_word r; + + if (map_bankwidth_is_1(map)) + r.x[0] = *(unsigned char *)ptr; + else if (map_bankwidth_is_2(map)) + r.x[0] = get_unaligned((uint16_t *)ptr); + else if (map_bankwidth_is_4(map)) + r.x[0] = get_unaligned((uint32_t *)ptr); +#if BITS_PER_LONG >= 64 + else if (map_bankwidth_is_8(map)) + r.x[0] = get_unaligned((uint64_t *)ptr); +#endif + else if (map_bankwidth_is_large(map)) + memcpy(r.x, ptr, map->bankwidth); + + return r; } -static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs) +static inline map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len) { - __raw_writew(datum, map->virt + ofs); - mb(); + int i; + + if (map_bankwidth_is_large(map)) { + char *dest = (char *)&orig; + memcpy(dest+start, buf, len); + } else { + for (i=start; i < start+len; i++) { + int bitpos; +#ifdef __LITTLE_ENDIAN + bitpos = i*8; +#else /* __BIG_ENDIAN */ + bitpos = (map_bankwidth(map)-1-i)*8; +#endif + orig.x[0] &= ~(0xff << bitpos); + orig.x[0] |= buf[i] << bitpos; + } + } + return orig; } -static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs) +static inline map_word map_word_ff(struct map_info *map) { - __raw_writel(datum, map->virt + ofs); - mb(); + map_word r; + int i; + + for (i=0; ivirt + ofs); + else if (map_bankwidth_is_2(map)) + r.x[0] = __raw_readw(map->virt + ofs); + else if (map_bankwidth_is_4(map)) + r.x[0] = __raw_readl(map->virt + ofs); +#if BITS_PER_LONG >= 64 + else if (map_bankwidth_is_8(map)) + r.x[0] = __raw_readq(map->virt + ofs); +#endif + else if (map_bankwidth_is_large(map)) + memcpy_fromio(r.x, map->virt+ofs, map->bankwidth); + + return r; } -static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs) +static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs) { -#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ - BUG(); -#else - __raw_writell(datum, map->virt + ofs); + if (map_bankwidth_is_1(map)) + __raw_writeb(datum.x[0], map->virt + ofs); + else if (map_bankwidth_is_2(map)) + __raw_writew(datum.x[0], map->virt + ofs); + else if (map_bankwidth_is_4(map)) + __raw_writel(datum.x[0], map->virt + ofs); +#if BITS_PER_LONG >= 64 + else if (map_bankwidth_is_8(map)) + __raw_writeq(datum.x[0], map->virt + ofs); +#endif + else if (map_bankwidth_is_large(map)) + memcpy_toio(map->virt+ofs, datum.x, map->bankwidth); mb(); -#endif /* CFI_B8 */ } -static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { - memcpy_fromio(to, map->virt + from, len); + if (map->cached) + memcpy(to, (char *)map->cached + from, len); + else + memcpy_fromio(to, map->virt + from, len); } -static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { memcpy_toio(map->virt + to, from, len); } -#define simple_map_init(map) do { } while (0) +#ifdef CONFIG_MTD_COMPLEX_MAPPINGS +#define map_read(map, ofs) (map)->read(map, ofs) +#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len) +#define map_write(map, datum, ofs) (map)->write(map, datum, ofs) +#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len) + +extern void simple_map_init(struct map_info *); +#define map_is_linear(map) (map->phys != NO_XIP) + +#else +#define map_read(map, ofs) inline_map_read(map, ofs) +#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len) +#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs) +#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len) + + +#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth)) #define map_is_linear(map) (1) #endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 2f0e43db0..fd7b15d07 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,10 +1,17 @@ - -/* $Id: mtd.h,v 1.45 2003/05/20 21:56:40 dwmw2 Exp $ */ +/* + * $Id: mtd.h,v 1.54 2004/07/15 01:13:12 dwmw2 Exp $ + * + * Copyright (C) 1999-2003 David Woodhouse et al. + * + * Released under GPL + */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ -#ifdef __KERNEL__ +#ifndef __KERNEL__ +#error This is a kernel header. Perhaps include mtd-user.h instead? +#endif #include #include @@ -12,115 +19,26 @@ #include #include -#endif /* __KERNEL__ */ - -struct erase_info_user { - u_int32_t start; - u_int32_t length; -}; - -struct mtd_oob_buf { - u_int32_t start; - u_int32_t length; - unsigned char __user *ptr; -}; +#include #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 #define MAX_MTD_DEVICES 16 - - -#define MTD_ABSENT 0 -#define MTD_RAM 1 -#define MTD_ROM 2 -#define MTD_NORFLASH 3 -#define MTD_NANDFLASH 4 -#define MTD_PEROM 5 -#define MTD_OTHER 14 -#define MTD_UNKNOWN 15 - - - -#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) -#define MTD_SET_BITS 2 // Bits can be set -#define MTD_ERASEABLE 4 // Has an erase function -#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible -#define MTD_VOLATILE 16 // Set for RAMs -#define MTD_XIP 32 // eXecute-In-Place possible -#define MTD_OOB 64 // Out-of-band data (NAND flash) -#define MTD_ECC 128 // Device capable of automatic ECC - -// Some common devices / combinations of capabilities -#define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) -#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) -#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) -#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) - - -// Types of automatic ECC/Checksum available -#define MTD_ECC_NONE 0 // No automatic ECC available -#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip -#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices - -struct mtd_info_user { - u_char type; - u_int32_t flags; - u_int32_t size; // Total size of the MTD - u_int32_t erasesize; - u_int32_t oobblock; // Size of OOB blocks (e.g. 512) - u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) - u_int32_t ecctype; - u_int32_t eccsize; -}; - -struct region_info_user { - u_int32_t offset; /* At which this region starts, - * from the beginning of the MTD */ - u_int32_t erasesize; /* For this region */ - u_int32_t numblocks; /* Number of blocks in this region */ - u_int32_t regionindex; -}; - -#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) -#define MEMERASE _IOW('M', 2, struct erase_info_user) -#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) -#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) -#define MEMLOCK _IOW('M', 5, struct erase_info_user) -#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) -#define MEMGETREGIONCOUNT _IOR('M', 7, int) -#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) -#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) - -struct nand_oobinfo { - int useecc; - int eccpos[6]; -}; - - -#ifndef __KERNEL__ - -typedef struct mtd_info_user mtd_info_t; -typedef struct erase_info_user erase_info_t; -typedef struct region_info_user region_info_t; -typedef struct nand_oobinfo nand_oobinfo_t; - - /* User-space ioctl definitions */ - -#else /* __KERNEL__ */ - - #define MTD_ERASE_PENDING 0x01 #define MTD_ERASING 0x02 #define MTD_ERASE_SUSPEND 0x04 #define MTD_ERASE_DONE 0x08 #define MTD_ERASE_FAILED 0x10 +/* If the erase fails, fail_addr might indicate exactly which block failed. If + fail_addr = 0xffffffff, the failure was not at the device level or was not + specific to any particular block. */ struct erase_info { struct mtd_info *mtd; u_int32_t addr; u_int32_t len; + u_int32_t fail_addr; u_long time; u_long retries; u_int dev; @@ -150,6 +68,7 @@ struct mtd_info { u_int32_t oobblock; // Size of OOB blocks (e.g. 512) u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) + u_int32_t oobavail; // Number of bytes in OOB area available for fs u_int32_t ecctype; u_int32_t eccsize; @@ -223,6 +142,10 @@ struct mtd_info { int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); + /* Bad block management functions */ + int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); + void *priv; struct module *owner; @@ -288,6 +211,4 @@ int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, #endif /* CONFIG_MTD_DEBUG */ -#endif /* __KERNEL__ */ - #endif /* __MTD_MTD_H__ */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 88d48a26b..7de9f8dfa 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -5,7 +5,7 @@ * Steven J. Hill * Thomas Gleixner * - * $Id: nand.h,v 1.25 2003/05/21 15:15:02 dwmw2 Exp $ + * $Id: nand.h,v 1.63 2004/07/07 16:29:43 gleixner Exp $ * * 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 @@ -44,6 +44,10 @@ * NAND_YAFFS_OOB * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL * Split manufacturer and device ID structures + * + * 02-08-2004 tglx added option field to nand structure for chip anomalities + * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id + * update of nand_chip structure description */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H @@ -51,22 +55,46 @@ #include #include #include +#include struct mtd_info; -/* - * Searches for a NAND device +/* Scan and identify a NAND device */ +extern int nand_scan (struct mtd_info *mtd, int max_chips); +/* Free resources held by the NAND device */ +extern void nand_release (struct mtd_info *mtd); + +/* Read raw data from the device without ECC */ +extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); + + +/* The maximum number of NAND chips in an array */ +#define NAND_MAX_CHIPS 8 + +/* This constant declares the max. oobsize / page, which + * is supported now. If you add a chip with bigger oobsize/page + * adjust this accordingly. */ -extern int nand_scan (struct mtd_info *mtd); +#define NAND_MAX_OOBSIZE 64 /* * Constants for hardware specific CLE/ALE/NCE function */ +/* Select the chip by setting nCE to low */ #define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ #define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ #define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ #define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ #define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ #define NAND_CTL_CLRALE 6 +/* Set write protection by setting WP to high. Not used! */ +#define NAND_CTL_SETWP 7 +/* Clear write protection by setting WP to low. Not used! */ +#define NAND_CTL_CLRWP 8 /* * Standard NAND flash commands @@ -77,35 +105,102 @@ extern int nand_scan (struct mtd_info *mtd); #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 +#define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +/* Extended commands for large page devices */ +#define NAND_CMD_READSTART 0x30 +#define NAND_CMD_CACHEDPROG 0x15 + +/* Status bits */ +#define NAND_STATUS_FAIL 0x01 +#define NAND_STATUS_FAIL_N1 0x02 +#define NAND_STATUS_TRUE_READY 0x20 +#define NAND_STATUS_READY 0x40 +#define NAND_STATUS_WP 0x80 + /* * Constants for ECC_MODES - * - * NONE: No ECC - * SOFT: Software ECC 3 byte ECC per 256 Byte data - * HW3_256: Hardware ECC 3 byte ECC per 256 Byte data - * HW3_512: Hardware ECC 3 byte ECC per 512 Byte data - * - * -*/ + */ + +/* No ECC. Usage is not recommended ! */ #define NAND_ECC_NONE 0 +/* Software ECC 3 byte ECC per 256 Byte data */ #define NAND_ECC_SOFT 1 +/* Hardware ECC 3 byte ECC per 256 Byte data */ #define NAND_ECC_HW3_256 2 +/* Hardware ECC 3 byte ECC per 512 Byte data */ #define NAND_ECC_HW3_512 3 +/* Hardware ECC 3 byte ECC per 512 Byte data */ #define NAND_ECC_HW6_512 4 -#define NAND_ECC_DISKONCHIP 5 +/* Hardware ECC 8 byte ECC per 512 Byte data */ +#define NAND_ECC_HW8_512 6 /* * Constants for Hardware ECC */ +/* Reset Hardware ECC for read */ #define NAND_ECC_READ 0 +/* Reset Hardware ECC for write */ #define NAND_ECC_WRITE 1 - +/* Enable Hardware ECC before syndrom is read back from flash */ +#define NAND_ECC_READSYN 2 + +/* Option constants for bizarre disfunctionality and real +* features +*/ +/* Chip can not auto increment pages */ +#define NAND_NO_AUTOINCR 0x00000001 +/* Buswitdh is 16 bit */ +#define NAND_BUSWIDTH_16 0x00000002 +/* Device supports partial programming without padding */ +#define NAND_NO_PADDING 0x00000004 +/* Chip has cache program function */ +#define NAND_CACHEPRG 0x00000008 +/* Chip has copy back function */ +#define NAND_COPYBACK 0x00000010 +/* AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information */ +#define NAND_IS_AND 0x00000020 +/* Chip has a array of 4 pages which can be read without + * additional ready /busy waits */ +#define NAND_4PAGE_ARRAY 0x00000040 + +/* Options valid for Samsung large page devices */ +#define NAND_SAMSUNG_LP_OPTIONS \ + (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) + +/* Macros to identify the above */ +#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR)) +#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) +#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) +#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) + +/* Mask to zero out the chip options, which come from the id table */ +#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) + +/* Non chip related options */ +/* Use a flash based bad block table. This option is passed to the + * default bad block table function. */ +#define NAND_USE_FLASH_BBT 0x00010000 +/* The hw ecc generator provides a syndrome instead a ecc value on read + * This can only work if we have the ecc bytes directly behind the + * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ +#define NAND_HWECC_SYNDROME 0x00020000 + + +/* Options set by nand scan */ +/* Nand scan has allocated oob_buf */ +#define NAND_OOBBUF_ALLOC 0x40000000 +/* Nand scan has allocated data_buf */ +#define NAND_DATABUF_ALLOC 0x80000000 + + /* + * nand_state_t - chip states * Enumeration for NAND flash chip state */ typedef enum { @@ -113,71 +208,116 @@ typedef enum { FL_READING, FL_WRITING, FL_ERASING, - FL_SYNCING + FL_SYNCING, + FL_CACHEDPRG, } nand_state_t; -/* - * NAND Private Flash Chip Data - * - * Structure overview: - * - * IO_ADDR_R - address to read the 8 I/O lines of the flash device - * - * IO_ADDR_W - address to write the 8 I/O lines of the flash device - * - * hwcontrol - hardwarespecific function for accesing control-lines - * - * dev_ready - hardwarespecific function for accesing device ready/busy line - * - * waitfunc - hardwarespecific function for wait on ready - * - * calculate_ecc - function for ecc calculation or readback from ecc hardware - * - * correct_data - function for ecc correction, matching to ecc generator (sw/hw) - * - * enable_hwecc - function to enable (reset) hardware ecc generator - * - * eccmod - mode of ecc: see constants - * - * eccsize - databytes used per ecc-calculation - * - * chip_delay - chip dependent delay for transfering data from array to read regs (tR) - * - * chip_lock - spinlock used to protect access to this structure - * - * wq - wait queue to sleep on if a NAND operation is in progress - * - * state - give the current state of the NAND device - * - * page_shift - number of address bits in a page (column address bits) - * - * data_buf - data buffer passed to/from MTD user modules - * - * data_cache - data cache for redundant page access and shadow for - * ECC failure - * - * cache_page - number of last valid page in page_cache +/** + * struct nand_chip - NAND Private Flash Chip Data + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @read_byte: [REPLACEABLE] read one byte from the chip + * @write_byte: [REPLACEABLE] write one byte to the chip + * @read_word: [REPLACEABLE] read one word from the chip + * @write_word: [REPLACEABLE] write one word to the chip + * @write_buf: [REPLACEABLE] write data from the buffer to the chip + * @read_buf: [REPLACEABLE] read data from the chip into the buffer + * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data + * @select_chip: [REPLACEABLE] select chip nr + * @block_bad: [REPLACEABLE] check, if the block is bad + * @block_markbad: [REPLACEABLE] mark the block bad + * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line + * If set to NULL no access to ready/busy is available and the ready/busy information + * is read from the chip status register + * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip + * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready + * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware + * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) + * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only + * be provided if a hardware ECC is available + * @erase_cmd: [INTERN] erase command write function, selectable due to AND support + * @scan_bbt: [REPLACEABLE] function to scan bad block table + * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines + * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccsteps: [INTERN] number of ecc calculation steps per page + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) + * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip + * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress + * @state: [INTERN] the current state of the NAND device + * @page_shift: [INTERN] number of address bits in a page (column address bits) + * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock + * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry + * @chip_shift: [INTERN] number of address bits in one chip + * @data_buf: [INTERN] internal buffer for one page + oob + * @oob_buf: [INTERN] oob buffer for one eraseblock + * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized + * @data_poi: [INTERN] pointer to a data buffer + * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about + * special functionality. See the defines for further explanation + * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @numchips: [INTERN] number of physical chips + * @chipsize: [INTERN] the size of one chip for multichip arrays + * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 + * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf + * @autooob: [REPLACEABLE] the default (auto)placement scheme + * @bbt: [INTERN] bad block table pointer + * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup + * @bbt_md: [REPLACEABLE] bad block table mirror descriptor + * @priv: [OPTIONAL] pointer to private chip date */ + struct nand_chip { unsigned long IO_ADDR_R; unsigned long IO_ADDR_W; - void (*hwcontrol)(int cmd); - int (*dev_ready)(void); + + u_char (*read_byte)(struct mtd_info *mtd); + void (*write_byte)(struct mtd_info *mtd, u_char byte); + u16 (*read_word)(struct mtd_info *mtd); + void (*write_word)(struct mtd_info *mtd, u16 word); + + void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); + int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); - void (*calculate_ecc)(const u_char *dat, u_char *ecc_code); - int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc); - void (*enable_hwecc)(int mode); + int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + void (*enable_hwecc)(struct mtd_info *mtd, int mode); + void (*erase_cmd)(struct mtd_info *mtd, int page); + int (*scan_bbt)(struct mtd_info *mtd); int eccmode; int eccsize; + int eccsteps; int chip_delay; - spinlock_t chip_lock; + spinlock_t chip_lock; wait_queue_head_t wq; nand_state_t state; int page_shift; + int phys_erase_shift; + int bbt_erase_shift; + int chip_shift; u_char *data_buf; + u_char *oob_buf; + int oobdirty; u_char *data_poi; + unsigned int options; + int badblockpos; + int numchips; + unsigned long chipsize; + int pagemask; + int pagebuf; + struct nand_oobinfo *autooob; + uint8_t *bbt; + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; + void *priv; }; /* @@ -187,46 +327,35 @@ struct nand_chip { #define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 -/* - * NAND Flash Device ID Structure - * - * Structure overview: - * - * name - Identify the device type - * - * id - device ID code - * - * chipshift - total number of address bits for the device which - * is used to calculate address offsets and the total - * number of bytes the device is capable of. - * - * page256 - denotes if flash device has 256 byte pages or not. - * - * pageadrlen - number of bytes minus one needed to hold the - * complete address into the flash array. Keep in - * mind that when a read or write is done to a - * specific address, the address is input serially - * 8 bits at a time. This structure member is used - * by the read/write routines as a loop index for - * shifting the address out 8 bits at a time. - * - * erasesize - size of an erase block in the flash device. +/** + * struct nand_flash_dev - NAND Flash Device ID Structure + * + * @name: Identify the device type + * @id: device ID code + * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 + * If the pagesize is 0, then the real pagesize + * and the eraseize are determined from the + * extended id bytes in the chip + * @erasesize: Size of an erase block in the flash device. + * @chipsize: Total chipsize in Mega Bytes + * @options: Bitfield to store chip relevant options */ struct nand_flash_dev { - char * name; + char *name; int id; - int chipshift; + unsigned long pagesize; + unsigned long chipsize; unsigned long erasesize; - char page256; + unsigned long options; }; -/* - * NAND Flash Manufacturer ID Structure - * - * name - Manufacturer name - * - * id - manufacturer ID code of device. +/** + * struct nand_manufacturers - NAND Flash Manufacturer ID Structure + * @name: Manufacturer name + * @id: manufacturer ID code of device. */ struct nand_manufacturers { int id; @@ -236,9 +365,85 @@ struct nand_manufacturers { extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; +/** + * struct nand_bbt_descr - bad block table descriptor + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE + * when bbt is searched, then we store the found bbts pages here. + * Its an array and supports up to 8 chips now + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob are of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This number of + * blocks is reserved at the end of the device where the tables are + * written. + * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than + * bad) block in the stored bbt + * @pattern: pattern to identify bad block table or factory marked good / + * bad blocks, can be NULL, if len = 0 + * + * Descriptor for the bad block table marker and the descriptor for the + * pattern which identifies good and bad blocks. The assumption is made + * that the pattern and the version count are always located in the oob area + * of the first block. + */ +struct nand_bbt_descr { + int options; + int pages[NAND_MAX_CHIPS]; + int offs; + int veroffs; + uint8_t version[NAND_MAX_CHIPS]; + int len; + int maxblocks; + int reserved_block_code; + uint8_t *pattern; +}; + +/* Options for the bad block table descriptors */ + +/* The number of bits used per block in the bbt on the device */ +#define NAND_BBT_NRBITS_MSK 0x0000000F +#define NAND_BBT_1BIT 0x00000001 +#define NAND_BBT_2BIT 0x00000002 +#define NAND_BBT_4BIT 0x00000004 +#define NAND_BBT_8BIT 0x00000008 +/* The bad block table is in the last good block of the device */ +#define NAND_BBT_LASTBLOCK 0x00000010 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_ABSPAGE 0x00000020 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_SEARCH 0x00000040 +/* bbt is stored per chip on multichip devices */ +#define NAND_BBT_PERCHIP 0x00000080 +/* bbt has a version counter at offset veroffs */ +#define NAND_BBT_VERSION 0x00000100 +/* Create a bbt if none axists */ +#define NAND_BBT_CREATE 0x00000200 +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000400 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00000800 +/* Write bbt if neccecary */ +#define NAND_BBT_WRITE 0x00001000 +/* Read and write back block contents when writing bbt */ +#define NAND_BBT_SAVECONTENT 0x00002000 +/* Search good / bad pattern on the first and the second page */ +#define NAND_BBT_SCAN2NDPAGE 0x00004000 + +/* The maximum number of blocks to scan for a bbt */ +#define NAND_BBT_SCAN_MAXBLOCKS 4 + +extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); +extern int nand_default_bbt (struct mtd_info *mtd); +extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); +extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); + /* * Constants for oob configuration */ -#define NAND_BADBLOCK_POS 5 +#define NAND_SMALL_BADBLOCK_POS 5 +#define NAND_LARGE_BADBLOCK_POS 0 #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index c3b493c99..12c5bc342 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -3,7 +3,7 @@ * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: nand_ecc.h,v 1.2 2003/02/20 13:34:20 sjhill Exp $ + * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $ * * 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 @@ -12,17 +12,19 @@ * This file is the header for the ECC algorithm. */ -/* - * Creates non-inverted ECC code from line parity - */ -void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code); +#ifndef __MTD_NAND_ECC_H__ +#define __MTD_NAND_ECC_H__ + +struct mtd_info; /* * Calculate 3 byte ECC code for 256 byte block */ -void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); /* * Detect and correct a 1 bit error for 256 byte block */ -int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + +#endif /* __MTD_NAND_ECC_H__ */ diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index fd57ffddd..d35d2c21f 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -1,5 +1,5 @@ /* - * $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $ + * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $ * * (C) 1999-2003 David Woodhouse */ @@ -10,71 +10,7 @@ #include #include -/* Block Control Information */ - -struct nftl_bci { - unsigned char ECCSig[6]; - __u8 Status; - __u8 Status1; -}__attribute__((packed)); - -/* Unit Control Information */ - -struct nftl_uci0 { - __u16 VirtUnitNum; - __u16 ReplUnitNum; - __u16 SpareVirtUnitNum; - __u16 SpareReplUnitNum; -} __attribute__((packed)); - -struct nftl_uci1 { - __u32 WearInfo; - __u16 EraseMark; - __u16 EraseMark1; -} __attribute__((packed)); - -struct nftl_uci2 { - __u16 FoldMark; - __u16 FoldMark1; - __u32 unused; -} __attribute__((packed)); - -union nftl_uci { - struct nftl_uci0 a; - struct nftl_uci1 b; - struct nftl_uci2 c; -}; - -struct nftl_oob { - struct nftl_bci b; - union nftl_uci u; -}; - -/* NFTL Media Header */ - -struct NFTLMediaHeader { - char DataOrgID[6]; - __u16 NumEraseUnits; - __u16 FirstPhysicalEUN; - __u32 FormattedSize; - unsigned char UnitSizeFactor; -} __attribute__((packed)); - -#define MAX_ERASE_ZONES (8192 - 512) - -#define ERASE_MARK 0x3c69 -#define SECTOR_FREE 0xff -#define SECTOR_USED 0x55 -#define SECTOR_IGNORE 0x11 -#define SECTOR_DELETED 0x00 - -#define FOLD_MARK_IN_PROGRESS 0x5555 - -#define ZONE_GOOD 0xff -#define ZONE_BAD_ORIGINAL 0 -#define ZONE_BAD_MARKED 7 - -#ifdef __KERNEL__ +#include /* these info are used in ReplUnitTable */ #define BLOCK_NIL 0xffff /* last block of a chain */ @@ -101,6 +37,7 @@ struct NFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; + struct nand_oobinfo oobinfo; }; int NFTL_mount(struct NFTLrecord *s); @@ -114,6 +51,4 @@ int NFTL_formatblock(struct NFTLrecord *s, int block); #define MAX_SECTORS_PER_UNIT 64 #define NFTL_PARTN_BITS 4 -#endif /* __KERNEL__ */ - #endif /* __MTD_NFTL_H__ */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 5c5f8770b..100ea79de 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: partitions.h,v 1.14 2003/05/20 21:56:29 dwmw2 Exp $ + * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $ */ #ifndef MTD_PARTITIONS_H @@ -50,7 +50,7 @@ struct mtd_partition { #define MTDPART_SIZ_FULL (0) -int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); +int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); /* diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 93f7492ed..2307f11d8 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -130,6 +130,13 @@ struct rs_multiport_struct { * reason (mips != alpha!) */ #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) +#elif defined(CONFIG_SBC8560) +/* + * WindRiver did something similarly broken on their SBC8560 board. The + * UART tristates its IRQ output while OUT2 is clear, but they pulled + * the interrupt line _up_ instead of down, so if we register the IRQ + * while the UART is in that state, we die in an IRQ storm. */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) #else #define ALPHA_KLUDGE_MCR 0 #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 1ad37531b..4e57b4566 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -86,6 +86,9 @@ /* PPC CPM type number */ #define PORT_CPM 58 +/* Marvell MPSC for PPC & MIPS */ +#define PORT_MPSC 59 + #ifdef __KERNEL__ #include diff --git a/mm/shmem.c b/mm/shmem.c index 58c9b3ca5..f0e5fc7b6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -337,7 +337,6 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); struct page *page = NULL; swp_entry_t *entry; - static const swp_entry_t unswapped = { 0 }; if (sgp != SGP_WRITE && ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) @@ -345,7 +344,7 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long while (!(entry = shmem_swp_entry(info, index, &page))) { if (sgp == SGP_READ) - return (swp_entry_t *) &unswapped; + return shmem_swp_map(ZERO_PAGE(0)); /* * Test free_blocks against 1 not 0, since we have 1 data * page (and perhaps indirect index pages) yet to allocate: -- 2.47.0