Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  onenand_base.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright © 2005-2009 Samsung Electronics
 *  Copyright © 2007 Nokia Corporation
 *
 *  Kyungmin Park <kyungmin.park@samsung.com>
 *
 *  Credits:
 * Adrian Hunter <ext-adrian.hunter@nokia.com>:
 * auto-placement support, read-while load support, various fixes
 *
 * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
 * Flex-OneNAND support
 * Amul Kumar Saha <amul.saha at samsung.com>
 * OTP support
 */


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>

#include <asm/io.h>

/*
 * Multiblock erase if number of blocks to erase is 2 or more.
 * Maximum number of blocks for simultaneous erase is 64.
 */

#define MB_ERASE_MIN_BLK_COUNT 2
#define MB_ERASE_MAX_BLK_COUNT 64

/* Default Flex-OneNAND boundary and lock respectively */
static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 };

module_param_array(flex_bdry, int, NULL, 0400);
MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND"
    "Syntax:flex_bdry=DIE_BDRY,LOCK,..."
    "DIE_BDRY: SLC boundary of the die"
    "LOCK: Locking information for SLC boundary"
    " : 0->Set boundary in unlocked status"
    " : 1->Set boundary in locked status");

/* Default OneNAND/Flex-OneNAND OTP options*/
static int otp;

module_param(otp, int, 0400);
MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP"
   "Syntax : otp=LOCK_TYPE"
   "LOCK_TYPE : Keys issued, for specific OTP Lock type"
   " : 0 -> Default (No Blocks Locked)"
   " : 1 -> OTP Block lock"
   " : 2 -> 1st Block lock"
   " : 3 -> BOTH OTP Block and 1st Block lock");

/*
 * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
 * For now, we expose only 64 out of 80 ecc bytes
 */

static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section,
         struct mtd_oob_region *oobregion)
{
 if (section > 7)
  return -ERANGE;

 oobregion->offset = (section * 16) + 6;
 oobregion->length = 10;

 return 0;
}

static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section,
          struct mtd_oob_region *oobregion)
{
 if (section > 7)
  return -ERANGE;

 oobregion->offset = (section * 16) + 2;
 oobregion->length = 4;

 return 0;
}

static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = {
 .ecc = flexonenand_ooblayout_ecc,
 .free = flexonenand_ooblayout_free,
};

/*
 * onenand_oob_128 - oob info for OneNAND with 4KB page
 *
 * Based on specification:
 * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
 *
 */

static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
         struct mtd_oob_region *oobregion)
{
 if (section > 7)
  return -ERANGE;

 oobregion->offset = (section * 16) + 7;
 oobregion->length = 9;

 return 0;
}

static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section,
          struct mtd_oob_region *oobregion)
{
 if (section >= 8)
  return -ERANGE;

 /*
 * free bytes are using the spare area fields marked as
 * "Managed by internal ECC logic for Logical Sector Number area"
 */

 oobregion->offset = (section * 16) + 2;
 oobregion->length = 3;

 return 0;
}

static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
 .ecc = onenand_ooblayout_128_ecc,
 .free = onenand_ooblayout_128_free,
};

/*
 * onenand_oob_32_64 - oob info for large (2KB) page
 */

static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
           struct mtd_oob_region *oobregion)
{
 if (section > 3)
  return -ERANGE;

 oobregion->offset = (section * 16) + 8;
 oobregion->length = 5;

 return 0;
}

static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section,
     struct mtd_oob_region *oobregion)
{
 int sections = (mtd->oobsize / 32) * 2;

 if (section >= sections)
  return -ERANGE;

 if (section & 1) {
  oobregion->offset = ((section - 1) * 16) + 14;
  oobregion->length = 2;
 } else  {
  oobregion->offset = (section * 16) + 2;
  oobregion->length = 3;
 }

 return 0;
}

static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = {
 .ecc = onenand_ooblayout_32_64_ecc,
 .free = onenand_ooblayout_32_64_free,
};

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 */
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
};

/**
 * onenand_readw - [OneNAND Interface] Read OneNAND register
 * @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
 * @value: value to write
 * @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
 * @this: onenand chip data structure
 * @block: the block
 * @return translated block address if DDP, otherwise same
 *
 * Setup Start Address 1 Register (F100h)
 */

static int onenand_block_address(struct onenand_chip *thisint block)
{
 /* Device Flash Core select, NAND Flash Block Address */
 if (block & this->density_mask)
  return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);

 return block;
}

/**
 * onenand_bufferram_address - [DEFAULT] Get bufferram address
 * @this: onenand chip data structure
 * @block: the block
 * @return set DBS value if DDP, otherwise 0
 *
 * Setup Start Address 2 Register (F101h) for DDP
 */

static int onenand_bufferram_address(struct onenand_chip *thisint block)
{
 /* Device BufferRAM Select */
 if (block & this->density_mask)
  return ONENAND_DDP_CHIP1;

 return ONENAND_DDP_CHIP0;
}

/**
 * onenand_page_address - [DEFAULT] Get page address
 * @page: the page address
 * @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
 * @dataram1: DataRAM index
 * @sectors: the sector address
 * @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);
}

/**
 * flexonenand_block- For given address return block number
 * @this:         - OneNAND device structure
 * @addr: - Address for which block number is needed
 */

static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr)
{
 unsigned boundary, blk, die = 0;

 if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
  die = 1;
  addr -= this->diesize[0];
 }

 boundary = this->boundary[die];

 blk = addr >> (this->erase_shift - 1);
 if (blk > boundary)
  blk = (blk + boundary + 1) >> 1;

 blk += die ? this->density_mask : 0;
 return blk;
}

inline unsigned onenand_block(struct onenand_chip *this, loff_t addr)
{
 if (!FLEXONENAND(this))
  return addr >> this->erase_shift;
 return flexonenand_block(this, addr);
}

/**
 * flexonenand_addr - Return address of the block
 * @this: OneNAND device structure
 * @block: Block number on Flex-OneNAND
 *
 * Return address of the block
 */

static loff_t flexonenand_addr(struct onenand_chip *thisint block)
{
 loff_t ofs = 0;
 int die = 0, boundary;

 if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
  block -= this->density_mask;
  die = 1;
  ofs = this->diesize[0];
 }

 boundary = this->boundary[die];
 ofs += (loff_t)block << (this->erase_shift - 1);
 if (block > (boundary + 1))
  ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1);
 return ofs;
}

loff_t onenand_addr(struct onenand_chip *thisint block)
{
 if (!FLEXONENAND(this))
  return (loff_t)block << this->erase_shift;
 return flexonenand_addr(this, block);
}
EXPORT_SYMBOL(onenand_addr);

/**
 * onenand_get_density - [DEFAULT] Get OneNAND density
 * @dev_id: OneNAND device ID
 *
 * Get OneNAND density from device ID
 */

static inline int onenand_get_density(int dev_id)
{
 int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
 return (density & ONENAND_DEVICE_DENSITY_MASK);
}

/**
 * flexonenand_region - [Flex-OneNAND] Return erase region of addr
 * @mtd: MTD device structure
 * @addr: address whose erase region needs to be identified
 */

int flexonenand_region(struct mtd_info *mtd, loff_t addr)
{
 int i;

 for (i = 0; i < mtd->numeraseregions; i++)
  if (addr < mtd->eraseregions[i].offset)
   break;
 return i - 1;
}
EXPORT_SYMBOL(flexonenand_region);

