Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/mtd/nand/raw/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 175 kB image not shown  

Quelle  nand_base.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Overview:
 *   This is the generic MTD driver for NAND flash devices. It should be
 *   capable of working with almost all NAND chips currently available.
 *
 * Additional technical information is available on
 * http://www.linux-mtd.infradead.org/doc/nand.html
 *
 *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
 *   2002-2006 Thomas Gleixner (tglx@linutronix.de)
 *
 *  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.
 * BBT table is not serialized, has to be fixed
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/nand-ecc-sw-bch.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>

#include "internals.h"

static int nand_pairing_dist3_get_info(struct mtd_info *mtd, int page,
           struct mtd_pairing_info *info)
{
 int lastpage = (mtd->erasesize / mtd->writesize) - 1;
 int dist = 3;

 if (page == lastpage)
  dist = 2;

 if (!page || (page & 1)) {
  info->group = 0;
  info->pair = (page + 1) / 2;
 } else {
  info->group = 1;
  info->pair = (page + 1 - dist) / 2;
 }

 return 0;
}

static int nand_pairing_dist3_get_wunit(struct mtd_info *mtd,
     const struct mtd_pairing_info *info)
{
 int lastpair = ((mtd->erasesize / mtd->writesize) - 1) / 2;
 int page = info->pair * 2;
 int dist = 3;

 if (!info->group && !info->pair)
  return 0;

 if (info->pair == lastpair && info->group)
  dist = 2;

 if (!info->group)
  page--;
 else if (info->pair)
  page += dist - 1;

 if (page >= mtd->erasesize / mtd->writesize)
  return -EINVAL;

 return page;
}

const struct mtd_pairing_scheme dist3_pairing_scheme = {
 .ngroups = 2,
 .get_info = nand_pairing_dist3_get_info,
 .get_wunit = nand_pairing_dist3_get_wunit,
};

static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
{
 int ret = 0;

 /* Start address must align on block boundary */
 if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
  pr_debug("%s: unaligned address\n", __func__);
  ret = -EINVAL;
 }

 /* Length must align on block boundary */
 if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
  pr_debug("%s: length not block aligned\n", __func__);
  ret = -EINVAL;
 }

 return ret;
}

/**
 * nand_extract_bits - Copy unaligned bits from one buffer to another one
 * @dst: destination buffer
 * @dst_off: bit offset at which the writing starts
 * @src: source buffer
 * @src_off: bit offset at which the reading starts
 * @nbits: number of bits to copy from @src to @dst
 *
 * Copy bits from one memory region to another (overlap authorized).
 */

void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
         unsigned int src_off, unsigned int nbits)
{
 unsigned int tmp, n;

 dst += dst_off / 8;
 dst_off %= 8;
 src += src_off / 8;
 src_off %= 8;

 while (nbits) {
  n = min3(8 - dst_off, 8 - src_off, nbits);

  tmp = (*src >> src_off) & GENMASK(n - 1, 0);
  *dst &= ~GENMASK(n - 1 + dst_off, dst_off);
  *dst |= tmp << dst_off;

  dst_off += n;
  if (dst_off >= 8) {
   dst++;
   dst_off -= 8;
  }

  src_off += n;
  if (src_off >= 8) {
   src++;
   src_off -= 8;
  }

  nbits -= n;
 }
}
EXPORT_SYMBOL_GPL(nand_extract_bits);

/**
 * nand_select_target() - Select a NAND target (A.K.A. die)
 * @chip: NAND chip object
 * @cs: the CS line to select. Note that this CS id is always from the chip
 * PoV, not the controller one
 *
 * Select a NAND target so that further operations executed on @chip go to the
 * selected NAND target.
 */

void nand_select_target(struct nand_chip *chip, unsigned int cs)
{
 /*
 * cs should always lie between 0 and nanddev_ntargets(), when that's
 * not the case it's a bug and the caller should be fixed.
 */

 if (WARN_ON(cs > nanddev_ntargets(&chip->base)))
  return;

 chip->cur_cs = cs;

 if (chip->legacy.select_chip)
  chip->legacy.select_chip(chip, cs);
}
EXPORT_SYMBOL_GPL(nand_select_target);

/**
 * nand_deselect_target() - Deselect the currently selected target
 * @chip: NAND chip object
 *
 * Deselect the currently selected NAND target. The result of operations
 * executed on @chip after the target has been deselected is undefined.
 */

void nand_deselect_target(struct nand_chip *chip)
{
 if (chip->legacy.select_chip)
  chip->legacy.select_chip(chip, -1);

 chip->cur_cs = -1;
}
EXPORT_SYMBOL_GPL(nand_deselect_target);

/**
 * nand_release_device - [GENERIC] release chip
 * @chip: NAND chip object
 *
 * Release chip lock and wake up anyone waiting on the device.
 */

static void nand_release_device(struct nand_chip *chip)
{
 /* Release the controller and the chip */
 mutex_unlock(&chip->controller->lock);
 mutex_unlock(&chip->lock);
}

/**
 * nand_bbm_get_next_page - Get the next page for bad block markers
 * @chip: NAND chip object
 * @page: First page to start checking for bad block marker usage
 *
 * Returns an integer that corresponds to the page offset within a block, for
 * a page that is used to store bad block markers. If no more pages are
 * available, -EINVAL is returned.
 */

int nand_bbm_get_next_page(struct nand_chip *chip, int page)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 int last_page = ((mtd->erasesize - mtd->writesize) >>
    chip->page_shift) & chip->pagemask;
 unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE
  | NAND_BBM_LASTPAGE;

 if (page == 0 && !(chip->options & bbm_flags))
  return 0;
 if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE)
  return 0;
 if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE)
  return 1;
 if (page <= last_page && chip->options & NAND_BBM_LASTPAGE)
  return last_page;

 return -EINVAL;
}

/**
 * nand_block_bad - [DEFAULT] Read bad block marker from the chip
 * @chip: NAND chip object
 * @ofs: offset from device start
 *
 * Check, if the block is bad.
 */

static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
{
 int first_page, page_offset;
 int res;
 u8 bad;

 first_page = (int)(ofs >> chip->page_shift) & chip->pagemask;
 page_offset = nand_bbm_get_next_page(chip, 0);

 while (page_offset >= 0) {
  res = chip->ecc.read_oob(chip, first_page + page_offset);
  if (res < 0)
   return res;

  bad = chip->oob_poi[chip->badblockpos];

  if (likely(chip->badblockbits == 8))
   res = bad != 0xFF;
  else
   res = hweight8(bad) < chip->badblockbits;
  if (res)
   return res;

  page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
 }

 return 0;
}

/**
 * nand_region_is_secured() - Check if the region is secured
 * @chip: NAND chip object
 * @offset: Offset of the region to check
 * @size: Size of the region to check
 *
 * Checks if the region is secured by comparing the offset and size with the
 * list of secure regions obtained from DT. Returns true if the region is
 * secured else false.
 */

static bool nand_region_is_secured(struct nand_chip *chip, loff_t offset, u64 size)
{
 int i;

 /* Skip touching the secure regions if present */
 for (i = 0; i < chip->nr_secure_regions; i++) {
  const struct nand_secure_region *region = &chip->secure_regions[i];

  if (offset + size <= region->offset ||
      offset >= region->offset + region->size)
   continue;

  pr_debug("%s: Region 0x%llx - 0x%llx is secured!",
    __func__, offset, offset + size);

  return true;
 }

 return false;
}

