Merge with /home/wd/git/u-boot/testing-NAND/ to add new NAND handling.
authorBartlomiej Sieka <tur@semihalf.com>
Fri, 24 Feb 2006 08:37:22 +0000 (09:37 +0100)
committerBartlomiej Sieka <tur@pollux.(none)>
Fri, 24 Feb 2006 08:37:22 +0000 (09:37 +0100)
24 files changed:
1  2 
CHANGELOG
Makefile
board/dave/PPChameleonEVB/Makefile
board/dave/PPChameleonEVB/PPChameleonEVB.c
board/dave/PPChameleonEVB/config.mk
board/dave/PPChameleonEVB/nand.c
board/netstar/config.mk
board/netstar/crcek
board/netstar/crcit
board/netstar/eeprom
common/Makefile
common/cmd_jffs2.c
common/cmd_nand.c
drivers/nand/diskonchip.c
drivers/nand/nand.c
drivers/nand/nand_base.c
drivers/nand/nand_bbt.c
drivers/nand/nand_ecc.c
drivers/nand/nand_ids.c
fs/jffs2/jffs2_1pass.c
include/configs/PPChameleonEVB.h
include/linux/mtd/nand.h
include/linux/mtd/nand_new.h
include/nand.h

diff --cc CHANGELOG
+++ b/CHANGELOG
  ======================================================================
 -Changes for U-Boot 1.1.4:
 +Changes since U-Boot 1.1.4:
  ======================================================================
  
 -* Rewrite of NAND code based on what is in 2.6.12 Linux kernel