/**
 * onenand_command - [DEFAULT] Send command to OneNAND device
 * @mtd: MTD device structure
 * @cmd: the command to be sent
 * @addr: offset to read from or write to
 * @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, block, page;

 /* Address translation */
 switch (cmd) {
 case ONENAND_CMD_UNLOCK:
 case ONENAND_CMD_LOCK:
 case ONENAND_CMD_LOCK_TIGHT:
 case ONENAND_CMD_UNLOCK_ALL:
  block = -1;
  page = -1;
  break;

 case FLEXONENAND_CMD_PI_ACCESS:
  /* addr contains die index */
  block = addr * this->density_mask;
  page = -1;
  break;

 case ONENAND_CMD_ERASE:
 case ONENAND_CMD_MULTIBLOCK_ERASE:
 case ONENAND_CMD_ERASE_VERIFY:
 case ONENAND_CMD_BUFFERRAM:
 case ONENAND_CMD_OTP_ACCESS:
  block = onenand_block(this, addr);
  page = -1;
  break;

 case FLEXONENAND_CMD_READ_PI:
  cmd = ONENAND_CMD_READ;
  block = addr * this->density_mask;
  page = 0;
  break;

 default:
  block = onenand_block(this, addr);
  if (FLEXONENAND(this))
   page = (int) (addr - onenand_addr(this, block))>>\
    this->page_shift;
  else
   page = (int) (addr >> this->page_shift);
  if (ONENAND_IS_2PLANE(this)) {
   /* Make the even block number */
   block &= ~1;
   /* Is it the odd plane? */
   if (addr & this->writesize)
    block++;
   page >>= 1;
  }
  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, block);
  this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);

  if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this))
   /* It is always BufferRAM0 */
   ONENAND_SET_BUFFERRAM0(this);
  else
   /* 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, block);
  this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);

  /* Select DataRAM for DDP */
  value = onenand_bufferram_address(this, block);
  this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 }

 if (page != -1) {
  /* Now we use page size operation */
  int sectors = 0, count = 0;
  int dataram;

  switch (cmd) {
  case FLEXONENAND_CMD_RECOVER_LSB:
  case ONENAND_CMD_READ:
  case ONENAND_CMD_READOOB:
   if (ONENAND_IS_4KB_PAGE(this))
    /* It is always BufferRAM0 */
    dataram = ONENAND_SET_BUFFERRAM0(this);
   else
    dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
   break;

  default:
   if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
    cmd = ONENAND_CMD_2X_PROG;
   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);
 }

 /* 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_read_ecc - return ecc status
 * @this: onenand chip structure
 */

static inline int onenand_read_ecc(struct onenand_chip *this)
{
 int ecc, i, result = 0;

 if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this))
  return this->read_word(this->base + ONENAND_REG_ECC_STATUS);

 for (i = 0; i < 4; i++) {
  ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2);
  if (likely(!ecc))
   continue;
  if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
   return ONENAND_ECC_2BIT_ALL;
  else
   result = ONENAND_ECC_1BIT_ALL;
 }

 return result;
}

/**
 * onenand_wait - [DEFAULT] wait until the command is done
 * @mtd: MTD device structure
 * @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 long timeout;
 unsigned int flags = ONENAND_INT_MASTER;
 unsigned int interrupt = 0;
 unsigned int ctrl;

 /* The 20 msec is enough */
 timeout = jiffies + msecs_to_jiffies(20);
 while (time_before(jiffies, timeout)) {
  interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);

  if (interrupt & flags)
   break;

  if (state != FL_READING && state != FL_PREPARING_ERASE)
   cond_resched();
 }
 /* To get correct interrupt status in timeout case */
 interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);

 ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);

 /*
 * In the Spec. it checks the controller status first
 * However if you get the correct information in case of
 * power off recovery (POR) test, it should read ECC status first
 */

 if (interrupt & ONENAND_INT_READ) {
  int ecc = onenand_read_ecc(this);
  if (ecc) {
   if (ecc & ONENAND_ECC_2BIT_ALL) {
    printk(KERN_ERR "%s: ECC error = 0x%04x\n",
     __func__, ecc);
    mtd->ecc_stats.failed++;
    return -EBADMSG;
   } else if (ecc & ONENAND_ECC_1BIT_ALL) {
    printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n",
     __func__, ecc);
    mtd->ecc_stats.corrected++;
   }
  }
 } else if (state == FL_READING) {
  printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n",
   __func__, ctrl, interrupt);
  return -EIO;
 }

 if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) {
  printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n",
         __func__, ctrl, interrupt);
  return -EIO;
 }

 if (!(interrupt & ONENAND_INT_MASTER)) {
  printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n",
         __func__, ctrl, interrupt);
  return -EIO;
 }

 /* If there's controller error, it's a real error */
 if (ctrl & ONENAND_CTRL_ERROR) {
  printk(KERN_ERR "%s: controller error = 0x%04x\n",
   __func__, ctrl);
  if (ctrl & ONENAND_CTRL_LOCK)
   printk(KERN_ERR "%s: it's locked error.\n", __func__);
  return -EIO;
 }

 return 0;
}

/*
 * onenand_interrupt - [DEFAULT] onenand interrupt handler
 * @irq: onenand interrupt number
 * @dev_id: interrupt data
 *
 * complete the work
 */

static irqreturn_t onenand_interrupt(int irq, void *data)
{
 struct onenand_chip *this = data;

 /* To handle shared interrupt */
 if (!this->complete.done)
  complete(&this->complete);

 return IRQ_HANDLED;
}

/*
 * onenand_interrupt_wait - [DEFAULT] wait until the command is done
 * @mtd: MTD device structure
 * @state: state to select the max. timeout value
 *
 * Wait for command done.
 */

static int onenand_interrupt_wait(struct mtd_info *mtd, int state)
{
 struct onenand_chip *this = mtd->priv;

 wait_for_completion(&this->complete);

 return onenand_wait(mtd, state);
}

/*
 * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait
 * @mtd: MTD device structure
 * @state: state to select the max. timeout value
 *
 * Try interrupt based wait (It is used one-time)
 */

static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state)
{
 struct onenand_chip *this = mtd->priv;
 unsigned long remain, timeout;

 /* We use interrupt wait first */
 this->wait = onenand_interrupt_wait;

 timeout = msecs_to_jiffies(100);
 remain = wait_for_completion_timeout(&this->complete, timeout);
 if (!remain) {
  printk(KERN_INFO "OneNAND: There's no interrupt. "
    "We use the normal wait\n");

  /* Release the irq */
  free_irq(this->irq, this);

  this->wait = onenand_wait;
 }

 return onenand_wait(mtd, state);
}

/*
 * onenand_setup_wait - [OneNAND Interface] setup onenand wait method
 * @mtd: MTD device structure
 *
 * There's two method to wait onenand work
 * 1. polling - read interrupt status register
 * 2. interrupt - use the kernel interrupt method
 */