static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
{
 struct mtd_info *mtd = nand_to_mtd(chip);

 if (chip->options & NAND_NO_BBM_QUIRK)
  return 0;

 /* Check if the region is secured */
 if (nand_region_is_secured(chip, ofs, mtd->erasesize))
  return -EIO;

 if (mtd_check_expert_analysis_mode())
  return 0;

 if (chip->legacy.block_bad)
  return chip->legacy.block_bad(chip, ofs);

 return nand_block_bad(chip, ofs);
}

/**
 * nand_get_device - [GENERIC] Get chip for selected access
 * @chip: NAND chip structure
 *
 * Lock the device and its controller for exclusive access
 */

static void nand_get_device(struct nand_chip *chip)
{
 /* Wait until the device is resumed. */
 while (1) {
  mutex_lock(&chip->lock);
  if (!chip->suspended) {
   mutex_lock(&chip->controller->lock);
   return;
  }
  mutex_unlock(&chip->lock);

  wait_event(chip->resume_wq, !chip->suspended);
 }
}

/**
 * nand_check_wp - [GENERIC] check if the chip is write protected
 * @chip: NAND chip object
 *
 * Check, if the device is write protected. The function expects, that the
 * device is already selected.
 */

static int nand_check_wp(struct nand_chip *chip)
{
 u8 status;
 int ret;

 /* Broken xD cards report WP despite being writable */
 if (chip->options & NAND_BROKEN_XD)
  return 0;

 /* controller responsible for NAND write protect */
 if (chip->controller->controller_wp)
  return 0;

 /* Check the WP bit */
 ret = nand_status_op(chip, &status);
 if (ret)
  return ret;

 return status & NAND_STATUS_WP ? 0 : 1;
}

/**
 * nand_fill_oob - [INTERN] Transfer client buffer to oob
 * @chip: NAND chip object
 * @oob: oob data buffer
 * @len: oob data write length
 * @ops: oob ops structure
 */

static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
         struct mtd_oob_ops *ops)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 int ret;

 /*
 * Initialise to all 0xFF, to avoid the possibility of left over OOB
 * data from a previous OOB read.
 */

 memset(chip->oob_poi, 0xff, mtd->oobsize);

 switch (ops->mode) {

 case MTD_OPS_PLACE_OOB:
 case MTD_OPS_RAW:
  memcpy(chip->oob_poi + ops->ooboffs, oob, len);
  return oob + len;

 case MTD_OPS_AUTO_OOB:
  ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
        ops->ooboffs, len);
  BUG_ON(ret);
  return oob + len;

 default:
  BUG();
 }
 return NULL;
}

/**
 * nand_do_write_oob - [MTD Interface] NAND write out-of-band
 * @chip: NAND chip object
 * @to: offset to write to
 * @ops: oob operation description structure
 *
 * NAND write out-of-band.
 */

static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
        struct mtd_oob_ops *ops)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 int chipnr, page, status, len, ret;

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

 len = mtd_oobavail(mtd, ops);

 /* Do not allow write past end of page */
 if ((ops->ooboffs + ops->ooblen) > len) {
  pr_debug("%s: attempt to write past end of page\n",
    __func__);
  return -EINVAL;
 }

 /* Check if the region is secured */
 if (nand_region_is_secured(chip, to, ops->ooblen))
  return -EIO;

 chipnr = (int)(to >> chip->chip_shift);

 /*
 * 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.
 */

 ret = nand_reset(chip, chipnr);
 if (ret)
  return ret;

 nand_select_target(chip, chipnr);

 /* Shift to get page */
 page = (int)(to >> chip->page_shift);

 /* Check, if it is write protected */
 if (nand_check_wp(chip)) {
  nand_deselect_target(chip);
  return -EROFS;
 }

 /* Invalidate the page cache, if we write to the cached page */
 if (page == chip->pagecache.page)
  chip->pagecache.page = -1;

 nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);

 if (ops->mode == MTD_OPS_RAW)
  status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
 else
  status = chip->ecc.write_oob(chip, page & chip->pagemask);

 nand_deselect_target(chip);

 if (status)
  return status;

 ops->oobretlen = ops->ooblen;

 return 0;
}

/**
 * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
 * @chip: NAND chip object
 * @ofs: offset from device start
 *
 * This is the default implementation, which can be overridden by a hardware
 * specific driver. It provides the details for writing a bad block marker to a
 * block.
 */

static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 struct mtd_oob_ops ops;
 uint8_t buf[2] = { 0, 0 };
 int ret = 0, res, page_offset;

 memset(&ops, 0, sizeof(ops));
 ops.oobbuf = buf;
 ops.ooboffs = chip->badblockpos;
 if (chip->options & NAND_BUSWIDTH_16) {
  ops.ooboffs &= ~0x01;
  ops.len = ops.ooblen = 2;
 } else {
  ops.len = ops.ooblen = 1;
 }
 ops.mode = MTD_OPS_PLACE_OOB;

 page_offset = nand_bbm_get_next_page(chip, 0);

 while (page_offset >= 0) {
  res = nand_do_write_oob(chip,
     ofs + (page_offset * mtd->writesize),
     &ops);

  if (!ret)
   ret = res;

  page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
 }

 return ret;
}

/**
 * nand_markbad_bbm - mark a block by updating the BBM
 * @chip: NAND chip object
 * @ofs: offset of the block to mark bad
 */

int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs)
{
 if (chip->legacy.block_markbad)
  return chip->legacy.block_markbad(chip, ofs);

 return nand_default_block_markbad(chip, ofs);
}

/**
 * nand_block_markbad_lowlevel - mark a block bad
 * @chip: NAND chip object
 * @ofs: offset from device start
 *
 * This function performs the generic NAND bad block marking steps (i.e., bad
 * block table(s) and/or marker(s)). We only allow the hardware driver to
 * specify how to write bad block markers to OOB (chip->legacy.block_markbad).
 *
 * We try operations in the following order:
 *
 *  (1) erase the affected block, to allow OOB marker to be written cleanly
 *  (2) write bad block marker to OOB area of affected block (unless flag
 *      NAND_BBT_NO_OOB_BBM is present)
 *  (3) update the BBT
 *
 * Note that we retain the first error encountered in (2) or (3), finish the
 * procedures, and dump the error in the end.
*/

static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 int res, ret = 0;

 if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
  struct erase_info einfo;

  /* Attempt erase before marking OOB */
  memset(&einfo, 0, sizeof(einfo));
  einfo.addr = ofs;
  einfo.len = 1ULL << chip->phys_erase_shift;
  nand_erase_nand(chip, &einfo, 0);

  /* Write bad block marker to OOB */
  nand_get_device(chip);

  ret = nand_markbad_bbm(chip, ofs);
  nand_release_device(chip);
 }

 /* Mark block bad in BBT */
 if (chip->bbt) {
  res = nand_markbad_bbt(chip, ofs);
  if (!ret)
   ret = res;
 }

 if (!ret)
  mtd->ecc_stats.badblocks++;

 return ret;
}

/**
 * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
 * @mtd: MTD device structure
 * @ofs: offset from device start
 *
 * Check if the block is marked as reserved.
 */

static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
 struct nand_chip *chip = mtd_to_nand(mtd);

 if (!chip->bbt)
  return 0;
 /* Return info from the table */
 return nand_isreserved_bbt(chip, ofs);
}

/**
 * nand_block_checkbad - [GENERIC] Check if a block is marked bad
 * @chip: NAND chip object
 * @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 nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)
{
 /* Return info from the table */
 if (chip->bbt)
  return nand_isbad_bbt(chip, ofs, allowbbt);

 return nand_isbad_bbm(chip, ofs);
}