++* Merge the new NAND code (testing-NAND brach)
++
++  Rewrite of NAND code based on what is in 2.6.12 Linux kernel
+   Patch by Ladislav Michl, 29 Jun 2005
 +* Update default environment for INKA4x00 board.
 +
 +* Cleanup U-Boot boot messages on ARM.
 +
 +  To match the U-Boot user interface on ARM platforms to the U-Boot
 +  standard (as on PPC platforms), some messages with debug character
 +  are removed from the default U-Boot build.
 +  Enable DEBUG for lib_arm/board.c to enable debug messages.
 +  New CONFIG_DISPLAY_CPUINFO and CONFIG_DISPLAY_BOARDINFO options.
 +  Patch  by Stefan Roese, 24 Jan 2006
 +
 +* Fix various compiler warnings on ppc4xx builds (ELDK 4.0)
 +  Patch by Stefan Roese, 18 Jan 2006
 +
 +* Add VGA support (CT69000) to CPCI750 board.
 +  Insert missing __le32_to_cpu() for filesize in ext2fs_read_file().
 +  Patch by Reinhard Arlt, 30 Dec 2005
 +
 +* PMC405 and CPCI405: Moved configuration of pci resources
 +  into config file.
 +  PMC405 and CPCI2DP: Added firmware download and booting via pci.
 +  Patch by Matthias Fuchs, 20 Dec 2005
 +
 +* Fix 28F256J3A support on PM520 board
 +  (without bank-switching only 32 MB can be accessed)
 +
 +* Fix mkimage bug with multifile images created on 64 bit systems.
 +
 +* Add support for 28F256J3A flash (=> 64 MB) on PM520 board
 +
 +* Fix compiler problem with at91rm9200dk board.
 +  Patch by Eugen Bigz, 19 Dec 2005
 +
 +======================================================================
 +Changes for U-Boot 1.1.4:
 +======================================================================
 +
 +* Changes to Yellowstone & Yosemite 440EP/GR eval boards:
 +  - Changed GPIO setup to enable another address line in order to
 +    address 64M of FLASH.
 +  - Added function sdram_tr1_set to auto calculate the tr1 value for
 +    the DDR.
 +  Patch by Steven Blakeslee, 12 Dec 2005
 +
 +* MPC5200:  Set PCI retry counter to 0 = infinite retry;
 +  The default of 255 is too short for slow devices.
 +  Patch by Martin Nykodym, 12 Dec 2005
 +
 +* Change port configuration for O2DNT (CODEC1 on PSC1).
 +
 +* Fix register for PCI async mode on PPC440EP
 +  Patch by Youngchul Bang, 08 Dec 2005
 +
 +* Fix U-Boot linking problems (add .eh_frame segment to linker script)
 +  This segment may be required by some libgcc.a functions
 +  (like _udivdi3).
 +
 +* Fix DPRAM offset/size for MPC8541/8555.
 +  Simplify TQM85xx Makefile handling.
 +
 +* Fix data overflow (typo?) in rtc/ds1302.c
 +
 +* Fix U-Boot compilation for MIPS boards using ELDK 4.0
 +
 +* Add support for TQM8541/8555 boards, TQM85xx support reworked:
 +  - Support for TQM8541/8555 boards added.
 +  - Complete rework of TQM8540/8560 support.
 +  - Common TQM85xx code now supports all current TQM85xx platforms
 +    (TQM8540/8541/8555/8560).
 +  - DDR SDRAM size detection added.
 +  - CAS latency default values can be overwritten by setting "serial#"
 +    to e.g. "ABC0001 casl=25" -> CAS latency 2.5 will be used.
 +    If problems are detected with this non default CAS latency,
 +    the default values will be used instead.
 +  - Flash size detection added.
 +  - Moved FCC ethernet driver initialization behind TSEC driver init
 +    -> TSEC is first device.
 +  Patch by Stefan Roese, 30 Nov 2005
 +
 +* Add support for AMCC 440SP, add support for AMCC Luan 440SP eval board.
 +  Patch by John Otken, 23 Nov 2005
 +
 +* Changed PPC44x startup message (cpu info, speed...) to common style:
 +  On PPC44x platforms, the startup message generated in "cpu.c" only
 +  comprised the ppc type and revision but not additional information
 +  like speed etc. Those speed infos where printed in the board specific
 +  code. This new implementation now prints all CPU infos in the common
 +  cpu specific code. No board specific code is needed anymore and
 +  therefore removed from all current 44x implementations.
 +  Patch by Stefan Roese, 27 Nov 2005
 +
 +* Adjust TQM834x PHY addresses for latest hardware revision.
 +
 +* Increase malloc arena on TQM5200 board to 256 kB.
 +  With 64 kb uniform flash sector size the old value of 128 kB was
 +  too small.
 +
 +* Fix miiphy global data initialization (problem on 4xx boards when
 +  no ethaddr is assigned). Initialization moved from
 +  miiphy_register() to eth_initialize().
 +
 +  Based on initial patch for 4xx platform by Matthias Fuchs.
 +
 +* Remove unnnecessary #include <linux/types.h> from include/asm-*/u-boot.h
 +
 +* Allow use of include/image.h and include/asm-*/u-boot.h in proprietary code.
 +  The COPYING file was extended to make clear that these files can be
 +  used in non-GPL code, too.
 +  Also, a corresponding note was placed in the headers of the affected files.
 +
 +* Add support for Prodrive P3P440 board:
 +  - Added onboard PPC440 DDR autodetection in cpu/ppc/sdram.c
 +  - CFG_FLASH_QUIET_TEST added to use the common CFI driver
 +    for bank autodetection
 +  Patch by Stefan Roese, 22 Nov 2005
 +
 +* Change all '$(...)' variable references into '${...}'
 +  which makes the environment compatible with the hush shell.
 +  WARNING: Support for the old '$(...)' syntax will be
 +  discontinued in a later version.
 +
 +* Minor changes to init flags in TQM834x PCI.
 +
 +* Fix Bamboo DDR SDRAM initialization (problem with onboard SDRAM)
 +  Patch by Stefan Roese, 15 Nov 2005
 +
 +* New PPC 405EP board added: CMS700
 +  Added CONFIG_NET_MULTI for VOM405 board.
 +  Added reset_phy() for VOM405 board.
 +  Patch by Matthias Fuchs, 09 Nov 2005
 +
 +* Updated PCI mapping for esd CPCI2DP board.
 +  Add support for error LED.
 +  Patch by Matthias Fuchs, 07 Nov 2005
 +
 +* Fix MPC85xx PCI support (pci_register_hose() before pci config access)
 +  Patch by Stefan Roese, 07 Nov 2005
 +
 +* Correct PPC Timebase register definitions (SPRN_TBRL...)
 +  Patch by Stefan Roese, 07 Nov 2005
 +
 +* Adjust bd->bi_flashstart on Yellowstone & Yosemite to correct size
 +  Patch by Stefan Roese, 05 Nov 2005
 +
 +* Additional fix for external IRQ config on Yellowstone & Yosemite
 +  Patch by Stefan Roese, 03 Nov 2005
 +
 +* Add support for Ocotea pass 3 with 440GX Rev. F
 +  Patch by Stefan Roese, 01 Nov 2005
 +
 +* Fix external IRQ configuration on Yellowstone & Yosemite
 +  Patch by Stefan Roese, 28 Oct 2005
 +
 +* Add support for multiple PHYs.
 +  Tested on the following boards:
 +      cmcpu2      (at91rm9200/ether.c)
 +      PPChameleon (ppc4xx/4xx_enet.c)
 +      yukon       (mpc8220/fec.c)
 +      uc100       (mpc8xx/fec.c)
 +      tqm834x     (mpc834x/tsec.c) with EEPRO100
 +      lite5200    (mpc5xxx/fec.c) with EEPRO100 card (drivers/eepro100.c)
 +  Main changes include:
 +  common/miiphyutil.c
 +  - miiphy_register routine was added to allow multiple PHYs to be registered
 +  - miiphy_read and miiphy_write are now defined in this file, and
 +    require additional argument (char *devname)
 +  - other miiphy_* routines also require additional device name argument
 +  ../lib_i386/board.c
 +  ../lib_ppc/board.c
 +  Calling reset_phy() was moved to be executed *after* eth_initialize().
 +  This is necessary as now some of the implementations of reset_phy()
 +  may need to use miiphy_reset() which is not allowed before eth_initialize()
 +  as eth_initialize registers all required miiphy_* routines.
 +  Tested on IP860 and PHY initializes properly after this change.
 +
 +* Correct includes for flat tree builder.
 +
 +* Fix conflicting types (flash_write()) in trab auto_update.c.
 +
 +* Add PCI support for the TQM834x board.
 +
 +* Add missing 4xx board to MAKEALL
 +  Patch by Stefan Roese, 20 Oct 2005
 +
 +* Fix conflicting types (flash_write()) in esd auto_update.c
 +  Patch by Stefan Roese, 20 Oct 2005
 +
 +* Fix problem with sleep in NetConsole (use get_timer())
 +  Patch by Stefan Roese, 20 Oct 2005
 +
 +* Add NetConsole Support for AMCC eval boards
 +  Patch by Stefan Roese, 20 Oct 2005
 +
 +* Fix NetConsole support on 4xx (only print eth link on 1st transfer)
 +  Patch by Stefan Roese, 18 Oct 2005
 +
 +* Add fat & ext2 support to AMCC 440EP boards Yosemite & Bamboo.
 +  Fix identation on ext2ls help entry.
 +  Patch by Stefan Roese, 14 Oct 2005
 +
 +* Add support for TQM834x boards.
 +  Cleanup.
 +
 +* Cleanup for GCC-4.x
 +
 +* Add documentation for Open Firmware Flat Tree and usage.
 +  Patch by Pantelis Antoniou, 13 Oct 2005
 +
 +* Add missing files for Pantelis Antoniou's patch
 +  Patch by Pantelis Antoniou, 04 Sep 2005
 +
 +* Fix problem in ppc4xx eth-driver without ethaddr (only without
 +  CONFIG_NET_MULTI set)
 +  Patch by Stefan Roese, 10 Oct 2005
 +
 +* Fix gzip bmp support (test if malloc fails, warning when truncated).
 +  Increase CFG_VIDEO_LOGO_MAX_SIZE on HH405 board.
 +  Patch by Stefan Roese, 07 Oct 2005
 +
 +* Add support for OF flat tree for the STXtc board.
 +  Patch by Pantelis Antoniou, 04 Sep 2005
 +
 +* Support passing of OF flat trees to the kernel.
 +  Patch by Pantelis Antoniou, 04 Sep 2005
 +
 +* Cleanup
 +
 +* Add support for NetSilicon NS7520 processor.
 +  Patch by Art Shipkowski, 12 May 2005
 +
 +* Add support for AP1000 board.
 +  Patch by James MacAulay, 07 Oct 2005
 +
 +* Eliminate hard-coded address of Ethernet transfer buffer on at91rm9200
 +  Patch by Anders Larsen, 07 Oct 2005
 +
 +  The Atmel errata #11 states that the transfer buffer descriptor
 +  table must be aligned on a 16-word boundary. As it turned out, this
 +  is insufficient - it seems the table must be aligned on a boundary
 +  at least as large as the table itself (in Linux this is not an
 +  issue - the table is aligned on a PAGE_SIZE (4096) boundary).
 +
 +* Fixed compilation for ARM when using a (standard) hard-FP toolchain
 +  Patch by Anders Larsen, 07 Oct 2005
 +
 +* Cleanup warnings for cpu/arm720t & cpu/arm1136 files.
 +  sed the linker scripts, rather than pre-process them.
 +  Patch by Peter Pearse, 07 Oct 2005
 +
 +* Update make target for ARM supported boards.
 +  Use lowlevel_init() instead of platformsetup() [rename].
 +  Patch by Peter Pearse, 06 Oct 2005
 +
 +* Fix booting from serial dataflash on AT91RM9200
 +  Patch by Peter Menzebach, 29 Aug 2005
 +
 +* Add JFFS2 support for TRAB board
 +  Patch by Martin Krause, 25 Aug 2005
 +
 +* Remove unnecessary dependency of netconsole on CONFIG_NET_MULTI
 +  Patch by Marcus Hall, 24 Aug 2005
 +
 +* Fix the machine-id of the Cogent csb637 board
 +  Patch by Anders Larsen, 05 Oct 2005
 +
 +* Complete support for the KwikByte KB920x boards
 +  Patch by Anders Larsen, 05 Oct 2005
 +
 +* Set the AT91RM9200 clock to asynchronous mode
 +  Patch by Anders Larsen, 03 May 2005
 +
 +* Set the AT91RM9200 clock to synchronous mode
 +  Patch by Anders Larsen, 29 Apr 2005
 +
 +* Add support for Cogent csb637
 +  Patch by Anders Larsen, 29 Apr 2005
 +
 +* Fix dm9161.c initialization
 +  Patch by Anders Larsen, 29 Apr 2005
 +
 +* Fix problems introduced by Patch by Steven Scholz, 02 Mar 2005
 +  (8e2be51de8dd03c1ce4d06cbb18ad06133d47cd5)
 +
 +* Move dm9161.c and lxt972.c into cpu/arm920t/at91rm9200
 +  Patch by Anders Larsen, 29 Apr 2005
 +
 +* Fix device partition intialization for SystemACE disks.
 +  Patch by Stephen Williams, 28 Apr 2005
 +
 +* Added support for KwikByte KB920x boards (based on AT91RM9200)
 +  Patch by Matt ?? <kb9200_dev@kwikbyte.com>, 27 Apr 2005
 +
 +* Add support for S29GL064M-R3 flash chip on xsengine board
 +  Patch by Kurt Stremerch, 18 Apr 2005
 +
 +* E500 update: repoint IVPR to RAM when code is relocated
 +  Patch by Kylo Ginsberg, 13 Apr 2005
 +
 +* Fix loop end test in lib_generic/string.c:strswab()
 +  Patch by Andrew Dyer, October 10, 2005
 +  Signed-off-by: Andrew Dyer <amdyer@gmail.com>
 +
 +* Cleanup
 +
 +* Update ARM Integrator boards:
 +  Correct addessing errors in platform files.
 +  Split off common core module data from Integrator header files to
 +  include/armcoremodule.h.
 +  Patch by Peter Pearse, 04 Oct 2005
 +
 +* Make sure only supported compiler options are used
 +  Import "cc-option" shell function from kernel and
 +  use it to get the correct ARM GCC options for individual CPUs
 +  Patch by Peter Pearse, 30 Jun 2005
 +
 +* Fix 440GR to print correct cpu revision
 +  Patch by Stefan Roese, 04 Oct 2005
 +
 +* Change board message on AMCC Yosemite & Yellowstone to common style
 +  Patch by Stefan Roese, 03 Oct 2005
 +
 +* Fix compiler warning
 +
 +* Fix FEC PHY addresses for TQM85xx boards
 +
 +* Fix uninitialized variable problem in hush shell
 +  Patch by Lars Rostock, 26 Sep 2005
 +
 +* Undo change of f6e20fc6ca... to include/configs/trab.h
 +  (Must have been an accident?)
 +
 +* Add support for AT91RM9200 OHCI Controller.
 +  Patch by Eric Benard, 07 Apr 2005
 +
 +* Update ARM mach-types.h
 +  Patch by Eric Benard, 07 Apr 2005
 +
 +* Add support for MP2USB board.
 +  Patch by Eric Benard, 07 Apr 2005
 +
 +* Add board support for armadillo HT1070
 +  Patch by Rowel Atienza, 06 Apr 2005
 +
 +* Second Ethernet address enabled for MPC885ADS and MPC8272ADS.
 +  Patch by Vitaly Bordug, 30 Mar 2005
 +
 +* Add iopset command on mpc8xx
 +  Patch by Daniel Eisenhut, 25 Mar 2005
 +
 +* Add support for MII in eepro100 driver.
 +  Patch by Gleb Natapov, 21 Mar 2005
 +
 +* Fixes to the Lubbock (PXA 25x) support:
 +  - Resolve the FIXME with respect to saving the u-boot environment.
 +  - Make the default load address land in real memory.
 +  - Fix lan91c96 SMC_{in,out}{b,w,l}() macros
 +  Patch by David Brownell, 10 Mar 2005
 +
 +* Add Barco Streaming Video Card (SVC) and Sample Compress Network (SCN) board
 +  Patch by Marc Leeman, 04 Mar 2005
 +
 +* OMAP242x H4 board update
 +  - fix for ES2 differences.
 +  - switch to using the cfi_flash driver.
 +  - fix SRAM build address.
 +  - fix for GP device operation.
 +  - unlock SRAM for GP devices.
 +  - display more device information.
 +  - fix potential deadlock in omap24xx_i2c driver.
 +  - fix DLL load values to match dpllout*1 operation.
 +  - fix 2nd chip select init for combo DDR device.
 +  - add support for CFI Intel 28F256L18 on H4 board.
 +  Patch by Richard Woodruff, 03 Mar 2005
 +
 +* Fix formating in include/asm-arm/arch-at91rm9200/AT91RM9200.h
 +  Patch by Steven Scholz, 02 Mar 2005
 +
 +* Fix typo in eth.c
 +  Patch by Ara Avanesyan, 24 Feb 2005
 +
 +* Remove unneeded #include <malloc.h>
 +  Patch by Ladislav Michl, 22 Feb 2005
 +
 +* Add cramfs support for m68k
 +  Patch by Zachary Landau, 21 Feb 2005
 +
 +* Update ep8260: Fix flash timeouts; improve clock resolution for faster UARTs
 +  Patch by Jeff Angielski, 21 Feb 2005
 +
 +* Fix au1x00_serial baud rate calculation:
 +  remove hardcoded cpu clock divisor and use register instead;
 +  round up instead of truncate
 +  Patch by Andrew Dyer, 15 Feb 2005
 +
 +* Add Xilinx Spartan3 family FPGA support
 +  Patch by Kurt Stremerch, 14 Feb 2005
 +
 +* Fix drivers/cfi_flash.c: use info->reset_cmd instead of FLASH_CMD_RESET
 +  Patch by Zachary Landau, 11 Feb 2005
 +
 +* Fix VOH405 Support
 +  Patch by Matthias Fuchs, 25 Sep 2005
 +
 +* Added support for PCI bridge on MPC8272ADS
 +  Patch by Vitaly Bordug, Feb 09 2005
 +
 +* Update multicore CM9XX support for Integrator AP to allow booting from flash
 +  Patch by Jean-Paul Saman, 8 Feb 2005
 +
 +* Fix strswab() to reliably find end of string
 +  Patch by Andrew Dyer, 08 Feb 2005
 +
 +* Fix typos in include/ppc440.h
 +  Patch by Andrew E Mileski, 04 Feb 2005
 +
 +* Add Vibren (was Accelent) PXA255 IDP Support
 +  Patch by Cliff Brake, 04 Feb 2005
 +
 +* Fix tools/bmp_logo.c using incorrect offset to pixel data
 +  Patch by Andrew Dyer, 31 Jan 2005
 +
 +* Add ARM946E cpu and core module targets; remap memory to 0x00000000
 +  Patch by Peter Pearse, 2 Feb 2005
 +
 +* Fix error handling in tools/env/fw_env.c
 +  Patch by Ara Avanesyan, 01 Feb 2005
 +
 +* Fix MGT5100 PSC baudrate calculation
 +  Patch by Sebastian Schau, 27 Jan 2005
 +
 +* OMAP242x fix for GP device booting
 +  - Add SRAM unlock for GP devices.
 +  - Change DDR DLL unlock value to allow DPLLout*1 operation.
 +  Patches by Richard Woodruff, 21 Jan 2005:
 +
 +* Add support for AMD's Pb1x00 eval board;
 +  add MII routines to the au1x00 ethernet driver;
 +  add USB ohci driver (work in progress)
 +  Patch by Thomas Sailer, 20 Jan 2005
 +
 +* Update omap5912osk board
 +  Use drivers/cfi_flash.c instead of private flash driver;
 +  Remove hardcoded personalized settings from omap5912osk.h;
 +  Fix spacing with (RO) marks in 'flinfo' output.
 +  Patch by Michael Bendzick, 14 Jan 2005
 +
 +* Fix warnings for PCI code on ixp
 +  Patch by Joe <lgxue@yahoo.com>, 13 Jan 2005
 +
 +* virtex2 fix for bogus download error messages
 +  The virtex2 FPGA download code watches for init going active during
 +  a download of config data as an error condition. init also goes
 +  active after a configuration is finished in concert with the done
 +  signal. So far, the code does not check for done active until all
 +  of the configuration data is sent. If configuration data has a few
 +  extra pad bytes at the end, this would cause an error message even
 +  though the download had suceeded.
 +  NOTE: virtex2 slave serial and spartan2 versions may still have the
 +  same problem.
 +  Patch by Andrew Dyer, 12 Jan 2005
 +
 +* Optimize flash_make_cmd in drivers/cfi_flash.c for little endian
 +  Fix "WARNING: flash_make_cmd: unsuppported LittleEndian mode"
 +  message when probing for nonexistent flash in little endian mode.
 +  As a side effect more efficient and smaller code is generated,
 +  which is always a Good Thing (TM).
 +  Patch by Ladislav Michl, 24 Sep 2005
 +
 +* Update for TFTP using a fixed UDP port
 +  Use the approved environment variable names. Added "tftpdstp" to
 +  allow ports other than 69 per Tolunay Orkun's recommendation.
 +  Patch by Jerry Van Baren, 12 Jan 2005
 +
 +* Allow to force TFTP to use a fixed UDP port
 +  (Add a configuration option CONFIG_TFTP_PORT and optional env
 +  variable tftpport)
 +  Patch by Jerry Van Baren, 10 Jan 2005
 +
 +* Fix ethernet timeouts on dbau1550 and other au1x00 systems
 +  Patch by Leif Lindholm, 29 Dec 2004
 +
 +* Cleanup: fix broken builds
 +
 +* Fix PHY address argument passing with mii info command
 +  Patch by Andrew Dyer, 28 Dec 2004
 +
 +* Cleanup (PPC4xx is AMCC now)
 +
 +* esd CPCI2DP board added
 +  Patch by Matthias Fuchs, 22 Sep 2005
 +
 +* esd PMC405 board updated
 +  Patch by Matthias Fuchs, 22 Sep 2005
 +
 +* Add SM501 support to HH405 board.
 +  Add support for gzip compressed bmp's (CONFIG_VIDEO_BMP_GZIP).
 +  Add support for eeprom write-enable (CFG_EEPROM_WREN).
 +  Patch by Stefan Roese, 22 Sep 2005
 +
 +* Fix autonegotiation in tsec ethernet driver
 +  Patch by Stefan Roese, 21 Sep 2005
 +
 +* Fix bug in auto_update (trab board)
 +  Patch by Martin Krause, 16 Sep 2005
 +
 +* Fix computation of framebuffer palette for 8bpp LCD bitmaps
 +  Patch by Francesco Mandracci, 16 Sep 2005
 +
 +* Update configuration for INKA4x0 board
 +
 +* Update configuration for PM854 board
 +  Based on patch by R. Loeffl, 20 Jul 2005
 +
 +* Add PCI support to TQM8540 and TQM8560 boards
 +  Patch by Stefan Roese, 15 Sep 2005
 +
 +* Update AMCC Yosemite to get a consistent setup for all AMCC eval
 +  boards (baudrate, environment...). Flash driver fixed.
 +  Patch by Stefan Roese, 15 Sep 2005
 +
 +* Fix problem in 440GP ethernet driver (ebony). Add support for 2nd
 +  ethernet port on ebony.
 +  Patch by Stefan Roese, 7 Sep 2005
 +
 +* Added support for mtddevnum and mtddevname variables (mtdparts command)
 +
 +* Change default console baud rate for stxxtc board
 +
 +* Add I2C support to TQM8540 and TQM8560 boards (EEPROM, RTC, LM75-DTT).
 +  Patch by Stefan Roese, 31 Aug 2005
 +
 +* Fix default command set (don't include CFG_CMD_DISPLAY command)
 +  Patch by Pantelis Antoniou, 02 Sep 2005
 +
 +* Cleanup
 +
 +* Enable SM712 driver support for HMI1001 board.
 +
 +* Fix problems with ld version 2.16 (dot outside sections problem)
 +  Pointed out by Gerhard Jaeger, 31 Aug 2005;
 +  cf. http://sourceware.org/ml/binutils/2005-08/msg00412.html
 +
 +* Prepare U-Boot for gcc-4.x: fix global data pointer initialization
 +
 +* Adjust CS3 timings on HMI1001 board for dot matrix display under Linux
 +
 +* Add keyboard and dot matrix display support for HMI1001 board.
 +
 +* Prepare U-Boot for gcc-4.x
 +
 +* Fixed Bamboo port to enable running without DDR-DIMM
 +  (Bamboo has also 64MB onboard DDR)
 +  Patch by Stefan Roese, 24 Aug 2005
 +
 +* Merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c
 +  now handling all 4xx cpu's
 +  Patch by Stefan Roese, 16 Aug 2005
 +
 +* Fix make dependencies for at91rm9200 and ks8695 cpus
 +  Patch by Steven Scholz, 23 Aug 2005
 +
 +* Add JFFS2 support for TQM5200 board
 +
 +* Add esd cpci5200 and pf5200 boards
 +  Patch by Reinhard Arlt, 22 Aug 2005
 +
 +* Fix sysclock for TQM8540 and TQM8560 boards
 +  Patch by Martin Krause, 25 Jul 2005
 +
 +* Initialize serial# and ethaddr from manufacturer data in EEPROM on CMC-PU2
 +  Patch by Martin Krause, 08 Jun 2005
 +
 +* Add new board specific commands for TQM5200/STK52XX
 +  - Sound commands (beep, wav, sound)
 +  - Test commands (led, can, backlight, rs232)
 +  Patch by Martin Krause, 02 May 2005
 +
 +* Change main clock on CMC-PU2 board from 207 MHz to 179 MHz
 +  because of a bug in the AT91RM9200 CPU PLL
 +  Patch by Martin Krause, 22 Apr 2005
 +
 +* Add automatic HW detection for another CMC_PU2 variant
 +  Patch by Martin Krause, 20 Apr 2005
 +
 +* Remove CONFIG_AT91RM9200DK in CMC-PU2 configuration
 +  Patch by Martin Krause, 19 Apr 2005
 +
 +* Fix initialization problem on TQM5200 without SM501
 +  Patch by Martin Krause, 08 Apr 2005
 +
 +* Add RTC support for STK52XX.200
 +  Patch by Martin Krause, 07 Apr 2005
 +
 +* Add support for IFM o2dnt board
 +
  * Enable PCI on hmi1001 board
  
  * Fix return values of the jffs2 commands ls/fsload/fsinfo,
diff --cc Makefile
+++ b/Makefile
@@@ -121,6 -121,7 +121,7 @@@ LIBS += drivers/libdrivers.
  LIBS += drivers/sk98lin/libsk98lin.a
  LIBS += post/libpost.a post/cpu/libcpu.a
  LIBS += common/libcommon.a
++LIBS += $(BOARDLIBS)
  .PHONY : $(LIBS)
  
  # Add GCC lib
@@@ -25,7 -25,7 +25,7 @@@ include $(TOPDIR)/config.m
  
  LIB   = lib$(BOARD).a
  
--OBJS  = $(BOARD).o flash.o
++OBJS  = $(BOARD).o flash.o nand.o
  
  $(LIB):       $(OBJS) $(SOBJS)
        $(AR) crv $@ $^
@@@ -238,33 -238,33 +238,6 @@@ int testdram (void
  
  /* ------------------------------------------------------------------------- */
  
--#if (CONFIG_COMMANDS & CFG_CMD_NAND)
--extern ulong
--nand_probe(ulong physadr);
--
--void
--nand_init(void)
--{
--      ulong totlen = 0;
--
--/*
--      The HI model is equipped with a large block NAND chip not supported yet
--      by U-Boot
--    (CONFIG_PPCHAMELEON_MODULE_MODEL == CONFIG_PPCHAMELEON_MODULE_HI)
--*/
--
--#if (CONFIG_PPCHAMELEON_MODULE_MODEL == CONFIG_PPCHAMELEON_MODULE_ME)
--      debug ("Probing at 0x%.8x\n", CFG_NAND0_BASE);
--      totlen += nand_probe (CFG_NAND0_BASE);
--#endif        /* CONFIG_PPCHAMELEON_MODULE_ME, CONFIG_PPCHAMELEON_MODULE_HI */
--
--      debug ("Probing at 0x%.8x\n", CFG_NAND1_BASE);
--      totlen += nand_probe (CFG_NAND1_BASE);
--
--      printf ("%3lu MB\n", totlen >>20);
--}
--#endif
--
  #ifdef CONFIG_CFB_CONSOLE
  # ifdef CONFIG_CONSOLE_EXTRA_INFO
  # include <video_fb.h>
@@@ -1,5 -1,5 +1,5 @@@
  #
--# (C) Copyright 2000
++# (C) Copyright 2000, 2006
  # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  #
  # See file CREDITS for list of people who contributed to this
  #
  
  # Reserve 256 kB for Monitor
--TEXT_BASE = 0xFFFC0000
++#TEXT_BASE = 0xFFFC0000
  
  # Reserve 320 kB for Monitor
--#TEXT_BASE = 0xFFFB0000
++TEXT_BASE = 0xFFFB0000
++
++# Compile the new NAND code (needed iff #ifdef CONFIG_NEW_NAND_CODE)
++BOARDLIBS = drivers/nand/libnand.a
index 0000000,0000000..16c67cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,147 @@@
++/*
++ * (C) Copyright 2006 DENX Software Engineering
++ *
++ * See file CREDITS for list of people who contributed to this
++ * project.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include <common.h>
++
++#if (CONFIG_COMMANDS & CFG_CMD_NAND)
++#ifdef CONFIG_NEW_NAND_CODE
++/* new NAND handling */
++
++#include <nand.h>
++
++/*
++ * hardware specific access to control-lines
++ * function borrowed from Linux 2.6 (drivers/mtd/nand/ppchameleonevb.c)
++ */
++static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
++{
++      struct nand_chip *this = mtdinfo->priv;
++      ulong base = (ulong) this->IO_ADDR_W;
++
++      switch(cmd) {
++      case NAND_CTL_SETCLE:
++              MACRO_NAND_CTL_SETCLE((unsigned long)base);
++              break;
++      case NAND_CTL_CLRCLE:
++              MACRO_NAND_CTL_CLRCLE((unsigned long)base);
++              break;
++      case NAND_CTL_SETALE:
++              MACRO_NAND_CTL_SETALE((unsigned long)base);
++              break;
++      case NAND_CTL_CLRALE:
++              MACRO_NAND_CTL_CLRALE((unsigned long)base);
++              break;
++      case NAND_CTL_SETNCE:
++              MACRO_NAND_ENABLE_CE((unsigned long)base);
++              break;
++      case NAND_CTL_CLRNCE:
++              MACRO_NAND_DISABLE_CE((unsigned long)base);
++              break;
++      }
++}
++
++
++/*
++ * read device ready pin
++ * function +/- borrowed from Linux 2.6 (drivers/mtd/nand/ppchameleonevb.c)
++ */
++static int ppchameleonevb_device_ready(struct mtd_info *mtdinfo)
++{
++      struct nand_chip *this = mtdinfo->priv;
++      ulong rb_gpio_pin;
++
++      /* use the base addr to find out which chip are we dealing with */
++      switch((ulong) this->IO_ADDR_W) {
++      case CFG_NAND0_BASE:
++              rb_gpio_pin = CFG_NAND0_RDY;
++              break;
++      case CFG_NAND1_BASE:
++              rb_gpio_pin = CFG_NAND1_RDY;
++              break;
++      default: /* this should never happen */
++              return 0;
++              break;
++      }
++
++        if (in32(GPIO0_IR) & rb_gpio_pin)
++              return 1;
++      return 0;
++}
++
++
++/*
++ * Board-specific NAND initialization. The following members of the
++ * argument are board-specific (per include/linux/mtd/nand_new.h):
++ * - 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
++ * - enable_hwecc?: function to enable (reset)  hardware ecc generator. Must
++ *   only be provided if a hardware ECC is available
++ * - eccmode: mode of ecc, see defines
++ * - chip_delay: chip dependent delay for transfering data from array to
++ *   read regs (tR)
++ * - options: various chip options. They can partly be set to inform
++ *   nand_scan about special functionality. See the defines for further
++ *   explanation
++ * Members with a "?" were not set in the merged testing-NAND branch,
++ * so they are not set here either.
++ */
++void board_nand_init(struct nand_chip *nand)
++{
++
++      nand->hwcontrol = ppchameleonevb_hwcontrol;
++      nand->dev_ready = ppchameleonevb_device_ready;
++      nand->eccmode = NAND_ECC_SOFT;
++      nand->chip_delay = NAND_BIG_DELAY_US;
++      nand->options = NAND_SAMSUNG_LP_OPTIONS;
++}
++
++#else
++
++/* old NAND handling */
++extern ulong
++nand_probe(ulong physadr);
++
++void
++nand_init(void)
++{
++      ulong totlen = 0;
++
++/*
++      The HI model is equipped with a large block NAND chip not supported yet
++      by U-Boot
++    (CONFIG_PPCHAMELEON_MODULE_MODEL == CONFIG_PPCHAMELEON_MODULE_HI)
++*/
++
++#if (CONFIG_PPCHAMELEON_MODULE_MODEL == CONFIG_PPCHAMELEON_MODULE_ME)
++      debug ("Probing at 0x%.8x\n", CFG_NAND0_BASE);
++      totlen += nand_probe (CFG_NAND0_BASE);
++#endif        /* CONFIG_PPCHAMELEON_MODULE_ME, CONFIG_PPCHAMELEON_MODULE_HI */
++
++      debug ("Probing at 0x%.8x\n", CFG_NAND1_BASE);
++      totlen += nand_probe (CFG_NAND1_BASE);
++
++      printf ("%3lu MB\n", totlen >>20);
++}
++#endif
++#endif
index 0000000,8b73e97..57a34c4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,11 +1,15 @@@
+ #
+ # Linux-Kernel is expected to be at 1000'8000,
+ # entry 1000'8000 (mem base + reserved)
+ #
+ # We load ourself to internal RAM at 2001'2000
+ # Check map file when changing TEXT_BASE.
+ # Everything has fit into 192kB internal SRAM!
+ #
+ # XXX TEXT_BASE = 0x20012000
+ TEXT_BASE = 0x13FC0000
++
++# Compile the new NAND code (needed iff #ifdef CONFIG_NEW_NAND_CODE)
++BOARDLIBS = drivers/nand/libnand.a
++
index 0000000,0000000..9593f89
new file mode 100755 (executable)
Binary files differ
index 0000000,0000000..98ae42e
new file mode 100755 (executable)
Binary files differ
index 0000000,0000000..c30c98b
new file mode 100755 (executable)
Binary files differ
diff --cc common/Makefile
@@@ -37,19 -37,16 +37,19 @@@ COBJS      = main.o ACEX1K.o altera.o bedbug
          cmd_i2c.o cmd_ide.o cmd_immap.o cmd_itest.o cmd_jffs2.o \
          cmd_load.o cmd_log.o \
          cmd_mem.o cmd_mii.o cmd_misc.o cmd_mmc.o \
-         cmd_nand.o cmd_net.o cmd_nvedit.o \
+         cmd_nand.o cmd_nand_new.o cmd_net.o cmd_nvedit.o \
          cmd_pci.o cmd_pcmcia.o cmd_portio.o \
 -        cmd_reginfo.o cmd_reiser.o cmd_scsi.o cmd_spi.o cmd_universe.o cmd_usb.o cmd_vfd.o \
 +        cmd_reginfo.o cmd_reiser.o cmd_scsi.o cmd_spi.o cmd_universe.o \
 +        cmd_usb.o cmd_vfd.o \
          command.o console.o devices.o dlmalloc.o docecc.o \
          environment.o env_common.o \
 -        env_nand.o env_dataflash.o env_flash.o env_eeprom.o env_nvram.o env_nowhere.o exports.o \
 -        flash.o fpga.o \
 +        env_nand.o env_dataflash.o env_flash.o env_eeprom.o \
 +        env_nvram.o env_nowhere.o \
 +        exports.o \
 +        flash.o fpga.o ft_build.o \
          hush.o kgdb.o lcd.o lists.o lynxkdi.o \
          memsize.o miiphybb.o miiphyutil.o \
 -        s_record.o serial.o soft_i2c.o soft_spi.o spartan2.o \
 +        s_record.o serial.o soft_i2c.o soft_spi.o spartan2.o spartan3.o \
          usb.o usb_kbd.o usb_storage.o \
          virtex2.o xilinx.o
  
  
  #include <cramfs/cramfs_fs.h>
  
+ #ifdef CONFIG_NEW_NAND_CODE
+ #include <nand.h>
+ #endif
  /* enable/disable debugging messages */
--#define       DEBUG
--#undef        DEBUG
++#define       DEBUG_JFFS
++#undef        DEBUG_JFFS
  
--#ifdef  DEBUG
++#ifdef  DEBUG_JFFS
  # define DEBUGF(fmt, args...) printf(fmt ,##args)
  #else
  # define DEBUGF(fmt, args...)
  
  /* this flag needs to be set in part_info struct mask_flags
   * field for read-only partitions */
--#define MTD_WRITEABLE         1
++#define MTD_WRITEABLE_CMD             1
  
  #ifdef CONFIG_JFFS2_CMDLINE
  /* default values for mtdids and mtdparts variables */
@@@ -646,7 -600,7 +653,7 @@@ static int part_parse(const char *cons
        /* test for options */
        mask_flags = 0;
        if (strncmp(p, "ro", 2) == 0) {
--              mask_flags |= MTD_WRITEABLE;
++              mask_flags |= MTD_WRITEABLE_CMD;
                p += 2;
        }
  
@@@ -711,8 -665,9 +718,9 @@@ static int device_validate(u8 type, u8 
        if (type == MTD_DEV_TYPE_NOR) {
  #if (CONFIG_COMMANDS & CFG_CMD_FLASH)
                if (num < CFG_MAX_FLASH_BANKS) {
 -                      extern flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
 +                      extern flash_info_t flash_info[];
                        *size = flash_info[num].size;
                        return 0;
                }
  
@@@ -1169,7 -1120,7 +1181,7 @@@ static int generate_mtdparts(char *buf
                        }
  
                        /* ro mask flag */
--                      if (part->mask_flags && MTD_WRITEABLE) {
++                      if (part->mask_flags && MTD_WRITEABLE_CMD) {
                                len = 2;
                                if (len > maxlen)
                                        goto cleanup;
@@@ -21,7 -21,7 +21,7 @@@
  # define SHOW_BOOT_PROGRESS(arg)
  #endif
  
- #if (CONFIG_COMMANDS & CFG_CMD_NAND)
 -#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined CONFIG_NEW_NAND_CODE
++#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CONFIG_NEW_NAND_CODE)
  
  #include <linux/mtd/nand.h>
  #include <linux/mtd/nand_ids.h>
index 0000000,b421d4c..07e2549
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1782 +1,1785 @@@
+ /*
+  * drivers/mtd/nand/diskonchip.c
+  *
+  * (C) 2003 Red Hat, Inc.
+  * (C) 2004 Dan Brown <dan_brown@ieee.org>
+  * (C) 2004 Kalev Lember <kalev@smartlink.ee>
+  *
+  * Author: David Woodhouse <dwmw2@infradead.org>
+  * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
+  * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
+  *
+  * Error correction code lifted from the old docecc code
+  * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+  * Copyright (C) 2000 Netgem S.A.
+  * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
+  *
+  * Interface to generic NAND code for M-Systems DiskOnChip devices
+  *
+  * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $
+  */
++#include <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+ #include <linux/delay.h>
+ #include <linux/rslib.h>
+ #include <linux/moduleparam.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/doc2000.h>
+ #include <linux/mtd/compatmac.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/mtd/inftl.h>
+ /* Where to look for the devices? */
+ #ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
+ #define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
+ #endif
+ static unsigned long __initdata doc_locations[] = {
+ #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
+ #ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
+       0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+       0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+       0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+       0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+       0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+ #else /*  CONFIG_MTD_DOCPROBE_HIGH */
+       0xc8000, 0xca000, 0xcc000, 0xce000,
+       0xd0000, 0xd2000, 0xd4000, 0xd6000,
+       0xd8000, 0xda000, 0xdc000, 0xde000,
+       0xe0000, 0xe2000, 0xe4000, 0xe6000,
+       0xe8000, 0xea000, 0xec000, 0xee000,
+ #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
+ #elif defined(__PPC__)
+       0xe4000000,
+ #elif defined(CONFIG_MOMENCO_OCELOT)
+       0x2f000000,
+       0xff000000,
+ #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+       0xff000000,
+ ##else
+ #warning Unknown architecture for DiskOnChip. No default probe locations defined
+ #endif
+       0xffffffff };
+ static struct mtd_info *doclist = NULL;
+ struct doc_priv {
+       void __iomem *virtadr;
+       unsigned long physadr;
+       u_char ChipID;
+       u_char CDSNControl;
+       int chips_per_floor; /* The number of chips detected on each floor */
+       int curfloor;
+       int curchip;
+       int mh0_page;
+       int mh1_page;
+       struct mtd_info *nextdoc;
+ };
+ /* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
+    MediaHeader.  The spec says to just keep going, I think, but that's just
+    silly. */
+ #define MAX_MEDIAHEADER_SCAN 8
+ /* This is the syndrome computed by the HW ecc generator upon reading an empty
+    page, one with all 0xff for data and stored ecc code. */
+ static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
+ /* This is the ecc value computed by the HW ecc generator upon writing an empty
+    page, one with all 0xff for data. */
+ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
+ #define INFTL_BBT_RESERVED_BLOCKS 4
+ #define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
+ #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
+ #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
+ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
+ static void doc200x_select_chip(struct mtd_info *mtd, int chip);
+ static int debug=0;
+ module_param(debug, int, 0);
+ static int try_dword=1;
+ module_param(try_dword, int, 0);
+ static int no_ecc_failures=0;
+ module_param(no_ecc_failures, int, 0);
+ #ifdef CONFIG_MTD_PARTITIONS
+ static int no_autopart=0;
+ module_param(no_autopart, int, 0);
+ #endif
+ #ifdef MTD_NAND_DISKONCHIP_BBTWRITE
+ static int inftl_bbt_write=1;
+ #else
+ static int inftl_bbt_write=0;
+ #endif
+ module_param(inftl_bbt_write, int, 0);
+ static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
+ module_param(doc_config_location, ulong, 0);
+ MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+ /* Sector size for HW ECC */
+ #define SECTOR_SIZE 512
+ /* The sector bytes are packed into NB_DATA 10 bit words */
+ #define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
+ /* Number of roots */
+ #define NROOTS 4
+ /* First consective root */
+ #define FCR 510
+ /* Number of symbols */
+ #define NN 1023
+ /* the Reed Solomon control structure */
+ static struct rs_control *rs_decoder;
+ /*
+  * The HW decoder in the DoC ASIC's provides us a error syndrome,
+  * which we must convert to a standard syndrom usable by the generic
+  * Reed-Solomon library code.
+  *
+  * Fabrice Bellard figured this out in the old docecc code. I added
+  * some comments, improved a minor bit and converted it to make use
+  * of the generic Reed-Solomon libary. tglx
+  */
+ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
+ {
+       int i, j, nerr, errpos[8];
+       uint8_t parity;
+       uint16_t ds[4], s[5], tmp, errval[8], syn[4];
+       /* Convert the ecc bytes into words */
+       ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
+       ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
+       ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
+       ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
+       parity = ecc[1];
+       /* Initialize the syndrom buffer */
+       for (i = 0; i < NROOTS; i++)
+               s[i] = ds[0];
+       /*
+        *  Evaluate
+        *  s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
+        *  where x = alpha^(FCR + i)
+        */
+       for(j = 1; j < NROOTS; j++) {
+               if(ds[j] == 0)
+                       continue;
+               tmp = rs->index_of[ds[j]];
+               for(i = 0; i < NROOTS; i++)
+                       s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
+       }
+       /* Calc s[i] = s[i] / alpha^(v + i) */
+       for (i = 0; i < NROOTS; i++) {
+               if (syn[i])
+                       syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
+       }
+       /* Call the decoder library */
+       nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
+       /* Incorrectable errors ? */
+       if (nerr < 0)
+               return nerr;
+       /*
+        * Correct the errors. The bitpositions are a bit of magic,
+        * but they are given by the design of the de/encoder circuit
+        * in the DoC ASIC's.
+        */
+       for(i = 0;i < nerr; i++) {
+               int index, bitpos, pos = 1015 - errpos[i];
+               uint8_t val;
+               if (pos >= NB_DATA && pos < 1019)
+                       continue;
+               if (pos < NB_DATA) {
+                       /* extract bit position (MSB first) */
+                       pos = 10 * (NB_DATA - 1 - pos) - 6;
+                       /* now correct the following 10 bits. At most two bytes
+                          can be modified since pos is even */
+                       index = (pos >> 3) ^ 1;
+                       bitpos = pos & 7;
+                       if ((index >= 0 && index < SECTOR_SIZE) ||
+                           index == (SECTOR_SIZE + 1)) {
+                               val = (uint8_t) (errval[i] >> (2 + bitpos));
+                               parity ^= val;
+                               if (index < SECTOR_SIZE)
+                                       data[index] ^= val;
+                       }
+                       index = ((pos >> 3) + 1) ^ 1;
+                       bitpos = (bitpos + 10) & 7;
+                       if (bitpos == 0)
+                               bitpos = 8;
+                       if ((index >= 0 && index < SECTOR_SIZE) ||
+                           index == (SECTOR_SIZE + 1)) {
+                               val = (uint8_t)(errval[i] << (8 - bitpos));
+                               parity ^= val;
+                               if (index < SECTOR_SIZE)
+                                       data[index] ^= val;
+                       }
+               }
+       }
+       /* If the parity is wrong, no rescue possible */
+       return parity ? -1 : nerr;
+ }
+ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
+ {
+       volatile char dummy;
+       int i;
+       for (i = 0; i < cycles; i++) {
+               if (DoC_is_Millennium(doc))
+                       dummy = ReadDOC(doc->virtadr, NOP);
+               else if (DoC_is_MillenniumPlus(doc))
+                       dummy = ReadDOC(doc->virtadr, Mplus_NOP);
+               else
+                       dummy = ReadDOC(doc->virtadr, DOCStatus);
+       }
+ }
+ #define CDSN_CTRL_FR_B_MASK   (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
+ /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+ static int _DoC_WaitReady(struct doc_priv *doc)
+ {
+       void __iomem *docptr = doc->virtadr;
+       unsigned long timeo = jiffies + (HZ * 10);
+       if(debug) printk("_DoC_WaitReady...\n");
+       /* Out-of-line routine to wait for chip response */
+       if (DoC_is_MillenniumPlus(doc)) {
+               while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+                       if (time_after(jiffies, timeo)) {
+                               printk("_DoC_WaitReady timed out.\n");
+                               return -EIO;
+                       }
+                       udelay(1);
+                       cond_resched();
+               }
+       } else {
+               while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+                       if (time_after(jiffies, timeo)) {
+                               printk("_DoC_WaitReady timed out.\n");
+                               return -EIO;
+                       }
+                       udelay(1);
+                       cond_resched();
+               }
+       }
+       return 0;
+ }
+ static inline int DoC_WaitReady(struct doc_priv *doc)
+ {
+       void __iomem *docptr = doc->virtadr;
+       int ret = 0;
+       if (DoC_is_MillenniumPlus(doc)) {
+               DoC_Delay(doc, 4);
+               if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
+                       /* Call the out-of-line routine to wait */
+                       ret = _DoC_WaitReady(doc);
+       } else {
+               DoC_Delay(doc, 4);
+               if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+                       /* Call the out-of-line routine to wait */
+                       ret = _DoC_WaitReady(doc);
+               DoC_Delay(doc, 2);
+       }
+       if(debug) printk("DoC_WaitReady OK\n");
+       return ret;
+ }
+ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       if(debug)printk("write_byte %02x\n", datum);
+       WriteDOC(datum, docptr, CDSNSlowIO);
+       WriteDOC(datum, docptr, 2k_CDSN_IO);
+ }
+ static u_char doc2000_read_byte(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       u_char ret;
+       ReadDOC(docptr, CDSNSlowIO);
+       DoC_Delay(doc, 2);
+       ret = ReadDOC(docptr, 2k_CDSN_IO);
+       if (debug) printk("read_byte returns %02x\n", ret);
+       return ret;
+ }
+ static void doc2000_writebuf(struct mtd_info *mtd,
+                            const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug)printk("writebuf of %d bytes: ", len);
+       for (i=0; i < len; i++) {
+               WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
+               if (debug && i < 16)
+                       printk("%02x ", buf[i]);
+       }
+       if (debug) printk("\n");
+ }
+ static void doc2000_readbuf(struct mtd_info *mtd,
+                           u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug)printk("readbuf of %d bytes: ", len);
+       for (i=0; i < len; i++) {
+               buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
+       }
+ }
+ static void doc2000_readbuf_dword(struct mtd_info *mtd,
+                           u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug) printk("readbuf_dword of %d bytes: ", len);
+       if (unlikely((((unsigned long)buf)|len) & 3)) {
+               for (i=0; i < len; i++) {
+                       *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
+               }
+       } else {
+               for (i=0; i < len; i+=4) {
+                       *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
+               }
+       }
+ }
+ static int doc2000_verifybuf(struct mtd_info *mtd,
+                             const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       for (i=0; i < len; i++)
+               if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
+                       return -EFAULT;
+       return 0;
+ }
+ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       uint16_t ret;
+       doc200x_select_chip(mtd, nr);
+       doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+       this->write_byte(mtd, NAND_CMD_READID);
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+       doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+       this->write_byte(mtd, 0);
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+       ret = this->read_byte(mtd) << 8;
+       ret |= this->read_byte(mtd);
+       if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
+               /* First chip probe. See if we get same results by 32-bit access */
+               union {
+                       uint32_t dword;
+                       uint8_t byte[4];
+               } ident;
+               void __iomem *docptr = doc->virtadr;
+               doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+               doc2000_write_byte(mtd, NAND_CMD_READID);
+               doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+               doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+               doc2000_write_byte(mtd, 0);
+               doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+               ident.dword = readl(docptr + DoC_2k_CDSN_IO);
+               if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
+                       printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
+                       this->read_buf = &doc2000_readbuf_dword;
+               }
+       }
+       return ret;
+ }
+ static void __init doc2000_count_chips(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       uint16_t mfrid;
+       int i;
+       /* Max 4 chips per floor on DiskOnChip 2000 */
+       doc->chips_per_floor = 4;
+       /* Find out what the first chip is */
+       mfrid = doc200x_ident_chip(mtd, 0);
+       /* Find how many chips in each floor. */
+       for (i = 1; i < 4; i++) {
+               if (doc200x_ident_chip(mtd, i) != mfrid)
+                       break;
+       }
+       doc->chips_per_floor = i;
+       printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
+ }
+ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+ {
+       struct doc_priv *doc = this->priv;
+       int status;
+       DoC_WaitReady(doc);
+       this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+       DoC_WaitReady(doc);
+       status = (int)this->read_byte(mtd);
+       return status;
+ }
+ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       WriteDOC(datum, docptr, CDSNSlowIO);
+       WriteDOC(datum, docptr, Mil_CDSN_IO);
+       WriteDOC(datum, docptr, WritePipeTerm);
+ }
+ static u_char doc2001_read_byte(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       /*ReadDOC(docptr, CDSNSlowIO); */
+       /* 11.4.5 -- delay twice to allow extended length cycle */
+       DoC_Delay(doc, 2);
+       ReadDOC(docptr, ReadPipeInit);
+       /*return ReadDOC(docptr, Mil_CDSN_IO); */
+       return ReadDOC(docptr, LastDataRead);
+ }
+ static void doc2001_writebuf(struct mtd_info *mtd,
+                            const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       for (i=0; i < len; i++)
+               WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+       /* Terminate write pipeline */
+       WriteDOC(0x00, docptr, WritePipeTerm);
+ }
+ static void doc2001_readbuf(struct mtd_info *mtd,
+                           u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       /* Start read pipeline */
+       ReadDOC(docptr, ReadPipeInit);
+       for (i=0; i < len-1; i++)
+               buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
+       /* Terminate read pipeline */
+       buf[i] = ReadDOC(docptr, LastDataRead);
+ }
+ static int doc2001_verifybuf(struct mtd_info *mtd,
+                            const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       /* Start read pipeline */
+       ReadDOC(docptr, ReadPipeInit);
+       for (i=0; i < len-1; i++)
+               if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+                       ReadDOC(docptr, LastDataRead);
+                       return i;
+               }
+       if (buf[i] != ReadDOC(docptr, LastDataRead))
+               return i;
+       return 0;
+ }
+ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       u_char ret;
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       ret = ReadDOC(docptr, Mplus_LastDataRead);
+       if (debug) printk("read_byte returns %02x\n", ret);
+       return ret;
+ }
+ static void doc2001plus_writebuf(struct mtd_info *mtd,
+                            const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug)printk("writebuf of %d bytes: ", len);
+       for (i=0; i < len; i++) {
+               WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+               if (debug && i < 16)
+                       printk("%02x ", buf[i]);
+       }
+       if (debug) printk("\n");
+ }
+ static void doc2001plus_readbuf(struct mtd_info *mtd,
+                           u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug)printk("readbuf of %d bytes: ", len);
+       /* Start read pipeline */
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       for (i=0; i < len-2; i++) {
+               buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+               if (debug && i < 16)
+                       printk("%02x ", buf[i]);
+       }
+       /* Terminate read pipeline */
+       buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
+       if (debug && i < 16)
+               printk("%02x ", buf[len-2]);
+       buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
+       if (debug && i < 16)
+               printk("%02x ", buf[len-1]);
+       if (debug) printk("\n");
+ }
+ static int doc2001plus_verifybuf(struct mtd_info *mtd,
+                            const u_char *buf, int len)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       if (debug)printk("verifybuf of %d bytes: ", len);
+       /* Start read pipeline */
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       ReadDOC(docptr, Mplus_ReadPipeInit);
+       for (i=0; i < len-2; i++)
+               if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+                       ReadDOC(docptr, Mplus_LastDataRead);
+                       ReadDOC(docptr, Mplus_LastDataRead);
+                       return i;
+               }
+       if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead))
+               return len-2;
+       if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead))
+               return len-1;
+       return 0;
+ }
+ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int floor = 0;
+       if(debug)printk("select chip (%d)\n", chip);
+       if (chip == -1) {
+               /* Disable flash internally */
+               WriteDOC(0, docptr, Mplus_FlashSelect);
+               return;
+       }
+       floor = chip / doc->chips_per_floor;
+       chip -= (floor *  doc->chips_per_floor);
+       /* Assert ChipEnable and deassert WriteProtect */
+       WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
+       this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+       doc->curchip = chip;
+       doc->curfloor = floor;
+ }
+ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int floor = 0;
+       if(debug)printk("select chip (%d)\n", chip);
+       if (chip == -1)
+               return;
+       floor = chip / doc->chips_per_floor;
+       chip -= (floor *  doc->chips_per_floor);
+       /* 11.4.4 -- deassert CE before changing chip */
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+       WriteDOC(floor, docptr, FloorSelect);
+       WriteDOC(chip, docptr, CDSNDeviceSelect);
+       doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
+       doc->curchip = chip;
+       doc->curfloor = floor;
+ }
+ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       switch(cmd) {
+       case NAND_CTL_SETNCE:
+               doc->CDSNControl |= CDSN_CTRL_CE;
+               break;
+       case NAND_CTL_CLRNCE:
+               doc->CDSNControl &= ~CDSN_CTRL_CE;
+               break;
+       case NAND_CTL_SETCLE:
+               doc->CDSNControl |= CDSN_CTRL_CLE;
+               break;
+       case NAND_CTL_CLRCLE:
+               doc->CDSNControl &= ~CDSN_CTRL_CLE;
+               break;
+       case NAND_CTL_SETALE:
+               doc->CDSNControl |= CDSN_CTRL_ALE;
+               break;
+       case NAND_CTL_CLRALE:
+               doc->CDSNControl &= ~CDSN_CTRL_ALE;
+               break;
+       case NAND_CTL_SETWP:
+               doc->CDSNControl |= CDSN_CTRL_WP;
+               break;
+       case NAND_CTL_CLRWP:
+               doc->CDSNControl &= ~CDSN_CTRL_WP;
+               break;
+       }
+       if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+       WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+       /* 11.4.3 -- 4 NOPs after CSDNControl write */
+       DoC_Delay(doc, 4);
+ }
+ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       /*
+        * Must terminate write pipeline before sending any commands
+        * to the device.
+        */
+       if (command == NAND_CMD_PAGEPROG) {
+               WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+               WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+       }
+       /*
+        * Write out the command to the device.
+        */
+       if (command == NAND_CMD_SEQIN) {
+               int readcmd;
+               if (column >= mtd->oobblock) {
+                       /* OOB area */
+                       column -= mtd->oobblock;
+                       readcmd = NAND_CMD_READOOB;
+               } else if (column < 256) {
+                       /* First 256 bytes --> READ0 */
+                       readcmd = NAND_CMD_READ0;
+               } else {
+                       column -= 256;
+                       readcmd = NAND_CMD_READ1;
+               }
+               WriteDOC(readcmd, docptr, Mplus_FlashCmd);
+       }
+       WriteDOC(command, docptr, Mplus_FlashCmd);
+       WriteDOC(0, docptr, Mplus_WritePipeTerm);
+       WriteDOC(0, docptr, Mplus_WritePipeTerm);
+       if (column != -1 || page_addr != -1) {
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       WriteDOC(column, docptr, Mplus_FlashAddress);
+               }
+               if (page_addr != -1) {
+                       WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress);
+                       WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
+                       /* One more address cycle for higher density devices */
+                       if (this->chipsize & 0x0c000000) {
+                               WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
+                               printk("high density\n");
+                       }
+               }
+               WriteDOC(0, docptr, Mplus_WritePipeTerm);
+               WriteDOC(0, docptr, Mplus_WritePipeTerm);
+               /* deassert ALE */
+               if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID)
+                       WriteDOC(0, docptr, Mplus_FlashControl);
+       }
+       /*
+        * program and erase have their own busy handlers
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+       case NAND_CMD_RESET:
+               if (this->dev_ready)
+                       break;
+               udelay(this->chip_delay);
+               WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
+               WriteDOC(0, docptr, Mplus_WritePipeTerm);
+               WriteDOC(0, docptr, Mplus_WritePipeTerm);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+       /* This applies to read commands */
+       default:
+               /*
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }
+       }
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+ }
+ static int doc200x_dev_ready(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       if (DoC_is_MillenniumPlus(doc)) {
+               /* 11.4.2 -- must NOP four times before checking FR/B# */
+               DoC_Delay(doc, 4);
+               if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+                       if(debug)
+                               printk("not ready\n");
+                       return 0;
+               }
+               if (debug)printk("was ready\n");
+               return 1;
+       } else {
+               /* 11.4.2 -- must NOP four times before checking FR/B# */
+               DoC_Delay(doc, 4);
+               if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+                       if(debug)
+                               printk("not ready\n");
+                       return 0;
+               }
+               /* 11.4.2 -- Must NOP twice if it's ready */
+               DoC_Delay(doc, 2);
+               if (debug)printk("was ready\n");
+               return 1;
+       }
+ }
+ static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+ {
+       /* This is our last resort if we couldn't find or create a BBT.  Just
+          pretend all blocks are good. */
+       return 0;
+ }
+ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       /* Prime the ECC engine */
+       switch(mode) {
+       case NAND_ECC_READ:
+               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+               WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+               break;
+       case NAND_ECC_WRITE:
+               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+               WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+               break;
+       }
+ }
+ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       /* Prime the ECC engine */
+       switch(mode) {
+       case NAND_ECC_READ:
+               WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+               WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
+               break;
+       case NAND_ECC_WRITE:
+               WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+               WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
+               break;
+       }
+ }
+ /* This code is only called on write */
+ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+                                unsigned char *ecc_code)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       int i;
+       int emptymatch = 1;
+       /* flush the pipeline */
+       if (DoC_is_2000(doc)) {
+               WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+       } else if (DoC_is_MillenniumPlus(doc)) {
+               WriteDOC(0, docptr, Mplus_NOP);
+               WriteDOC(0, docptr, Mplus_NOP);
+               WriteDOC(0, docptr, Mplus_NOP);
+       } else {
+               WriteDOC(0, docptr, NOP);
+               WriteDOC(0, docptr, NOP);
+               WriteDOC(0, docptr, NOP);
+       }
+       for (i = 0; i < 6; i++) {
+               if (DoC_is_MillenniumPlus(doc))
+                       ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+               else
+                       ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+               if (ecc_code[i] != empty_write_ecc[i])
+                       emptymatch = 0;
+       }
+       if (DoC_is_MillenniumPlus(doc))
+               WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+       else
+               WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ #if 0
+       /* If emptymatch=1, we might have an all-0xff data buffer.  Check. */
+       if (emptymatch) {
+               /* Note: this somewhat expensive test should not be triggered
+                  often.  It could be optimized away by examining the data in
+                  the writebuf routine, and remembering the result. */
+               for (i = 0; i < 512; i++) {
+                       if (dat[i] == 0xff) continue;
+                       emptymatch = 0;
+                       break;
+               }
+       }
+       /* If emptymatch still =1, we do have an all-0xff data buffer.
+          Return all-0xff ecc value instead of the computed one, so
+          it'll look just like a freshly-erased page. */
+       if (emptymatch) memset(ecc_code, 0xff, 6);
+ #endif
+       return 0;
+ }
+ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+ {
+       int i, ret = 0;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       void __iomem *docptr = doc->virtadr;
+       volatile u_char dummy;
+       int emptymatch = 1;
+       /* flush the pipeline */
+       if (DoC_is_2000(doc)) {
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+       } else if (DoC_is_MillenniumPlus(doc)) {
+               dummy = ReadDOC(docptr, Mplus_ECCConf);
+               dummy = ReadDOC(docptr, Mplus_ECCConf);
+               dummy = ReadDOC(docptr, Mplus_ECCConf);
+       } else {
+               dummy = ReadDOC(docptr, ECCConf);
+               dummy = ReadDOC(docptr, ECCConf);
+               dummy = ReadDOC(docptr, ECCConf);
+       }
+       /* Error occured ? */
+       if (dummy & 0x80) {
+               for (i = 0; i < 6; i++) {
+                       if (DoC_is_MillenniumPlus(doc))
+                               calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+                       else
+                               calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+                       if (calc_ecc[i] != empty_read_syndrome[i])
+                               emptymatch = 0;
+               }
+               /* If emptymatch=1, the read syndrome is consistent with an
+                  all-0xff data and stored ecc block.  Check the stored ecc. */
+               if (emptymatch) {
+                       for (i = 0; i < 6; i++) {
+                               if (read_ecc[i] == 0xff) continue;
+                               emptymatch = 0;
+                               break;
+                       }
+               }
+               /* If emptymatch still =1, check the data block. */
+               if (emptymatch) {
+               /* Note: this somewhat expensive test should not be triggered
+                  often.  It could be optimized away by examining the data in
+                  the readbuf routine, and remembering the result. */
+                       for (i = 0; i < 512; i++) {
+                               if (dat[i] == 0xff) continue;
+                               emptymatch = 0;
+                               break;
+                       }
+               }
+               /* If emptymatch still =1, this is almost certainly a freshly-
+                  erased block, in which case the ECC will not come out right.
+                  We'll suppress the error and tell the caller everything's
+                  OK.  Because it is. */
+               if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc);
+               if (ret > 0)
+                       printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
+       }
+       if (DoC_is_MillenniumPlus(doc))
+               WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+       else
+               WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+       if (no_ecc_failures && (ret == -1)) {
+               printk(KERN_ERR "suppressing ECC failure\n");
+               ret = 0;
+       }
+       return ret;
+ }
+ /*u_char mydatabuf[528]; */
+ static struct nand_oobinfo doc200x_oobinfo = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 6,
+       .eccpos = {0, 1, 2, 3, 4, 5},
+       .oobfree = { {8, 8} }
+ };
+ /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
+    On sucessful return, buf will contain a copy of the media header for
+    further processing.  id is the string to scan for, and will presumably be
+    either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
+    header.  The page #s of the found media headers are placed in mh0_page and
+    mh1_page in the DOC private structure. */
+ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
+                                    const char *id, int findmirror)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+       int ret;
+       size_t retlen;
+       end = min(end, mtd->size); /* paranoia */
+       for (offs = 0; offs < end; offs += mtd->erasesize) {
+               ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+               if (retlen != mtd->oobblock) continue;
+               if (ret) {
+                       printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n",
+                               offs);
+               }
+               if (memcmp(buf, id, 6)) continue;
+               printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
+               if (doc->mh0_page == -1) {
+                       doc->mh0_page = offs >> this->page_shift;
+                       if (!findmirror) return 1;
+                       continue;
+               }
+               doc->mh1_page = offs >> this->page_shift;
+               return 2;
+       }
+       if (doc->mh0_page == -1) {
+               printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
+               return 0;
+       }
+       /* Only one mediaheader was found.  We want buf to contain a
+          mediaheader on return, so we'll have to re-read the one we found. */
+       offs = doc->mh0_page << this->page_shift;
+       ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+       if (retlen != mtd->oobblock) {
+               /* Insanity.  Give up. */
+               printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
+               return 0;
+       }
+       return 1;
+ }
+ static inline int __init nftl_partscan(struct mtd_info *mtd,
+                               struct mtd_partition *parts)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       int ret = 0;
+       u_char *buf;
+       struct NFTLMediaHeader *mh;
+       const unsigned psize = 1 << this->page_shift;
+       unsigned blocks, maxblocks;
+       int offs, numheaders;
+       buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+       if (!buf) {
+               printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+               return 0;
+       }
+       if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
+       mh = (struct NFTLMediaHeader *) buf;
+ /*#ifdef CONFIG_MTD_DEBUG_VERBOSE */
+ /*    if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */
+       printk(KERN_INFO "    DataOrgID        = %s\n"
+                        "    NumEraseUnits    = %d\n"
+                        "    FirstPhysicalEUN = %d\n"
+                        "    FormattedSize    = %d\n"
+                        "    UnitSizeFactor   = %d\n",
+               mh->DataOrgID, mh->NumEraseUnits,
+               mh->FirstPhysicalEUN, mh->FormattedSize,
+               mh->UnitSizeFactor);
+ /*#endif */
+       blocks = mtd->size >> this->phys_erase_shift;
+       maxblocks = min(32768U, mtd->erasesize - psize);
+       if (mh->UnitSizeFactor == 0x00) {
+               /* Auto-determine UnitSizeFactor.  The constraints are:
+                  - There can be at most 32768 virtual blocks.
+                  - There can be at most (virtual block size - page size)
+                    virtual blocks (because MediaHeader+BBT must fit in 1).
+               */
+               mh->UnitSizeFactor = 0xff;
+               while (blocks > maxblocks) {
+                       blocks >>= 1;
+                       maxblocks = min(32768U, (maxblocks << 1) + psize);
+                       mh->UnitSizeFactor--;
+               }
+               printk(KERN_WARNING "UnitSizeFactor=0x00 detected.  Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
+       }
+       /* NOTE: The lines below modify internal variables of the NAND and MTD
+          layers; variables with have already been configured by nand_scan.
+          Unfortunately, we didn't know before this point what these values
+          should be.  Thus, this code is somewhat dependant on the exact
+          implementation of the NAND layer.  */
+       if (mh->UnitSizeFactor != 0xff) {
+               this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
+               mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
+               printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
+               blocks = mtd->size >> this->bbt_erase_shift;
+               maxblocks = min(32768U, mtd->erasesize - psize);
+       }
+       if (blocks > maxblocks) {
+               printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size.  Aborting.\n", mh->UnitSizeFactor);
+               goto out;
+       }
+       /* Skip past the media headers. */
+       offs = max(doc->mh0_page, doc->mh1_page);
+       offs <<= this->page_shift;
+       offs += mtd->erasesize;
+       /*parts[0].name = " DiskOnChip Boot / Media Header partition"; */
+       /*parts[0].offset = 0; */
+       /*parts[0].size = offs; */
+       parts[0].name = " DiskOnChip BDTL partition";
+       parts[0].offset = offs;
+       parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+       offs += parts[0].size;
+       if (offs < mtd->size) {
+               parts[1].name = " DiskOnChip Remainder partition";
+               parts[1].offset = offs;
+               parts[1].size = mtd->size - offs;
+               ret = 2;
+               goto out;
+       }
+       ret = 1;
+ out:
+       kfree(buf);
+       return ret;
+ }
+ /* This is a stripped-down copy of the code in inftlmount.c */
+ static inline int __init inftl_partscan(struct mtd_info *mtd,
+                                struct mtd_partition *parts)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       int ret = 0;
+       u_char *buf;
+       struct INFTLMediaHeader *mh;
+       struct INFTLPartition *ip;
+       int numparts = 0;
+       int blocks;
+       int vshift, lastvunit = 0;
+       int i;
+       int end = mtd->size;
+       if (inftl_bbt_write)
+               end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
+       buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+       if (!buf) {
+               printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+               return 0;
+       }
+       if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out;
+       doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
+       mh = (struct INFTLMediaHeader *) buf;
+       mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
+       mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
+       mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
+       mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
+       mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
+       mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
+ /*#ifdef CONFIG_MTD_DEBUG_VERBOSE */
+ /*    if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */
+       printk(KERN_INFO "    bootRecordID          = %s\n"
+                        "    NoOfBootImageBlocks   = %d\n"
+                        "    NoOfBinaryPartitions  = %d\n"
+                        "    NoOfBDTLPartitions    = %d\n"
+                        "    BlockMultiplerBits    = %d\n"
+                        "    FormatFlgs            = %d\n"
+                        "    OsakVersion           = %d.%d.%d.%d\n"
+                        "    PercentUsed           = %d\n",
+               mh->bootRecordID, mh->NoOfBootImageBlocks,
+               mh->NoOfBinaryPartitions,
+               mh->NoOfBDTLPartitions,
+               mh->BlockMultiplierBits, mh->FormatFlags,
+               ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+               ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+               ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+               ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+               mh->PercentUsed);
+ /*#endif */
+       vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
+       blocks = mtd->size >> vshift;
+       if (blocks > 32768) {
+               printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size.  Aborting.\n", mh->BlockMultiplierBits);
+               goto out;
+       }
+       blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
+       if (inftl_bbt_write && (blocks > mtd->erasesize)) {
+               printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported.  FIX ME!\n");
+               goto out;
+       }
+       /* Scan the partitions */
+       for (i = 0; (i < 4); i++) {
+               ip = &(mh->Partitions[i]);
+               ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
+               ip->firstUnit = le32_to_cpu(ip->firstUnit);
+               ip->lastUnit = le32_to_cpu(ip->lastUnit);
+               ip->flags = le32_to_cpu(ip->flags);
+               ip->spareUnits = le32_to_cpu(ip->spareUnits);
+               ip->Reserved0 = le32_to_cpu(ip->Reserved0);
+ /*#ifdef CONFIG_MTD_DEBUG_VERBOSE */
+ /*            if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */
+               printk(KERN_INFO        "    PARTITION[%d] ->\n"
+                       "        virtualUnits    = %d\n"
+                       "        firstUnit       = %d\n"
+                       "        lastUnit        = %d\n"
+                       "        flags           = 0x%x\n"
+                       "        spareUnits      = %d\n",
+                       i, ip->virtualUnits, ip->firstUnit,
+                       ip->lastUnit, ip->flags,
+                       ip->spareUnits);
+ /*#endif */
+ /*
+               if ((i == 0) && (ip->firstUnit > 0)) {
+                       parts[0].name = " DiskOnChip IPL / Media Header partition";
+                       parts[0].offset = 0;
+                       parts[0].size = mtd->erasesize * ip->firstUnit;
+                       numparts = 1;
+               }
+ */
+               if (ip->flags & INFTL_BINARY)
+                       parts[numparts].name = " DiskOnChip BDK partition";
+               else
+                       parts[numparts].name = " DiskOnChip BDTL partition";
+               parts[numparts].offset = ip->firstUnit << vshift;
+               parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
+               numparts++;
+               if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit;
+               if (ip->flags & INFTL_LAST) break;
+       }
+       lastvunit++;
+       if ((lastvunit << vshift) < end) {
+               parts[numparts].name = " DiskOnChip Remainder partition";
+               parts[numparts].offset = lastvunit << vshift;
+               parts[numparts].size = end - parts[numparts].offset;
+               numparts++;
+       }
+       ret = numparts;
+ out:
+       kfree(buf);
+       return ret;
+ }
+ static int __init nftl_scan_bbt(struct mtd_info *mtd)
+ {
+       int ret, numparts;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       struct mtd_partition parts[2];
+       memset((char *) parts, 0, sizeof(parts));
+       /* On NFTL, we have to find the media headers before we can read the
+          BBTs, since they're stored in the media header eraseblocks. */
+       numparts = nftl_partscan(mtd, parts);
+       if (!numparts) return -EIO;
+       this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+                               NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+                               NAND_BBT_VERSION;
+       this->bbt_td->veroffs = 7;
+       this->bbt_td->pages[0] = doc->mh0_page + 1;
+       if (doc->mh1_page != -1) {
+               this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+                                       NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+                                       NAND_BBT_VERSION;
+               this->bbt_md->veroffs = 7;
+               this->bbt_md->pages[0] = doc->mh1_page + 1;
+       } else {
+               this->bbt_md = NULL;
+       }
+       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+          At least as nand_bbt.c is currently written. */
+       if ((ret = nand_scan_bbt(mtd, NULL)))
+               return ret;
+       add_mtd_device(mtd);
+ #ifdef CONFIG_MTD_PARTITIONS
+       if (!no_autopart)
+               add_mtd_partitions(mtd, parts, numparts);
+ #endif
+       return 0;
+ }
+ static int __init inftl_scan_bbt(struct mtd_info *mtd)
+ {
+       int ret, numparts;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       struct mtd_partition parts[5];
+       if (this->numchips > doc->chips_per_floor) {
+               printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
+               return -EIO;
+       }
+       if (DoC_is_MillenniumPlus(doc)) {
+               this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE;
+               if (inftl_bbt_write)
+                       this->bbt_td->options |= NAND_BBT_WRITE;
+               this->bbt_td->pages[0] = 2;
+               this->bbt_md = NULL;
+       } else {
+               this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+                                       NAND_BBT_VERSION;
+               if (inftl_bbt_write)
+                       this->bbt_td->options |= NAND_BBT_WRITE;
+               this->bbt_td->offs = 8;
+               this->bbt_td->len = 8;
+               this->bbt_td->veroffs = 7;
+               this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+               this->bbt_td->reserved_block_code = 0x01;
+               this->bbt_td->pattern = "MSYS_BBT";
+               this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+                                       NAND_BBT_VERSION;
+               if (inftl_bbt_write)
+                       this->bbt_md->options |= NAND_BBT_WRITE;
+               this->bbt_md->offs = 8;
+               this->bbt_md->len = 8;
+               this->bbt_md->veroffs = 7;
+               this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+               this->bbt_md->reserved_block_code = 0x01;
+               this->bbt_md->pattern = "TBB_SYSM";
+       }
+       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+          At least as nand_bbt.c is currently written. */
+       if ((ret = nand_scan_bbt(mtd, NULL)))
+               return ret;
+       memset((char *) parts, 0, sizeof(parts));
+       numparts = inftl_partscan(mtd, parts);
+       /* At least for now, require the INFTL Media Header.  We could probably
+          do without it for non-INFTL use, since all it gives us is
+          autopartitioning, but I want to give it more thought. */
+       if (!numparts) return -EIO;
+       add_mtd_device(mtd);
+ #ifdef CONFIG_MTD_PARTITIONS
+       if (!no_autopart)
+               add_mtd_partitions(mtd, parts, numparts);
+ #endif
+       return 0;
+ }
+ static inline int __init doc2000_init(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       this->write_byte = doc2000_write_byte;
+       this->read_byte = doc2000_read_byte;
+       this->write_buf = doc2000_writebuf;
+       this->read_buf = doc2000_readbuf;
+       this->verify_buf = doc2000_verifybuf;
+       this->scan_bbt = nftl_scan_bbt;
+       doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
+       doc2000_count_chips(mtd);
+       mtd->name = "DiskOnChip 2000 (NFTL Model)";
+       return (4 * doc->chips_per_floor);
+ }
+ static inline int __init doc2001_init(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       this->write_byte = doc2001_write_byte;
+       this->read_byte = doc2001_read_byte;
+       this->write_buf = doc2001_writebuf;
+       this->read_buf = doc2001_readbuf;
+       this->verify_buf = doc2001_verifybuf;
+       ReadDOC(doc->virtadr, ChipID);
+       ReadDOC(doc->virtadr, ChipID);
+       ReadDOC(doc->virtadr, ChipID);
+       if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
+               /* It's not a Millennium; it's one of the newer
+                  DiskOnChip 2000 units with a similar ASIC.
+                  Treat it like a Millennium, except that it
+                  can have multiple chips. */
+               doc2000_count_chips(mtd);
+               mtd->name = "DiskOnChip 2000 (INFTL Model)";
+               this->scan_bbt = inftl_scan_bbt;
+               return (4 * doc->chips_per_floor);
+       } else {
+               /* Bog-standard Millennium */
+               doc->chips_per_floor = 1;
+               mtd->name = "DiskOnChip Millennium";
+               this->scan_bbt = nftl_scan_bbt;
+               return 1;
+       }
+ }
+ static inline int __init doc2001plus_init(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = this->priv;
+       this->write_byte = NULL;
+       this->read_byte = doc2001plus_read_byte;
+       this->write_buf = doc2001plus_writebuf;
+       this->read_buf = doc2001plus_readbuf;
+       this->verify_buf = doc2001plus_verifybuf;
+       this->scan_bbt = inftl_scan_bbt;
+       this->hwcontrol = NULL;
+       this->select_chip = doc2001plus_select_chip;
+       this->cmdfunc = doc2001plus_command;
+       this->enable_hwecc = doc2001plus_enable_hwecc;
+       doc->chips_per_floor = 1;
+       mtd->name = "DiskOnChip Millennium Plus";
+       return 1;
+ }
+ static inline int __init doc_probe(unsigned long physadr)
+ {
+       unsigned char ChipID;
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       struct doc_priv *doc;
+       void __iomem *virtadr;
+       unsigned char save_control;
+       unsigned char tmp, tmpb, tmpc;
+       int reg, len, numchips;
+       int ret = 0;
+       virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
+       if (!virtadr) {
+               printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
+               return -EIO;
+       }
+       /* It's not possible to cleanly detect the DiskOnChip - the
+        * bootup procedure will put the device into reset mode, and
+        * it's not possible to talk to it without actually writing
+        * to the DOCControl register. So we store the current contents
+        * of the DOCControl register's location, in case we later decide
+        * that it's not a DiskOnChip, and want to put it back how we
+        * found it.
+        */
+       save_control = ReadDOC(virtadr, DOCControl);
+       /* Reset the DiskOnChip ASIC */
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
+                virtadr, DOCControl);
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
+                virtadr, DOCControl);
+       /* Enable the DiskOnChip ASIC */
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
+                virtadr, DOCControl);
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
+                virtadr, DOCControl);
+       ChipID = ReadDOC(virtadr, ChipID);
+       switch(ChipID) {
+       case DOC_ChipID_Doc2k:
+               reg = DoC_2k_ECCStatus;
+               break;
+       case DOC_ChipID_DocMil:
+               reg = DoC_ECCConf;
+               break;
+       case DOC_ChipID_DocMilPlus16:
+       case DOC_ChipID_DocMilPlus32:
+       case 0:
+               /* Possible Millennium Plus, need to do more checks */
+               /* Possibly release from power down mode */
+               for (tmp = 0; (tmp < 4); tmp++)
+                       ReadDOC(virtadr, Mplus_Power);
+               /* Reset the Millennium Plus ASIC */
+               tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+                       DOC_MODE_BDECT;
+               WriteDOC(tmp, virtadr, Mplus_DOCControl);
+               WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+               mdelay(1);
+               /* Enable the Millennium Plus ASIC */
+               tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+                       DOC_MODE_BDECT;
+               WriteDOC(tmp, virtadr, Mplus_DOCControl);
+               WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+               mdelay(1);
+               ChipID = ReadDOC(virtadr, ChipID);
+               switch (ChipID) {
+               case DOC_ChipID_DocMilPlus16:
+                       reg = DoC_Mplus_Toggle;
+                       break;
+               case DOC_ChipID_DocMilPlus32:
+                       printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
+               default:
+                       ret = -ENODEV;
+                       goto notfound;
+               }
+               break;
+       default:
+               ret = -ENODEV;
+               goto notfound;
+       }
+       /* Check the TOGGLE bit in the ECC register */
+       tmp  = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       if ((tmp == tmpb) || (tmp != tmpc)) {
+               printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
+               ret = -ENODEV;
+               goto notfound;
+       }
+       for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+               unsigned char oldval;
+               unsigned char newval;
+               nand = mtd->priv;
+               doc = nand->priv;
+               /* Use the alias resolution register to determine if this is
+                  in fact the same DOC aliased to a new address.  If writes
+                  to one chip's alias resolution register change the value on
+                  the other chip, they're the same chip. */
+               if (ChipID == DOC_ChipID_DocMilPlus16) {
+                       oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+                       newval = ReadDOC(virtadr, Mplus_AliasResolution);
+               } else {
+                       oldval = ReadDOC(doc->virtadr, AliasResolution);
+                       newval = ReadDOC(virtadr, AliasResolution);
+               }
+               if (oldval != newval)
+                       continue;
+               if (ChipID == DOC_ChipID_DocMilPlus16) {
+                       WriteDOC(~newval, virtadr, Mplus_AliasResolution);
+                       oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+                       WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */
+               } else {
+                       WriteDOC(~newval, virtadr, AliasResolution);
+                       oldval = ReadDOC(doc->virtadr, AliasResolution);
+                       WriteDOC(newval, virtadr, AliasResolution); /* restore it */
+               }
+               newval = ~newval;
+               if (oldval == newval) {
+                       printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
+                       goto notfound;
+               }
+       }
+       printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
+       len = sizeof(struct mtd_info) +
+             sizeof(struct nand_chip) +
+             sizeof(struct doc_priv) +
+             (2 * sizeof(struct nand_bbt_descr));
+       mtd =  kmalloc(len, GFP_KERNEL);
+       if (!mtd) {
+               printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
+               ret = -ENOMEM;
+               goto fail;
+       }
+       memset(mtd, 0, len);
+       nand                    = (struct nand_chip *) (mtd + 1);
+       doc                     = (struct doc_priv *) (nand + 1);
+       nand->bbt_td            = (struct nand_bbt_descr *) (doc + 1);
+       nand->bbt_md            = nand->bbt_td + 1;
+       mtd->priv               = nand;
+       mtd->owner              = THIS_MODULE;
+       nand->priv              = doc;
+       nand->select_chip       = doc200x_select_chip;
+       nand->hwcontrol         = doc200x_hwcontrol;
+       nand->dev_ready         = doc200x_dev_ready;
+       nand->waitfunc          = doc200x_wait;
+       nand->block_bad         = doc200x_block_bad;
+       nand->enable_hwecc      = doc200x_enable_hwecc;
+       nand->calculate_ecc     = doc200x_calculate_ecc;
+       nand->correct_data      = doc200x_correct_data;
+       nand->autooob           = &doc200x_oobinfo;
+       nand->eccmode           = NAND_ECC_HW6_512;
+       nand->options           = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+       doc->physadr            = physadr;
+       doc->virtadr            = virtadr;
+       doc->ChipID             = ChipID;
+       doc->curfloor           = -1;
+       doc->curchip            = -1;
+       doc->mh0_page           = -1;
+       doc->mh1_page           = -1;
+       doc->nextdoc            = doclist;
+       if (ChipID == DOC_ChipID_Doc2k)
+               numchips = doc2000_init(mtd);
+       else if (ChipID == DOC_ChipID_DocMilPlus16)
+               numchips = doc2001plus_init(mtd);
+       else
+               numchips = doc2001_init(mtd);
+       if ((ret = nand_scan(mtd, numchips))) {
+               /* DBB note: i believe nand_release is necessary here, as
+                  buffers may have been allocated in nand_base.  Check with
+                  Thomas. FIX ME! */
+               /* nand_release will call del_mtd_device, but we haven't yet
+                  added it.  This is handled without incident by
+                  del_mtd_device, as far as I can tell. */
+               nand_release(mtd);
+               kfree(mtd);
+               goto fail;
+       }
+       /* Success! */
+       doclist = mtd;
+       return 0;
+ notfound:
+       /* Put back the contents of the DOCControl register, in case it's not
+          actually a DiskOnChip.  */
+       WriteDOC(save_control, virtadr, DOCControl);
+ fail:
+       iounmap(virtadr);
+       return ret;
+ }
+ static void release_nanddoc(void)
+ {
+       struct mtd_info *mtd, *nextmtd;
+       struct nand_chip *nand;
+       struct doc_priv *doc;
+       for (mtd = doclist; mtd; mtd = nextmtd) {
+               nand = mtd->priv;
+               doc = nand->priv;
+               nextmtd = doc->nextdoc;
+               nand_release(mtd);
+               iounmap(doc->virtadr);
+               kfree(mtd);
+       }
+ }
+ static int __init init_nanddoc(void)
+ {
+       int i, ret = 0;
+       /* We could create the decoder on demand, if memory is a concern.
+        * This way we have it handy, if an error happens
+        *
+        * Symbolsize is 10 (bits)
+        * Primitve polynomial is x^10+x^3+1
+        * first consecutive root is 510
+        * primitve element to generate roots = 1
+        * generator polinomial degree = 4
+        */
+       rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
+       if (!rs_decoder) {
+               printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
+               return -ENOMEM;
+       }
+       if (doc_config_location) {
+               printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+               ret = doc_probe(doc_config_location);
+               if (ret < 0)
+                       goto outerr;
+       } else {
+               for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+                       doc_probe(doc_locations[i]);
+               }
+       }
+       /* No banner message any more. Print a message if no DiskOnChip
+          found, so the user knows we at least tried. */
+       if (!doclist) {
+               printk(KERN_INFO "No valid DiskOnChip devices found\n");
+               ret = -ENODEV;
+               goto outerr;
+       }
+       return 0;
+ outerr:
+       free_rs(rs_decoder);
+       return ret;
+ }
+ static void __exit cleanup_nanddoc(void)
+ {
+       /* Cleanup the nand/DoC resources */
+       release_nanddoc();
+       /* Free the reed solomon resources */
+       if (rs_decoder) {
+               free_rs(rs_decoder);
+       }
+ }
+ module_init(init_nanddoc);
+ module_exit(cleanup_nanddoc);
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+ MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n");
++#endif /* CONFIG_NEW_NAND_CODE */
index 0000000,d187c89..bc85005
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,71 +1,76 @@@
 -      nand->IO_ADDR_R = nand->IO_ADDR_W = base_addr;