static void onenand_setup_wait(struct mtd_info *mtd)
{
 struct onenand_chip *this = mtd->priv;
 int syscfg;

 init_completion(&this->complete);

 if (this->irq <= 0) {
  this->wait = onenand_wait;
  return;
 }

 if (request_irq(this->irq, &onenand_interrupt,
    IRQF_SHARED, "onenand"this)) {
  /* If we can't get irq, use the normal wait */
  this->wait = onenand_wait;
  return;
 }

 /* Enable interrupt */
 syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
 syscfg |= ONENAND_SYS_CFG1_IOBE;
 this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);

 this->wait = onenand_try_interrupt_wait;
}

/**
 * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
 * @mtd: MTD data structure
 * @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)) {
  /* Note: the 'this->writesize' is a real page size */
  if (area == ONENAND_DATARAM)
   return this->writesize;
  if (area == ONENAND_SPARERAM)
   return mtd->oobsize;
 }

 return 0;
}

/**
 * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
 * @mtd: MTD data structure
 * @area: BufferRAM area
 * @buffer: the databuffer to put/get data
 * @offset: offset to read from or write to
 * @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);

 if (ONENAND_CHECK_BYTE_ACCESS(count)) {
  unsigned short word;

  /* Align with word(16-bit) size */
  count--;

  /* Read word and save byte */
  word = this->read_word(bufferram + offset + count);
  buffer[count] = (word & 0xff);
 }

 memcpy(buffer, bufferram + offset, count);

 return 0;
}

/**
 * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
 * @mtd: MTD data structure
 * @area: BufferRAM area
 * @buffer: the databuffer to put/get data
 * @offset: offset to read from or write to
 * @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);

 if (ONENAND_CHECK_BYTE_ACCESS(count)) {
  unsigned short word;

  /* Align with word(16-bit) size */
  count--;

  /* Read word and save byte */
  word = this->read_word(bufferram + offset + count);
  buffer[count] = (word & 0xff);
 }

 memcpy(buffer, bufferram + offset, count);

 this->mmcontrol(mtd, 0);

 return 0;
}

/**
 * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
 * @mtd: MTD data structure
 * @area: BufferRAM area
 * @buffer: the databuffer to put/get data
 * @offset: offset to read from or write to
 * @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);

 if (ONENAND_CHECK_BYTE_ACCESS(count)) {
  unsigned short word;
  int byte_offset;

  /* Align with word(16-bit) size */
  count--;

  /* Calculate byte access offset */
  byte_offset = offset + count;

  /* Read word and save byte */
  word = this->read_word(bufferram + byte_offset);
  word = (word & ~0xff) | buffer[count];
  this->write_word(word, bufferram + byte_offset);
 }

 memcpy(bufferram + offset, buffer, count);

 return 0;
}

/**
 * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
 * @mtd: MTD data structure
 * @addr: address to check
 * @return blockpage address
 *
 * Get blockpage address at 2x program mode
 */

static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
{
 struct onenand_chip *this = mtd->priv;
 int blockpage, block, page;

 /* Calculate the even block number */
 block = (int) (addr >> this->erase_shift) & ~1;
 /* Is it the odd plane? */
 if (addr & this->writesize)
  block++;
 page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
 blockpage = (block << 7) | page;

 return blockpage;
}

/**
 * onenand_check_bufferram - [GENERIC] Check BufferRAM information
 * @mtd: MTD data structure
 * @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 blockpage, found = 0;
 unsigned int i;

 if (ONENAND_IS_2PLANE(this))
  blockpage = onenand_get_2x_blockpage(mtd, addr);
 else
  blockpage = (int) (addr >> this->page_shift);

 /* Is there valid data? */
 i = ONENAND_CURRENT_BUFFERRAM(this);
 if (this->bufferram[i].blockpage == blockpage)
  found = 1;
 else {
  /* Check another BufferRAM */
  i = ONENAND_NEXT_BUFFERRAM(this);
  if (this->bufferram[i].blockpage == blockpage) {
   ONENAND_SET_NEXT_BUFFERRAM(this);
   found = 1;
  }
 }

 if (found && ONENAND_IS_DDP(this)) {
  /* Select DataRAM for DDP */
  int block = onenand_block(this, addr);
  int value = onenand_bufferram_address(this, block);
  this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 }

 return found;
}

/**
 * onenand_update_bufferram - [GENERIC] Update BufferRAM information
 * @mtd: MTD data structure
 * @addr: address to update
 * @valid: valid flag
 *
 * Update BufferRAM information
 */

static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
  int valid)
{
 struct onenand_chip *this = mtd->priv;
 int blockpage;
 unsigned int i;

 if (ONENAND_IS_2PLANE(this))
  blockpage = onenand_get_2x_blockpage(mtd, addr);
 else
  blockpage = (int) (addr >> this->page_shift);

 /* Invalidate another BufferRAM */
 i = ONENAND_NEXT_BUFFERRAM(this);
 if (this->bufferram[i].blockpage == blockpage)
  this->bufferram[i].blockpage = -1;

 /* Update BufferRAM */
 i = ONENAND_CURRENT_BUFFERRAM(this);
 if (valid)
  this->bufferram[i].blockpage = blockpage;
 else
  this->bufferram[i].blockpage = -1;
}

/**
 * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
 * @mtd: MTD data structure
 * @addr: start address to invalidate
 * @len: length to invalidate
 *
 * Invalidate BufferRAM information
 */

static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
  unsigned int len)
{
 struct onenand_chip *this = mtd->priv;
 int i;
 loff_t end_addr = addr + len;

 /* Invalidate BufferRAM */
 for (i = 0; i < MAX_BUFFERRAM; i++) {
  loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
  if (buf_addr >= addr && buf_addr < end_addr)
   this->bufferram[i].blockpage = -1;
 }
}

/**
 * onenand_get_device - [GENERIC] Get chip for selected access
 * @mtd: MTD device structure
 * @new_state: the state which is requested
 *
 * Get the device and lock it for exclusive access
 */

static int onenand_get_device(struct mtd_info *mtd, int new_state)
{
 struct onenand_chip *this = mtd->priv;
 DECLARE_WAITQUEUE(wait, current);

 /*
 * Grab the lock and see if the device is available
 */

 while (1) {
  spin_lock(&this->chip_lock);
  if (this->state == FL_READY) {
   this->state = new_state;
   spin_unlock(&this->chip_lock);
   if (new_state != FL_PM_SUSPENDED && this->enable)
    this->enable(mtd);
   break;
  }
  if (new_state == FL_PM_SUSPENDED) {
   spin_unlock(&this->chip_lock);
   return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
  }
  set_current_state(TASK_UNINTERRUPTIBLE);
  add_wait_queue(&this->wq, &wait);
  spin_unlock(&this->chip_lock);
  schedule();
  remove_wait_queue(&this->wq, &wait);
 }

 return 0;
}

/**
 * onenand_release_device - [GENERIC] release chip
 * @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)
{
 struct onenand_chip *this = mtd->priv;

 if (this->state != FL_PM_SUSPENDED && this->disable)
  this->disable(mtd);
 /* Release the chip */
 spin_lock(&this->chip_lock);
 this->state = FL_READY;
 wake_up(&this->wq);
 spin_unlock(&this->chip_lock);
}

/**
 * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
 * @mtd: MTD device structure
 * @buf: destination address
 * @column: oob offset to read from
 * @thislen: oob length to read
 */

static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
    int thislen)
{
 struct onenand_chip *this = mtd->priv;

 this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0,
        mtd->oobsize);
 return mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf,
        column, thislen);
}

