From 59829cc189378c142c13d2aa8d9a897d8bef3961 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 24 Nov 2007 21:26:56 +0100 Subject: drivers/mtd : move mtd drivers to drivers/mtd Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- Makefile | 16 +- drivers/Makefile | 54 - drivers/at45.c | 562 ------- drivers/cfi_flash.c | 1528 ------------------- drivers/dataflash.c | 507 ------- drivers/mtd/Makefile | 49 + drivers/mtd/at45.c | 562 +++++++ drivers/mtd/cfi_flash.c | 1528 +++++++++++++++++++ drivers/mtd/dataflash.c | 507 +++++++ drivers/mtd/mw_eeprom.c | 241 +++ drivers/mtd/nand/Makefile | 51 + drivers/mtd/nand/diskonchip.c | 1787 ++++++++++++++++++++++ drivers/mtd/nand/nand.c | 83 + drivers/mtd/nand/nand_base.c | 2668 +++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_bbt.c | 1052 +++++++++++++ drivers/mtd/nand/nand_ecc.c | 200 +++ drivers/mtd/nand/nand_ids.c | 129 ++ drivers/mtd/nand/nand_util.c | 872 +++++++++++ drivers/mtd/nand_legacy/Makefile | 45 + drivers/mtd/nand_legacy/nand_legacy.c | 1612 ++++++++++++++++++++ drivers/mtd/onenand/Makefile | 44 + drivers/mtd/onenand/onenand_base.c | 1294 ++++++++++++++++ drivers/mtd/onenand/onenand_bbt.c | 265 ++++ drivers/mw_eeprom.c | 241 --- drivers/nand/Makefile | 51 - drivers/nand/diskonchip.c | 1787 ---------------------- drivers/nand/nand.c | 83 - drivers/nand/nand_base.c | 2668 --------------------------------- drivers/nand/nand_bbt.c | 1052 ------------- drivers/nand/nand_ecc.c | 200 --- drivers/nand/nand_ids.c | 129 -- drivers/nand/nand_util.c | 872 ----------- drivers/nand_legacy/Makefile | 45 - drivers/nand_legacy/nand_legacy.c | 1612 -------------------- drivers/onenand/Makefile | 44 - drivers/onenand/onenand_base.c | 1294 ---------------- drivers/onenand/onenand_bbt.c | 265 ---- nand_spl/board/amcc/acadia/Makefile | 2 +- nand_spl/board/amcc/bamboo/Makefile | 2 +- nand_spl/board/amcc/sequoia/Makefile | 2 +- 40 files changed, 13000 insertions(+), 13005 deletions(-) delete mode 100755 drivers/Makefile delete mode 100644 drivers/at45.c delete mode 100644 drivers/cfi_flash.c delete mode 100644 drivers/dataflash.c create mode 100644 drivers/mtd/Makefile create mode 100644 drivers/mtd/at45.c create mode 100644 drivers/mtd/cfi_flash.c create mode 100644 drivers/mtd/dataflash.c create mode 100644 drivers/mtd/mw_eeprom.c create mode 100644 drivers/mtd/nand/Makefile create mode 100644 drivers/mtd/nand/diskonchip.c create mode 100644 drivers/mtd/nand/nand.c create mode 100644 drivers/mtd/nand/nand_base.c create mode 100644 drivers/mtd/nand/nand_bbt.c create mode 100644 drivers/mtd/nand/nand_ecc.c create mode 100644 drivers/mtd/nand/nand_ids.c create mode 100644 drivers/mtd/nand/nand_util.c create mode 100644 drivers/mtd/nand_legacy/Makefile create mode 100644 drivers/mtd/nand_legacy/nand_legacy.c create mode 100644 drivers/mtd/onenand/Makefile create mode 100644 drivers/mtd/onenand/onenand_base.c create mode 100644 drivers/mtd/onenand/onenand_bbt.c delete mode 100644 drivers/mw_eeprom.c delete mode 100644 drivers/nand/Makefile delete mode 100644 drivers/nand/diskonchip.c delete mode 100644 drivers/nand/nand.c delete mode 100644 drivers/nand/nand_base.c delete mode 100644 drivers/nand/nand_bbt.c delete mode 100644 drivers/nand/nand_ecc.c delete mode 100644 drivers/nand/nand_ids.c delete mode 100644 drivers/nand/nand_util.c delete mode 100644 drivers/nand_legacy/Makefile delete mode 100644 drivers/nand_legacy/nand_legacy.c delete mode 100644 drivers/onenand/Makefile delete mode 100644 drivers/onenand/onenand_base.c delete mode 100644 drivers/onenand/onenand_bbt.c diff --git a/Makefile b/Makefile index 00a4e2a2a4..5f3f0b303d 100644 --- a/Makefile +++ b/Makefile @@ -205,30 +205,30 @@ LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a LIBS += net/libnet.a LIBS += disk/libdisk.a -LIBS += drivers/libdrivers.a LIBS += drivers/bios_emulator/libatibiosemu.a LIBS += drivers/block/libblock.a LIBS += drivers/hwmon/libhwmon.a LIBS += drivers/i2c/libi2c.a LIBS += drivers/input/libinput.a LIBS += drivers/misc/libmisc.a -LIBS += drivers/nand/libnand.a -LIBS += drivers/nand_legacy/libnand_legacy.a +LIBS += drivers/mtd/libmtd.a +LIBS += drivers/mtd/nand/libnand.a +LIBS += drivers/mtd/nand_legacy/libnand_legacy.a +LIBS += drivers/mtd/onenand/libonenand.a LIBS += drivers/net/libnet.a LIBS += drivers/net/sk98lin/libsk98lin.a -LIBS += drivers/onenand/libonenand.a LIBS += drivers/pci/libpci.a LIBS += drivers/pcmcia/libpcmcia.a -LIBS += drivers/rtc/librtc.a -LIBS += drivers/serial/libserial.a -LIBS += drivers/usb/libusb.a -LIBS += drivers/video/libvideo.a ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/qe.a endif ifeq ($(CPU),mpc85xx) LIBS += drivers/qe/qe.a endif +LIBS += drivers/rtc/librtc.a +LIBS += drivers/serial/libserial.a +LIBS += drivers/usb/libusb.a +LIBS += drivers/video/libvideo.a LIBS += post/libpost.a post/drivers/libpostdrivers.a LIBS += $(shell if [ -d post/lib_$(ARCH) ]; then echo \ "post/lib_$(ARCH)/libpost$(ARCH).a"; fi) diff --git a/drivers/Makefile b/drivers/Makefile deleted file mode 100755 index d596a4ee3b..0000000000 --- a/drivers/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# -# (C) Copyright 2000-2007 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# See file CREDITS for list of people who contributed to this -# project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA -# - -include $(TOPDIR)/config.mk - -# CFLAGS += -DET_DEBUG -DDEBUG - -LIB = $(obj)libdrivers.a - -# -# Block and Flash Drivers -# -COBJS-y += at45.o -COBJS-y += cfi_flash.o -COBJS-y += dataflash.o -COBJS-y += mw_eeprom.o - -COBJS := $(COBJS-y) -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) - -all: $(LIB) - -$(LIB): $(obj).depend $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -######################################################################### - -# defines $(obj).depend target -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/at45.c b/drivers/at45.c deleted file mode 100644 index dac987a43a..0000000000 --- a/drivers/at45.c +++ /dev/null @@ -1,562 +0,0 @@ -/* Driver for ATMEL DataFlash support - * Author : Hamid Ikdoumi (Atmel) - * - * 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 -#include - -#ifdef CONFIG_HAS_DATAFLASH -#include - -/* - * spi.c API - */ -extern unsigned int AT91F_SpiWrite(AT91PS_DataflashDesc pDesc); -extern void AT91F_SpiEnable(int cs); - -#define AT91C_TIMEOUT_WRDY 200000 - -/*----------------------------------------------------------------------*/ -/* \fn AT91F_DataFlashSendCommand */ -/* \brief Generic function to send a command to the dataflash */ -/*----------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashSendCommand(AT91PS_DataFlash pDataFlash, - unsigned char OpCode, - unsigned int CmdSize, - unsigned int DataflashAddress) -{ - unsigned int adr; - - if ((pDataFlash->pDataFlashDesc->state) != IDLE) - return DATAFLASH_BUSY; - - /* process the address to obtain page address and byte address */ - adr = ((DataflashAddress / (pDataFlash->pDevice->pages_size)) << - pDataFlash->pDevice->page_offset) + - (DataflashAddress % (pDataFlash->pDevice->pages_size)); - - /* fill the command buffer */ - pDataFlash->pDataFlashDesc->command[0] = OpCode; - if (pDataFlash->pDevice->pages_number >= 16384) { - pDataFlash->pDataFlashDesc->command[1] = - (unsigned char)((adr & 0x0F000000) >> 24); - pDataFlash->pDataFlashDesc->command[2] = - (unsigned char)((adr & 0x00FF0000) >> 16); - pDataFlash->pDataFlashDesc->command[3] = - (unsigned char)((adr & 0x0000FF00) >> 8); - pDataFlash->pDataFlashDesc->command[4] = - (unsigned char)(adr & 0x000000FF); - } else { - pDataFlash->pDataFlashDesc->command[1] = - (unsigned char)((adr & 0x00FF0000) >> 16); - pDataFlash->pDataFlashDesc->command[2] = - (unsigned char)((adr & 0x0000FF00) >> 8); - pDataFlash->pDataFlashDesc->command[3] = - (unsigned char)(adr & 0x000000FF); - pDataFlash->pDataFlashDesc->command[4] = 0; - } - pDataFlash->pDataFlashDesc->command[5] = 0; - pDataFlash->pDataFlashDesc->command[6] = 0; - pDataFlash->pDataFlashDesc->command[7] = 0; - - /* Initialize the SpiData structure for the spi write fuction */ - pDataFlash->pDataFlashDesc->tx_cmd_pt = - pDataFlash->pDataFlashDesc->command; - pDataFlash->pDataFlashDesc->tx_cmd_size = CmdSize; - pDataFlash->pDataFlashDesc->rx_cmd_pt = - pDataFlash->pDataFlashDesc->command; - pDataFlash->pDataFlashDesc->rx_cmd_size = CmdSize; - - /* send the command and read the data */ - return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); -} - -/*----------------------------------------------------------------------*/ -/* \fn AT91F_DataFlashGetStatus */ -/* \brief Read the status register of the dataflash */ -/*----------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashGetStatus(AT91PS_DataflashDesc pDesc) -{ - AT91S_DataFlashStatus status; - - /* if a transfert is in progress ==> return 0 */ - if ((pDesc->state) != IDLE) - return DATAFLASH_BUSY; - - /* first send the read status command (D7H) */ - pDesc->command[0] = DB_STATUS; - pDesc->command[1] = 0; - - pDesc->DataFlash_state = GET_STATUS; - pDesc->tx_data_size = 0; /* Transmit the command */ - /* and receive response */ - pDesc->tx_cmd_pt = pDesc->command; - pDesc->rx_cmd_pt = pDesc->command; - pDesc->rx_cmd_size = 2; - pDesc->tx_cmd_size = 2; - status = AT91F_SpiWrite(pDesc); - - pDesc->DataFlash_state = *((unsigned char *)(pDesc->rx_cmd_pt) + 1); - - return status; -} - -/*----------------------------------------------------------------------*/ -/* \fn AT91F_DataFlashWaitReady */ -/* \brief wait for dataflash ready (bit7 of the status register == 1) */ -/*----------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashWaitReady(AT91PS_DataflashDesc - pDataFlashDesc, - unsigned int timeout) -{ - pDataFlashDesc->DataFlash_state = IDLE; - - do { - AT91F_DataFlashGetStatus(pDataFlashDesc); - timeout--; - } while (((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) && - (timeout > 0)); - - if ((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) - return DATAFLASH_ERROR; - - return DATAFLASH_OK; -} - -/*--------------------------------------------------------------------------*/ -/* Function Name : AT91F_DataFlashContinuousRead */ -/* Object : Continuous stream Read */ -/* Input Parameters : DataFlash Service */ -/* : = dataflash address */ -/* : <*dataBuffer> = data buffer pointer */ -/* : = data buffer size */ -/* Return value : State of the dataflash */ -/*--------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashContinuousRead( - AT91PS_DataFlash pDataFlash, - int src, - unsigned char *dataBuffer, - int sizeToRead) -{ - AT91S_DataFlashStatus status; - /* Test the size to read in the device */ - if ((src + sizeToRead) > - (pDataFlash->pDevice->pages_size * - (pDataFlash->pDevice->pages_number))) - return DATAFLASH_MEMORY_OVERFLOW; - - pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; - pDataFlash->pDataFlashDesc->rx_data_size = sizeToRead; - pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; - pDataFlash->pDataFlashDesc->tx_data_size = sizeToRead; - - status = AT91F_DataFlashSendCommand( - pDataFlash, DB_CONTINUOUS_ARRAY_READ, 8, src); - /* Send the command to the dataflash */ - return (status); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_DataFlashPagePgmBuf */ -/* Object : Main memory page program thru buffer 1 or buffer 2 */ -/* Input Parameters : DataFlash Service */ -/* : <*src> = Source buffer */ -/* : = dataflash destination address */ -/* : = data buffer size */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashPagePgmBuf(AT91PS_DataFlash pDataFlash, - unsigned char *src, - unsigned int dest, - unsigned int SizeToWrite) -{ - int cmdsize; - pDataFlash->pDataFlashDesc->tx_data_pt = src; - pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; - pDataFlash->pDataFlashDesc->rx_data_pt = src; - pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; - - cmdsize = 4; - /* Send the command to the dataflash */ - if (pDataFlash->pDevice->pages_number >= 16384) - cmdsize = 5; - return (AT91F_DataFlashSendCommand( - pDataFlash, DB_PAGE_PGM_BUF1, cmdsize, dest)); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_MainMemoryToBufferTransfert */ -/* Object : Read a page in the SRAM Buffer 1 or 2 */ -/* Input Parameters : DataFlash Service */ -/* : Page concerned */ -/* : */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_MainMemoryToBufferTransfert( - AT91PS_DataFlash - pDataFlash, - unsigned char - BufferCommand, - unsigned int page) -{ - int cmdsize; - /* Test if the buffer command is legal */ - if ((BufferCommand != DB_PAGE_2_BUF1_TRF) && - (BufferCommand != DB_PAGE_2_BUF2_TRF)) { - return DATAFLASH_BAD_COMMAND; - } - - /* no data to transmit or receive */ - pDataFlash->pDataFlashDesc->tx_data_size = 0; - cmdsize = 4; - if (pDataFlash->pDevice->pages_number >= 16384) - cmdsize = 5; - return (AT91F_DataFlashSendCommand( - pDataFlash, BufferCommand, cmdsize, - page * pDataFlash->pDevice->pages_size)); -} - -/*-------------------------------------------------------------------------- */ -/* Function Name : AT91F_DataFlashWriteBuffer */ -/* Object : Write data to the internal sram buffer 1 or 2 */ -/* Input Parameters : DataFlash Service */ -/* : = command to write buffer1 or 2 */ -/* : <*dataBuffer> = data buffer to write */ -/* : = address in the internal buffer */ -/* : = data buffer size */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashWriteBuffer( - AT91PS_DataFlash pDataFlash, - unsigned char BufferCommand, - unsigned char *dataBuffer, - unsigned int bufferAddress, - int SizeToWrite) -{ - int cmdsize; - /* Test if the buffer command is legal */ - if ((BufferCommand != DB_BUF1_WRITE) && - (BufferCommand != DB_BUF2_WRITE)) { - return DATAFLASH_BAD_COMMAND; - } - - /* buffer address must be lower than page size */ - if (bufferAddress > pDataFlash->pDevice->pages_size) - return DATAFLASH_BAD_ADDRESS; - - if ((pDataFlash->pDataFlashDesc->state) != IDLE) - return DATAFLASH_BUSY; - - /* Send first Write Command */ - pDataFlash->pDataFlashDesc->command[0] = BufferCommand; - pDataFlash->pDataFlashDesc->command[1] = 0; - if (pDataFlash->pDevice->pages_number >= 16384) { - pDataFlash->pDataFlashDesc->command[2] = 0; - pDataFlash->pDataFlashDesc->command[3] = - (unsigned char)(((unsigned int)(bufferAddress & - pDataFlash->pDevice-> - byte_mask)) >> 8); - pDataFlash->pDataFlashDesc->command[4] = - (unsigned char)((unsigned int)bufferAddress & 0x00FF); - cmdsize = 5; - } else { - pDataFlash->pDataFlashDesc->command[2] = - (unsigned char)(((unsigned int)(bufferAddress & - pDataFlash->pDevice-> - byte_mask)) >> 8); - pDataFlash->pDataFlashDesc->command[3] = - (unsigned char)((unsigned int)bufferAddress & 0x00FF); - pDataFlash->pDataFlashDesc->command[4] = 0; - cmdsize = 4; - } - - pDataFlash->pDataFlashDesc->tx_cmd_pt = - pDataFlash->pDataFlashDesc->command; - pDataFlash->pDataFlashDesc->tx_cmd_size = cmdsize; - pDataFlash->pDataFlashDesc->rx_cmd_pt = - pDataFlash->pDataFlashDesc->command; - pDataFlash->pDataFlashDesc->rx_cmd_size = cmdsize; - - pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; - pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; - pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; - pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; - - return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_PageErase */ -/* Object : Erase a page */ -/* Input Parameters : DataFlash Service */ -/* : Page concerned */ -/* : */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_PageErase( - AT91PS_DataFlash pDataFlash, - unsigned int page) -{ - int cmdsize; - /* Test if the buffer command is legal */ - /* no data to transmit or receive */ - pDataFlash->pDataFlashDesc->tx_data_size = 0; - - cmdsize = 4; - if (pDataFlash->pDevice->pages_number >= 16384) - cmdsize = 5; - return (AT91F_DataFlashSendCommand(pDataFlash, - DB_PAGE_ERASE, cmdsize, - page * pDataFlash->pDevice->pages_size)); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_BlockErase */ -/* Object : Erase a Block */ -/* Input Parameters : DataFlash Service */ -/* : Page concerned */ -/* : */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_BlockErase( - AT91PS_DataFlash pDataFlash, - unsigned int block) -{ - int cmdsize; - /* Test if the buffer command is legal */ - /* no data to transmit or receive */ - pDataFlash->pDataFlashDesc->tx_data_size = 0; - cmdsize = 4; - if (pDataFlash->pDevice->pages_number >= 16384) - cmdsize = 5; - return (AT91F_DataFlashSendCommand(pDataFlash, DB_BLOCK_ERASE, cmdsize, - block * 8 * - pDataFlash->pDevice->pages_size)); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_WriteBufferToMain */ -/* Object : Write buffer to the main memory */ -/* Input Parameters : DataFlash Service */ -/* : = command to send to buffer1 or buffer2 */ -/* : = main memory address */ -/* Return value : State of the dataflash */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_WriteBufferToMain(AT91PS_DataFlash pDataFlash, - unsigned char BufferCommand, - unsigned int dest) -{ - int cmdsize; - /* Test if the buffer command is correct */ - if ((BufferCommand != DB_BUF1_PAGE_PGM) && - (BufferCommand != DB_BUF1_PAGE_ERASE_PGM) && - (BufferCommand != DB_BUF2_PAGE_PGM) && - (BufferCommand != DB_BUF2_PAGE_ERASE_PGM)) - return DATAFLASH_BAD_COMMAND; - - /* no data to transmit or receive */ - pDataFlash->pDataFlashDesc->tx_data_size = 0; - - cmdsize = 4; - if (pDataFlash->pDevice->pages_number >= 16384) - cmdsize = 5; - /* Send the command to the dataflash */ - return (AT91F_DataFlashSendCommand(pDataFlash, BufferCommand, - cmdsize, dest)); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_PartialPageWrite */ -/* Object : Erase partielly a page */ -/* Input Parameters : = page number */ -/* : = adr to begin the fading */ -/* : = Number of bytes to erase */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_PartialPageWrite(AT91PS_DataFlash pDataFlash, - unsigned char *src, - unsigned int dest, - unsigned int size) -{ - unsigned int page; - unsigned int AdrInPage; - - page = dest / (pDataFlash->pDevice->pages_size); - AdrInPage = dest % (pDataFlash->pDevice->pages_size); - - /* Read the contents of the page in the Sram Buffer */ - AT91F_MainMemoryToBufferTransfert(pDataFlash, DB_PAGE_2_BUF1_TRF, page); - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - /*Update the SRAM buffer */ - AT91F_DataFlashWriteBuffer(pDataFlash, DB_BUF1_WRITE, src, - AdrInPage, size); - - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - - /* Erase page if a 128 Mbits device */ - if (pDataFlash->pDevice->pages_number >= 16384) { - AT91F_PageErase(pDataFlash, page); - /* Rewrite the modified Sram Buffer in the main memory */ - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - } - - /* Rewrite the modified Sram Buffer in the main memory */ - return (AT91F_WriteBufferToMain(pDataFlash, DB_BUF1_PAGE_ERASE_PGM, - (page * - pDataFlash->pDevice->pages_size))); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_DataFlashWrite */ -/* Object : */ -/* Input Parameters : <*src> = Source buffer */ -/* : = dataflash adress */ -/* : = data buffer size */ -/*---------------------------------------------------------------------------*/ -AT91S_DataFlashStatus AT91F_DataFlashWrite(AT91PS_DataFlash pDataFlash, - unsigned char *src, - int dest, int size) -{ - unsigned int length; - unsigned int page; - unsigned int status; - - AT91F_SpiEnable(pDataFlash->pDevice->cs); - - if ((dest + size) > (pDataFlash->pDevice->pages_size * - (pDataFlash->pDevice->pages_number))) - return DATAFLASH_MEMORY_OVERFLOW; - - /* If destination does not fit a page start address */ - if ((dest % ((unsigned int)(pDataFlash->pDevice->pages_size))) != 0) { - length = - pDataFlash->pDevice->pages_size - - (dest % ((unsigned int)(pDataFlash->pDevice->pages_size))); - - if (size < length) - length = size; - - if (!AT91F_PartialPageWrite(pDataFlash, src, dest, length)) - return DATAFLASH_ERROR; - - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - - /* Update size, source and destination pointers */ - size -= length; - dest += length; - src += length; - } - - while ((size - pDataFlash->pDevice->pages_size) >= 0) { - /* program dataflash page */ - page = (unsigned int)dest / (pDataFlash->pDevice->pages_size); - - status = AT91F_DataFlashWriteBuffer(pDataFlash, - DB_BUF1_WRITE, src, 0, - pDataFlash->pDevice-> - pages_size); - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - - status = AT91F_PageErase(pDataFlash, page); - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - if (!status) - return DATAFLASH_ERROR; - - status = AT91F_WriteBufferToMain(pDataFlash, - DB_BUF1_PAGE_PGM, dest); - if (!status) - return DATAFLASH_ERROR; - - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - - /* Update size, source and destination pointers */ - size -= pDataFlash->pDevice->pages_size; - dest += pDataFlash->pDevice->pages_size; - src += pDataFlash->pDevice->pages_size; - } - - /* If still some bytes to read */ - if (size > 0) { - /* program dataflash page */ - if (!AT91F_PartialPageWrite(pDataFlash, src, dest, size)) - return DATAFLASH_ERROR; - - AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY); - } - return DATAFLASH_OK; -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_DataFlashRead */ -/* Object : Read a block in dataflash */ -/* Input Parameters : */ -/* Return value : */ -/*---------------------------------------------------------------------------*/ -int AT91F_DataFlashRead(AT91PS_DataFlash pDataFlash, - unsigned long addr, unsigned long size, char *buffer) -{ - unsigned long SizeToRead; - - AT91F_SpiEnable(pDataFlash->pDevice->cs); - - if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY) != DATAFLASH_OK) - return -1; - - while (size) { - SizeToRead = (size < 0x8000) ? size : 0x8000; - - if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, - AT91C_TIMEOUT_WRDY) != - DATAFLASH_OK) - return -1; - - if (AT91F_DataFlashContinuousRead(pDataFlash, addr, - (uchar *) buffer, - SizeToRead) != DATAFLASH_OK) - return -1; - - size -= SizeToRead; - addr += SizeToRead; - buffer += SizeToRead; - } - - return DATAFLASH_OK; -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : AT91F_DataflashProbe */ -/* Object : */ -/* Input Parameters : */ -/* Return value : Dataflash status register */ -/*---------------------------------------------------------------------------*/ -int AT91F_DataflashProbe(int cs, AT91PS_DataflashDesc pDesc) -{ - AT91F_SpiEnable(cs); - AT91F_DataFlashGetStatus(pDesc); - return ((pDesc->command[1] == 0xFF) ? 0 : pDesc->command[1] & 0x3C); -} -#endif diff --git a/drivers/cfi_flash.c b/drivers/cfi_flash.c deleted file mode 100644 index 5579a1efc1..0000000000 --- a/drivers/cfi_flash.c +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * (C) Copyright 2002-2004 - * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com - * - * Copyright (C) 2003 Arabella Software Ltd. - * Yuli Barcohen - * - * Copyright (C) 2004 - * Ed Okerson - * - * Copyright (C) 2006 - * Tolunay Orkun - * - * 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 - * - */ - -/* The DEBUG define must be before common to enable debugging */ -/* #define DEBUG */ - -#include -#include -#include -#include -#include -#ifdef CFG_FLASH_CFI_DRIVER - -/* - * This file implements a Common Flash Interface (CFI) driver for U-Boot. - * The width of the port and the width of the chips are determined at initialization. - * These widths are used to calculate the address for access CFI data structures. - * - * References - * JEDEC Standard JESD68 - Common Flash Interface (CFI) - * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes - * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets - * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet - * AMD CFI Specification, Release 2.0 December 1, 2001 - * AMD/Spansion Application Note: Migration from Single-byte to Three-byte - * Device IDs, Publication Number 25538 Revision A, November 8, 2001 - * - * define CFG_WRITE_SWAPPED_DATA, if you have to swap the Bytes between - * reading and writing ... (yes there is such a Hardware). - */ - -#ifndef CFG_FLASH_BANKS_LIST -#define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE } -#endif - -#define FLASH_CMD_CFI 0x98 -#define FLASH_CMD_READ_ID 0x90 -#define FLASH_CMD_RESET 0xff -#define FLASH_CMD_BLOCK_ERASE 0x20 -#define FLASH_CMD_ERASE_CONFIRM 0xD0 -#define FLASH_CMD_WRITE 0x40 -#define FLASH_CMD_PROTECT 0x60 -#define FLASH_CMD_PROTECT_SET 0x01 -#define FLASH_CMD_PROTECT_CLEAR 0xD0 -#define FLASH_CMD_CLEAR_STATUS 0x50 -#define FLASH_CMD_WRITE_TO_BUFFER 0xE8 -#define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0 - -#define FLASH_STATUS_DONE 0x80 -#define FLASH_STATUS_ESS 0x40 -#define FLASH_STATUS_ECLBS 0x20 -#define FLASH_STATUS_PSLBS 0x10 -#define FLASH_STATUS_VPENS 0x08 -#define FLASH_STATUS_PSS 0x04 -#define FLASH_STATUS_DPS 0x02 -#define FLASH_STATUS_R 0x01 -#define FLASH_STATUS_PROTECT 0x01 - -#define AMD_CMD_RESET 0xF0 -#define AMD_CMD_WRITE 0xA0 -#define AMD_CMD_ERASE_START 0x80 -#define AMD_CMD_ERASE_SECTOR 0x30 -#define AMD_CMD_UNLOCK_START 0xAA -#define AMD_CMD_UNLOCK_ACK 0x55 -#define AMD_CMD_WRITE_TO_BUFFER 0x25 -#define AMD_CMD_WRITE_BUFFER_CONFIRM 0x29 - -#define AMD_STATUS_TOGGLE 0x40 -#define AMD_STATUS_ERROR 0x20 - -#define AMD_ADDR_ERASE_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) -#define AMD_ADDR_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) -#define AMD_ADDR_ACK ((info->portwidth == FLASH_CFI_8BIT) ? 0x555 : 0x2AA) - -#define FLASH_OFFSET_MANUFACTURER_ID 0x00 -#define FLASH_OFFSET_DEVICE_ID 0x01 -#define FLASH_OFFSET_DEVICE_ID2 0x0E -#define FLASH_OFFSET_DEVICE_ID3 0x0F -#define FLASH_OFFSET_CFI 0x55 -#define FLASH_OFFSET_CFI_ALT 0x555 -#define FLASH_OFFSET_CFI_RESP 0x10 -#define FLASH_OFFSET_PRIMARY_VENDOR 0x13 -#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x15 /* extended query table primary addr */ -#define FLASH_OFFSET_WTOUT 0x1F -#define FLASH_OFFSET_WBTOUT 0x20 -#define FLASH_OFFSET_ETOUT 0x21 -#define FLASH_OFFSET_CETOUT 0x22 -#define FLASH_OFFSET_WMAX_TOUT 0x23 -#define FLASH_OFFSET_WBMAX_TOUT 0x24 -#define FLASH_OFFSET_EMAX_TOUT 0x25 -#define FLASH_OFFSET_CEMAX_TOUT 0x26 -#define FLASH_OFFSET_SIZE 0x27 -#define FLASH_OFFSET_INTERFACE 0x28 -#define FLASH_OFFSET_BUFFER_SIZE 0x2A -#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C -#define FLASH_OFFSET_ERASE_REGIONS 0x2D -#define FLASH_OFFSET_PROTECT 0x02 -#define FLASH_OFFSET_USER_PROTECTION 0x85 -#define FLASH_OFFSET_INTEL_PROTECTION 0x81 - -#define CFI_CMDSET_NONE 0 -#define CFI_CMDSET_INTEL_EXTENDED 1 -#define CFI_CMDSET_AMD_STANDARD 2 -#define CFI_CMDSET_INTEL_STANDARD 3 -#define CFI_CMDSET_AMD_EXTENDED 4 -#define CFI_CMDSET_MITSU_STANDARD 256 -#define CFI_CMDSET_MITSU_EXTENDED 257 -#define CFI_CMDSET_SST 258 - -#ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */ -# undef FLASH_CMD_RESET -# define FLASH_CMD_RESET AMD_CMD_RESET /* use AMD-Reset instead */ -#endif - -typedef union { - unsigned char c; - unsigned short w; - unsigned long l; - unsigned long long ll; -} cfiword_t; - -typedef union { - volatile unsigned char *cp; - volatile unsigned short *wp; - volatile unsigned long *lp; - volatile unsigned long long *llp; -} cfiptr_t; - -#define NUM_ERASE_REGIONS 4 /* max. number of erase regions */ - -static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT}; - -/* use CFG_MAX_FLASH_BANKS_DETECT if defined */ -#ifdef CFG_MAX_FLASH_BANKS_DETECT -static ulong bank_base[CFG_MAX_FLASH_BANKS_DETECT] = CFG_FLASH_BANKS_LIST; -flash_info_t flash_info[CFG_MAX_FLASH_BANKS_DETECT]; /* FLASH chips info */ -#else -static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST; -flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */ -#endif - -/* - * Check if chip width is defined. If not, start detecting with 8bit. - */ -#ifndef CFG_FLASH_CFI_WIDTH -#define CFG_FLASH_CFI_WIDTH FLASH_CFI_8BIT -#endif - - -/*----------------------------------------------------------------------- - * Functions - */ - -typedef unsigned long flash_sect_t; - -static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c); -static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf); -static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect); -static int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -static void flash_read_jedec_ids (flash_info_t * info); -static int flash_detect_cfi (flash_info_t * info); -static int flash_write_cfiword (flash_info_t * info, ulong dest, cfiword_t cword); -static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, - ulong tout, char *prompt); -ulong flash_get_size (ulong base, int banknum); -#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE) -static flash_info_t *flash_get_info(ulong base); -#endif -#ifdef CFG_FLASH_USE_BUFFER_WRITE -static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, int len); -#endif - -/*----------------------------------------------------------------------- - * create an address based on the offset and the port width - */ -inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, uint offset) -{ - return ((uchar *) (info->start[sect] + (offset * info->portwidth))); -} - -#ifdef DEBUG -/*----------------------------------------------------------------------- - * Debug support - */ -void print_longlong (char *str, unsigned long long data) -{ - int i; - char *cp; - - cp = (unsigned char *) &data; - for (i = 0; i < 8; i++) - sprintf (&str[i * 2], "%2.2x", *cp++); -} -static void flash_printqry (flash_info_t * info, flash_sect_t sect) -{ - cfiptr_t cptr; - int x, y; - - for (x = 0; x < 0x40; x += 16U / info->portwidth) { - cptr.cp = - flash_make_addr (info, sect, - x + FLASH_OFFSET_CFI_RESP); - debug ("%p : ", cptr.cp); - for (y = 0; y < 16; y++) { - debug ("%2.2x ", cptr.cp[y]); - } - debug (" "); - for (y = 0; y < 16; y++) { - if (cptr.cp[y] >= 0x20 && cptr.cp[y] <= 0x7e) { - debug ("%c", cptr.cp[y]); - } else { - debug ("."); - } - } - debug ("\n"); - } -} -#endif - - -/*----------------------------------------------------------------------- - * read a character at a port width address - */ -inline uchar flash_read_uchar (flash_info_t * info, uint offset) -{ - uchar *cp; - - cp = flash_make_addr (info, 0, offset); -#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - return (cp[0]); -#else - return (cp[info->portwidth - 1]); -#endif -} - -/*----------------------------------------------------------------------- - * read a short word by swapping for ppc format. - */ -ushort flash_read_ushort (flash_info_t * info, flash_sect_t sect, uint offset) -{ - uchar *addr; - ushort retval; - -#ifdef DEBUG - int x; -#endif - addr = flash_make_addr (info, sect, offset); - -#ifdef DEBUG - debug ("ushort addr is at %p info->portwidth = %d\n", addr, - info->portwidth); - for (x = 0; x < 2 * info->portwidth; x++) { - debug ("addr[%x] = 0x%x\n", x, addr[x]); - } -#endif -#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - retval = ((addr[(info->portwidth)] << 8) | addr[0]); -#else - retval = ((addr[(2 * info->portwidth) - 1] << 8) | - addr[info->portwidth - 1]); -#endif - - debug ("retval = 0x%x\n", retval); - return retval; -} - -/*----------------------------------------------------------------------- - * read a long word by picking the least significant byte of each maximum - * port size word. Swap for ppc format. - */ -ulong flash_read_long (flash_info_t * info, flash_sect_t sect, uint offset) -{ - uchar *addr; - ulong retval; - -#ifdef DEBUG - int x; -#endif - addr = flash_make_addr (info, sect, offset); - -#ifdef DEBUG - debug ("long addr is at %p info->portwidth = %d\n", addr, - info->portwidth); - for (x = 0; x < 4 * info->portwidth; x++) { - debug ("addr[%x] = 0x%x\n", x, addr[x]); - } -#endif -#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - retval = (addr[0] << 16) | (addr[(info->portwidth)] << 24) | - (addr[(2 * info->portwidth)]) | (addr[(3 * info->portwidth)] << 8); -#else - retval = (addr[(2 * info->portwidth) - 1] << 24) | - (addr[(info->portwidth) - 1] << 16) | - (addr[(4 * info->portwidth) - 1] << 8) | - addr[(3 * info->portwidth) - 1]; -#endif - return retval; -} - - -/*----------------------------------------------------------------------- - */ -unsigned long flash_init (void) -{ - unsigned long size = 0; - int i; - -#ifdef CFG_FLASH_PROTECTION - char *s = getenv("unlock"); -#endif - - /* Init: no FLASHes known */ - for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { - flash_info[i].flash_id = FLASH_UNKNOWN; - size += flash_info[i].size = flash_get_size (bank_base[i], i); - if (flash_info[i].flash_id == FLASH_UNKNOWN) { -#ifndef CFG_FLASH_QUIET_TEST - printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", - i+1, flash_info[i].size, flash_info[i].size << 20); -#endif /* CFG_FLASH_QUIET_TEST */ - } -#ifdef CFG_FLASH_PROTECTION - else if ((s != NULL) && (strcmp(s, "yes") == 0)) { - /* - * Only the U-Boot image and it's environment is protected, - * all other sectors are unprotected (unlocked) if flash - * hardware protection is used (CFG_FLASH_PROTECTION) and - * the environment variable "unlock" is set to "yes". - */ - if (flash_info[i].legacy_unlock) { - int k; - - /* - * Disable legacy_unlock temporarily, since - * flash_real_protect would relock all other sectors - * again otherwise. - */ - flash_info[i].legacy_unlock = 0; - - /* - * Legacy unlocking (e.g. Intel J3) -> unlock only one - * sector. This will unlock all sectors. - */ - flash_real_protect (&flash_info[i], 0, 0); - - flash_info[i].legacy_unlock = 1; - - /* - * Manually mark other sectors as unlocked (unprotected) - */ - for (k = 1; k < flash_info[i].sector_count; k++) - flash_info[i].protect[k] = 0; - } else { - /* - * No legancy unlocking -> unlock all sectors - */ - flash_protect (FLAG_PROTECT_CLEAR, - flash_info[i].start[0], - flash_info[i].start[0] + flash_info[i].size - 1, - &flash_info[i]); - } - } -#endif /* CFG_FLASH_PROTECTION */ - } - - /* Monitor protection ON by default */ -#if (CFG_MONITOR_BASE >= CFG_FLASH_BASE) - flash_protect (FLAG_PROTECT_SET, - CFG_MONITOR_BASE, - CFG_MONITOR_BASE + monitor_flash_len - 1, - flash_get_info(CFG_MONITOR_BASE)); -#endif - - /* Environment protection ON by default */ -#ifdef CFG_ENV_IS_IN_FLASH - flash_protect (FLAG_PROTECT_SET, - CFG_ENV_ADDR, - CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1, - flash_get_info(CFG_ENV_ADDR)); -#endif - - /* Redundant environment protection ON by default */ -#ifdef CFG_ENV_ADDR_REDUND - flash_protect (FLAG_PROTECT_SET, - CFG_ENV_ADDR_REDUND, - CFG_ENV_ADDR_REDUND + CFG_ENV_SIZE_REDUND - 1, - flash_get_info(CFG_ENV_ADDR_REDUND)); -#endif - return (size); -} - -/*----------------------------------------------------------------------- - */ -#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE) -static flash_info_t *flash_get_info(ulong base) -{ - int i; - flash_info_t * info = 0; - - for (i = 0; i < CFG_MAX_FLASH_BANKS; i ++) { - info = & flash_info[i]; - if (info->size && info->start[0] <= base && - base <= info->start[0] + info->size - 1) - break; - } - - return i == CFG_MAX_FLASH_BANKS ? 0 : info; -} -#endif - -/*----------------------------------------------------------------------- - */ -int flash_erase (flash_info_t * info, int s_first, int s_last) -{ - int rcode = 0; - int prot; - flash_sect_t sect; - - if (info->flash_id != FLASH_MAN_CFI) { - puts ("Can't erase unknown flash type - aborted\n"); - return 1; - } - if ((s_first < 0) || (s_first > s_last)) { - puts ("- no sectors to erase\n"); - return 1; - } - - prot = 0; - for (sect = s_first; sect <= s_last; ++sect) { - if (info->protect[sect]) { - prot++; - } - } - if (prot) { - printf ("- Warning: %d protected sectors will not be erased!\n", prot); - } else { - putc ('\n'); - } - - - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - case CFI_CMDSET_INTEL_EXTENDED: - flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE); - flash_write_cmd (info, sect, 0, FLASH_CMD_ERASE_CONFIRM); - break; - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - flash_unlock_seq (info, sect); - flash_write_cmd (info, sect, AMD_ADDR_ERASE_START, - AMD_CMD_ERASE_START); - flash_unlock_seq (info, sect); - flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR); - break; - default: - debug ("Unkown flash vendor %d\n", - info->vendor); - break; - } - - if (flash_full_status_check - (info, sect, info->erase_blk_tout, "erase")) { - rcode = 1; - } else - putc ('.'); - } - } - puts (" done\n"); - return rcode; -} - -/*----------------------------------------------------------------------- - */ -void flash_print_info (flash_info_t * info) -{ - int i; - - if (info->flash_id != FLASH_MAN_CFI) { - puts ("missing or unknown FLASH type\n"); - return; - } - - printf ("CFI conformant FLASH (%d x %d)", - (info->portwidth << 3), (info->chipwidth << 3)); - printf (" Size: %ld MB in %d Sectors\n", - info->size >> 20, info->sector_count); - printf (" "); - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - printf ("Intel Standard"); - break; - case CFI_CMDSET_INTEL_EXTENDED: - printf ("Intel Extended"); - break; - case CFI_CMDSET_AMD_STANDARD: - printf ("AMD Standard"); - break; - case CFI_CMDSET_AMD_EXTENDED: - printf ("AMD Extended"); - break; - default: - printf ("Unknown (%d)", info->vendor); - break; - } - printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X", - info->manufacturer_id, info->device_id); - if (info->device_id == 0x7E) { - printf("%04X", info->device_id2); - } - printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n", - info->erase_blk_tout, - info->write_tout); - if (info->buffer_size > 1) { - printf (" Buffer write timeout: %ld ms, buffer size: %d bytes\n", - info->buffer_write_tout, - info->buffer_size); - } - - puts ("\n Sector Start Addresses:"); - for (i = 0; i < info->sector_count; ++i) { - if ((i % 5) == 0) - printf ("\n"); -#ifdef CFG_FLASH_EMPTY_INFO - int k; - int size; - int erased; - volatile unsigned long *flash; - - /* - * Check if whole sector is erased - */ - if (i != (info->sector_count - 1)) - size = info->start[i + 1] - info->start[i]; - else - size = info->start[0] + info->size - info->start[i]; - erased = 1; - flash = (volatile unsigned long *) info->start[i]; - size = size >> 2; /* divide by 4 for longword access */ - for (k = 0; k < size; k++) { - if (*flash++ != 0xffffffff) { - erased = 0; - break; - } - } - - /* print empty and read-only info */ - printf (" %08lX %c %s ", - info->start[i], - erased ? 'E' : ' ', - info->protect[i] ? "RO" : " "); -#else /* ! CFG_FLASH_EMPTY_INFO */ - printf (" %08lX %s ", - info->start[i], - info->protect[i] ? "RO" : " "); -#endif - } - putc ('\n'); - return; -} - -/*----------------------------------------------------------------------- - * Copy memory to flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) -{ - ulong wp; - ulong cp; - int aln; - cfiword_t cword; - int i, rc; - -#ifdef CFG_FLASH_USE_BUFFER_WRITE - int buffered_size; -#endif - /* get lower aligned address */ - /* get lower aligned address */ - wp = (addr & ~(info->portwidth - 1)); - - /* handle unaligned start */ - if ((aln = addr - wp) != 0) { - cword.l = 0; - cp = wp; - for (i = 0; i < aln; ++i, ++cp) - flash_add_byte (info, &cword, (*(uchar *) cp)); - - for (; (i < info->portwidth) && (cnt > 0); i++) { - flash_add_byte (info, &cword, *src++); - cnt--; - cp++; - } - for (; (cnt == 0) && (i < info->portwidth); ++i, ++cp) - flash_add_byte (info, &cword, (*(uchar *) cp)); - if ((rc = flash_write_cfiword (info, wp, cword)) != 0) - return rc; - wp = cp; - } - - /* handle the aligned part */ -#ifdef CFG_FLASH_USE_BUFFER_WRITE - buffered_size = (info->portwidth / info->chipwidth); - buffered_size *= info->buffer_size; - while (cnt >= info->portwidth) { - /* prohibit buffer write when buffer_size is 1 */ - if (info->buffer_size == 1) { - cword.l = 0; - for (i = 0; i < info->portwidth; i++) - flash_add_byte (info, &cword, *src++); - if ((rc = flash_write_cfiword (info, wp, cword)) != 0) - return rc; - wp += info->portwidth; - cnt -= info->portwidth; - continue; - } - - /* write buffer until next buffered_size aligned boundary */ - i = buffered_size - (wp % buffered_size); - if (i > cnt) - i = cnt; - if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) - return rc; - i -= i & (info->portwidth - 1); - wp += i; - src += i; - cnt -= i; - } -#else - while (cnt >= info->portwidth) { - cword.l = 0; - for (i = 0; i < info->portwidth; i++) { - flash_add_byte (info, &cword, *src++); - } - if ((rc = flash_write_cfiword (info, wp, cword)) != 0) - return rc; - wp += info->portwidth; - cnt -= info->portwidth; - } -#endif /* CFG_FLASH_USE_BUFFER_WRITE */ - if (cnt == 0) { - return (0); - } - - /* - * handle unaligned tail bytes - */ - cword.l = 0; - for (i = 0, cp = wp; (i < info->portwidth) && (cnt > 0); ++i, ++cp) { - flash_add_byte (info, &cword, *src++); - --cnt; - } - for (; i < info->portwidth; ++i, ++cp) { - flash_add_byte (info, &cword, (*(uchar *) cp)); - } - - return flash_write_cfiword (info, wp, cword); -} - -/*----------------------------------------------------------------------- - */ -#ifdef CFG_FLASH_PROTECTION - -int flash_real_protect (flash_info_t * info, long sector, int prot) -{ - int retcode = 0; - - flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); - if (prot) - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); - else - flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); - - if ((retcode = - flash_full_status_check (info, sector, info->erase_blk_tout, - prot ? "protect" : "unprotect")) == 0) { - - info->protect[sector] = prot; - - /* - * On some of Intel's flash chips (marked via legacy_unlock) - * unprotect unprotects all locking. - */ - if ((prot == 0) && (info->legacy_unlock)) { - flash_sect_t i; - - for (i = 0; i < info->sector_count; i++) { - if (info->protect[i]) - flash_real_protect (info, i, 1); - } - } - } - return retcode; -} - -/*----------------------------------------------------------------------- - * flash_read_user_serial - read the OneTimeProgramming cells - */ -void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, - int len) -{ - uchar *src; - uchar *dst; - - dst = buffer; - src = flash_make_addr (info, 0, FLASH_OFFSET_USER_PROTECTION); - flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); - memcpy (dst, src + offset, len); - flash_write_cmd (info, 0, 0, info->cmd_reset); -} - -/* - * flash_read_factory_serial - read the device Id from the protection area - */ -void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, - int len) -{ - uchar *src; - - src = flash_make_addr (info, 0, FLASH_OFFSET_INTEL_PROTECTION); - flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); - memcpy (buffer, src + offset, len); - flash_write_cmd (info, 0, 0, info->cmd_reset); -} - -#endif /* CFG_FLASH_PROTECTION */ - -/* - * flash_is_busy - check to see if the flash is busy - * This routine checks the status of the chip and returns true if the chip is busy - */ -static int flash_is_busy (flash_info_t * info, flash_sect_t sect) -{ - int retval; - - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - case CFI_CMDSET_INTEL_EXTENDED: - retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); - break; - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); - break; - default: - retval = 0; - } - debug ("flash_is_busy: %d\n", retval); - return retval; -} - -/*----------------------------------------------------------------------- - * wait for XSR.7 to be set. Time out with an error if it does not. - * This routine does not set the flash to read-array mode. - */ -static int flash_status_check (flash_info_t * info, flash_sect_t sector, - ulong tout, char *prompt) -{ - ulong start; - -#if CFG_HZ != 1000 - tout *= CFG_HZ/1000; -#endif - - /* Wait for command completion */ - start = get_timer (0); - while (flash_is_busy (info, sector)) { - if (get_timer (start) > tout) { - printf ("Flash %s timeout at address %lx data %lx\n", - prompt, info->start[sector], - flash_read_long (info, sector, 0)); - flash_write_cmd (info, sector, 0, info->cmd_reset); - return ERR_TIMOUT; - } - udelay (1); /* also triggers watchdog */ - } - return ERR_OK; -} - -/*----------------------------------------------------------------------- - * Wait for XSR.7 to be set, if it times out print an error, otherwise do a full status check. - * This routine sets the flash to read-array mode. - */ -static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, - ulong tout, char *prompt) -{ - int retcode; - - retcode = flash_status_check (info, sector, tout, prompt); - switch (info->vendor) { - case CFI_CMDSET_INTEL_EXTENDED: - case CFI_CMDSET_INTEL_STANDARD: - if ((retcode == ERR_OK) - && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { - retcode = ERR_INVAL; - printf ("Flash %s error at address %lx\n", prompt, - info->start[sector]); - if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { - puts ("Command Sequence Error.\n"); - } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { - puts ("Block Erase Error.\n"); - retcode = ERR_NOT_ERASED; - } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) { - puts ("Locking Error\n"); - } - if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { - puts ("Block locked.\n"); - retcode = ERR_PROTECTED; - } - if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) - puts ("Vpp Low Error.\n"); - } - flash_write_cmd (info, sector, 0, info->cmd_reset); - break; - default: - break; - } - return retcode; -} - -/*----------------------------------------------------------------------- - */ -static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) -{ -#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) - unsigned short w; - unsigned int l; - unsigned long long ll; -#endif - - switch (info->portwidth) { - case FLASH_CFI_8BIT: - cword->c = c; - break; - case FLASH_CFI_16BIT: -#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) - w = c; - w <<= 8; - cword->w = (cword->w >> 8) | w; -#else - cword->w = (cword->w << 8) | c; -#endif - break; - case FLASH_CFI_32BIT: -#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) - l = c; - l <<= 24; - cword->l = (cword->l >> 8) | l; -#else - cword->l = (cword->l << 8) | c; -#endif - break; - case FLASH_CFI_64BIT: -#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) - ll = c; - ll <<= 56; - cword->ll = (cword->ll >> 8) | ll; -#else - cword->ll = (cword->ll << 8) | c; -#endif - break; - } -} - - -/*----------------------------------------------------------------------- - * make a proper sized command based on the port and chip widths - */ -static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) -{ - int i; - uchar *cp = (uchar *) cmdbuf; - -#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) - for (i = info->portwidth; i > 0; i--) -#else - for (i = 1; i <= info->portwidth; i++) -#endif - *cp++ = (i & (info->chipwidth - 1)) ? '\0' : cmd; -} - -/* - * Write a proper sized command to the correct address - */ -static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) -{ - - volatile cfiptr_t addr; - cfiword_t cword; - - addr.cp = flash_make_addr (info, sect, offset); - flash_make_cmd (info, cmd, &cword); - switch (info->portwidth) { - case FLASH_CFI_8BIT: - debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr.cp, cmd, - cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - *addr.cp = cword.c; - break; - case FLASH_CFI_16BIT: - debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr.wp, - cmd, cword.w, - info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - *addr.wp = cword.w; - break; - case FLASH_CFI_32BIT: - debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr.lp, - cmd, cword.l, - info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - *addr.lp = cword.l; - break; - case FLASH_CFI_64BIT: -#ifdef DEBUG - { - char str[20]; - - print_longlong (str, cword.ll); - - debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n", - addr.llp, cmd, str, - info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - } -#endif - *addr.llp = cword.ll; - break; - } - - /* Ensure all the instructions are fully finished */ - sync(); -} - -static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect) -{ - flash_write_cmd (info, sect, AMD_ADDR_START, AMD_CMD_UNLOCK_START); - flash_write_cmd (info, sect, AMD_ADDR_ACK, AMD_CMD_UNLOCK_ACK); -} - -/*----------------------------------------------------------------------- - */ -static int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) -{ - cfiptr_t cptr; - cfiword_t cword; - int retval; - - cptr.cp = flash_make_addr (info, sect, offset); - flash_make_cmd (info, cmd, &cword); - - debug ("is= cmd %x(%c) addr %p ", cmd, cmd, cptr.cp); - switch (info->portwidth) { - case FLASH_CFI_8BIT: - debug ("is= %x %x\n", cptr.cp[0], cword.c); - retval = (cptr.cp[0] == cword.c); - break; - case FLASH_CFI_16BIT: - debug ("is= %4.4x %4.4x\n", cptr.wp[0], cword.w); - retval = (cptr.wp[0] == cword.w); - break; - case FLASH_CFI_32BIT: - debug ("is= %8.8lx %8.8lx\n", cptr.lp[0], cword.l); - retval = (cptr.lp[0] == cword.l); - break; - case FLASH_CFI_64BIT: -#ifdef DEBUG - { - char str1[20]; - char str2[20]; - - print_longlong (str1, cptr.llp[0]); - print_longlong (str2, cword.ll); - debug ("is= %s %s\n", str1, str2); - } -#endif - retval = (cptr.llp[0] == cword.ll); - break; - default: - retval = 0; - break; - } - return retval; -} - -/*----------------------------------------------------------------------- - */ -static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) -{ - cfiptr_t cptr; - cfiword_t cword; - int retval; - - cptr.cp = flash_make_addr (info, sect, offset); - flash_make_cmd (info, cmd, &cword); - switch (info->portwidth) { - case FLASH_CFI_8BIT: - retval = ((cptr.cp[0] & cword.c) == cword.c); - break; - case FLASH_CFI_16BIT: - retval = ((cptr.wp[0] & cword.w) == cword.w); - break; - case FLASH_CFI_32BIT: - retval = ((cptr.lp[0] & cword.l) == cword.l); - break; - case FLASH_CFI_64BIT: - retval = ((cptr.llp[0] & cword.ll) == cword.ll); - break; - default: - retval = 0; - break; - } - return retval; -} - -/*----------------------------------------------------------------------- - */ -static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) -{ - cfiptr_t cptr; - cfiword_t cword; - int retval; - - cptr.cp = flash_make_addr (info, sect, offset); - flash_make_cmd (info, cmd, &cword); - switch (info->portwidth) { - case FLASH_CFI_8BIT: - retval = ((cptr.cp[0] & cword.c) != (cptr.cp[0] & cword.c)); - break; - case FLASH_CFI_16BIT: - retval = ((cptr.wp[0] & cword.w) != (cptr.wp[0] & cword.w)); - break; - case FLASH_CFI_32BIT: - retval = ((cptr.lp[0] & cword.l) != (cptr.lp[0] & cword.l)); - break; - case FLASH_CFI_64BIT: - retval = ((cptr.llp[0] & cword.ll) != - (cptr.llp[0] & cword.ll)); - break; - default: - retval = 0; - break; - } - return retval; -} - -/*----------------------------------------------------------------------- - * read jedec ids from device and set corresponding fields in info struct - * - * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct - * -*/ -static void flash_read_jedec_ids (flash_info_t * info) -{ - info->manufacturer_id = 0; - info->device_id = 0; - info->device_id2 = 0; - - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - case CFI_CMDSET_INTEL_EXTENDED: - flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); - flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID); - udelay(1000); /* some flash are slow to respond */ - info->manufacturer_id = flash_read_uchar (info, - FLASH_OFFSET_MANUFACTURER_ID); - info->device_id = flash_read_uchar (info, - FLASH_OFFSET_DEVICE_ID); - flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); - break; - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - flash_write_cmd(info, 0, 0, AMD_CMD_RESET); - flash_unlock_seq(info, 0); - flash_write_cmd(info, 0, AMD_ADDR_START, FLASH_CMD_READ_ID); - udelay(1000); /* some flash are slow to respond */ - info->manufacturer_id = flash_read_uchar (info, - FLASH_OFFSET_MANUFACTURER_ID); - info->device_id = flash_read_uchar (info, - FLASH_OFFSET_DEVICE_ID); - if (info->device_id == 0x7E) { - /* AMD 3-byte (expanded) device ids */ - info->device_id2 = flash_read_uchar (info, - FLASH_OFFSET_DEVICE_ID2); - info->device_id2 <<= 8; - info->device_id2 |= flash_read_uchar (info, - FLASH_OFFSET_DEVICE_ID3); - } - flash_write_cmd(info, 0, 0, AMD_CMD_RESET); - break; - default: - break; - } -} - -/*----------------------------------------------------------------------- - * detect if flash is compatible with the Common Flash Interface (CFI) - * http://www.jedec.org/download/search/jesd68.pdf - * -*/ -static int flash_detect_cfi (flash_info_t * info) -{ - int cfi_offset; - debug ("flash detect cfi\n"); - - for (info->portwidth = CFG_FLASH_CFI_WIDTH; - info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { - for (info->chipwidth = FLASH_CFI_BY8; - info->chipwidth <= info->portwidth; - info->chipwidth <<= 1) { - flash_write_cmd (info, 0, 0, info->cmd_reset); - for (cfi_offset=0; cfi_offset < sizeof(flash_offset_cfi)/sizeof(uint); cfi_offset++) { - flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], FLASH_CMD_CFI); - if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') - && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') - && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { - info->interface = flash_read_ushort (info, 0, FLASH_OFFSET_INTERFACE); - info->cfi_offset=flash_offset_cfi[cfi_offset]; - debug ("device interface is %d\n", - info->interface); - debug ("found port %d chip %d ", - info->portwidth, info->chipwidth); - debug ("port %d bits chip %d bits\n", - info->portwidth << CFI_FLASH_SHIFT_WIDTH, - info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - return 1; - } - } - } - } - debug ("not found\n"); - return 0; -} - -/* - * The following code cannot be run from FLASH! - * - */ -ulong flash_get_size (ulong base, int banknum) -{ - flash_info_t *info = &flash_info[banknum]; - int i, j; - flash_sect_t sect_cnt; - unsigned long sector; - unsigned long tmp; - int size_ratio; - uchar num_erase_regions; - int erase_region_size; - int erase_region_count; - int geometry_reversed = 0; - - info->ext_addr = 0; - info->cfi_version = 0; -#ifdef CFG_FLASH_PROTECTION - info->legacy_unlock = 0; -#endif - - info->start[0] = base; - - if (flash_detect_cfi (info)) { - info->vendor = flash_read_ushort (info, 0, - FLASH_OFFSET_PRIMARY_VENDOR); - flash_read_jedec_ids (info); - flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); - num_erase_regions = flash_read_uchar (info, - FLASH_OFFSET_NUM_ERASE_REGIONS); - info->ext_addr = flash_read_ushort (info, 0, - FLASH_OFFSET_EXT_QUERY_T_P_ADDR); - if (info->ext_addr) { - info->cfi_version = (ushort) flash_read_uchar (info, - info->ext_addr + 3) << 8; - info->cfi_version |= (ushort) flash_read_uchar (info, - info->ext_addr + 4); - } -#ifdef DEBUG - flash_printqry (info, 0); -#endif - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - case CFI_CMDSET_INTEL_EXTENDED: - default: - info->cmd_reset = FLASH_CMD_RESET; -#ifdef CFG_FLASH_PROTECTION - /* read legacy lock/unlock bit from intel flash */ - if (info->ext_addr) { - info->legacy_unlock = flash_read_uchar (info, - info->ext_addr + 5) & 0x08; - } -#endif - break; - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - info->cmd_reset = AMD_CMD_RESET; - /* check if flash geometry needs reversal */ - if (num_erase_regions <= 1) - break; - /* reverse geometry if top boot part */ - if (info->cfi_version < 0x3131) { - /* CFI < 1.1, try to guess from device id */ - if ((info->device_id & 0x80) != 0) { - geometry_reversed = 1; - } - break; - } - /* CFI >= 1.1, deduct from top/bottom flag */ - /* note: ext_addr is valid since cfi_version > 0 */ - if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { - geometry_reversed = 1; - } - break; - } - - debug ("manufacturer is %d\n", info->vendor); - debug ("manufacturer id is 0x%x\n", info->manufacturer_id); - debug ("device id is 0x%x\n", info->device_id); - debug ("device id2 is 0x%x\n", info->device_id2); - debug ("cfi version is 0x%04x\n", info->cfi_version); - - size_ratio = info->portwidth / info->chipwidth; - /* if the chip is x8/x16 reduce the ratio by half */ - if ((info->interface == FLASH_CFI_X8X16) - && (info->chipwidth == FLASH_CFI_BY8)) { - size_ratio >>= 1; - } - debug ("size_ratio %d port %d bits chip %d bits\n", - size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, - info->chipwidth << CFI_FLASH_SHIFT_WIDTH); - debug ("found %d erase regions\n", num_erase_regions); - sect_cnt = 0; - sector = base; - for (i = 0; i < num_erase_regions; i++) { - if (i > NUM_ERASE_REGIONS) { - printf ("%d erase regions found, only %d used\n", - num_erase_regions, NUM_ERASE_REGIONS); - break; - } - if (geometry_reversed) - tmp = flash_read_long (info, 0, - FLASH_OFFSET_ERASE_REGIONS + - (num_erase_regions - 1 - i) * 4); - else - tmp = flash_read_long (info, 0, - FLASH_OFFSET_ERASE_REGIONS + - i * 4); - erase_region_size = - (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; - tmp >>= 16; - erase_region_count = (tmp & 0xffff) + 1; - debug ("erase_region_count = %d erase_region_size = %d\n", - erase_region_count, erase_region_size); - for (j = 0; j < erase_region_count; j++) { - info->start[sect_cnt] = sector; - sector += (erase_region_size * size_ratio); - - /* - * Only read protection status from supported devices (intel...) - */ - switch (info->vendor) { - case CFI_CMDSET_INTEL_EXTENDED: - case CFI_CMDSET_INTEL_STANDARD: - info->protect[sect_cnt] = - flash_isset (info, sect_cnt, - FLASH_OFFSET_PROTECT, - FLASH_STATUS_PROTECT); - break; - default: - info->protect[sect_cnt] = 0; /* default: not protected */ - } - - sect_cnt++; - } - } - - info->sector_count = sect_cnt; - /* multiply the size by the number of chips */ - info->size = (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * size_ratio; - info->buffer_size = (1 << flash_read_ushort (info, 0, FLASH_OFFSET_BUFFER_SIZE)); - tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT); - info->erase_blk_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT))); - tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT)) * - (1 << flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT)); - info->buffer_write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */ - tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT)) * - (1 << flash_read_uchar (info, FLASH_OFFSET_WMAX_TOUT)); - info->write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */ - info->flash_id = FLASH_MAN_CFI; - if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) { - info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */ - } - } - - flash_write_cmd (info, 0, 0, info->cmd_reset); - return (info->size); -} - -/* loop through the sectors from the highest address - * when the passed address is greater or equal to the sector address - * we have a match - */ -static flash_sect_t find_sector (flash_info_t * info, ulong addr) -{ - flash_sect_t sector; - - for (sector = info->sector_count - 1; sector >= 0; sector--) { - if (addr >= info->start[sector]) - break; - } - return sector; -} - -/*----------------------------------------------------------------------- - */ -static int flash_write_cfiword (flash_info_t * info, ulong dest, - cfiword_t cword) -{ - cfiptr_t ctladdr; - cfiptr_t cptr; - int flag; - - ctladdr.cp = flash_make_addr (info, 0, 0); - cptr.cp = (uchar *) dest; - - /* Check if Flash is (sufficiently) erased */ - switch (info->portwidth) { - case FLASH_CFI_8BIT: - flag = ((cptr.cp[0] & cword.c) == cword.c); - break; - case FLASH_CFI_16BIT: - flag = ((cptr.wp[0] & cword.w) == cword.w); - break; - case FLASH_CFI_32BIT: - flag = ((cptr.lp[0] & cword.l) == cword.l); - break; - case FLASH_CFI_64BIT: - flag = ((cptr.llp[0] & cword.ll) == cword.ll); - break; - default: - return 2; - } - if (!flag) - return 2; - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts (); - - switch (info->vendor) { - case CFI_CMDSET_INTEL_EXTENDED: - case CFI_CMDSET_INTEL_STANDARD: - flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); - break; - case CFI_CMDSET_AMD_EXTENDED: - case CFI_CMDSET_AMD_STANDARD: - flash_unlock_seq (info, 0); - flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE); - break; - } - - switch (info->portwidth) { - case FLASH_CFI_8BIT: - cptr.cp[0] = cword.c; - break; - case FLASH_CFI_16BIT: - cptr.wp[0] = cword.w; - break; - case FLASH_CFI_32BIT: - cptr.lp[0] = cword.l; - break; - case FLASH_CFI_64BIT: - cptr.llp[0] = cword.ll; - break; - } - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts (); - - return flash_full_status_check (info, find_sector (info, dest), - info->write_tout, "write"); -} - -#ifdef CFG_FLASH_USE_BUFFER_WRITE - -static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, - int len) -{ - flash_sect_t sector; - int cnt; - int retcode; - volatile cfiptr_t src; - volatile cfiptr_t dst; - - switch (info->vendor) { - case CFI_CMDSET_INTEL_STANDARD: - case CFI_CMDSET_INTEL_EXTENDED: - src.cp = cp; - dst.cp = (uchar *) dest; - sector = find_sector (info, dest); - flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); - flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); - if ((retcode = flash_status_check (info, sector, info->buffer_write_tout, - "write to buffer")) == ERR_OK) { - /* reduce the number of loops by the width of the port */ - switch (info->portwidth) { - case FLASH_CFI_8BIT: - cnt = len; - break; - case FLASH_CFI_16BIT: - cnt = len >> 1; - break; - case FLASH_CFI_32BIT: - cnt = len >> 2; - break; - case FLASH_CFI_64BIT: - cnt = len >> 3; - break; - default: - return ERR_INVAL; - break; - } - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); - while (cnt-- > 0) { - switch (info->portwidth) { - case FLASH_CFI_8BIT: - *dst.cp++ = *src.cp++; - break; - case FLASH_CFI_16BIT: - *dst.wp++ = *src.wp++; - break; - case FLASH_CFI_32BIT: - *dst.lp++ = *src.lp++; - break; - case FLASH_CFI_64BIT: - *dst.llp++ = *src.llp++; - break; - default: - return ERR_INVAL; - break; - } - } - flash_write_cmd (info, sector, 0, - FLASH_CMD_WRITE_BUFFER_CONFIRM); - retcode = flash_full_status_check (info, sector, - info->buffer_write_tout, - "buffer write"); - } - return retcode; - - case CFI_CMDSET_AMD_STANDARD: - case CFI_CMDSET_AMD_EXTENDED: - src.cp = cp; - dst.cp = (uchar *) dest; - sector = find_sector (info, dest); - - flash_unlock_seq(info,0); - flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_TO_BUFFER); - - switch (info->portwidth) { - case FLASH_CFI_8BIT: - cnt = len; - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); - while (cnt-- > 0) *dst.cp++ = *src.cp++; - break; - case FLASH_CFI_16BIT: - cnt = len >> 1; - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); - while (cnt-- > 0) *dst.wp++ = *src.wp++; - break; - case FLASH_CFI_32BIT: - cnt = len >> 2; - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); - while (cnt-- > 0) *dst.lp++ = *src.lp++; - break; - case FLASH_CFI_64BIT: - cnt = len >> 3; - flash_write_cmd (info, sector, 0, (uchar) cnt - 1); - while (cnt-- > 0) *dst.llp++ = *src.llp++; - break; - default: - return ERR_INVAL; - } - - flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); - retcode = flash_full_status_check (info, sector, info->buffer_write_tout, - "buffer write"); - return retcode; - - default: - debug ("Unknown Command Set\n"); - return ERR_INVAL; - } -} -#endif /* CFG_FLASH_USE_BUFFER_WRITE */ - -#endif /* CFG_FLASH_CFI */ diff --git a/drivers/dataflash.c b/drivers/dataflash.c deleted file mode 100644 index 91903c8c8f..0000000000 --- a/drivers/dataflash.c +++ /dev/null @@ -1,507 +0,0 @@ -/* LowLevel function for ATMEL DataFlash support - * Author : Hamid Ikdoumi (Atmel) - * - * 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 -#include -#ifdef CONFIG_HAS_DATAFLASH -#include -#include - -AT91S_DATAFLASH_INFO dataflash_info[CFG_MAX_DATAFLASH_BANKS]; -static AT91S_DataFlash DataFlashInst; - -#ifdef CONFIG_AT91SAM9260EK -int cs[][CFG_MAX_DATAFLASH_BANKS] = { - {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0}, /* Logical adress, CS */ - {CFG_DATAFLASH_LOGIC_ADDR_CS1, 1} -}; -#elif defined(CONFIG_AT91SAM9263EK) -int cs[][CFG_MAX_DATAFLASH_BANKS] = { - {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0} /* Logical adress, CS */ -}; -#else -int cs[][CFG_MAX_DATAFLASH_BANKS] = { - {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0}, /* Logical adress, CS */ - {CFG_DATAFLASH_LOGIC_ADDR_CS3, 3} -}; -#endif - -/*define the area offsets*/ -#if defined(CONFIG_AT91SAM9261EK) || defined(CONFIG_AT91SAM9260EK) || defined(CONFIG_AT91SAM9263EK) -#if defined(CONFIG_NEW_PARTITION) -dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { - {0x00000000, 0x00003FFF, FLAG_PROTECT_SET, 0, "Bootstrap"}, /* ROM code */ - {0x00004200, 0x000083FF, FLAG_PROTECT_CLEAR, 0, "Environment"}, /* u-boot environment */ - {0x00008400, 0x0003DDFF, FLAG_PROTECT_SET, 0, "U-Boot"}, /* u-boot code */ - {0x0003DE00, 0x00041FFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "MON"}, /* Room for alternative boot monitor */ - {0x00042000, 0x0018BFFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "OS"}, /* data area size to tune */ - {0x0018C000, 0xFFFFFFFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "FS"}, /* data area size to tune */ -}; -#else -dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { - {0, 0x3fff, FLAG_PROTECT_SET}, /* ROM code */ - {0x4000, 0x7fff, FLAG_PROTECT_CLEAR}, /* u-boot environment */ - {0x8000, 0x37fff, FLAG_PROTECT_SET}, /* u-boot code */ - {0x38000, 0x1fffff, FLAG_PROTECT_CLEAR}, /* data area size to tune */ -}; -#endif -#elif defined(CONFIG_NEW_PARTITION) -/*define the area offsets*/ -/* Invalid partitions should be defined with start > end */ -dataflash_protect_t area_list[NB_DATAFLASH_AREA*CFG_MAX_DATAFLASH_BANKS] = { - {0x00000000, 0x000083ff, FLAG_PROTECT_SET, 0, "Bootstrap"}, /* ROM code */ - {0x00008400, 0x00020fff, FLAG_PROTECT_SET, 0, "U-Boot"}, /* u-boot code */ - {0x00021000, 0x000293ff, FLAG_PROTECT_CLEAR, 0, "Environment"}, /* u-boot environment 8Kb */ - {0x00029400, 0x00041fff, FLAG_PROTECT_INVALID, 0, ""}, /* Rest of Sector 1 */ - {0x00042000, 0x0018Bfff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "OS"}, /* data area size to tune */ - {0x0018C000, 0xffffffff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "FS"}, /* data area size to tune */ - - {0x00000000, 0xffffffff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "Data"}, /* data area */ - {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ - {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ - {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ - {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ - {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ -}; -#else -dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { - {0, 0x7fff, FLAG_PROTECT_SET}, /* ROM code */ - {0x8000, 0x1ffff, FLAG_PROTECT_SET}, /* u-boot code */ - {0x20000, 0x27fff, FLAG_PROTECT_CLEAR}, /* u-boot environment */ - {0x28000, 0x1fffff, FLAG_PROTECT_CLEAR}, /* data area size to tune */ -}; -#endif - -extern void AT91F_SpiInit (void); -extern int AT91F_DataflashProbe (int i, AT91PS_DataflashDesc pDesc); -extern int AT91F_DataFlashRead (AT91PS_DataFlash pDataFlash, - unsigned long addr, - unsigned long size, char *buffer); -extern int AT91F_DataFlashWrite( AT91PS_DataFlash pDataFlash, - unsigned char *src, - int dest, - int size ); - -int AT91F_DataflashInit (void) -{ - int i, j; - int dfcode; - int part = 0; - int last_part; - int found[CFG_MAX_DATAFLASH_BANKS]; - unsigned char protected; - - AT91F_SpiInit (); - - for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { - found[i] = 0; - dataflash_info[i].Desc.state = IDLE; - dataflash_info[i].id = 0; - dataflash_info[i].Device.pages_number = 0; - dfcode = AT91F_DataflashProbe (cs[i][1], - &dataflash_info[i].Desc); - - switch (dfcode) { - case AT45DB161: - dataflash_info[i].Device.pages_number = 4096; - dataflash_info[i].Device.pages_size = 528; - dataflash_info[i].Device.page_offset = 10; - dataflash_info[i].Device.byte_mask = 0x300; - dataflash_info[i].Device.cs = cs[i][1]; - dataflash_info[i].Desc.DataFlash_state = IDLE; - dataflash_info[i].logical_address = cs[i][0]; - dataflash_info[i].id = dfcode; - found[i] += dfcode;; - break; - - case AT45DB321: - dataflash_info[i].Device.pages_number = 8192; - dataflash_info[i].Device.pages_size = 528; - dataflash_info[i].Device.page_offset = 10; - dataflash_info[i].Device.byte_mask = 0x300; - dataflash_info[i].Device.cs = cs[i][1]; - dataflash_info[i].Desc.DataFlash_state = IDLE; - dataflash_info[i].logical_address = cs[i][0]; - dataflash_info[i].id = dfcode; - found[i] += dfcode;; - break; - - case AT45DB642: - dataflash_info[i].Device.pages_number = 8192; - dataflash_info[i].Device.pages_size = 1056; - dataflash_info[i].Device.page_offset = 11; - dataflash_info[i].Device.byte_mask = 0x700; - dataflash_info[i].Device.cs = cs[i][1]; - dataflash_info[i].Desc.DataFlash_state = IDLE; - dataflash_info[i].logical_address = cs[i][0]; - dataflash_info[i].id = dfcode; - found[i] += dfcode;; - break; - - case AT45DB128: - dataflash_info[i].Device.pages_number = 16384; - dataflash_info[i].Device.pages_size = 1056; - dataflash_info[i].Device.page_offset = 11; - dataflash_info[i].Device.byte_mask = 0x700; - dataflash_info[i].Device.cs = cs[i][1]; - dataflash_info[i].Desc.DataFlash_state = IDLE; - dataflash_info[i].logical_address = cs[i][0]; - dataflash_info[i].id = dfcode; - found[i] += dfcode;; - break; - - default: - dfcode = 0; - break; - } - /* set the last area end to the dataflash size*/ - area_list[NB_DATAFLASH_AREA -1].end = - (dataflash_info[i].Device.pages_number * - dataflash_info[i].Device.pages_size)-1; - - last_part=0; - /* set the area addresses */ - for(j = 0; jpDataFlashDesc = &(dataflash_info[i].Desc); - pFlash->pDevice = &(dataflash_info[i].Device); - *addr -= dataflash_info[i].logical_address; - return (pFlash); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : addr_dataflash */ -/* Object : Test if address is valid */ -/*---------------------------------------------------------------------------*/ -int addr_dataflash (unsigned long addr) -{ - int addr_valid = 0; - int i; - - for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { - if ((((int) addr) & 0xFF000000) == - dataflash_info[i].logical_address) { - addr_valid = 1; - break; - } - } - - return addr_valid; -} -/*---------------------------------------------------------------------------*/ -/* Function Name : size_dataflash */ -/* Object : Test if address is valid regarding the size */ -/*---------------------------------------------------------------------------*/ -int size_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr, - unsigned long size) -{ - /* is outside the dataflash */ - if (((int)addr & 0x0FFFFFFF) > (pdataFlash->pDevice->pages_size * - pdataFlash->pDevice->pages_number)) return 0; - /* is too large for the dataflash */ - if (size > ((pdataFlash->pDevice->pages_size * - pdataFlash->pDevice->pages_number) - - ((int)addr & 0x0FFFFFFF))) return 0; - - return 1; -} -/*---------------------------------------------------------------------------*/ -/* Function Name : prot_dataflash */ -/* Object : Test if destination area is protected */ -/*---------------------------------------------------------------------------*/ -int prot_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr) -{ -int area; - /* find area */ - for (area=0; area < NB_DATAFLASH_AREA; area++) { - if ((addr >= pdataFlash->pDevice->area_list[area].start) && - (addr < pdataFlash->pDevice->area_list[area].end)) - break; - } - if (area == NB_DATAFLASH_AREA) - return -1; - - /*test protection value*/ - if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_SET) - return 0; - if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_INVALID) - return 0; - - return 1; -} -/*--------------------------------------------------------------------------*/ -/* Function Name : dataflash_real_protect */ -/* Object : protect/unprotect area */ -/*--------------------------------------------------------------------------*/ -int dataflash_real_protect (int flag, unsigned long start_addr, - unsigned long end_addr) -{ -int i,j, area1, area2, addr_valid = 0; - /* find dataflash */ - for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { - if ((((int) start_addr) & 0xF0000000) == - dataflash_info[i].logical_address) { - addr_valid = 1; - break; - } - } - if (!addr_valid) { - return -1; - } - /* find start area */ - for (area1=0; area1 < NB_DATAFLASH_AREA; area1++) { - if (start_addr == dataflash_info[i].Device.area_list[area1].start) - break; - } - if (area1 == NB_DATAFLASH_AREA) return -1; - /* find end area */ - for (area2=0; area2 < NB_DATAFLASH_AREA; area2++) { - if (end_addr == dataflash_info[i].Device.area_list[area2].end) - break; - } - if (area2 == NB_DATAFLASH_AREA) - return -1; - - /*set protection value*/ - for(j = area1; j < area2+1 ; j++) - if(dataflash_info[i].Device.area_list[j].protected - != FLAG_PROTECT_INVALID) { - if (flag == 0) { - dataflash_info[i].Device.area_list[j].protected - = FLAG_PROTECT_CLEAR; - } else { - dataflash_info[i].Device.area_list[j].protected - = FLAG_PROTECT_SET; - } - } - - return (area2-area1+1); -} - -/*---------------------------------------------------------------------------*/ -/* Function Name : read_dataflash */ -/* Object : dataflash memory read */ -/*---------------------------------------------------------------------------*/ -int read_dataflash (unsigned long addr, unsigned long size, char *result) -{ - unsigned long AddrToRead = addr; - AT91PS_DataFlash pFlash = &DataFlashInst; - - pFlash = AT91F_DataflashSelect (pFlash, &AddrToRead); - - if (pFlash == 0) - return ERR_UNKNOWN_FLASH_TYPE; - - if (size_dataflash(pFlash,addr,size) == 0) - return ERR_INVAL; - - return (AT91F_DataFlashRead (pFlash, AddrToRead, size, result)); -} - - -/*---------------------------------------------------------------------------*/ -/* Function Name : write_dataflash */ -/* Object : write a block in dataflash */ -/*---------------------------------------------------------------------------*/ -int write_dataflash (unsigned long addr_dest, unsigned long addr_src, - unsigned long size) -{ - unsigned long AddrToWrite = addr_dest; - AT91PS_DataFlash pFlash = &DataFlashInst; - - pFlash = AT91F_DataflashSelect (pFlash, &AddrToWrite); - - if (pFlash == 0) - return ERR_UNKNOWN_FLASH_TYPE; - - if (size_dataflash(pFlash,addr_dest,size) == 0) - return ERR_INVAL; - - if (prot_dataflash(pFlash,addr_dest) == 0) - return ERR_PROTECTED; - - if (AddrToWrite == -1) - return -1; - - return AT91F_DataFlashWrite (pFlash, (uchar *)addr_src, - AddrToWrite, size); -} - - -void dataflash_perror (int err) -{ - switch (err) { - case ERR_OK: - break; - case ERR_TIMOUT: - printf("Timeout writing to DataFlash\n"); - break; - case ERR_PROTECTED: - printf("Can't write to protected/invalid DataFlash sectors\n"); - break; - case ERR_INVAL: - printf("Outside available DataFlash\n"); - break; - case ERR_UNKNOWN_FLASH_TYPE: - printf("Unknown Type of DataFlash\n"); - break; - case ERR_PROG_ERROR: - printf("General DataFlash Programming Error\n"); - break; - default: - printf("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); - break; - } -} - -#endif diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile new file mode 100644 index 0000000000..95c5e02af9 --- /dev/null +++ b/drivers/mtd/Makefile @@ -0,0 +1,49 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libmtd.a + +COBJS-y += at45.o +COBJS-y += cfi_flash.o +COBJS-y += dataflash.o +COBJS-y += mw_eeprom.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/at45.c b/drivers/mtd/at45.c new file mode 100644 index 0000000000..dac987a43a --- /dev/null +++ b/drivers/mtd/at45.c @@ -0,0 +1,562 @@ +/* Driver for ATMEL DataFlash support + * Author : Hamid Ikdoumi (Atmel) + * + * 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 +#include + +#ifdef CONFIG_HAS_DATAFLASH +#include + +/* + * spi.c API + */ +extern unsigned int AT91F_SpiWrite(AT91PS_DataflashDesc pDesc); +extern void AT91F_SpiEnable(int cs); + +#define AT91C_TIMEOUT_WRDY 200000 + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashSendCommand */ +/* \brief Generic function to send a command to the dataflash */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashSendCommand(AT91PS_DataFlash pDataFlash, + unsigned char OpCode, + unsigned int CmdSize, + unsigned int DataflashAddress) +{ + unsigned int adr; + + if ((pDataFlash->pDataFlashDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* process the address to obtain page address and byte address */ + adr = ((DataflashAddress / (pDataFlash->pDevice->pages_size)) << + pDataFlash->pDevice->page_offset) + + (DataflashAddress % (pDataFlash->pDevice->pages_size)); + + /* fill the command buffer */ + pDataFlash->pDataFlashDesc->command[0] = OpCode; + if (pDataFlash->pDevice->pages_number >= 16384) { + pDataFlash->pDataFlashDesc->command[1] = + (unsigned char)((adr & 0x0F000000) >> 24); + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)((adr & 0x00FF0000) >> 16); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)((adr & 0x0000FF00) >> 8); + pDataFlash->pDataFlashDesc->command[4] = + (unsigned char)(adr & 0x000000FF); + } else { + pDataFlash->pDataFlashDesc->command[1] = + (unsigned char)((adr & 0x00FF0000) >> 16); + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)((adr & 0x0000FF00) >> 8); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)(adr & 0x000000FF); + pDataFlash->pDataFlashDesc->command[4] = 0; + } + pDataFlash->pDataFlashDesc->command[5] = 0; + pDataFlash->pDataFlashDesc->command[6] = 0; + pDataFlash->pDataFlashDesc->command[7] = 0; + + /* Initialize the SpiData structure for the spi write fuction */ + pDataFlash->pDataFlashDesc->tx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->tx_cmd_size = CmdSize; + pDataFlash->pDataFlashDesc->rx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->rx_cmd_size = CmdSize; + + /* send the command and read the data */ + return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); +} + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashGetStatus */ +/* \brief Read the status register of the dataflash */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashGetStatus(AT91PS_DataflashDesc pDesc) +{ + AT91S_DataFlashStatus status; + + /* if a transfert is in progress ==> return 0 */ + if ((pDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* first send the read status command (D7H) */ + pDesc->command[0] = DB_STATUS; + pDesc->command[1] = 0; + + pDesc->DataFlash_state = GET_STATUS; + pDesc->tx_data_size = 0; /* Transmit the command */ + /* and receive response */ + pDesc->tx_cmd_pt = pDesc->command; + pDesc->rx_cmd_pt = pDesc->command; + pDesc->rx_cmd_size = 2; + pDesc->tx_cmd_size = 2; + status = AT91F_SpiWrite(pDesc); + + pDesc->DataFlash_state = *((unsigned char *)(pDesc->rx_cmd_pt) + 1); + + return status; +} + +/*----------------------------------------------------------------------*/ +/* \fn AT91F_DataFlashWaitReady */ +/* \brief wait for dataflash ready (bit7 of the status register == 1) */ +/*----------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWaitReady(AT91PS_DataflashDesc + pDataFlashDesc, + unsigned int timeout) +{ + pDataFlashDesc->DataFlash_state = IDLE; + + do { + AT91F_DataFlashGetStatus(pDataFlashDesc); + timeout--; + } while (((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) && + (timeout > 0)); + + if ((pDataFlashDesc->DataFlash_state & 0x80) != 0x80) + return DATAFLASH_ERROR; + + return DATAFLASH_OK; +} + +/*--------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashContinuousRead */ +/* Object : Continuous stream Read */ +/* Input Parameters : DataFlash Service */ +/* : = dataflash address */ +/* : <*dataBuffer> = data buffer pointer */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*--------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashContinuousRead( + AT91PS_DataFlash pDataFlash, + int src, + unsigned char *dataBuffer, + int sizeToRead) +{ + AT91S_DataFlashStatus status; + /* Test the size to read in the device */ + if ((src + sizeToRead) > + (pDataFlash->pDevice->pages_size * + (pDataFlash->pDevice->pages_number))) + return DATAFLASH_MEMORY_OVERFLOW; + + pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->rx_data_size = sizeToRead; + pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->tx_data_size = sizeToRead; + + status = AT91F_DataFlashSendCommand( + pDataFlash, DB_CONTINUOUS_ARRAY_READ, 8, src); + /* Send the command to the dataflash */ + return (status); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashPagePgmBuf */ +/* Object : Main memory page program thru buffer 1 or buffer 2 */ +/* Input Parameters : DataFlash Service */ +/* : <*src> = Source buffer */ +/* : = dataflash destination address */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashPagePgmBuf(AT91PS_DataFlash pDataFlash, + unsigned char *src, + unsigned int dest, + unsigned int SizeToWrite) +{ + int cmdsize; + pDataFlash->pDataFlashDesc->tx_data_pt = src; + pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; + pDataFlash->pDataFlashDesc->rx_data_pt = src; + pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; + + cmdsize = 4; + /* Send the command to the dataflash */ + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand( + pDataFlash, DB_PAGE_PGM_BUF1, cmdsize, dest)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_MainMemoryToBufferTransfert */ +/* Object : Read a page in the SRAM Buffer 1 or 2 */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_MainMemoryToBufferTransfert( + AT91PS_DataFlash + pDataFlash, + unsigned char + BufferCommand, + unsigned int page) +{ + int cmdsize; + /* Test if the buffer command is legal */ + if ((BufferCommand != DB_PAGE_2_BUF1_TRF) && + (BufferCommand != DB_PAGE_2_BUF2_TRF)) { + return DATAFLASH_BAD_COMMAND; + } + + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand( + pDataFlash, BufferCommand, cmdsize, + page * pDataFlash->pDevice->pages_size)); +} + +/*-------------------------------------------------------------------------- */ +/* Function Name : AT91F_DataFlashWriteBuffer */ +/* Object : Write data to the internal sram buffer 1 or 2 */ +/* Input Parameters : DataFlash Service */ +/* : = command to write buffer1 or 2 */ +/* : <*dataBuffer> = data buffer to write */ +/* : = address in the internal buffer */ +/* : = data buffer size */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWriteBuffer( + AT91PS_DataFlash pDataFlash, + unsigned char BufferCommand, + unsigned char *dataBuffer, + unsigned int bufferAddress, + int SizeToWrite) +{ + int cmdsize; + /* Test if the buffer command is legal */ + if ((BufferCommand != DB_BUF1_WRITE) && + (BufferCommand != DB_BUF2_WRITE)) { + return DATAFLASH_BAD_COMMAND; + } + + /* buffer address must be lower than page size */ + if (bufferAddress > pDataFlash->pDevice->pages_size) + return DATAFLASH_BAD_ADDRESS; + + if ((pDataFlash->pDataFlashDesc->state) != IDLE) + return DATAFLASH_BUSY; + + /* Send first Write Command */ + pDataFlash->pDataFlashDesc->command[0] = BufferCommand; + pDataFlash->pDataFlashDesc->command[1] = 0; + if (pDataFlash->pDevice->pages_number >= 16384) { + pDataFlash->pDataFlashDesc->command[2] = 0; + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)(((unsigned int)(bufferAddress & + pDataFlash->pDevice-> + byte_mask)) >> 8); + pDataFlash->pDataFlashDesc->command[4] = + (unsigned char)((unsigned int)bufferAddress & 0x00FF); + cmdsize = 5; + } else { + pDataFlash->pDataFlashDesc->command[2] = + (unsigned char)(((unsigned int)(bufferAddress & + pDataFlash->pDevice-> + byte_mask)) >> 8); + pDataFlash->pDataFlashDesc->command[3] = + (unsigned char)((unsigned int)bufferAddress & 0x00FF); + pDataFlash->pDataFlashDesc->command[4] = 0; + cmdsize = 4; + } + + pDataFlash->pDataFlashDesc->tx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->tx_cmd_size = cmdsize; + pDataFlash->pDataFlashDesc->rx_cmd_pt = + pDataFlash->pDataFlashDesc->command; + pDataFlash->pDataFlashDesc->rx_cmd_size = cmdsize; + + pDataFlash->pDataFlashDesc->rx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->tx_data_pt = dataBuffer; + pDataFlash->pDataFlashDesc->rx_data_size = SizeToWrite; + pDataFlash->pDataFlashDesc->tx_data_size = SizeToWrite; + + return AT91F_SpiWrite(pDataFlash->pDataFlashDesc); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_PageErase */ +/* Object : Erase a page */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_PageErase( + AT91PS_DataFlash pDataFlash, + unsigned int page) +{ + int cmdsize; + /* Test if the buffer command is legal */ + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand(pDataFlash, + DB_PAGE_ERASE, cmdsize, + page * pDataFlash->pDevice->pages_size)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_BlockErase */ +/* Object : Erase a Block */ +/* Input Parameters : DataFlash Service */ +/* : Page concerned */ +/* : */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_BlockErase( + AT91PS_DataFlash pDataFlash, + unsigned int block) +{ + int cmdsize; + /* Test if the buffer command is legal */ + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + return (AT91F_DataFlashSendCommand(pDataFlash, DB_BLOCK_ERASE, cmdsize, + block * 8 * + pDataFlash->pDevice->pages_size)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_WriteBufferToMain */ +/* Object : Write buffer to the main memory */ +/* Input Parameters : DataFlash Service */ +/* : = command to send to buffer1 or buffer2 */ +/* : = main memory address */ +/* Return value : State of the dataflash */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_WriteBufferToMain(AT91PS_DataFlash pDataFlash, + unsigned char BufferCommand, + unsigned int dest) +{ + int cmdsize; + /* Test if the buffer command is correct */ + if ((BufferCommand != DB_BUF1_PAGE_PGM) && + (BufferCommand != DB_BUF1_PAGE_ERASE_PGM) && + (BufferCommand != DB_BUF2_PAGE_PGM) && + (BufferCommand != DB_BUF2_PAGE_ERASE_PGM)) + return DATAFLASH_BAD_COMMAND; + + /* no data to transmit or receive */ + pDataFlash->pDataFlashDesc->tx_data_size = 0; + + cmdsize = 4; + if (pDataFlash->pDevice->pages_number >= 16384) + cmdsize = 5; + /* Send the command to the dataflash */ + return (AT91F_DataFlashSendCommand(pDataFlash, BufferCommand, + cmdsize, dest)); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_PartialPageWrite */ +/* Object : Erase partielly a page */ +/* Input Parameters : = page number */ +/* : = adr to begin the fading */ +/* : = Number of bytes to erase */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_PartialPageWrite(AT91PS_DataFlash pDataFlash, + unsigned char *src, + unsigned int dest, + unsigned int size) +{ + unsigned int page; + unsigned int AdrInPage; + + page = dest / (pDataFlash->pDevice->pages_size); + AdrInPage = dest % (pDataFlash->pDevice->pages_size); + + /* Read the contents of the page in the Sram Buffer */ + AT91F_MainMemoryToBufferTransfert(pDataFlash, DB_PAGE_2_BUF1_TRF, page); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + /*Update the SRAM buffer */ + AT91F_DataFlashWriteBuffer(pDataFlash, DB_BUF1_WRITE, src, + AdrInPage, size); + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Erase page if a 128 Mbits device */ + if (pDataFlash->pDevice->pages_number >= 16384) { + AT91F_PageErase(pDataFlash, page); + /* Rewrite the modified Sram Buffer in the main memory */ + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + } + + /* Rewrite the modified Sram Buffer in the main memory */ + return (AT91F_WriteBufferToMain(pDataFlash, DB_BUF1_PAGE_ERASE_PGM, + (page * + pDataFlash->pDevice->pages_size))); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashWrite */ +/* Object : */ +/* Input Parameters : <*src> = Source buffer */ +/* : = dataflash adress */ +/* : = data buffer size */ +/*---------------------------------------------------------------------------*/ +AT91S_DataFlashStatus AT91F_DataFlashWrite(AT91PS_DataFlash pDataFlash, + unsigned char *src, + int dest, int size) +{ + unsigned int length; + unsigned int page; + unsigned int status; + + AT91F_SpiEnable(pDataFlash->pDevice->cs); + + if ((dest + size) > (pDataFlash->pDevice->pages_size * + (pDataFlash->pDevice->pages_number))) + return DATAFLASH_MEMORY_OVERFLOW; + + /* If destination does not fit a page start address */ + if ((dest % ((unsigned int)(pDataFlash->pDevice->pages_size))) != 0) { + length = + pDataFlash->pDevice->pages_size - + (dest % ((unsigned int)(pDataFlash->pDevice->pages_size))); + + if (size < length) + length = size; + + if (!AT91F_PartialPageWrite(pDataFlash, src, dest, length)) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Update size, source and destination pointers */ + size -= length; + dest += length; + src += length; + } + + while ((size - pDataFlash->pDevice->pages_size) >= 0) { + /* program dataflash page */ + page = (unsigned int)dest / (pDataFlash->pDevice->pages_size); + + status = AT91F_DataFlashWriteBuffer(pDataFlash, + DB_BUF1_WRITE, src, 0, + pDataFlash->pDevice-> + pages_size); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + status = AT91F_PageErase(pDataFlash, page); + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + if (!status) + return DATAFLASH_ERROR; + + status = AT91F_WriteBufferToMain(pDataFlash, + DB_BUF1_PAGE_PGM, dest); + if (!status) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + + /* Update size, source and destination pointers */ + size -= pDataFlash->pDevice->pages_size; + dest += pDataFlash->pDevice->pages_size; + src += pDataFlash->pDevice->pages_size; + } + + /* If still some bytes to read */ + if (size > 0) { + /* program dataflash page */ + if (!AT91F_PartialPageWrite(pDataFlash, src, dest, size)) + return DATAFLASH_ERROR; + + AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY); + } + return DATAFLASH_OK; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataFlashRead */ +/* Object : Read a block in dataflash */ +/* Input Parameters : */ +/* Return value : */ +/*---------------------------------------------------------------------------*/ +int AT91F_DataFlashRead(AT91PS_DataFlash pDataFlash, + unsigned long addr, unsigned long size, char *buffer) +{ + unsigned long SizeToRead; + + AT91F_SpiEnable(pDataFlash->pDevice->cs); + + if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY) != DATAFLASH_OK) + return -1; + + while (size) { + SizeToRead = (size < 0x8000) ? size : 0x8000; + + if (AT91F_DataFlashWaitReady(pDataFlash->pDataFlashDesc, + AT91C_TIMEOUT_WRDY) != + DATAFLASH_OK) + return -1; + + if (AT91F_DataFlashContinuousRead(pDataFlash, addr, + (uchar *) buffer, + SizeToRead) != DATAFLASH_OK) + return -1; + + size -= SizeToRead; + addr += SizeToRead; + buffer += SizeToRead; + } + + return DATAFLASH_OK; +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : AT91F_DataflashProbe */ +/* Object : */ +/* Input Parameters : */ +/* Return value : Dataflash status register */ +/*---------------------------------------------------------------------------*/ +int AT91F_DataflashProbe(int cs, AT91PS_DataflashDesc pDesc) +{ + AT91F_SpiEnable(cs); + AT91F_DataFlashGetStatus(pDesc); + return ((pDesc->command[1] == 0xFF) ? 0 : pDesc->command[1] & 0x3C); +} +#endif diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c new file mode 100644 index 0000000000..5579a1efc1 --- /dev/null +++ b/drivers/mtd/cfi_flash.c @@ -0,0 +1,1528 @@ +/* + * (C) Copyright 2002-2004 + * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com + * + * Copyright (C) 2003 Arabella Software Ltd. + * Yuli Barcohen + * + * Copyright (C) 2004 + * Ed Okerson + * + * Copyright (C) 2006 + * Tolunay Orkun + * + * 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 + * + */ + +/* The DEBUG define must be before common to enable debugging */ +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#ifdef CFG_FLASH_CFI_DRIVER + +/* + * This file implements a Common Flash Interface (CFI) driver for U-Boot. + * The width of the port and the width of the chips are determined at initialization. + * These widths are used to calculate the address for access CFI data structures. + * + * References + * JEDEC Standard JESD68 - Common Flash Interface (CFI) + * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes + * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets + * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet + * AMD CFI Specification, Release 2.0 December 1, 2001 + * AMD/Spansion Application Note: Migration from Single-byte to Three-byte + * Device IDs, Publication Number 25538 Revision A, November 8, 2001 + * + * define CFG_WRITE_SWAPPED_DATA, if you have to swap the Bytes between + * reading and writing ... (yes there is such a Hardware). + */ + +#ifndef CFG_FLASH_BANKS_LIST +#define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE } +#endif + +#define FLASH_CMD_CFI 0x98 +#define FLASH_CMD_READ_ID 0x90 +#define FLASH_CMD_RESET 0xff +#define FLASH_CMD_BLOCK_ERASE 0x20 +#define FLASH_CMD_ERASE_CONFIRM 0xD0 +#define FLASH_CMD_WRITE 0x40 +#define FLASH_CMD_PROTECT 0x60 +#define FLASH_CMD_PROTECT_SET 0x01 +#define FLASH_CMD_PROTECT_CLEAR 0xD0 +#define FLASH_CMD_CLEAR_STATUS 0x50 +#define FLASH_CMD_WRITE_TO_BUFFER 0xE8 +#define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0 + +#define FLASH_STATUS_DONE 0x80 +#define FLASH_STATUS_ESS 0x40 +#define FLASH_STATUS_ECLBS 0x20 +#define FLASH_STATUS_PSLBS 0x10 +#define FLASH_STATUS_VPENS 0x08 +#define FLASH_STATUS_PSS 0x04 +#define FLASH_STATUS_DPS 0x02 +#define FLASH_STATUS_R 0x01 +#define FLASH_STATUS_PROTECT 0x01 + +#define AMD_CMD_RESET 0xF0 +#define AMD_CMD_WRITE 0xA0 +#define AMD_CMD_ERASE_START 0x80 +#define AMD_CMD_ERASE_SECTOR 0x30 +#define AMD_CMD_UNLOCK_START 0xAA +#define AMD_CMD_UNLOCK_ACK 0x55 +#define AMD_CMD_WRITE_TO_BUFFER 0x25 +#define AMD_CMD_WRITE_BUFFER_CONFIRM 0x29 + +#define AMD_STATUS_TOGGLE 0x40 +#define AMD_STATUS_ERROR 0x20 + +#define AMD_ADDR_ERASE_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) +#define AMD_ADDR_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) +#define AMD_ADDR_ACK ((info->portwidth == FLASH_CFI_8BIT) ? 0x555 : 0x2AA) + +#define FLASH_OFFSET_MANUFACTURER_ID 0x00 +#define FLASH_OFFSET_DEVICE_ID 0x01 +#define FLASH_OFFSET_DEVICE_ID2 0x0E +#define FLASH_OFFSET_DEVICE_ID3 0x0F +#define FLASH_OFFSET_CFI 0x55 +#define FLASH_OFFSET_CFI_ALT 0x555 +#define FLASH_OFFSET_CFI_RESP 0x10 +#define FLASH_OFFSET_PRIMARY_VENDOR 0x13 +#define FLASH_OFFSET_EXT_QUERY_T_P_ADDR 0x15 /* extended query table primary addr */ +#define FLASH_OFFSET_WTOUT 0x1F +#define FLASH_OFFSET_WBTOUT 0x20 +#define FLASH_OFFSET_ETOUT 0x21 +#define FLASH_OFFSET_CETOUT 0x22 +#define FLASH_OFFSET_WMAX_TOUT 0x23 +#define FLASH_OFFSET_WBMAX_TOUT 0x24 +#define FLASH_OFFSET_EMAX_TOUT 0x25 +#define FLASH_OFFSET_CEMAX_TOUT 0x26 +#define FLASH_OFFSET_SIZE 0x27 +#define FLASH_OFFSET_INTERFACE 0x28 +#define FLASH_OFFSET_BUFFER_SIZE 0x2A +#define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C +#define FLASH_OFFSET_ERASE_REGIONS 0x2D +#define FLASH_OFFSET_PROTECT 0x02 +#define FLASH_OFFSET_USER_PROTECTION 0x85 +#define FLASH_OFFSET_INTEL_PROTECTION 0x81 + +#define CFI_CMDSET_NONE 0 +#define CFI_CMDSET_INTEL_EXTENDED 1 +#define CFI_CMDSET_AMD_STANDARD 2 +#define CFI_CMDSET_INTEL_STANDARD 3 +#define CFI_CMDSET_AMD_EXTENDED 4 +#define CFI_CMDSET_MITSU_STANDARD 256 +#define CFI_CMDSET_MITSU_EXTENDED 257 +#define CFI_CMDSET_SST 258 + +#ifdef CFG_FLASH_CFI_AMD_RESET /* needed for STM_ID_29W320DB on UC100 */ +# undef FLASH_CMD_RESET +# define FLASH_CMD_RESET AMD_CMD_RESET /* use AMD-Reset instead */ +#endif + +typedef union { + unsigned char c; + unsigned short w; + unsigned long l; + unsigned long long ll; +} cfiword_t; + +typedef union { + volatile unsigned char *cp; + volatile unsigned short *wp; + volatile unsigned long *lp; + volatile unsigned long long *llp; +} cfiptr_t; + +#define NUM_ERASE_REGIONS 4 /* max. number of erase regions */ + +static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT}; + +/* use CFG_MAX_FLASH_BANKS_DETECT if defined */ +#ifdef CFG_MAX_FLASH_BANKS_DETECT +static ulong bank_base[CFG_MAX_FLASH_BANKS_DETECT] = CFG_FLASH_BANKS_LIST; +flash_info_t flash_info[CFG_MAX_FLASH_BANKS_DETECT]; /* FLASH chips info */ +#else +static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST; +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */ +#endif + +/* + * Check if chip width is defined. If not, start detecting with 8bit. + */ +#ifndef CFG_FLASH_CFI_WIDTH +#define CFG_FLASH_CFI_WIDTH FLASH_CFI_8BIT +#endif + + +/*----------------------------------------------------------------------- + * Functions + */ + +typedef unsigned long flash_sect_t; + +static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c); +static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf); +static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); +static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect); +static int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); +static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); +static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); +static void flash_read_jedec_ids (flash_info_t * info); +static int flash_detect_cfi (flash_info_t * info); +static int flash_write_cfiword (flash_info_t * info, ulong dest, cfiword_t cword); +static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, + ulong tout, char *prompt); +ulong flash_get_size (ulong base, int banknum); +#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE) +static flash_info_t *flash_get_info(ulong base); +#endif +#ifdef CFG_FLASH_USE_BUFFER_WRITE +static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, int len); +#endif + +/*----------------------------------------------------------------------- + * create an address based on the offset and the port width + */ +inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, uint offset) +{ + return ((uchar *) (info->start[sect] + (offset * info->portwidth))); +} + +#ifdef DEBUG +/*----------------------------------------------------------------------- + * Debug support + */ +void print_longlong (char *str, unsigned long long data) +{ + int i; + char *cp; + + cp = (unsigned char *) &data; + for (i = 0; i < 8; i++) + sprintf (&str[i * 2], "%2.2x", *cp++); +} +static void flash_printqry (flash_info_t * info, flash_sect_t sect) +{ + cfiptr_t cptr; + int x, y; + + for (x = 0; x < 0x40; x += 16U / info->portwidth) { + cptr.cp = + flash_make_addr (info, sect, + x + FLASH_OFFSET_CFI_RESP); + debug ("%p : ", cptr.cp); + for (y = 0; y < 16; y++) { + debug ("%2.2x ", cptr.cp[y]); + } + debug (" "); + for (y = 0; y < 16; y++) { + if (cptr.cp[y] >= 0x20 && cptr.cp[y] <= 0x7e) { + debug ("%c", cptr.cp[y]); + } else { + debug ("."); + } + } + debug ("\n"); + } +} +#endif + + +/*----------------------------------------------------------------------- + * read a character at a port width address + */ +inline uchar flash_read_uchar (flash_info_t * info, uint offset) +{ + uchar *cp; + + cp = flash_make_addr (info, 0, offset); +#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) + return (cp[0]); +#else + return (cp[info->portwidth - 1]); +#endif +} + +/*----------------------------------------------------------------------- + * read a short word by swapping for ppc format. + */ +ushort flash_read_ushort (flash_info_t * info, flash_sect_t sect, uint offset) +{ + uchar *addr; + ushort retval; + +#ifdef DEBUG + int x; +#endif + addr = flash_make_addr (info, sect, offset); + +#ifdef DEBUG + debug ("ushort addr is at %p info->portwidth = %d\n", addr, + info->portwidth); + for (x = 0; x < 2 * info->portwidth; x++) { + debug ("addr[%x] = 0x%x\n", x, addr[x]); + } +#endif +#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) + retval = ((addr[(info->portwidth)] << 8) | addr[0]); +#else + retval = ((addr[(2 * info->portwidth) - 1] << 8) | + addr[info->portwidth - 1]); +#endif + + debug ("retval = 0x%x\n", retval); + return retval; +} + +/*----------------------------------------------------------------------- + * read a long word by picking the least significant byte of each maximum + * port size word. Swap for ppc format. + */ +ulong flash_read_long (flash_info_t * info, flash_sect_t sect, uint offset) +{ + uchar *addr; + ulong retval; + +#ifdef DEBUG + int x; +#endif + addr = flash_make_addr (info, sect, offset); + +#ifdef DEBUG + debug ("long addr is at %p info->portwidth = %d\n", addr, + info->portwidth); + for (x = 0; x < 4 * info->portwidth; x++) { + debug ("addr[%x] = 0x%x\n", x, addr[x]); + } +#endif +#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) + retval = (addr[0] << 16) | (addr[(info->portwidth)] << 24) | + (addr[(2 * info->portwidth)]) | (addr[(3 * info->portwidth)] << 8); +#else + retval = (addr[(2 * info->portwidth) - 1] << 24) | + (addr[(info->portwidth) - 1] << 16) | + (addr[(4 * info->portwidth) - 1] << 8) | + addr[(3 * info->portwidth) - 1]; +#endif + return retval; +} + + +/*----------------------------------------------------------------------- + */ +unsigned long flash_init (void) +{ + unsigned long size = 0; + int i; + +#ifdef CFG_FLASH_PROTECTION + char *s = getenv("unlock"); +#endif + + /* Init: no FLASHes known */ + for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { + flash_info[i].flash_id = FLASH_UNKNOWN; + size += flash_info[i].size = flash_get_size (bank_base[i], i); + if (flash_info[i].flash_id == FLASH_UNKNOWN) { +#ifndef CFG_FLASH_QUIET_TEST + printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", + i+1, flash_info[i].size, flash_info[i].size << 20); +#endif /* CFG_FLASH_QUIET_TEST */ + } +#ifdef CFG_FLASH_PROTECTION + else if ((s != NULL) && (strcmp(s, "yes") == 0)) { + /* + * Only the U-Boot image and it's environment is protected, + * all other sectors are unprotected (unlocked) if flash + * hardware protection is used (CFG_FLASH_PROTECTION) and + * the environment variable "unlock" is set to "yes". + */ + if (flash_info[i].legacy_unlock) { + int k; + + /* + * Disable legacy_unlock temporarily, since + * flash_real_protect would relock all other sectors + * again otherwise. + */ + flash_info[i].legacy_unlock = 0; + + /* + * Legacy unlocking (e.g. Intel J3) -> unlock only one + * sector. This will unlock all sectors. + */ + flash_real_protect (&flash_info[i], 0, 0); + + flash_info[i].legacy_unlock = 1; + + /* + * Manually mark other sectors as unlocked (unprotected) + */ + for (k = 1; k < flash_info[i].sector_count; k++) + flash_info[i].protect[k] = 0; + } else { + /* + * No legancy unlocking -> unlock all sectors + */ + flash_protect (FLAG_PROTECT_CLEAR, + flash_info[i].start[0], + flash_info[i].start[0] + flash_info[i].size - 1, + &flash_info[i]); + } + } +#endif /* CFG_FLASH_PROTECTION */ + } + + /* Monitor protection ON by default */ +#if (CFG_MONITOR_BASE >= CFG_FLASH_BASE) + flash_protect (FLAG_PROTECT_SET, + CFG_MONITOR_BASE, + CFG_MONITOR_BASE + monitor_flash_len - 1, + flash_get_info(CFG_MONITOR_BASE)); +#endif + + /* Environment protection ON by default */ +#ifdef CFG_ENV_IS_IN_FLASH + flash_protect (FLAG_PROTECT_SET, + CFG_ENV_ADDR, + CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1, + flash_get_info(CFG_ENV_ADDR)); +#endif + + /* Redundant environment protection ON by default */ +#ifdef CFG_ENV_ADDR_REDUND + flash_protect (FLAG_PROTECT_SET, + CFG_ENV_ADDR_REDUND, + CFG_ENV_ADDR_REDUND + CFG_ENV_SIZE_REDUND - 1, + flash_get_info(CFG_ENV_ADDR_REDUND)); +#endif + return (size); +} + +/*----------------------------------------------------------------------- + */ +#if defined(CFG_ENV_IS_IN_FLASH) || defined(CFG_ENV_ADDR_REDUND) || (CFG_MONITOR_BASE >= CFG_FLASH_BASE) +static flash_info_t *flash_get_info(ulong base) +{ + int i; + flash_info_t * info = 0; + + for (i = 0; i < CFG_MAX_FLASH_BANKS; i ++) { + info = & flash_info[i]; + if (info->size && info->start[0] <= base && + base <= info->start[0] + info->size - 1) + break; + } + + return i == CFG_MAX_FLASH_BANKS ? 0 : info; +} +#endif + +/*----------------------------------------------------------------------- + */ +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ + int rcode = 0; + int prot; + flash_sect_t sect; + + if (info->flash_id != FLASH_MAN_CFI) { + puts ("Can't erase unknown flash type - aborted\n"); + return 1; + } + if ((s_first < 0) || (s_first > s_last)) { + puts ("- no sectors to erase\n"); + return 1; + } + + prot = 0; + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) { + prot++; + } + } + if (prot) { + printf ("- Warning: %d protected sectors will not be erased!\n", prot); + } else { + putc ('\n'); + } + + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { /* not protected */ + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE); + flash_write_cmd (info, sect, 0, FLASH_CMD_ERASE_CONFIRM); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + flash_unlock_seq (info, sect); + flash_write_cmd (info, sect, AMD_ADDR_ERASE_START, + AMD_CMD_ERASE_START); + flash_unlock_seq (info, sect); + flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR); + break; + default: + debug ("Unkown flash vendor %d\n", + info->vendor); + break; + } + + if (flash_full_status_check + (info, sect, info->erase_blk_tout, "erase")) { + rcode = 1; + } else + putc ('.'); + } + } + puts (" done\n"); + return rcode; +} + +/*----------------------------------------------------------------------- + */ +void flash_print_info (flash_info_t * info) +{ + int i; + + if (info->flash_id != FLASH_MAN_CFI) { + puts ("missing or unknown FLASH type\n"); + return; + } + + printf ("CFI conformant FLASH (%d x %d)", + (info->portwidth << 3), (info->chipwidth << 3)); + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + printf (" "); + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + printf ("Intel Standard"); + break; + case CFI_CMDSET_INTEL_EXTENDED: + printf ("Intel Extended"); + break; + case CFI_CMDSET_AMD_STANDARD: + printf ("AMD Standard"); + break; + case CFI_CMDSET_AMD_EXTENDED: + printf ("AMD Extended"); + break; + default: + printf ("Unknown (%d)", info->vendor); + break; + } + printf (" command set, Manufacturer ID: 0x%02X, Device ID: 0x%02X", + info->manufacturer_id, info->device_id); + if (info->device_id == 0x7E) { + printf("%04X", info->device_id2); + } + printf ("\n Erase timeout: %ld ms, write timeout: %ld ms\n", + info->erase_blk_tout, + info->write_tout); + if (info->buffer_size > 1) { + printf (" Buffer write timeout: %ld ms, buffer size: %d bytes\n", + info->buffer_write_tout, + info->buffer_size); + } + + puts ("\n Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) + printf ("\n"); +#ifdef CFG_FLASH_EMPTY_INFO + int k; + int size; + int erased; + volatile unsigned long *flash; + + /* + * Check if whole sector is erased + */ + if (i != (info->sector_count - 1)) + size = info->start[i + 1] - info->start[i]; + else + size = info->start[0] + info->size - info->start[i]; + erased = 1; + flash = (volatile unsigned long *) info->start[i]; + size = size >> 2; /* divide by 4 for longword access */ + for (k = 0; k < size; k++) { + if (*flash++ != 0xffffffff) { + erased = 0; + break; + } + } + + /* print empty and read-only info */ + printf (" %08lX %c %s ", + info->start[i], + erased ? 'E' : ' ', + info->protect[i] ? "RO" : " "); +#else /* ! CFG_FLASH_EMPTY_INFO */ + printf (" %08lX %s ", + info->start[i], + info->protect[i] ? "RO" : " "); +#endif + } + putc ('\n'); + return; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + ulong wp; + ulong cp; + int aln; + cfiword_t cword; + int i, rc; + +#ifdef CFG_FLASH_USE_BUFFER_WRITE + int buffered_size; +#endif + /* get lower aligned address */ + /* get lower aligned address */ + wp = (addr & ~(info->portwidth - 1)); + + /* handle unaligned start */ + if ((aln = addr - wp) != 0) { + cword.l = 0; + cp = wp; + for (i = 0; i < aln; ++i, ++cp) + flash_add_byte (info, &cword, (*(uchar *) cp)); + + for (; (i < info->portwidth) && (cnt > 0); i++) { + flash_add_byte (info, &cword, *src++); + cnt--; + cp++; + } + for (; (cnt == 0) && (i < info->portwidth); ++i, ++cp) + flash_add_byte (info, &cword, (*(uchar *) cp)); + if ((rc = flash_write_cfiword (info, wp, cword)) != 0) + return rc; + wp = cp; + } + + /* handle the aligned part */ +#ifdef CFG_FLASH_USE_BUFFER_WRITE + buffered_size = (info->portwidth / info->chipwidth); + buffered_size *= info->buffer_size; + while (cnt >= info->portwidth) { + /* prohibit buffer write when buffer_size is 1 */ + if (info->buffer_size == 1) { + cword.l = 0; + for (i = 0; i < info->portwidth; i++) + flash_add_byte (info, &cword, *src++); + if ((rc = flash_write_cfiword (info, wp, cword)) != 0) + return rc; + wp += info->portwidth; + cnt -= info->portwidth; + continue; + } + + /* write buffer until next buffered_size aligned boundary */ + i = buffered_size - (wp % buffered_size); + if (i > cnt) + i = cnt; + if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) + return rc; + i -= i & (info->portwidth - 1); + wp += i; + src += i; + cnt -= i; + } +#else + while (cnt >= info->portwidth) { + cword.l = 0; + for (i = 0; i < info->portwidth; i++) { + flash_add_byte (info, &cword, *src++); + } + if ((rc = flash_write_cfiword (info, wp, cword)) != 0) + return rc; + wp += info->portwidth; + cnt -= info->portwidth; + } +#endif /* CFG_FLASH_USE_BUFFER_WRITE */ + if (cnt == 0) { + return (0); + } + + /* + * handle unaligned tail bytes + */ + cword.l = 0; + for (i = 0, cp = wp; (i < info->portwidth) && (cnt > 0); ++i, ++cp) { + flash_add_byte (info, &cword, *src++); + --cnt; + } + for (; i < info->portwidth; ++i, ++cp) { + flash_add_byte (info, &cword, (*(uchar *) cp)); + } + + return flash_write_cfiword (info, wp, cword); +} + +/*----------------------------------------------------------------------- + */ +#ifdef CFG_FLASH_PROTECTION + +int flash_real_protect (flash_info_t * info, long sector, int prot) +{ + int retcode = 0; + + flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); + if (prot) + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); + else + flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); + + if ((retcode = + flash_full_status_check (info, sector, info->erase_blk_tout, + prot ? "protect" : "unprotect")) == 0) { + + info->protect[sector] = prot; + + /* + * On some of Intel's flash chips (marked via legacy_unlock) + * unprotect unprotects all locking. + */ + if ((prot == 0) && (info->legacy_unlock)) { + flash_sect_t i; + + for (i = 0; i < info->sector_count; i++) { + if (info->protect[i]) + flash_real_protect (info, i, 1); + } + } + } + return retcode; +} + +/*----------------------------------------------------------------------- + * flash_read_user_serial - read the OneTimeProgramming cells + */ +void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, + int len) +{ + uchar *src; + uchar *dst; + + dst = buffer; + src = flash_make_addr (info, 0, FLASH_OFFSET_USER_PROTECTION); + flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); + memcpy (dst, src + offset, len); + flash_write_cmd (info, 0, 0, info->cmd_reset); +} + +/* + * flash_read_factory_serial - read the device Id from the protection area + */ +void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, + int len) +{ + uchar *src; + + src = flash_make_addr (info, 0, FLASH_OFFSET_INTEL_PROTECTION); + flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID); + memcpy (buffer, src + offset, len); + flash_write_cmd (info, 0, 0, info->cmd_reset); +} + +#endif /* CFG_FLASH_PROTECTION */ + +/* + * flash_is_busy - check to see if the flash is busy + * This routine checks the status of the chip and returns true if the chip is busy + */ +static int flash_is_busy (flash_info_t * info, flash_sect_t sect) +{ + int retval; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); + break; + default: + retval = 0; + } + debug ("flash_is_busy: %d\n", retval); + return retval; +} + +/*----------------------------------------------------------------------- + * wait for XSR.7 to be set. Time out with an error if it does not. + * This routine does not set the flash to read-array mode. + */ +static int flash_status_check (flash_info_t * info, flash_sect_t sector, + ulong tout, char *prompt) +{ + ulong start; + +#if CFG_HZ != 1000 + tout *= CFG_HZ/1000; +#endif + + /* Wait for command completion */ + start = get_timer (0); + while (flash_is_busy (info, sector)) { + if (get_timer (start) > tout) { + printf ("Flash %s timeout at address %lx data %lx\n", + prompt, info->start[sector], + flash_read_long (info, sector, 0)); + flash_write_cmd (info, sector, 0, info->cmd_reset); + return ERR_TIMOUT; + } + udelay (1); /* also triggers watchdog */ + } + return ERR_OK; +} + +/*----------------------------------------------------------------------- + * Wait for XSR.7 to be set, if it times out print an error, otherwise do a full status check. + * This routine sets the flash to read-array mode. + */ +static int flash_full_status_check (flash_info_t * info, flash_sect_t sector, + ulong tout, char *prompt) +{ + int retcode; + + retcode = flash_status_check (info, sector, tout, prompt); + switch (info->vendor) { + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + if ((retcode == ERR_OK) + && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { + retcode = ERR_INVAL; + printf ("Flash %s error at address %lx\n", prompt, + info->start[sector]); + if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { + puts ("Command Sequence Error.\n"); + } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { + puts ("Block Erase Error.\n"); + retcode = ERR_NOT_ERASED; + } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) { + puts ("Locking Error\n"); + } + if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { + puts ("Block locked.\n"); + retcode = ERR_PROTECTED; + } + if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) + puts ("Vpp Low Error.\n"); + } + flash_write_cmd (info, sector, 0, info->cmd_reset); + break; + default: + break; + } + return retcode; +} + +/*----------------------------------------------------------------------- + */ +static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) +{ +#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) + unsigned short w; + unsigned int l; + unsigned long long ll; +#endif + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + cword->c = c; + break; + case FLASH_CFI_16BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) + w = c; + w <<= 8; + cword->w = (cword->w >> 8) | w; +#else + cword->w = (cword->w << 8) | c; +#endif + break; + case FLASH_CFI_32BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) + l = c; + l <<= 24; + cword->l = (cword->l >> 8) | l; +#else + cword->l = (cword->l << 8) | c; +#endif + break; + case FLASH_CFI_64BIT: +#if defined(__LITTLE_ENDIAN) && !defined(CFG_WRITE_SWAPPED_DATA) + ll = c; + ll <<= 56; + cword->ll = (cword->ll >> 8) | ll; +#else + cword->ll = (cword->ll << 8) | c; +#endif + break; + } +} + + +/*----------------------------------------------------------------------- + * make a proper sized command based on the port and chip widths + */ +static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) +{ + int i; + uchar *cp = (uchar *) cmdbuf; + +#if defined(__LITTLE_ENDIAN) || defined(CFG_WRITE_SWAPPED_DATA) + for (i = info->portwidth; i > 0; i--) +#else + for (i = 1; i <= info->portwidth; i++) +#endif + *cp++ = (i & (info->chipwidth - 1)) ? '\0' : cmd; +} + +/* + * Write a proper sized command to the correct address + */ +static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +{ + + volatile cfiptr_t addr; + cfiword_t cword; + + addr.cp = flash_make_addr (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr.cp, cmd, + cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + *addr.cp = cword.c; + break; + case FLASH_CFI_16BIT: + debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr.wp, + cmd, cword.w, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + *addr.wp = cword.w; + break; + case FLASH_CFI_32BIT: + debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit\n", addr.lp, + cmd, cword.l, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + *addr.lp = cword.l; + break; + case FLASH_CFI_64BIT: +#ifdef DEBUG + { + char str[20]; + + print_longlong (str, cword.ll); + + debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n", + addr.llp, cmd, str, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + } +#endif + *addr.llp = cword.ll; + break; + } + + /* Ensure all the instructions are fully finished */ + sync(); +} + +static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect) +{ + flash_write_cmd (info, sect, AMD_ADDR_START, AMD_CMD_UNLOCK_START); + flash_write_cmd (info, sect, AMD_ADDR_ACK, AMD_CMD_UNLOCK_ACK); +} + +/*----------------------------------------------------------------------- + */ +static int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +{ + cfiptr_t cptr; + cfiword_t cword; + int retval; + + cptr.cp = flash_make_addr (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + + debug ("is= cmd %x(%c) addr %p ", cmd, cmd, cptr.cp); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + debug ("is= %x %x\n", cptr.cp[0], cword.c); + retval = (cptr.cp[0] == cword.c); + break; + case FLASH_CFI_16BIT: + debug ("is= %4.4x %4.4x\n", cptr.wp[0], cword.w); + retval = (cptr.wp[0] == cword.w); + break; + case FLASH_CFI_32BIT: + debug ("is= %8.8lx %8.8lx\n", cptr.lp[0], cword.l); + retval = (cptr.lp[0] == cword.l); + break; + case FLASH_CFI_64BIT: +#ifdef DEBUG + { + char str1[20]; + char str2[20]; + + print_longlong (str1, cptr.llp[0]); + print_longlong (str2, cword.ll); + debug ("is= %s %s\n", str1, str2); + } +#endif + retval = (cptr.llp[0] == cword.ll); + break; + default: + retval = 0; + break; + } + return retval; +} + +/*----------------------------------------------------------------------- + */ +static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +{ + cfiptr_t cptr; + cfiword_t cword; + int retval; + + cptr.cp = flash_make_addr (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + retval = ((cptr.cp[0] & cword.c) == cword.c); + break; + case FLASH_CFI_16BIT: + retval = ((cptr.wp[0] & cword.w) == cword.w); + break; + case FLASH_CFI_32BIT: + retval = ((cptr.lp[0] & cword.l) == cword.l); + break; + case FLASH_CFI_64BIT: + retval = ((cptr.llp[0] & cword.ll) == cword.ll); + break; + default: + retval = 0; + break; + } + return retval; +} + +/*----------------------------------------------------------------------- + */ +static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +{ + cfiptr_t cptr; + cfiword_t cword; + int retval; + + cptr.cp = flash_make_addr (info, sect, offset); + flash_make_cmd (info, cmd, &cword); + switch (info->portwidth) { + case FLASH_CFI_8BIT: + retval = ((cptr.cp[0] & cword.c) != (cptr.cp[0] & cword.c)); + break; + case FLASH_CFI_16BIT: + retval = ((cptr.wp[0] & cword.w) != (cptr.wp[0] & cword.w)); + break; + case FLASH_CFI_32BIT: + retval = ((cptr.lp[0] & cword.l) != (cptr.lp[0] & cword.l)); + break; + case FLASH_CFI_64BIT: + retval = ((cptr.llp[0] & cword.ll) != + (cptr.llp[0] & cword.ll)); + break; + default: + retval = 0; + break; + } + return retval; +} + +/*----------------------------------------------------------------------- + * read jedec ids from device and set corresponding fields in info struct + * + * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct + * +*/ +static void flash_read_jedec_ids (flash_info_t * info) +{ + info->manufacturer_id = 0; + info->device_id = 0; + info->device_id2 = 0; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); + flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID); + udelay(1000); /* some flash are slow to respond */ + info->manufacturer_id = flash_read_uchar (info, + FLASH_OFFSET_MANUFACTURER_ID); + info->device_id = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID); + flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + flash_unlock_seq(info, 0); + flash_write_cmd(info, 0, AMD_ADDR_START, FLASH_CMD_READ_ID); + udelay(1000); /* some flash are slow to respond */ + info->manufacturer_id = flash_read_uchar (info, + FLASH_OFFSET_MANUFACTURER_ID); + info->device_id = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID); + if (info->device_id == 0x7E) { + /* AMD 3-byte (expanded) device ids */ + info->device_id2 = flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID2); + info->device_id2 <<= 8; + info->device_id2 |= flash_read_uchar (info, + FLASH_OFFSET_DEVICE_ID3); + } + flash_write_cmd(info, 0, 0, AMD_CMD_RESET); + break; + default: + break; + } +} + +/*----------------------------------------------------------------------- + * detect if flash is compatible with the Common Flash Interface (CFI) + * http://www.jedec.org/download/search/jesd68.pdf + * +*/ +static int flash_detect_cfi (flash_info_t * info) +{ + int cfi_offset; + debug ("flash detect cfi\n"); + + for (info->portwidth = CFG_FLASH_CFI_WIDTH; + info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) { + for (info->chipwidth = FLASH_CFI_BY8; + info->chipwidth <= info->portwidth; + info->chipwidth <<= 1) { + flash_write_cmd (info, 0, 0, info->cmd_reset); + for (cfi_offset=0; cfi_offset < sizeof(flash_offset_cfi)/sizeof(uint); cfi_offset++) { + flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], FLASH_CMD_CFI); + if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') + && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { + info->interface = flash_read_ushort (info, 0, FLASH_OFFSET_INTERFACE); + info->cfi_offset=flash_offset_cfi[cfi_offset]; + debug ("device interface is %d\n", + info->interface); + debug ("found port %d chip %d ", + info->portwidth, info->chipwidth); + debug ("port %d bits chip %d bits\n", + info->portwidth << CFI_FLASH_SHIFT_WIDTH, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + return 1; + } + } + } + } + debug ("not found\n"); + return 0; +} + +/* + * The following code cannot be run from FLASH! + * + */ +ulong flash_get_size (ulong base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + int i, j; + flash_sect_t sect_cnt; + unsigned long sector; + unsigned long tmp; + int size_ratio; + uchar num_erase_regions; + int erase_region_size; + int erase_region_count; + int geometry_reversed = 0; + + info->ext_addr = 0; + info->cfi_version = 0; +#ifdef CFG_FLASH_PROTECTION + info->legacy_unlock = 0; +#endif + + info->start[0] = base; + + if (flash_detect_cfi (info)) { + info->vendor = flash_read_ushort (info, 0, + FLASH_OFFSET_PRIMARY_VENDOR); + flash_read_jedec_ids (info); + flash_write_cmd (info, 0, info->cfi_offset, FLASH_CMD_CFI); + num_erase_regions = flash_read_uchar (info, + FLASH_OFFSET_NUM_ERASE_REGIONS); + info->ext_addr = flash_read_ushort (info, 0, + FLASH_OFFSET_EXT_QUERY_T_P_ADDR); + if (info->ext_addr) { + info->cfi_version = (ushort) flash_read_uchar (info, + info->ext_addr + 3) << 8; + info->cfi_version |= (ushort) flash_read_uchar (info, + info->ext_addr + 4); + } +#ifdef DEBUG + flash_printqry (info, 0); +#endif + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + default: + info->cmd_reset = FLASH_CMD_RESET; +#ifdef CFG_FLASH_PROTECTION + /* read legacy lock/unlock bit from intel flash */ + if (info->ext_addr) { + info->legacy_unlock = flash_read_uchar (info, + info->ext_addr + 5) & 0x08; + } +#endif + break; + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + info->cmd_reset = AMD_CMD_RESET; + /* check if flash geometry needs reversal */ + if (num_erase_regions <= 1) + break; + /* reverse geometry if top boot part */ + if (info->cfi_version < 0x3131) { + /* CFI < 1.1, try to guess from device id */ + if ((info->device_id & 0x80) != 0) { + geometry_reversed = 1; + } + break; + } + /* CFI >= 1.1, deduct from top/bottom flag */ + /* note: ext_addr is valid since cfi_version > 0 */ + if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) { + geometry_reversed = 1; + } + break; + } + + debug ("manufacturer is %d\n", info->vendor); + debug ("manufacturer id is 0x%x\n", info->manufacturer_id); + debug ("device id is 0x%x\n", info->device_id); + debug ("device id2 is 0x%x\n", info->device_id2); + debug ("cfi version is 0x%04x\n", info->cfi_version); + + size_ratio = info->portwidth / info->chipwidth; + /* if the chip is x8/x16 reduce the ratio by half */ + if ((info->interface == FLASH_CFI_X8X16) + && (info->chipwidth == FLASH_CFI_BY8)) { + size_ratio >>= 1; + } + debug ("size_ratio %d port %d bits chip %d bits\n", + size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, + info->chipwidth << CFI_FLASH_SHIFT_WIDTH); + debug ("found %d erase regions\n", num_erase_regions); + sect_cnt = 0; + sector = base; + for (i = 0; i < num_erase_regions; i++) { + if (i > NUM_ERASE_REGIONS) { + printf ("%d erase regions found, only %d used\n", + num_erase_regions, NUM_ERASE_REGIONS); + break; + } + if (geometry_reversed) + tmp = flash_read_long (info, 0, + FLASH_OFFSET_ERASE_REGIONS + + (num_erase_regions - 1 - i) * 4); + else + tmp = flash_read_long (info, 0, + FLASH_OFFSET_ERASE_REGIONS + + i * 4); + erase_region_size = + (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; + tmp >>= 16; + erase_region_count = (tmp & 0xffff) + 1; + debug ("erase_region_count = %d erase_region_size = %d\n", + erase_region_count, erase_region_size); + for (j = 0; j < erase_region_count; j++) { + info->start[sect_cnt] = sector; + sector += (erase_region_size * size_ratio); + + /* + * Only read protection status from supported devices (intel...) + */ + switch (info->vendor) { + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + info->protect[sect_cnt] = + flash_isset (info, sect_cnt, + FLASH_OFFSET_PROTECT, + FLASH_STATUS_PROTECT); + break; + default: + info->protect[sect_cnt] = 0; /* default: not protected */ + } + + sect_cnt++; + } + } + + info->sector_count = sect_cnt; + /* multiply the size by the number of chips */ + info->size = (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * size_ratio; + info->buffer_size = (1 << flash_read_ushort (info, 0, FLASH_OFFSET_BUFFER_SIZE)); + tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT); + info->erase_blk_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT))); + tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT)) * + (1 << flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT)); + info->buffer_write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */ + tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT)) * + (1 << flash_read_uchar (info, FLASH_OFFSET_WMAX_TOUT)); + info->write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */ + info->flash_id = FLASH_MAN_CFI; + if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) { + info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */ + } + } + + flash_write_cmd (info, 0, 0, info->cmd_reset); + return (info->size); +} + +/* loop through the sectors from the highest address + * when the passed address is greater or equal to the sector address + * we have a match + */ +static flash_sect_t find_sector (flash_info_t * info, ulong addr) +{ + flash_sect_t sector; + + for (sector = info->sector_count - 1; sector >= 0; sector--) { + if (addr >= info->start[sector]) + break; + } + return sector; +} + +/*----------------------------------------------------------------------- + */ +static int flash_write_cfiword (flash_info_t * info, ulong dest, + cfiword_t cword) +{ + cfiptr_t ctladdr; + cfiptr_t cptr; + int flag; + + ctladdr.cp = flash_make_addr (info, 0, 0); + cptr.cp = (uchar *) dest; + + /* Check if Flash is (sufficiently) erased */ + switch (info->portwidth) { + case FLASH_CFI_8BIT: + flag = ((cptr.cp[0] & cword.c) == cword.c); + break; + case FLASH_CFI_16BIT: + flag = ((cptr.wp[0] & cword.w) == cword.w); + break; + case FLASH_CFI_32BIT: + flag = ((cptr.lp[0] & cword.l) == cword.l); + break; + case FLASH_CFI_64BIT: + flag = ((cptr.llp[0] & cword.ll) == cword.ll); + break; + default: + return 2; + } + if (!flag) + return 2; + + /* Disable interrupts which might cause a timeout here */ + flag = disable_interrupts (); + + switch (info->vendor) { + case CFI_CMDSET_INTEL_EXTENDED: + case CFI_CMDSET_INTEL_STANDARD: + flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); + break; + case CFI_CMDSET_AMD_EXTENDED: + case CFI_CMDSET_AMD_STANDARD: + flash_unlock_seq (info, 0); + flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE); + break; + } + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + cptr.cp[0] = cword.c; + break; + case FLASH_CFI_16BIT: + cptr.wp[0] = cword.w; + break; + case FLASH_CFI_32BIT: + cptr.lp[0] = cword.l; + break; + case FLASH_CFI_64BIT: + cptr.llp[0] = cword.ll; + break; + } + + /* re-enable interrupts if necessary */ + if (flag) + enable_interrupts (); + + return flash_full_status_check (info, find_sector (info, dest), + info->write_tout, "write"); +} + +#ifdef CFG_FLASH_USE_BUFFER_WRITE + +static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, + int len) +{ + flash_sect_t sector; + int cnt; + int retcode; + volatile cfiptr_t src; + volatile cfiptr_t dst; + + switch (info->vendor) { + case CFI_CMDSET_INTEL_STANDARD: + case CFI_CMDSET_INTEL_EXTENDED: + src.cp = cp; + dst.cp = (uchar *) dest; + sector = find_sector (info, dest); + flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); + flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); + if ((retcode = flash_status_check (info, sector, info->buffer_write_tout, + "write to buffer")) == ERR_OK) { + /* reduce the number of loops by the width of the port */ + switch (info->portwidth) { + case FLASH_CFI_8BIT: + cnt = len; + break; + case FLASH_CFI_16BIT: + cnt = len >> 1; + break; + case FLASH_CFI_32BIT: + cnt = len >> 2; + break; + case FLASH_CFI_64BIT: + cnt = len >> 3; + break; + default: + return ERR_INVAL; + break; + } + flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + while (cnt-- > 0) { + switch (info->portwidth) { + case FLASH_CFI_8BIT: + *dst.cp++ = *src.cp++; + break; + case FLASH_CFI_16BIT: + *dst.wp++ = *src.wp++; + break; + case FLASH_CFI_32BIT: + *dst.lp++ = *src.lp++; + break; + case FLASH_CFI_64BIT: + *dst.llp++ = *src.llp++; + break; + default: + return ERR_INVAL; + break; + } + } + flash_write_cmd (info, sector, 0, + FLASH_CMD_WRITE_BUFFER_CONFIRM); + retcode = flash_full_status_check (info, sector, + info->buffer_write_tout, + "buffer write"); + } + return retcode; + + case CFI_CMDSET_AMD_STANDARD: + case CFI_CMDSET_AMD_EXTENDED: + src.cp = cp; + dst.cp = (uchar *) dest; + sector = find_sector (info, dest); + + flash_unlock_seq(info,0); + flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_TO_BUFFER); + + switch (info->portwidth) { + case FLASH_CFI_8BIT: + cnt = len; + flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + while (cnt-- > 0) *dst.cp++ = *src.cp++; + break; + case FLASH_CFI_16BIT: + cnt = len >> 1; + flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + while (cnt-- > 0) *dst.wp++ = *src.wp++; + break; + case FLASH_CFI_32BIT: + cnt = len >> 2; + flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + while (cnt-- > 0) *dst.lp++ = *src.lp++; + break; + case FLASH_CFI_64BIT: + cnt = len >> 3; + flash_write_cmd (info, sector, 0, (uchar) cnt - 1); + while (cnt-- > 0) *dst.llp++ = *src.llp++; + break; + default: + return ERR_INVAL; + } + + flash_write_cmd (info, sector, 0, AMD_CMD_WRITE_BUFFER_CONFIRM); + retcode = flash_full_status_check (info, sector, info->buffer_write_tout, + "buffer write"); + return retcode; + + default: + debug ("Unknown Command Set\n"); + return ERR_INVAL; + } +} +#endif /* CFG_FLASH_USE_BUFFER_WRITE */ + +#endif /* CFG_FLASH_CFI */ diff --git a/drivers/mtd/dataflash.c b/drivers/mtd/dataflash.c new file mode 100644 index 0000000000..91903c8c8f --- /dev/null +++ b/drivers/mtd/dataflash.c @@ -0,0 +1,507 @@ +/* LowLevel function for ATMEL DataFlash support + * Author : Hamid Ikdoumi (Atmel) + * + * 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 +#include +#ifdef CONFIG_HAS_DATAFLASH +#include +#include + +AT91S_DATAFLASH_INFO dataflash_info[CFG_MAX_DATAFLASH_BANKS]; +static AT91S_DataFlash DataFlashInst; + +#ifdef CONFIG_AT91SAM9260EK +int cs[][CFG_MAX_DATAFLASH_BANKS] = { + {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0}, /* Logical adress, CS */ + {CFG_DATAFLASH_LOGIC_ADDR_CS1, 1} +}; +#elif defined(CONFIG_AT91SAM9263EK) +int cs[][CFG_MAX_DATAFLASH_BANKS] = { + {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0} /* Logical adress, CS */ +}; +#else +int cs[][CFG_MAX_DATAFLASH_BANKS] = { + {CFG_DATAFLASH_LOGIC_ADDR_CS0, 0}, /* Logical adress, CS */ + {CFG_DATAFLASH_LOGIC_ADDR_CS3, 3} +}; +#endif + +/*define the area offsets*/ +#if defined(CONFIG_AT91SAM9261EK) || defined(CONFIG_AT91SAM9260EK) || defined(CONFIG_AT91SAM9263EK) +#if defined(CONFIG_NEW_PARTITION) +dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { + {0x00000000, 0x00003FFF, FLAG_PROTECT_SET, 0, "Bootstrap"}, /* ROM code */ + {0x00004200, 0x000083FF, FLAG_PROTECT_CLEAR, 0, "Environment"}, /* u-boot environment */ + {0x00008400, 0x0003DDFF, FLAG_PROTECT_SET, 0, "U-Boot"}, /* u-boot code */ + {0x0003DE00, 0x00041FFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "MON"}, /* Room for alternative boot monitor */ + {0x00042000, 0x0018BFFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "OS"}, /* data area size to tune */ + {0x0018C000, 0xFFFFFFFF, FLAG_PROTECT_CLEAR, FLAG_SETENV, "FS"}, /* data area size to tune */ +}; +#else +dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { + {0, 0x3fff, FLAG_PROTECT_SET}, /* ROM code */ + {0x4000, 0x7fff, FLAG_PROTECT_CLEAR}, /* u-boot environment */ + {0x8000, 0x37fff, FLAG_PROTECT_SET}, /* u-boot code */ + {0x38000, 0x1fffff, FLAG_PROTECT_CLEAR}, /* data area size to tune */ +}; +#endif +#elif defined(CONFIG_NEW_PARTITION) +/*define the area offsets*/ +/* Invalid partitions should be defined with start > end */ +dataflash_protect_t area_list[NB_DATAFLASH_AREA*CFG_MAX_DATAFLASH_BANKS] = { + {0x00000000, 0x000083ff, FLAG_PROTECT_SET, 0, "Bootstrap"}, /* ROM code */ + {0x00008400, 0x00020fff, FLAG_PROTECT_SET, 0, "U-Boot"}, /* u-boot code */ + {0x00021000, 0x000293ff, FLAG_PROTECT_CLEAR, 0, "Environment"}, /* u-boot environment 8Kb */ + {0x00029400, 0x00041fff, FLAG_PROTECT_INVALID, 0, ""}, /* Rest of Sector 1 */ + {0x00042000, 0x0018Bfff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "OS"}, /* data area size to tune */ + {0x0018C000, 0xffffffff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "FS"}, /* data area size to tune */ + + {0x00000000, 0xffffffff, FLAG_PROTECT_CLEAR, FLAG_SETENV, "Data"}, /* data area */ + {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ + {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ + {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ + {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ + {0xffffffff, 0x00000000, FLAG_PROTECT_INVALID, 0, ""}, /* Invalid */ +}; +#else +dataflash_protect_t area_list[NB_DATAFLASH_AREA] = { + {0, 0x7fff, FLAG_PROTECT_SET}, /* ROM code */ + {0x8000, 0x1ffff, FLAG_PROTECT_SET}, /* u-boot code */ + {0x20000, 0x27fff, FLAG_PROTECT_CLEAR}, /* u-boot environment */ + {0x28000, 0x1fffff, FLAG_PROTECT_CLEAR}, /* data area size to tune */ +}; +#endif + +extern void AT91F_SpiInit (void); +extern int AT91F_DataflashProbe (int i, AT91PS_DataflashDesc pDesc); +extern int AT91F_DataFlashRead (AT91PS_DataFlash pDataFlash, + unsigned long addr, + unsigned long size, char *buffer); +extern int AT91F_DataFlashWrite( AT91PS_DataFlash pDataFlash, + unsigned char *src, + int dest, + int size ); + +int AT91F_DataflashInit (void) +{ + int i, j; + int dfcode; + int part = 0; + int last_part; + int found[CFG_MAX_DATAFLASH_BANKS]; + unsigned char protected; + + AT91F_SpiInit (); + + for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { + found[i] = 0; + dataflash_info[i].Desc.state = IDLE; + dataflash_info[i].id = 0; + dataflash_info[i].Device.pages_number = 0; + dfcode = AT91F_DataflashProbe (cs[i][1], + &dataflash_info[i].Desc); + + switch (dfcode) { + case AT45DB161: + dataflash_info[i].Device.pages_number = 4096; + dataflash_info[i].Device.pages_size = 528; + dataflash_info[i].Device.page_offset = 10; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i][1]; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i][0]; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB321: + dataflash_info[i].Device.pages_number = 8192; + dataflash_info[i].Device.pages_size = 528; + dataflash_info[i].Device.page_offset = 10; + dataflash_info[i].Device.byte_mask = 0x300; + dataflash_info[i].Device.cs = cs[i][1]; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i][0]; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB642: + dataflash_info[i].Device.pages_number = 8192; + dataflash_info[i].Device.pages_size = 1056; + dataflash_info[i].Device.page_offset = 11; + dataflash_info[i].Device.byte_mask = 0x700; + dataflash_info[i].Device.cs = cs[i][1]; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i][0]; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + case AT45DB128: + dataflash_info[i].Device.pages_number = 16384; + dataflash_info[i].Device.pages_size = 1056; + dataflash_info[i].Device.page_offset = 11; + dataflash_info[i].Device.byte_mask = 0x700; + dataflash_info[i].Device.cs = cs[i][1]; + dataflash_info[i].Desc.DataFlash_state = IDLE; + dataflash_info[i].logical_address = cs[i][0]; + dataflash_info[i].id = dfcode; + found[i] += dfcode;; + break; + + default: + dfcode = 0; + break; + } + /* set the last area end to the dataflash size*/ + area_list[NB_DATAFLASH_AREA -1].end = + (dataflash_info[i].Device.pages_number * + dataflash_info[i].Device.pages_size)-1; + + last_part=0; + /* set the area addresses */ + for(j = 0; jpDataFlashDesc = &(dataflash_info[i].Desc); + pFlash->pDevice = &(dataflash_info[i].Device); + *addr -= dataflash_info[i].logical_address; + return (pFlash); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : addr_dataflash */ +/* Object : Test if address is valid */ +/*---------------------------------------------------------------------------*/ +int addr_dataflash (unsigned long addr) +{ + int addr_valid = 0; + int i; + + for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { + if ((((int) addr) & 0xFF000000) == + dataflash_info[i].logical_address) { + addr_valid = 1; + break; + } + } + + return addr_valid; +} +/*---------------------------------------------------------------------------*/ +/* Function Name : size_dataflash */ +/* Object : Test if address is valid regarding the size */ +/*---------------------------------------------------------------------------*/ +int size_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr, + unsigned long size) +{ + /* is outside the dataflash */ + if (((int)addr & 0x0FFFFFFF) > (pdataFlash->pDevice->pages_size * + pdataFlash->pDevice->pages_number)) return 0; + /* is too large for the dataflash */ + if (size > ((pdataFlash->pDevice->pages_size * + pdataFlash->pDevice->pages_number) - + ((int)addr & 0x0FFFFFFF))) return 0; + + return 1; +} +/*---------------------------------------------------------------------------*/ +/* Function Name : prot_dataflash */ +/* Object : Test if destination area is protected */ +/*---------------------------------------------------------------------------*/ +int prot_dataflash (AT91PS_DataFlash pdataFlash, unsigned long addr) +{ +int area; + /* find area */ + for (area=0; area < NB_DATAFLASH_AREA; area++) { + if ((addr >= pdataFlash->pDevice->area_list[area].start) && + (addr < pdataFlash->pDevice->area_list[area].end)) + break; + } + if (area == NB_DATAFLASH_AREA) + return -1; + + /*test protection value*/ + if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_SET) + return 0; + if (pdataFlash->pDevice->area_list[area].protected == FLAG_PROTECT_INVALID) + return 0; + + return 1; +} +/*--------------------------------------------------------------------------*/ +/* Function Name : dataflash_real_protect */ +/* Object : protect/unprotect area */ +/*--------------------------------------------------------------------------*/ +int dataflash_real_protect (int flag, unsigned long start_addr, + unsigned long end_addr) +{ +int i,j, area1, area2, addr_valid = 0; + /* find dataflash */ + for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { + if ((((int) start_addr) & 0xF0000000) == + dataflash_info[i].logical_address) { + addr_valid = 1; + break; + } + } + if (!addr_valid) { + return -1; + } + /* find start area */ + for (area1=0; area1 < NB_DATAFLASH_AREA; area1++) { + if (start_addr == dataflash_info[i].Device.area_list[area1].start) + break; + } + if (area1 == NB_DATAFLASH_AREA) return -1; + /* find end area */ + for (area2=0; area2 < NB_DATAFLASH_AREA; area2++) { + if (end_addr == dataflash_info[i].Device.area_list[area2].end) + break; + } + if (area2 == NB_DATAFLASH_AREA) + return -1; + + /*set protection value*/ + for(j = area1; j < area2+1 ; j++) + if(dataflash_info[i].Device.area_list[j].protected + != FLAG_PROTECT_INVALID) { + if (flag == 0) { + dataflash_info[i].Device.area_list[j].protected + = FLAG_PROTECT_CLEAR; + } else { + dataflash_info[i].Device.area_list[j].protected + = FLAG_PROTECT_SET; + } + } + + return (area2-area1+1); +} + +/*---------------------------------------------------------------------------*/ +/* Function Name : read_dataflash */ +/* Object : dataflash memory read */ +/*---------------------------------------------------------------------------*/ +int read_dataflash (unsigned long addr, unsigned long size, char *result) +{ + unsigned long AddrToRead = addr; + AT91PS_DataFlash pFlash = &DataFlashInst; + + pFlash = AT91F_DataflashSelect (pFlash, &AddrToRead); + + if (pFlash == 0) + return ERR_UNKNOWN_FLASH_TYPE; + + if (size_dataflash(pFlash,addr,size) == 0) + return ERR_INVAL; + + return (AT91F_DataFlashRead (pFlash, AddrToRead, size, result)); +} + + +/*---------------------------------------------------------------------------*/ +/* Function Name : write_dataflash */ +/* Object : write a block in dataflash */ +/*---------------------------------------------------------------------------*/ +int write_dataflash (unsigned long addr_dest, unsigned long addr_src, + unsigned long size) +{ + unsigned long AddrToWrite = addr_dest; + AT91PS_DataFlash pFlash = &DataFlashInst; + + pFlash = AT91F_DataflashSelect (pFlash, &AddrToWrite); + + if (pFlash == 0) + return ERR_UNKNOWN_FLASH_TYPE; + + if (size_dataflash(pFlash,addr_dest,size) == 0) + return ERR_INVAL; + + if (prot_dataflash(pFlash,addr_dest) == 0) + return ERR_PROTECTED; + + if (AddrToWrite == -1) + return -1; + + return AT91F_DataFlashWrite (pFlash, (uchar *)addr_src, + AddrToWrite, size); +} + + +void dataflash_perror (int err) +{ + switch (err) { + case ERR_OK: + break; + case ERR_TIMOUT: + printf("Timeout writing to DataFlash\n"); + break; + case ERR_PROTECTED: + printf("Can't write to protected/invalid DataFlash sectors\n"); + break; + case ERR_INVAL: + printf("Outside available DataFlash\n"); + break; + case ERR_UNKNOWN_FLASH_TYPE: + printf("Unknown Type of DataFlash\n"); + break; + case ERR_PROG_ERROR: + printf("General DataFlash Programming Error\n"); + break; + default: + printf("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err); + break; + } +} + +#endif diff --git a/drivers/mtd/mw_eeprom.c b/drivers/mtd/mw_eeprom.c new file mode 100644 index 0000000000..2b3348810d --- /dev/null +++ b/drivers/mtd/mw_eeprom.c @@ -0,0 +1,241 @@ +/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */ + +#include + +#ifdef CONFIG_MW_EEPROM + +#include + +/* + * Serial EEPROM opcodes, including start bit + */ +#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */ +#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */ +#define EEP_OPC_READ 0x6 /* 3-bit opcode */ + +#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */ +#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */ +#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */ +#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */ + +static int addrlen; + +static void mw_eeprom_select(int dev) +{ + ssi_set_interface(2048, 0, 0, 0); + ssi_chip_select(0); + udelay(1); + ssi_chip_select(dev); + udelay(1); +} + +static int mw_eeprom_size(int dev) +{ + int x; + u16 res; + + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_READ); + + res = ssi_txrx_byte(0) << 8; + res |= ssi_rx_byte(); + for (x = 0; x < 16; x++) { + if (! (res & 0x8000)) { + break; + } + res <<= 1; + } + ssi_chip_select(0); + + return x; +} + +int mw_eeprom_erase_enable(int dev) +{ + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_ERASE_EN); + ssi_tx_byte(0); + udelay(1); + ssi_chip_select(0); + + return 0; +} + +int mw_eeprom_erase_disable(int dev) +{ + mw_eeprom_select(dev); + ssi_tx_byte(EEP_OPC_ERASE_DIS); + ssi_tx_byte(0); + udelay(1); + ssi_chip_select(0); + + return 0; +} + + +u32 mw_eeprom_read_word(int dev, int addr) +{ + u16 rcv; + u16 res; + int bits; + + mw_eeprom_select(dev); + ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f)); + rcv = ssi_txrx_byte(addr << (13 - addrlen)); + res = rcv << (16 - addrlen); + bits = 4 + addrlen; + + while (bits>0) { + rcv = ssi_rx_byte(); + if (bits > 7) { + res |= rcv << (bits - 8); + } else { + res |= rcv >> (8 - bits); + } + bits -= 8; + } + + ssi_chip_select(0); + + return res; +} + +int mw_eeprom_write_word(int dev, int addr, u16 data) +{ + u8 byte1=0; + u8 byte2=0; + + mw_eeprom_erase_enable(dev); + mw_eeprom_select(dev); + + switch (addrlen) { + case 6: + byte1 = EEP_OPC_WRITE >> 2; + byte2 = (EEP_OPC_WRITE << 6)&0xc0; + byte2 |= addr; + break; + case 7: + byte1 = EEP_OPC_WRITE >> 1; + byte2 = (EEP_OPC_WRITE << 7)&0x80; + byte2 |= addr; + break; + case 8: + byte1 = EEP_OPC_WRITE; + byte2 = addr; + break; + case 9: + byte1 = EEP_OPC_WRITE << 1; + byte1 |= addr >> 8; + byte2 = addr & 0xff; + break; + case 10: + byte1 = EEP_OPC_WRITE << 2; + byte1 |= addr >> 8; + byte2 = addr & 0xff; + break; + default: + printf("Unsupported number of address bits: %d\n", addrlen); + return -1; + + } + + ssi_tx_byte(byte1); + ssi_tx_byte(byte2); + ssi_tx_byte(data >> 8); + ssi_tx_byte(data & 0xff); + ssi_chip_select(0); + udelay(10000); /* Worst case */ + mw_eeprom_erase_disable(dev); + + return 0; +} + + +int mw_eeprom_write(int dev, int addr, u8 *buffer, int len) +{ + int done; + + done = 0; + if (addr & 1) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + temp &= 0xff00; + temp |= buffer[0]; + + mw_eeprom_write_word(dev, addr >> 1, temp); + len--; + addr++; + buffer++; + done++; + } + + while (len <= 2) { + mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer); + len-=2; + addr+=2; + buffer+=2; + done+=2; + } + + if (len) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + temp &= 0x00ff; + temp |= buffer[0] << 8; + + mw_eeprom_write_word(dev, addr >> 1, temp); + len--; + addr++; + buffer++; + done++; + } + + return done; +} + + +int mw_eeprom_read(int dev, int addr, u8 *buffer, int len) +{ + int done; + + done = 0; + if (addr & 1) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + buffer[0]= temp & 0xff; + + len--; + addr++; + buffer++; + done++; + } + + while (len <= 2) { + *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1); + len-=2; + addr+=2; + buffer+=2; + done+=2; + } + + if (len) { + u16 temp = mw_eeprom_read_word(dev, addr >> 1); + buffer[0] = temp >> 8; + + len--; + addr++; + buffer++; + done++; + } + + return done; +} + +int mw_eeprom_probe(int dev) +{ + addrlen = mw_eeprom_size(dev); + + if (addrlen < 6 || addrlen > 10) { + return -1; + } + return 0; +} + +#endif diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile new file mode 100644 index 0000000000..42864f98f4 --- /dev/null +++ b/drivers/mtd/nand/Makefile @@ -0,0 +1,51 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libnand.a + +COBJS-y += nand.o +COBJS-y += nand_base.o +COBJS-y += nand_ids.o +COBJS-y += nand_ecc.o +COBJS-y += nand_bbt.o +COBJS-y += nand_util.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c new file mode 100644 index 0000000000..e17af70d07 --- /dev/null +++ b/drivers/mtd/nand/diskonchip.c @@ -0,0 +1,1787 @@ +/* + * drivers/mtd/nand/diskonchip.c + * + * (C) 2003 Red Hat, Inc. + * (C) 2004 Dan Brown + * (C) 2004 Kalev Lember + * + * Author: David Woodhouse + * Additional Diskonchip 2000 and Millennium support by Dan Brown + * Diskonchip Millennium Plus support by Kalev Lember + * + * 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 + * + * 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 + +#if !defined(CFG_NAND_LEGACY) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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 "); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); +#endif diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c new file mode 100644 index 0000000000..27b5792bcc --- /dev/null +++ b/drivers/mtd/nand/nand.c @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. + * Ladislav Michl + * + * 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 + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include + +#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 int 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; + + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + if (board_nand_init(nand) == 0) { + if (nand_scan(mtd, 1) == 0) { + if (!mtd->name) + mtd->name = (char *)default_nand_name; + } else + mtd->name = NULL; + } else { + mtd->name = NULL; + mtd->size = 0; + } + +} + +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)); + +#ifdef CFG_NAND_SELECT_DEVICE + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); +#endif +} + +#endif diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c new file mode 100644 index 0000000000..151f535c58 --- /dev/null +++ b/drivers/mtd/nand/nand_base.c @@ -0,0 +1,2668 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#endif + +#include + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_JFFS2_NAND +#include +#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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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; + + page = (int)(ofs >> this->page_shift) & this->pagemask; + + if (getchip) { + 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); + } + + if (this->options & NAND_BUSWIDTH_16) { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page); + 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); + 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) / 1000; + else + timeo = (CFG_HZ * 20) / 1000; + + 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); + + reset_timer(); + + while (1) { + if (get_timer(0) > timeo) { + printf("Timeout!"); + return 0x01; + } + + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) + break; + } + } +#ifdef PPCHAMELON_NAND_TIMER_HACK + reset_timer(); + while (get_timer(0) < 10); +#endif /* PPCHAMELON_NAND_TIMER_HACK */ + + 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; + uint *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; + unsigned *oob_config; + int 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; + bufstart = (u_char*) &buf[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; + oob = 0; + this->oobdirty = 1; + 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) { +#ifndef NAND_ALLOW_ERASE_ALL + /* 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; + } +#endif + /* 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 & 0x01)) * (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) { +#ifndef CFG_NAND_QUIET_TEST + printk (KERN_WARNING "No NAND device found!!!\n"); +#endif + 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 diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c new file mode 100644 index 0000000000..19a9bc2a5b --- /dev/null +++ b/drivers/mtd/nand/nand_bbt.c @@ -0,0 +1,1052 @@ +/* + * 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 + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include +#include +#include +#include + +#include + +/** + * 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; + + 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 diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c new file mode 100644 index 0000000000..4c532b0794 --- /dev/null +++ b/drivers/mtd/nand/nand_ecc.c @@ -0,0 +1,200 @@ +/* + * 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 + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include + +/* + * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), + * only nand_correct_data() is needed + */ + +#ifndef CONFIG_NAND_SPL +/* + * 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_calculate_ecc - [NAND Interface] Calculate 3-byte ECC 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) +{ + uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; + int i; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + + /* Build up column parity */ + for(i = 0; i < 256; i++) { + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[*dat++]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) i; + reg2 ^= ~((uint8_t) i); + } + } + + /* Create non-inverted ECC code from line parity */ + tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ + tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ + tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ + tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ + tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ + tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ + tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ + tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ + + tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ + tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ + tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ + tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ + tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ + tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ + tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ + tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + + /* Calculate final ECC code */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + ecc_code[0] = ~tmp2; + ecc_code[1] = ~tmp1; +#else + ecc_code[0] = ~tmp1; + ecc_code[1] = ~tmp2; +#endif + ecc_code[2] = ((~reg1) << 2) | 0x03; + + return 0; +} +#endif /* CONFIG_NAND_SPL */ + +static inline int countbits(uint32_t byte) +{ + int res = 0; + + for (;byte; byte >>= 1) + res += byte & 0x01; + return res; +} + +/** + * 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) +{ + uint8_t s0, s1, s2; + +#ifdef CONFIG_MTD_NAND_ECC_SMC + s0 = calc_ecc[0] ^ read_ecc[0]; + s1 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#else + s1 = calc_ecc[0] ^ read_ecc[0]; + s0 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#endif + if ((s0 | s1 | s2) == 0) + return 0; + + /* Check for a single bit error */ + if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && + ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && + ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + + uint32_t byteoffs, bitnum; + + byteoffs = (s1 << 0) & 0x80; + byteoffs |= (s1 << 1) & 0x40; + byteoffs |= (s1 << 2) & 0x20; + byteoffs |= (s1 << 3) & 0x10; + + byteoffs |= (s0 >> 4) & 0x08; + byteoffs |= (s0 >> 3) & 0x04; + byteoffs |= (s0 >> 2) & 0x02; + byteoffs |= (s0 >> 1) & 0x01; + + bitnum = (s2 >> 5) & 0x04; + bitnum |= (s2 >> 4) & 0x02; + bitnum |= (s2 >> 3) & 0x01; + + dat[byteoffs] ^= (1 << bitnum); + + return 1; + } + + if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) + return 1; + + return -1; +} + +#endif diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c new file mode 100644 index 0000000000..6d7e347fba --- /dev/null +++ b/drivers/mtd/nand/nand_ids.c @@ -0,0 +1,129 @@ +/* + * 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 + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include + +/* +* Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size +*/ +struct nand_flash_dev nand_flash_ids[] = { + {"NAND 1MiB 5V 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"}, + {NAND_MFR_MICRON, "Micron"}, + {0x0, "Unknown"} +}; +#endif diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c new file mode 100644 index 0000000000..4fd4e166e6 --- /dev/null +++ b/drivers/mtd/nand/nand_util.c @@ -0,0 +1,872 @@ +/* + * drivers/nand/nand_util.c + * + * Copyright (C) 2006 by Weiss-Electronic GmbH. + * All rights reserved. + * + * @author: Guido Classen + * @descr: NAND Flash support + * @references: borrowed heavily from Linux mtd-utils code: + * flash_eraseall.c by Arcom Control System Ltd + * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) + * and Thomas Gleixner (tglx@linutronix.de) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 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 + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include +#include +#include +#include + +#include +#include + +typedef struct erase_info erase_info_t; +typedef struct mtd_info mtd_info_t; + +/* support only for native endian JFFS2 */ +#define cpu_to_je16(x) (x) +#define cpu_to_je32(x) (x) + +/*****************************************************************************/ +static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/** + * nand_erase_opts: - erase NAND flash with support for various options + * (jffs2 formating) + * + * @param meminfo NAND device to erase + * @param opts options, @see struct nand_erase_options + * @return 0 in case of success + * + * This code is ported from flash_eraseall.c from Linux mtd utils by + * Arcom Control System Ltd. + */ +int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) +{ + struct jffs2_unknown_node cleanmarker; + int clmpos = 0; + int clmlen = 8; + erase_info_t erase; + ulong erase_length; + int isNAND; + int bbtest = 1; + int result; + int percent_complete = -1; + int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; + const char *mtd_device = meminfo->name; + + memset(&erase, 0, sizeof(erase)); + + erase.mtd = meminfo; + erase.len = meminfo->erasesize; + erase.addr = opts->offset; + erase_length = opts->length; + + isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; + + if (opts->jffs2) { + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + if (isNAND) { + struct nand_oobinfo *oobinfo = &meminfo->oobinfo; + + /* check for autoplacement */ + if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { + /* get the position of the free bytes */ + if (!oobinfo->oobfree[0][1]) { + printf(" Eeep. Autoplacement selected " + "and no empty space in oob\n"); + return -1; + } + clmpos = oobinfo->oobfree[0][0]; + clmlen = oobinfo->oobfree[0][1]; + if (clmlen > 8) + clmlen = 8; + } else { + /* legacy mode */ + switch (meminfo->oobsize) { + case 8: + clmpos = 6; + clmlen = 2; + break; + case 16: + clmpos = 8; + clmlen = 8; + break; + case 64: + clmpos = 16; + clmlen = 8; + break; + } + } + + cleanmarker.totlen = cpu_to_je32(8); + } else { + cleanmarker.totlen = + cpu_to_je32(sizeof(struct jffs2_unknown_node)); + } + cleanmarker.hdr_crc = cpu_to_je32( + crc32_no_comp(0, (unsigned char *) &cleanmarker, + sizeof(struct jffs2_unknown_node) - 4)); + } + + /* scrub option allows to erase badblock. To prevent internal + * check from erase() method, set block check method to dummy + * and disable bad block table while erasing. + */ + if (opts->scrub) { + struct nand_chip *priv_nand = meminfo->priv; + + nand_block_bad_old = priv_nand->block_bad; + priv_nand->block_bad = nand_block_bad_scrub; + /* we don't need the bad block table anymore... + * after scrub, there are no bad blocks left! + */ + if (priv_nand->bbt) { + kfree(priv_nand->bbt); + } + priv_nand->bbt = NULL; + } + + for (; + erase.addr < opts->offset + erase_length; + erase.addr += meminfo->erasesize) { + + WATCHDOG_RESET (); + + if (!opts->scrub && bbtest) { + int ret = meminfo->block_isbad(meminfo, erase.addr); + if (ret > 0) { + if (!opts->quiet) + printf("\rSkipping bad block at " + "0x%08x " + " \n", + erase.addr); + continue; + + } else if (ret < 0) { + printf("\n%s: MTD get bad block failed: %d\n", + mtd_device, + ret); + return -1; + } + } + + result = meminfo->erase(meminfo, &erase); + if (result != 0) { + printf("\n%s: MTD Erase failure: %d\n", + mtd_device, result); + continue; + } + + /* format for JFFS2 ? */ + if (opts->jffs2) { + + /* write cleanmarker */ + if (isNAND) { + size_t written; + result = meminfo->write_oob(meminfo, + erase.addr + clmpos, + clmlen, + &written, + (unsigned char *) + &cleanmarker); + if (result != 0) { + printf("\n%s: MTD writeoob failure: %d\n", + mtd_device, result); + continue; + } + } else { + printf("\n%s: this erase routine only supports" + " NAND devices!\n", + mtd_device); + } + } + + if (!opts->quiet) { + unsigned long long n =(unsigned long long) + (erase.addr + meminfo->erasesize - opts->offset) + * 100; + int percent; + + do_div(n, erase_length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + percent_complete = percent; + + printf("\rErasing at 0x%x -- %3d%% complete.", + erase.addr, percent); + + if (opts->jffs2 && result == 0) + printf(" Cleanmarker written at 0x%x.", + erase.addr); + } + } + } + if (!opts->quiet) + printf("\n"); + + if (nand_block_bad_old) { + struct nand_chip *priv_nand = meminfo->priv; + + priv_nand->block_bad = nand_block_bad_old; + priv_nand->scan_bbt(meminfo); + } + + return 0; +} + +#define MAX_PAGE_SIZE 2048 +#define MAX_OOB_SIZE 64 + +/* + * buffer array used for writing data + */ +static unsigned char data_buf[MAX_PAGE_SIZE]; +static unsigned char oob_buf[MAX_OOB_SIZE]; + +/* OOB layouts to pass into the kernel as default */ +static struct nand_oobinfo none_oobinfo = { + .useecc = MTD_NANDECC_OFF, +}; + +static struct nand_oobinfo jffs2_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +static struct nand_oobinfo yaffs_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +static struct nand_oobinfo autoplace_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE +}; + +/** + * nand_write_opts: - write image to NAND flash with support for various options + * + * @param meminfo NAND device to erase + * @param opts write options (@see nand_write_options) + * @return 0 in case of success + * + * This code is ported from nandwrite.c from Linux mtd utils by + * Steven J. Hill and Thomas Gleixner. + */ +int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) +{ + int imglen = 0; + int pagelen; + int baderaseblock; + int blockstart = -1; + loff_t offs; + int readlen; + int oobinfochanged = 0; + int percent_complete = -1; + struct nand_oobinfo old_oobinfo; + ulong mtdoffset = opts->offset; + ulong erasesize_blockalign; + u_char *buffer = opts->buffer; + size_t written; + int result; + + if (opts->pad && opts->writeoob) { + printf("Can't pad when oob data is present.\n"); + return -1; + } + + /* set erasesize to specified number of blocks - to match + * jffs2 (virtual) block size */ + if (opts->blockalign == 0) { + erasesize_blockalign = meminfo->erasesize; + } else { + erasesize_blockalign = meminfo->erasesize * opts->blockalign; + } + + /* make sure device page sizes are valid */ + if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) + && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) + && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { + printf("Unknown flash (not normal NAND)\n"); + return -1; + } + + /* read the current oob info */ + memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); + + /* write without ecc? */ + if (opts->noecc) { + memcpy(&meminfo->oobinfo, &none_oobinfo, + sizeof(meminfo->oobinfo)); + oobinfochanged = 1; + } + + /* autoplace ECC? */ + if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + + memcpy(&meminfo->oobinfo, &autoplace_oobinfo, + sizeof(meminfo->oobinfo)); + oobinfochanged = 1; + } + + /* force OOB layout for jffs2 or yaffs? */ + if (opts->forcejffs2 || opts->forceyaffs) { + struct nand_oobinfo *oobsel = + opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; + + if (meminfo->oobsize == 8) { + if (opts->forceyaffs) { + printf("YAFSS cannot operate on " + "256 Byte page size\n"); + goto restoreoob; + } + /* Adjust number of ecc bytes */ + jffs2_oobinfo.eccbytes = 3; + } + + memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); + } + + /* get image length */ + imglen = opts->length; + pagelen = meminfo->oobblock + + ((opts->writeoob != 0) ? meminfo->oobsize : 0); + + /* check, if file is pagealigned */ + if ((!opts->pad) && ((imglen % pagelen) != 0)) { + printf("Input block length is not page aligned\n"); + goto restoreoob; + } + + /* check, if length fits into device */ + if (((imglen / pagelen) * meminfo->oobblock) + > (meminfo->size - opts->offset)) { + printf("Image %d bytes, NAND page %d bytes, " + "OOB area %u bytes, device size %u bytes\n", + imglen, pagelen, meminfo->oobblock, meminfo->size); + printf("Input block does not fit into device\n"); + goto restoreoob; + } + + if (!opts->quiet) + printf("\n"); + + /* get data from input and write to the device */ + while (imglen && (mtdoffset < meminfo->size)) { + + WATCHDOG_RESET (); + + /* + * new eraseblock, check for bad block(s). Stay in the + * loop to be sure if the offset changes because of + * a bad block, that the next block that will be + * written to is also checked. Thus avoiding errors if + * the block(s) after the skipped block(s) is also bad + * (number of blocks depending on the blockalign + */ + while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { + blockstart = mtdoffset & (~erasesize_blockalign+1); + offs = blockstart; + baderaseblock = 0; + + /* check all the blocks in an erase block for + * bad blocks */ + do { + int ret = meminfo->block_isbad(meminfo, offs); + + if (ret < 0) { + printf("Bad block check failed\n"); + goto restoreoob; + } + if (ret == 1) { + baderaseblock = 1; + if (!opts->quiet) + printf("\rBad block at 0x%lx " + "in erase block from " + "0x%x will be skipped\n", + (long) offs, + blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + + erasesize_blockalign; + } + offs += erasesize_blockalign + / opts->blockalign; + } while (offs < blockstart + erasesize_blockalign); + } + + readlen = meminfo->oobblock; + if (opts->pad && (imglen < readlen)) { + readlen = imglen; + memset(data_buf + readlen, 0xff, + meminfo->oobblock - readlen); + } + + /* read page data from input memory buffer */ + memcpy(data_buf, buffer, readlen); + buffer += readlen; + + if (opts->writeoob) { + /* read OOB data from input memory block, exit + * on failure */ + memcpy(oob_buf, buffer, meminfo->oobsize); + buffer += meminfo->oobsize; + + /* write OOB data first, as ecc will be placed + * in there*/ + result = meminfo->write_oob(meminfo, + mtdoffset, + meminfo->oobsize, + &written, + (unsigned char *) + &oob_buf); + + if (result != 0) { + printf("\nMTD writeoob failure: %d\n", + result); + goto restoreoob; + } + imglen -= meminfo->oobsize; + } + + /* write out the page data */ + result = meminfo->write(meminfo, + mtdoffset, + meminfo->oobblock, + &written, + (unsigned char *) &data_buf); + + if (result != 0) { + printf("writing NAND page at offset 0x%lx failed\n", + mtdoffset); + goto restoreoob; + } + imglen -= readlen; + + if (!opts->quiet) { + unsigned long long n = (unsigned long long) + (opts->length-imglen) * 100; + int percent; + + do_div(n, opts->length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + printf("\rWriting data at 0x%x " + "-- %3d%% complete.", + mtdoffset, percent); + percent_complete = percent; + } + } + + mtdoffset += meminfo->oobblock; + } + + if (!opts->quiet) + printf("\n"); + +restoreoob: + if (oobinfochanged) { + memcpy(&meminfo->oobinfo, &old_oobinfo, + sizeof(meminfo->oobinfo)); + } + + if (imglen > 0) { + printf("Data did not fit into device, due to bad blocks\n"); + return -1; + } + + /* return happy */ + return 0; +} + +/** + * nand_read_opts: - read image from NAND flash with support for various options + * + * @param meminfo NAND device to erase + * @param opts read options (@see struct nand_read_options) + * @return 0 in case of success + * + */ +int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) +{ + int imglen = opts->length; + int pagelen; + int baderaseblock; + int blockstart = -1; + int percent_complete = -1; + loff_t offs; + size_t readlen; + ulong mtdoffset = opts->offset; + u_char *buffer = opts->buffer; + int result; + + /* make sure device page sizes are valid */ + if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) + && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) + && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { + printf("Unknown flash (not normal NAND)\n"); + return -1; + } + + pagelen = meminfo->oobblock + + ((opts->readoob != 0) ? meminfo->oobsize : 0); + + /* check, if length is not larger than device */ + if (((imglen / pagelen) * meminfo->oobblock) + > (meminfo->size - opts->offset)) { + printf("Image %d bytes, NAND page %d bytes, " + "OOB area %u bytes, device size %u bytes\n", + imglen, pagelen, meminfo->oobblock, meminfo->size); + printf("Input block is larger than device\n"); + return -1; + } + + if (!opts->quiet) + printf("\n"); + + /* get data from input and write to the device */ + while (imglen && (mtdoffset < meminfo->size)) { + + WATCHDOG_RESET (); + + /* + * new eraseblock, check for bad block(s). Stay in the + * loop to be sure if the offset changes because of + * a bad block, that the next block that will be + * written to is also checked. Thus avoiding errors if + * the block(s) after the skipped block(s) is also bad + * (number of blocks depending on the blockalign + */ + while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { + blockstart = mtdoffset & (~meminfo->erasesize+1); + offs = blockstart; + baderaseblock = 0; + + /* check all the blocks in an erase block for + * bad blocks */ + do { + int ret = meminfo->block_isbad(meminfo, offs); + + if (ret < 0) { + printf("Bad block check failed\n"); + return -1; + } + if (ret == 1) { + baderaseblock = 1; + if (!opts->quiet) + printf("\rBad block at 0x%lx " + "in erase block from " + "0x%x will be skipped\n", + (long) offs, + blockstart); + } + + if (baderaseblock) { + mtdoffset = blockstart + + meminfo->erasesize; + } + offs += meminfo->erasesize; + + } while (offs < blockstart + meminfo->erasesize); + } + + + /* read page data to memory buffer */ + result = meminfo->read(meminfo, + mtdoffset, + meminfo->oobblock, + &readlen, + (unsigned char *) &data_buf); + + if (result != 0) { + printf("reading NAND page at offset 0x%lx failed\n", + mtdoffset); + return -1; + } + + if (imglen < readlen) { + readlen = imglen; + } + + memcpy(buffer, data_buf, readlen); + buffer += readlen; + imglen -= readlen; + + if (opts->readoob) { + result = meminfo->read_oob(meminfo, + mtdoffset, + meminfo->oobsize, + &readlen, + (unsigned char *) + &oob_buf); + + if (result != 0) { + printf("\nMTD readoob failure: %d\n", + result); + return -1; + } + + + if (imglen < readlen) { + readlen = imglen; + } + + memcpy(buffer, oob_buf, readlen); + + buffer += readlen; + imglen -= readlen; + } + + if (!opts->quiet) { + unsigned long long n = (unsigned long long) + (opts->length-imglen) * 100; + int percent; + + do_div(n, opts->length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + if (!opts->quiet) + printf("\rReading data from 0x%x " + "-- %3d%% complete.", + mtdoffset, percent); + percent_complete = percent; + } + } + + mtdoffset += meminfo->oobblock; + } + + if (!opts->quiet) + printf("\n"); + + if (imglen > 0) { + printf("Could not read entire image due to bad blocks\n"); + return -1; + } + + /* return happy */ + return 0; +} + +/****************************************************************************** + * Support for locking / unlocking operations of some NAND devices + *****************************************************************************/ + +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_LOCK_TIGHT 0x2c +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 +#define NAND_CMD_LOCK_STATUS 0x7a + +/** + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT + * state + * + * @param meminfo nand mtd instance + * @param tight bring device in lock tight mode + * + * @return 0 on success, -1 in case of error + * + * The lock / lock-tight command only applies to the whole chip. To get some + * parts of the chip lock and others unlocked use the following sequence: + * + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) + * - Call nand_unlock() once for each consecutive area to be unlocked + * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) + * + * If the device is in lock-tight state software can't change the + * current active lock/unlock state of all pages. nand_lock() / nand_unlock() + * calls will fail. It is only posible to leave lock-tight state by + * an hardware signal (low pulse on _WP pin) or by power down. + */ +int nand_lock(nand_info_t *meminfo, int tight) +{ + int ret = 0; + int status; + struct nand_chip *this = meminfo->priv; + + /* select the NAND device */ + this->select_chip(meminfo, 0); + + this->cmdfunc(meminfo, + (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), + -1, -1); + + /* call wait ready function */ + status = this->waitfunc(meminfo, this, FL_WRITING); + + /* see if device thinks it succeeded */ + if (status & 0x01) { + ret = -1; + } + + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +/** + * nand_get_lock_status: - query current lock state from one page of NAND + * flash + * + * @param meminfo nand mtd instance + * @param offset page address to query (muss be page aligned!) + * + * @return -1 in case of error + * >0 lock status: + * bitfield with the following combinations: + * NAND_LOCK_STATUS_TIGHT: page in tight state + * NAND_LOCK_STATUS_LOCK: page locked + * NAND_LOCK_STATUS_UNLOCK: page unlocked + * + */ +int nand_get_lock_status(nand_info_t *meminfo, ulong offset) +{ + int ret = 0; + int chipnr; + int page; + struct nand_chip *this = meminfo->priv; + + /* select the NAND device */ + chipnr = (int)(offset >> this->chip_shift); + this->select_chip(meminfo, chipnr); + + + if ((offset & (meminfo->oobblock - 1)) != 0) { + printf ("nand_get_lock_status: " + "Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + /* check the Lock Status */ + page = (int)(offset >> this->page_shift); + this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); + + ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT + | NAND_LOCK_STATUS_LOCK + | NAND_LOCK_STATUS_UNLOCK); + + out: + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +/** + * nand_unlock: - Unlock area of NAND pages + * only one consecutive area can be unlocked at one time! + * + * @param meminfo nand mtd instance + * @param start start byte address + * @param length number of bytes to unlock (must be a multiple of + * page size nand->oobblock) + * + * @return 0 on success, -1 in case of error + */ +int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) +{ + int ret = 0; + int chipnr; + int status; + int page; + struct nand_chip *this = meminfo->priv; + printf ("nand_unlock: start: %08x, length: %d!\n", + (int)start, (int)length); + + /* select the NAND device */ + chipnr = (int)(start >> this->chip_shift); + this->select_chip(meminfo, chipnr); + + /* check the WP bit */ + this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); + if ((this->read_byte(meminfo) & 0x80) == 0) { + printf ("nand_unlock: Device is write protected!\n"); + ret = -1; + goto out; + } + + if ((start & (meminfo->oobblock - 1)) != 0) { + printf ("nand_unlock: Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { + printf ("nand_unlock: Length must be a multiple of nand page " + "size!\n"); + ret = -1; + goto out; + } + + /* submit address of first page to unlock */ + page = (int)(start >> this->page_shift); + this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); + + /* submit ADDRESS of LAST page to unlock */ + page += (int)(length >> this->page_shift) - 1; + this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); + + /* call wait ready function */ + status = this->waitfunc(meminfo, this, FL_WRITING); + /* see if device thinks it succeeded */ + if (status & 0x01) { + /* there was an error */ + ret = -1; + goto out; + } + + out: + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +#endif diff --git a/drivers/mtd/nand_legacy/Makefile b/drivers/mtd/nand_legacy/Makefile new file mode 100644 index 0000000000..95314d80ba --- /dev/null +++ b/drivers/mtd/nand_legacy/Makefile @@ -0,0 +1,45 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libnand_legacy.a + +COBJS := nand_legacy.o + +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/nand_legacy/nand_legacy.c b/drivers/mtd/nand_legacy/nand_legacy.c new file mode 100644 index 0000000000..49d2ebb67d --- /dev/null +++ b/drivers/mtd/nand_legacy/nand_legacy.c @@ -0,0 +1,1612 @@ +/* + * (C) 2006 Denx + * Driver for NAND support, Rick Bronson + * borrowed heavily from: + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * + * Added 16-bit nand support + * (C) 2004 Texas Instruments + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY) + +#include +#include +#include + +#ifdef CONFIG_OMAP1510 +void archflashwp(void *archdata, int wp); +#endif + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) + +#undef PSYCHO_DEBUG +#undef NAND_DEBUG + +/* ****************** WARNING ********************* + * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will + * erase (or at least attempt to erase) blocks that are marked + * bad. This can be very handy if you are _sure_ that the block + * is OK, say because you marked a good block bad to test bad + * block handling and you are done testing, or if you have + * accidentally marked blocks bad. + * + * Erasing factory marked bad blocks is a _bad_ idea. If the + * erase succeeds there is no reliable way to find them again, + * and attempting to program or erase bad blocks can affect + * the data in _other_ (good) blocks. + */ +#define ALLOW_ERASE_BAD_DEBUG 0 + +#define CONFIG_MTD_NAND_ECC /* enable ECC */ +#define CONFIG_MTD_NAND_ECC_JFFS2 + +/* bits for nand_legacy_rw() `cmd'; or together as needed */ +#define NANDRW_READ 0x01 +#define NANDRW_WRITE 0x00 +#define NANDRW_JFFS2 0x02 +#define NANDRW_JFFS2_SKIP 0x04 + + +/* + * Exported variables etc. + */ + +/* Definition of the out of band configuration structure */ +struct nand_oob_config { + /* position of ECC bytes inside oob */ + int ecc_pos[6]; + /* position of bad blk flag inside oob -1 = inactive */ + int badblock_pos; + /* position of ECC valid flag inside oob -1 = inactive */ + int eccvalid_pos; +} oob_config = { {0}, 0, 0}; + +struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; + +int curr_device = -1; /* Current NAND Device */ + + +/* + * Exported functionss + */ +int nand_legacy_erase(struct nand_chip* nand, size_t ofs, + size_t len, int clean); +int nand_legacy_rw(struct nand_chip* nand, int cmd, + size_t start, size_t len, + size_t * retlen, u_char * buf); +void nand_print(struct nand_chip *nand); +void nand_print_bad(struct nand_chip *nand); +int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, + size_t * retlen, u_char * buf); +int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, + size_t * retlen, const u_char * buf); + +/* + * Internals + */ +static int NanD_WaitReady(struct nand_chip *nand, int ale_wait); +static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, + size_t * retlen, u_char *buf, u_char *ecc_code); +static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, + size_t * retlen, const u_char * buf, + u_char * ecc_code); +#ifdef CONFIG_MTD_NAND_ECC +static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); +static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); +#endif + + +/* + * + * Function definitions + * + */ + +/* returns 0 if block containing pos is OK: + * valid erase block and + * not marked bad, or no bad mark position is specified + * returns 1 if marked bad or otherwise invalid + */ +static int check_block (struct nand_chip *nand, unsigned long pos) +{ + size_t retlen; + uint8_t oob_data; + uint16_t oob_data16[6]; + int page0 = pos & (-nand->erasesize); + int page1 = page0 + nand->oobblock; + int badpos = oob_config.badblock_pos; + + if (pos >= nand->totlen) + return 1; + + if (badpos < 0) + return 0; /* no way to check, assume OK */ + + if (nand->bus16) { + if (nand_read_oob(nand, (page0 + 0), 12, &retlen, (uint8_t *)oob_data16) + || (oob_data16[2] & 0xff00) != 0xff00) + return 1; + if (nand_read_oob(nand, (page1 + 0), 12, &retlen, (uint8_t *)oob_data16) + || (oob_data16[2] & 0xff00) != 0xff00) + return 1; + } else { + /* Note - bad block marker can be on first or second page */ + if (nand_read_oob(nand, page0 + badpos, 1, &retlen, (unsigned char *)&oob_data) + || oob_data != 0xff + || nand_read_oob (nand, page1 + badpos, 1, &retlen, (unsigned char *)&oob_data) + || oob_data != 0xff) + return 1; + } + + return 0; +} + +/* print bad blocks in NAND flash */ +void nand_print_bad(struct nand_chip* nand) +{ + unsigned long pos; + + for (pos = 0; pos < nand->totlen; pos += nand->erasesize) { + if (check_block(nand, pos)) + printf(" 0x%8.8lx\n", pos); + } + puts("\n"); +} + +/* cmd: 0: NANDRW_WRITE write, fail on bad block + * 1: NANDRW_READ read, fail on bad block + * 2: NANDRW_WRITE | NANDRW_JFFS2 write, skip bad blocks + * 3: NANDRW_READ | NANDRW_JFFS2 read, data all 0xff for bad blocks + * 7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks + */ +int nand_legacy_rw (struct nand_chip* nand, int cmd, + size_t start, size_t len, + size_t * retlen, u_char * buf) +{ + int ret = 0, n, total = 0; + char eccbuf[6]; + /* eblk (once set) is the start of the erase block containing the + * data being processed. + */ + unsigned long eblk = ~0; /* force mismatch on first pass */ + unsigned long erasesize = nand->erasesize; + + while (len) { + if ((start & (-erasesize)) != eblk) { + /* have crossed into new erase block, deal with + * it if it is sure marked bad. + */ + eblk = start & (-erasesize); /* start of block */ + if (check_block(nand, eblk)) { + if (cmd == (NANDRW_READ | NANDRW_JFFS2)) { + while (len > 0 && + start - eblk < erasesize) { + *(buf++) = 0xff; + ++start; + ++total; + --len; + } + continue; + } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) { + start += erasesize; + continue; + } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) { + /* skip bad block */ + start += erasesize; + continue; + } else { + ret = 1; + break; + } + } + } + /* The ECC will not be calculated correctly if + less than 512 is written or read */ + /* Is request at least 512 bytes AND it starts on a proper boundry */ + if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200)) + printf("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\n"); + + if (cmd & NANDRW_READ) { + ret = nand_read_ecc(nand, start, + min(len, eblk + erasesize - start), + (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); + } else { + ret = nand_write_ecc(nand, start, + min(len, eblk + erasesize - start), + (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); + } + + if (ret) + break; + + start += n; + buf += n; + total += n; + len -= n; + } + if (retlen) + *retlen = total; + + return ret; +} + +void nand_print(struct nand_chip *nand) +{ + if (nand->numchips > 1) { + printf("%s at 0x%lx,\n" + "\t %d chips %s, size %d MB, \n" + "\t total size %ld MB, sector size %ld kB\n", + nand->name, nand->IO_ADDR, nand->numchips, + nand->chips_name, 1 << (nand->chipshift - 20), + nand->totlen >> 20, nand->erasesize >> 10); + } + else { + printf("%s at 0x%lx (", nand->chips_name, nand->IO_ADDR); + print_size(nand->totlen, ", "); + print_size(nand->erasesize, " sector)\n"); + } +} + +/* ------------------------------------------------------------------------- */ + +static int NanD_WaitReady(struct nand_chip *nand, int ale_wait) +{ + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; + +#ifdef NAND_NO_RB /* in config file, shorter delays currently wrap accesses */ + if(ale_wait) + NAND_WAIT_READY(nand); /* do the worst case 25us wait */ + else + udelay(10); +#else /* has functional r/b signal */ + NAND_WAIT_READY(nand); +#endif + return ret; +} + +/* NanD_Command: Send a flash command to the flash chip */ + +static inline int NanD_Command(struct nand_chip *nand, unsigned char command) +{ + unsigned long nandptr = nand->IO_ADDR; + + /* Assert the CLE (Command Latch Enable) line to the flash chip */ + NAND_CTL_SETCLE(nandptr); + + /* Send the command */ + WRITE_NAND_COMMAND(command, nandptr); + + /* Lower the CLE line */ + NAND_CTL_CLRCLE(nandptr); + +#ifdef NAND_NO_RB + if(command == NAND_CMD_RESET){ + u_char ret_val; + NanD_Command(nand, NAND_CMD_STATUS); + do { + ret_val = READ_NAND(nandptr);/* wait till ready */ + } while((ret_val & 0x40) != 0x40); + } +#endif + return NanD_WaitReady(nand, 0); +} + +/* NanD_Address: Set the current address for the flash chip */ + +static int NanD_Address(struct nand_chip *nand, int numbytes, unsigned long ofs) +{ + unsigned long nandptr; + int i; + + nandptr = nand->IO_ADDR; + + /* Assert the ALE (Address Latch Enable) line to the flash chip */ + NAND_CTL_SETALE(nandptr); + + /* Send the address */ + /* Devices with 256-byte page are addressed as: + * Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) + * there is no device on the market with page256 + * and more than 24 bits. + * Devices with 512-byte page are addressed as: + * Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) + * 25-31 is sent only if the chip support it. + * bit 8 changes the read command to be sent + * (NAND_CMD_READ0 or NAND_CMD_READ1). + */ + + if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) + WRITE_NAND_ADDRESS(ofs, nandptr); + + ofs = ofs >> nand->page_shift; + + if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { + for (i = 0; i < nand->pageadrlen; i++, ofs = ofs >> 8) { + WRITE_NAND_ADDRESS(ofs, nandptr); + } + } + + /* Lower the ALE line */ + NAND_CTL_CLRALE(nandptr); + + /* Wait for the chip to respond */ + return NanD_WaitReady(nand, 1); +} + +/* NanD_SelectChip: Select a given flash chip within the current floor */ + +static inline int NanD_SelectChip(struct nand_chip *nand, int chip) +{ + /* Wait for it to be ready */ + return NanD_WaitReady(nand, 0); +} + +/* NanD_IdentChip: Identify a given NAND chip given {floor,chip} */ + +static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) +{ + int mfr, id, i; + + NAND_ENABLE_CE(nand); /* set pin low */ + /* Reset the chip */ + if (NanD_Command(nand, NAND_CMD_RESET)) { +#ifdef NAND_DEBUG + printf("NanD_Command (reset) for %d,%d returned true\n", + floor, chip); +#endif + NAND_DISABLE_CE(nand); /* set pin high */ + return 0; + } + + /* Read the NAND chip ID: 1. Send ReadID command */ + if (NanD_Command(nand, NAND_CMD_READID)) { +#ifdef NAND_DEBUG + printf("NanD_Command (ReadID) for %d,%d returned true\n", + floor, chip); +#endif + NAND_DISABLE_CE(nand); /* set pin high */ + return 0; + } + + /* Read the NAND chip ID: 2. Send address byte zero */ + NanD_Address(nand, ADDR_COLUMN, 0); + + /* Read the manufacturer and device id codes from the device */ + + mfr = READ_NAND(nand->IO_ADDR); + + id = READ_NAND(nand->IO_ADDR); + + NAND_DISABLE_CE(nand); /* set pin high */ + +#ifdef NAND_DEBUG + printf("NanD_Command (ReadID) got %x %x\n", mfr, id); +#endif + if (mfr == 0xff || mfr == 0) { + /* No response - return failure */ + return 0; + } + + /* Check it's the same as the first chip we identified. + * M-Systems say that any given nand_chip device should only + * contain _one_ type of flash part, although that's not a + * hardware restriction. */ + if (nand->mfr) { + if (nand->mfr == mfr && nand->id == id) { + return 1; /* This is another the same the first */ + } else { + printf("Flash chip at floor %d, chip %d is different:\n", + floor, chip); + } + } + + /* Print and store the manufacturer and ID codes. */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (mfr == nand_flash_ids[i].manufacture_id && + id == nand_flash_ids[i].model_id) { +#ifdef NAND_DEBUG + printf("Flash chip found:\n\t Manufacturer ID: 0x%2.2X, " + "Chip ID: 0x%2.2X (%s)\n", mfr, id, + nand_flash_ids[i].name); +#endif + if (!nand->mfr) { + nand->mfr = mfr; + nand->id = id; + nand->chipshift = + nand_flash_ids[i].chipshift; + nand->page256 = nand_flash_ids[i].page256; + nand->eccsize = 256; + if (nand->page256) { + nand->oobblock = 256; + nand->oobsize = 8; + nand->page_shift = 8; + } else { + nand->oobblock = 512; + nand->oobsize = 16; + nand->page_shift = 9; + } + nand->pageadrlen = nand_flash_ids[i].pageadrlen; + nand->erasesize = nand_flash_ids[i].erasesize; + nand->chips_name = nand_flash_ids[i].name; + nand->bus16 = nand_flash_ids[i].bus16; + return 1; + } + return 0; + } + } + + +#ifdef NAND_DEBUG + /* We haven't fully identified the chip. Print as much as we know. */ + printf("Unknown flash chip found: %2.2X %2.2X\n", + id, mfr); +#endif + + return 0; +} + +/* NanD_ScanChips: Find all NAND chips present in a nand_chip, and identify them */ + +static void NanD_ScanChips(struct nand_chip *nand) +{ + int floor, chip; + int numchips[NAND_MAX_FLOORS]; + int maxchips = NAND_MAX_CHIPS; + int ret = 1; + + nand->numchips = 0; + nand->mfr = 0; + nand->id = 0; + + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { + ret = 1; + numchips[floor] = 0; + for (chip = 0; chip < maxchips && ret != 0; chip++) { + + ret = NanD_IdentChip(nand, floor, chip); + if (ret) { + numchips[floor]++; + nand->numchips++; + } + } + } + + /* If there are none at all that we recognise, bail */ + if (!nand->numchips) { +#ifdef NAND_DEBUG + puts ("No NAND flash chips recognised.\n"); +#endif + return; + } + + /* Allocate an array to hold the information for each chip */ + nand->chips = malloc(sizeof(struct Nand) * nand->numchips); + if (!nand->chips) { + puts ("No memory for allocating chip info structures\n"); + return; + } + + ret = 0; + + /* Fill out the chip array with {floor, chipno} for each + * detected chip in the device. */ + for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { + for (chip = 0; chip < numchips[floor]; chip++) { + nand->chips[ret].floor = floor; + nand->chips[ret].chip = chip; + nand->chips[ret].curadr = 0; + nand->chips[ret].curmode = 0x50; + ret++; + } + } + + /* Calculate and print the total size of the device */ + nand->totlen = nand->numchips * (1 << nand->chipshift); + +#ifdef NAND_DEBUG + printf("%d flash chips found. Total nand_chip size: %ld MB\n", + nand->numchips, nand->totlen >> 20); +#endif +} + +/* we need to be fast here, 1 us per read translates to 1 second per meg */ +static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr) +{ + unsigned long nandptr = nand->IO_ADDR; + + NanD_Command (nand, NAND_CMD_READ0); + + if (nand->bus16) { + u16 val; + + while (cntr >= 16) { + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + cntr -= 16; + } + + while (cntr > 0) { + val = READ_NAND (nandptr); + *data_buf++ = val & 0xff; + *data_buf++ = val >> 8; + cntr -= 2; + } + } else { + while (cntr >= 16) { + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + *data_buf++ = READ_NAND (nandptr); + cntr -= 16; + } + + while (cntr > 0) { + *data_buf++ = READ_NAND (nandptr); + cntr--; + } + } +} + +/* + * NAND read with ECC + */ +static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, + size_t * retlen, u_char *buf, u_char *ecc_code) +{ + int col, page; + int ecc_status = 0; +#ifdef CONFIG_MTD_NAND_ECC + int j; + int ecc_failed = 0; + u_char *data_poi; + u_char ecc_calc[6]; +#endif + + /* Do not allow reads past end of device */ + if ((start + len) > nand->totlen) { + printf ("%s: Attempt read beyond end of device %x %x %x\n", + __FUNCTION__, (uint) start, (uint) len, (uint) nand->totlen); + *retlen = 0; + return -1; + } + + /* First we calculate the starting page */ + /*page = shr(start, nand->page_shift);*/ + page = start >> nand->page_shift; + + /* Get raw starting column */ + col = start & (nand->oobblock - 1); + + /* Initialize return value */ + *retlen = 0; + + /* Select the NAND device */ + NAND_ENABLE_CE(nand); /* set pin low */ + + /* Loop until all data read */ + while (*retlen < len) { + +#ifdef CONFIG_MTD_NAND_ECC + /* Do we have this page in cache ? */ + if (nand->cache_page == page) + goto readdata; + /* Send the read command */ + NanD_Command(nand, NAND_CMD_READ0); + if (nand->bus16) { + NanD_Address(nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address(nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + + /* Read in a page + oob data */ + NanD_ReadBuf(nand, nand->data_buf, nand->oobblock + nand->oobsize); + + /* copy data into cache, for read out of cache and if ecc fails */ + if (nand->data_cache) { + memcpy (nand->data_cache, nand->data_buf, + nand->oobblock + nand->oobsize); + } + + /* Pick the ECC bytes out of the oob data */ + for (j = 0; j < 6; j++) { + ecc_code[j] = nand->data_buf[(nand->oobblock + oob_config.ecc_pos[j])]; + } + + /* Calculate the ECC and verify it */ + /* If block was not written with ECC, skip ECC */ + if (oob_config.eccvalid_pos != -1 && + (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0x0f) != 0x0f) { + + nand_calculate_ecc (&nand->data_buf[0], &ecc_calc[0]); + switch (nand_correct_data (&nand->data_buf[0], &ecc_code[0], &ecc_calc[0])) { + case -1: + printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); + ecc_failed++; + break; + case 1: + case 2: /* transfer ECC corrected data to cache */ + if (nand->data_cache) + memcpy (nand->data_cache, nand->data_buf, 256); + break; + } + } + + if (oob_config.eccvalid_pos != -1 && + nand->oobblock == 512 && (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0xf0) != 0xf0) { + + nand_calculate_ecc (&nand->data_buf[256], &ecc_calc[3]); + switch (nand_correct_data (&nand->data_buf[256], &ecc_code[3], &ecc_calc[3])) { + case -1: + printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); + ecc_failed++; + break; + case 1: + case 2: /* transfer ECC corrected data to cache */ + if (nand->data_cache) + memcpy (&nand->data_cache[256], &nand->data_buf[256], 256); + break; + } + } +readdata: + /* Read the data from ECC data buffer into return buffer */ + data_poi = (nand->data_cache) ? nand->data_cache : nand->data_buf; + data_poi += col; + if ((*retlen + (nand->oobblock - col)) >= len) { + memcpy (buf + *retlen, data_poi, len - *retlen); + *retlen = len; + } else { + memcpy (buf + *retlen, data_poi, nand->oobblock - col); + *retlen += nand->oobblock - col; + } + /* Set cache page address, invalidate, if ecc_failed */ + nand->cache_page = (nand->data_cache && !ecc_failed) ? page : -1; + + ecc_status += ecc_failed; + ecc_failed = 0; + +#else + /* Send the read command */ + NanD_Command(nand, NAND_CMD_READ0); + if (nand->bus16) { + NanD_Address(nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address(nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + + /* Read the data directly into the return buffer */ + if ((*retlen + (nand->oobblock - col)) >= len) { + NanD_ReadBuf(nand, buf + *retlen, len - *retlen); + *retlen = len; + /* We're done */ + continue; + } else { + NanD_ReadBuf(nand, buf + *retlen, nand->oobblock - col); + *retlen += nand->oobblock - col; + } +#endif + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + page++; + } + + /* De-select the NAND device */ + NAND_DISABLE_CE(nand); /* set pin high */ + + /* + * Return success, if no ECC failures, else -EIO + * fs driver will take care of that, because + * retlen == desired len and result == -EIO + */ + return ecc_status ? -1 : 0; +} + +/* + * Nand_page_program function is used for write and writev ! + */ +static int nand_write_page (struct nand_chip *nand, + int page, int col, int last, u_char * ecc_code) +{ + + int i; + unsigned long nandptr = nand->IO_ADDR; + +#ifdef CONFIG_MTD_NAND_ECC +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + int ecc_bytes = (nand->oobblock == 512) ? 6 : 3; +#endif +#endif + /* pad oob area */ + for (i = nand->oobblock; i < nand->oobblock + nand->oobsize; i++) + nand->data_buf[i] = 0xff; + +#ifdef CONFIG_MTD_NAND_ECC + /* Zero out the ECC array */ + for (i = 0; i < 6; i++) + ecc_code[i] = 0x00; + + /* Read back previous written data, if col > 0 */ + if (col) { + NanD_Command (nand, NAND_CMD_READ0); + if (nand->bus16) { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + + if (nand->bus16) { + u16 val; + + for (i = 0; i < col; i += 2) { + val = READ_NAND (nandptr); + nand->data_buf[i] = val & 0xff; + nand->data_buf[i + 1] = val >> 8; + } + } else { + for (i = 0; i < col; i++) + nand->data_buf[i] = READ_NAND (nandptr); + } + } + + /* Calculate and write the ECC if we have enough data */ + if ((col < nand->eccsize) && (last >= nand->eccsize)) { + nand_calculate_ecc (&nand->data_buf[0], &(ecc_code[0])); + for (i = 0; i < 3; i++) { + nand->data_buf[(nand->oobblock + + oob_config.ecc_pos[i])] = ecc_code[i]; + } + if (oob_config.eccvalid_pos != -1) { + nand->data_buf[nand->oobblock + + oob_config.eccvalid_pos] = 0xf0; + } + } + + /* Calculate and write the second ECC if we have enough data */ + if ((nand->oobblock == 512) && (last == nand->oobblock)) { + nand_calculate_ecc (&nand->data_buf[256], &(ecc_code[3])); + for (i = 3; i < 6; i++) { + nand->data_buf[(nand->oobblock + + oob_config.ecc_pos[i])] = ecc_code[i]; + } + if (oob_config.eccvalid_pos != -1) { + nand->data_buf[nand->oobblock + + oob_config.eccvalid_pos] &= 0x0f; + } + } +#endif + /* Prepad for partial page programming !!! */ + for (i = 0; i < col; i++) + nand->data_buf[i] = 0xff; + + /* Postpad for partial page programming !!! oob is already padded */ + for (i = last; i < nand->oobblock; i++) + nand->data_buf[i] = 0xff; + + /* Send command to begin auto page programming */ + NanD_Command (nand, NAND_CMD_READ0); + NanD_Command (nand, NAND_CMD_SEQIN); + if (nand->bus16) { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + + /* Write out complete page of data */ + if (nand->bus16) { + for (i = 0; i < (nand->oobblock + nand->oobsize); i += 2) { + WRITE_NAND (nand->data_buf[i] + + (nand->data_buf[i + 1] << 8), + nand->IO_ADDR); + } + } else { + for (i = 0; i < (nand->oobblock + nand->oobsize); i++) + WRITE_NAND (nand->data_buf[i], nand->IO_ADDR); + } + + /* Send command to actually program the data */ + NanD_Command (nand, NAND_CMD_PAGEPROG); + NanD_Command (nand, NAND_CMD_STATUS); +#ifdef NAND_NO_RB + { + u_char ret_val; + + do { + ret_val = READ_NAND (nandptr); /* wait till ready */ + } while ((ret_val & 0x40) != 0x40); + } +#endif + /* See if device thinks it succeeded */ + if (READ_NAND (nand->IO_ADDR) & 0x01) { + printf ("%s: Failed write, page 0x%08x, ", __FUNCTION__, + page); + return -1; + } +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* + * 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. + */ + + /* Send command to read back the page */ + if (col < nand->eccsize) + NanD_Command (nand, NAND_CMD_READ0); + else + NanD_Command (nand, NAND_CMD_READ1); + if (nand->bus16) { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + + /* Loop through and verify the data */ + if (nand->bus16) { + for (i = col; i < last; i = +2) { + if ((nand->data_buf[i] + + (nand->data_buf[i + 1] << 8)) != READ_NAND (nand->IO_ADDR)) { + printf ("%s: Failed write verify, page 0x%08x ", + __FUNCTION__, page); + return -1; + } + } + } else { + for (i = col; i < last; i++) { + if (nand->data_buf[i] != READ_NAND (nand->IO_ADDR)) { + printf ("%s: Failed write verify, page 0x%08x ", + __FUNCTION__, page); + return -1; + } + } + } + +#ifdef CONFIG_MTD_NAND_ECC + /* + * We also want to check that the ECC bytes wrote + * correctly for the same reasons stated above. + */ + NanD_Command (nand, NAND_CMD_READOOB); + if (nand->bus16) { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + (col >> 1)); + } else { + NanD_Address (nand, ADDR_COLUMN_PAGE, + (page << nand->page_shift) + col); + } + if (nand->bus16) { + for (i = 0; i < nand->oobsize; i += 2) { + u16 val; + + val = READ_NAND (nand->IO_ADDR); + nand->data_buf[i] = val & 0xff; + nand->data_buf[i + 1] = val >> 8; + } + } else { + for (i = 0; i < nand->oobsize; i++) { + nand->data_buf[i] = READ_NAND (nand->IO_ADDR); + } + } + for (i = 0; i < ecc_bytes; i++) { + if ((nand->data_buf[(oob_config.ecc_pos[i])] != ecc_code[i]) && ecc_code[i]) { + printf ("%s: Failed ECC write " + "verify, page 0x%08x, " + "%6i bytes were succesful\n", + __FUNCTION__, page, i); + return -1; + } + } +#endif /* CONFIG_MTD_NAND_ECC */ +#endif /* CONFIG_MTD_NAND_VERIFY_WRITE */ + return 0; +} + +static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * ecc_code) +{ + int i, page, col, cnt, ret = 0; + + /* Do not allow write past end of device */ + if ((to + len) > nand->totlen) { + printf ("%s: Attempt to write past end of page\n", __FUNCTION__); + return -1; + } + + /* Shift to get page */ + page = ((int) to) >> nand->page_shift; + + /* Get the starting column */ + col = to & (nand->oobblock - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Select the NAND device */ +#ifdef CONFIG_OMAP1510 + archflashwp(0,0); +#endif +#ifdef CFG_NAND_WP + NAND_WP_OFF(); +#endif + + NAND_ENABLE_CE(nand); /* set pin low */ + + /* Check the WP bit */ + NanD_Command(nand, NAND_CMD_STATUS); + if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { + printf ("%s: Device is write protected!!!\n", __FUNCTION__); + ret = -1; + goto out; + } + + /* Loop until all data is written */ + while (*retlen < len) { + /* Invalidate cache, if we write to this page */ + if (nand->cache_page == page) + nand->cache_page = -1; + + /* Write data into buffer */ + if ((col + len) >= nand->oobblock) { + for (i = col, cnt = 0; i < nand->oobblock; i++, cnt++) { + nand->data_buf[i] = buf[(*retlen + cnt)]; + } + } else { + for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++) { + nand->data_buf[i] = buf[(*retlen + cnt)]; + } + } + /* We use the same function for write and writev !) */ + ret = nand_write_page (nand, page, col, i, ecc_code); + if (ret) + goto out; + + /* Next data start at page boundary */ + col = 0; + + /* Update written bytes count */ + *retlen += cnt; + + /* Increment page address */ + page++; + } + + /* Return happy */ + *retlen = len; + +out: + /* De-select the NAND device */ + NAND_DISABLE_CE(nand); /* set pin high */ +#ifdef CONFIG_OMAP1510 + archflashwp(0,1); +#endif +#ifdef CFG_NAND_WP + NAND_WP_ON(); +#endif + + return ret; +} + +/* read from the 16 bytes of oob data that correspond to a 512 byte + * page or 2 256-byte pages. + */ +int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, + size_t * retlen, u_char * buf) +{ + int len256 = 0; + struct Nand *mychip; + int ret = 0; + + mychip = &nand->chips[ofs >> nand->chipshift]; + + /* update address for 2M x 8bit devices. OOB starts on the second */ + /* page to maintain compatibility with nand_read_ecc. */ + if (nand->page256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + NAND_ENABLE_CE(nand); /* set pin low */ + NanD_Command(nand, NAND_CMD_READOOB); + if (nand->bus16) { + NanD_Address(nand, ADDR_COLUMN_PAGE, + ((ofs >> nand->page_shift) << nand->page_shift) + + ((ofs & (nand->oobblock - 1)) >> 1)); + } else { + NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + } + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + NanD_ReadBuf(nand, buf, len256); + + NanD_Command(nand, NAND_CMD_READOOB); + NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); + } + + NanD_ReadBuf(nand, &buf[len256], len - len256); + + *retlen = len; + /* Reading the full OOB data drops us off of the end of the page, + * causing the flash device to go into busy mode, so we need + * to wait until ready 11.4.1 and Toshiba TC58256FT nands */ + + ret = NanD_WaitReady(nand, 1); + NAND_DISABLE_CE(nand); /* set pin high */ + + return ret; + +} + +/* write to the 16 bytes of oob data that correspond to a 512 byte + * page or 2 256-byte pages. + */ +int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, + size_t * retlen, const u_char * buf) +{ + int len256 = 0; + int i; + unsigned long nandptr = nand->IO_ADDR; + +#ifdef PSYCHO_DEBUG + printf("nand_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n", + (long)ofs, len, buf[0], buf[1], buf[2], buf[3], + buf[8], buf[9], buf[14],buf[15]); +#endif + + NAND_ENABLE_CE(nand); /* set pin low to enable chip */ + + /* Reset the chip */ + NanD_Command(nand, NAND_CMD_RESET); + + /* issue the Read2 command to set the pointer to the Spare Data Area. */ + NanD_Command(nand, NAND_CMD_READOOB); + if (nand->bus16) { + NanD_Address(nand, ADDR_COLUMN_PAGE, + ((ofs >> nand->page_shift) << nand->page_shift) + + ((ofs & (nand->oobblock - 1)) >> 1)); + } else { + NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + } + + /* update address for 2M x 8bit devices. OOB starts on the second */ + /* page to maintain compatibility with nand_read_ecc. */ + if (nand->page256) { + if (!(ofs & 0x8)) + ofs += 0x100; + else + ofs -= 0x8; + } + + /* issue the Serial Data In command to initial the Page Program process */ + NanD_Command(nand, NAND_CMD_SEQIN); + if (nand->bus16) { + NanD_Address(nand, ADDR_COLUMN_PAGE, + ((ofs >> nand->page_shift) << nand->page_shift) + + ((ofs & (nand->oobblock - 1)) >> 1)); + } else { + NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); + } + + /* treat crossing 8-byte OOB data for 2M x 8bit devices */ + /* Note: datasheet says it should automaticaly wrap to the */ + /* next OOB block, but it didn't work here. mf. */ + if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { + len256 = (ofs | 0x7) + 1 - ofs; + for (i = 0; i < len256; i++) + WRITE_NAND(buf[i], nandptr); + + NanD_Command(nand, NAND_CMD_PAGEPROG); + NanD_Command(nand, NAND_CMD_STATUS); +#ifdef NAND_NO_RB + { u_char ret_val; + do { + ret_val = READ_NAND(nandptr); /* wait till ready */ + } while ((ret_val & 0x40) != 0x40); + } +#endif + if (READ_NAND(nandptr) & 1) { + puts ("Error programming oob data\n"); + /* There was an error */ + NAND_DISABLE_CE(nand); /* set pin high */ + *retlen = 0; + return -1; + } + NanD_Command(nand, NAND_CMD_SEQIN); + NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); + } + + if (nand->bus16) { + for (i = len256; i < len; i += 2) { + WRITE_NAND(buf[i] + (buf[i+1] << 8), nandptr); + } + } else { + for (i = len256; i < len; i++) + WRITE_NAND(buf[i], nandptr); + } + + NanD_Command(nand, NAND_CMD_PAGEPROG); + NanD_Command(nand, NAND_CMD_STATUS); +#ifdef NAND_NO_RB + { u_char ret_val; + do { + ret_val = READ_NAND(nandptr); /* wait till ready */ + } while ((ret_val & 0x40) != 0x40); + } +#endif + if (READ_NAND(nandptr) & 1) { + puts ("Error programming oob data\n"); + /* There was an error */ + NAND_DISABLE_CE(nand); /* set pin high */ + *retlen = 0; + return -1; + } + + NAND_DISABLE_CE(nand); /* set pin high */ + *retlen = len; + return 0; + +} + +int nand_legacy_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean) +{ + /* This is defined as a structure so it will work on any system + * using native endian jffs2 (the default). + */ + static struct jffs2_unknown_node clean_marker = { + JFFS2_MAGIC_BITMASK, + JFFS2_NODETYPE_CLEANMARKER, + 8 /* 8 bytes in this node */ + }; + unsigned long nandptr; + struct Nand *mychip; + int ret = 0; + + if (ofs & (nand->erasesize-1) || len & (nand->erasesize-1)) { + printf ("Offset and size must be sector aligned, erasesize = %d\n", + (int) nand->erasesize); + return -1; + } + + nandptr = nand->IO_ADDR; + + /* Select the NAND device */ +#ifdef CONFIG_OMAP1510 + archflashwp(0,0); +#endif +#ifdef CFG_NAND_WP + NAND_WP_OFF(); +#endif + NAND_ENABLE_CE(nand); /* set pin low */ + + /* Check the WP bit */ + NanD_Command(nand, NAND_CMD_STATUS); + if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { + printf ("nand_write_ecc: Device is write protected!!!\n"); + ret = -1; + goto out; + } + + /* Check the WP bit */ + NanD_Command(nand, NAND_CMD_STATUS); + if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { + printf ("%s: Device is write protected!!!\n", __FUNCTION__); + ret = -1; + goto out; + } + + /* FIXME: Do nand in the background. Use timers or schedule_task() */ + while(len) { + /*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/ + mychip = &nand->chips[ofs >> nand->chipshift]; + + /* always check for bad block first, genuine bad blocks + * should _never_ be erased. + */ + if (ALLOW_ERASE_BAD_DEBUG || !check_block(nand, ofs)) { + /* Select the NAND device */ + NAND_ENABLE_CE(nand); /* set pin low */ + + NanD_Command(nand, NAND_CMD_ERASE1); + NanD_Address(nand, ADDR_PAGE, ofs); + NanD_Command(nand, NAND_CMD_ERASE2); + + NanD_Command(nand, NAND_CMD_STATUS); + +#ifdef NAND_NO_RB + { u_char ret_val; + do { + ret_val = READ_NAND(nandptr); /* wait till ready */ + } while ((ret_val & 0x40) != 0x40); + } +#endif + if (READ_NAND(nandptr) & 1) { + printf ("%s: Error erasing at 0x%lx\n", + __FUNCTION__, (long)ofs); + /* There was an error */ + ret = -1; + goto out; + } + if (clean) { + int n; /* return value not used */ + int p, l; + + /* clean marker position and size depend + * on the page size, since 256 byte pages + * only have 8 bytes of oob data + */ + if (nand->page256) { + p = NAND_JFFS2_OOB8_FSDAPOS; + l = NAND_JFFS2_OOB8_FSDALEN; + } else { + p = NAND_JFFS2_OOB16_FSDAPOS; + l = NAND_JFFS2_OOB16_FSDALEN; + } + + ret = nand_write_oob(nand, ofs + p, l, (size_t *)&n, + (u_char *)&clean_marker); + /* quit here if write failed */ + if (ret) + goto out; + } + } + ofs += nand->erasesize; + len -= nand->erasesize; + } + +out: + /* De-select the NAND device */ + NAND_DISABLE_CE(nand); /* set pin high */ +#ifdef CONFIG_OMAP1510 + archflashwp(0,1); +#endif +#ifdef CFG_NAND_WP + NAND_WP_ON(); +#endif + + return ret; +} + + +static inline int nandcheck(unsigned long potential, unsigned long physadr) +{ + return 0; +} + +unsigned long nand_probe(unsigned long physadr) +{ + struct nand_chip *nand = NULL; + int i = 0, ChipID = 1; + +#ifdef CONFIG_MTD_NAND_ECC_JFFS2 + oob_config.ecc_pos[0] = NAND_JFFS2_OOB_ECCPOS0; + oob_config.ecc_pos[1] = NAND_JFFS2_OOB_ECCPOS1; + oob_config.ecc_pos[2] = NAND_JFFS2_OOB_ECCPOS2; + oob_config.ecc_pos[3] = NAND_JFFS2_OOB_ECCPOS3; + oob_config.ecc_pos[4] = NAND_JFFS2_OOB_ECCPOS4; + oob_config.ecc_pos[5] = NAND_JFFS2_OOB_ECCPOS5; + oob_config.eccvalid_pos = 4; +#else + oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0; + oob_config.ecc_pos[1] = NAND_NOOB_ECCPOS1; + oob_config.ecc_pos[2] = NAND_NOOB_ECCPOS2; + oob_config.ecc_pos[3] = NAND_NOOB_ECCPOS3; + oob_config.ecc_pos[4] = NAND_NOOB_ECCPOS4; + oob_config.ecc_pos[5] = NAND_NOOB_ECCPOS5; + oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS; +#endif + oob_config.badblock_pos = 5; + + for (i=0; iIO_ADDR = physadr; + nand->cache_page = -1; /* init the cache page */ + NanD_ScanChips(nand); + + if (nand->totlen == 0) { + /* no chips found, clean up and quit */ + memset((char *)nand, 0, sizeof(struct nand_chip)); + nand->ChipID = NAND_ChipID_UNKNOWN; + return (0); + } + + nand->ChipID = ChipID; + if (curr_device == -1) + curr_device = i; + + nand->data_buf = malloc (nand->oobblock + nand->oobsize); + if (!nand->data_buf) { + puts ("Cannot allocate memory for data structures.\n"); + return (0); + } + + return (nand->totlen); +} + +#ifdef CONFIG_MTD_NAND_ECC +/* + * 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 +}; + + +/* + * 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; +} + +/* + * Calculate 3 byte ECC code for 256 byte block + */ +static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) +{ + u_char idx, reg1, reg3; + int j; + + /* Initialize variables */ + reg1 = 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; + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (u_char) j; + } + } + + /* Create non-inverted ECC code from line parity */ + nand_trans_result((reg1 & 0x40) ? ~reg3 : reg3, 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; +} + +/* + * Detect and correct a 1 bit error for 256 byte block + */ +static int nand_correct_data (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 + +#ifdef CONFIG_JFFS2_NAND +int read_jffs2_nand(size_t start, size_t len, + size_t * retlen, u_char * buf, int nanddev) +{ + return nand_legacy_rw(nand_dev_desc + nanddev, NANDRW_READ | NANDRW_JFFS2, + start, len, retlen, buf); +} +#endif /* CONFIG_JFFS2_NAND */ + +#endif diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile new file mode 100644 index 0000000000..2049413327 --- /dev/null +++ b/drivers/mtd/onenand/Makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2005-2007 Samsung Electronics. +# Kyungmin Park +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libonenand.a + +COBJS := onenand_base.o onenand_bbt.o + +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c new file mode 100644 index 0000000000..7983a4a0d8 --- /dev/null +++ b/drivers/mtd/onenand/onenand_base.c @@ -0,0 +1,1294 @@ +/* + * linux/drivers/mtd/onenand/onenand_base.c + * + * Copyright (C) 2005-2007 Samsung Electronics + * Kyungmin Park + * + * 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 + +#ifdef CONFIG_CMD_ONENAND + +#include +#include +#include + +#include +#include + +static const unsigned char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ +}; + +/** + * onenand_readw - [OneNAND Interface] Read OneNAND register + * @param addr address to read + * + * Read OneNAND register + */ +static unsigned short onenand_readw(void __iomem * addr) +{ + return readw(addr); +} + +/** + * onenand_writew - [OneNAND Interface] Write OneNAND register with value + * @param value value to write + * @param addr address to write + * + * Write OneNAND register with value + */ +static void onenand_writew(unsigned short value, void __iomem * addr) +{ + writew(value, addr); +} + +/** + * onenand_block_address - [DEFAULT] Get block address + * @param device the device id + * @param block the block + * @return translated block address if DDP, otherwise same + * + * Setup Start Address 1 Register (F100h) + */ +static int onenand_block_address(int device, int block) +{ + if (device & ONENAND_DEVICE_IS_DDP) { + /* Device Flash Core select, NAND Flash Block Address */ + int dfs = 0, density, mask; + + density = device >> ONENAND_DEVICE_DENSITY_SHIFT; + mask = (1 << (density + 6)); + + if (block & mask) + dfs = 1; + + return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1)); + } + + return block; +} + +/** + * onenand_bufferram_address - [DEFAULT] Get bufferram address + * @param device the device id + * @param block the block + * @return set DBS value if DDP, otherwise 0 + * + * Setup Start Address 2 Register (F101h) for DDP + */ +static int onenand_bufferram_address(int device, int block) +{ + if (device & ONENAND_DEVICE_IS_DDP) { + /* Device BufferRAM Select */ + int dbs = 0, density, mask; + + density = device >> ONENAND_DEVICE_DENSITY_SHIFT; + mask = (1 << (density + 6)); + + if (block & mask) + dbs = 1; + + return (dbs << ONENAND_DDP_SHIFT); + } + + return 0; +} + +/** + * onenand_page_address - [DEFAULT] Get page address + * @param page the page address + * @param sector the sector address + * @return combined page and sector address + * + * Setup Start Address 8 Register (F107h) + */ +static int onenand_page_address(int page, int sector) +{ + /* Flash Page Address, Flash Sector Address */ + int fpa, fsa; + + fpa = page & ONENAND_FPA_MASK; + fsa = sector & ONENAND_FSA_MASK; + + return ((fpa << ONENAND_FPA_SHIFT) | fsa); +} + +/** + * onenand_buffer_address - [DEFAULT] Get buffer address + * @param dataram1 DataRAM index + * @param sectors the sector address + * @param count the number of sectors + * @return the start buffer value + * + * Setup Start Buffer Register (F200h) + */ +static int onenand_buffer_address(int dataram1, int sectors, int count) +{ + int bsa, bsc; + + /* BufferRAM Sector Address */ + bsa = sectors & ONENAND_BSA_MASK; + + if (dataram1) + bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ + else + bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ + + /* BufferRAM Sector Count */ + bsc = count & ONENAND_BSC_MASK; + + return ((bsa << ONENAND_BSA_SHIFT) | bsc); +} + +/** + * onenand_command - [DEFAULT] Send command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + * + * Send command to OneNAND device. This function is used for middle/large page + * devices (1KB/2KB Bytes per page) + */ +static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value, readcmd = 0; + int block, page; + /* Now we use page size operation */ + int sectors = 4, count = 4; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_LOCK: + case ONENAND_CMD_LOCK_TIGHT: + block = -1; + page = -1; + break; + + case ONENAND_CMD_ERASE: + case ONENAND_CMD_BUFFERRAM: + block = (int)(addr >> this->erase_shift); + page = -1; + break; + + default: + block = (int)(addr >> this->erase_shift); + page = (int)(addr >> this->page_shift); + page &= this->page_mask; + break; + } + + /* NOTE: The setting order of the registers is very important! */ + if (cmd == ONENAND_CMD_BUFFERRAM) { + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); + + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); + + return 0; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS1); + } + + if (page != -1) { + int dataram; + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + readcmd = 1; + break; + + default: + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + + if (readcmd) { + /* Select DataRAM for DDP */ + value = + onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + + ONENAND_REG_START_ADDRESS2); + } + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** + * onenand_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. This applies to all OneNAND command + * Read can take up to 30us, erase up to 2ms and program up to 350us + * according to general OneNAND specs + */ +static int onenand_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned int flags = ONENAND_INT_MASTER; + unsigned int interrupt = 0; + unsigned int ctrl, ecc; + + while (1) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & flags) + break; + } + + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (ctrl & ONENAND_CTRL_ERROR) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_wait: controller error = 0x%04x\n", ctrl); + return -EAGAIN; + } + + if (ctrl & ONENAND_CTRL_LOCK) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_wait: it's locked error = 0x%04x\n", ctrl); + return -EIO; + } + + if (interrupt & ONENAND_INT_READ) { + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + if (ecc & ONENAND_ECC_2BIT_ALL) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + + return 0; +} + +/** + * onenand_bufferram_offset - [DEFAULT] BufferRAM offset + * @param mtd MTD data structure + * @param area BufferRAM area + * @return offset given area + * + * Return BufferRAM offset given area + */ +static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + return mtd->oobblock; + if (area == ONENAND_SPARERAM) + return mtd->oobsize; + } + + return 0; +} + +/** + * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area + */ +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + memcpy(buffer, bufferram + offset, count); + + return 0; +} + +/** + * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Read the BufferRAM area with Sync. Burst Mode + */ +static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); + + memcpy(buffer, bufferram + offset, count); + + this->mmcontrol(mtd, 0); + + return 0; +} + +/** + * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area + * @param mtd MTD data structure + * @param area BufferRAM area + * @param buffer the databuffer to put/get data + * @param offset offset to read from or write to + * @param count number of bytes to read/write + * + * Write the BufferRAM area + */ +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + + bufferram = this->base + area; + bufferram += onenand_bufferram_offset(mtd, area); + + memcpy(bufferram + offset, buffer, count); + + return 0; +} + +/** + * onenand_check_bufferram - [GENERIC] Check BufferRAM information + * @param mtd MTD data structure + * @param addr address to check + * @return 1 if there are valid data, otherwise 0 + * + * Check bufferram if there is data we required + */ +static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int block, page; + int i; + + block = (int)(addr >> this->erase_shift); + page = (int)(addr >> this->page_shift); + page &= this->page_mask; + + i = ONENAND_CURRENT_BUFFERRAM(this); + + /* Is there valid data? */ + if (this->bufferram[i].block == block && + this->bufferram[i].page == page && this->bufferram[i].valid) + return 1; + + return 0; +} + +/** + * onenand_update_bufferram - [GENERIC] Update BufferRAM information + * @param mtd MTD data structure + * @param addr address to update + * @param valid valid flag + * + * Update BufferRAM information + */ +static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, + int valid) +{ + struct onenand_chip *this = mtd->priv; + int block, page; + int i; + + block = (int)(addr >> this->erase_shift); + page = (int)(addr >> this->page_shift); + page &= this->page_mask; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + if (this->bufferram[i].block == block && + this->bufferram[i].page == page) + this->bufferram[i].valid = 0; + } + + /* Update BufferRAM */ + i = ONENAND_CURRENT_BUFFERRAM(this); + this->bufferram[i].block = block; + this->bufferram[i].page = page; + this->bufferram[i].valid = valid; + + return 0; +} + +/** + * onenand_get_device - [GENERIC] Get chip for selected access + * @param mtd MTD device structure + * @param new_state the state which is requested + * + * Get the device and lock it for exclusive access + */ +static void onenand_get_device(struct mtd_info *mtd, int new_state) +{ + /* Do nothing */ +} + +/** + * onenand_release_device - [GENERIC] release chip + * @param mtd MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void onenand_release_device(struct mtd_info *mtd) +{ + /* Do nothing */ +} + +/** + * onenand_read_ecc - [MTD Interface] Read data with ECC + * @param mtd MTD device structure + * @param from offset to read from + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put data + * @param oob_buf filesystem supplied oob data buffer + * @param oobsel oob selection structure + * + * OneNAND read with ECC + */ +static int onenand_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) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, column; + int thislen; + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_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, + "onenand_read_ecc: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + while (read < len) { + thislen = min_t(int, mtd->oobblock, len - read); + + column = from & (mtd->oobblock - 1); + if (column + thislen > mtd->oobblock) + thislen = mtd->oobblock - column; + + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, + mtd->oobblock); + ret = this->wait(mtd, FL_READING); + /* First copy data and check return value for ECC handling */ + onenand_update_bufferram(mtd, from, 1); + } + + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, + thislen); + + read += thislen; + if (read == len) + break; + + if (ret) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_read_ecc: read failed = %d\n", ret); + break; + } + + from += thislen; + buf += thislen; + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_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 ret; +} + +/** + * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc + * @param mtd MTD device structure + * @param from offset to read from + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put data + * + * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL +*/ +int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); +} + +/** + * onenand_read_oob - [MTD Interface] OneNAND read out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put data + * + * OneNAND read out-of-band data from the spare area + */ +int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", + (unsigned int)from, (int)len); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_read_oob: Attempt read beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + while (read < len) { + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = this->wait(mtd, FL_READING); + /* First copy data and check return value for ECC handling */ + + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, + thislen); + + read += thislen; + if (read == len) + break; + + if (ret) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_read_oob: read failed = %d\n", ret); + break; + } + + buf += thislen; + /* Read more? */ + if (read < len) { + /* Page size */ + from += mtd->oobblock; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + *retlen = read; + return ret; +} + +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE +/** + * onenand_verify_page - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param block block address + * @param page page address + * + * Check DataRAM area directly + */ +static int onenand_verify_page(struct mtd_info *mtd, u_char * buf, + loff_t addr, int block, int page) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *dataram0, *dataram1; + int ret = 0; + + this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; + + onenand_update_bufferram(mtd, addr, 1); + + /* Check, if the two dataram areas are same */ + dataram0 = this->base + ONENAND_DATARAM; + dataram1 = dataram0 + mtd->oobblock; + + if (memcmp(dataram0, dataram1, mtd->oobblock)) + return -EBADMSG; + + return 0; +} +#else +#define onenand_verify_page(...) (0) +#endif + +#define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0) + +/** + * onenand_write_ecc - [MTD Interface] OneNAND write with ECC + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * @param eccbuf filesystem supplied oob data buffer + * @param oobsel oob selection structure + * + * OneNAND write with ECC + */ +static int onenand_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) +{ + struct onenand_chip *this = mtd->priv; + int written = 0; + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_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 writes past end of device */ + if (unlikely((to + len) > mtd->size)) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_write_ecc: Attempt write to past end of device\n"); + return -EINVAL; + } + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_WRITING); + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, mtd->oobblock, len - written); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); + + this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); + this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, + mtd->oobsize); + + this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); + + onenand_update_bufferram(mtd, to, 1); + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_write_ecc: write filaed %d\n", ret); + break; + } + + written += thislen; + + /* Only check verify write turn on */ + ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); + if (ret) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_write_ecc: verify failed %d\n", ret); + break; + } + + if (written == len) + break; + + to += thislen; + buf += thislen; + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + *retlen = written; + + return ret; +} + +/** + * onenand_write - [MTD Interface] compability function for onenand_write_ecc + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * This function simply calls onenand_write_ecc + * with oob buffer and oobsel = NULL + */ +int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL); +} + +/** + * onenand_write_oob - [MTD Interface] OneNAND write out-of-band + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * OneNAND write out-of-band + */ +int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct onenand_chip *this = mtd->priv; + int column, status; + int written = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int)to, (int)len); + + /* Initialize retlen, in case of early exit */ + *retlen = 0; + + /* Do not allow writes past end of device */ + if (unlikely((to + len) > mtd->size)) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_write_oob: Attempt write to past end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_WRITING); + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, mtd->oobsize, len - written); + + column = to & (mtd->oobsize - 1); + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); + + this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, + mtd->oobsize); + this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, + thislen); + + this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + + onenand_update_bufferram(mtd, to, 0); + + status = this->wait(mtd, FL_WRITING); + if (status) + break; + + written += thislen; + if (written == len) + break; + + to += thislen; + buf += thislen; + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + *retlen = written; + + return 0; +} + +/** + * onenand_erase - [MTD Interface] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * + * Erase one ore more blocks + */ +int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + unsigned int block_size; + loff_t addr; + int len; + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", + (unsigned int)instr->addr, (unsigned int)instr->len); + + block_size = (1 << this->erase_shift); + + /* Start address must align on block boundary */ + if (unlikely(instr->addr & (block_size - 1))) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (unlikely(instr->len & (block_size - 1))) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if (unlikely((instr->len + instr->addr) > mtd->size)) { + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_erase: Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_ERASING); + + /* Loop throught the pages */ + len = instr->len; + addr = instr->addr; + + instr->state = MTD_ERASING; + + while (len) { + + /* TODO Check badblock */ + + this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + + ret = this->wait(mtd, FL_ERASING); + /* Check, if it is write protected */ + if (ret) { + if (ret == -EPERM) + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_erase: Device is write protected!!!\n"); + else + DEBUG(MTD_DEBUG_LEVEL0, + "onenand_erase: Failed erase, block %d\n", + (unsigned)(addr >> this->erase_shift)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + goto erase_exit; + } + + len -= block_size; + addr += block_size; + } + + 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 */ + onenand_release_device(mtd); + + return ret; +} + +/** + * onenand_sync - [MTD Interface] sync + * @param mtd MTD device structure + * + * Sync is actually a wait for chip ready function + */ +void onenand_sync(struct mtd_info *mtd) +{ + DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_SYNCING); + + /* Release it and go back */ + onenand_release_device(mtd); +} + +/** + * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + */ +int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * TODO + * 1. Bad block table (BBT) + * -> using NAND BBT to support JFFS2 + * 2. Bad block management (BBM) + * -> bad block replace scheme + * + * Currently we do nothing + */ + return 0; +} + +/** + * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + */ +int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* see above */ + return 0; +} + +/** + * onenand_unlock - [MTD Interface] Unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Unlock one or more blocks + */ +int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct onenand_chip *this = mtd->priv; + int start, end, block, value, status; + + start = ofs >> this->erase_shift; + end = len >> this->erase_shift; + + /* Continuous lock scheme */ + if (this->options & ONENAND_CONT_LOCK) { + /* Set start block address */ + this->write_word(start, + this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Set end block address */ + this->write_word(end - 1, + this->base + ONENAND_REG_END_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_UNLOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) + printk(KERN_ERR "wp status = 0x%x\n", status); + + return 0; + } + + /* Block lock scheme */ + for (block = start; block < end; block++) { + /* Set start block address */ + this->write_word(block, + this->base + ONENAND_REG_START_BLOCK_ADDRESS); + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_UNLOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Set block address for read block status */ + value = onenand_block_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS1); + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) + printk(KERN_ERR "block = %d, wp status = 0x%x\n", + block, status); + } + + return 0; +} + +/** + * onenand_print_device_info - Print device ID + * @param device device ID + * + * Print device ID + */ +void onenand_print_device_info(int device, int verbose) +{ + int vcc, demuxed, ddp, density; + + if (!verbose) + return; + + vcc = device & ONENAND_DEVICE_VCC_MASK; + demuxed = device & ONENAND_DEVICE_IS_DEMUX; + ddp = device & ONENAND_DEVICE_IS_DDP; + density = device >> ONENAND_DEVICE_DENSITY_SHIFT; + printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + demuxed ? "" : "Muxed ", + ddp ? "(DDP)" : "", + (16 << density), vcc ? "2.65/3.3" : "1.8", device); +} + +static const struct onenand_manufacturers onenand_manuf_ids[] = { + {ONENAND_MFR_SAMSUNG, "Samsung"}, + {ONENAND_MFR_UNKNOWN, "Unknown"} +}; + +/** + * onenand_check_maf - Check manufacturer ID + * @param manuf manufacturer ID + * + * Check manufacturer ID + */ +static int onenand_check_maf(int manuf) +{ + int i; + + for (i = 0; onenand_manuf_ids[i].id; i++) { + if (manuf == onenand_manuf_ids[i].id) + break; + } + +#ifdef ONENAND_DEBUG + printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", + onenand_manuf_ids[i].name, manuf); +#endif + + return (i != ONENAND_MFR_UNKNOWN); +} + +/** + * onenand_probe - [OneNAND Interface] Probe the OneNAND device + * @param mtd MTD device structure + * + * OneNAND detection method: + * Compare the the values from command with ones from register + */ +static int onenand_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int bram_maf_id, bram_dev_id, maf_id, dev_id; + int version_id; + int density; + + /* Send the command for reading device ID from BootRAM */ + this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); + + /* Read manufacturer and device IDs from BootRAM */ + bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); + bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); + + /* Check manufacturer ID */ + if (onenand_check_maf(bram_maf_id)) + return -ENXIO; + + /* Reset OneNAND to read default register values */ + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); + + { + int i; + for (i = 0; i < 10000; i++) ; + } + + /* Read manufacturer and device IDs from Register */ + maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + + /* Check OneNAND device */ + if (maf_id != bram_maf_id || dev_id != bram_dev_id) + return -ENXIO; + + /* Flash device information */ + onenand_print_device_info(dev_id, 0); + this->device_id = dev_id; + + density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + this->chipsize = (16 << density) << 20; + + /* OneNAND page size & block size */ + /* The data buffer size is equal to page size */ + mtd->oobblock = + this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + mtd->oobsize = mtd->oobblock >> 5; + /* Pagers per block is always 64 in OneNAND */ + mtd->erasesize = mtd->oobblock << 6; + + this->erase_shift = ffs(mtd->erasesize) - 1; + this->page_shift = ffs(mtd->oobblock) - 1; + this->ppb_shift = (this->erase_shift - this->page_shift); + this->page_mask = (mtd->erasesize / mtd->oobblock) - 1; + + /* REVIST: Multichip handling */ + + mtd->size = this->chipsize; + + /* Version ID */ + version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); +#ifdef ONENAND_DEBUG + printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); +#endif + + /* Lock scheme */ + if (density <= ONENAND_DEVICE_DENSITY_512Mb && + !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { + printk(KERN_INFO "Lock scheme is Continues Lock\n"); + this->options |= ONENAND_CONT_LOCK; + } + + return 0; +} + +/** + * onenand_scan - [OneNAND Interface] Scan for the OneNAND device + * @param mtd MTD device structure + * @param 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. + */ +int onenand_scan(struct mtd_info *mtd, int maxchips) +{ + struct onenand_chip *this = mtd->priv; + + if (!this->read_word) + this->read_word = onenand_readw; + if (!this->write_word) + this->write_word = onenand_writew; + + if (!this->command) + this->command = onenand_command; + if (!this->wait) + this->wait = onenand_wait; + + if (!this->read_bufferram) + this->read_bufferram = onenand_read_bufferram; + if (!this->write_bufferram) + this->write_bufferram = onenand_write_bufferram; + + if (onenand_probe(mtd)) + return -ENXIO; + + /* Set Sync. Burst Read after probing */ + if (this->mmcontrol) { + printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); + this->read_bufferram = onenand_sync_read_bufferram; + } + + onenand_unlock(mtd, 0, mtd->size); + + return onenand_default_bbt(mtd); +} + +/** + * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device + * @param mtd MTD device structure + */ +void onenand_release(struct mtd_info *mtd) +{ +} + +/* + * OneNAND initialization at U-Boot + */ +struct mtd_info onenand_mtd; +struct onenand_chip onenand_chip; + +void onenand_init(void) +{ + memset(&onenand_mtd, 0, sizeof(struct mtd_info)); + memset(&onenand_chip, 0, sizeof(struct onenand_chip)); + + onenand_chip.base = (void *)CFG_ONENAND_BASE; + onenand_mtd.priv = &onenand_chip; + + onenand_scan(&onenand_mtd, 1); + + puts("OneNAND: "); + print_size(onenand_mtd.size, "\n"); +} + +#endif /* CONFIG_CMD_ONENAND */ diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c new file mode 100644 index 0000000000..5a610ee5ea --- /dev/null +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -0,0 +1,265 @@ +/* + * linux/drivers/mtd/onenand/onenand_bbt.c + * + * Bad Block Table support for the OneNAND driver + * + * Copyright(c) 2005-2007 Samsung Electronics + * Kyungmin Park + * + * TODO: + * Split BBT core and chip specific BBT. + * + * 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 + +#ifdef CONFIG_CMD_ONENAND + +#include +#include +#include +#include + +#include + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @param buf the buffer to search + * @param len the length of buffer to search + * @param paglen the pagelength + * @param td search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check and the pattern is expected to start + * at offset 0. + */ +static int check_short_pattern(uint8_t * buf, int len, int paglen, + struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @param mtd MTD device structure + * @param buf temporary buffer + * @param bd descriptor for the good/bad block search pattern + * @param 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 int create_bbt(struct mtd_info *mtd, uint8_t * buf, + struct nand_bbt_descr *bd, int chip) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + + printk(KERN_INFO "Scanning device for bad blocks\n"); + + len = 1; + + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + + /* chip == -1 case only */ + /* Note that numblocks is 2 * (real numblocks) here; + * see i += 2 below as it makses shifting and masking less painful + */ + numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); + startblock = 0; + from = 0; + + for (i = startblock; i < numblocks;) { + int ret; + + for (j = 0; j < len; j++) { + size_t retlen; + + /* No need to read pages fully, + * just read required OOB bytes */ + ret = onenand_read_oob(mtd, + from + j * mtd->oobblock + + bd->offs, readlen, &retlen, + &buf[0]); + + if (ret && ret != -EAGAIN) { + printk("ret = %d\n", ret); + return ret; + } + + if (check_short_pattern + (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING + "Bad eraseblock %d at 0x%08x\n", i >> 1, + (unsigned int)from); + break; + } + } + i += 2; + from += (1 << bbm->bbt_erase_shift); + } + + return 0; +} + +/** + * onenand_memory_bbt - [GENERIC] create a memory based bad block table + * @param mtd MTD device structure + * @param 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 inline int onenand_memory_bbt(struct mtd_info *mtd, + struct nand_bbt_descr *bd) +{ + unsigned char data_buf[MAX_ONENAND_PAGESIZE]; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt(mtd, data_buf, bd, -1); +} + +/** + * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad + * @param mtd MTD device structure + * @param offs offset in the device + * @param allowbbt allow access to bad block table region + */ +static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int)(offs >> (bbm->bbt_erase_shift - 1)); + res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + DEBUG(MTD_DEBUG_LEVEL2, + "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + + switch ((int)res) { + case 0x00: + return 0; + case 0x01: + return 1; + case 0x02: + return allowbbt ? 0 : 1; + } + + return 1; +} + +/** + * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) + * @param mtd MTD device structure + * @param 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 onenand_free_bbt function. + * + */ +int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm = this->bbm; + int len, ret = 0; + + len = mtd->size >> (this->erase_shift + 2); + /* Allocate memory (2bit per block) */ + bbm->bbt = malloc(len); + if (!bbm->bbt) { + printk(KERN_ERR "onenand_scan_bbt: Out of memory\n"); + return -ENOMEM; + } + /* Clear the memory bad block table */ + memset(bbm->bbt, 0x00, len); + + /* Set the bad block position */ + bbm->badblockpos = ONENAND_BADBLOCK_POS; + + /* Set erase shift */ + bbm->bbt_erase_shift = this->erase_shift; + + if (!bbm->isbad_bbt) + bbm->isbad_bbt = onenand_isbad_bbt; + + /* Scan the device to build a memory based bad block table */ + if ((ret = onenand_memory_bbt(mtd, bd))) { + printk(KERN_ERR + "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); + free(bbm->bbt); + bbm->bbt = NULL; + } + + return ret; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern, +}; + +/** + * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device + * @param mtd MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the onenand_scan_bbt function + */ +int onenand_default_bbt(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct bbm_info *bbm; + + this->bbm = malloc(sizeof(struct bbm_info)); + if (!this->bbm) + return -ENOMEM; + + bbm = this->bbm; + + memset(bbm, 0, sizeof(struct bbm_info)); + + /* 1KB page has same configuration as 2KB page */ + if (!bbm->badblock_pattern) + bbm->badblock_pattern = &largepage_memorybased; + + return onenand_scan_bbt(mtd, bbm->badblock_pattern); +} + +#endif /* CFG_CMD_ONENAND */ diff --git a/drivers/mw_eeprom.c b/drivers/mw_eeprom.c deleted file mode 100644 index 2b3348810d..0000000000 --- a/drivers/mw_eeprom.c +++ /dev/null @@ -1,241 +0,0 @@ -/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */ - -#include - -#ifdef CONFIG_MW_EEPROM - -#include - -/* - * Serial EEPROM opcodes, including start bit - */ -#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */ -#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */ -#define EEP_OPC_READ 0x6 /* 3-bit opcode */ - -#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */ -#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */ -#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */ -#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */ - -static int addrlen; - -static void mw_eeprom_select(int dev) -{ - ssi_set_interface(2048, 0, 0, 0); - ssi_chip_select(0); - udelay(1); - ssi_chip_select(dev); - udelay(1); -} - -static int mw_eeprom_size(int dev) -{ - int x; - u16 res; - - mw_eeprom_select(dev); - ssi_tx_byte(EEP_OPC_READ); - - res = ssi_txrx_byte(0) << 8; - res |= ssi_rx_byte(); - for (x = 0; x < 16; x++) { - if (! (res & 0x8000)) { - break; - } - res <<= 1; - } - ssi_chip_select(0); - - return x; -} - -int mw_eeprom_erase_enable(int dev) -{ - mw_eeprom_select(dev); - ssi_tx_byte(EEP_OPC_ERASE_EN); - ssi_tx_byte(0); - udelay(1); - ssi_chip_select(0); - - return 0; -} - -int mw_eeprom_erase_disable(int dev) -{ - mw_eeprom_select(dev); - ssi_tx_byte(EEP_OPC_ERASE_DIS); - ssi_tx_byte(0); - udelay(1); - ssi_chip_select(0); - - return 0; -} - - -u32 mw_eeprom_read_word(int dev, int addr) -{ - u16 rcv; - u16 res; - int bits; - - mw_eeprom_select(dev); - ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f)); - rcv = ssi_txrx_byte(addr << (13 - addrlen)); - res = rcv << (16 - addrlen); - bits = 4 + addrlen; - - while (bits>0) { - rcv = ssi_rx_byte(); - if (bits > 7) { - res |= rcv << (bits - 8); - } else { - res |= rcv >> (8 - bits); - } - bits -= 8; - } - - ssi_chip_select(0); - - return res; -} - -int mw_eeprom_write_word(int dev, int addr, u16 data) -{ - u8 byte1=0; - u8 byte2=0; - - mw_eeprom_erase_enable(dev); - mw_eeprom_select(dev); - - switch (addrlen) { - case 6: - byte1 = EEP_OPC_WRITE >> 2; - byte2 = (EEP_OPC_WRITE << 6)&0xc0; - byte2 |= addr; - break; - case 7: - byte1 = EEP_OPC_WRITE >> 1; - byte2 = (EEP_OPC_WRITE << 7)&0x80; - byte2 |= addr; - break; - case 8: - byte1 = EEP_OPC_WRITE; - byte2 = addr; - break; - case 9: - byte1 = EEP_OPC_WRITE << 1; - byte1 |= addr >> 8; - byte2 = addr & 0xff; - break; - case 10: - byte1 = EEP_OPC_WRITE << 2; - byte1 |= addr >> 8; - byte2 = addr & 0xff; - break; - default: - printf("Unsupported number of address bits: %d\n", addrlen); - return -1; - - } - - ssi_tx_byte(byte1); - ssi_tx_byte(byte2); - ssi_tx_byte(data >> 8); - ssi_tx_byte(data & 0xff); - ssi_chip_select(0); - udelay(10000); /* Worst case */ - mw_eeprom_erase_disable(dev); - - return 0; -} - - -int mw_eeprom_write(int dev, int addr, u8 *buffer, int len) -{ - int done; - - done = 0; - if (addr & 1) { - u16 temp = mw_eeprom_read_word(dev, addr >> 1); - temp &= 0xff00; - temp |= buffer[0]; - - mw_eeprom_write_word(dev, addr >> 1, temp); - len--; - addr++; - buffer++; - done++; - } - - while (len <= 2) { - mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer); - len-=2; - addr+=2; - buffer+=2; - done+=2; - } - - if (len) { - u16 temp = mw_eeprom_read_word(dev, addr >> 1); - temp &= 0x00ff; - temp |= buffer[0] << 8; - - mw_eeprom_write_word(dev, addr >> 1, temp); - len--; - addr++; - buffer++; - done++; - } - - return done; -} - - -int mw_eeprom_read(int dev, int addr, u8 *buffer, int len) -{ - int done; - - done = 0; - if (addr & 1) { - u16 temp = mw_eeprom_read_word(dev, addr >> 1); - buffer[0]= temp & 0xff; - - len--; - addr++; - buffer++; - done++; - } - - while (len <= 2) { - *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1); - len-=2; - addr+=2; - buffer+=2; - done+=2; - } - - if (len) { - u16 temp = mw_eeprom_read_word(dev, addr >> 1); - buffer[0] = temp >> 8; - - len--; - addr++; - buffer++; - done++; - } - - return done; -} - -int mw_eeprom_probe(int dev) -{ - addrlen = mw_eeprom_size(dev); - - if (addrlen < 6 || addrlen > 10) { - return -1; - } - return 0; -} - -#endif diff --git a/drivers/nand/Makefile b/drivers/nand/Makefile deleted file mode 100644 index 42864f98f4..0000000000 --- a/drivers/nand/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# -# (C) Copyright 2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# See file CREDITS for list of people who contributed to this -# project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA -# - -include $(TOPDIR)/config.mk - -LIB := $(obj)libnand.a - -COBJS-y += nand.o -COBJS-y += nand_base.o -COBJS-y += nand_ids.o -COBJS-y += nand_ecc.o -COBJS-y += nand_bbt.o -COBJS-y += nand_util.o - -COBJS := $(COBJS-y) -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) - -all: $(LIB) - -$(LIB): $(obj).depend $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -######################################################################### - -# defines $(obj).depend target -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/nand/diskonchip.c b/drivers/nand/diskonchip.c deleted file mode 100644 index e17af70d07..0000000000 --- a/drivers/nand/diskonchip.c +++ /dev/null @@ -1,1787 +0,0 @@ -/* - * drivers/mtd/nand/diskonchip.c - * - * (C) 2003 Red Hat, Inc. - * (C) 2004 Dan Brown - * (C) 2004 Kalev Lember - * - * Author: David Woodhouse - * Additional Diskonchip 2000 and Millennium support by Dan Brown - * Diskonchip Millennium Plus support by Kalev Lember - * - * 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 - * - * 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 - -#if !defined(CFG_NAND_LEGACY) - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* 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 "); -MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); -#endif diff --git a/drivers/nand/nand.c b/drivers/nand/nand.c deleted file mode 100644 index 27b5792bcc..0000000000 --- a/drivers/nand/nand.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * (C) Copyright 2005 - * 2N Telekomunikace, a.s. - * Ladislav Michl - * - * 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 - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include - -#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 int 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; - - nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; - if (board_nand_init(nand) == 0) { - if (nand_scan(mtd, 1) == 0) { - if (!mtd->name) - mtd->name = (char *)default_nand_name; - } else - mtd->name = NULL; - } else { - mtd->name = NULL; - mtd->size = 0; - } - -} - -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)); - -#ifdef CFG_NAND_SELECT_DEVICE - /* - * Select the chip in the board/cpu specific driver - */ - board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); -#endif -} - -#endif diff --git a/drivers/nand/nand_base.c b/drivers/nand/nand_base.c deleted file mode 100644 index 151f535c58..0000000000 --- a/drivers/nand/nand_base.c +++ /dev/null @@ -1,2668 +0,0 @@ -/* - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_MTD_PARTITIONS -#include -#endif - -#endif - -#include - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifdef CONFIG_JFFS2_NAND -#include -#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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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; - - page = (int)(ofs >> this->page_shift) & this->pagemask; - - if (getchip) { - 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); - } - - if (this->options & NAND_BUSWIDTH_16) { - this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page); - 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); - 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) / 1000; - else - timeo = (CFG_HZ * 20) / 1000; - - 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); - - reset_timer(); - - while (1) { - if (get_timer(0) > timeo) { - printf("Timeout!"); - return 0x01; - } - - if (this->dev_ready) { - if (this->dev_ready(mtd)) - break; - } else { - if (this->read_byte(mtd) & NAND_STATUS_READY) - break; - } - } -#ifdef PPCHAMELON_NAND_TIMER_HACK - reset_timer(); - while (get_timer(0) < 10); -#endif /* PPCHAMELON_NAND_TIMER_HACK */ - - 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; - uint *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; - unsigned *oob_config; - int 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; - bufstart = (u_char*) &buf[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; - oob = 0; - this->oobdirty = 1; - 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) { -#ifndef NAND_ALLOW_ERASE_ALL - /* 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; - } -#endif - /* 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 & 0x01)) * (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) { -#ifndef CFG_NAND_QUIET_TEST - printk (KERN_WARNING "No NAND device found!!!\n"); -#endif - 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 diff --git a/drivers/nand/nand_bbt.c b/drivers/nand/nand_bbt.c deleted file mode 100644 index 19a9bc2a5b..0000000000 --- a/drivers/nand/nand_bbt.c +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * 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 - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include -#include -#include -#include - -#include - -/** - * 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; - - 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 diff --git a/drivers/nand/nand_ecc.c b/drivers/nand/nand_ecc.c deleted file mode 100644 index 4c532b0794..0000000000 --- a/drivers/nand/nand_ecc.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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 - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include - -/* - * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), - * only nand_correct_data() is needed - */ - -#ifndef CONFIG_NAND_SPL -/* - * 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_calculate_ecc - [NAND Interface] Calculate 3-byte ECC 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) -{ - uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; - int i; - - /* Initialize variables */ - reg1 = reg2 = reg3 = 0; - - /* Build up column parity */ - for(i = 0; i < 256; i++) { - /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[*dat++]; - reg1 ^= (idx & 0x3f); - - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (uint8_t) i; - reg2 ^= ~((uint8_t) i); - } - } - - /* Create non-inverted ECC code from line parity */ - tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ - tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ - tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ - tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ - tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ - tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ - tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ - tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ - - tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ - tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ - tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ - tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ - tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ - tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ - tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ - tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ - - /* Calculate final ECC code */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - ecc_code[0] = ~tmp2; - ecc_code[1] = ~tmp1; -#else - ecc_code[0] = ~tmp1; - ecc_code[1] = ~tmp2; -#endif - ecc_code[2] = ((~reg1) << 2) | 0x03; - - return 0; -} -#endif /* CONFIG_NAND_SPL */ - -static inline int countbits(uint32_t byte) -{ - int res = 0; - - for (;byte; byte >>= 1) - res += byte & 0x01; - return res; -} - -/** - * 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) -{ - uint8_t s0, s1, s2; - -#ifdef CONFIG_MTD_NAND_ECC_SMC - s0 = calc_ecc[0] ^ read_ecc[0]; - s1 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; -#else - s1 = calc_ecc[0] ^ read_ecc[0]; - s0 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; -#endif - if ((s0 | s1 | s2) == 0) - return 0; - - /* Check for a single bit error */ - if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && - ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && - ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { - - uint32_t byteoffs, bitnum; - - byteoffs = (s1 << 0) & 0x80; - byteoffs |= (s1 << 1) & 0x40; - byteoffs |= (s1 << 2) & 0x20; - byteoffs |= (s1 << 3) & 0x10; - - byteoffs |= (s0 >> 4) & 0x08; - byteoffs |= (s0 >> 3) & 0x04; - byteoffs |= (s0 >> 2) & 0x02; - byteoffs |= (s0 >> 1) & 0x01; - - bitnum = (s2 >> 5) & 0x04; - bitnum |= (s2 >> 4) & 0x02; - bitnum |= (s2 >> 3) & 0x01; - - dat[byteoffs] ^= (1 << bitnum); - - return 1; - } - - if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) - return 1; - - return -1; -} - -#endif diff --git a/drivers/nand/nand_ids.c b/drivers/nand/nand_ids.c deleted file mode 100644 index 6d7e347fba..0000000000 --- a/drivers/nand/nand_ids.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include - -/* -* Chip ID list -* -* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, -* options -* -* Pagesize; 0, 256, 512 -* 0 get this information from the extended chip ID -+ 256 256 Byte page size -* 512 512 Byte page size -*/ -struct nand_flash_dev nand_flash_ids[] = { - {"NAND 1MiB 5V 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"}, - {NAND_MFR_MICRON, "Micron"}, - {0x0, "Unknown"} -}; -#endif diff --git a/drivers/nand/nand_util.c b/drivers/nand/nand_util.c deleted file mode 100644 index 4fd4e166e6..0000000000 --- a/drivers/nand/nand_util.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * drivers/nand/nand_util.c - * - * Copyright (C) 2006 by Weiss-Electronic GmbH. - * All rights reserved. - * - * @author: Guido Classen - * @descr: NAND Flash support - * @references: borrowed heavily from Linux mtd-utils code: - * flash_eraseall.c by Arcom Control System Ltd - * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) - * and Thomas Gleixner (tglx@linutronix.de) - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License 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 - -#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) - -#include -#include -#include -#include - -#include -#include - -typedef struct erase_info erase_info_t; -typedef struct mtd_info mtd_info_t; - -/* support only for native endian JFFS2 */ -#define cpu_to_je16(x) (x) -#define cpu_to_je32(x) (x) - -/*****************************************************************************/ -static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) -{ - return 0; -} - -/** - * nand_erase_opts: - erase NAND flash with support for various options - * (jffs2 formating) - * - * @param meminfo NAND device to erase - * @param opts options, @see struct nand_erase_options - * @return 0 in case of success - * - * This code is ported from flash_eraseall.c from Linux mtd utils by - * Arcom Control System Ltd. - */ -int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) -{ - struct jffs2_unknown_node cleanmarker; - int clmpos = 0; - int clmlen = 8; - erase_info_t erase; - ulong erase_length; - int isNAND; - int bbtest = 1; - int result; - int percent_complete = -1; - int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; - const char *mtd_device = meminfo->name; - - memset(&erase, 0, sizeof(erase)); - - erase.mtd = meminfo; - erase.len = meminfo->erasesize; - erase.addr = opts->offset; - erase_length = opts->length; - - isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; - - if (opts->jffs2) { - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - if (isNAND) { - struct nand_oobinfo *oobinfo = &meminfo->oobinfo; - - /* check for autoplacement */ - if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { - /* get the position of the free bytes */ - if (!oobinfo->oobfree[0][1]) { - printf(" Eeep. Autoplacement selected " - "and no empty space in oob\n"); - return -1; - } - clmpos = oobinfo->oobfree[0][0]; - clmlen = oobinfo->oobfree[0][1]; - if (clmlen > 8) - clmlen = 8; - } else { - /* legacy mode */ - switch (meminfo->oobsize) { - case 8: - clmpos = 6; - clmlen = 2; - break; - case 16: - clmpos = 8; - clmlen = 8; - break; - case 64: - clmpos = 16; - clmlen = 8; - break; - } - } - - cleanmarker.totlen = cpu_to_je32(8); - } else { - cleanmarker.totlen = - cpu_to_je32(sizeof(struct jffs2_unknown_node)); - } - cleanmarker.hdr_crc = cpu_to_je32( - crc32_no_comp(0, (unsigned char *) &cleanmarker, - sizeof(struct jffs2_unknown_node) - 4)); - } - - /* scrub option allows to erase badblock. To prevent internal - * check from erase() method, set block check method to dummy - * and disable bad block table while erasing. - */ - if (opts->scrub) { - struct nand_chip *priv_nand = meminfo->priv; - - nand_block_bad_old = priv_nand->block_bad; - priv_nand->block_bad = nand_block_bad_scrub; - /* we don't need the bad block table anymore... - * after scrub, there are no bad blocks left! - */ - if (priv_nand->bbt) { - kfree(priv_nand->bbt); - } - priv_nand->bbt = NULL; - } - - for (; - erase.addr < opts->offset + erase_length; - erase.addr += meminfo->erasesize) { - - WATCHDOG_RESET (); - - if (!opts->scrub && bbtest) { - int ret = meminfo->block_isbad(meminfo, erase.addr); - if (ret > 0) { - if (!opts->quiet) - printf("\rSkipping bad block at " - "0x%08x " - " \n", - erase.addr); - continue; - - } else if (ret < 0) { - printf("\n%s: MTD get bad block failed: %d\n", - mtd_device, - ret); - return -1; - } - } - - result = meminfo->erase(meminfo, &erase); - if (result != 0) { - printf("\n%s: MTD Erase failure: %d\n", - mtd_device, result); - continue; - } - - /* format for JFFS2 ? */ - if (opts->jffs2) { - - /* write cleanmarker */ - if (isNAND) { - size_t written; - result = meminfo->write_oob(meminfo, - erase.addr + clmpos, - clmlen, - &written, - (unsigned char *) - &cleanmarker); - if (result != 0) { - printf("\n%s: MTD writeoob failure: %d\n", - mtd_device, result); - continue; - } - } else { - printf("\n%s: this erase routine only supports" - " NAND devices!\n", - mtd_device); - } - } - - if (!opts->quiet) { - unsigned long long n =(unsigned long long) - (erase.addr + meminfo->erasesize - opts->offset) - * 100; - int percent; - - do_div(n, erase_length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - percent_complete = percent; - - printf("\rErasing at 0x%x -- %3d%% complete.", - erase.addr, percent); - - if (opts->jffs2 && result == 0) - printf(" Cleanmarker written at 0x%x.", - erase.addr); - } - } - } - if (!opts->quiet) - printf("\n"); - - if (nand_block_bad_old) { - struct nand_chip *priv_nand = meminfo->priv; - - priv_nand->block_bad = nand_block_bad_old; - priv_nand->scan_bbt(meminfo); - } - - return 0; -} - -#define MAX_PAGE_SIZE 2048 -#define MAX_OOB_SIZE 64 - -/* - * buffer array used for writing data - */ -static unsigned char data_buf[MAX_PAGE_SIZE]; -static unsigned char oob_buf[MAX_OOB_SIZE]; - -/* OOB layouts to pass into the kernel as default */ -static struct nand_oobinfo none_oobinfo = { - .useecc = MTD_NANDECC_OFF, -}; - -static struct nand_oobinfo jffs2_oobinfo = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = { 0, 1, 2, 3, 6, 7 } -}; - -static struct nand_oobinfo yaffs_oobinfo = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = { 8, 9, 10, 13, 14, 15} -}; - -static struct nand_oobinfo autoplace_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE -}; - -/** - * nand_write_opts: - write image to NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts write options (@see nand_write_options) - * @return 0 in case of success - * - * This code is ported from nandwrite.c from Linux mtd utils by - * Steven J. Hill and Thomas Gleixner. - */ -int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) -{ - int imglen = 0; - int pagelen; - int baderaseblock; - int blockstart = -1; - loff_t offs; - int readlen; - int oobinfochanged = 0; - int percent_complete = -1; - struct nand_oobinfo old_oobinfo; - ulong mtdoffset = opts->offset; - ulong erasesize_blockalign; - u_char *buffer = opts->buffer; - size_t written; - int result; - - if (opts->pad && opts->writeoob) { - printf("Can't pad when oob data is present.\n"); - return -1; - } - - /* set erasesize to specified number of blocks - to match - * jffs2 (virtual) block size */ - if (opts->blockalign == 0) { - erasesize_blockalign = meminfo->erasesize; - } else { - erasesize_blockalign = meminfo->erasesize * opts->blockalign; - } - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - /* read the current oob info */ - memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); - - /* write without ecc? */ - if (opts->noecc) { - memcpy(&meminfo->oobinfo, &none_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; - } - - /* autoplace ECC? */ - if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { - - memcpy(&meminfo->oobinfo, &autoplace_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; - } - - /* force OOB layout for jffs2 or yaffs? */ - if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_oobinfo *oobsel = - opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; - - if (meminfo->oobsize == 8) { - if (opts->forceyaffs) { - printf("YAFSS cannot operate on " - "256 Byte page size\n"); - goto restoreoob; - } - /* Adjust number of ecc bytes */ - jffs2_oobinfo.eccbytes = 3; - } - - memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); - } - - /* get image length */ - imglen = opts->length; - pagelen = meminfo->oobblock - + ((opts->writeoob != 0) ? meminfo->oobsize : 0); - - /* check, if file is pagealigned */ - if ((!opts->pad) && ((imglen % pagelen) != 0)) { - printf("Input block length is not page aligned\n"); - goto restoreoob; - } - - /* check, if length fits into device */ - if (((imglen / pagelen) * meminfo->oobblock) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); - printf("Input block does not fit into device\n"); - goto restoreoob; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { - blockstart = mtdoffset & (~erasesize_blockalign+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - goto restoreoob; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + erasesize_blockalign; - } - offs += erasesize_blockalign - / opts->blockalign; - } while (offs < blockstart + erasesize_blockalign); - } - - readlen = meminfo->oobblock; - if (opts->pad && (imglen < readlen)) { - readlen = imglen; - memset(data_buf + readlen, 0xff, - meminfo->oobblock - readlen); - } - - /* read page data from input memory buffer */ - memcpy(data_buf, buffer, readlen); - buffer += readlen; - - if (opts->writeoob) { - /* read OOB data from input memory block, exit - * on failure */ - memcpy(oob_buf, buffer, meminfo->oobsize); - buffer += meminfo->oobsize; - - /* write OOB data first, as ecc will be placed - * in there*/ - result = meminfo->write_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &written, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD writeoob failure: %d\n", - result); - goto restoreoob; - } - imglen -= meminfo->oobsize; - } - - /* write out the page data */ - result = meminfo->write(meminfo, - mtdoffset, - meminfo->oobblock, - &written, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("writing NAND page at offset 0x%lx failed\n", - mtdoffset); - goto restoreoob; - } - imglen -= readlen; - - if (!opts->quiet) { - unsigned long long n = (unsigned long long) - (opts->length-imglen) * 100; - int percent; - - do_div(n, opts->length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - printf("\rWriting data at 0x%x " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->oobblock; - } - - if (!opts->quiet) - printf("\n"); - -restoreoob: - if (oobinfochanged) { - memcpy(&meminfo->oobinfo, &old_oobinfo, - sizeof(meminfo->oobinfo)); - } - - if (imglen > 0) { - printf("Data did not fit into device, due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} - -/** - * nand_read_opts: - read image from NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts read options (@see struct nand_read_options) - * @return 0 in case of success - * - */ -int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) -{ - int imglen = opts->length; - int pagelen; - int baderaseblock; - int blockstart = -1; - int percent_complete = -1; - loff_t offs; - size_t readlen; - ulong mtdoffset = opts->offset; - u_char *buffer = opts->buffer; - int result; - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - pagelen = meminfo->oobblock - + ((opts->readoob != 0) ? meminfo->oobsize : 0); - - /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->oobblock) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); - printf("Input block is larger than device\n"); - return -1; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { - blockstart = mtdoffset & (~meminfo->erasesize+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - return -1; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + meminfo->erasesize; - } - offs += meminfo->erasesize; - - } while (offs < blockstart + meminfo->erasesize); - } - - - /* read page data to memory buffer */ - result = meminfo->read(meminfo, - mtdoffset, - meminfo->oobblock, - &readlen, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("reading NAND page at offset 0x%lx failed\n", - mtdoffset); - return -1; - } - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, data_buf, readlen); - buffer += readlen; - imglen -= readlen; - - if (opts->readoob) { - result = meminfo->read_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &readlen, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD readoob failure: %d\n", - result); - return -1; - } - - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, oob_buf, readlen); - - buffer += readlen; - imglen -= readlen; - } - - if (!opts->quiet) { - unsigned long long n = (unsigned long long) - (opts->length-imglen) * 100; - int percent; - - do_div(n, opts->length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - if (!opts->quiet) - printf("\rReading data from 0x%x " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->oobblock; - } - - if (!opts->quiet) - printf("\n"); - - if (imglen > 0) { - printf("Could not read entire image due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} - -/****************************************************************************** - * Support for locking / unlocking operations of some NAND devices - *****************************************************************************/ - -#define NAND_CMD_LOCK 0x2a -#define NAND_CMD_LOCK_TIGHT 0x2c -#define NAND_CMD_UNLOCK1 0x23 -#define NAND_CMD_UNLOCK2 0x24 -#define NAND_CMD_LOCK_STATUS 0x7a - -/** - * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT - * state - * - * @param meminfo nand mtd instance - * @param tight bring device in lock tight mode - * - * @return 0 on success, -1 in case of error - * - * The lock / lock-tight command only applies to the whole chip. To get some - * parts of the chip lock and others unlocked use the following sequence: - * - * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) - * - Call nand_unlock() once for each consecutive area to be unlocked - * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) - * - * If the device is in lock-tight state software can't change the - * current active lock/unlock state of all pages. nand_lock() / nand_unlock() - * calls will fail. It is only posible to leave lock-tight state by - * an hardware signal (low pulse on _WP pin) or by power down. - */ -int nand_lock(nand_info_t *meminfo, int tight) -{ - int ret = 0; - int status; - struct nand_chip *this = meminfo->priv; - - /* select the NAND device */ - this->select_chip(meminfo, 0); - - this->cmdfunc(meminfo, - (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), - -1, -1); - - /* call wait ready function */ - status = this->waitfunc(meminfo, this, FL_WRITING); - - /* see if device thinks it succeeded */ - if (status & 0x01) { - ret = -1; - } - - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - -/** - * nand_get_lock_status: - query current lock state from one page of NAND - * flash - * - * @param meminfo nand mtd instance - * @param offset page address to query (muss be page aligned!) - * - * @return -1 in case of error - * >0 lock status: - * bitfield with the following combinations: - * NAND_LOCK_STATUS_TIGHT: page in tight state - * NAND_LOCK_STATUS_LOCK: page locked - * NAND_LOCK_STATUS_UNLOCK: page unlocked - * - */ -int nand_get_lock_status(nand_info_t *meminfo, ulong offset) -{ - int ret = 0; - int chipnr; - int page; - struct nand_chip *this = meminfo->priv; - - /* select the NAND device */ - chipnr = (int)(offset >> this->chip_shift); - this->select_chip(meminfo, chipnr); - - - if ((offset & (meminfo->oobblock - 1)) != 0) { - printf ("nand_get_lock_status: " - "Start address must be beginning of " - "nand page!\n"); - ret = -1; - goto out; - } - - /* check the Lock Status */ - page = (int)(offset >> this->page_shift); - this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); - - ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT - | NAND_LOCK_STATUS_LOCK - | NAND_LOCK_STATUS_UNLOCK); - - out: - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - -/** - * nand_unlock: - Unlock area of NAND pages - * only one consecutive area can be unlocked at one time! - * - * @param meminfo nand mtd instance - * @param start start byte address - * @param length number of bytes to unlock (must be a multiple of - * page size nand->oobblock) - * - * @return 0 on success, -1 in case of error - */ -int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) -{ - int ret = 0; - int chipnr; - int status; - int page; - struct nand_chip *this = meminfo->priv; - printf ("nand_unlock: start: %08x, length: %d!\n", - (int)start, (int)length); - - /* select the NAND device */ - chipnr = (int)(start >> this->chip_shift); - this->select_chip(meminfo, chipnr); - - /* check the WP bit */ - this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); - if ((this->read_byte(meminfo) & 0x80) == 0) { - printf ("nand_unlock: Device is write protected!\n"); - ret = -1; - goto out; - } - - if ((start & (meminfo->oobblock - 1)) != 0) { - printf ("nand_unlock: Start address must be beginning of " - "nand page!\n"); - ret = -1; - goto out; - } - - if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { - printf ("nand_unlock: Length must be a multiple of nand page " - "size!\n"); - ret = -1; - goto out; - } - - /* submit address of first page to unlock */ - page = (int)(start >> this->page_shift); - this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); - - /* submit ADDRESS of LAST page to unlock */ - page += (int)(length >> this->page_shift) - 1; - this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); - - /* call wait ready function */ - status = this->waitfunc(meminfo, this, FL_WRITING); - /* see if device thinks it succeeded */ - if (status & 0x01) { - /* there was an error */ - ret = -1; - goto out; - } - - out: - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - -#endif diff --git a/drivers/nand_legacy/Makefile b/drivers/nand_legacy/Makefile deleted file mode 100644 index 95314d80ba..0000000000 --- a/drivers/nand_legacy/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -# -# (C) Copyright 2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# See file CREDITS for list of people who contributed to this -# project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA -# - -include $(TOPDIR)/config.mk - -LIB := $(obj)libnand_legacy.a - -COBJS := nand_legacy.o - -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) - -all: $(LIB) - -$(LIB): $(obj).depend $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -######################################################################### - -# defines $(obj).depend target -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/nand_legacy/nand_legacy.c b/drivers/nand_legacy/nand_legacy.c deleted file mode 100644 index 49d2ebb67d..0000000000 --- a/drivers/nand_legacy/nand_legacy.c +++ /dev/null @@ -1,1612 +0,0 @@ -/* - * (C) 2006 Denx - * Driver for NAND support, Rick Bronson - * borrowed heavily from: - * (c) 1999 Machine Vision Holdings, Inc. - * (c) 1999, 2000 David Woodhouse - * - * Added 16-bit nand support - * (C) 2004 Texas Instruments - */ - -#include -#include -#include -#include -#include - -#if defined(CONFIG_CMD_NAND) && defined(CFG_NAND_LEGACY) - -#include -#include -#include - -#ifdef CONFIG_OMAP1510 -void archflashwp(void *archdata, int wp); -#endif - -#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) - -#undef PSYCHO_DEBUG -#undef NAND_DEBUG - -/* ****************** WARNING ********************* - * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will - * erase (or at least attempt to erase) blocks that are marked - * bad. This can be very handy if you are _sure_ that the block - * is OK, say because you marked a good block bad to test bad - * block handling and you are done testing, or if you have - * accidentally marked blocks bad. - * - * Erasing factory marked bad blocks is a _bad_ idea. If the - * erase succeeds there is no reliable way to find them again, - * and attempting to program or erase bad blocks can affect - * the data in _other_ (good) blocks. - */ -#define ALLOW_ERASE_BAD_DEBUG 0 - -#define CONFIG_MTD_NAND_ECC /* enable ECC */ -#define CONFIG_MTD_NAND_ECC_JFFS2 - -/* bits for nand_legacy_rw() `cmd'; or together as needed */ -#define NANDRW_READ 0x01 -#define NANDRW_WRITE 0x00 -#define NANDRW_JFFS2 0x02 -#define NANDRW_JFFS2_SKIP 0x04 - - -/* - * Exported variables etc. - */ - -/* Definition of the out of band configuration structure */ -struct nand_oob_config { - /* position of ECC bytes inside oob */ - int ecc_pos[6]; - /* position of bad blk flag inside oob -1 = inactive */ - int badblock_pos; - /* position of ECC valid flag inside oob -1 = inactive */ - int eccvalid_pos; -} oob_config = { {0}, 0, 0}; - -struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}}; - -int curr_device = -1; /* Current NAND Device */ - - -/* - * Exported functionss - */ -int nand_legacy_erase(struct nand_chip* nand, size_t ofs, - size_t len, int clean); -int nand_legacy_rw(struct nand_chip* nand, int cmd, - size_t start, size_t len, - size_t * retlen, u_char * buf); -void nand_print(struct nand_chip *nand); -void nand_print_bad(struct nand_chip *nand); -int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, u_char * buf); -int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, const u_char * buf); - -/* - * Internals - */ -static int NanD_WaitReady(struct nand_chip *nand, int ale_wait); -static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, - size_t * retlen, u_char *buf, u_char *ecc_code); -static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, - size_t * retlen, const u_char * buf, - u_char * ecc_code); -#ifdef CONFIG_MTD_NAND_ECC -static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); -static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); -#endif - - -/* - * - * Function definitions - * - */ - -/* returns 0 if block containing pos is OK: - * valid erase block and - * not marked bad, or no bad mark position is specified - * returns 1 if marked bad or otherwise invalid - */ -static int check_block (struct nand_chip *nand, unsigned long pos) -{ - size_t retlen; - uint8_t oob_data; - uint16_t oob_data16[6]; - int page0 = pos & (-nand->erasesize); - int page1 = page0 + nand->oobblock; - int badpos = oob_config.badblock_pos; - - if (pos >= nand->totlen) - return 1; - - if (badpos < 0) - return 0; /* no way to check, assume OK */ - - if (nand->bus16) { - if (nand_read_oob(nand, (page0 + 0), 12, &retlen, (uint8_t *)oob_data16) - || (oob_data16[2] & 0xff00) != 0xff00) - return 1; - if (nand_read_oob(nand, (page1 + 0), 12, &retlen, (uint8_t *)oob_data16) - || (oob_data16[2] & 0xff00) != 0xff00) - return 1; - } else { - /* Note - bad block marker can be on first or second page */ - if (nand_read_oob(nand, page0 + badpos, 1, &retlen, (unsigned char *)&oob_data) - || oob_data != 0xff - || nand_read_oob (nand, page1 + badpos, 1, &retlen, (unsigned char *)&oob_data) - || oob_data != 0xff) - return 1; - } - - return 0; -} - -/* print bad blocks in NAND flash */ -void nand_print_bad(struct nand_chip* nand) -{ - unsigned long pos; - - for (pos = 0; pos < nand->totlen; pos += nand->erasesize) { - if (check_block(nand, pos)) - printf(" 0x%8.8lx\n", pos); - } - puts("\n"); -} - -/* cmd: 0: NANDRW_WRITE write, fail on bad block - * 1: NANDRW_READ read, fail on bad block - * 2: NANDRW_WRITE | NANDRW_JFFS2 write, skip bad blocks - * 3: NANDRW_READ | NANDRW_JFFS2 read, data all 0xff for bad blocks - * 7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks - */ -int nand_legacy_rw (struct nand_chip* nand, int cmd, - size_t start, size_t len, - size_t * retlen, u_char * buf) -{ - int ret = 0, n, total = 0; - char eccbuf[6]; - /* eblk (once set) is the start of the erase block containing the - * data being processed. - */ - unsigned long eblk = ~0; /* force mismatch on first pass */ - unsigned long erasesize = nand->erasesize; - - while (len) { - if ((start & (-erasesize)) != eblk) { - /* have crossed into new erase block, deal with - * it if it is sure marked bad. - */ - eblk = start & (-erasesize); /* start of block */ - if (check_block(nand, eblk)) { - if (cmd == (NANDRW_READ | NANDRW_JFFS2)) { - while (len > 0 && - start - eblk < erasesize) { - *(buf++) = 0xff; - ++start; - ++total; - --len; - } - continue; - } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) { - start += erasesize; - continue; - } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) { - /* skip bad block */ - start += erasesize; - continue; - } else { - ret = 1; - break; - } - } - } - /* The ECC will not be calculated correctly if - less than 512 is written or read */ - /* Is request at least 512 bytes AND it starts on a proper boundry */ - if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200)) - printf("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\n"); - - if (cmd & NANDRW_READ) { - ret = nand_read_ecc(nand, start, - min(len, eblk + erasesize - start), - (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); - } else { - ret = nand_write_ecc(nand, start, - min(len, eblk + erasesize - start), - (size_t *)&n, (u_char*)buf, (u_char *)eccbuf); - } - - if (ret) - break; - - start += n; - buf += n; - total += n; - len -= n; - } - if (retlen) - *retlen = total; - - return ret; -} - -void nand_print(struct nand_chip *nand) -{ - if (nand->numchips > 1) { - printf("%s at 0x%lx,\n" - "\t %d chips %s, size %d MB, \n" - "\t total size %ld MB, sector size %ld kB\n", - nand->name, nand->IO_ADDR, nand->numchips, - nand->chips_name, 1 << (nand->chipshift - 20), - nand->totlen >> 20, nand->erasesize >> 10); - } - else { - printf("%s at 0x%lx (", nand->chips_name, nand->IO_ADDR); - print_size(nand->totlen, ", "); - print_size(nand->erasesize, " sector)\n"); - } -} - -/* ------------------------------------------------------------------------- */ - -static int NanD_WaitReady(struct nand_chip *nand, int ale_wait) -{ - /* This is inline, to optimise the common case, where it's ready instantly */ - int ret = 0; - -#ifdef NAND_NO_RB /* in config file, shorter delays currently wrap accesses */ - if(ale_wait) - NAND_WAIT_READY(nand); /* do the worst case 25us wait */ - else - udelay(10); -#else /* has functional r/b signal */ - NAND_WAIT_READY(nand); -#endif - return ret; -} - -/* NanD_Command: Send a flash command to the flash chip */ - -static inline int NanD_Command(struct nand_chip *nand, unsigned char command) -{ - unsigned long nandptr = nand->IO_ADDR; - - /* Assert the CLE (Command Latch Enable) line to the flash chip */ - NAND_CTL_SETCLE(nandptr); - - /* Send the command */ - WRITE_NAND_COMMAND(command, nandptr); - - /* Lower the CLE line */ - NAND_CTL_CLRCLE(nandptr); - -#ifdef NAND_NO_RB - if(command == NAND_CMD_RESET){ - u_char ret_val; - NanD_Command(nand, NAND_CMD_STATUS); - do { - ret_val = READ_NAND(nandptr);/* wait till ready */ - } while((ret_val & 0x40) != 0x40); - } -#endif - return NanD_WaitReady(nand, 0); -} - -/* NanD_Address: Set the current address for the flash chip */ - -static int NanD_Address(struct nand_chip *nand, int numbytes, unsigned long ofs) -{ - unsigned long nandptr; - int i; - - nandptr = nand->IO_ADDR; - - /* Assert the ALE (Address Latch Enable) line to the flash chip */ - NAND_CTL_SETALE(nandptr); - - /* Send the address */ - /* Devices with 256-byte page are addressed as: - * Column (bits 0-7), Page (bits 8-15, 16-23, 24-31) - * there is no device on the market with page256 - * and more than 24 bits. - * Devices with 512-byte page are addressed as: - * Column (bits 0-7), Page (bits 9-16, 17-24, 25-31) - * 25-31 is sent only if the chip support it. - * bit 8 changes the read command to be sent - * (NAND_CMD_READ0 or NAND_CMD_READ1). - */ - - if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) - WRITE_NAND_ADDRESS(ofs, nandptr); - - ofs = ofs >> nand->page_shift; - - if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) { - for (i = 0; i < nand->pageadrlen; i++, ofs = ofs >> 8) { - WRITE_NAND_ADDRESS(ofs, nandptr); - } - } - - /* Lower the ALE line */ - NAND_CTL_CLRALE(nandptr); - - /* Wait for the chip to respond */ - return NanD_WaitReady(nand, 1); -} - -/* NanD_SelectChip: Select a given flash chip within the current floor */ - -static inline int NanD_SelectChip(struct nand_chip *nand, int chip) -{ - /* Wait for it to be ready */ - return NanD_WaitReady(nand, 0); -} - -/* NanD_IdentChip: Identify a given NAND chip given {floor,chip} */ - -static int NanD_IdentChip(struct nand_chip *nand, int floor, int chip) -{ - int mfr, id, i; - - NAND_ENABLE_CE(nand); /* set pin low */ - /* Reset the chip */ - if (NanD_Command(nand, NAND_CMD_RESET)) { -#ifdef NAND_DEBUG - printf("NanD_Command (reset) for %d,%d returned true\n", - floor, chip); -#endif - NAND_DISABLE_CE(nand); /* set pin high */ - return 0; - } - - /* Read the NAND chip ID: 1. Send ReadID command */ - if (NanD_Command(nand, NAND_CMD_READID)) { -#ifdef NAND_DEBUG - printf("NanD_Command (ReadID) for %d,%d returned true\n", - floor, chip); -#endif - NAND_DISABLE_CE(nand); /* set pin high */ - return 0; - } - - /* Read the NAND chip ID: 2. Send address byte zero */ - NanD_Address(nand, ADDR_COLUMN, 0); - - /* Read the manufacturer and device id codes from the device */ - - mfr = READ_NAND(nand->IO_ADDR); - - id = READ_NAND(nand->IO_ADDR); - - NAND_DISABLE_CE(nand); /* set pin high */ - -#ifdef NAND_DEBUG - printf("NanD_Command (ReadID) got %x %x\n", mfr, id); -#endif - if (mfr == 0xff || mfr == 0) { - /* No response - return failure */ - return 0; - } - - /* Check it's the same as the first chip we identified. - * M-Systems say that any given nand_chip device should only - * contain _one_ type of flash part, although that's not a - * hardware restriction. */ - if (nand->mfr) { - if (nand->mfr == mfr && nand->id == id) { - return 1; /* This is another the same the first */ - } else { - printf("Flash chip at floor %d, chip %d is different:\n", - floor, chip); - } - } - - /* Print and store the manufacturer and ID codes. */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (mfr == nand_flash_ids[i].manufacture_id && - id == nand_flash_ids[i].model_id) { -#ifdef NAND_DEBUG - printf("Flash chip found:\n\t Manufacturer ID: 0x%2.2X, " - "Chip ID: 0x%2.2X (%s)\n", mfr, id, - nand_flash_ids[i].name); -#endif - if (!nand->mfr) { - nand->mfr = mfr; - nand->id = id; - nand->chipshift = - nand_flash_ids[i].chipshift; - nand->page256 = nand_flash_ids[i].page256; - nand->eccsize = 256; - if (nand->page256) { - nand->oobblock = 256; - nand->oobsize = 8; - nand->page_shift = 8; - } else { - nand->oobblock = 512; - nand->oobsize = 16; - nand->page_shift = 9; - } - nand->pageadrlen = nand_flash_ids[i].pageadrlen; - nand->erasesize = nand_flash_ids[i].erasesize; - nand->chips_name = nand_flash_ids[i].name; - nand->bus16 = nand_flash_ids[i].bus16; - return 1; - } - return 0; - } - } - - -#ifdef NAND_DEBUG - /* We haven't fully identified the chip. Print as much as we know. */ - printf("Unknown flash chip found: %2.2X %2.2X\n", - id, mfr); -#endif - - return 0; -} - -/* NanD_ScanChips: Find all NAND chips present in a nand_chip, and identify them */ - -static void NanD_ScanChips(struct nand_chip *nand) -{ - int floor, chip; - int numchips[NAND_MAX_FLOORS]; - int maxchips = NAND_MAX_CHIPS; - int ret = 1; - - nand->numchips = 0; - nand->mfr = 0; - nand->id = 0; - - - /* For each floor, find the number of valid chips it contains */ - for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { - ret = 1; - numchips[floor] = 0; - for (chip = 0; chip < maxchips && ret != 0; chip++) { - - ret = NanD_IdentChip(nand, floor, chip); - if (ret) { - numchips[floor]++; - nand->numchips++; - } - } - } - - /* If there are none at all that we recognise, bail */ - if (!nand->numchips) { -#ifdef NAND_DEBUG - puts ("No NAND flash chips recognised.\n"); -#endif - return; - } - - /* Allocate an array to hold the information for each chip */ - nand->chips = malloc(sizeof(struct Nand) * nand->numchips); - if (!nand->chips) { - puts ("No memory for allocating chip info structures\n"); - return; - } - - ret = 0; - - /* Fill out the chip array with {floor, chipno} for each - * detected chip in the device. */ - for (floor = 0; floor < NAND_MAX_FLOORS; floor++) { - for (chip = 0; chip < numchips[floor]; chip++) { - nand->chips[ret].floor = floor; - nand->chips[ret].chip = chip; - nand->chips[ret].curadr = 0; - nand->chips[ret].curmode = 0x50; - ret++; - } - } - - /* Calculate and print the total size of the device */ - nand->totlen = nand->numchips * (1 << nand->chipshift); - -#ifdef NAND_DEBUG - printf("%d flash chips found. Total nand_chip size: %ld MB\n", - nand->numchips, nand->totlen >> 20); -#endif -} - -/* we need to be fast here, 1 us per read translates to 1 second per meg */ -static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr) -{ - unsigned long nandptr = nand->IO_ADDR; - - NanD_Command (nand, NAND_CMD_READ0); - - if (nand->bus16) { - u16 val; - - while (cntr >= 16) { - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - cntr -= 16; - } - - while (cntr > 0) { - val = READ_NAND (nandptr); - *data_buf++ = val & 0xff; - *data_buf++ = val >> 8; - cntr -= 2; - } - } else { - while (cntr >= 16) { - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - *data_buf++ = READ_NAND (nandptr); - cntr -= 16; - } - - while (cntr > 0) { - *data_buf++ = READ_NAND (nandptr); - cntr--; - } - } -} - -/* - * NAND read with ECC - */ -static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, - size_t * retlen, u_char *buf, u_char *ecc_code) -{ - int col, page; - int ecc_status = 0; -#ifdef CONFIG_MTD_NAND_ECC - int j; - int ecc_failed = 0; - u_char *data_poi; - u_char ecc_calc[6]; -#endif - - /* Do not allow reads past end of device */ - if ((start + len) > nand->totlen) { - printf ("%s: Attempt read beyond end of device %x %x %x\n", - __FUNCTION__, (uint) start, (uint) len, (uint) nand->totlen); - *retlen = 0; - return -1; - } - - /* First we calculate the starting page */ - /*page = shr(start, nand->page_shift);*/ - page = start >> nand->page_shift; - - /* Get raw starting column */ - col = start & (nand->oobblock - 1); - - /* Initialize return value */ - *retlen = 0; - - /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Loop until all data read */ - while (*retlen < len) { - -#ifdef CONFIG_MTD_NAND_ECC - /* Do we have this page in cache ? */ - if (nand->cache_page == page) - goto readdata; - /* Send the read command */ - NanD_Command(nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Read in a page + oob data */ - NanD_ReadBuf(nand, nand->data_buf, nand->oobblock + nand->oobsize); - - /* copy data into cache, for read out of cache and if ecc fails */ - if (nand->data_cache) { - memcpy (nand->data_cache, nand->data_buf, - nand->oobblock + nand->oobsize); - } - - /* Pick the ECC bytes out of the oob data */ - for (j = 0; j < 6; j++) { - ecc_code[j] = nand->data_buf[(nand->oobblock + oob_config.ecc_pos[j])]; - } - - /* Calculate the ECC and verify it */ - /* If block was not written with ECC, skip ECC */ - if (oob_config.eccvalid_pos != -1 && - (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0x0f) != 0x0f) { - - nand_calculate_ecc (&nand->data_buf[0], &ecc_calc[0]); - switch (nand_correct_data (&nand->data_buf[0], &ecc_code[0], &ecc_calc[0])) { - case -1: - printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); - ecc_failed++; - break; - case 1: - case 2: /* transfer ECC corrected data to cache */ - if (nand->data_cache) - memcpy (nand->data_cache, nand->data_buf, 256); - break; - } - } - - if (oob_config.eccvalid_pos != -1 && - nand->oobblock == 512 && (nand->data_buf[nand->oobblock + oob_config.eccvalid_pos] & 0xf0) != 0xf0) { - - nand_calculate_ecc (&nand->data_buf[256], &ecc_calc[3]); - switch (nand_correct_data (&nand->data_buf[256], &ecc_code[3], &ecc_calc[3])) { - case -1: - printf ("%s: Failed ECC read, page 0x%08x\n", __FUNCTION__, page); - ecc_failed++; - break; - case 1: - case 2: /* transfer ECC corrected data to cache */ - if (nand->data_cache) - memcpy (&nand->data_cache[256], &nand->data_buf[256], 256); - break; - } - } -readdata: - /* Read the data from ECC data buffer into return buffer */ - data_poi = (nand->data_cache) ? nand->data_cache : nand->data_buf; - data_poi += col; - if ((*retlen + (nand->oobblock - col)) >= len) { - memcpy (buf + *retlen, data_poi, len - *retlen); - *retlen = len; - } else { - memcpy (buf + *retlen, data_poi, nand->oobblock - col); - *retlen += nand->oobblock - col; - } - /* Set cache page address, invalidate, if ecc_failed */ - nand->cache_page = (nand->data_cache && !ecc_failed) ? page : -1; - - ecc_status += ecc_failed; - ecc_failed = 0; - -#else - /* Send the read command */ - NanD_Command(nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Read the data directly into the return buffer */ - if ((*retlen + (nand->oobblock - col)) >= len) { - NanD_ReadBuf(nand, buf + *retlen, len - *retlen); - *retlen = len; - /* We're done */ - continue; - } else { - NanD_ReadBuf(nand, buf + *retlen, nand->oobblock - col); - *retlen += nand->oobblock - col; - } -#endif - /* For subsequent reads align to page boundary. */ - col = 0; - /* Increment page address */ - page++; - } - - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ - - /* - * Return success, if no ECC failures, else -EIO - * fs driver will take care of that, because - * retlen == desired len and result == -EIO - */ - return ecc_status ? -1 : 0; -} - -/* - * Nand_page_program function is used for write and writev ! - */ -static int nand_write_page (struct nand_chip *nand, - int page, int col, int last, u_char * ecc_code) -{ - - int i; - unsigned long nandptr = nand->IO_ADDR; - -#ifdef CONFIG_MTD_NAND_ECC -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - int ecc_bytes = (nand->oobblock == 512) ? 6 : 3; -#endif -#endif - /* pad oob area */ - for (i = nand->oobblock; i < nand->oobblock + nand->oobsize; i++) - nand->data_buf[i] = 0xff; - -#ifdef CONFIG_MTD_NAND_ECC - /* Zero out the ECC array */ - for (i = 0; i < 6; i++) - ecc_code[i] = 0x00; - - /* Read back previous written data, if col > 0 */ - if (col) { - NanD_Command (nand, NAND_CMD_READ0); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - if (nand->bus16) { - u16 val; - - for (i = 0; i < col; i += 2) { - val = READ_NAND (nandptr); - nand->data_buf[i] = val & 0xff; - nand->data_buf[i + 1] = val >> 8; - } - } else { - for (i = 0; i < col; i++) - nand->data_buf[i] = READ_NAND (nandptr); - } - } - - /* Calculate and write the ECC if we have enough data */ - if ((col < nand->eccsize) && (last >= nand->eccsize)) { - nand_calculate_ecc (&nand->data_buf[0], &(ecc_code[0])); - for (i = 0; i < 3; i++) { - nand->data_buf[(nand->oobblock + - oob_config.ecc_pos[i])] = ecc_code[i]; - } - if (oob_config.eccvalid_pos != -1) { - nand->data_buf[nand->oobblock + - oob_config.eccvalid_pos] = 0xf0; - } - } - - /* Calculate and write the second ECC if we have enough data */ - if ((nand->oobblock == 512) && (last == nand->oobblock)) { - nand_calculate_ecc (&nand->data_buf[256], &(ecc_code[3])); - for (i = 3; i < 6; i++) { - nand->data_buf[(nand->oobblock + - oob_config.ecc_pos[i])] = ecc_code[i]; - } - if (oob_config.eccvalid_pos != -1) { - nand->data_buf[nand->oobblock + - oob_config.eccvalid_pos] &= 0x0f; - } - } -#endif - /* Prepad for partial page programming !!! */ - for (i = 0; i < col; i++) - nand->data_buf[i] = 0xff; - - /* Postpad for partial page programming !!! oob is already padded */ - for (i = last; i < nand->oobblock; i++) - nand->data_buf[i] = 0xff; - - /* Send command to begin auto page programming */ - NanD_Command (nand, NAND_CMD_READ0); - NanD_Command (nand, NAND_CMD_SEQIN); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Write out complete page of data */ - if (nand->bus16) { - for (i = 0; i < (nand->oobblock + nand->oobsize); i += 2) { - WRITE_NAND (nand->data_buf[i] + - (nand->data_buf[i + 1] << 8), - nand->IO_ADDR); - } - } else { - for (i = 0; i < (nand->oobblock + nand->oobsize); i++) - WRITE_NAND (nand->data_buf[i], nand->IO_ADDR); - } - - /* Send command to actually program the data */ - NanD_Command (nand, NAND_CMD_PAGEPROG); - NanD_Command (nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { - u_char ret_val; - - do { - ret_val = READ_NAND (nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - /* See if device thinks it succeeded */ - if (READ_NAND (nand->IO_ADDR) & 0x01) { - printf ("%s: Failed write, page 0x%08x, ", __FUNCTION__, - page); - return -1; - } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* - * 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. - */ - - /* Send command to read back the page */ - if (col < nand->eccsize) - NanD_Command (nand, NAND_CMD_READ0); - else - NanD_Command (nand, NAND_CMD_READ1); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - - /* Loop through and verify the data */ - if (nand->bus16) { - for (i = col; i < last; i = +2) { - if ((nand->data_buf[i] + - (nand->data_buf[i + 1] << 8)) != READ_NAND (nand->IO_ADDR)) { - printf ("%s: Failed write verify, page 0x%08x ", - __FUNCTION__, page); - return -1; - } - } - } else { - for (i = col; i < last; i++) { - if (nand->data_buf[i] != READ_NAND (nand->IO_ADDR)) { - printf ("%s: Failed write verify, page 0x%08x ", - __FUNCTION__, page); - return -1; - } - } - } - -#ifdef CONFIG_MTD_NAND_ECC - /* - * We also want to check that the ECC bytes wrote - * correctly for the same reasons stated above. - */ - NanD_Command (nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + (col >> 1)); - } else { - NanD_Address (nand, ADDR_COLUMN_PAGE, - (page << nand->page_shift) + col); - } - if (nand->bus16) { - for (i = 0; i < nand->oobsize; i += 2) { - u16 val; - - val = READ_NAND (nand->IO_ADDR); - nand->data_buf[i] = val & 0xff; - nand->data_buf[i + 1] = val >> 8; - } - } else { - for (i = 0; i < nand->oobsize; i++) { - nand->data_buf[i] = READ_NAND (nand->IO_ADDR); - } - } - for (i = 0; i < ecc_bytes; i++) { - if ((nand->data_buf[(oob_config.ecc_pos[i])] != ecc_code[i]) && ecc_code[i]) { - printf ("%s: Failed ECC write " - "verify, page 0x%08x, " - "%6i bytes were succesful\n", - __FUNCTION__, page, i); - return -1; - } - } -#endif /* CONFIG_MTD_NAND_ECC */ -#endif /* CONFIG_MTD_NAND_VERIFY_WRITE */ - return 0; -} - -static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, - size_t * retlen, const u_char * buf, u_char * ecc_code) -{ - int i, page, col, cnt, ret = 0; - - /* Do not allow write past end of device */ - if ((to + len) > nand->totlen) { - printf ("%s: Attempt to write past end of page\n", __FUNCTION__); - return -1; - } - - /* Shift to get page */ - page = ((int) to) >> nand->page_shift; - - /* Get the starting column */ - col = to & (nand->oobblock - 1); - - /* Initialize return length value */ - *retlen = 0; - - /* Select the NAND device */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,0); -#endif -#ifdef CFG_NAND_WP - NAND_WP_OFF(); -#endif - - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("%s: Device is write protected!!!\n", __FUNCTION__); - ret = -1; - goto out; - } - - /* Loop until all data is written */ - while (*retlen < len) { - /* Invalidate cache, if we write to this page */ - if (nand->cache_page == page) - nand->cache_page = -1; - - /* Write data into buffer */ - if ((col + len) >= nand->oobblock) { - for (i = col, cnt = 0; i < nand->oobblock; i++, cnt++) { - nand->data_buf[i] = buf[(*retlen + cnt)]; - } - } else { - for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++) { - nand->data_buf[i] = buf[(*retlen + cnt)]; - } - } - /* We use the same function for write and writev !) */ - ret = nand_write_page (nand, page, col, i, ecc_code); - if (ret) - goto out; - - /* Next data start at page boundary */ - col = 0; - - /* Update written bytes count */ - *retlen += cnt; - - /* Increment page address */ - page++; - } - - /* Return happy */ - *retlen = len; - -out: - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,1); -#endif -#ifdef CFG_NAND_WP - NAND_WP_ON(); -#endif - - return ret; -} - -/* read from the 16 bytes of oob data that correspond to a 512 byte - * page or 2 256-byte pages. - */ -int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, u_char * buf) -{ - int len256 = 0; - struct Nand *mychip; - int ret = 0; - - mychip = &nand->chips[ofs >> nand->chipshift]; - - /* update address for 2M x 8bit devices. OOB starts on the second */ - /* page to maintain compatibility with nand_read_ecc. */ - if (nand->page256) { - if (!(ofs & 0x8)) - ofs += 0x100; - else - ofs -= 0x8; - } - - NAND_ENABLE_CE(nand); /* set pin low */ - NanD_Command(nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } - - /* treat crossing 8-byte OOB data for 2M x 8bit devices */ - /* Note: datasheet says it should automaticaly wrap to the */ - /* next OOB block, but it didn't work here. mf. */ - if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { - len256 = (ofs | 0x7) + 1 - ofs; - NanD_ReadBuf(nand, buf, len256); - - NanD_Command(nand, NAND_CMD_READOOB); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); - } - - NanD_ReadBuf(nand, &buf[len256], len - len256); - - *retlen = len; - /* Reading the full OOB data drops us off of the end of the page, - * causing the flash device to go into busy mode, so we need - * to wait until ready 11.4.1 and Toshiba TC58256FT nands */ - - ret = NanD_WaitReady(nand, 1); - NAND_DISABLE_CE(nand); /* set pin high */ - - return ret; - -} - -/* write to the 16 bytes of oob data that correspond to a 512 byte - * page or 2 256-byte pages. - */ -int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len, - size_t * retlen, const u_char * buf) -{ - int len256 = 0; - int i; - unsigned long nandptr = nand->IO_ADDR; - -#ifdef PSYCHO_DEBUG - printf("nand_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n", - (long)ofs, len, buf[0], buf[1], buf[2], buf[3], - buf[8], buf[9], buf[14],buf[15]); -#endif - - NAND_ENABLE_CE(nand); /* set pin low to enable chip */ - - /* Reset the chip */ - NanD_Command(nand, NAND_CMD_RESET); - - /* issue the Read2 command to set the pointer to the Spare Data Area. */ - NanD_Command(nand, NAND_CMD_READOOB); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } - - /* update address for 2M x 8bit devices. OOB starts on the second */ - /* page to maintain compatibility with nand_read_ecc. */ - if (nand->page256) { - if (!(ofs & 0x8)) - ofs += 0x100; - else - ofs -= 0x8; - } - - /* issue the Serial Data In command to initial the Page Program process */ - NanD_Command(nand, NAND_CMD_SEQIN); - if (nand->bus16) { - NanD_Address(nand, ADDR_COLUMN_PAGE, - ((ofs >> nand->page_shift) << nand->page_shift) + - ((ofs & (nand->oobblock - 1)) >> 1)); - } else { - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs); - } - - /* treat crossing 8-byte OOB data for 2M x 8bit devices */ - /* Note: datasheet says it should automaticaly wrap to the */ - /* next OOB block, but it didn't work here. mf. */ - if (nand->page256 && ofs + len > (ofs | 0x7) + 1) { - len256 = (ofs | 0x7) + 1 - ofs; - for (i = 0; i < len256; i++) - WRITE_NAND(buf[i], nandptr); - - NanD_Command(nand, NAND_CMD_PAGEPROG); - NanD_Command(nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - puts ("Error programming oob data\n"); - /* There was an error */ - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = 0; - return -1; - } - NanD_Command(nand, NAND_CMD_SEQIN); - NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff)); - } - - if (nand->bus16) { - for (i = len256; i < len; i += 2) { - WRITE_NAND(buf[i] + (buf[i+1] << 8), nandptr); - } - } else { - for (i = len256; i < len; i++) - WRITE_NAND(buf[i], nandptr); - } - - NanD_Command(nand, NAND_CMD_PAGEPROG); - NanD_Command(nand, NAND_CMD_STATUS); -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - puts ("Error programming oob data\n"); - /* There was an error */ - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = 0; - return -1; - } - - NAND_DISABLE_CE(nand); /* set pin high */ - *retlen = len; - return 0; - -} - -int nand_legacy_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean) -{ - /* This is defined as a structure so it will work on any system - * using native endian jffs2 (the default). - */ - static struct jffs2_unknown_node clean_marker = { - JFFS2_MAGIC_BITMASK, - JFFS2_NODETYPE_CLEANMARKER, - 8 /* 8 bytes in this node */ - }; - unsigned long nandptr; - struct Nand *mychip; - int ret = 0; - - if (ofs & (nand->erasesize-1) || len & (nand->erasesize-1)) { - printf ("Offset and size must be sector aligned, erasesize = %d\n", - (int) nand->erasesize); - return -1; - } - - nandptr = nand->IO_ADDR; - - /* Select the NAND device */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,0); -#endif -#ifdef CFG_NAND_WP - NAND_WP_OFF(); -#endif - NAND_ENABLE_CE(nand); /* set pin low */ - - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("nand_write_ecc: Device is write protected!!!\n"); - ret = -1; - goto out; - } - - /* Check the WP bit */ - NanD_Command(nand, NAND_CMD_STATUS); - if (!(READ_NAND(nand->IO_ADDR) & 0x80)) { - printf ("%s: Device is write protected!!!\n", __FUNCTION__); - ret = -1; - goto out; - } - - /* FIXME: Do nand in the background. Use timers or schedule_task() */ - while(len) { - /*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/ - mychip = &nand->chips[ofs >> nand->chipshift]; - - /* always check for bad block first, genuine bad blocks - * should _never_ be erased. - */ - if (ALLOW_ERASE_BAD_DEBUG || !check_block(nand, ofs)) { - /* Select the NAND device */ - NAND_ENABLE_CE(nand); /* set pin low */ - - NanD_Command(nand, NAND_CMD_ERASE1); - NanD_Address(nand, ADDR_PAGE, ofs); - NanD_Command(nand, NAND_CMD_ERASE2); - - NanD_Command(nand, NAND_CMD_STATUS); - -#ifdef NAND_NO_RB - { u_char ret_val; - do { - ret_val = READ_NAND(nandptr); /* wait till ready */ - } while ((ret_val & 0x40) != 0x40); - } -#endif - if (READ_NAND(nandptr) & 1) { - printf ("%s: Error erasing at 0x%lx\n", - __FUNCTION__, (long)ofs); - /* There was an error */ - ret = -1; - goto out; - } - if (clean) { - int n; /* return value not used */ - int p, l; - - /* clean marker position and size depend - * on the page size, since 256 byte pages - * only have 8 bytes of oob data - */ - if (nand->page256) { - p = NAND_JFFS2_OOB8_FSDAPOS; - l = NAND_JFFS2_OOB8_FSDALEN; - } else { - p = NAND_JFFS2_OOB16_FSDAPOS; - l = NAND_JFFS2_OOB16_FSDALEN; - } - - ret = nand_write_oob(nand, ofs + p, l, (size_t *)&n, - (u_char *)&clean_marker); - /* quit here if write failed */ - if (ret) - goto out; - } - } - ofs += nand->erasesize; - len -= nand->erasesize; - } - -out: - /* De-select the NAND device */ - NAND_DISABLE_CE(nand); /* set pin high */ -#ifdef CONFIG_OMAP1510 - archflashwp(0,1); -#endif -#ifdef CFG_NAND_WP - NAND_WP_ON(); -#endif - - return ret; -} - - -static inline int nandcheck(unsigned long potential, unsigned long physadr) -{ - return 0; -} - -unsigned long nand_probe(unsigned long physadr) -{ - struct nand_chip *nand = NULL; - int i = 0, ChipID = 1; - -#ifdef CONFIG_MTD_NAND_ECC_JFFS2 - oob_config.ecc_pos[0] = NAND_JFFS2_OOB_ECCPOS0; - oob_config.ecc_pos[1] = NAND_JFFS2_OOB_ECCPOS1; - oob_config.ecc_pos[2] = NAND_JFFS2_OOB_ECCPOS2; - oob_config.ecc_pos[3] = NAND_JFFS2_OOB_ECCPOS3; - oob_config.ecc_pos[4] = NAND_JFFS2_OOB_ECCPOS4; - oob_config.ecc_pos[5] = NAND_JFFS2_OOB_ECCPOS5; - oob_config.eccvalid_pos = 4; -#else - oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0; - oob_config.ecc_pos[1] = NAND_NOOB_ECCPOS1; - oob_config.ecc_pos[2] = NAND_NOOB_ECCPOS2; - oob_config.ecc_pos[3] = NAND_NOOB_ECCPOS3; - oob_config.ecc_pos[4] = NAND_NOOB_ECCPOS4; - oob_config.ecc_pos[5] = NAND_NOOB_ECCPOS5; - oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS; -#endif - oob_config.badblock_pos = 5; - - for (i=0; iIO_ADDR = physadr; - nand->cache_page = -1; /* init the cache page */ - NanD_ScanChips(nand); - - if (nand->totlen == 0) { - /* no chips found, clean up and quit */ - memset((char *)nand, 0, sizeof(struct nand_chip)); - nand->ChipID = NAND_ChipID_UNKNOWN; - return (0); - } - - nand->ChipID = ChipID; - if (curr_device == -1) - curr_device = i; - - nand->data_buf = malloc (nand->oobblock + nand->oobsize); - if (!nand->data_buf) { - puts ("Cannot allocate memory for data structures.\n"); - return (0); - } - - return (nand->totlen); -} - -#ifdef CONFIG_MTD_NAND_ECC -/* - * 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 -}; - - -/* - * 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; -} - -/* - * Calculate 3 byte ECC code for 256 byte block - */ -static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) -{ - u_char idx, reg1, reg3; - int j; - - /* Initialize variables */ - reg1 = 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; - - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (u_char) j; - } - } - - /* Create non-inverted ECC code from line parity */ - nand_trans_result((reg1 & 0x40) ? ~reg3 : reg3, 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; -} - -/* - * Detect and correct a 1 bit error for 256 byte block - */ -static int nand_correct_data (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 - -#ifdef CONFIG_JFFS2_NAND -int read_jffs2_nand(size_t start, size_t len, - size_t * retlen, u_char * buf, int nanddev) -{ - return nand_legacy_rw(nand_dev_desc + nanddev, NANDRW_READ | NANDRW_JFFS2, - start, len, retlen, buf); -} -#endif /* CONFIG_JFFS2_NAND */ - -#endif diff --git a/drivers/onenand/Makefile b/drivers/onenand/Makefile deleted file mode 100644 index 2049413327..0000000000 --- a/drivers/onenand/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2005-2007 Samsung Electronics. -# Kyungmin Park -# -# See file CREDITS for list of people who contributed to this -# project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA -# - -include $(TOPDIR)/config.mk - -LIB := $(obj)libonenand.a - -COBJS := onenand_base.o onenand_bbt.o - -SRCS := $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) - -all: $(LIB) - -$(LIB): $(obj).depend $(OBJS) - $(AR) $(ARFLAGS) $@ $(OBJS) - -######################################################################### - -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/onenand/onenand_base.c b/drivers/onenand/onenand_base.c deleted file mode 100644 index 7983a4a0d8..0000000000 --- a/drivers/onenand/onenand_base.c +++ /dev/null @@ -1,1294 +0,0 @@ -/* - * linux/drivers/mtd/onenand/onenand_base.c - * - * Copyright (C) 2005-2007 Samsung Electronics - * Kyungmin Park - * - * 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 - -#ifdef CONFIG_CMD_ONENAND - -#include -#include -#include - -#include -#include - -static const unsigned char ffchars[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ -}; - -/** - * onenand_readw - [OneNAND Interface] Read OneNAND register - * @param addr address to read - * - * Read OneNAND register - */ -static unsigned short onenand_readw(void __iomem * addr) -{ - return readw(addr); -} - -/** - * onenand_writew - [OneNAND Interface] Write OneNAND register with value - * @param value value to write - * @param addr address to write - * - * Write OneNAND register with value - */ -static void onenand_writew(unsigned short value, void __iomem * addr) -{ - writew(value, addr); -} - -/** - * onenand_block_address - [DEFAULT] Get block address - * @param device the device id - * @param block the block - * @return translated block address if DDP, otherwise same - * - * Setup Start Address 1 Register (F100h) - */ -static int onenand_block_address(int device, int block) -{ - if (device & ONENAND_DEVICE_IS_DDP) { - /* Device Flash Core select, NAND Flash Block Address */ - int dfs = 0, density, mask; - - density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); - - if (block & mask) - dfs = 1; - - return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1)); - } - - return block; -} - -/** - * onenand_bufferram_address - [DEFAULT] Get bufferram address - * @param device the device id - * @param block the block - * @return set DBS value if DDP, otherwise 0 - * - * Setup Start Address 2 Register (F101h) for DDP - */ -static int onenand_bufferram_address(int device, int block) -{ - if (device & ONENAND_DEVICE_IS_DDP) { - /* Device BufferRAM Select */ - int dbs = 0, density, mask; - - density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); - - if (block & mask) - dbs = 1; - - return (dbs << ONENAND_DDP_SHIFT); - } - - return 0; -} - -/** - * onenand_page_address - [DEFAULT] Get page address - * @param page the page address - * @param sector the sector address - * @return combined page and sector address - * - * Setup Start Address 8 Register (F107h) - */ -static int onenand_page_address(int page, int sector) -{ - /* Flash Page Address, Flash Sector Address */ - int fpa, fsa; - - fpa = page & ONENAND_FPA_MASK; - fsa = sector & ONENAND_FSA_MASK; - - return ((fpa << ONENAND_FPA_SHIFT) | fsa); -} - -/** - * onenand_buffer_address - [DEFAULT] Get buffer address - * @param dataram1 DataRAM index - * @param sectors the sector address - * @param count the number of sectors - * @return the start buffer value - * - * Setup Start Buffer Register (F200h) - */ -static int onenand_buffer_address(int dataram1, int sectors, int count) -{ - int bsa, bsc; - - /* BufferRAM Sector Address */ - bsa = sectors & ONENAND_BSA_MASK; - - if (dataram1) - bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ - else - bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ - - /* BufferRAM Sector Count */ - bsc = count & ONENAND_BSC_MASK; - - return ((bsa << ONENAND_BSA_SHIFT) | bsc); -} - -/** - * onenand_command - [DEFAULT] Send command to OneNAND device - * @param mtd MTD device structure - * @param cmd the command to be sent - * @param addr offset to read from or write to - * @param len number of bytes to read or write - * - * Send command to OneNAND device. This function is used for middle/large page - * devices (1KB/2KB Bytes per page) - */ -static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, - size_t len) -{ - struct onenand_chip *this = mtd->priv; - int value, readcmd = 0; - int block, page; - /* Now we use page size operation */ - int sectors = 4, count = 4; - - /* Address translation */ - switch (cmd) { - case ONENAND_CMD_UNLOCK: - case ONENAND_CMD_LOCK: - case ONENAND_CMD_LOCK_TIGHT: - block = -1; - page = -1; - break; - - case ONENAND_CMD_ERASE: - case ONENAND_CMD_BUFFERRAM: - block = (int)(addr >> this->erase_shift); - page = -1; - break; - - default: - block = (int)(addr >> this->erase_shift); - page = (int)(addr >> this->page_shift); - page &= this->page_mask; - break; - } - - /* NOTE: The setting order of the registers is very important! */ - if (cmd == ONENAND_CMD_BUFFERRAM) { - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this->device_id, block); - this->write_word(value, - this->base + ONENAND_REG_START_ADDRESS2); - - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); - - return 0; - } - - if (block != -1) { - /* Write 'DFS, FBA' of Flash */ - value = onenand_block_address(this->device_id, block); - this->write_word(value, - this->base + ONENAND_REG_START_ADDRESS1); - } - - if (page != -1) { - int dataram; - - switch (cmd) { - case ONENAND_CMD_READ: - case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - readcmd = 1; - break; - - default: - dataram = ONENAND_CURRENT_BUFFERRAM(this); - break; - } - - /* Write 'FPA, FSA' of Flash */ - value = onenand_page_address(page, sectors); - this->write_word(value, - this->base + ONENAND_REG_START_ADDRESS8); - - /* Write 'BSA, BSC' of DataRAM */ - value = onenand_buffer_address(dataram, sectors, count); - this->write_word(value, this->base + ONENAND_REG_START_BUFFER); - - if (readcmd) { - /* Select DataRAM for DDP */ - value = - onenand_bufferram_address(this->device_id, block); - this->write_word(value, - this->base + - ONENAND_REG_START_ADDRESS2); - } - } - - /* Interrupt clear */ - this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); - /* Write command */ - this->write_word(cmd, this->base + ONENAND_REG_COMMAND); - - return 0; -} - -/** - * onenand_wait - [DEFAULT] wait until the command is done - * @param mtd MTD device structure - * @param state state to select the max. timeout value - * - * Wait for command done. This applies to all OneNAND command - * Read can take up to 30us, erase up to 2ms and program up to 350us - * according to general OneNAND specs - */ -static int onenand_wait(struct mtd_info *mtd, int state) -{ - struct onenand_chip *this = mtd->priv; - unsigned int flags = ONENAND_INT_MASTER; - unsigned int interrupt = 0; - unsigned int ctrl, ecc; - - while (1) { - interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); - if (interrupt & flags) - break; - } - - ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); - - if (ctrl & ONENAND_CTRL_ERROR) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: controller error = 0x%04x\n", ctrl); - return -EAGAIN; - } - - if (ctrl & ONENAND_CTRL_LOCK) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: it's locked error = 0x%04x\n", ctrl); - return -EIO; - } - - if (interrupt & ONENAND_INT_READ) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_wait: ECC error = 0x%04x\n", ecc); - return -EBADMSG; - } - } - - return 0; -} - -/** - * onenand_bufferram_offset - [DEFAULT] BufferRAM offset - * @param mtd MTD data structure - * @param area BufferRAM area - * @return offset given area - * - * Return BufferRAM offset given area - */ -static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) -{ - struct onenand_chip *this = mtd->priv; - - if (ONENAND_CURRENT_BUFFERRAM(this)) { - if (area == ONENAND_DATARAM) - return mtd->oobblock; - if (area == ONENAND_SPARERAM) - return mtd->oobsize; - } - - return 0; -} - -/** - * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Read the BufferRAM area - */ -static int onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - bufferram += onenand_bufferram_offset(mtd, area); - - memcpy(buffer, bufferram + offset, count); - - return 0; -} - -/** - * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Read the BufferRAM area with Sync. Burst Mode - */ -static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - bufferram += onenand_bufferram_offset(mtd, area); - - this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); - - memcpy(buffer, bufferram + offset, count); - - this->mmcontrol(mtd, 0); - - return 0; -} - -/** - * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area - * @param mtd MTD data structure - * @param area BufferRAM area - * @param buffer the databuffer to put/get data - * @param offset offset to read from or write to - * @param count number of bytes to read/write - * - * Write the BufferRAM area - */ -static int onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, int offset, - size_t count) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *bufferram; - - bufferram = this->base + area; - bufferram += onenand_bufferram_offset(mtd, area); - - memcpy(bufferram + offset, buffer, count); - - return 0; -} - -/** - * onenand_check_bufferram - [GENERIC] Check BufferRAM information - * @param mtd MTD data structure - * @param addr address to check - * @return 1 if there are valid data, otherwise 0 - * - * Check bufferram if there is data we required - */ -static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) -{ - struct onenand_chip *this = mtd->priv; - int block, page; - int i; - - block = (int)(addr >> this->erase_shift); - page = (int)(addr >> this->page_shift); - page &= this->page_mask; - - i = ONENAND_CURRENT_BUFFERRAM(this); - - /* Is there valid data? */ - if (this->bufferram[i].block == block && - this->bufferram[i].page == page && this->bufferram[i].valid) - return 1; - - return 0; -} - -/** - * onenand_update_bufferram - [GENERIC] Update BufferRAM information - * @param mtd MTD data structure - * @param addr address to update - * @param valid valid flag - * - * Update BufferRAM information - */ -static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, - int valid) -{ - struct onenand_chip *this = mtd->priv; - int block, page; - int i; - - block = (int)(addr >> this->erase_shift); - page = (int)(addr >> this->page_shift); - page &= this->page_mask; - - /* Invalidate BufferRAM */ - for (i = 0; i < MAX_BUFFERRAM; i++) { - if (this->bufferram[i].block == block && - this->bufferram[i].page == page) - this->bufferram[i].valid = 0; - } - - /* Update BufferRAM */ - i = ONENAND_CURRENT_BUFFERRAM(this); - this->bufferram[i].block = block; - this->bufferram[i].page = page; - this->bufferram[i].valid = valid; - - return 0; -} - -/** - * onenand_get_device - [GENERIC] Get chip for selected access - * @param mtd MTD device structure - * @param new_state the state which is requested - * - * Get the device and lock it for exclusive access - */ -static void onenand_get_device(struct mtd_info *mtd, int new_state) -{ - /* Do nothing */ -} - -/** - * onenand_release_device - [GENERIC] release chip - * @param mtd MTD device structure - * - * Deselect, release chip lock and wake up anyone waiting on the device - */ -static void onenand_release_device(struct mtd_info *mtd) -{ - /* Do nothing */ -} - -/** - * onenand_read_ecc - [MTD Interface] Read data with ECC - * @param mtd MTD device structure - * @param from offset to read from - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put data - * @param oob_buf filesystem supplied oob data buffer - * @param oobsel oob selection structure - * - * OneNAND read with ECC - */ -static int onenand_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) -{ - struct onenand_chip *this = mtd->priv; - int read = 0, column; - int thislen; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_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, - "onenand_read_ecc: Attempt read beyond end of device\n"); - *retlen = 0; - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_READING); - - while (read < len) { - thislen = min_t(int, mtd->oobblock, len - read); - - column = from & (mtd->oobblock - 1); - if (column + thislen > mtd->oobblock) - thislen = mtd->oobblock - column; - - if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, - mtd->oobblock); - ret = this->wait(mtd, FL_READING); - /* First copy data and check return value for ECC handling */ - onenand_update_bufferram(mtd, from, 1); - } - - this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, - thislen); - - read += thislen; - if (read == len) - break; - - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_ecc: read failed = %d\n", ret); - break; - } - - from += thislen; - buf += thislen; - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_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 ret; -} - -/** - * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc - * @param mtd MTD device structure - * @param from offset to read from - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put data - * - * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL -*/ -int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf) -{ - return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); -} - -/** - * onenand_read_oob - [MTD Interface] OneNAND read out-of-band - * @param mtd MTD device structure - * @param from offset to read from - * @param len number of bytes to read - * @param retlen pointer to variable to store the number of read bytes - * @param buf the databuffer to put data - * - * OneNAND read out-of-band data from the spare area - */ -int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf) -{ - struct onenand_chip *this = mtd->priv; - int read = 0, thislen, column; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", - (unsigned int)from, (int)len); - - /* Initialize return length value */ - *retlen = 0; - - /* Do not allow reads past end of device */ - if (unlikely((from + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_oob: Attempt read beyond end of device\n"); - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_READING); - - column = from & (mtd->oobsize - 1); - - while (read < len) { - thislen = mtd->oobsize - column; - thislen = min_t(int, thislen, len); - - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); - - onenand_update_bufferram(mtd, from, 0); - - ret = this->wait(mtd, FL_READING); - /* First copy data and check return value for ECC handling */ - - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, - thislen); - - read += thislen; - if (read == len) - break; - - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_read_oob: read failed = %d\n", ret); - break; - } - - buf += thislen; - /* Read more? */ - if (read < len) { - /* Page size */ - from += mtd->oobblock; - column = 0; - } - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - - *retlen = read; - return ret; -} - -#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE -/** - * onenand_verify_page - [GENERIC] verify the chip contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify - * @param block block address - * @param page page address - * - * Check DataRAM area directly - */ -static int onenand_verify_page(struct mtd_info *mtd, u_char * buf, - loff_t addr, int block, int page) -{ - struct onenand_chip *this = mtd->priv; - void __iomem *dataram0, *dataram1; - int ret = 0; - - this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); - - ret = this->wait(mtd, FL_READING); - if (ret) - return ret; - - onenand_update_bufferram(mtd, addr, 1); - - /* Check, if the two dataram areas are same */ - dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->oobblock; - - if (memcmp(dataram0, dataram1, mtd->oobblock)) - return -EBADMSG; - - return 0; -} -#else -#define onenand_verify_page(...) (0) -#endif - -#define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0) - -/** - * onenand_write_ecc - [MTD Interface] OneNAND write with ECC - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * @param eccbuf filesystem supplied oob data buffer - * @param oobsel oob selection structure - * - * OneNAND write with ECC - */ -static int onenand_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) -{ - struct onenand_chip *this = mtd->priv; - int written = 0; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_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 writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: Attempt write to past end of device\n"); - return -EINVAL; - } - - /* Reject writes, which are not page aligned */ - if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: Attempt to write not page aligned data\n"); - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_WRITING); - - /* Loop until all data write */ - while (written < len) { - int thislen = min_t(int, mtd->oobblock, len - written); - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); - - this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); - this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, - mtd->oobsize); - - this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); - - onenand_update_bufferram(mtd, to, 1); - - ret = this->wait(mtd, FL_WRITING); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: write filaed %d\n", ret); - break; - } - - written += thislen; - - /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); - if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_ecc: verify failed %d\n", ret); - break; - } - - if (written == len) - break; - - to += thislen; - buf += thislen; - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - - *retlen = written; - - return ret; -} - -/** - * onenand_write - [MTD Interface] compability function for onenand_write_ecc - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * - * This function simply calls onenand_write_ecc - * with oob buffer and oobsel = NULL - */ -int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf) -{ - return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL); -} - -/** - * onenand_write_oob - [MTD Interface] OneNAND write out-of-band - * @param mtd MTD device structure - * @param to offset to write to - * @param len number of bytes to write - * @param retlen pointer to variable to store the number of written bytes - * @param buf the data to write - * - * OneNAND write out-of-band - */ -int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t * retlen, const u_char * buf) -{ - struct onenand_chip *this = mtd->priv; - int column, status; - int written = 0; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)len); - - /* Initialize retlen, in case of early exit */ - *retlen = 0; - - /* Do not allow writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_write_oob: Attempt write to past end of device\n"); - return -EINVAL; - } - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_WRITING); - - /* Loop until all data write */ - while (written < len) { - int thislen = min_t(int, mtd->oobsize, len - written); - - column = to & (mtd->oobsize - 1); - - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); - - this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, - mtd->oobsize); - this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, - thislen); - - this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); - - onenand_update_bufferram(mtd, to, 0); - - status = this->wait(mtd, FL_WRITING); - if (status) - break; - - written += thislen; - if (written == len) - break; - - to += thislen; - buf += thislen; - } - - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - - *retlen = written; - - return 0; -} - -/** - * onenand_erase - [MTD Interface] erase block(s) - * @param mtd MTD device structure - * @param instr erase instruction - * - * Erase one ore more blocks - */ -int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct onenand_chip *this = mtd->priv; - unsigned int block_size; - loff_t addr; - int len; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); - - block_size = (1 << this->erase_shift); - - /* Start address must align on block boundary */ - if (unlikely(instr->addr & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); - return -EINVAL; - } - - /* Length must align on block boundary */ - if (unlikely(instr->len & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Length not block aligned\n"); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if (unlikely((instr->len + instr->addr) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Erase past end of device\n"); - return -EINVAL; - } - - instr->fail_addr = 0xffffffff; - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_ERASING); - - /* Loop throught the pages */ - len = instr->len; - addr = instr->addr; - - instr->state = MTD_ERASING; - - while (len) { - - /* TODO Check badblock */ - - this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); - - ret = this->wait(mtd, FL_ERASING); - /* Check, if it is write protected */ - if (ret) { - if (ret == -EPERM) - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Device is write protected!!!\n"); - else - DEBUG(MTD_DEBUG_LEVEL0, - "onenand_erase: Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = addr; - goto erase_exit; - } - - len -= block_size; - addr += block_size; - } - - 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 */ - onenand_release_device(mtd); - - return ret; -} - -/** - * onenand_sync - [MTD Interface] sync - * @param mtd MTD device structure - * - * Sync is actually a wait for chip ready function - */ -void onenand_sync(struct mtd_info *mtd) -{ - DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_SYNCING); - - /* Release it and go back */ - onenand_release_device(mtd); -} - -/** - * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - */ -int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) -{ - /* - * TODO - * 1. Bad block table (BBT) - * -> using NAND BBT to support JFFS2 - * 2. Bad block management (BBM) - * -> bad block replace scheme - * - * Currently we do nothing - */ - return 0; -} - -/** - * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - */ -int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - /* see above */ - return 0; -} - -/** - * onenand_unlock - [MTD Interface] Unlock block(s) - * @param mtd MTD device structure - * @param ofs offset relative to mtd start - * @param len number of bytes to unlock - * - * Unlock one or more blocks - */ -int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - struct onenand_chip *this = mtd->priv; - int start, end, block, value, status; - - start = ofs >> this->erase_shift; - end = len >> this->erase_shift; - - /* Continuous lock scheme */ - if (this->options & ONENAND_CONT_LOCK) { - /* Set start block address */ - this->write_word(start, - this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Set end block address */ - this->write_word(end - 1, - this->base + ONENAND_REG_END_BLOCK_ADDRESS); - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); - - /* There's no return value */ - this->wait(mtd, FL_UNLOCKING); - - /* Sanity check */ - while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) - & ONENAND_CTRL_ONGO) - continue; - - /* Check lock status */ - status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) - printk(KERN_ERR "wp status = 0x%x\n", status); - - return 0; - } - - /* Block lock scheme */ - for (block = start; block < end; block++) { - /* Set start block address */ - this->write_word(block, - this->base + ONENAND_REG_START_BLOCK_ADDRESS); - /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); - - /* There's no return value */ - this->wait(mtd, FL_UNLOCKING); - - /* Sanity check */ - while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) - & ONENAND_CTRL_ONGO) - continue; - - /* Set block address for read block status */ - value = onenand_block_address(this->device_id, block); - this->write_word(value, - this->base + ONENAND_REG_START_ADDRESS1); - - /* Check lock status */ - status = this->read_word(this->base + ONENAND_REG_WP_STATUS); - if (!(status & ONENAND_WP_US)) - printk(KERN_ERR "block = %d, wp status = 0x%x\n", - block, status); - } - - return 0; -} - -/** - * onenand_print_device_info - Print device ID - * @param device device ID - * - * Print device ID - */ -void onenand_print_device_info(int device, int verbose) -{ - int vcc, demuxed, ddp, density; - - if (!verbose) - return; - - vcc = device & ONENAND_DEVICE_VCC_MASK; - demuxed = device & ONENAND_DEVICE_IS_DEMUX; - ddp = device & ONENAND_DEVICE_IS_DDP; - density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", - demuxed ? "" : "Muxed ", - ddp ? "(DDP)" : "", - (16 << density), vcc ? "2.65/3.3" : "1.8", device); -} - -static const struct onenand_manufacturers onenand_manuf_ids[] = { - {ONENAND_MFR_SAMSUNG, "Samsung"}, - {ONENAND_MFR_UNKNOWN, "Unknown"} -}; - -/** - * onenand_check_maf - Check manufacturer ID - * @param manuf manufacturer ID - * - * Check manufacturer ID - */ -static int onenand_check_maf(int manuf) -{ - int i; - - for (i = 0; onenand_manuf_ids[i].id; i++) { - if (manuf == onenand_manuf_ids[i].id) - break; - } - -#ifdef ONENAND_DEBUG - printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", - onenand_manuf_ids[i].name, manuf); -#endif - - return (i != ONENAND_MFR_UNKNOWN); -} - -/** - * onenand_probe - [OneNAND Interface] Probe the OneNAND device - * @param mtd MTD device structure - * - * OneNAND detection method: - * Compare the the values from command with ones from register - */ -static int onenand_probe(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - int bram_maf_id, bram_dev_id, maf_id, dev_id; - int version_id; - int density; - - /* Send the command for reading device ID from BootRAM */ - this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); - - /* Read manufacturer and device IDs from BootRAM */ - bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); - bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); - - /* Check manufacturer ID */ - if (onenand_check_maf(bram_maf_id)) - return -ENXIO; - - /* Reset OneNAND to read default register values */ - this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); - - { - int i; - for (i = 0; i < 10000; i++) ; - } - - /* Read manufacturer and device IDs from Register */ - maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); - dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); - - /* Check OneNAND device */ - if (maf_id != bram_maf_id || dev_id != bram_dev_id) - return -ENXIO; - - /* Flash device information */ - onenand_print_device_info(dev_id, 0); - this->device_id = dev_id; - - density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; - this->chipsize = (16 << density) << 20; - - /* OneNAND page size & block size */ - /* The data buffer size is equal to page size */ - mtd->oobblock = - this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); - mtd->oobsize = mtd->oobblock >> 5; - /* Pagers per block is always 64 in OneNAND */ - mtd->erasesize = mtd->oobblock << 6; - - this->erase_shift = ffs(mtd->erasesize) - 1; - this->page_shift = ffs(mtd->oobblock) - 1; - this->ppb_shift = (this->erase_shift - this->page_shift); - this->page_mask = (mtd->erasesize / mtd->oobblock) - 1; - - /* REVIST: Multichip handling */ - - mtd->size = this->chipsize; - - /* Version ID */ - version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); -#ifdef ONENAND_DEBUG - printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); -#endif - - /* Lock scheme */ - if (density <= ONENAND_DEVICE_DENSITY_512Mb && - !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { - printk(KERN_INFO "Lock scheme is Continues Lock\n"); - this->options |= ONENAND_CONT_LOCK; - } - - return 0; -} - -/** - * onenand_scan - [OneNAND Interface] Scan for the OneNAND device - * @param mtd MTD device structure - * @param 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. - */ -int onenand_scan(struct mtd_info *mtd, int maxchips) -{ - struct onenand_chip *this = mtd->priv; - - if (!this->read_word) - this->read_word = onenand_readw; - if (!this->write_word) - this->write_word = onenand_writew; - - if (!this->command) - this->command = onenand_command; - if (!this->wait) - this->wait = onenand_wait; - - if (!this->read_bufferram) - this->read_bufferram = onenand_read_bufferram; - if (!this->write_bufferram) - this->write_bufferram = onenand_write_bufferram; - - if (onenand_probe(mtd)) - return -ENXIO; - - /* Set Sync. Burst Read after probing */ - if (this->mmcontrol) { - printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); - this->read_bufferram = onenand_sync_read_bufferram; - } - - onenand_unlock(mtd, 0, mtd->size); - - return onenand_default_bbt(mtd); -} - -/** - * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device - * @param mtd MTD device structure - */ -void onenand_release(struct mtd_info *mtd) -{ -} - -/* - * OneNAND initialization at U-Boot - */ -struct mtd_info onenand_mtd; -struct onenand_chip onenand_chip; - -void onenand_init(void) -{ - memset(&onenand_mtd, 0, sizeof(struct mtd_info)); - memset(&onenand_chip, 0, sizeof(struct onenand_chip)); - - onenand_chip.base = (void *)CFG_ONENAND_BASE; - onenand_mtd.priv = &onenand_chip; - - onenand_scan(&onenand_mtd, 1); - - puts("OneNAND: "); - print_size(onenand_mtd.size, "\n"); -} - -#endif /* CONFIG_CMD_ONENAND */ diff --git a/drivers/onenand/onenand_bbt.c b/drivers/onenand/onenand_bbt.c deleted file mode 100644 index 5a610ee5ea..0000000000 --- a/drivers/onenand/onenand_bbt.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * linux/drivers/mtd/onenand/onenand_bbt.c - * - * Bad Block Table support for the OneNAND driver - * - * Copyright(c) 2005-2007 Samsung Electronics - * Kyungmin Park - * - * TODO: - * Split BBT core and chip specific BBT. - * - * 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 - -#ifdef CONFIG_CMD_ONENAND - -#include -#include -#include -#include - -#include - -/** - * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @param buf the buffer to search - * @param len the length of buffer to search - * @param paglen the pagelength - * @param td search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block - * tables and good / bad block identifiers. Same as check_pattern, but - * no optional empty check and the pattern is expected to start - * at offset 0. - */ -static int check_short_pattern(uint8_t * buf, int len, int paglen, - struct nand_bbt_descr *td) -{ - int i; - uint8_t *p = buf; - - /* Compare the pattern */ - for (i = 0; i < td->len; i++) { - if (p[i] != td->pattern[i]) - return -1; - } - return 0; -} - -/** - * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @param mtd MTD device structure - * @param buf temporary buffer - * @param bd descriptor for the good/bad block search pattern - * @param 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 int create_bbt(struct mtd_info *mtd, uint8_t * buf, - struct nand_bbt_descr *bd, int chip) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int i, j, numblocks, len, scanlen; - int startblock; - loff_t from; - size_t readlen, ooblen; - - printk(KERN_INFO "Scanning device for bad blocks\n"); - - len = 1; - - /* We need only read few bytes from the OOB area */ - scanlen = ooblen = 0; - readlen = bd->len; - - /* chip == -1 case only */ - /* Note that numblocks is 2 * (real numblocks) here; - * see i += 2 below as it makses shifting and masking less painful - */ - numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); - startblock = 0; - from = 0; - - for (i = startblock; i < numblocks;) { - int ret; - - for (j = 0; j < len; j++) { - size_t retlen; - - /* No need to read pages fully, - * just read required OOB bytes */ - ret = onenand_read_oob(mtd, - from + j * mtd->oobblock + - bd->offs, readlen, &retlen, - &buf[0]); - - if (ret && ret != -EAGAIN) { - printk("ret = %d\n", ret); - return ret; - } - - if (check_short_pattern - (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { - bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING - "Bad eraseblock %d at 0x%08x\n", i >> 1, - (unsigned int)from); - break; - } - } - i += 2; - from += (1 << bbm->bbt_erase_shift); - } - - return 0; -} - -/** - * onenand_memory_bbt - [GENERIC] create a memory based bad block table - * @param mtd MTD device structure - * @param 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 inline int onenand_memory_bbt(struct mtd_info *mtd, - struct nand_bbt_descr *bd) -{ - unsigned char data_buf[MAX_ONENAND_PAGESIZE]; - - bd->options &= ~NAND_BBT_SCANEMPTY; - return create_bbt(mtd, data_buf, bd, -1); -} - -/** - * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad - * @param mtd MTD device structure - * @param offs offset in the device - * @param allowbbt allow access to bad block table region - */ -static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int block; - uint8_t res; - - /* Get block number * 2 */ - block = (int)(offs >> (bbm->bbt_erase_shift - 1)); - res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; - - DEBUG(MTD_DEBUG_LEVEL2, - "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); - - switch ((int)res) { - case 0x00: - return 0; - case 0x01: - return 1; - case 0x02: - return allowbbt ? 0 : 1; - } - - return 1; -} - -/** - * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s) - * @param mtd MTD device structure - * @param 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 onenand_free_bbt function. - * - */ -int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; - int len, ret = 0; - - len = mtd->size >> (this->erase_shift + 2); - /* Allocate memory (2bit per block) */ - bbm->bbt = malloc(len); - if (!bbm->bbt) { - printk(KERN_ERR "onenand_scan_bbt: Out of memory\n"); - return -ENOMEM; - } - /* Clear the memory bad block table */ - memset(bbm->bbt, 0x00, len); - - /* Set the bad block position */ - bbm->badblockpos = ONENAND_BADBLOCK_POS; - - /* Set erase shift */ - bbm->bbt_erase_shift = this->erase_shift; - - if (!bbm->isbad_bbt) - bbm->isbad_bbt = onenand_isbad_bbt; - - /* Scan the device to build a memory based bad block table */ - if ((ret = onenand_memory_bbt(mtd, bd))) { - printk(KERN_ERR - "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); - free(bbm->bbt); - bbm->bbt = NULL; - } - - return ret; -} - -/* - * Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. - */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern, -}; - -/** - * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device - * @param mtd MTD device structure - * - * This function selects the default bad block table - * support for the device and calls the onenand_scan_bbt function - */ -int onenand_default_bbt(struct mtd_info *mtd) -{ - struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm; - - this->bbm = malloc(sizeof(struct bbm_info)); - if (!this->bbm) - return -ENOMEM; - - bbm = this->bbm; - - memset(bbm, 0, sizeof(struct bbm_info)); - - /* 1KB page has same configuration as 2KB page */ - if (!bbm->badblock_pattern) - bbm->badblock_pattern = &largepage_memorybased; - - return onenand_scan_bbt(mtd, bbm->badblock_pattern); -} - -#endif /* CFG_CMD_ONENAND */ diff --git a/nand_spl/board/amcc/acadia/Makefile b/nand_spl/board/amcc/acadia/Makefile index 926476f91b..2d4d6016eb 100644 --- a/nand_spl/board/amcc/acadia/Makefile +++ b/nand_spl/board/amcc/acadia/Makefile @@ -96,7 +96,7 @@ $(obj)nand_boot.c: # from drivers/nand directory $(obj)nand_ecc.c: @rm -f $(obj)nand_ecc.c - ln -s $(SRCTREE)/drivers/nand/nand_ecc.c $(obj)nand_ecc.c + ln -s $(SRCTREE)/drivers/mtd/nand/nand_ecc.c $(obj)nand_ecc.c ######################################################################### diff --git a/nand_spl/board/amcc/bamboo/Makefile b/nand_spl/board/amcc/bamboo/Makefile index 8b5461dcf4..3a633fb887 100644 --- a/nand_spl/board/amcc/bamboo/Makefile +++ b/nand_spl/board/amcc/bamboo/Makefile @@ -82,7 +82,7 @@ $(obj)nand_boot.c: # from drivers/nand directory $(obj)nand_ecc.c: @rm -f $(obj)nand_ecc.c - ln -s $(SRCTREE)/drivers/nand/nand_ecc.c $(obj)nand_ecc.c + ln -s $(SRCTREE)/drivers/mtd/nand/nand_ecc.c $(obj)nand_ecc.c ifneq ($(OBJTREE), $(SRCTREE)) $(obj)sdram.c: diff --git a/nand_spl/board/amcc/sequoia/Makefile b/nand_spl/board/amcc/sequoia/Makefile index ec1be5a768..78bf071f59 100644 --- a/nand_spl/board/amcc/sequoia/Makefile +++ b/nand_spl/board/amcc/sequoia/Makefile @@ -88,7 +88,7 @@ $(obj)nand_boot.c: # from drivers/nand directory $(obj)nand_ecc.c: @rm -f $(obj)nand_ecc.c - ln -s $(SRCTREE)/drivers/nand/nand_ecc.c $(obj)nand_ecc.c + ln -s $(SRCTREE)/drivers/mtd/nand/nand_ecc.c $(obj)nand_ecc.c ######################################################################### -- cgit v1.2.3