/**
 * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1
 * @chip: NAND chip structure
 * @timeout_ms: Timeout in ms
 *
 * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1.
 * If that does not happen whitin the specified timeout, -ETIMEDOUT is
 * returned.
 *
 * This helper is intended to be used when the controller does not have access
 * to the NAND R/B pin.
 *
 * Be aware that calling this helper from an ->exec_op() implementation means
 * ->exec_op() must be re-entrant.
 *
 * Return 0 if the NAND chip is ready, a negative error otherwise.
 */

int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
{
 const struct nand_interface_config *conf;
 u8 status = 0;
 int ret;

 if (!nand_has_exec_op(chip))
  return -ENOTSUPP;

 /* Wait tWB before polling the STATUS reg. */
 conf = nand_get_interface_config(chip);
 ndelay(NAND_COMMON_TIMING_NS(conf, tWB_max));

 ret = nand_status_op(chip, NULL);
 if (ret)
  return ret;

 /*
 * +1 below is necessary because if we are now in the last fraction
 * of jiffy and msecs_to_jiffies is 1 then we will wait only that
 * small jiffy fraction - possibly leading to false timeout
 */

 timeout_ms = jiffies + msecs_to_jiffies(timeout_ms) + 1;
 do {
  ret = nand_read_data_op(chip, &status, sizeof(status), true,
     false);
  if (ret)
   break;

  if (status & NAND_STATUS_READY)
   break;

  /*
 * Typical lowest execution time for a tR on most NANDs is 10us,
 * use this as polling delay before doing something smarter (ie.
 * deriving a delay from the timeout value, timeout_ms/ratio).
 */

  udelay(10);
 } while (time_before(jiffies, timeout_ms));

 /*
 * We have to exit READ_STATUS mode in order to read real data on the
 * bus in case the WAITRDY instruction is preceding a DATA_IN
 * instruction.
 */

 nand_exit_status_op(chip);

 if (ret)
  return ret;

 return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT;
};
EXPORT_SYMBOL_GPL(nand_soft_waitrdy);

/**
 * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
 * @chip: NAND chip structure
 * @gpiod: GPIO descriptor of R/B pin
 * @timeout_ms: Timeout in ms
 *
 * Poll the R/B GPIO pin until it becomes ready. If that does not happen
 * whitin the specified timeout, -ETIMEDOUT is returned.
 *
 * This helper is intended to be used when the controller has access to the
 * NAND R/B pin over GPIO.
 *
 * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
 */

int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
        unsigned long timeout_ms)
{

 /*
 * Wait until R/B pin indicates chip is ready or timeout occurs.
 * +1 below is necessary because if we are now in the last fraction
 * of jiffy and msecs_to_jiffies is 1 then we will wait only that
 * small jiffy fraction - possibly leading to false timeout.
 */

 timeout_ms = jiffies + msecs_to_jiffies(timeout_ms) + 1;
 do {
  if (gpiod_get_value_cansleep(gpiod))
   return 0;

  cond_resched();
 } while (time_before(jiffies, timeout_ms));

 return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT;
};
EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);

/**
 * panic_nand_wait - [GENERIC] wait until the command is done
 * @chip: NAND chip structure
 * @timeo: timeout
 *
 * Wait for command done. This is a helper function for nand_wait used when
 * we are in interrupt context. May happen when in panic and trying to write
 * an oops through mtdoops.
 */

void panic_nand_wait(struct nand_chip *chip, unsigned long timeo)
{
 int i;
 for (i = 0; i < timeo; i++) {
  if (chip->legacy.dev_ready) {
   if (chip->legacy.dev_ready(chip))
    break;
  } else {
   int ret;
   u8 status;

   ret = nand_read_data_op(chip, &status, sizeof(status),
      truefalse);
   if (ret)
    return;

   if (status & NAND_STATUS_READY)
    break;
  }
  mdelay(1);
 }
}

static bool nand_supports_get_features(struct nand_chip *chip, int addr)
{
 return (chip->parameters.supports_set_get_features &&
  test_bit(addr, chip->parameters.get_feature_list));
}

static bool nand_supports_set_features(struct nand_chip *chip, int addr)
{
 return (chip->parameters.supports_set_get_features &&
  test_bit(addr, chip->parameters.set_feature_list));
}

/**
 * nand_reset_interface - Reset data interface and timings
 * @chip: The NAND chip
 * @chipnr: Internal die id
 *
 * Reset the Data interface and timings to ONFI mode 0.
 *
 * Returns 0 for success or negative error code otherwise.
 */

static int nand_reset_interface(struct nand_chip *chip, int chipnr)
{
 const struct nand_controller_ops *ops = chip->controller->ops;
 int ret;

 if (!nand_controller_can_setup_interface(chip))
  return 0;

 /*
 * The ONFI specification says:
 * "
 * To transition from NV-DDR or NV-DDR2 to the SDR data
 * interface, the host shall use the Reset (FFh) command
 * using SDR timing mode 0. A device in any timing mode is
 * required to recognize Reset (FFh) command issued in SDR
 * timing mode 0.
 * "
 *
 * Configure the data interface in SDR mode and set the
 * timings to timing mode 0.
 */


 chip->current_interface_config = nand_get_reset_interface_config();
 ret = ops->setup_interface(chip, chipnr,
       chip->current_interface_config);
 if (ret)
  pr_err("Failed to configure data interface to SDR timing mode 0\n");

 return ret;
}

/**
 * nand_setup_interface - Setup the best data interface and timings
 * @chip: The NAND chip
 * @chipnr: Internal die id
 *
 * Configure what has been reported to be the best data interface and NAND
 * timings supported by the chip and the driver.
 *
 * Returns 0 for success or negative error code otherwise.
 */

static int nand_setup_interface(struct nand_chip *chip, int chipnr)
{
 const struct nand_controller_ops *ops = chip->controller->ops;
 u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { }, request;
 int ret;

 if (!nand_controller_can_setup_interface(chip))
  return 0;

 /*
 * A nand_reset_interface() put both the NAND chip and the NAND
 * controller in timings mode 0. If the default mode for this chip is
 * also 0, no need to proceed to the change again. Plus, at probe time,
 * nand_setup_interface() uses ->set/get_features() which would
 * fail anyway as the parameter page is not available yet.
 */

 if (!chip->best_interface_config)
  return 0;

 request = chip->best_interface_config->timings.mode;
 if (nand_interface_is_sdr(chip->best_interface_config))
  request |= ONFI_DATA_INTERFACE_SDR;
 else
  request |= ONFI_DATA_INTERFACE_NVDDR;
 tmode_param[0] = request;

 /* Change the mode on the chip side (if supported by the NAND chip) */
 if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
  nand_select_target(chip, chipnr);
  ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
     tmode_param);
  nand_deselect_target(chip);
  if (ret)
   return ret;
 }

 /* Change the mode on the controller side */
 ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
 if (ret)
  return ret;

 /* Check the mode has been accepted by the chip, if supported */
 if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
  goto update_interface_config;

 memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
 nand_select_target(chip, chipnr);
 ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
    tmode_param);
 nand_deselect_target(chip);
 if (ret)
  goto err_reset_chip;

 if (request != tmode_param[0]) {
  pr_warn("%s timing mode %d not acknowledged by the NAND chip\n",
   nand_interface_is_nvddr(chip->best_interface_config) ? "NV-DDR" : "SDR",
   chip->best_interface_config->timings.mode);
  pr_debug("NAND chip would work in %s timing mode %d\n",
    tmode_param[0] & ONFI_DATA_INTERFACE_NVDDR ? "NV-DDR" : "SDR",
    (unsigned int)ONFI_TIMING_MODE_PARAM(tmode_param[0]));
  goto err_reset_chip;
 }

update_interface_config:
 chip->current_interface_config = chip->best_interface_config;

 return 0;