/**
 * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
 * @mtd: MTD device structure
 * @addr: address to recover
 * @status: return value from onenand_wait / onenand_bbt_wait
 *
 * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
 * lower page address and MSB page has higher page address in paired pages.
 * If power off occurs during MSB page program, the paired LSB page data can
 * become corrupt. LSB page recovery read is a way to read LSB page though page
 * data are corrupted. When uncorrectable error occurs as a result of LSB page
 * read after power up, issue LSB page recovery read.
 */

static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
{
 struct onenand_chip *this = mtd->priv;
 int i;

 /* Recovery is only for Flex-OneNAND */
 if (!FLEXONENAND(this))
  return status;

 /* check if we failed due to uncorrectable error */
 if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
  return status;

 /* check if address lies in MLC region */
 i = flexonenand_region(mtd, addr);
 if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
  return status;

 /* We are attempting to reread, so decrement stats.failed
 * which was incremented by onenand_wait due to read failure
 */

 printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n",
  __func__);
 mtd->ecc_stats.failed--;

 /* Issue the LSB page recovery command */
 this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
 return this->wait(mtd, FL_READING);
}

/**
 * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
 * @mtd: MTD device structure
 * @from: offset to read from
 * @ops: oob operation description structure
 *
 * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
 * So, read-while-load is not present.
 */

static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
    struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 struct mtd_ecc_stats stats;
 size_t len = ops->len;
 size_t ooblen = ops->ooblen;
 u_char *buf = ops->datbuf;
 u_char *oobbuf = ops->oobbuf;
 int read = 0, column, thislen;
 int oobread = 0, oobcolumn, thisooblen, oobsize;
 int ret = 0;
 int writesize = this->writesize;

 pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
   (int)len);

 oobsize = mtd_oobavail(mtd, ops);
 oobcolumn = from & (mtd->oobsize - 1);

 /* Do not allow reads past end of device */
 if (from + len > mtd->size) {
  printk(KERN_ERR "%s: Attempt read beyond end of device\n",
   __func__);
  ops->retlen = 0;
  ops->oobretlen = 0;
  return -EINVAL;
 }

 stats = mtd->ecc_stats;

 while (read < len) {
  cond_resched();

  thislen = min_t(int, writesize, len - read);

  column = from & (writesize - 1);
  if (column + thislen > writesize)
   thislen = writesize - column;

  if (!onenand_check_bufferram(mtd, from)) {
   this->command(mtd, ONENAND_CMD_READ, from, writesize);

   ret = this->wait(mtd, FL_READING);
   if (unlikely(ret))
    ret = onenand_recover_lsb(mtd, from, ret);
   onenand_update_bufferram(mtd, from, !ret);
   if (mtd_is_eccerr(ret))
    ret = 0;
   if (ret)
    break;
  }

  this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
  if (oobbuf) {
   thisooblen = oobsize - oobcolumn;
   thisooblen = min_t(int, thisooblen, ooblen - oobread);

   if (ops->mode == MTD_OPS_AUTO_OOB)
    onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
   else
    this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
   oobread += thisooblen;
   oobbuf += thisooblen;
   oobcolumn = 0;
  }

  read += thislen;
  if (read == len)
   break;

  from += thislen;
  buf += thislen;
 }

 /*
 * Return success, if no ECC failures, else -EBADMSG
 * fs driver will take care of that, because
 * retlen == desired len and result == -EBADMSG
 */

 ops->retlen = read;
 ops->oobretlen = oobread;

 if (ret)
  return ret;

 if (mtd->ecc_stats.failed - stats.failed)
  return -EBADMSG;

 /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
 return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
}

/**
 * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
 * @mtd: MTD device structure
 * @from: offset to read from
 * @ops: oob operation description structure
 *
 * OneNAND read main and/or out-of-band data
 */

static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
    struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 struct mtd_ecc_stats stats;
 size_t len = ops->len;
 size_t ooblen = ops->ooblen;
 u_char *buf = ops->datbuf;
 u_char *oobbuf = ops->oobbuf;
 int read = 0, column, thislen;
 int oobread = 0, oobcolumn, thisooblen, oobsize;
 int ret = 0, boundary = 0;
 int writesize = this->writesize;

 pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
   (int)len);

 oobsize = mtd_oobavail(mtd, ops);
 oobcolumn = from & (mtd->oobsize - 1);

 /* Do not allow reads past end of device */
 if ((from + len) > mtd->size) {
  printk(KERN_ERR "%s: Attempt read beyond end of device\n",
   __func__);
  ops->retlen = 0;
  ops->oobretlen = 0;
  return -EINVAL;
 }

 stats = mtd->ecc_stats;

 /* Read-while-load method */

 /* Do first load to bufferRAM */
 if (read < len) {
  if (!onenand_check_bufferram(mtd, from)) {
   this->command(mtd, ONENAND_CMD_READ, from, writesize);
   ret = this->wait(mtd, FL_READING);
   onenand_update_bufferram(mtd, from, !ret);
   if (mtd_is_eccerr(ret))
    ret = 0;
  }
 }

 thislen = min_t(int, writesize, len - read);
 column = from & (writesize - 1);
 if (column + thislen > writesize)
  thislen = writesize - column;

 while (!ret) {
  /* If there is more to load then start next load */
  from += thislen;
  if (read + thislen < len) {
   this->command(mtd, ONENAND_CMD_READ, from, writesize);
   /*
 * Chip boundary handling in DDP
 * Now we issued chip 1 read and pointed chip 1
 * bufferram so we have to point chip 0 bufferram.
 */

   if (ONENAND_IS_DDP(this) &&
       unlikely(from == (this->chipsize >> 1))) {
    this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
    boundary = 1;
   } else
    boundary = 0;
   ONENAND_SET_PREV_BUFFERRAM(this);
  }
  /* While load is going, read from last bufferRAM */
  this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);

  /* Read oob area if needed */
  if (oobbuf) {
   thisooblen = oobsize - oobcolumn;
   thisooblen = min_t(int, thisooblen, ooblen - oobread);

   if (ops->mode == MTD_OPS_AUTO_OOB)
    onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
   else
    this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
   oobread += thisooblen;
   oobbuf += thisooblen;
   oobcolumn = 0;
  }

  /* See if we are done */
  read += thislen;
  if (read == len)
   break;
  /* Set up for next read from bufferRAM */
  if (unlikely(boundary))
   this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
  ONENAND_SET_NEXT_BUFFERRAM(this);
  buf += thislen;
  thislen = min_t(int, writesize, len - read);
  column = 0;
  cond_resched();
  /* Now wait for load */
  ret = this->wait(mtd, FL_READING);
  onenand_update_bufferram(mtd, from, !ret);
  if (mtd_is_eccerr(ret))
   ret = 0;
 }

 /*
 * Return success, if no ECC failures, else -EBADMSG
 * fs driver will take care of that, because
 * retlen == desired len and result == -EBADMSG
 */

 ops->retlen = read;
 ops->oobretlen = oobread;

 if (ret)
  return ret;

 if (mtd->ecc_stats.failed - stats.failed)
  return -EBADMSG;

 /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
 return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
}

/**
 * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
 * @mtd: MTD device structure
 * @from: offset to read from
 * @ops: oob operation description structure
 *
 * OneNAND read out-of-band data from the spare area
 */