+ /*
+  * (C) Copyright 2005
+  * 2N Telekomunikace, a.s. <www.2n.cz>
+  * Ladislav Michl <michl@2n.cz>
+  *
+  * See file CREDITS for list of people who contributed to this
+  * project.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+  * MA 02111-1307 USA
+  */
+ #include <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #if (CONFIG_COMMANDS & CFG_CMD_NAND)
+ #include <nand.h>
+ #ifndef CFG_NAND_BASE_LIST
+ #define CFG_NAND_BASE_LIST { CFG_NAND_BASE }
+ #endif
+ int nand_curr_device = -1;
+ nand_info_t nand_info[CFG_MAX_NAND_DEVICE];
+ static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];
+ static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST;
+ static const char default_nand_name[] = "nand";
+ extern void board_nand_init(struct nand_chip *nand);
+ static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
+                          ulong base_addr)
+ {
+       mtd->priv = nand;
 -                      mtd->name = default_nand_name;
++      nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
+       board_nand_init(nand);
+       if (nand_scan(mtd, 1) == 0) {
+               if (!mtd->name)
 -
++                      mtd->name = (char *)default_nand_name;
+       } else
+               mtd->name = NULL;
+ }
+ void nand_init(void)
+ {
+       int i;
 -      }
++      unsigned int size = 0;
+       for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
+               nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
++              size += nand_info[i].size;
+               if (nand_curr_device == -1)
+                       nand_curr_device = i;
++}
++      printf("%lu MiB\n", size / (1024 * 1024));
+ }
+ #endif
++#endif /* CONFIG_NEW_NAND_CODE */
++
index 0000000,9ec5af9..b039c3c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2658 +1,2664 @@@
 -      reset_timer_masked();