err_reset_chip:
 /*
 * Fallback to mode 0 if the chip explicitly did not ack the chosen
 * timing mode.
 */

 nand_reset_interface(chip, chipnr);
 nand_select_target(chip, chipnr);
 nand_reset_op(chip);
 nand_deselect_target(chip);

 return ret;
}

/**
 * nand_choose_best_sdr_timings - Pick up the best SDR timings that both the
 *                                NAND controller and the NAND chip support
 * @chip: the NAND chip
 * @iface: the interface configuration (can eventually be updated)
 * @spec_timings: specific timings, when not fitting the ONFI specification
 *
 * If specific timings are provided, use them. Otherwise, retrieve supported
 * timing modes from ONFI information.
 */

int nand_choose_best_sdr_timings(struct nand_chip *chip,
     struct nand_interface_config *iface,
     struct nand_sdr_timings *spec_timings)
{
 const struct nand_controller_ops *ops = chip->controller->ops;
 int best_mode = 0, mode, ret = -EOPNOTSUPP;

 iface->type = NAND_SDR_IFACE;

 if (spec_timings) {
  iface->timings.sdr = *spec_timings;
  iface->timings.mode = onfi_find_closest_sdr_mode(spec_timings);

  /* Verify the controller supports the requested interface */
  ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
        iface);
  if (!ret) {
   chip->best_interface_config = iface;
   return ret;
  }

  /* Fallback to slower modes */
  best_mode = iface->timings.mode;
 } else if (chip->parameters.onfi) {
  best_mode = fls(chip->parameters.onfi->sdr_timing_modes) - 1;
 }

 for (mode = best_mode; mode >= 0; mode--) {
  onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, mode);

  ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
        iface);
  if (!ret) {
   chip->best_interface_config = iface;
   break;
  }
 }

 return ret;
}

/**
 * nand_choose_best_nvddr_timings - Pick up the best NVDDR timings that both the
 *                                  NAND controller and the NAND chip support
 * @chip: the NAND chip
 * @iface: the interface configuration (can eventually be updated)
 * @spec_timings: specific timings, when not fitting the ONFI specification
 *
 * If specific timings are provided, use them. Otherwise, retrieve supported
 * timing modes from ONFI information.
 */

int nand_choose_best_nvddr_timings(struct nand_chip *chip,
       struct nand_interface_config *iface,
       struct nand_nvddr_timings *spec_timings)
{
 const struct nand_controller_ops *ops = chip->controller->ops;
 int best_mode = 0, mode, ret = -EOPNOTSUPP;

 iface->type = NAND_NVDDR_IFACE;

 if (spec_timings) {
  iface->timings.nvddr = *spec_timings;
  iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings);

  /* Verify the controller supports the requested interface */
  ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
        iface);
  if (!ret) {
   chip->best_interface_config = iface;
   return ret;
  }

  /* Fallback to slower modes */
  best_mode = iface->timings.mode;
 } else if (chip->parameters.onfi) {
  best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1;
 }

 for (mode = best_mode; mode >= 0; mode--) {
  onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode);

  ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
        iface);
  if (!ret) {
   chip->best_interface_config = iface;
   break;
  }
 }

 return ret;
}

/**
 * nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both
 *                            NAND controller and the NAND chip support
 * @chip: the NAND chip
 * @iface: the interface configuration (can eventually be updated)
 *
 * If specific timings are provided, use them. Otherwise, retrieve supported
 * timing modes from ONFI information.
 */

static int nand_choose_best_timings(struct nand_chip *chip,
        struct nand_interface_config *iface)
{
 int ret;

 /* Try the fastest timings: NV-DDR */
 ret = nand_choose_best_nvddr_timings(chip, iface, NULL);
 if (!ret)
  return 0;

 /* Fallback to SDR timings otherwise */
 return nand_choose_best_sdr_timings(chip, iface, NULL);
}

/**
 * nand_choose_interface_config - find the best data interface and timings
 * @chip: The NAND chip
 *
 * Find the best data interface and NAND timings supported by the chip
 * and the driver. Eventually let the NAND manufacturer driver propose his own
 * set of timings.
 *
 * After this function nand_chip->interface_config is initialized with the best
 * timing mode available.
 *
 * Returns 0 for success or negative error code otherwise.
 */

static int nand_choose_interface_config(struct nand_chip *chip)
{
 struct nand_interface_config *iface;
 int ret;

 if (!nand_controller_can_setup_interface(chip))
  return 0;

 iface = kzalloc(sizeof(*iface), GFP_KERNEL);
 if (!iface)
  return -ENOMEM;

 if (chip->ops.choose_interface_config)
  ret = chip->ops.choose_interface_config(chip, iface);
 else
  ret = nand_choose_best_timings(chip, iface);

 if (ret)
  kfree(iface);

 return ret;
}

/**
 * nand_fill_column_cycles - fill the column cycles of an address
 * @chip: The NAND chip
 * @addrs: Array of address cycles to fill
 * @offset_in_page: The offset in the page
 *
 * Fills the first or the first two bytes of the @addrs field depending
 * on the NAND bus width and the page size.
 *
 * Returns the number of cycles needed to encode the column, or a negative
 * error code in case one of the arguments is invalid.
 */

static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs,
       unsigned int offset_in_page)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 bool ident_stage = !mtd->writesize;

 /* Bypass all checks during NAND identification */
 if (likely(!ident_stage)) {
  /* Make sure the offset is less than the actual page size. */
  if (offset_in_page > mtd->writesize + mtd->oobsize)
   return -EINVAL;

  /*
 * On small page NANDs, there's a dedicated command to access the OOB
 * area, and the column address is relative to the start of the OOB
 * area, not the start of the page. Asjust the address accordingly.
 */

  if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize)
   offset_in_page -= mtd->writesize;

  /*
 * The offset in page is expressed in bytes, if the NAND bus is 16-bit
 * wide, then it must be divided by 2.
 */

  if (chip->options & NAND_BUSWIDTH_16) {
   if (WARN_ON(offset_in_page % 2))
    return -EINVAL;

   offset_in_page /= 2;
  }
 }

 addrs[0] = offset_in_page;

 /*
 * Small page NANDs use 1 cycle for the columns, while large page NANDs
 * need 2
 */

 if (!ident_stage && mtd->writesize <= 512)
  return 1;

 addrs[1] = offset_in_page >> 8;

 return 2;
}

static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
         unsigned int offset_in_page, void *buf,
         unsigned int len)
{
 const struct nand_interface_config *conf =
  nand_get_interface_config(chip);
 struct mtd_info *mtd = nand_to_mtd(chip);
 u8 addrs[4];
 struct nand_op_instr instrs[] = {
  NAND_OP_CMD(NAND_CMD_READ0, 0),
  NAND_OP_ADDR(3, addrs, NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
     NAND_COMMON_TIMING_NS(conf, tRR_min)),
  NAND_OP_DATA_IN(len, buf, 0),
 };
 struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 int ret;

 /* Drop the DATA_IN instruction if len is set to 0. */
 if (!len)
  op.ninstrs--;

 if (offset_in_page >= mtd->writesize)
  instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
 else if (offset_in_page >= 256 &&
   !(chip->options & NAND_BUSWIDTH_16))
  instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;

 ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
 if (ret < 0)
  return ret;

 addrs[1] = page;
 addrs[2] = page >> 8;

 if (chip->options & NAND_ROW_ADDR_3) {
  addrs[3] = page >> 16;
  instrs[1].ctx.addr.naddrs++;
 }

 return nand_exec_op(chip, &op);
}