static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
   struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 struct mtd_ecc_stats stats;
 int read = 0, thislen, column, oobsize;
 size_t len = ops->ooblen;
 unsigned int mode = ops->mode;
 u_char *buf = ops->oobbuf;
 int ret = 0, readcmd;

 from += ops->ooboffs;

 pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
   (int)len);

 /* Initialize return length value */
 ops->oobretlen = 0;

 if (mode == MTD_OPS_AUTO_OOB)
  oobsize = mtd->oobavail;
 else
  oobsize = mtd->oobsize;

 column = from & (mtd->oobsize - 1);

 if (unlikely(column >= oobsize)) {
  printk(KERN_ERR "%s: Attempted to start read outside oob\n",
   __func__);
  return -EINVAL;
 }

 stats = mtd->ecc_stats;

 readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;

 while (read < len) {
  cond_resched();

  thislen = oobsize - column;
  thislen = min_t(int, thislen, len);

  this->command(mtd, readcmd, from, mtd->oobsize);

  onenand_update_bufferram(mtd, from, 0);

  ret = this->wait(mtd, FL_READING);
  if (unlikely(ret))
   ret = onenand_recover_lsb(mtd, from, ret);

  if (ret && !mtd_is_eccerr(ret)) {
   printk(KERN_ERR "%s: read failed = 0x%x\n",
    __func__, ret);
   break;
  }

  if (mode == MTD_OPS_AUTO_OOB)
   onenand_transfer_auto_oob(mtd, buf, column, thislen);
  else
   this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);

  read += thislen;

  if (read == len)
   break;

  buf += thislen;

  /* Read more? */
  if (read < len) {
   /* Page size */
   from += mtd->writesize;
   column = 0;
  }
 }

 ops->oobretlen = read;

 if (ret)
  return ret;

 if (mtd->ecc_stats.failed - stats.failed)
  return -EBADMSG;

 return 0;
}

/**
 * onenand_read_oob - [MTD Interface] Read main and/or out-of-band
 * @mtd: MTD device structure
 * @from: offset to read from
 * @ops: oob operation description structure
 *
 * Read main and/or out-of-band
 */

static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
       struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 struct mtd_ecc_stats old_stats;
 int ret;

 switch (ops->mode) {
 case MTD_OPS_PLACE_OOB:
 case MTD_OPS_AUTO_OOB:
  break;
 case MTD_OPS_RAW:
  /* Not implemented yet */
 default:
  return -EINVAL;
 }

 onenand_get_device(mtd, FL_READING);

 old_stats = mtd->ecc_stats;

 if (ops->datbuf)
  ret = ONENAND_IS_4KB_PAGE(this) ?
   onenand_mlc_read_ops_nolock(mtd, from, ops) :
   onenand_read_ops_nolock(mtd, from, ops);
 else
  ret = onenand_read_oob_nolock(mtd, from, ops);

 if (ops->stats) {
  ops->stats->uncorrectable_errors +=
   mtd->ecc_stats.failed - old_stats.failed;
  ops->stats->corrected_bitflips +=
   mtd->ecc_stats.corrected - old_stats.corrected;
 }

 onenand_release_device(mtd);

 return ret;
}

/**
 * onenand_bbt_wait - [DEFAULT] wait until the command is done
 * @mtd: MTD device structure
 * @state: state to select the max. timeout value
 *
 * Wait for command done.
 */

static int onenand_bbt_wait(struct mtd_info *mtd, int state)
{
 struct onenand_chip *this = mtd->priv;
 unsigned long timeout;
 unsigned int interrupt, ctrl, ecc, addr1, addr8;

 /* The 20 msec is enough */
 timeout = jiffies + msecs_to_jiffies(20);
 while (time_before(jiffies, timeout)) {
  interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
  if (interrupt & ONENAND_INT_MASTER)
   break;
 }
 /* To get correct interrupt status in timeout case */
 interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
 ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1);
 addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8);

 if (interrupt & ONENAND_INT_READ) {
  ecc = onenand_read_ecc(this);
  if (ecc & ONENAND_ECC_2BIT_ALL) {
   printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x "
          "intr 0x%04x addr1 %#x addr8 %#x\n",
          __func__, ecc, ctrl, interrupt, addr1, addr8);
   return ONENAND_BBT_READ_ECC_ERROR;
  }
 } else {
  printk(KERN_ERR "%s: read timeout! ctrl 0x%04x "
         "intr 0x%04x addr1 %#x addr8 %#x\n",
         __func__, ctrl, interrupt, addr1, addr8);
  return ONENAND_BBT_READ_FATAL_ERROR;
 }

 /* Initial bad block case: 0x2400 or 0x0400 */
 if (ctrl & ONENAND_CTRL_ERROR) {
  printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x "
         "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8);
  return ONENAND_BBT_READ_ERROR;
 }

 return 0;
}

/**
 * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
 * @mtd: MTD device structure
 * @from: offset to read from
 * @ops: oob operation description structure
 *
 * OneNAND read out-of-band data from the spare area for bbt scan
 */

int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, 
       struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 int read = 0, thislen, column;
 int ret = 0, readcmd;
 size_t len = ops->ooblen;
 u_char *buf = ops->oobbuf;

 pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from,
   len);

 /* Initialize return value */
 ops->oobretlen = 0;

 /* Do not allow reads past end of device */
 if (unlikely((from + len) > mtd->size)) {
  printk(KERN_ERR "%s: Attempt read beyond end of device\n",
   __func__);
  return ONENAND_BBT_READ_FATAL_ERROR;
 }

 /* Grab the lock and see if the device is available */
 onenand_get_device(mtd, FL_READING);

 column = from & (mtd->oobsize - 1);

 readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;

 while (read < len) {
  cond_resched();

  thislen = mtd->oobsize - column;
  thislen = min_t(int, thislen, len);

  this->command(mtd, readcmd, from, mtd->oobsize);

  onenand_update_bufferram(mtd, from, 0);

  ret = this->bbt_wait(mtd, FL_READING);
  if (unlikely(ret))
   ret = onenand_recover_lsb(mtd, from, ret);

  if (ret)
   break;

  this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
  read += thislen;
  if (read == len)
   break;

  buf += thislen;

  /* Read more? */
  if (read < len) {
   /* Update Page size */
   from += this->writesize;
   column = 0;
  }
 }

 /* Deselect and wake up anyone waiting on the device */
 onenand_release_device(mtd);

 ops->oobretlen = read;
 return ret;
}

#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
/**
 * onenand_verify_oob - [GENERIC] verify the oob contents after a write
 * @mtd: MTD device structure
 * @buf: the databuffer to verify
 * @to: offset to read from
 */

static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
 struct onenand_chip *this = mtd->priv;
 u_char *oob_buf = this->oob_buf;
 int status, i, readcmd;

 readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;

 this->command(mtd, readcmd, to, mtd->oobsize);
 onenand_update_bufferram(mtd, to, 0);
 status = this->wait(mtd, FL_READING);
 if (status)
  return status;

 this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
 for (i = 0; i < mtd->oobsize; i++)
  if (buf[i] != 0xFF && buf[i] != oob_buf[i])
   return -EBADMSG;

 return 0;
}

/**
 * onenand_verify - [GENERIC] verify the chip contents after a write
 * @mtd:          MTD device structure
 * @buf:          the databuffer to verify
 * @addr:         offset to read from
 * @len:          number of bytes to read and compare
 */