+ /*
+  *  drivers/mtd/nand.c
+  *
+  *  Overview:
+  *   This is the generic MTD driver for NAND flash devices. It should be
+  *   capable of working with almost all NAND chips currently available.
+  *   Basic support for AG-AND chips is provided.
+  *
+  *    Additional technical information is available on
+  *    http://www.linux-mtd.infradead.org/tech/nand.html
+  *
+  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+  *              2002 Thomas Gleixner (tglx@linutronix.de)
+  *
+  *  02-08-2004  tglx: support for strange chips, which cannot auto increment
+  *            pages on read / read_oob
+  *
+  *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
+  *            pointed this out, as he marked an auto increment capable chip
+  *            as NOAUTOINCR in the board driver.
+  *            Make reads over block boundaries work too
+  *
+  *  04-14-2004        tglx: first working version for 2k page size chips
+  *
+  *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
+  *
+  *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared
+  *            among multiple independend devices. Suggestions and initial patch
+  *            from Ben Dooks <ben-mtd@fluff.org>
+  *
+  * Credits:
+  *    David Woodhouse for adding multichip support
+  *
+  *    Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+  *    rework for 2K page size chips
+  *
+  * TODO:
+  *    Enable cached programming for 2k page size chips
+  *    Check, if mtd->ecctype should be set to MTD_ECC_HW
+  *    if we have HW ecc support.
+  *    The AG-AND chips have nice features for speed improvement,
+  *    which are not supported yet. Read / program 4 pages in one go.
+  *
+  * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen 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
+  * published by the Free Software Foundation.
+  *
+  */
+ /* XXX U-BOOT XXX */
+ #if 0
+ #include <linux/delay.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/types.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+ #include <linux/mtd/compatmac.h>
+ #include <linux/interrupt.h>
+ #include <linux/bitops.h>
+ #include <asm/io.h>
+ #ifdef CONFIG_MTD_PARTITIONS
+ #include <linux/mtd/partitions.h>
+ #endif
+ #endif
+ #include <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #if (CONFIG_COMMANDS & CFG_CMD_NAND)
+ #include <malloc.h>
+ #include <watchdog.h>
+ #include <linux/mtd/compat.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+ #include <asm/io.h>
+ #include <asm/errno.h>
+ #ifdef CONFIG_JFFS2_NAND
+ #include <jffs2/jffs2.h>
+ #endif
+ /* Define default oob placement schemes for large and small page devices */
+ static struct nand_oobinfo nand_oob_8 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 3,
+       .eccpos = {0, 1, 2},
+       .oobfree = { {3, 2}, {6, 2} }
+ };
+ static struct nand_oobinfo nand_oob_16 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 6,
+       .eccpos = {0, 1, 2, 3, 6, 7},
+       .oobfree = { {8, 8} }
+ };
+ static struct nand_oobinfo nand_oob_64 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 24,
+       .eccpos = {
+               40, 41, 42, 43, 44, 45, 46, 47,
+               48, 49, 50, 51, 52, 53, 54, 55,
+               56, 57, 58, 59, 60, 61, 62, 63},
+       .oobfree = { {2, 38} }
+ };
+ /* This is used for padding purposes in nand_write_oob */
+ static u_char ffchars[] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ };
+ /*
+  * NAND low-level MTD interface functions
+  */
+ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
+ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
+ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                         size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+ static int nand_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 *oobsel);
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+ /* XXX U-BOOT XXX */
+ #if 0
+ static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
+                       unsigned long count, loff_t to, size_t * retlen);
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
+                       unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+ #endif
+ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+ static void nand_sync (struct mtd_info *mtd);
+ /* Some internal functions */
+ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
+               struct nand_oobinfo *oobsel, int mode);
+ #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
+       u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
+ #else
+ #define nand_verify_pages(...) (0)
+ #endif
+ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+ /**
+  * nand_release_device - [GENERIC] release chip
+  * @mtd:      MTD device structure
+  *
+  * Deselect, release chip lock and wake up anyone waiting on the device
+  */
+ /* XXX U-BOOT XXX */
+ #if 0
+ static void nand_release_device (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* De-select the NAND device */
+       this->select_chip(mtd, -1);
+       /* Do we have a hardware controller ? */
+       if (this->controller) {
+               spin_lock(&this->controller->lock);
+               this->controller->active = NULL;
+               spin_unlock(&this->controller->lock);
+       }
+       /* Release the chip */
+       spin_lock (&this->chip_lock);
+       this->state = FL_READY;
+       wake_up (&this->wq);
+       spin_unlock (&this->chip_lock);
+ }
+ #else
+ static void nand_release_device (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       this->select_chip(mtd, -1);     /* De-select the NAND device */
+ }
+ #endif
+ /**
+  * nand_read_byte - [DEFAULT] read one byte from the chip
+  * @mtd:      MTD device structure
+  *
+  * Default read function for 8bit buswith
+  */
+ static u_char nand_read_byte(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       return readb(this->IO_ADDR_R);
+ }
+ /**
+  * nand_write_byte - [DEFAULT] write one byte to the chip
+  * @mtd:      MTD device structure
+  * @byte:     pointer to data byte to write
+  *
+  * Default write function for 8it buswith
+  */
+ static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+ {
+       struct nand_chip *this = mtd->priv;
+       writeb(byte, this->IO_ADDR_W);
+ }
+ /**
+  * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
+  * @mtd:      MTD device structure
+  *
+  * Default read function for 16bit buswith with
+  * endianess conversion
+  */
+ static u_char nand_read_byte16(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+ }
+ /**
+  * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
+  * @mtd:      MTD device structure
+  * @byte:     pointer to data byte to write
+  *
+  * Default write function for 16bit buswith with
+  * endianess conversion
+  */
+ static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
+ {
+       struct nand_chip *this = mtd->priv;
+       writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+ }
+ /**
+  * nand_read_word - [DEFAULT] read one word from the chip
+  * @mtd:      MTD device structure
+  *
+  * Default read function for 16bit buswith without
+  * endianess conversion
+  */
+ static u16 nand_read_word(struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       return readw(this->IO_ADDR_R);
+ }
+ /**
+  * nand_write_word - [DEFAULT] write one word to the chip
+  * @mtd:      MTD device structure
+  * @word:     data word to write
+  *
+  * Default write function for 16bit buswith without
+  * endianess conversion
+  */
+ static void nand_write_word(struct mtd_info *mtd, u16 word)
+ {
+       struct nand_chip *this = mtd->priv;
+       writew(word, this->IO_ADDR_W);
+ }
+ /**
+  * nand_select_chip - [DEFAULT] control CE line
+  * @mtd:      MTD device structure
+  * @chip:     chipnumber to select, -1 for deselect
+  *
+  * Default select function for 1 chip devices.
+  */
+ static void nand_select_chip(struct mtd_info *mtd, int chip)
+ {
+       struct nand_chip *this = mtd->priv;
+       switch(chip) {
+       case -1:
+               this->hwcontrol(mtd, NAND_CTL_CLRNCE);
+               break;
+       case 0:
+               this->hwcontrol(mtd, NAND_CTL_SETNCE);
+               break;
+       default:
+               BUG();
+       }
+ }
+ /**
+  * nand_write_buf - [DEFAULT] write buffer to chip
+  * @mtd:      MTD device structure
+  * @buf:      data buffer
+  * @len:      number of bytes to write
+  *
+  * Default write function for 8bit buswith
+  */
+ static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       for (i=0; i<len; i++)
+               writeb(buf[i], this->IO_ADDR_W);
+ }
+ /**
+  * nand_read_buf - [DEFAULT] read chip data into buffer
+  * @mtd:      MTD device structure
+  * @buf:      buffer to store date
+  * @len:      number of bytes to read
+  *
+  * Default read function for 8bit buswith
+  */
+ static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       for (i=0; i<len; i++)
+               buf[i] = readb(this->IO_ADDR_R);
+ }
+ /**
+  * nand_verify_buf - [DEFAULT] Verify chip data against buffer
+  * @mtd:      MTD device structure
+  * @buf:      buffer containing the data to compare
+  * @len:      number of bytes to compare
+  *
+  * Default verify function for 8bit buswith
+  */
+ static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       for (i=0; i<len; i++)
+               if (buf[i] != readb(this->IO_ADDR_R))
+                       return -EFAULT;
+       return 0;
+ }
+ /**
+  * nand_write_buf16 - [DEFAULT] write buffer to chip
+  * @mtd:      MTD device structure
+  * @buf:      data buffer
+  * @len:      number of bytes to write
+  *
+  * Default write function for 16bit buswith
+  */
+ static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+       for (i=0; i<len; i++)
+               writew(p[i], this->IO_ADDR_W);
+ }
+ /**
+  * nand_read_buf16 - [DEFAULT] read chip data into buffer
+  * @mtd:      MTD device structure
+  * @buf:      buffer to store date
+  * @len:      number of bytes to read
+  *
+  * Default read function for 16bit buswith
+  */
+ static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+       for (i=0; i<len; i++)
+               p[i] = readw(this->IO_ADDR_R);
+ }
+ /**
+  * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
+  * @mtd:      MTD device structure
+  * @buf:      buffer containing the data to compare
+  * @len:      number of bytes to compare
+  *
+  * Default verify function for 16bit buswith
+  */
+ static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+ {
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+       for (i=0; i<len; i++)
+               if (p[i] != readw(this->IO_ADDR_R))
+                       return -EFAULT;
+       return 0;
+ }
+ /**
+  * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+  * @mtd:      MTD device structure
+  * @ofs:      offset from device start
+  * @getchip:  0, if the chip is already selected
+  *
+  * Check, if the block is bad.
+  */
+ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+ {
+       int page, chipnr, res = 0;
+       struct nand_chip *this = mtd->priv;
+       u16 bad;
+       if (getchip) {
+               page = (int)(ofs >> this->page_shift);
+               chipnr = (int)(ofs >> this->chip_shift);
+               /* Grab the lock and see if the device is available */
+               nand_get_device (this, mtd, FL_READING);
+               /* Select the NAND device */
+               this->select_chip(mtd, chipnr);
+       } else
+               page = (int) ofs;
+       if (this->options & NAND_BUSWIDTH_16) {
+               this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
+               bad = cpu_to_le16(this->read_word(mtd));
+               if (this->badblockpos & 0x1)
+                       bad >>= 1;
+               if ((bad & 0xFF) != 0xff)
+                       res = 1;
+       } else {
+               this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
+               if (this->read_byte(mtd) != 0xff)
+                       res = 1;
+       }
+       if (getchip) {
+               /* Deselect and wake up anyone waiting on the device */
+               nand_release_device(mtd);
+       }
+       return res;
+ }
+ /**
+  * nand_default_block_markbad - [DEFAULT] mark a block bad
+  * @mtd:      MTD device structure
+  * @ofs:      offset from device start
+  *
+  * This is the default implementation, which can be overridden by
+  * a hardware specific driver.
+ */
+ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+ {
+       struct nand_chip *this = mtd->priv;
+       u_char buf[2] = {0, 0};
+       size_t  retlen;
+       int block;
+       /* Get block number */
+       block = ((int) ofs) >> this->bbt_erase_shift;
+       this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+       /* Do we have a flash based bad block table ? */
+       if (this->options & NAND_USE_FLASH_BBT)
+               return nand_update_bbt (mtd, ofs);
+       /* We write two bytes, so we dont have to mess with 16 bit access */
+       ofs += mtd->oobsize + (this->badblockpos & ~0x01);
+       return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+ }
+ /**
+  * nand_check_wp - [GENERIC] check if the chip is write protected
+  * @mtd:      MTD device structure
+  * Check, if the device is write protected
+  *
+  * The function expects, that the device is already selected
+  */
+ static int nand_check_wp (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Check the WP bit */
+       this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+       return (this->read_byte(mtd) & 0x80) ? 0 : 1;
+ }
+ /**
+  * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+  * @mtd:      MTD device structure
+  * @ofs:      offset from device start
+  * @getchip:  0, if the chip is already selected
+  * @allowbbt: 1, if its allowed to access the bbt area
+  *
+  * Check, if the block is bad. Either by reading the bad block table or
+  * calling of the scan function.
+  */
+ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+ {
+       struct nand_chip *this = mtd->priv;
+       if (!this->bbt)
+               return this->block_bad(mtd, ofs, getchip);
+       /* Return info from the table */
+       return nand_isbad_bbt (mtd, ofs, allowbbt);
+ }
+ /**
+  * nand_command - [DEFAULT] Send command to NAND device
+  * @mtd:      MTD device structure
+  * @command:  the command to be sent
+  * @column:   the column address for this command, -1 if none
+  * @page_addr:        the page address for this command, -1 if none
+  *
+  * Send command to NAND device. This function is used for small page
+  * devices (256/512 Bytes per page)
+  */
+ static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+ {
+       register struct nand_chip *this = mtd->priv;
+       /* Begin command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_SETCLE);
+       /*
+        * Write out the command to the device.
+        */
+       if (command == NAND_CMD_SEQIN) {
+               int readcmd;
+               if (column >= mtd->oobblock) {
+                       /* OOB area */
+                       column -= mtd->oobblock;
+                       readcmd = NAND_CMD_READOOB;
+               } else if (column < 256) {
+                       /* First 256 bytes --> READ0 */
+                       readcmd = NAND_CMD_READ0;
+               } else {
+                       column -= 256;
+                       readcmd = NAND_CMD_READ1;
+               }
+               this->write_byte(mtd, readcmd);
+       }
+       this->write_byte(mtd, command);
+       /* Set ALE and clear CLE to start address cycle */
+       this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+       if (column != -1 || page_addr != -1) {
+               this->hwcontrol(mtd, NAND_CTL_SETALE);
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       this->write_byte(mtd, column);
+               }
+               if (page_addr != -1) {
+                       this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                       this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+                       /* One more address cycle for devices > 32MiB */
+                       if (this->chipsize > (32 << 20))
+                               this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+               }
+               /* Latch in address */
+               this->hwcontrol(mtd, NAND_CTL_CLRALE);
+       }
+       /*
+        * program and erase have their own busy handlers
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+       case NAND_CMD_RESET:
+               if (this->dev_ready)
+                       break;
+               udelay(this->chip_delay);
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               this->write_byte(mtd, NAND_CMD_STATUS);
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+       /* This applies to read commands */
+       default:
+               /*
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }
+       }
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+ }
+ /**
+  * nand_command_lp - [DEFAULT] Send command to NAND large page device
+  * @mtd:      MTD device structure
+  * @command:  the command to be sent
+  * @column:   the column address for this command, -1 if none
+  * @page_addr:        the page address for this command, -1 if none
+  *
+  * Send command to NAND device. This is the version for the new large page devices
+  * We dont have the seperate regions as we have in the small page devices.
+  * We must emulate NAND_CMD_READOOB to keep the code compatible.
+  *
+  */
+ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+ {
+       register struct nand_chip *this = mtd->priv;
+       /* Emulate NAND_CMD_READOOB */
+       if (command == NAND_CMD_READOOB) {
+               column += mtd->oobblock;
+               command = NAND_CMD_READ0;
+       }
+       /* Begin command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_SETCLE);
+       /* Write out the command to the device. */
+       this->write_byte(mtd, command);
+       /* End command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+       if (column != -1 || page_addr != -1) {
+               this->hwcontrol(mtd, NAND_CTL_SETALE);
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       this->write_byte(mtd, column & 0xff);
+                       this->write_byte(mtd, column >> 8);
+               }
+               if (page_addr != -1) {
+                       this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                       this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+                       /* One more address cycle for devices > 128MiB */
+                       if (this->chipsize > (128 << 20))
+                               this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
+               }
+               /* Latch in address */
+               this->hwcontrol(mtd, NAND_CTL_CLRALE);
+       }
+       /*
+        * program and erase have their own busy handlers
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+       case NAND_CMD_CACHEDPROG:
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+       case NAND_CMD_RESET:
+               if (this->dev_ready)
+                       break;
+               udelay(this->chip_delay);
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               this->write_byte(mtd, NAND_CMD_STATUS);
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+       case NAND_CMD_READ0:
+               /* Begin command latch cycle */
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               /* Write out the start read command */
+               this->write_byte(mtd, NAND_CMD_READSTART);
+               /* End command latch cycle */
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               /* Fall through into ready check */
+       /* This applies to read commands */
+       default:
+               /*
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }
+       }
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+ }
+ /**
+  * nand_get_device - [GENERIC] Get chip for selected access
+  * @this:     the nand chip descriptor
+  * @mtd:      MTD device structure
+  * @new_state:        the state which is requested
+  *
+  * Get the device and lock it for exclusive access
+  */
+ /* XXX U-BOOT XXX */
+ #if 0
+ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+ {
+       struct nand_chip *active = this;
+       DECLARE_WAITQUEUE (wait, current);
+       /*
+        * Grab the lock and see if the device is available
+       */
+ retry:
+       /* Hardware controller shared among independend devices */
+       if (this->controller) {
+               spin_lock (&this->controller->lock);
+               if (this->controller->active)
+                       active = this->controller->active;
+               else
+                       this->controller->active = this;
+               spin_unlock (&this->controller->lock);
+       }
+       if (active == this) {
+               spin_lock (&this->chip_lock);
+               if (this->state == FL_READY) {
+                       this->state = new_state;
+                       spin_unlock (&this->chip_lock);
+                       return;
+               }
+       }
+       set_current_state (TASK_UNINTERRUPTIBLE);
+       add_wait_queue (&active->wq, &wait);
+       spin_unlock (&active->chip_lock);
+       schedule ();
+       remove_wait_queue (&active->wq, &wait);
+       goto retry;
+ }
+ #else
+ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) {}
+ #endif
+ /**
+  * nand_wait - [DEFAULT]  wait until the command is done
+  * @mtd:      MTD device structure
+  * @this:     NAND chip structure
+  * @state:    state to select the max. timeout value
+  *
+  * Wait for command done. This applies to erase and program only
+  * Erase can take up to 400ms and program up to 20ms according to
+  * general NAND and SmartMedia specs
+  *
+ */
+ /* XXX U-BOOT XXX */
+ #if 0
+ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+ {
+       unsigned long   timeo = jiffies;
+       int     status;
+       if (state == FL_ERASING)
+                timeo += (HZ * 400) / 1000;
+       else
+                timeo += (HZ * 20) / 1000;
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+               this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+       else
+               this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+       while (time_before(jiffies, timeo)) {
+               /* Check, if we were interrupted */
+               if (this->state != state)
+                       return 0;
+               if (this->dev_ready) {
+                       if (this->dev_ready(mtd))
+                               break;
+               } else {
+                       if (this->read_byte(mtd) & NAND_STATUS_READY)
+                               break;
+               }
+               yield ();
+       }
+       status = (int) this->read_byte(mtd);
+       return status;
+       return 0;
+ }
+ #else
+ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+ {
+       unsigned long   timeo;
+       if (state == FL_ERASING)
+               timeo = CFG_HZ * 400;
+       else
+               timeo = CFG_HZ * 20;
+       if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+               this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+       else
+               this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
 -              if (get_timer_masked() > timeo)
++      reset_timer();
+       while (1) {
 -              printk (KERN_INFO "NAND device: Manufacturer ID:"
 -                      " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
 -                      nand_manuf_ids[j].name , nand_flash_ids[i].name);
++              if (get_timer(0) > timeo) {
++                      printf("Timeout!");
+                       return 0;
++                      }
+               if (this->dev_ready) {
+                       if (this->dev_ready(mtd))
+                               break;
+               } else {
+                       if (this->read_byte(mtd) & NAND_STATUS_READY)
+                               break;
+               }
+       }
++      /* XXX nand device 1 on dave (PPChameleonEVB) needs more time */
++      reset_timer();
++      while (get_timer(0) < 10);
++
+       return this->read_byte(mtd);
+ }
+ #endif
+ /**
+  * nand_write_page - [GENERIC] write one page
+  * @mtd:      MTD device structure
+  * @this:     NAND chip structure
+  * @page:     startpage inside the chip, must be called with (page & this->pagemask)
+  * @oob_buf:  out of band data buffer
+  * @oobsel:   out of band selecttion structre
+  * @cached:   1 = enable cached programming if supported by chip
+  *
+  * Nand_page_program function is used for write and writev !
+  * This function will always program a full page of data
+  * If you call it with a non page aligned buffer, you're lost :)
+  *
+  * Cached programming is not supported yet.
+  */
+ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,
+       u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
+ {
+       int     i, status;
+       u_char  ecc_code[32];
+       int     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+       int     *oob_config = oobsel->eccpos;
+       int     datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+       int     eccbytes = 0;
+       /* FIXME: Enable cached programming */
+       cached = 0;
+       /* Send command to begin auto page programming */
+       this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+       /* Write out complete page of data, take care of eccmode */
+       switch (eccmode) {
+       /* No ecc, write all */
+       case NAND_ECC_NONE:
+               printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
+               this->write_buf(mtd, this->data_poi, mtd->oobblock);
+               break;
+       /* Software ecc 3/256, write all */
+       case NAND_ECC_SOFT:
+               for (; eccsteps; eccsteps--) {
+                       this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+                       for (i = 0; i < 3; i++, eccidx++)
+                               oob_buf[oob_config[eccidx]] = ecc_code[i];
+                       datidx += this->eccsize;
+               }
+               this->write_buf(mtd, this->data_poi, mtd->oobblock);
+               break;
+       default:
+               eccbytes = this->eccbytes;
+               for (; eccsteps; eccsteps--) {
+                       /* enable hardware ecc logic for write */
+                       this->enable_hwecc(mtd, NAND_ECC_WRITE);
+                       this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+                       this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+                       for (i = 0; i < eccbytes; i++, eccidx++)
+                               oob_buf[oob_config[eccidx]] = ecc_code[i];
+                       /* If the hardware ecc provides syndromes then
+                        * the ecc code must be written immidiately after
+                        * the data bytes (words) */
+                       if (this->options & NAND_HWECC_SYNDROME)
+                               this->write_buf(mtd, ecc_code, eccbytes);
+                       datidx += this->eccsize;
+               }
+               break;
+       }
+       /* Write out OOB data */
+       if (this->options & NAND_HWECC_SYNDROME)
+               this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
+       else
+               this->write_buf(mtd, oob_buf, mtd->oobsize);
+       /* Send command to actually program the data */
+       this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
+       if (!cached) {
+               /* call wait ready function */
+               status = this->waitfunc (mtd, this, FL_WRITING);
+               /* See if device thinks it succeeded */
+               if (status & 0x01) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
+                       return -EIO;
+               }
+       } else {
+               /* FIXME: Implement cached programming ! */
+               /* wait until cache is ready*/
+               /* status = this->waitfunc (mtd, this, FL_CACHEDRPG); */
+       }
+       return 0;
+ }
+ #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+ /**
+  * nand_verify_pages - [GENERIC] verify the chip contents after a write
+  * @mtd:      MTD device structure
+  * @this:     NAND chip structure
+  * @page:     startpage inside the chip, must be called with (page & this->pagemask)
+  * @numpages: number of pages to verify
+  * @oob_buf:  out of band data buffer
+  * @oobsel:   out of band selecttion structre
+  * @chipnr:   number of the current chip
+  * @oobmode:  1 = full buffer verify, 0 = ecc only
+  *
+  * The NAND device assumes that it is always writing to a cleanly erased page.
+  * Hence, it performs its internal write verification only on bits that
+  * transitioned from 1 to 0. The device does NOT verify the whole page on a
+  * byte by byte basis. It is possible that the page was not completely erased
+  * or the page is becoming unusable due to wear. The read with ECC would catch
+  * the error later when the ECC page check fails, but we would rather catch
+  * it early in the page write stage. Better to write no data than invalid data.
+  */
+ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
+       u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
+ {
+       int     i, j, datidx = 0, oobofs = 0, res = -EIO;
+       int     eccsteps = this->eccsteps;
+       int     hweccbytes;
+       u_char  oobdata[64];
+       hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
+       /* Send command to read back the first page */
+       this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+       for(;;) {
+               for (j = 0; j < eccsteps; j++) {
+                       /* Loop through and verify the data */
+                       if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+                       datidx += mtd->eccsize;
+                       /* Have we a hw generator layout ? */
+                       if (!hweccbytes)
+                               continue;
+                       if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+                       oobofs += hweccbytes;
+               }
+               /* check, if we must compare all data or if we just have to
+                * compare the ecc bytes
+                */
+               if (oobmode) {
+                       if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+               } else {
+                       /* Read always, else autoincrement fails */
+                       this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
+                       if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
+                               int ecccnt = oobsel->eccbytes;
+                               for (i = 0; i < ecccnt; i++) {
+                                       int idx = oobsel->eccpos[i];
+                                       if (oobdata[idx] != oob_buf[oobofs + idx] ) {
+                                               DEBUG (MTD_DEBUG_LEVEL0,
+                                               "%s: Failed ECC write "
+                                               "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
+                                               goto out;
+                                       }
+                               }
+                       }
+               }
+               oobofs += mtd->oobsize - hweccbytes * eccsteps;
+               page++;
+               numpages--;
+               /* Apply delay or wait for ready/busy pin
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+                * Do this also before returning, so the chip is
+                * ready for the next command.
+               */
+               if (!this->dev_ready)
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));
+               /* All done, return happy */
+               if (!numpages)
+                       return 0;
+               /* Check, if the chip supports auto page increment */
+               if (!NAND_CANAUTOINCR(this))
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+       }
+       /*
+        * Terminate the read command. We come here in case of an error
+        * So we must issue a reset command.
+        */
+ out:
+       this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
+       return res;
+ }
+ #endif
+ /**
+  * nand_read - [MTD Interface] MTD compability function for nand_read_ecc
+  * @mtd:      MTD device structure
+  * @from:     offset to read from
+  * @len:      number of bytes to read
+  * @retlen:   pointer to variable to store the number of read bytes
+  * @buf:      the databuffer to put data
+  *
+  * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
+ */
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+ {
+       return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+ }
+ /**
+  * nand_read_ecc - [MTD Interface] Read data with ECC
+  * @mtd:      MTD device structure
+  * @from:     offset to read from
+  * @len:      number of bytes to read
+  * @retlen:   pointer to variable to store the number of read bytes
+  * @buf:      the databuffer to put data
+  * @oob_buf:  filesystem supplied oob data buffer
+  * @oobsel:   oob selection structure
+  *
+  * NAND read with ECC
+  */
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                         size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+ {
+       int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
+       int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
+       struct nand_chip *this = mtd->priv;
+       u_char *data_poi, *oob_data = oob_buf;
+       u_char ecc_calc[32];
+       u_char ecc_code[32];
+       int eccmode, eccsteps;
+       int     *oob_config, datidx;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+       int     eccbytes;
+       int     compareecc = 1;
+       int     oobreadlen;
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
+               *retlen = 0;
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd ,FL_READING);
+       /* use userspace supplied oobinfo, if zero */
+       if (oobsel == NULL)
+               oobsel = &mtd->oobinfo;
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+               oobsel = this->autooob;
+       eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+       oob_config = oobsel->eccpos;
+       /* Select the NAND device */
+       chipnr = (int)(from >> this->chip_shift);
+       this->select_chip(mtd, chipnr);
+       /* First we calculate the starting page */
+       realpage = (int) (from >> this->page_shift);
+       page = realpage & this->pagemask;
+       /* Get raw starting column */
+       col = from & (mtd->oobblock - 1);
+       end = mtd->oobblock;
+       ecc = this->eccsize;
+       eccbytes = this->eccbytes;
+       if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
+               compareecc = 0;
+       oobreadlen = mtd->oobsize;
+       if (this->options & NAND_HWECC_SYNDROME)
+               oobreadlen -= oobsel->eccbytes;
+       /* Loop until all data read */
+       while (read < len) {
+               int aligned = (!col && (len - read) >= end);
+               /*
+                * If the read is not page aligned, we have to read into data buffer
+                * due to ecc, else we read into return buffer direct
+                */
+               if (aligned)
+                       data_poi = &buf[read];
+               else
+                       data_poi = this->data_buf;
+               /* Check, if we have this page in the buffer
+                *
+                * FIXME: Make it work when we must provide oob data too,
+                * check the usage of data_buf oob field
+                */
+               if (realpage == this->pagebuf && !oob_buf) {
+                       /* aligned read ? */
+                       if (aligned)
+                               memcpy (data_poi, this->data_buf, end);
+                       goto readdata;
+               }
+               /* Check, if we must send the read command */
+               if (sndcmd) {
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+                       sndcmd = 0;
+               }
+               /* get oob area, if we have no oob buffer from fs-driver */
+               if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
+                       oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+                       oob_data = &this->data_buf[end];
+               eccsteps = this->eccsteps;
+               switch (eccmode) {
+               case NAND_ECC_NONE: {   /* No ECC, Read in a page */
+ /* XXX U-BOOT XXX */
+ #if 0
+                       static unsigned long lastwhinge = 0;
+                       if ((lastwhinge / HZ) != (jiffies / HZ)) {
+                               printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
+                               lastwhinge = jiffies;
+                       }
+ #else
+                       puts("Reading data from NAND FLASH without ECC is not recommended\n");
+ #endif
+                       this->read_buf(mtd, data_poi, end);
+                       break;
+               }
+               case NAND_ECC_SOFT:     /* Software ECC 3/256: Read in a page + oob data */
+                       this->read_buf(mtd, data_poi, end);
+                       for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
+                               this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+                       break;
+               default:
+                       for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
+                               this->enable_hwecc(mtd, NAND_ECC_READ);
+                               this->read_buf(mtd, &data_poi[datidx], ecc);
+                               /* HW ecc with syndrome calculation must read the
+                                * syndrome from flash immidiately after the data */
+                               if (!compareecc) {
+                                       /* Some hw ecc generators need to know when the
+                                        * syndrome is read from flash */
+                                       this->enable_hwecc(mtd, NAND_ECC_READSYN);
+                                       this->read_buf(mtd, &oob_data[i], eccbytes);
+                                       /* We calc error correction directly, it checks the hw
+                                        * generator for an error, reads back the syndrome and
+                                        * does the error correction on the fly */
+                                       if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
+                                               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
+                                                       "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
+                                               ecc_failed++;
+                                       }
+                               } else {
+                                       this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+                               }
+                       }
+                       break;
+               }
+               /* read oobdata */
+               this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
+               /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
+               if (!compareecc)
+                       goto readoob;
+               /* Pick the ECC bytes out of the oob data */
+               for (j = 0; j < oobsel->eccbytes; j++)
+                       ecc_code[j] = oob_data[oob_config[j]];
+               /* correct data, if neccecary */
+               for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
+                       ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+                       /* Get next chunk of ecc bytes */
+                       j += eccbytes;
+                       /* Check, if we have a fs supplied oob-buffer,
+                        * This is the legacy mode. Used by YAFFS1
+                        * Should go away some day
+                        */
+                       if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
+                               int *p = (int *)(&oob_data[mtd->oobsize]);
+                               p[i] = ecc_status;
+                       }
+                       if (ecc_status == -1) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
+                               ecc_failed++;
+                       }
+               }
+       readoob:
+               /* check, if we have a fs supplied oob-buffer */
+               if (oob_buf) {
+                       /* without autoplace. Legacy mode used by YAFFS1 */
+                       switch(oobsel->useecc) {
+                       case MTD_NANDECC_AUTOPLACE:
+                       case MTD_NANDECC_AUTOPL_USR:
+                               /* Walk through the autoplace chunks */
+                               for (i = 0, j = 0; j < mtd->oobavail; i++) {
+                                       int from = oobsel->oobfree[i][0];
+                                       int num = oobsel->oobfree[i][1];
+                                       memcpy(&oob_buf[oob], &oob_data[from], num);
+                                       j+= num;
+                               }
+                               oob += mtd->oobavail;
+                               break;
+                       case MTD_NANDECC_PLACE:
+                               /* YAFFS1 legacy mode */
+                               oob_data += this->eccsteps * sizeof (int);
+                       default:
+                               oob_data += mtd->oobsize;
+                       }
+               }
+       readdata:
+               /* Partial page read, transfer data into fs buffer */
+               if (!aligned) {
+                       for (j = col; j < end && read < len; j++)
+                               buf[read++] = data_poi[j];
+                       this->pagebuf = realpage;
+               } else
+                       read += mtd->oobblock;
+               /* Apply delay or wait for ready/busy pin
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+               */
+               if (!this->dev_ready)
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));
+               if (read == len)
+                       break;
+               /* For subsequent reads align to page boundary. */
+               col = 0;
+               /* Increment page address */
+               realpage++;
+               page = realpage & this->pagemask;
+               /* Check, if we cross a chip boundary */
+               if (!page) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+               /* Check, if the chip supports auto page increment
+                * or if we have hit a block boundary.
+               */
+               if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+                       sndcmd = 1;
+       }
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       /*
+        * Return success, if no ECC failures, else -EBADMSG
+        * fs driver will take care of that, because
+        * retlen == desired len and result == -EBADMSG
+        */
+       *retlen = read;
+       return ecc_failed ? -EBADMSG : 0;
+ }
+ /**
+  * nand_read_oob - [MTD Interface] NAND read out-of-band
+  * @mtd:      MTD device structure
+  * @from:     offset to read from
+  * @len:      number of bytes to read
+  * @retlen:   pointer to variable to store the number of read bytes
+  * @buf:      the databuffer to put data
+  *
+  * NAND read out-of-band data from the spare area
+  */
+ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+ {
+       int i, col, page, chipnr;
+       struct nand_chip *this = mtd->priv;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+       /* Shift to get page */
+       page = (int)(from >> this->page_shift);
+       chipnr = (int)(from >> this->chip_shift);
+       /* Mask to get column */
+       col = from & (mtd->oobsize - 1);
+       /* Initialize return length value */
+       *retlen = 0;
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+               *retlen = 0;
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd , FL_READING);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+       /* Send the read command */
+       this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
+       /*
+        * Read the data, if we read more than one page
+        * oob data, let the device transfer the data !
+        */
+       i = 0;
+       while (i < len) {
+               int thislen = mtd->oobsize - col;
+               thislen = min_t(int, thislen, len);
+               this->read_buf(mtd, &buf[i], thislen);
+               i += thislen;
+               /* Apply delay or wait for ready/busy pin
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+               */
+               if (!this->dev_ready)
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));
+               /* Read more ? */
+               if (i < len) {
+                       page++;
+                       col = 0;
+                       /* Check, if we cross a chip boundary */
+                       if (!(page & this->pagemask)) {
+                               chipnr++;
+                               this->select_chip(mtd, -1);
+                               this->select_chip(mtd, chipnr);
+                       }
+                       /* Check, if the chip supports auto page increment
+                        * or if we have hit a block boundary.
+                       */
+                       if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
+                               /* For subsequent page reads set offset to 0 */
+                               this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
+                       }
+               }
+       }
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       /* Return happy */
+       *retlen = len;
+       return 0;
+ }
+ /**
+  * nand_read_raw - [GENERIC] Read raw data including oob into buffer
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @from:     offset to read from
+  * @len:      number of bytes to read
+  * @ooblen:   number of oob data bytes to read
+  *
+  * Read raw data including oob into buffer
+  */
+ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
+ {
+       struct nand_chip *this = mtd->priv;
+       int page = (int) (from >> this->page_shift);
+       int chip = (int) (from >> this->chip_shift);
+       int sndcmd = 1;
+       int cnt = 0;
+       int pagesize = mtd->oobblock + mtd->oobsize;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd , FL_READING);
+       this->select_chip (mtd, chip);
+       /* Add requested oob length */
+       len += ooblen;
+       while (len) {
+               if (sndcmd)
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+               sndcmd = 0;
+               this->read_buf (mtd, &buf[cnt], pagesize);
+               len -= pagesize;
+               cnt += pagesize;
+               page++;
+               if (!this->dev_ready)
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));
+               /* Check, if the chip supports auto page increment */
+               if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+                       sndcmd = 1;
+       }
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       return 0;
+ }
+ /**
+  * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
+  * @mtd:      MTD device structure
+  * @fsbuf:    buffer given by fs driver
+  * @oobsel:   out of band selection structre
+  * @autoplace:        1 = place given buffer into the oob bytes
+  * @numpages: number of pages to prepare
+  *
+  * Return:
+  * 1. Filesystem buffer available and autoplacement is off,
+  *    return filesystem buffer
+  * 2. No filesystem buffer or autoplace is off, return internal
+  *    buffer
+  * 3. Filesystem buffer is given and autoplace selected
+  *    put data from fs buffer into internal buffer and
+  *    retrun internal buffer
+  *
+  * Note: The internal buffer is filled with 0xff. This must
+  * be done only once, when no autoplacement happens
+  * Autoplacement sets the buffer dirty flag, which
+  * forces the 0xff fill before using the buffer again.
+  *
+ */
+ static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
+               int autoplace, int numpages)
+ {
+       struct nand_chip *this = mtd->priv;
+       int i, len, ofs;
+       /* Zero copy fs supplied buffer */
+       if (fsbuf && !autoplace)
+               return fsbuf;
+       /* Check, if the buffer must be filled with ff again */
+       if (this->oobdirty) {
+               memset (this->oob_buf, 0xff,
+                       mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+               this->oobdirty = 0;
+       }
+       /* If we have no autoplacement or no fs buffer use the internal one */
+       if (!autoplace || !fsbuf)
+               return this->oob_buf;
+       /* Walk through the pages and place the data */
+       this->oobdirty = 1;
+       ofs = 0;
+       while (numpages--) {
+               for (i = 0, len = 0; len < mtd->oobavail; i++) {
+                       int to = ofs + oobsel->oobfree[i][0];
+                       int num = oobsel->oobfree[i][1];
+                       memcpy (&this->oob_buf[to], fsbuf, num);
+                       len += num;
+                       fsbuf += num;
+               }
+               ofs += mtd->oobavail;
+       }
+       return this->oob_buf;
+ }
+ #define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+ /**
+  * nand_write - [MTD Interface] compability function for nand_write_ecc
+  * @mtd:      MTD device structure
+  * @to:               offset to write to
+  * @len:      number of bytes to write
+  * @retlen:   pointer to variable to store the number of written bytes
+  * @buf:      the data to write
+  *
+  * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+  *
+ */
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+       return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+ }
+ /**
+  * nand_write_ecc - [MTD Interface] NAND write with ECC
+  * @mtd:      MTD device structure
+  * @to:               offset to write to
+  * @len:      number of bytes to write
+  * @retlen:   pointer to variable to store the number of written bytes
+  * @buf:      the data to write
+  * @eccbuf:   filesystem supplied oob data buffer
+  * @oobsel:   oob selection structure
+  *
+  * NAND write with ECC
+  */
+ static int nand_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 *oobsel)
+ {
+       int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
+       int autoplace = 0, numpages, totalpages;
+       struct nand_chip *this = mtd->priv;
+       u_char *oobbuf, *bufstart;
+       int     ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+       /* Initialize retlen, in case of early exit */
+       *retlen = 0;
+       /* Do not allow write past end of device */
+       if ((to + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
+               return -EINVAL;
+       }
+       /* reject writes, which are not page aligned */
+       if (NOTALIGNED (to) || NOTALIGNED(len)) {
+               printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd, FL_WRITING);
+       /* Calculate chipnr */
+       chipnr = (int)(to >> this->chip_shift);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+       /* if oobsel is NULL, use chip defaults */
+       if (oobsel == NULL)
+               oobsel = &mtd->oobinfo;
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+               oobsel = this->autooob;
+               autoplace = 1;
+       }
+       if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+               autoplace = 1;
+       /* Setup variables and oob buffer */
+       totalpages = len >> this->page_shift;
+       page = (int) (to >> this->page_shift);
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page <= this->pagebuf && this->pagebuf < (page + totalpages))
+               this->pagebuf = -1;
+       /* Set it relative to chip */
+       page &= this->pagemask;
+       startpage = page;
+       /* Calc number of pages we can write in one go */
+       numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
+       oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
+       bufstart = (u_char *)buf;
+       /* Loop until all data is written */
+       while (written < len) {
+               this->data_poi = (u_char*) &buf[written];
+               /* Write one page. If this is the last page to write
+                * or the last page in this block, then use the
+                * real pageprogram command, else select cached programming
+                * if supported by the chip.
+                */
+               ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
+               if (ret) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
+                       goto out;
+               }
+               /* Next oob page */
+               oob += mtd->oobsize;
+               /* Update written bytes count */
+               written += mtd->oobblock;
+               if (written == len)
+                       goto cmp;
+               /* Increment page address */
+               page++;
+               /* Have we hit a block boundary ? Then we have to verify and
+                * if verify is ok, we have to setup the oob buffer for
+                * the next pages.
+               */
+               if (!(page & (ppblock - 1))){
+                       int ofs;
+                       this->data_poi = bufstart;
+                       ret = nand_verify_pages (mtd, this, startpage,
+                               page - startpage,
+                               oobbuf, oobsel, chipnr, (eccbuf != NULL));
+                       if (ret) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+                               goto out;
+                       }
+                       *retlen = written;
+                       ofs = autoplace ? mtd->oobavail : mtd->oobsize;
+                       if (eccbuf)
+                               eccbuf += (page - startpage) * ofs;
+                       totalpages -= page - startpage;
+                       numpages = min (totalpages, ppblock);
+                       page &= this->pagemask;
+                       startpage = page;
+                       oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,
+                                       autoplace, numpages);
+                       /* Check, if we cross a chip boundary */
+                       if (!page) {
+                               chipnr++;
+                               this->select_chip(mtd, -1);
+                               this->select_chip(mtd, chipnr);
+                       }
+               }
+       }
+       /* Verify the remaining pages */
+ cmp:
+       this->data_poi = bufstart;
+       ret = nand_verify_pages (mtd, this, startpage, totalpages,
+               oobbuf, oobsel, chipnr, (eccbuf != NULL));
+       if (!ret)
+               *retlen = written;
+       else
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+ out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       return ret;
+ }
+ /**
+  * nand_write_oob - [MTD Interface] NAND write out-of-band
+  * @mtd:      MTD device structure
+  * @to:               offset to write to
+  * @len:      number of bytes to write
+  * @retlen:   pointer to variable to store the number of written bytes
+  * @buf:      the data to write
+  *
+  * NAND write out-of-band
+  */
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+       int column, page, status, ret = -EIO, chipnr;
+       struct nand_chip *this = mtd->priv;
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+       /* Shift to get page */
+       page = (int) (to >> this->page_shift);
+       chipnr = (int) (to >> this->chip_shift);
+       /* Mask to get column */
+       column = to & (mtd->oobsize - 1);
+       /* Initialize return length value */
+       *retlen = 0;
+       /* Do not allow write past end of page */
+       if ((column + len) > mtd->oobsize) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd, FL_WRITING);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+       /* Reset the chip. Some chips (like the Toshiba TC5832DC found
+          in one of my DiskOnChip 2000 test units) will clear the whole
+          data page too if we don't do this. I have no clue why, but
+          I seem to have 'fixed' it in the doc2000 driver in
+          August 1999.  dwmw2. */
+       this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page == this->pagebuf)
+               this->pagebuf = -1;
+       if (NAND_MUST_PAD(this)) {
+               /* Write out desired data */
+               this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
+               /* prepad 0xff for partial programming */
+               this->write_buf(mtd, ffchars, column);
+               /* write data */
+               this->write_buf(mtd, buf, len);
+               /* postpad 0xff for partial programming */
+               this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+       } else {
+               /* Write out desired data */
+               this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
+               /* write data */
+               this->write_buf(mtd, buf, len);
+       }
+       /* Send command to program the OOB data */
+       this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+       status = this->waitfunc (mtd, this, FL_WRITING);
+       /* See if device thinks it succeeded */
+       if (status & 0x01) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
+               ret = -EIO;
+               goto out;
+       }
+       /* Return happy */
+       *retlen = len;
+ #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+       /* Send command to read back the data */
+       this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
+       if (this->verify_buf(mtd, buf, len)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+               ret = -EIO;
+               goto out;
+       }
+ #endif
+       ret = 0;
+ out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       return ret;
+ }
+ /* XXX U-BOOT XXX */
+ #if 0
+ /**
+  * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
+  * @mtd:      MTD device structure
+  * @vecs:     the iovectors to write
+  * @count:    number of vectors
+  * @to:               offset to write to
+  * @retlen:   pointer to variable to store the number of written bytes
+  *
+  * NAND write with kvec. This just calls the ecc function
+  */
+ static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
+               loff_t to, size_t * retlen)
+ {
+       return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));
+ }
+ /**
+  * nand_writev_ecc - [MTD Interface] write with iovec with ecc
+  * @mtd:      MTD device structure
+  * @vecs:     the iovectors to write
+  * @count:    number of vectors
+  * @to:               offset to write to
+  * @retlen:   pointer to variable to store the number of written bytes
+  * @eccbuf:   filesystem supplied oob data buffer
+  * @oobsel:   oob selection structure
+  *
+  * NAND write with iovec with ecc
+  */
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
+               loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+       int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
+       int oob, numpages, autoplace = 0, startpage;
+       struct nand_chip *this = mtd->priv;
+       int     ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+       u_char *oobbuf, *bufstart;
+       /* Preset written len for early exit */
+       *retlen = 0;
+       /* Calculate total length of data */
+       total_len = 0;
+       for (i = 0; i < count; i++)
+               total_len += (int) vecs[i].iov_len;
+       DEBUG (MTD_DEBUG_LEVEL3,
+              "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
+       /* Do not allow write past end of page */
+       if ((to + total_len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
+               return -EINVAL;
+       }
+       /* reject writes, which are not page aligned */
+       if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
+               printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+               return -EINVAL;
+       }
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd, FL_WRITING);
+       /* Get the current chip-nr */
+       chipnr = (int) (to >> this->chip_shift);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+       /* if oobsel is NULL, use chip defaults */
+       if (oobsel == NULL)
+               oobsel = &mtd->oobinfo;
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+               oobsel = this->autooob;
+               autoplace = 1;
+       }
+       if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+               autoplace = 1;
+       /* Setup start page */
+       page = (int) (to >> this->page_shift);
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))
+               this->pagebuf = -1;
+       startpage = page & this->pagemask;
+       /* Loop until all kvec' data has been written */
+       len = 0;
+       while (count) {
+               /* If the given tuple is >= pagesize then
+                * write it out from the iov
+                */
+               if ((vecs->iov_len - len) >= mtd->oobblock) {
+                       /* Calc number of pages we can write
+                        * out of this iov in one go */
+                       numpages = (vecs->iov_len - len) >> this->page_shift;
+                       /* Do not cross block boundaries */
+                       numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
+                       oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+                       bufstart = (u_char *)vecs->iov_base;
+                       bufstart += len;
+                       this->data_poi = bufstart;
+                       oob = 0;
+                       for (i = 1; i <= numpages; i++) {
+                               /* Write one page. If this is the last page to write
+                                * then use the real pageprogram command, else select
+                                * cached programming if supported by the chip.
+                                */
+                               ret = nand_write_page (mtd, this, page & this->pagemask,
+                                       &oobbuf[oob], oobsel, i != numpages);
+                               if (ret)
+                                       goto out;
+                               this->data_poi += mtd->oobblock;
+                               len += mtd->oobblock;
+                               oob += mtd->oobsize;
+                               page++;
+                       }
+                       /* Check, if we have to switch to the next tuple */
+                       if (len >= (int) vecs->iov_len) {
+                               vecs++;
+                               len = 0;
+                               count--;
+                       }
+               } else {
+                       /* We must use the internal buffer, read data out of each
+                        * tuple until we have a full page to write
+                        */
+                       int cnt = 0;
+                       while (cnt < mtd->oobblock) {
+                               if (vecs->iov_base != NULL && vecs->iov_len)
+                                       this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+                               /* Check, if we have to switch to the next tuple */
+                               if (len >= (int) vecs->iov_len) {
+                                       vecs++;
+                                       len = 0;
+                                       count--;
+                               }
+                       }
+                       this->pagebuf = page;
+                       this->data_poi = this->data_buf;
+                       bufstart = this->data_poi;
+                       numpages = 1;
+                       oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+                       ret = nand_write_page (mtd, this, page & this->pagemask,
+                               oobbuf, oobsel, 0);
+                       if (ret)
+                               goto out;
+                       page++;
+               }
+               this->data_poi = bufstart;
+               ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
+               if (ret)
+                       goto out;
+               written += mtd->oobblock * numpages;
+               /* All done ? */
+               if (!count)
+                       break;
+               startpage = page & this->pagemask;
+               /* Check, if we cross a chip boundary */
+               if (!startpage) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+       }
+       ret = 0;
+ out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       *retlen = written;
+       return ret;
+ }
+ #endif
+ /**
+  * single_erease_cmd - [GENERIC] NAND standard block erase command function
+  * @mtd:      MTD device structure
+  * @page:     the page address of the block which will be erased
+  *
+  * Standard erase command for NAND chips
+  */
+ static void single_erase_cmd (struct mtd_info *mtd, int page)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Send commands to erase a block */
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+       this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+ }
+ /**
+  * multi_erease_cmd - [GENERIC] AND specific block erase command function
+  * @mtd:      MTD device structure
+  * @page:     the page address of the block which will be erased
+  *
+  * AND multi block erase command function
+  * Erase 4 consecutive blocks
+  */
+ static void multi_erase_cmd (struct mtd_info *mtd, int page)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Send commands to erase a block */
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+       this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+ }
+ /**
+  * nand_erase - [MTD Interface] erase block(s)
+  * @mtd:      MTD device structure
+  * @instr:    erase instruction
+  *
+  * Erase one ore more blocks
+  */
+ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+ {
+       return nand_erase_nand (mtd, instr, 0);
+ }
+ /**
+  * nand_erase_intern - [NAND Interface] erase block(s)
+  * @mtd:      MTD device structure
+  * @instr:    erase instruction
+  * @allowbbt: allow erasing the bbt area
+  *
+  * Erase one ore more blocks
+  */
+ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
+ {
+       int page, len, status, pages_per_block, ret, chipnr;
+       struct nand_chip *this = mtd->priv;
+       DEBUG (MTD_DEBUG_LEVEL3,
+              "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+       /* Start address must align on block boundary */
+       if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+               return -EINVAL;
+       }
+       /* Length must align on block boundary */
+       if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
+               return -EINVAL;
+       }
+       /* Do not allow erase past end of device */
+       if ((instr->len + instr->addr) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
+               return -EINVAL;
+       }
+       instr->fail_addr = 0xffffffff;
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd, FL_ERASING);
+       /* Shift to get first page */
+       page = (int) (instr->addr >> this->page_shift);
+       chipnr = (int) (instr->addr >> this->chip_shift);
+       /* Calculate pages in each block */
+       pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+       /* Check the WP bit */
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+               instr->state = MTD_ERASE_FAILED;
+               goto erase_exit;
+       }
+       /* Loop through the pages */
+       len = instr->len;
+       instr->state = MTD_ERASING;
+       while (len) {
+               /* Check if we have a bad block, we do not erase bad blocks ! */
+               if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
+                       printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+                       instr->state = MTD_ERASE_FAILED;
+                       goto erase_exit;
+               }
+               /* Invalidate the page cache, if we erase the block which contains
+                  the current cached page */
+               if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
+                       this->pagebuf = -1;
+               this->erase_cmd (mtd, page & this->pagemask);
+               status = this->waitfunc (mtd, this, FL_ERASING);
+               /* See if block erase succeeded */
+               if (status & 0x01) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
+                       instr->state = MTD_ERASE_FAILED;
+                       instr->fail_addr = (page << this->page_shift);
+                       goto erase_exit;
+               }
+               /* Increment page address and decrement length */
+               len -= (1 << this->phys_erase_shift);
+               page += pages_per_block;
+               /* Check, if we cross a chip boundary */
+               if (len && !(page & this->pagemask)) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+       }
+       instr->state = MTD_ERASE_DONE;
+ erase_exit:
+       ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+       /* Do call back function */
+       if (!ret)
+               mtd_erase_callback(instr);
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       /* Return more or less happy */
+       return ret;
+ }
+ /**
+  * nand_sync - [MTD Interface] sync
+  * @mtd:      MTD device structure
+  *
+  * Sync is actually a wait for chip ready function
+  */
+ static void nand_sync (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+       /* Grab the lock and see if the device is available */
+       nand_get_device (this, mtd, FL_SYNCING);
+       /* Release it and go back */
+       nand_release_device (mtd);
+ }
+ /**
+  * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+  * @mtd:      MTD device structure
+  * @ofs:      offset relative to mtd start
+  */
+ static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
+ {
+       /* Check for invalid offset */
+       if (ofs > mtd->size)
+               return -EINVAL;
+       return nand_block_checkbad (mtd, ofs, 1, 0);
+ }
+ /**
+  * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+  * @mtd:      MTD device structure
+  * @ofs:      offset relative to mtd start
+  */
+ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
+ {
+       struct nand_chip *this = mtd->priv;
+       int ret;
+       if ((ret = nand_block_isbad(mtd, ofs))) {
+               /* If it was bad already, return success and do nothing. */
+               if (ret > 0)
+                       return 0;
+               return ret;
+       }
+       return this->block_markbad(mtd, ofs);
+ }
+ /**
+  * nand_scan - [NAND Interface] Scan for the NAND device
+  * @mtd:      MTD device structure
+  * @maxchips: Number of chips to scan for
+  *
+  * This fills out all the not initialized function pointers
+  * with the defaults.
+  * The flash ID is read and the mtd/chip structures are
+  * filled with the appropriate values. Buffers are allocated if
+  * they are not provided by the board driver
+  *
+  */
+ int nand_scan (struct mtd_info *mtd, int maxchips)
+ {
+       int i, j, nand_maf_id, nand_dev_id, busw;
+       struct nand_chip *this = mtd->priv;
+       /* Get buswidth to select the correct functions*/
+       busw = this->options & NAND_BUSWIDTH_16;
+       /* check for proper chip_delay setup, set 20us if not */
+       if (!this->chip_delay)
+               this->chip_delay = 20;
+       /* check, if a user supplied command function given */
+       if (this->cmdfunc == NULL)
+               this->cmdfunc = nand_command;
+       /* check, if a user supplied wait function given */
+       if (this->waitfunc == NULL)
+               this->waitfunc = nand_wait;
+       if (!this->select_chip)
+               this->select_chip = nand_select_chip;
+       if (!this->write_byte)
+               this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+       if (!this->read_byte)
+               this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+       if (!this->write_word)
+               this->write_word = nand_write_word;
+       if (!this->read_word)
+               this->read_word = nand_read_word;
+       if (!this->block_bad)
+               this->block_bad = nand_block_bad;
+       if (!this->block_markbad)
+               this->block_markbad = nand_default_block_markbad;
+       if (!this->write_buf)
+               this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+       if (!this->read_buf)
+               this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+       if (!this->verify_buf)
+               this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+       if (!this->scan_bbt)
+               this->scan_bbt = nand_default_bbt;
+       /* Select the device */
+       this->select_chip(mtd, 0);
+       /* Send the command for reading device ID */
+       this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+       /* Read manufacturer and device IDs */
+       nand_maf_id = this->read_byte(mtd);
+       nand_dev_id = this->read_byte(mtd);
+       /* Print and store flash device information */
+       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+               if (nand_dev_id != nand_flash_ids[i].id)
+                       continue;
+               if (!mtd->name) mtd->name = nand_flash_ids[i].name;
+               this->chipsize = nand_flash_ids[i].chipsize << 20;
+               /* New devices have all the information in additional id bytes */
+               if (!nand_flash_ids[i].pagesize) {
+                       int extid;
+                       /* The 3rd id byte contains non relevant data ATM */
+                       extid = this->read_byte(mtd);
+                       /* The 4th id byte is the important one */
+                       extid = this->read_byte(mtd);
+                       /* Calc pagesize */
+                       mtd->oobblock = 1024 << (extid & 0x3);
+                       extid >>= 2;
+                       /* Calc oobsize */
+                       mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);
+                       extid >>= 2;
+                       /* Calc blocksize. Blocksize is multiples of 64KiB */
+                       mtd->erasesize = (64 * 1024)  << (extid & 0x03);
+                       extid >>= 2;
+                       /* Get buswidth information */
+                       busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+               } else {
+                       /* Old devices have this data hardcoded in the
+                        * device id table */
+                       mtd->erasesize = nand_flash_ids[i].erasesize;
+                       mtd->oobblock = nand_flash_ids[i].pagesize;
+                       mtd->oobsize = mtd->oobblock / 32;
+                       busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
+               }
+               /* Check, if buswidth is correct. Hardware drivers should set
+                * this correct ! */
+               if (busw != (this->options & NAND_BUSWIDTH_16)) {
+                       printk (KERN_INFO "NAND device: Manufacturer ID:"
+                               " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
+                               nand_manuf_ids[i].name , mtd->name);
+                       printk (KERN_WARNING
+                               "NAND bus width %d instead %d bit\n",
+                                       (this->options & NAND_BUSWIDTH_16) ? 16 : 8,
+                                       busw ? 16 : 8);
+                       this->select_chip(mtd, -1);
+                       return 1;
+               }
+               /* Calculate the address shift from the page size */
+               this->page_shift = ffs(mtd->oobblock) - 1;
+               this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
+               this->chip_shift = ffs(this->chipsize) - 1;
+               /* Set the bad block position */
+               this->badblockpos = mtd->oobblock > 512 ?
+                       NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+               /* Get chip options, preserve non chip based options */
+               this->options &= ~NAND_CHIPOPTIONS_MSK;
+               this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
+               /* Set this as a default. Board drivers can override it, if neccecary */
+               this->options |= NAND_NO_AUTOINCR;
+               /* Check if this is a not a samsung device. Do not clear the options
+                * for chips which are not having an extended id.
+                */
+               if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
+                       this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+               /* Check for AND chips with 4 page planes */
+               if (this->options & NAND_4PAGE_ARRAY)
+                       this->erase_cmd = multi_erase_cmd;
+               else
+                       this->erase_cmd = single_erase_cmd;
+               /* Do not replace user supplied command function ! */
+               if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
+                       this->cmdfunc = nand_command_lp;
+               /* Try to identify manufacturer */
+               for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
+                       if (nand_manuf_ids[j].id == nand_maf_id)
+                               break;
+               }
+               break;
+       }
+       if (!nand_flash_ids[i].name) {
+               printk (KERN_WARNING "No NAND device found!!!\n");
+               this->select_chip(mtd, -1);
+               return 1;
+       }
+       for (i=1; i < maxchips; i++) {
+               this->select_chip(mtd, i);
+               /* Send the command for reading device ID */
+               this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+               /* Read manufacturer and device IDs */
+               if (nand_maf_id != this->read_byte(mtd) ||
+                   nand_dev_id != this->read_byte(mtd))
+                       break;
+       }
+       if (i > 1)
+               printk(KERN_INFO "%d NAND chips detected\n", i);
+       /* Allocate buffers, if neccecary */
+       if (!this->oob_buf) {
+               size_t len;
+               len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
+               this->oob_buf = kmalloc (len, GFP_KERNEL);
+               if (!this->oob_buf) {
+                       printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+                       return -ENOMEM;
+               }
+               this->options |= NAND_OOBBUF_ALLOC;
+       }
+       if (!this->data_buf) {
+               size_t len;
+               len = mtd->oobblock + mtd->oobsize;
+               this->data_buf = kmalloc (len, GFP_KERNEL);
+               if (!this->data_buf) {
+                       if (this->options & NAND_OOBBUF_ALLOC)
+                               kfree (this->oob_buf);
+                       printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+                       return -ENOMEM;
+               }
+               this->options |= NAND_DATABUF_ALLOC;
+       }
+       /* Store the number of chips and calc total size for mtd */
+       this->numchips = i;
+       mtd->size = i * this->chipsize;
+       /* Convert chipsize to number of pages per chip -1. */
+       this->pagemask = (this->chipsize >> this->page_shift) - 1;
+       /* Preset the internal oob buffer */
+       memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+       /* If no default placement scheme is given, select an
+        * appropriate one */
+       if (!this->autooob) {
+               /* Select the appropriate default oob placement scheme for
+                * placement agnostic filesystems */
+               switch (mtd->oobsize) {
+               case 8:
+                       this->autooob = &nand_oob_8;
+                       break;
+               case 16:
+                       this->autooob = &nand_oob_16;
+                       break;
+               case 64:
+                       this->autooob = &nand_oob_64;
+                       break;
+               default:
+                       printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
+                               mtd->oobsize);
+ /*                    BUG(); */
+               }
+       }
+       /* The number of bytes available for the filesystem to place fs dependend
+        * oob data */
+       if (this->options & NAND_BUSWIDTH_16) {
+               mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
+               if (this->autooob->eccbytes & 0x01)
+                       mtd->oobavail--;
+       } else
+               mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
+       /*
+        * check ECC mode, default to software
+        * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+        * fallback to software ECC
+       */
+       this->eccsize = 256;    /* set default eccsize */
+       this->eccbytes = 3;
+       switch (this->eccmode) {
+       case NAND_ECC_HW12_2048:
+               if (mtd->oobblock < 2048) {
+                       printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
+                              mtd->oobblock);
+                       this->eccmode = NAND_ECC_SOFT;
+                       this->calculate_ecc = nand_calculate_ecc;
+                       this->correct_data = nand_correct_data;
+               } else
+                       this->eccsize = 2048;
+               break;
+       case NAND_ECC_HW3_512:
+       case NAND_ECC_HW6_512:
+       case NAND_ECC_HW8_512:
+               if (mtd->oobblock == 256) {
+                       printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+                       this->eccmode = NAND_ECC_SOFT;
+                       this->calculate_ecc = nand_calculate_ecc;
+                       this->correct_data = nand_correct_data;
+               } else
+                       this->eccsize = 512; /* set eccsize to 512 */
+               break;
+       case NAND_ECC_HW3_256:
+               break;
+       case NAND_ECC_NONE:
+               printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+               this->eccmode = NAND_ECC_NONE;
+               break;
+       case NAND_ECC_SOFT:
+               this->calculate_ecc = nand_calculate_ecc;
+               this->correct_data = nand_correct_data;
+               break;
+       default:
+               printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+ /*            BUG(); */
+       }
+       /* Check hardware ecc function availability and adjust number of ecc bytes per
+        * calculation step
+       */
+       switch (this->eccmode) {
+       case NAND_ECC_HW12_2048:
+               this->eccbytes += 4;
+       case NAND_ECC_HW8_512:
+               this->eccbytes += 2;
+       case NAND_ECC_HW6_512:
+               this->eccbytes += 3;
+       case NAND_ECC_HW3_512:
+       case NAND_ECC_HW3_256:
+               if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
+                       break;
+               printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
+ /*            BUG();  */
+       }
+       mtd->eccsize = this->eccsize;
+       /* Set the number of read / write steps for one page to ensure ECC generation */
+       switch (this->eccmode) {
+       case NAND_ECC_HW12_2048:
+               this->eccsteps = mtd->oobblock / 2048;
+               break;
+       case NAND_ECC_HW3_512:
+       case NAND_ECC_HW6_512:
+       case NAND_ECC_HW8_512:
+               this->eccsteps = mtd->oobblock / 512;
+               break;
+       case NAND_ECC_HW3_256:
+       case NAND_ECC_SOFT:
+               this->eccsteps = mtd->oobblock / 256;
+               break;
+       case NAND_ECC_NONE:
+               this->eccsteps = 1;
+               break;
+       }
+ /* XXX U-BOOT XXX */
+ #if 0
+       /* Initialize state, waitqueue and spinlock */
+       this->state = FL_READY;
+       init_waitqueue_head (&this->wq);
+       spin_lock_init (&this->chip_lock);
+ #endif
+       /* De-select the device */
+       this->select_chip(mtd, -1);
+       /* Invalidate the pagebuffer reference */
+       this->pagebuf = -1;
+       /* Fill in remaining MTD driver data */
+       mtd->type = MTD_NANDFLASH;
+       mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+       mtd->ecctype = MTD_ECC_SW;
+       mtd->erase = nand_erase;
+       mtd->point = NULL;
+       mtd->unpoint = NULL;
+       mtd->read = nand_read;
+       mtd->write = nand_write;
+       mtd->read_ecc = nand_read_ecc;
+       mtd->write_ecc = nand_write_ecc;
+       mtd->read_oob = nand_read_oob;
+       mtd->write_oob = nand_write_oob;
+ /* XXX U-BOOT XXX */
+ #if 0
+       mtd->readv = NULL;
+       mtd->writev = nand_writev;
+       mtd->writev_ecc = nand_writev_ecc;
+ #endif
+       mtd->sync = nand_sync;
+ /* XXX U-BOOT XXX */
+ #if 0
+       mtd->lock = NULL;
+       mtd->unlock = NULL;
+       mtd->suspend = NULL;
+       mtd->resume = NULL;
+ #endif
+       mtd->block_isbad = nand_block_isbad;
+       mtd->block_markbad = nand_block_markbad;
+       /* and make the autooob the default one */
+       memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
+ /* XXX U-BOOT XXX */
+ #if 0
+       mtd->owner = THIS_MODULE;
+ #endif
+       /* Build bad block table */
+       return this->scan_bbt (mtd);
+ }
+ /**
+  * nand_release - [NAND Interface] Free resources held by the NAND device
+  * @mtd:      MTD device structure
+  */
+ void nand_release (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+ #ifdef CONFIG_MTD_PARTITIONS
+       /* Deregister partitions */
+       del_mtd_partitions (mtd);
+ #endif
+       /* Deregister the device */
+ /* XXX U-BOOT XXX */
+ #if 0
+       del_mtd_device (mtd);
+ #endif
+       /* Free bad block table memory, if allocated */
+       if (this->bbt)
+               kfree (this->bbt);
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_OOBBUF_ALLOC)
+               kfree (this->oob_buf);
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_DATABUF_ALLOC)
+               kfree (this->data_buf);
+ }
+ #endif
++#endif /* CONFIG_NEW_NAND_CODE */
++
index 0000000,dfa88a3..f481308
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1056 +1,1055 @@@
 -      printk (KERN_INFO "Scanning device for bad blocks\n");
 -