static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
         unsigned int offset_in_page, void *buf,
         unsigned int len)
{
 const struct nand_interface_config *conf =
  nand_get_interface_config(chip);
 u8 addrs[5];
 struct nand_op_instr instrs[] = {
  NAND_OP_CMD(NAND_CMD_READ0, 0),
  NAND_OP_ADDR(4, addrs, 0),
  NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
     NAND_COMMON_TIMING_NS(conf, tRR_min)),
  NAND_OP_DATA_IN(len, buf, 0),
 };
 struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 int ret;

 /* Drop the DATA_IN instruction if len is set to 0. */
 if (!len)
  op.ninstrs--;

 ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
 if (ret < 0)
  return ret;

 addrs[2] = page;
 addrs[3] = page >> 8;

 if (chip->options & NAND_ROW_ADDR_3) {
  addrs[4] = page >> 16;
  instrs[1].ctx.addr.naddrs++;
 }

 return nand_exec_op(chip, &op);
}

static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
{
 /* lun is expected to be very small */
 return (lun * pages_per_lun) + pages_per_lun - 1;
}

static void rawnand_cap_cont_reads(struct nand_chip *chip)
{
 struct nand_memory_organization *memorg;
 unsigned int ppl, first_lun, last_lun;

 memorg = nanddev_get_memorg(&chip->base);
 ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
 first_lun = chip->cont_read.first_page / ppl;
 last_lun = chip->cont_read.last_page / ppl;

 /* Prevent sequential cache reads across LUN boundaries */
 if (first_lun != last_lun)
  chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
 else
  chip->cont_read.pause_page = chip->cont_read.last_page;

 if (chip->cont_read.first_page == chip->cont_read.pause_page) {
  chip->cont_read.first_page++;
  chip->cont_read.pause_page = min(chip->cont_read.last_page,
       rawnand_last_page_of_lun(ppl, first_lun + 1));
 }

 if (chip->cont_read.first_page >= chip->cont_read.last_page)
  chip->cont_read.ongoing = false;
}

static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
       unsigned int offset_in_page, void *buf,
       unsigned int len, bool check_only)
{
 const struct nand_interface_config *conf =
  nand_get_interface_config(chip);
 u8 addrs[5];
 struct nand_op_instr start_instrs[] = {
  NAND_OP_CMD(NAND_CMD_READ0, 0),
  NAND_OP_ADDR(4, addrs, 0),
  NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), 0),
  NAND_OP_CMD(NAND_CMD_READCACHESEQ, NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
     NAND_COMMON_TIMING_NS(conf, tRR_min)),
  NAND_OP_DATA_IN(len, buf, 0),
 };
 struct nand_op_instr cont_instrs[] = {
  NAND_OP_CMD(page == chip->cont_read.pause_page ?
       NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ,
       NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
     NAND_COMMON_TIMING_NS(conf, tRR_min)),
  NAND_OP_DATA_IN(len, buf, 0),
 };
 struct nand_operation start_op = NAND_OPERATION(chip->cur_cs, start_instrs);
 struct nand_operation cont_op = NAND_OPERATION(chip->cur_cs, cont_instrs);
 int ret;

 if (!len) {
  start_op.ninstrs--;
  cont_op.ninstrs--;
 }

 ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
 if (ret < 0)
  return ret;

 addrs[2] = page;
 addrs[3] = page >> 8;

 if (chip->options & NAND_ROW_ADDR_3) {
  addrs[4] = page >> 16;
  start_instrs[1].ctx.addr.naddrs++;
 }

 /* Check if cache reads are supported */
 if (check_only) {
  if (nand_check_op(chip, &start_op) || nand_check_op(chip, &cont_op))
   return -EOPNOTSUPP;

  return 0;
 }

 if (page == chip->cont_read.first_page)
  ret = nand_exec_op(chip, &start_op);
 else
  ret = nand_exec_op(chip, &cont_op);
 if (ret)
  return ret;

 if (!chip->cont_read.ongoing)
  return 0;

 if (page == chip->cont_read.last_page) {
  chip->cont_read.ongoing = false;
 } else if (page == chip->cont_read.pause_page) {
  chip->cont_read.first_page++;
  rawnand_cap_cont_reads(chip);
 }

 return 0;
}

static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page)
{
 return chip->cont_read.ongoing && page >= chip->cont_read.first_page;
}

/**
 * nand_read_page_op - Do a READ PAGE operation
 * @chip: The NAND chip
 * @page: page to read
 * @offset_in_page: offset within the page
 * @buf: buffer used to store the data
 * @len: length of the buffer
 *
 * This function issues a READ PAGE operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_read_page_op(struct nand_chip *chip, unsigned int page,
        unsigned int offset_in_page, void *buf, unsigned int len)
{
 struct mtd_info *mtd = nand_to_mtd(chip);

 if (len && !buf)
  return -EINVAL;

 if (offset_in_page + len > mtd->writesize + mtd->oobsize)
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  if (mtd->writesize > 512) {
   if (rawnand_cont_read_ongoing(chip, page))
    return nand_lp_exec_cont_read_page_op(chip, page,
              offset_in_page,
              buf, len, false);
   else
    return nand_lp_exec_read_page_op(chip, page,
         offset_in_page, buf,
         len);
  }

  return nand_sp_exec_read_page_op(chip, page, offset_in_page,
       buf, len);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_READ0, offset_in_page, page);
 if (len)
  chip->legacy.read_buf(chip, buf, len);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_read_page_op);

/**
 * nand_read_param_page_op - Do a READ PARAMETER PAGE operation
 * @chip: The NAND chip
 * @page: parameter page to read
 * @buf: buffer used to store the data
 * @len: length of the buffer
 *
 * This function issues a READ PARAMETER PAGE operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
       unsigned int len)
{
 unsigned int i;
 u8 *p = buf;

 if (len && !buf)
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_PARAM, 0),
   NAND_OP_ADDR(1, &page,
         NAND_COMMON_TIMING_NS(conf, tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
      NAND_COMMON_TIMING_NS(conf, tRR_min)),
   NAND_OP_8BIT_DATA_IN(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  /* Drop the DATA_IN instruction if len is set to 0. */
  if (!len)
   op.ninstrs--;

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_PARAM, page, -1);
 for (i = 0; i < len; i++)
  p[i] = chip->legacy.read_byte(chip);

 return 0;
}

/**
 * nand_change_read_column_op - Do a CHANGE READ COLUMN operation
 * @chip: The NAND chip
 * @offset_in_page: offset within the page
 * @buf: buffer used to store the data
 * @len: length of the buffer
 * @force_8bit: force 8-bit bus access
 *
 * This function issues a CHANGE READ COLUMN operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_change_read_column_op(struct nand_chip *chip,
          unsigned int offset_in_page, void *buf,
          unsigned int len, bool force_8bit)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 bool ident_stage = !mtd->writesize;

 if (len && !buf)
  return -EINVAL;

 if (!ident_stage) {
  if (offset_in_page + len > mtd->writesize + mtd->oobsize)
   return -EINVAL;

  /* Small page NANDs do not support column change. */
  if (mtd->writesize <= 512)
   return -ENOTSUPP;
 }

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  u8 addrs[2] = {};
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_RNDOUT, 0),
   NAND_OP_ADDR(2, addrs, 0),
   NAND_OP_CMD(NAND_CMD_RNDOUTSTART,
        NAND_COMMON_TIMING_NS(conf, tCCS_min)),
   NAND_OP_DATA_IN(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  int ret;

  ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
  if (ret < 0)
   return ret;

  /* Drop the DATA_IN instruction if len is set to 0. */
  if (!len)
   op.ninstrs--;

  instrs[3].ctx.data.force_8bit = force_8bit;

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, offset_in_page, -1);
 if (len)
  chip->legacy.read_buf(chip, buf, len);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_change_read_column_op);