static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
{
 struct onenand_chip *this = mtd->priv;
 int ret = 0;
 int thislen, column;

 column = addr & (this->writesize - 1);

 while (len != 0) {
  thislen = min_t(int, this->writesize - column, len);

  this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);

  onenand_update_bufferram(mtd, addr, 0);

  ret = this->wait(mtd, FL_READING);
  if (ret)
   return ret;

  onenand_update_bufferram(mtd, addr, 1);

  this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);

  if (memcmp(buf, this->verify_buf + column, thislen))
   return -EBADMSG;

  len -= thislen;
  buf += thislen;
  addr += thislen;
  column = 0;
 }

 return 0;
}
#else
#define onenand_verify(...)  (0)
#define onenand_verify_oob(...)  (0)
#endif

#define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0)

static void onenand_panic_wait(struct mtd_info *mtd)
{
 struct onenand_chip *this = mtd->priv;
 unsigned int interrupt;
 int i;
 
 for (i = 0; i < 2000; i++) {
  interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
  if (interrupt & ONENAND_INT_MASTER)
   break;
  udelay(10);
 }
}

/**
 * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context
 * @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
 *
 * Write with ECC
 */

static int onenand_panic_write(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, subpage;
 int written = 0;

 if (this->state == FL_PM_SUSPENDED)
  return -EBUSY;

 /* Wait for any existing operation to clear */
 onenand_panic_wait(mtd);

 pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
   (int)len);

 /* Reject writes, which are not page aligned */
        if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
  printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
   __func__);
                return -EINVAL;
        }

 column = to & (mtd->writesize - 1);

 /* Loop until all data write */
 while (written < len) {
  int thislen = min_t(int, mtd->writesize - column, len - written);
  u_char *wbuf = (u_char *) buf;

  this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);

  /* Partial page write */
  subpage = thislen < mtd->writesize;
  if (subpage) {
   memset(this->page_buf, 0xff, mtd->writesize);
   memcpy(this->page_buf + column, buf, thislen);
   wbuf = this->page_buf;
  }

  this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
  this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);

  this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);

  onenand_panic_wait(mtd);

  /* In partial page write we don't update bufferram */
  onenand_update_bufferram(mtd, to, !subpage);
  if (ONENAND_IS_2PLANE(this)) {
   ONENAND_SET_BUFFERRAM1(this);
   onenand_update_bufferram(mtd, to + this->writesize, !subpage);
  }

  written += thislen;

  if (written == len)
   break;

  column = 0;
  to += thislen;
  buf += thislen;
 }

 *retlen = written;
 return 0;
}

/**
 * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
 * @mtd: MTD device structure
 * @oob_buf: oob buffer
 * @buf: source address
 * @column: oob offset to write to
 * @thislen: oob length to write
 */

static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
      const u_char *buf, int column, int thislen)
{
 return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen);
}

/**
 * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
 * @mtd: MTD device structure
 * @to: offset to write to
 * @ops: oob operation description structure
 *
 * Write main and/or oob with ECC
 */

static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
    struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 int written = 0, column, thislen = 0, subpage = 0;
 int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
 int oobwritten = 0, oobcolumn, thisooblen, oobsize;
 size_t len = ops->len;
 size_t ooblen = ops->ooblen;
 const u_char *buf = ops->datbuf;
 const u_char *oob = ops->oobbuf;
 u_char *oobbuf;
 int ret = 0, cmd;

 pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
   (int)len);

 /* Initialize retlen, in case of early exit */
 ops->retlen = 0;
 ops->oobretlen = 0;

 /* Reject writes, which are not page aligned */
        if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
  printk(KERN_ERR "%s: Attempt to write not page aligned data\n",
   __func__);
                return -EINVAL;
        }

 /* Check zero length */
 if (!len)
  return 0;
 oobsize = mtd_oobavail(mtd, ops);
 oobcolumn = to & (mtd->oobsize - 1);

 column = to & (mtd->writesize - 1);

 /* Loop until all data write */
 while (1) {
  if (written < len) {
   u_char *wbuf = (u_char *) buf;

   thislen = min_t(int, mtd->writesize - column, len - written);
   thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);

   cond_resched();

   this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);

   /* Partial page write */
   subpage = thislen < mtd->writesize;
   if (subpage) {
    memset(this->page_buf, 0xff, mtd->writesize);
    memcpy(this->page_buf + column, buf, thislen);
    wbuf = this->page_buf;
   }

   this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);

   if (oob) {
    oobbuf = this->oob_buf;

    /* We send data to spare ram with oobsize
 * to prevent byte access */

    memset(oobbuf, 0xff, mtd->oobsize);
    if (ops->mode == MTD_OPS_AUTO_OOB)
     onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
    else
     memcpy(oobbuf + oobcolumn, oob, thisooblen);

    oobwritten += thisooblen;
    oob += thisooblen;
    oobcolumn = 0;
   } else
    oobbuf = (u_char *) ffchars;

   this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
  } else
   ONENAND_SET_NEXT_BUFFERRAM(this);

  /*
 * 2 PLANE, MLC, and Flex-OneNAND do not support
 * write-while-program feature.
 */

  if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) {
   ONENAND_SET_PREV_BUFFERRAM(this);

   ret = this->wait(mtd, FL_WRITING);

   /* In partial page write we don't update bufferram */
   onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
   if (ret) {
    written -= prevlen;
    printk(KERN_ERR "%s: write failed %d\n",
     __func__, ret);
    break;
   }

   if (written == len) {
    /* Only check verify write turn on */
    ret = onenand_verify(mtd, buf - len, to - len, len);
    if (ret)
     printk(KERN_ERR "%s: verify failed %d\n",
      __func__, ret);
    break;
   }

   ONENAND_SET_NEXT_BUFFERRAM(this);
  }

  this->ongoing = 0;
  cmd = ONENAND_CMD_PROG;

  /* Exclude 1st OTP and OTP blocks for cache program feature */
  if (ONENAND_IS_CACHE_PROGRAM(this) &&
      likely(onenand_block(this, to) != 0) &&
      ONENAND_IS_4KB_PAGE(this) &&
      ((written + thislen) < len)) {
   cmd = ONENAND_CMD_2X_CACHE_PROG;
   this->ongoing = 1;
  }

  this->command(mtd, cmd, to, mtd->writesize);

  /*
 * 2 PLANE, MLC, and Flex-OneNAND wait here
 */

  if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) {
   ret = this->wait(mtd, FL_WRITING);

   /* In partial page write we don't update bufferram */
   onenand_update_bufferram(mtd, to, !ret && !subpage);
   if (ret) {
    printk(KERN_ERR "%s: write failed %d\n",
     __func__, ret);
    break;
   }

   /* Only check verify write turn on */
   ret = onenand_verify(mtd, buf, to, thislen);
   if (ret) {
    printk(KERN_ERR "%s: verify failed %d\n",
     __func__, ret);
    break;
   }

   written += thislen;

   if (written == len)
    break;

  } else
   written += thislen;

  column = 0;
  prev_subpage = subpage;
  prev = to;
  prevlen = thislen;
  to += thislen;
  buf += thislen;
  first = 0;
 }

 /* In error case, clear all bufferrams */
 if (written != len)
  onenand_invalidate_bufferram(mtd, 0, -1);

 ops->retlen = written;
 ops->oobretlen = oobwritten;

 return ret;
}