+ /*
+  *  drivers/mtd/nand_bbt.c
+  *
+  *  Overview:
+  *   Bad block table support for the NAND driver
+  *
+  *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+  *
+  * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 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
+  * published by the Free Software Foundation.
+  *
+  * Description:
+  *
+  * When nand_scan_bbt is called, then it tries to find the bad block table
+  * depending on the options in the bbt descriptor(s). If a bbt is found
+  * then the contents are read and the memory based bbt is created. If a
+  * mirrored bbt is selected then the mirror is searched too and the
+  * versions are compared. If the mirror has a greater version number
+  * than the mirror bbt is used to build the memory based bbt.
+  * If the tables are not versioned, then we "or" the bad block information.
+  * If one of the bbt's is out of date or does not exist it is (re)created.
+  * If no bbt exists at all then the device is scanned for factory marked
+  * good / bad blocks and the bad block tables are created.
+  *
+  * For manufacturer created bbts like the one found on M-SYS DOC devices
+  * the bbt is searched and read but never created
+  *
+  * The autogenerated bad block table is located in the last good blocks
+  * of the device. The table is mirrored, so it can be updated eventually.
+  * The table is marked in the oob area with an ident pattern and a version
+  * number which indicates which of both tables is more up to date.
+  *
+  * The table uses 2 bits per block
+  * 11b:       block is good
+  * 00b:       block is factory marked bad
+  * 01b, 10b:  block is marked bad due to wear
+  *
+  * The memory bad block table uses the following scheme:
+  * 00b:               block is good
+  * 01b:               block is marked bad due to wear
+  * 10b:               block is reserved (to protect the bbt area)
+  * 11b:               block is factory marked bad
+  *
+  * Multichip devices like DOC store the bad block info per floor.
+  *
+  * Following assumptions are made:
+  * - bbts start at a page boundary, if autolocated on a block boundary
+  * - the space neccecary for a bbt in FLASH does not exceed a block boundary
+  *
+  */
+ #include <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #if (CONFIG_COMMANDS & CFG_CMD_NAND)
+ #include <malloc.h>
+ #include <linux/mtd/compat.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <asm/errno.h>
+ /**
+  * check_pattern - [GENERIC] check if a pattern is in the buffer
+  * @buf:      the buffer to search
+  * @len:      the length of buffer to search
+  * @paglen:   the pagelength
+  * @td:               search pattern descriptor
+  *
+  * Check for a pattern at the given place. Used to search bad block
+  * tables and good / bad block identifiers.
+  * If the SCAN_EMPTY option is set then check, if all bytes except the
+  * pattern area contain 0xff
+  *
+ */
+ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+ {
+       int i, end;
+       uint8_t *p = buf;
+       end = paglen + td->offs;
+       if (td->options & NAND_BBT_SCANEMPTY) {
+               for (i = 0; i < end; i++) {
+                       if (p[i] != 0xff)
+                               return -1;
+               }
+       }
+       p += end;
+       /* Compare the pattern */
+       for (i = 0; i < td->len; i++) {
+               if (p[i] != td->pattern[i])
+                       return -1;
+       }
+       p += td->len;
+       end += td->len;
+       if (td->options & NAND_BBT_SCANEMPTY) {
+               for (i = end; i < len; i++) {
+                       if (*p++ != 0xff)
+                               return -1;
+               }
+       }
+       return 0;
+ }
+ /**
+  * read_bbt - [GENERIC] Read the bad block table starting from page
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @page:     the starting page
+  * @num:      the number of bbt descriptors to read
+  * @bits:     number of bits per block
+  * @offs:     offset in the memory table
+  * @reserved_block_code:      Pattern to identify reserved blocks
+  *
+  * Read the bad block table starting from page.
+  *
+  */
+ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
+       int bits, int offs, int reserved_block_code)
+ {
+       int res, i, j, act = 0;
+       struct nand_chip *this = mtd->priv;
+       size_t retlen, len, totlen;
+       loff_t from;
+       uint8_t msk = (uint8_t) ((1 << bits) - 1);
+       totlen = (num * bits) >> 3;
+       from = ((loff_t)page) << this->page_shift;
+       while (totlen) {
+               len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
+               res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
+               if (res < 0) {
+                       if (retlen != len) {
+                               printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
+                               return res;
+                       }
+                       printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+               }
+               /* Analyse data */
+               for (i = 0; i < len; i++) {
+                       uint8_t dat = buf[i];
+                       for (j = 0; j < 8; j += bits, act += 2) {
+                               uint8_t tmp = (dat >> j) & msk;
+                               if (tmp == msk)
+                                       continue;
+                               if (reserved_block_code &&
+                                   (tmp == reserved_block_code)) {
+                                       printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
+                                               ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+                                       this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+                                       continue;
+                               }
+                               /* Leave it for now, if its matured we can move this
+                                * message to MTD_DEBUG_LEVEL0 */
+                               printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
+                                       ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+                               /* Factory marked bad or worn out ? */
+                               if (tmp == 0)
+                                       this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+                               else
+                                       this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+                       }
+               }
+               totlen -= len;
+               from += len;
+       }
+       return 0;
+ }
+ /**
+  * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @td:               descriptor for the bad block table
+  * @chip:     read the table for a specific chip, -1 read all chips.
+  *            Applies only if NAND_BBT_PERCHIP option is set
+  *
+  * Read the bad block table for all chips starting at a given page
+  * We assume that the bbt bits are in consecutive order.
+ */
+ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+ {
+       struct nand_chip *this = mtd->priv;
+       int res = 0, i;
+       int bits;
+       bits = td->options & NAND_BBT_NRBITS_MSK;
+       if (td->options & NAND_BBT_PERCHIP) {
+               int offs = 0;
+               for (i = 0; i < this->numchips; i++) {
+                       if (chip == -1 || chip == i)
+                               res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+                       if (res)
+                               return res;
+                       offs += this->chipsize >> (this->bbt_erase_shift + 2);
+               }
+       } else {
+               res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+               if (res)
+                       return res;
+       }
+       return 0;
+ }
+ /**
+  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @td:               descriptor for the bad block table
+  * @md:               descriptor for the bad block table mirror
+  *
+  * Read the bad block table(s) for all chips starting at a given page
+  * We assume that the bbt bits are in consecutive order.
+  *
+ */
+ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
+       struct nand_bbt_descr *md)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Read the primary version, if available */
+       if (td->options & NAND_BBT_VERSION) {
+               nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
+               td->version[0] = buf[mtd->oobblock + td->veroffs];
+               printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+       }
+       /* Read the mirror version, if available */
+       if (md && (md->options & NAND_BBT_VERSION)) {
+               nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
+               md->version[0] = buf[mtd->oobblock + md->veroffs];
+               printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+       }
+       return 1;
+ }
+ /**
+  * create_bbt - [GENERIC] Create a bad block table by scanning the device
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @bd:               descriptor for the good/bad block search pattern
+  * @chip:     create the table for a specific chip, -1 read all chips.
+  *            Applies only if NAND_BBT_PERCHIP option is set
+  *
+  * Create a bad block table by scanning the device
+  * for the given good/bad block identify pattern
+  */
+ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+ {
+       struct nand_chip *this = mtd->priv;
+       int i, j, numblocks, len, scanlen;
+       int startblock;
+       loff_t from;
+       size_t readlen, ooblen;
 -                              printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
 -                                      i >> 1, (unsigned int) from);