/**
 * nand_read_oob_op - Do a READ OOB operation
 * @chip: The NAND chip
 * @page: page to read
 * @offset_in_oob: offset within the OOB area
 * @buf: buffer used to store the data
 * @len: length of the buffer
 *
 * This function issues a READ OOB operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_read_oob_op(struct nand_chip *chip, unsigned int page,
       unsigned int offset_in_oob, void *buf, unsigned int len)
{
 struct mtd_info *mtd = nand_to_mtd(chip);

 if (len && !buf)
  return -EINVAL;

 if (offset_in_oob + len > mtd->oobsize)
  return -EINVAL;

 if (nand_has_exec_op(chip))
  return nand_read_page_op(chip, page,
      mtd->writesize + offset_in_oob,
      buf, len);

 chip->legacy.cmdfunc(chip, NAND_CMD_READOOB, offset_in_oob, page);
 if (len)
  chip->legacy.read_buf(chip, buf, len);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_read_oob_op);

static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
      unsigned int offset_in_page, const void *buf,
      unsigned int len, bool prog)
{
 const struct nand_interface_config *conf =
  nand_get_interface_config(chip);
 struct mtd_info *mtd = nand_to_mtd(chip);
 u8 addrs[5] = {};
 struct nand_op_instr instrs[] = {
  /*
 * The first instruction will be dropped if we're dealing
 * with a large page NAND and adjusted if we're dealing
 * with a small page NAND and the page offset is > 255.
 */

  NAND_OP_CMD(NAND_CMD_READ0, 0),
  NAND_OP_CMD(NAND_CMD_SEQIN, 0),
  NAND_OP_ADDR(0, addrs, NAND_COMMON_TIMING_NS(conf, tADL_min)),
  NAND_OP_DATA_OUT(len, buf, 0),
  NAND_OP_CMD(NAND_CMD_PAGEPROG,
       NAND_COMMON_TIMING_NS(conf, tWB_max)),
  NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0),
 };
 struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
             instrs);
 int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);

 if (naddrs < 0)
  return naddrs;

 addrs[naddrs++] = page;
 addrs[naddrs++] = page >> 8;
 if (chip->options & NAND_ROW_ADDR_3)
  addrs[naddrs++] = page >> 16;

 instrs[2].ctx.addr.naddrs = naddrs;

 /* Drop the last two instructions if we're not programming the page. */
 if (!prog) {
  op.ninstrs -= 2;
  /* Also drop the DATA_OUT instruction if empty. */
  if (!len)
   op.ninstrs--;
 }

 if (mtd->writesize <= 512) {
  /*
 * Small pages need some more tweaking: we have to adjust the
 * first instruction depending on the page offset we're trying
 * to access.
 */

  if (offset_in_page >= mtd->writesize)
   instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB;
  else if (offset_in_page >= 256 &&
    !(chip->options & NAND_BUSWIDTH_16))
   instrs[0].ctx.cmd.opcode = NAND_CMD_READ1;
 } else {
  /*
 * Drop the first command if we're dealing with a large page
 * NAND.
 */

  op.instrs++;
  op.ninstrs--;
 }

 return nand_exec_op(chip, &op);
}

/**
 * nand_prog_page_begin_op - starts a PROG PAGE operation
 * @chip: The NAND chip
 * @page: page to write
 * @offset_in_page: offset within the page
 * @buf: buffer containing the data to write to the page
 * @len: length of the buffer
 *
 * This function issues the first half of a PROG PAGE operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
       unsigned int offset_in_page, const void *buf,
       unsigned int len)
{
 struct mtd_info *mtd = nand_to_mtd(chip);

 if (len && !buf)
  return -EINVAL;

 if (offset_in_page + len > mtd->writesize + mtd->oobsize)
  return -EINVAL;

 if (nand_has_exec_op(chip))
  return nand_exec_prog_page_op(chip, page, offset_in_page, buf,
           len, false);

 chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page, page);

 if (buf)
  chip->legacy.write_buf(chip, buf, len);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_prog_page_begin_op);

/**
 * nand_prog_page_end_op - ends a PROG PAGE operation
 * @chip: The NAND chip
 *
 * This function issues the second half of a PROG PAGE operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_prog_page_end_op(struct nand_chip *chip)
{
 int ret;
 u8 status;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_PAGEPROG,
        NAND_COMMON_TIMING_NS(conf, tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max),
      0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  ret = nand_exec_op(chip, &op);
  if (ret)
   return ret;

  ret = nand_status_op(chip, &status);
  if (ret)
   return ret;
 } else {
  chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
  ret = chip->legacy.waitfunc(chip);
  if (ret < 0)
   return ret;

  status = ret;
 }

 if (status & NAND_STATUS_FAIL)
  return -EIO;

 return 0;
}
EXPORT_SYMBOL_GPL(nand_prog_page_end_op);

/**
 * nand_prog_page_op - Do a full PROG PAGE operation
 * @chip: The NAND chip
 * @page: page to write
 * @offset_in_page: offset within the page
 * @buf: buffer containing the data to write to the page
 * @len: length of the buffer
 *
 * This function issues a full PROG PAGE operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
        unsigned int offset_in_page, const void *buf,
        unsigned int len)
{
 struct mtd_info *mtd = nand_to_mtd(chip);
 u8 status;
 int ret;

 if (!len || !buf)
  return -EINVAL;

 if (offset_in_page + len > mtd->writesize + mtd->oobsize)
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
      len, true);
  if (ret)
   return ret;

  ret = nand_status_op(chip, &status);
  if (ret)
   return ret;
 } else {
  chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page,
         page);
  chip->legacy.write_buf(chip, buf, len);
  chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
  ret = chip->legacy.waitfunc(chip);
  if (ret < 0)
   return ret;

  status = ret;
 }

 if (status & NAND_STATUS_FAIL)
  return -EIO;

 return 0;
}
EXPORT_SYMBOL_GPL(nand_prog_page_op);

/**
 * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation
 * @chip: The NAND chip
 * @offset_in_page: offset within the page
 * @buf: buffer containing the data to send to the NAND
 * @len: length of the buffer
 * @force_8bit: force 8-bit bus access
 *
 * This function issues a CHANGE WRITE COLUMN operation.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_change_write_column_op(struct nand_chip *chip,
    unsigned int offset_in_page,
    const void *buf, unsigned int len,
    bool force_8bit)
{
 struct mtd_info *mtd = nand_to_mtd(chip);

 if (len && !buf)
  return -EINVAL;

 if (offset_in_page + len > mtd->writesize + mtd->oobsize)
  return -EINVAL;

 /* Small page NANDs do not support column change. */
 if (mtd->writesize <= 512)
  return -ENOTSUPP;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  u8 addrs[2];
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_RNDIN, 0),
   NAND_OP_ADDR(2, addrs, NAND_COMMON_TIMING_NS(conf, tCCS_min)),
   NAND_OP_DATA_OUT(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  int ret;

  ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
  if (ret < 0)
   return ret;

  instrs[2].ctx.data.force_8bit = force_8bit;

  /* Drop the DATA_OUT instruction if len is set to 0. */
  if (!len)
   op.ninstrs--;

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_RNDIN, offset_in_page, -1);
 if (len)
  chip->legacy.write_buf(chip, buf, len);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_change_write_column_op);