/**
 * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
 * @mtd: MTD device structure
 * @to: offset to write to
 * @ops:                oob operation description structure
 *
 * OneNAND write out-of-band
 */

static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
        struct mtd_oob_ops *ops)
{
 struct onenand_chip *this = mtd->priv;
 int column, ret = 0, oobsize;
 int written = 0, oobcmd;
 u_char *oobbuf;
 size_t len = ops->ooblen;
 const u_char *buf = ops->oobbuf;
 unsigned int mode = ops->mode;

 to += ops->ooboffs;

 pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
   (int)len);

 /* Initialize retlen, in case of early exit */
 ops->oobretlen = 0;

 if (mode == MTD_OPS_AUTO_OOB)
  oobsize = mtd->oobavail;
 else
  oobsize = mtd->oobsize;

 column = to & (mtd->oobsize - 1);

 if (unlikely(column >= oobsize)) {
  printk(KERN_ERR "%s: Attempted to start write outside oob\n",
   __func__);
  return -EINVAL;
 }

 /* For compatibility with NAND: Do not allow write past end of page */
 if (unlikely(column + len > oobsize)) {
  printk(KERN_ERR "%s: Attempt to write past end of page\n",
   __func__);
  return -EINVAL;
 }

 oobbuf = this->oob_buf;

 oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;

 /* Loop until all data write */
 while (written < len) {
  int thislen = min_t(int, oobsize, len - written);

  cond_resched();

  this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);

  /* We send data to spare ram with oobsize
 * to prevent byte access */

  memset(oobbuf, 0xff, mtd->oobsize);
  if (mode == MTD_OPS_AUTO_OOB)
   onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
  else
   memcpy(oobbuf + column, buf, thislen);
  this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);

  if (ONENAND_IS_4KB_PAGE(this)) {
   /* Set main area of DataRAM to 0xff*/
   memset(this->page_buf, 0xff, mtd->writesize);
   this->write_bufferram(mtd, ONENAND_DATARAM,
      this->page_buf, 0, mtd->writesize);
  }

  this->command(mtd, oobcmd, to, mtd->oobsize);

  onenand_update_bufferram(mtd, to, 0);
  if (ONENAND_IS_2PLANE(this)) {
   ONENAND_SET_BUFFERRAM1(this);
   onenand_update_bufferram(mtd, to + this->writesize, 0);
  }

  ret = this->wait(mtd, FL_WRITING);
  if (ret) {
   printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
   break;
  }

  ret = onenand_verify_oob(mtd, oobbuf, to);
  if (ret) {
   printk(KERN_ERR "%s: verify failed %d\n",
    __func__, ret);
   break;
  }

  written += thislen;
  if (written == len)
   break;

  to += mtd->writesize;
  buf += thislen;
  column = 0;
 }

 ops->oobretlen = written;

 return ret;
}

/**
 * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
 * @mtd: MTD device structure
 * @to: offset to write
 * @ops: oob operation description structure
 */

static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
        struct mtd_oob_ops *ops)
{
 int ret;

 switch (ops->mode) {
 case MTD_OPS_PLACE_OOB:
 case MTD_OPS_AUTO_OOB:
  break;
 case MTD_OPS_RAW:
  /* Not implemented yet */
 default:
  return -EINVAL;
 }

 onenand_get_device(mtd, FL_WRITING);
 if (ops->datbuf)
  ret = onenand_write_ops_nolock(mtd, to, ops);
 else
  ret = onenand_write_oob_nolock(mtd, to, ops);
 onenand_release_device(mtd);

 return ret;
}

/**
 * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
 * @mtd: MTD device structure
 * @ofs: offset from device start
 * @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 onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
{
 struct onenand_chip *this = mtd->priv;
 struct bbm_info *bbm = this->bbm;

 /* Return info from the table */
 return bbm->isbad_bbt(mtd, ofs, allowbbt);
}


static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
        struct erase_info *instr)
{
 struct onenand_chip *this = mtd->priv;
 loff_t addr = instr->addr;
 int len = instr->len;
 unsigned int block_size = (1 << this->erase_shift);
 int ret = 0;

 while (len) {
  this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size);
  ret = this->wait(mtd, FL_VERIFYING_ERASE);
  if (ret) {
   printk(KERN_ERR "%s: Failed verify, block %d\n",
          __func__, onenand_block(this, addr));
   instr->fail_addr = addr;
   return -1;
  }
  len -= block_size;
  addr += block_size;
 }
 return 0;
}

/**
 * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
 * @mtd: MTD device structure
 * @instr: erase instruction
 * @block_size: block size
 *
 * Erase one or more blocks up to 64 block at a time
 */

static int onenand_multiblock_erase(struct mtd_info *mtd,
        struct erase_info *instr,
        unsigned int block_size)
{
 struct onenand_chip *this = mtd->priv;
 loff_t addr = instr->addr;
 int len = instr->len;
 int eb_count = 0;
 int ret = 0;
 int bdry_block = 0;

 if (ONENAND_IS_DDP(this)) {
  loff_t bdry_addr = this->chipsize >> 1;
  if (addr < bdry_addr && (addr + len) > bdry_addr)
   bdry_block = bdry_addr >> this->erase_shift;
 }

 /* Pre-check bbs */
 while (len) {
  /* Check if we have a bad block, we do not erase bad blocks */
  if (onenand_block_isbad_nolock(mtd, addr, 0)) {
   printk(KERN_WARNING "%s: attempt to erase a bad block "
          "at addr 0x%012llx\n",
          __func__, (unsigned long long) addr);
   return -EIO;
  }
  len -= block_size;
  addr += block_size;
 }

 len = instr->len;
 addr = instr->addr;

 /* loop over 64 eb batches */
 while (len) {
  struct erase_info verify_instr = *instr;
  int max_eb_count = MB_ERASE_MAX_BLK_COUNT;

  verify_instr.addr = addr;
  verify_instr.len = 0;

  /* do not cross chip boundary */
  if (bdry_block) {
   int this_block = (addr >> this->erase_shift);

   if (this_block < bdry_block) {
    max_eb_count = min(max_eb_count,
         (bdry_block - this_block));
   }
  }

  eb_count = 0;

  while (len > block_size && eb_count < (max_eb_count - 1)) {
   this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE,
          addr, block_size);
   onenand_invalidate_bufferram(mtd, addr, block_size);

   ret = this->wait(mtd, FL_PREPARING_ERASE);
   if (ret) {
    printk(KERN_ERR "%s: Failed multiblock erase, "
           "block %d\n", __func__,
           onenand_block(this, addr));
    instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
    return -EIO;
   }

   len -= block_size;
   addr += block_size;
   eb_count++;
  }

  /* last block of 64-eb series */
  cond_resched();
  this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
  onenand_invalidate_bufferram(mtd, addr, block_size);

  ret = this->wait(mtd, FL_ERASING);
  /* Check if it is write protected */
  if (ret) {
   printk(KERN_ERR "%s: Failed erase, block %d\n",
          __func__, onenand_block(this, addr));
   instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
   return -EIO;
  }

  len -= block_size;
  addr += block_size;
  eb_count++;

  /* verify */
  verify_instr.len = eb_count * block_size;
  if (onenand_multiblock_erase_verify(mtd, &verify_instr)) {
   instr->fail_addr = verify_instr.fail_addr;
   return -EIO;
  }

 }
 return 0;
}