+       if (bd->options & NAND_BBT_SCANALLPAGES)
+               len = 1 << (this->bbt_erase_shift - this->page_shift);
+       else {
+               if (bd->options & NAND_BBT_SCAN2NDPAGE)
+                       len = 2;
+               else
+                       len = 1;
+       }
+       scanlen = mtd->oobblock + mtd->oobsize;
+       readlen = len * mtd->oobblock;
+       ooblen = len * mtd->oobsize;
+       if (chip == -1) {
+               /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
+                * makes shifting and masking less painful */
+               numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+               startblock = 0;
+               from = 0;
+       } else {
+               if (chip >= this->numchips) {
+                       printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+                               chip + 1, this->numchips);
+                       return;
+               }
+               numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+               startblock = chip * numblocks;
+               numblocks += startblock;
+               from = startblock << (this->bbt_erase_shift - 1);
+       }
+       for (i = startblock; i < numblocks;) {
+               nand_read_raw (mtd, buf, from, readlen, ooblen);
+               for (j = 0; j < len; j++) {
+                       if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+                               this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+                               break;
+                       }
+               }
+               i += 2;
+               from += (1 << this->bbt_erase_shift);
+       }
+ }
+ /**
+  * search_bbt - [GENERIC] scan the device for a specific bad block table
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @td:               descriptor for the bad block table
+  *
+  * Read the bad block table by searching for a given ident pattern.
+  * Search is preformed either from the beginning up or from the end of
+  * the device downwards. The search starts always at the start of a
+  * block.
+  * If the option NAND_BBT_PERCHIP is given, each chip is searched
+  * for a bbt, which contains the bad block information of this chip.
+  * This is neccecary to provide support for certain DOC devices.
+  *
+  * The bbt ident pattern resides in the oob area of the first page
+  * in a block.
+  */
+ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+ {
+       struct nand_chip *this = mtd->priv;
+       int i, chips;
+       int bits, startblock, block, dir;
+       int scanlen = mtd->oobblock + mtd->oobsize;
+       int bbtblocks;
+       /* Search direction top -> down ? */
+       if (td->options & NAND_BBT_LASTBLOCK) {
+               startblock = (mtd->size >> this->bbt_erase_shift) -1;
+               dir = -1;
+       } else {
+               startblock = 0;
+               dir = 1;
+       }
+       /* Do we have a bbt per chip ? */
+       if (td->options & NAND_BBT_PERCHIP) {
+               chips = this->numchips;
+               bbtblocks = this->chipsize >> this->bbt_erase_shift;
+               startblock &= bbtblocks - 1;
+       } else {
+               chips = 1;
+               bbtblocks = mtd->size >> this->bbt_erase_shift;
+       }
+       /* Number of bits for each erase block in the bbt */
+       bits = td->options & NAND_BBT_NRBITS_MSK;
+       for (i = 0; i < chips; i++) {
+               /* Reset version information */
+               td->version[i] = 0;
+               td->pages[i] = -1;
+               /* Scan the maximum number of blocks */
+               for (block = 0; block < td->maxblocks; block++) {
+                       int actblock = startblock + dir * block;
+                       /* Read first page */
+                       nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
+                       if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
+                               td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+                               if (td->options & NAND_BBT_VERSION) {
+                                       td->version[i] = buf[mtd->oobblock + td->veroffs];
+                               }
+                               break;
+                       }
+               }
+               startblock += this->chipsize >> this->bbt_erase_shift;
+       }
+       /* Check, if we found a bbt for each requested chip */
+       for (i = 0; i < chips; i++) {
+               if (td->pages[i] == -1)
+                       printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
+               else
+                       printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
+       }
+       return 0;
+ }
+ /**
+  * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @td:               descriptor for the bad block table
+  * @md:               descriptor for the bad block table mirror
+  *
+  * Search and read the bad block table(s)
+ */
+ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
+       struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ {
+       /* Search the primary table */
+       search_bbt (mtd, buf, td);
+       /* Search the mirror table */
+       if (md)
+               search_bbt (mtd, buf, md);
+       /* Force result check */
+       return 1;
+ }
+ /**
+  * write_bbt - [GENERIC] (Re)write the bad block table
+  *
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @td:               descriptor for the bad block table
+  * @md:               descriptor for the bad block table mirror
+  * @chipsel:  selector for a specific chip, -1 for all
+  *
+  * (Re)write the bad block table
+  *
+ */
+ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
+       struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
+ {
+       struct nand_chip *this = mtd->priv;
+       struct nand_oobinfo oobinfo;
+       struct erase_info einfo;
+       int i, j, res, chip = 0;
+       int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+       int nrchips, bbtoffs, pageoffs;
+       uint8_t msk[4];
+       uint8_t rcode = td->reserved_block_code;
+       size_t retlen, len = 0;
+       loff_t to;
+       if (!rcode)
+               rcode = 0xff;
+       /* Write bad block table per chip rather than per device ? */
+       if (td->options & NAND_BBT_PERCHIP) {
+               numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
+               /* Full device write or specific chip ? */
+               if (chipsel == -1) {
+                       nrchips = this->numchips;
+               } else {
+                       nrchips = chipsel + 1;
+                       chip = chipsel;
+               }
+       } else {
+               numblocks = (int) (mtd->size >> this->bbt_erase_shift);
+               nrchips = 1;
+       }
+       /* Loop through the chips */
+       for (; chip < nrchips; chip++) {
+               /* There was already a version of the table, reuse the page
+                * This applies for absolute placement too, as we have the
+                * page nr. in td->pages.
+                */
+               if (td->pages[chip] != -1) {
+                       page = td->pages[chip];
+                       goto write;
+               }
+               /* Automatic placement of the bad block table */
+               /* Search direction top -> down ? */
+               if (td->options & NAND_BBT_LASTBLOCK) {
+                       startblock = numblocks * (chip + 1) - 1;
+                       dir = -1;
+               } else {
+                       startblock = chip * numblocks;
+                       dir = 1;
+               }
+               for (i = 0; i < td->maxblocks; i++) {
+                       int block = startblock + dir * i;
+                       /* Check, if the block is bad */
+                       switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
+                       case 0x01:
+                       case 0x03:
+                               continue;
+                       }
+                       page = block << (this->bbt_erase_shift - this->page_shift);
+                       /* Check, if the block is used by the mirror table */
+                       if (!md || md->pages[chip] != page)
+                               goto write;
+               }
+               printk (KERN_ERR "No space left to write bad block table\n");
+               return -ENOSPC;
+ write:
+               /* Set up shift count and masks for the flash table */
+               bits = td->options & NAND_BBT_NRBITS_MSK;
+               switch (bits) {
+               case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
+               case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
+               case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
+               case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
+               default: return -EINVAL;
+               }
+               bbtoffs = chip * (numblocks >> 2);
+               to = ((loff_t) page) << this->page_shift;
+               memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
+               oobinfo.useecc = MTD_NANDECC_PLACEONLY;
+               /* Must we save the block contents ? */
+               if (td->options & NAND_BBT_SAVECONTENT) {
+                       /* Make it block aligned */
+                       to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+                       len = 1 << this->bbt_erase_shift;
+                       res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+                       if (res < 0) {
+                               if (retlen != len) {
+                                       printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
+                                       return res;
+                               }
+                               printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
+                       }
+                       /* Calc the byte offset in the buffer */
+                       pageoffs = page - (int)(to >> this->page_shift);
+                       offs = pageoffs << this->page_shift;
+                       /* Preset the bbt area with 0xff */
+                       memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
+                       /* Preset the bbt's oob area with 0xff */
+                       memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
+                               ((len >> this->page_shift) - pageoffs) * mtd->oobsize);
+                       if (td->options & NAND_BBT_VERSION) {
+                               buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
+                       }
+               } else {
+                       /* Calc length */
+                       len = (size_t) (numblocks >> sft);
+                       /* Make it page aligned ! */
+                       len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
+                       /* Preset the buffer with 0xff */
+                       memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
+                       offs = 0;
+                       /* Pattern is located in oob area of first page */
+                       memcpy (&buf[len + td->offs], td->pattern, td->len);
+                       if (td->options & NAND_BBT_VERSION) {
+                               buf[len + td->veroffs] = td->version[chip];
+                       }
+               }
+               /* walk through the memory table */
+               for (i = 0; i < numblocks; ) {
+                       uint8_t dat;
+                       dat = this->bbt[bbtoffs + (i >> 2)];
+                       for (j = 0; j < 4; j++ , i++) {
+                               int sftcnt = (i << (3 - sft)) & sftmsk;
+                               /* Do not store the reserved bbt blocks ! */
+                               buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
+                               dat >>= 2;
+                       }
+               }
+               memset (&einfo, 0, sizeof (einfo));
+               einfo.mtd = mtd;
+               einfo.addr = (unsigned long) to;
+               einfo.len = 1 << this->bbt_erase_shift;
+               res = nand_erase_nand (mtd, &einfo, 1);
+               if (res < 0) {
+                       printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
+                       return res;
+               }
+               res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+               if (res < 0) {
+                       printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
+                       return res;
+               }
+               printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n",
+                       (unsigned int) to, td->version[chip]);
+               /* Mark it as used */
+               td->pages[chip] = page;
+       }
+       return 0;
+ }
+ /**
+  * nand_memory_bbt - [GENERIC] create a memory based bad block table
+  * @mtd:      MTD device structure
+  * @bd:               descriptor for the good/bad block search pattern
+  *
+  * The function creates a memory based bbt by scanning the device
+  * for manufacturer / software marked good / bad blocks
+ */
+ static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Ensure that we only scan for the pattern and nothing else */
+       bd->options = 0;
+       create_bbt (mtd, this->data_buf, bd, -1);
+       return 0;
+ }
+ /**
+  * check_create - [GENERIC] create and write bbt(s) if neccecary
+  * @mtd:      MTD device structure
+  * @buf:      temporary buffer
+  * @bd:               descriptor for the good/bad block search pattern
+  *
+  * The function checks the results of the previous call to read_bbt
+  * and creates / updates the bbt(s) if neccecary
+  * Creation is neccecary if no bbt was found for the chip/device
+  * Update is neccecary if one of the tables is missing or the
+  * version nr. of one table is less than the other
+ */
+ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+ {
+       int i, chips, writeops, chipsel, res;
+       struct nand_chip *this = mtd->priv;
+       struct nand_bbt_descr *td = this->bbt_td;
+       struct nand_bbt_descr *md = this->bbt_md;
+       struct nand_bbt_descr *rd, *rd2;
+       /* Do we have a bbt per chip ? */
+       if (td->options & NAND_BBT_PERCHIP)
+               chips = this->numchips;
+       else
+               chips = 1;
+       for (i = 0; i < chips; i++) {
+               writeops = 0;
+               rd = NULL;
+               rd2 = NULL;
+               /* Per chip or per device ? */
+               chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+               /* Mirrored table avilable ? */
+               if (md) {
+                       if (td->pages[i] == -1 && md->pages[i] == -1) {
+                               writeops = 0x03;
+                               goto create;
+                       }
+                       if (td->pages[i] == -1) {
+                               rd = md;
+                               td->version[i] = md->version[i];
+                               writeops = 1;
+                               goto writecheck;
+                       }
+                       if (md->pages[i] == -1) {
+                               rd = td;
+                               md->version[i] = td->version[i];
+                               writeops = 2;
+                               goto writecheck;
+                       }
+                       if (td->version[i] == md->version[i]) {
+                               rd = td;
+                               if (!(td->options & NAND_BBT_VERSION))
+                                       rd2 = md;
+                               goto writecheck;
+                       }
+                       if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+                               rd = td;
+                               md->version[i] = td->version[i];
+                               writeops = 2;
+                       } else {
+                               rd = md;
+                               td->version[i] = md->version[i];
+                               writeops = 1;
+                       }
+                       goto writecheck;
+               } else {
+                       if (td->pages[i] == -1) {
+                               writeops = 0x01;
+                               goto create;
+                       }
+                       rd = td;
+                       goto writecheck;
+               }
+ create:
+               /* Create the bad block table by scanning the device ? */
+               if (!(td->options & NAND_BBT_CREATE))
+                       continue;
+               /* Create the table in memory by scanning the chip(s) */
+               create_bbt (mtd, buf, bd, chipsel);
+               td->version[i] = 1;
+               if (md)
+                       md->version[i] = 1;
+ writecheck:
+               /* read back first ? */
+               if (rd)
+                       read_abs_bbt (mtd, buf, rd, chipsel);
+               /* If they weren't versioned, read both. */
+               if (rd2)
+                       read_abs_bbt (mtd, buf, rd2, chipsel);
+               /* Write the bad block table to the device ? */
+               if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+                       res = write_bbt (mtd, buf, td, md, chipsel);
+                       if (res < 0)
+                               return res;
+               }
+               /* Write the mirror bad block table to the device ? */
+               if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+                       res = write_bbt (mtd, buf, md, td, chipsel);
+                       if (res < 0)
+                               return res;
+               }
+       }
+       return 0;
+ }
+ /**
+  * mark_bbt_regions - [GENERIC] mark the bad block table regions
+  * @mtd:      MTD device structure
+  * @td:               bad block table descriptor
+  *
+  * The bad block table regions are marked as "bad" to prevent
+  * accidental erasures / writes. The regions are identified by
+  * the mark 0x02.
+ */
+ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
+ {
+       struct nand_chip *this = mtd->priv;
+       int i, j, chips, block, nrblocks, update;
+       uint8_t oldval, newval;
+       /* Do we have a bbt per chip ? */
+       if (td->options & NAND_BBT_PERCHIP) {
+               chips = this->numchips;
+               nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+       } else {
+               chips = 1;
+               nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+       }
+       for (i = 0; i < chips; i++) {
+               if ((td->options & NAND_BBT_ABSPAGE) ||
+                   !(td->options & NAND_BBT_WRITE)) {
+                       if (td->pages[i] == -1) continue;
+                       block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+                       block <<= 1;
+                       oldval = this->bbt[(block >> 3)];
+                       newval = oldval | (0x2 << (block & 0x06));
+                       this->bbt[(block >> 3)] = newval;
+                       if ((oldval != newval) && td->reserved_block_code)
+                               nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
+                       continue;
+               }
+               update = 0;
+               if (td->options & NAND_BBT_LASTBLOCK)
+                       block = ((i + 1) * nrblocks) - td->maxblocks;
+               else
+                       block = i * nrblocks;
+               block <<= 1;
+               for (j = 0; j < td->maxblocks; j++) {
+                       oldval = this->bbt[(block >> 3)];
+                       newval = oldval | (0x2 << (block & 0x06));
+                       this->bbt[(block >> 3)] = newval;
+                       if (oldval != newval) update = 1;
+                       block += 2;
+               }
+               /* If we want reserved blocks to be recorded to flash, and some
+                  new ones have been marked, then we need to update the stored
+                  bbts.  This should only happen once. */
+               if (update && td->reserved_block_code)
+                       nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
+       }
+ }
+ /**
+  * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+  * @mtd:      MTD device structure
+  * @bd:               descriptor for the good/bad block search pattern
+  *
+  * The function checks, if a bad block table(s) is/are already
+  * available. If not it scans the device for manufacturer
+  * marked good / bad blocks and writes the bad block table(s) to
+  * the selected place.
+  *
+  * The bad block table memory is allocated here. It must be freed
+  * by calling the nand_free_bbt function.
+  *
+ */
+ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+ {
+       struct nand_chip *this = mtd->priv;
+       int len, res = 0;
+       uint8_t *buf;
+       struct nand_bbt_descr *td = this->bbt_td;
+       struct nand_bbt_descr *md = this->bbt_md;
+       len = mtd->size >> (this->bbt_erase_shift + 2);
+       /* Allocate memory (2bit per block) */
+       this->bbt = kmalloc (len, GFP_KERNEL);
+       if (!this->bbt) {
+               printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
+               return -ENOMEM;
+       }
+       /* Clear the memory bad block table */
+       memset (this->bbt, 0x00, len);
+       /* If no primary table decriptor is given, scan the device
+        * to build a memory based bad block table
+        */
+       if (!td)
+               return nand_memory_bbt(mtd, bd);
+       /* Allocate a temporary buffer for one eraseblock incl. oob */
+       len = (1 << this->bbt_erase_shift);
+       len += (len >> this->page_shift) * mtd->oobsize;
+       buf = kmalloc (len, GFP_KERNEL);
+       if (!buf) {
+               printk (KERN_ERR "nand_bbt: Out of memory\n");
+               kfree (this->bbt);
+               this->bbt = NULL;
+               return -ENOMEM;
+       }
+       /* Is the bbt at a given page ? */
+       if (td->options & NAND_BBT_ABSPAGE) {
+               res = read_abs_bbts (mtd, buf, td, md);
+       } else {
+               /* Search the bad block table using a pattern in oob */
+               res = search_read_bbts (mtd, buf, td, md);
+       }
+       if (res)
+               res = check_create (mtd, buf, bd);
+       /* Prevent the bbt regions from erasing / writing */
+       mark_bbt_region (mtd, td);
+       if (md)
+               mark_bbt_region (mtd, md);
+       kfree (buf);
+       return res;
+ }
+ /**
+  * nand_update_bbt - [NAND Interface] update bad block table(s)
+  * @mtd:      MTD device structure
+  * @offs:     the offset of the newly marked block
+  *
+  * The function updates the bad block table(s)
+ */
+ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
+ {
+       struct nand_chip *this = mtd->priv;
+       int len, res = 0, writeops = 0;
+       int chip, chipsel;
+       uint8_t *buf;
+       struct nand_bbt_descr *td = this->bbt_td;
+       struct nand_bbt_descr *md = this->bbt_md;
+       if (!this->bbt || !td)
+               return -EINVAL;
+       len = mtd->size >> (this->bbt_erase_shift + 2);
+       /* Allocate a temporary buffer for one eraseblock incl. oob */
+       len = (1 << this->bbt_erase_shift);
+       len += (len >> this->page_shift) * mtd->oobsize;
+       buf = kmalloc (len, GFP_KERNEL);
+       if (!buf) {
+               printk (KERN_ERR "nand_update_bbt: Out of memory\n");
+               return -ENOMEM;
+       }
+       writeops = md != NULL ? 0x03 : 0x01;
+       /* Do we have a bbt per chip ? */
+       if (td->options & NAND_BBT_PERCHIP) {
+               chip = (int) (offs >> this->chip_shift);
+               chipsel = chip;
+       } else {
+               chip = 0;
+               chipsel = -1;
+       }
+       td->version[chip]++;
+       if (md)
+               md->version[chip]++;
+       /* Write the bad block table to the device ? */
+       if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+               res = write_bbt (mtd, buf, td, md, chipsel);
+               if (res < 0)
+                       goto out;
+       }
+       /* Write the mirror bad block table to the device ? */
+       if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+               res = write_bbt (mtd, buf, md, td, chipsel);
+       }
+ out:
+       kfree (buf);
+       return res;
+ }
+ /* Define some generic bad / good block scan pattern which are used
+  * while scanning a device for factory marked good / bad blocks
+  *
+  * The memory based patterns just
+  */
+ static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+ static struct nand_bbt_descr smallpage_memorybased = {
+       .options = 0,
+       .offs = 5,
+       .len = 1,
+       .pattern = scan_ff_pattern
+ };
+ static struct nand_bbt_descr largepage_memorybased = {
+       .options = 0,
+       .offs = 0,
+       .len = 2,
+       .pattern = scan_ff_pattern
+ };
+ static struct nand_bbt_descr smallpage_flashbased = {
+       .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+       .offs = 5,
+       .len = 1,
+       .pattern = scan_ff_pattern
+ };
+ static struct nand_bbt_descr largepage_flashbased = {
+       .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+       .offs = 0,
+       .len = 2,
+       .pattern = scan_ff_pattern
+ };
+ static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
+ static struct nand_bbt_descr agand_flashbased = {
+       .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+       .offs = 0x20,
+       .len = 6,
+       .pattern = scan_agand_pattern
+ };
+ /* Generic flash bbt decriptors
+ */
+ static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+ static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+ static struct nand_bbt_descr bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 8,
+       .len = 4,
+       .veroffs = 12,
+       .maxblocks = 4,
+       .pattern = bbt_pattern
+ };
+ static struct nand_bbt_descr bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 8,
+       .len = 4,
+       .veroffs = 12,
+       .maxblocks = 4,
+       .pattern = mirror_pattern
+ };
+ /**
+  * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
+  * @mtd:      MTD device structure
+  *
+  * This function selects the default bad block table
+  * support for the device and calls the nand_scan_bbt function
+  *
+ */
+ int nand_default_bbt (struct mtd_info *mtd)
+ {
+       struct nand_chip *this = mtd->priv;
+       /* Default for AG-AND. We must use a flash based
+        * bad block table as the devices have factory marked
+        * _good_ blocks. Erasing those blocks leads to loss
+        * of the good / bad information, so we _must_ store
+        * this information in a good / bad table during
+        * startup
+       */
+       if (this->options & NAND_IS_AND) {
+               /* Use the default pattern descriptors */
+               if (!this->bbt_td) {
+                       this->bbt_td = &bbt_main_descr;
+                       this->bbt_md = &bbt_mirror_descr;
+               }
+               this->options |= NAND_USE_FLASH_BBT;
+               return nand_scan_bbt (mtd, &agand_flashbased);
+       }
+       /* Is a flash based bad block table requested ? */
+       if (this->options & NAND_USE_FLASH_BBT) {
+               /* Use the default pattern descriptors */
+               if (!this->bbt_td) {
+                       this->bbt_td = &bbt_main_descr;
+                       this->bbt_md = &bbt_mirror_descr;
+               }
+               if (!this->badblock_pattern) {
+                       this->badblock_pattern = (mtd->oobblock > 512) ?
+                               &largepage_flashbased : &smallpage_flashbased;
+               }
+       } else {
+               this->bbt_td = NULL;
+               this->bbt_md = NULL;
+               if (!this->badblock_pattern) {
+                       this->badblock_pattern = (mtd->oobblock > 512) ?
+                               &largepage_memorybased : &smallpage_memorybased;
+               }
+       }
+       return nand_scan_bbt (mtd, this->badblock_pattern);
+ }
+ /**
+  * nand_isbad_bbt - [NAND Interface] Check if a block is bad
+  * @mtd:      MTD device structure
+  * @offs:     offset in the device
+  * @allowbbt: allow access to bad block table region
+  *
+  */
+ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
+ {
+       struct nand_chip *this = mtd->priv;
+       int block;
+       uint8_t res;
+       /* Get block number * 2 */
+       block = (int) (offs >> (this->bbt_erase_shift - 1));
+       res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+       DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+               (unsigned int)offs, res, block >> 1);
+       switch ((int)res) {
+       case 0x00:      return 0;
+       case 0x01:      return 1;
+       case 0x02:      return allowbbt ? 0 : 1;
+       }
+       return 1;
+ }
+ #endif
++#endif /* CONFIG_NEW_NAND_CODE */
++
index 0000000,6e11c22..4e610c1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,243 +1,247 @@@
+ /*
+  * This file contains an ECC algorithm from Toshiba that detects and
+  * corrects 1 bit errors in a 256 byte block of data.
+  *
+  * drivers/mtd/nand/nand_ecc.c
+  *
+  * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+  *                         Toshiba America Electronics Components, Inc.
+  *
+  * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
+  *
+  * 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 <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #if (CONFIG_COMMANDS & CFG_CMD_NAND)
++#include<linux/mtd/mtd.h>
+ /*
+  * Pre-calculated 256-way 1 byte column parity
+  */
+ static const u_char nand_ecc_precalc_table[] = {
+       0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+       0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+       0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+       0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+       0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+       0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+       0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+       0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+       0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+       0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+       0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+       0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+       0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+       0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+       0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+       0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+ };
+ /**
+  * 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,
+       u_char *ecc_code)
+ {
+       u_char a, b, i, tmp1, tmp2;
+       /* Initialize variables */
+       a = b = 0x80;
+       tmp1 = tmp2 = 0;
+       /* Calculate first ECC byte */
+       for (i = 0; i < 4; i++) {
+               if (reg3 & a)           /* LP15,13,11,9 --> ecc_code[0] */
+                       tmp1 |= b;
+               b >>= 1;
+               if (reg2 & a)           /* LP14,12,10,8 --> ecc_code[0] */
+                       tmp1 |= b;
+               b >>= 1;
+               a >>= 1;
+       }
+       /* Calculate second ECC byte */
+       b = 0x80;
+       for (i = 0; i < 4; i++) {
+               if (reg3 & a)           /* LP7,5,3,1 --> ecc_code[1] */
+                       tmp2 |= b;
+               b >>= 1;
+               if (reg2 & a)           /* LP6,4,2,0 --> ecc_code[1] */
+                       tmp2 |= b;
+               b >>= 1;
+               a >>= 1;
+       }
+       /* Store two of the ECC bytes */
+       ecc_code[0] = tmp1;
+       ecc_code[1] = tmp2;
+ }
+ /**
+  * 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
+  */
+ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+ {
+       u_char idx, reg1, reg2, reg3;
+       int j;
+       /* Initialize variables */
+       reg1 = reg2 = reg3 = 0;
+       ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+       /* Build up column parity */
+       for(j = 0; j < 256; j++) {
+               /* Get CP0 - CP5 from table */
+               idx = nand_ecc_precalc_table[dat[j]];
+               reg1 ^= (idx & 0x3f);
+               /* All bit XOR = 1 ? */
+               if (idx & 0x40) {
+                       reg3 ^= (u_char) j;
+                       reg2 ^= ~((u_char) j);
+               }
+       }
+       /* Create non-inverted ECC code from line parity */
+       nand_trans_result(reg2, reg3, ecc_code);
+       /* Calculate final 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(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;
+       /* Do error detection */
+       d1 = calc_ecc[0] ^ read_ecc[0];
+       d2 = calc_ecc[1] ^ read_ecc[1];
+       d3 = calc_ecc[2] ^ read_ecc[2];
+       if ((d1 | d2 | d3) == 0) {
+               /* No errors */
+               return 0;
+       }
+       else {
+               a = (d1 ^ (d1 >> 1)) & 0x55;
+               b = (d2 ^ (d2 >> 1)) & 0x55;
+               c = (d3 ^ (d3 >> 1)) & 0x54;
+               /* Found and will correct single bit error in the data */
+               if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
+                       c = 0x80;
+                       add = 0;
+                       a = 0x80;
+                       for (i=0; i<4; i++) {
+                               if (d1 & c)
+                                       add |= a;
+                               c >>= 2;
+                               a >>= 1;
+                       }
+                       c = 0x80;
+                       for (i=0; i<4; i++) {
+                               if (d2 & c)
+                                       add |= a;
+                               c >>= 2;
+                               a >>= 1;
+                       }
+                       bit = 0;
+                       b = 0x04;
+                       c = 0x80;
+                       for (i=0; i<3; i++) {
+                               if (d3 & c)
+                                       bit |= b;
+                               c >>= 2;
+                               b >>= 1;
+                       }
+                       b = 0x01;
+                       a = dat[add];
+                       a ^= (b << bit);
+                       dat[add] = a;
+                       return 1;
+               } else {
+                       i = 0;
+                       while (d1) {
+                               if (d1 & 0x01)
+                                       ++i;
+                               d1 >>= 1;
+                       }
+                       while (d2) {
+                               if (d2 & 0x01)
+                                       ++i;
+                               d2 >>= 1;
+                       }
+                       while (d3) {
+                               if (d3 & 0x01)
+                                       ++i;
+                               d3 >>= 1;
+                       }
+                       if (i == 1) {
+                               /* ECC Code Error Correction */
+                               read_ecc[0] = calc_ecc[0];
+                               read_ecc[1] = calc_ecc[1];
+                               read_ecc[2] = calc_ecc[2];
+                               return 2;
+                       }
+                       else {
+                               /* Uncorrectable Error */
+                               return -1;
+                       }
+               }
+       }
+       /* Should never happen */
+       return -1;
+ }
+ #endif        /* CONFIG_COMMANDS & CFG_CMD_NAND */
++#endif /* CONFIG_NEW_NAND_CODE */
++
index 0000000,39882cc..d355326
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,127 +1,131 @@@
+ /*
+  *  drivers/mtd/nandids.c
+  *
+  *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
+   *
+  * $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
+  * published by the Free Software Foundation.
+  *
+  */
++
+ #include <common.h>
++#ifdef CONFIG_NEW_NAND_CODE
+ #if (CONFIG_COMMANDS & CFG_CMD_NAND)
+ #include <linux/mtd/nand.h>
+ /*
+ *     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 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,}
+ };
+ /*
+ *     Manufacturer ID list
+ */
+ struct nand_manufacturers nand_manuf_ids[] = {
+       {NAND_MFR_TOSHIBA, "Toshiba"},
+       {NAND_MFR_SAMSUNG, "Samsung"},
+       {NAND_MFR_FUJITSU, "Fujitsu"},
+       {NAND_MFR_NATIONAL, "National"},
+       {NAND_MFR_RENESAS, "Renesas"},
+       {NAND_MFR_STMICRO, "ST Micro"},
+       {0x0, "Unknown"}
+ };
+ #endif
++#endif /* CONFIG_NEW_NAND_CODE */
++
Simple merge
   * NAND-FLASH stuff
   *-----------------------------------------------------------------------
   */
++
++/* Use the new NAND code. (BOARDLIBS = drivers/nand/libnand.a required) */
++#define CONFIG_NEW_NAND_CODE
  #define CFG_NAND0_BASE 0xFF400000
  #define CFG_NAND1_BASE 0xFF000000
--
--#define CFG_MAX_NAND_DEVICE   2       /* Max number of NAND devices           */
++#define CFG_NAND_BASE_LIST    { CFG_NAND0_BASE, CFG_NAND1_BASE }
++#define NAND_BIG_DELAY_US     25
++#define CFG_MAX_NAND_DEVICE   2       /* Max number of NAND devices */
  #define SECTORSIZE 512
  #define NAND_NO_RB
  
  #define CFG_NAND1_ALE (0x80000000 >> 16)  /* our ALE is GPIO16 */
  #define CFG_NAND1_RDY (0x80000000 >> 31)  /* our RDY is GPIO31 */
  
++#ifdef CONFIG_NEW_NAND_CODE
++#define MACRO_NAND_DISABLE_CE(nandptr) do \
++{ \
++      switch((unsigned long)nandptr) \
++      { \
++          case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND0_CE); \
++              break; \
++          case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND1_CE); \
++              break; \
++      } \
++} while(0)
++
++#define MACRO_NAND_ENABLE_CE(nandptr) do \
++{ \
++      switch((unsigned long)nandptr) \
++      { \
++          case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND0_CE); \
++              break; \
++          case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND1_CE); \
++              break; \
++      } \
++} while(0)
++
++#define MACRO_NAND_CTL_CLRALE(nandptr) do \
++{ \
++      switch((unsigned long)nandptr) \
++      { \
++          case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND0_ALE); \
++              break; \
++          case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND1_ALE); \
++              break; \
++      } \
++} while(0)
++
++#define MACRO_NAND_CTL_SETALE(nandptr) do \
++{ \
++      switch((unsigned long)nandptr) \
++      { \
++          case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND0_ALE); \
++              break; \
++          case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND1_ALE); \
++              break; \
++      } \
++} while(0)
++
++#define MACRO_NAND_CTL_CLRCLE(nandptr) do \
++{ \
++      switch((unsigned long)nandptr) \
++      { \
++          case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND0_CLE); \
++              break; \
++          case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) & ~CFG_NAND1_CLE); \
++              break; \
++      } \
++} while(0)
++
++#define MACRO_NAND_CTL_SETCLE(nandptr) do { \
++      switch((unsigned long)nandptr) { \
++      case CFG_NAND0_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND0_CLE); \
++              break; \
++      case CFG_NAND1_BASE: \
++              out32(GPIO0_OR, in32(GPIO0_OR) | CFG_NAND1_CLE); \
++              break; \
++      } \
++} while(0)
++#else
  #define NAND_DISABLE_CE(nand) do \
  { \
        switch((unsigned long)(((struct nand_chip *)nand)->IO_ADDR)) \
                break; \
        } \
  } while(0)
++#endif /* !CONFIG_NEW_NAND_CODE */
  
  #ifdef NAND_NO_RB
  /* constant delay (see also tR in the datasheet) */
  #define CFG_SDRAM_BASE                0x00000000
  
  /* Reserve 256 kB for Monitor */
++/*
  #define CFG_FLASH_BASE                0xFFFC0000
  #define CFG_MONITOR_BASE      CFG_FLASH_BASE
  #define CFG_MONITOR_LEN               (256 * 1024)
++*/
  
  /* Reserve 320 kB for Monitor */
--/*
  #define CFG_FLASH_BASE                0xFFFB0000
  #define CFG_MONITOR_BASE      CFG_FLASH_BASE
  #define CFG_MONITOR_LEN               (320 * 1024)
--*/
  
  #define CFG_MALLOC_LEN                (256 * 1024)    /* Reserve 256 kB for malloc()  */
  
  #ifndef __LINUX_MTD_NAND_H
  #define __LINUX_MTD_NAND_H
  
 -#include <linux/mtd/compat.h>
 -#include <linux/mtd/mtd.h>
 -
 -struct mtd_info;
 -/* 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.
 - */
 -#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
 -
++#ifdef CONFIG_NEW_NAND_CODE
++#include "nand_new.h"
++#else
  /*
   * Standard NAND flash commands
   */
@@@ -172,29 -387,85 +175,29 @@@ struct nand_flash_dev 
  /*
  * Constants for oob configuration
  */
 -#define NAND_SMALL_BADBLOCK_POS               5
 -#define NAND_LARGE_BADBLOCK_POS               0
 -
 +#define NAND_NOOB_ECCPOS0             0
 +#define NAND_NOOB_ECCPOS1             1
 +#define NAND_NOOB_ECCPOS2             2
 +#define NAND_NOOB_ECCPOS3             3
 +#define NAND_NOOB_ECCPOS4             6
 +#define NAND_NOOB_ECCPOS5             7
 +#define NAND_NOOB_BADBPOS             -1
 +#define NAND_NOOB_ECCVPOS             -1
 +
 +#define NAND_JFFS2_OOB_ECCPOS0                0
 +#define NAND_JFFS2_OOB_ECCPOS1                1
 +#define NAND_JFFS2_OOB_ECCPOS2                2
 +#define NAND_JFFS2_OOB_ECCPOS3                3
 +#define NAND_JFFS2_OOB_ECCPOS4                6
 +#define NAND_JFFS2_OOB_ECCPOS5                7
 +#define NAND_JFFS2_OOB_BADBPOS                5
 +#define NAND_JFFS2_OOB_ECCVPOS                4
 +
 +#define NAND_JFFS2_OOB8_FSDAPOS               6
 +#define NAND_JFFS2_OOB16_FSDAPOS      8
 +#define NAND_JFFS2_OOB8_FSDALEN               2
 +#define NAND_JFFS2_OOB16_FSDALEN      8
 +
 +unsigned long nand_probe(unsigned long physadr);
++#endif /* !CONFIG_NEW_NAND_CODE */
  #endif /* __LINUX_MTD_NAND_H */
index 0000000,0000000..7d4b805
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,469 @@@
++/*
++ *  linux/include/linux/mtd/nand.h
++ *
++ *  Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
++ *                     Steven J. Hill <sjhill@realitydiluted.com>
++ *                   Thomas Gleixner <tglx@linutronix.de>
++ *
++ * $Id: nand.h,v 1.68 2004/11/12 10:40:37 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
++ * published by the Free Software Foundation.
++ *
++ *  Info:
++ *   Contains standard defines and IDs for NAND flash devices
++ *
++ *  Changelog:
++ *   01-31-2000 DMW     Created
++ *   09-18-2000 SJH     Moved structure out of the Disk-On-Chip drivers
++ *                    so it can be used by other NAND flash device
++ *                    drivers. I also changed the copyright since none
++ *                    of the original contents of this file are specific
++ *                    to DoC devices. David can whack me with a baseball
++ *                    bat later if I did something naughty.
++ *   10-11-2000 SJH     Added private NAND flash structure for driver
++ *   10-24-2000 SJH     Added prototype for 'nand_scan' function
++ *   10-29-2001 TG    changed nand_chip structure to support
++ *                    hardwarespecific function for accessing control lines
++ *   02-21-2002 TG    added support for different read/write adress and
++ *                    ready/busy line access function
++ *   02-26-2002 TG    added chip_delay to nand_chip structure to optimize
++ *                    command delay times for different chips
++ *   04-28-2002 TG    OOB config defines moved from nand.c to avoid duplicate
++ *                    defines in jffs2/wbuf.c
++ *   08-07-2002 TG    forced bad block location to byte 5 of OOB, even if
++ *                    CONFIG_MTD_NAND_ECC_JFFS2 is not set
++ *   08-10-2002 TG    extensions to nand_chip structure to support HW-ECC
++ *
++ *   08-29-2002 tglx  nand_chip structure: data_poi for selecting
++ *                    internal / fs-driver buffer
++ *                    support for 6byte/512byte hardware ECC
++ *                    read_ecc, write_ecc extended for different oob-layout
++ *                    oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB,
++ *                    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_NEW_H
++#define __LINUX_MTD_NAND_NEW_H
++
++#include <linux/mtd/compat.h>
++#include <linux/mtd/mtd.h>
++
++struct mtd_info;
++/* 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);
++
++
++
++/* This constant declares the max. oobsize / page, which
++ * is supported now. If you add a chip with bigger oobsize/page
++ * adjust this accordingly.
++ */
++#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
++ */
++#define NAND_CMD_READ0                0
++#define NAND_CMD_READ1                1
++#define NAND_CMD_PAGEPROG     0x10
++#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
++ */
++
++/* 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
++/* Hardware ECC 8 byte ECC per 512 Byte data */
++#define NAND_ECC_HW8_512      6
++/* Hardware ECC 12 byte ECC per 2048 Byte data */
++#define NAND_ECC_HW12_2048    7
++
++/*
++ * 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 {
++      FL_READY,
++      FL_READING,
++      FL_WRITING,
++      FL_ERASING,
++      FL_SYNCING,
++      FL_CACHEDPRG,
++} nand_state_t;
++
++/* Keep gcc happy */
++struct nand_chip;
++
++#if 0
++/**
++ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
++ * @lock:               protection lock
++ * @active:           the mtd device which holds the controller currently
++ */
++struct nand_hw_control {
++      spinlock_t       lock;
++      struct nand_chip *active;
++};
++#endif
++
++/**
++ * 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
++ * @eccbytes:                 [INTERN] number of ecc bytes per ecc-calculation step
++ * @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
++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
++ * @controller:               [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
++ * @priv:             [OPTIONAL] pointer to private chip date
++ */
++
++struct nand_chip {
++      void  __iomem   *IO_ADDR_R;
++      void  __iomem   *IO_ADDR_W;
++
++      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);
++      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             eccbytes;
++      int             eccsteps;
++      int             chip_delay;
++#if 0
++      spinlock_t      chip_lock;
++      wait_queue_head_t wq;
++      nand_state_t    state;
++#endif
++      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;
++      struct nand_bbt_descr   *badblock_pattern;
++      struct nand_hw_control  *controller;
++      void            *priv;
++};
++
++/*
++ * NAND Flash Manufacturer ID Codes
++ */
++#define NAND_MFR_TOSHIBA      0x98
++#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
++
++/**
++ * 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;
++      int id;
++      unsigned long pagesize;
++      unsigned long chipsize;
++      unsigned long erasesize;
++      unsigned long options;
++};
++
++/**
++ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
++ * @name:     Manufacturer name
++ * @id:       manufacturer ID code of device.
++*/
++struct nand_manufacturers {
++      int id;
++      char * name;
++};
++
++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_SMALL_BADBLOCK_POS               5
++#define NAND_LARGE_BADBLOCK_POS               0
++
++#endif /* __LINUX_MTD_NAND_NEW_H */
diff --cc include/nand.h
index 0000000,3490347..905115b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,63 +1,63 @@@
 -      return info->read(info, ofs, *len, len, buf);
+ /*
+  * (C) Copyright 2005
+  * 2N Telekomunikace, a.s. <www.2n.cz>
+  * Ladislav Michl <michl@2n.cz>
+  *
+  * See file CREDITS for list of people who contributed to this
+  * project.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+  * MA 02111-1307 USA
+  */
+ #ifndef _NAND_H_
+ #define _NAND_H_
+ #include <linux/mtd/compat.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ typedef struct mtd_info nand_info_t;
+ extern int nand_curr_device;
+ extern nand_info_t nand_info[];
+ static inline int nand_read(nand_info_t *info, ulong ofs, ulong *len, u_char *buf)
+ {
 -      return info->write(info, ofs, *len, len, buf);
++      return info->read(info, ofs, *len, (size_t *)len, buf);
+ }
+ static inline int nand_write(nand_info_t *info, ulong ofs, ulong *len, u_char *buf)
+ {
++      return info->write(info, ofs, *len, (size_t *)len, buf);
+ }
+ static inline int nand_block_isbad(nand_info_t *info, ulong ofs)
+ {
+       return info->block_isbad(info, ofs);
+ }
+ static inline int nand_erase(nand_info_t *info, ulong off, ulong size)
+ {
+       struct erase_info instr;
+       instr.mtd = info;
+       instr.addr = off;
+       instr.len = size;
+       instr.callback = 0;
+       return info->erase(info, &instr);
+ }
+ #endif