/**
 * nand_readid_op - Do a READID operation
 * @chip: The NAND chip
 * @addr: address cycle to pass after the READID command
 * @buf: buffer used to store the ID
 * @len: length of the buffer
 *
 * This function sends a READID command and reads back the ID returned by the
 * NAND.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
     unsigned int len)
{
 unsigned int i;
 u8 *id = buf, *ddrbuf = NULL;

 if (len && !buf)
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_READID, 0),
   NAND_OP_ADDR(1, &addr,
         NAND_COMMON_TIMING_NS(conf, tADL_min)),
   NAND_OP_8BIT_DATA_IN(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  int ret;

  /* READ_ID data bytes are received twice in NV-DDR mode */
  if (len && nand_interface_is_nvddr(conf)) {
   ddrbuf = kcalloc(2, len, GFP_KERNEL);
   if (!ddrbuf)
    return -ENOMEM;

   instrs[2].ctx.data.len *= 2;
   instrs[2].ctx.data.buf.in = ddrbuf;
  }

  /* Drop the DATA_IN instruction if len is set to 0. */
  if (!len)
   op.ninstrs--;

  ret = nand_exec_op(chip, &op);
  if (!ret && len && nand_interface_is_nvddr(conf)) {
   for (i = 0; i < len; i++)
    id[i] = ddrbuf[i * 2];
  }

  kfree(ddrbuf);

  return ret;
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_READID, addr, -1);

 for (i = 0; i < len; i++)
  id[i] = chip->legacy.read_byte(chip);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_readid_op);

/**
 * nand_status_op - Do a STATUS operation
 * @chip: The NAND chip
 * @status: out variable to store the NAND status
 *
 * This function sends a STATUS command and reads back the status returned by
 * the NAND.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_status_op(struct nand_chip *chip, u8 *status)
{
 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  u8 ddrstatus[2];
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_STATUS,
        NAND_COMMON_TIMING_NS(conf, tADL_min)),
   NAND_OP_8BIT_DATA_IN(1, status, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  int ret;

  /* The status data byte will be received twice in NV-DDR mode */
  if (status && nand_interface_is_nvddr(conf)) {
   instrs[1].ctx.data.len *= 2;
   instrs[1].ctx.data.buf.in = ddrstatus;
  }

  if (!status)
   op.ninstrs--;

  ret = nand_exec_op(chip, &op);
  if (!ret && status && nand_interface_is_nvddr(conf))
   *status = ddrstatus[0];

  return ret;
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1);
 if (status)
  *status = chip->legacy.read_byte(chip);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_status_op);

/**
 * nand_exit_status_op - Exit a STATUS operation
 * @chip: The NAND chip
 *
 * This function sends a READ0 command to cancel the effect of the STATUS
 * command to avoid reading only the status until a new read command is sent.
 *
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_exit_status_op(struct nand_chip *chip)
{
 if (nand_has_exec_op(chip)) {
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_READ0, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_READ0, -1, -1);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_exit_status_op);

/**
 * nand_erase_op - Do an erase operation
 * @chip: The NAND chip
 * @eraseblock: block to erase
 *
 * This function sends an ERASE command and waits for the NAND to be ready
 * before returning.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
{
 unsigned int page = eraseblock <<
       (chip->phys_erase_shift - chip->page_shift);
 int ret;
 u8 status;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  u8 addrs[3] = { page, page >> 8, page >> 16 };
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_ERASE1, 0),
   NAND_OP_ADDR(2, addrs, 0),
   NAND_OP_CMD(NAND_CMD_ERASE2,
        NAND_COMMON_TIMING_NS(conf, tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max),
      0),
  };
  struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
              instrs);

  if (chip->options & NAND_ROW_ADDR_3)
   instrs[1].ctx.addr.naddrs++;

  ret = nand_exec_op(chip, &op);
  if (ret)
   return ret;

  ret = nand_status_op(chip, &status);
  if (ret)
   return ret;
 } else {
  chip->legacy.cmdfunc(chip, NAND_CMD_ERASE1, -1, page);
  chip->legacy.cmdfunc(chip, NAND_CMD_ERASE2, -1, -1);

  ret = chip->legacy.waitfunc(chip);
  if (ret < 0)
   return ret;

  status = ret;
 }

 if (status & NAND_STATUS_FAIL)
  return -EIO;

 return 0;
}
EXPORT_SYMBOL_GPL(nand_erase_op);

/**
 * nand_set_features_op - Do a SET FEATURES operation
 * @chip: The NAND chip
 * @feature: feature id
 * @data: 4 bytes of data
 *
 * This function sends a SET FEATURES command and waits for the NAND to be
 * ready before returning.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

static int nand_set_features_op(struct nand_chip *chip, u8 feature,
    const void *data)
{
 const u8 *params = data;
 int i, ret;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0),
   NAND_OP_ADDR(1, &feature, NAND_COMMON_TIMING_NS(conf,
         tADL_min)),
   NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data,
           NAND_COMMON_TIMING_NS(conf,
            tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
      0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_SET_FEATURES, feature, -1);
 for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
  chip->legacy.write_byte(chip, params[i]);

 ret = chip->legacy.waitfunc(chip);
 if (ret < 0)
  return ret;

 if (ret & NAND_STATUS_FAIL)
  return -EIO;

 return 0;
}

/**
 * nand_get_features_op - Do a GET FEATURES operation
 * @chip: The NAND chip
 * @feature: feature id
 * @data: 4 bytes of data
 *
 * This function sends a GET FEATURES command and waits for the NAND to be
 * ready before returning.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

static int nand_get_features_op(struct nand_chip *chip, u8 feature,
    void *data)
{
 u8 *params = data, ddrbuf[ONFI_SUBFEATURE_PARAM_LEN * 2];
 int i;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0),
   NAND_OP_ADDR(1, &feature,
         NAND_COMMON_TIMING_NS(conf, tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max),
      NAND_COMMON_TIMING_NS(conf, tRR_min)),
   NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
          data, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  int ret;

  /* GET_FEATURE data bytes are received twice in NV-DDR mode */
  if (nand_interface_is_nvddr(conf)) {
   instrs[3].ctx.data.len *= 2;
   instrs[3].ctx.data.buf.in = ddrbuf;
  }

  ret = nand_exec_op(chip, &op);
  if (nand_interface_is_nvddr(conf)) {
   for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; i++)
    params[i] = ddrbuf[i * 2];
  }

  return ret;
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, feature, -1);
 for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
  params[i] = chip->legacy.read_byte(chip);

 return 0;
}

static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
       unsigned int delay_ns)
{
 if (nand_has_exec_op(chip)) {
  struct nand_op_instr instrs[] = {
   NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms),
      PSEC_TO_NSEC(delay_ns)),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  return nand_exec_op(chip, &op);
 }

 /* Apply delay or wait for ready/busy pin */
 if (!chip->legacy.dev_ready)
  udelay(chip->legacy.chip_delay);
 else
  nand_wait_ready(chip);

 return 0;
}

/**
 * nand_reset_op - Do a reset operation
 * @chip: The NAND chip
 *
 * This function sends a RESET command and waits for the NAND to be ready
 * before returning.
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_reset_op(struct nand_chip *chip)
{
 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_CMD(NAND_CMD_RESET,
        NAND_COMMON_TIMING_NS(conf, tWB_max)),
   NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tRST_max),
      0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  return nand_exec_op(chip, &op);
 }

 chip->legacy.cmdfunc(chip, NAND_CMD_RESET, -1, -1);

 return 0;
}
EXPORT_SYMBOL_GPL(nand_reset_op);

/**
 * nand_read_data_op - Read data from the NAND
 * @chip: The NAND chip
 * @buf: buffer used to store the data
 * @len: length of the buffer
 * @force_8bit: force 8-bit bus access
 * @check_only: do not actually run the command, only checks if the
 *              controller driver supports it
 *
 * This function does a raw data read on the bus. Usually used after launching
 * another NAND operation like nand_read_page_op().
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
        bool force_8bit, bool check_only)
{
 if (!len || (!check_only && !buf))
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  const struct nand_interface_config *conf =
   nand_get_interface_config(chip);
  struct nand_op_instr instrs[] = {
   NAND_OP_DATA_IN(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
  u8 *ddrbuf = NULL;
  int ret, i;

  instrs[0].ctx.data.force_8bit = force_8bit;

  /*
 * Parameter payloads (ID, status, features, etc) do not go
 * through the same pipeline as regular data, hence the
 * force_8bit flag must be set and this also indicates that in
 * case NV-DDR timings are being used the data will be received
 * twice.
 */

  if (force_8bit && nand_interface_is_nvddr(conf)) {
   ddrbuf = kcalloc(2, len, GFP_KERNEL);
   if (!ddrbuf)
    return -ENOMEM;

   instrs[0].ctx.data.len *= 2;
   instrs[0].ctx.data.buf.in = ddrbuf;
  }

  if (check_only) {
   ret = nand_check_op(chip, &op);
   kfree(ddrbuf);
   return ret;
  }

  ret = nand_exec_op(chip, &op);
  if (!ret && force_8bit && nand_interface_is_nvddr(conf)) {
   u8 *dst = buf;

   for (i = 0; i < len; i++)
    dst[i] = ddrbuf[i * 2];
  }

  kfree(ddrbuf);

  return ret;
 }

 if (check_only)
  return 0;

 if (force_8bit) {
  u8 *p = buf;
  unsigned int i;

  for (i = 0; i < len; i++)
   p[i] = chip->legacy.read_byte(chip);
 } else {
  chip->legacy.read_buf(chip, buf, len);
 }

 return 0;
}
EXPORT_SYMBOL_GPL(nand_read_data_op);

/**
 * nand_write_data_op - Write data from the NAND
 * @chip: The NAND chip
 * @buf: buffer containing the data to send on the bus
 * @len: length of the buffer
 * @force_8bit: force 8-bit bus access
 *
 * This function does a raw data write on the bus. Usually used after launching
 * another NAND operation like nand_write_page_begin_op().
 * This function does not select/unselect the CS line.
 *
 * Returns 0 on success, a negative error code otherwise.
 */

int nand_write_data_op(struct nand_chip *chip, const void *buf,
         unsigned int len, bool force_8bit)
{
 if (!len || !buf)
  return -EINVAL;

 if (nand_has_exec_op(chip)) {
  struct nand_op_instr instrs[] = {
   NAND_OP_DATA_OUT(len, buf, 0),
  };
  struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);

  instrs[0].ctx.data.force_8bit = force_8bit;

  return nand_exec_op(chip, &op);
 }

 if (force_8bit) {
  const u8 *p = buf;
  unsigned int i;

  for (i = 0; i < len; i++)
   chip->legacy.write_byte(chip, p[i]);
 } else {
  chip->legacy.write_buf(chip, buf, len);
 }

 return 0;
}
EXPORT_SYMBOL_GPL(nand_write_data_op);

/**
 * struct nand_op_parser_ctx - Context used by the parser
 * @instrs: array of all the instructions that must be addressed
 * @ninstrs: length of the @instrs array
 * @subop: Sub-operation to be passed to the NAND controller
 *
 * This structure is used by the core to split NAND operations into
 * sub-operations that can be handled by the NAND controller.
 */

struct nand_op_parser_ctx {
 const struct nand_op_instr *instrs;
 unsigned int ninstrs;
 struct nand_subop subop;
};

/**
 * nand_op_parser_must_split_instr - Checks if an instruction must be split
 * @pat: the parser pattern element that matches @instr
 * @instr: pointer to the instruction to check
 * @start_offset: this is an in/out parameter. If @instr has already been
 *   split, then @start_offset is the offset from which to start
 *   (either an address cycle or an offset in the data buffer).
 *   Conversely, if the function returns true (ie. instr must be
 *   split), this parameter is updated to point to the first
 *   data/address cycle that has not been taken care of.
 *
 * Some NAND controllers are limited and cannot send X address cycles with a
 * unique operation, or cannot read/write more than Y bytes at the same time.
 * In this case, split the instruction that does not fit in a single
 * controller-operation into two or more chunks.
 *
 * Returns true if the instruction must be split, false otherwise.
 * The @start_offset parameter is also updated to the offset at which the next
 * bundle of instruction must start (if an address or a data instruction).
 */

static bool
nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat,
    const struct nand_op_instr *instr,
    unsigned int *start_offset)
{
 switch (pat->type) {
 case NAND_OP_ADDR_INSTR:
  if (!pat->ctx.addr.maxcycles)
   break;

  if (instr->ctx.addr.naddrs - *start_offset >
      pat->ctx.addr.maxcycles) {
   *start_offset += pat->ctx.addr.maxcycles;
   return true;
  }
  break;

 case NAND_OP_DATA_IN_INSTR:
 case NAND_OP_DATA_OUT_INSTR:
  if (!pat->ctx.data.maxlen)
   break;

  if (instr->ctx.data.len - *start_offset >
      pat->ctx.data.maxlen) {
   *start_offset += pat->ctx.data.maxlen;
   return true;
  }
  break;

 default:
  break;
 }

 return false;
}

/**
 * nand_op_parser_match_pat - Checks if a pattern matches the instructions
 *       remaining in the parser context
 * @pat: the pattern to test
 * @ctx: the parser context structure to match with the pattern @pat
 *
 * Check if @pat matches the set or a sub-set of instructions remaining in @ctx.
 * Returns true if this is the case, false ortherwise. When true is returned,
 * @ctx->subop is updated with the set of instructions to be passed to the
 * controller driver.
 */

static bool
nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat,
    struct nand_op_parser_ctx *ctx)
{
 unsigned int instr_offset = ctx->subop.first_instr_start_off;
 const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs;
 const struct nand_op_instr *instr = ctx->subop.instrs;
 unsigned int i, ninstrs;

 for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) {
  /*
 * The pattern instruction does not match the operation
 * instruction. If the instruction is marked optional in the
 * pattern definition, we skip the pattern element and continue
 * to the next one. If the element is mandatory, there's no
 * match and we can return false directly.
 */

  if (instr->type != pat->elems[i].type) {
   if (!pat->elems[i].optional)
    return false;

   continue;
  }

  /*
 * Now check the pattern element constraints. If the pattern is
 * not able to handle the whole instruction in a single step,
 * we have to split it.
 * The last_instr_end_off value comes back updated to point to
 * the position where we have to split the instruction (the
 * start of the next subop chunk).
 */

  if (nand_op_parser_must_split_instr(&pat->elems[i], instr,
          &instr_offset)) {
   ninstrs++;
   i++;
   break;
  }

  instr++;
  ninstrs++;
  instr_offset = 0;
 }

 /*
 * This can happen if all instructions of a pattern are optional.
 * Still, if there's not at least one instruction handled by this
 * pattern, this is not a match, and we should try the next one (if
 * any).
 */

 if (!ninstrs)
  return false;

 /*
 * We had a match on the pattern head, but the pattern may be longer
 * than the instructions we're asked to execute. We need to make sure
 * there's no mandatory elements in the pattern tail.
 */

 for (; i < pat->nelems; i++) {
  if (!pat->elems[i].optional)
   return false;
 }

 /*
 * We have a match: update the subop structure accordingly and return
 * true.
 */

 ctx->subop.ninstrs = ninstrs;
 ctx->subop.last_instr_end_off = instr_offset;

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

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.20 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.