/**
 * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
 * @mtd: MTD device structure
 * @instr: erase instruction
 * @region: erase region
 * @block_size: erase block size
 *
 * Erase one or more blocks one block at a time
 */

static int onenand_block_by_block_erase(struct mtd_info *mtd,
     struct erase_info *instr,
     struct mtd_erase_region_info *region,
     unsigned int block_size)
{
 struct onenand_chip *this = mtd->priv;
 loff_t addr = instr->addr;
 int len = instr->len;
 loff_t region_end = 0;
 int ret = 0;

 if (region) {
  /* region is set for Flex-OneNAND */
  region_end = region->offset + region->erasesize * region->numblocks;
 }

 /* Loop through the blocks */
 while (len) {
  cond_resched();

  /* Check if we have a bad block, we do not erase bad blocks */
  if (onenand_block_isbad_nolock(mtd, addr, 0)) {
   printk(KERN_WARNING "%s: attempt to erase a bad block "
     "at addr 0x%012llx\n",
     __func__, (unsigned long long) addr);
   return -EIO;
  }

  this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);

  onenand_invalidate_bufferram(mtd, addr, block_size);

  ret = this->wait(mtd, FL_ERASING);
  /* Check, if it is write protected */
  if (ret) {
   printk(KERN_ERR "%s: Failed erase, block %d\n",
    __func__, onenand_block(this, addr));
   instr->fail_addr = addr;
   return -EIO;
  }

  len -= block_size;
  addr += block_size;

  if (region && addr == region_end) {
   if (!len)
    break;
   region++;

   block_size = region->erasesize;
   region_end = region->offset + region->erasesize * region->numblocks;

   if (len & (block_size - 1)) {
    /* FIXME: This should be handled at MTD partitioning level. */
    printk(KERN_ERR "%s: Unaligned address\n",
     __func__);
    return -EIO;
   }
  }
 }
 return 0;
}

/**
 * onenand_erase - [MTD Interface] erase block(s)
 * @mtd: MTD device structure
 * @instr: erase instruction
 *
 * Erase one or more blocks
 */

static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
 struct onenand_chip *this = mtd->priv;
 unsigned int block_size;
 loff_t addr = instr->addr;
 loff_t len = instr->len;
 int ret = 0;
 struct mtd_erase_region_info *region = NULL;
 loff_t region_offset = 0;

 pr_debug("%s: start=0x%012llx, len=%llu\n", __func__,
   (unsigned long long)instr->addr,
   (unsigned long long)instr->len);

 if (FLEXONENAND(this)) {
  /* Find the eraseregion of this address */
  int i = flexonenand_region(mtd, addr);

  region = &mtd->eraseregions[i];
  block_size = region->erasesize;

  /* Start address within region must align on block boundary.
 * Erase region's start offset is always block start address.
 */

  region_offset = region->offset;
 } else
  block_size = 1 << this->erase_shift;

 /* Start address must align on block boundary */
 if (unlikely((addr - region_offset) & (block_size - 1))) {
  printk(KERN_ERR "%s: Unaligned address\n", __func__);
  return -EINVAL;
 }

 /* Length must align on block boundary */
 if (unlikely(len & (block_size - 1))) {
  printk(KERN_ERR "%s: Length not block aligned\n", __func__);
  return -EINVAL;
 }

 /* Grab the lock and see if the device is available */
 onenand_get_device(mtd, FL_ERASING);

 if (ONENAND_IS_4KB_PAGE(this) || region ||
     instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
  /* region is set for Flex-OneNAND (no mb erase) */
  ret = onenand_block_by_block_erase(mtd, instr,
         region, block_size);
 } else {
  ret = onenand_multiblock_erase(mtd, instr, block_size);
 }

 /* Deselect and wake up anyone waiting on the device */
 onenand_release_device(mtd);

 return ret;
}

/**
 * onenand_sync - [MTD Interface] sync
 * @mtd: MTD device structure
 *
 * Sync is actually a wait for chip ready function
 */

static void onenand_sync(struct mtd_info *mtd)
{
 pr_debug("%s: called\n", __func__);

 /* 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
 * @mtd: MTD device structure
 * @ofs: offset relative to mtd start
 *
 * Check whether the block is bad
 */

static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
 int ret;

 onenand_get_device(mtd, FL_READING);
 ret = onenand_block_isbad_nolock(mtd, ofs, 0);
 onenand_release_device(mtd);
 return ret;
}

/**
 * onenand_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 onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
 struct onenand_chip *this = mtd->priv;
 struct bbm_info *bbm = this->bbm;
 u_char buf[2] = {0, 0};
 struct mtd_oob_ops ops = {
  .mode = MTD_OPS_PLACE_OOB,
  .ooblen = 2,
  .oobbuf = buf,
  .ooboffs = 0,
 };
 int block;

 /* Get block number */
 block = onenand_block(this, ofs);
        if (bbm->bbt)
                bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);

        /* We write two bytes, so we don't have to mess with 16-bit access */
        ofs += mtd->oobsize + (this->badblockpos & ~0x01);
 /* FIXME : What to do when marking SLC block in partition
 *     with MLC erasesize? For now, it is not advisable to
 *    create partitions containing both SLC and MLC regions.
 */

 return onenand_write_oob_nolock(mtd, ofs, &ops);
}

/**
 * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
 * @mtd: MTD device structure
 * @ofs: offset relative to mtd start
 *
 * Mark the block as bad
 */

static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
 struct onenand_chip *this = mtd->priv;
 int ret;

 ret = onenand_block_isbad(mtd, ofs);
 if (ret) {
  /* If it was bad already, return success and do nothing */
  if (ret > 0)
   return 0;
  return ret;
 }

 onenand_get_device(mtd, FL_WRITING);
 ret = this->block_markbad(mtd, ofs);
 onenand_release_device(mtd);
 return ret;
}

/**
 * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
 * @mtd: MTD device structure
 * @ofs: offset relative to mtd start
 * @len: number of bytes to lock or unlock
 * @cmd: lock or unlock command
 *
 * Lock or unlock one or more blocks
 */

static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
{
 struct onenand_chip *this = mtd->priv;
 int start, end, block, value, status;
 int wp_status_mask;

 start = onenand_block(this, ofs);
 end = onenand_block(this, ofs + len) - 1;

 if (cmd == ONENAND_CMD_LOCK)
  wp_status_mask = ONENAND_WP_LS;
 else
  wp_status_mask = ONENAND_WP_US;

 /* Continuous lock scheme */
 if (this->options & ONENAND_HAS_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, this->base +  ONENAND_REG_END_BLOCK_ADDRESS);
  /* Write lock command */
  this->command(mtd, cmd, 0, 0);

  /* There's no return value */
  this->wait(mtd, FL_LOCKING);

  /* 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 & wp_status_mask))
   printk(KERN_ERR "%s: wp status = 0x%x\n",
    __func__, status);

  return 0;
 }

 /* Block lock scheme */
 for (block = start; block < end + 1; block++) {
  /* Set block address */
  value = onenand_block_address(this, block);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=96 G=93

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge