Merge tag 'nand/for-4.13' into MTD
From Boris: """ This pull request contains the following core changes: * addition of on-ecc support to Micron driver * addition of helpers to help drivers choose most appropriate ECC settings * deletion of dead-code (cached programming and ->errstat() hook) * make sure drivers that do not support the SET/GET FEATURES command return ENOTSUPP use a dummy ->set/get_features implementation returning -ENOTSUPP (required for Micron on-die ECC) * change the semantic of ecc->write_page() for drivers setting the NAND_ECC_CUSTOM_PAGE_ACCESS flag * support exiting 'GET STATUS' command in default ->cmdfunc() implementations * change the prototype of ->setup_data_interface() A bunch of driver related changes: * various cleanup, fixes and improvements of the MTK driver * OMAP DT bindings fixes * support for ->setup_data_interface() in the fsmc driver * support for imx7 in the gpmi driver * finalization of the denali driver rework (thanks to Masahiro for the work he's done on this driver) * fix "bitflips in erased pages" handling in the ifc driver * addition of PM ops and dynamic timing configuration to the atmel driver And as usual we also have a few minor cleanup/fixes/improvements patches across the subsystem. """
This commit is contained in:
@@ -753,6 +753,16 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
case NAND_CMD_READ0:
|
||||
/*
|
||||
* READ0 is sometimes used to exit GET STATUS mode. When this
|
||||
* is the case no address cycles are requested, and we can use
|
||||
* this information to detect that we should not wait for the
|
||||
* device to be ready.
|
||||
*/
|
||||
if (column == -1 && page_addr == -1)
|
||||
return;
|
||||
|
||||
default:
|
||||
/*
|
||||
* If we don't have access to the busy pin, we apply the given
|
||||
@@ -887,6 +897,15 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||
return;
|
||||
|
||||
case NAND_CMD_READ0:
|
||||
/*
|
||||
* READ0 is sometimes used to exit GET STATUS mode. When this
|
||||
* is the case no address cycles are requested, and we can use
|
||||
* this information to detect that READSTART should not be
|
||||
* issued.
|
||||
*/
|
||||
if (column == -1 && page_addr == -1)
|
||||
return;
|
||||
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
|
||||
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
|
||||
@@ -1042,12 +1061,13 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
/**
|
||||
* nand_reset_data_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_data_interface(struct nand_chip *chip)
|
||||
static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_data_interface *conf;
|
||||
@@ -1071,7 +1091,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
|
||||
*/
|
||||
|
||||
conf = nand_get_default_data_interface();
|
||||
ret = chip->setup_data_interface(mtd, conf, false);
|
||||
ret = chip->setup_data_interface(mtd, chipnr, conf);
|
||||
if (ret)
|
||||
pr_err("Failed to configure data interface to SDR timing mode 0\n");
|
||||
|
||||
@@ -1081,6 +1101,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
|
||||
/**
|
||||
* nand_setup_data_interface - Setup the best data interface and timings
|
||||
* @chip: The NAND chip
|
||||
* @chipnr: Internal die id
|
||||
*
|
||||
* Find and configure the best data interface and NAND timings supported by
|
||||
* the chip and the driver.
|
||||
@@ -1090,7 +1111,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
|
||||
*
|
||||
* Returns 0 for success or negative error code otherwise.
|
||||
*/
|
||||
static int nand_setup_data_interface(struct nand_chip *chip)
|
||||
static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
@@ -1114,7 +1135,7 @@ static int nand_setup_data_interface(struct nand_chip *chip)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = chip->setup_data_interface(mtd, chip->data_interface, false);
|
||||
ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -1165,8 +1186,10 @@ static int nand_init_data_interface(struct nand_chip *chip)
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = chip->setup_data_interface(mtd, chip->data_interface,
|
||||
true);
|
||||
/* Pass -1 to only */
|
||||
ret = chip->setup_data_interface(mtd,
|
||||
NAND_DATA_IFACE_CHECK_ONLY,
|
||||
chip->data_interface);
|
||||
if (!ret) {
|
||||
chip->onfi_timing_mode_default = mode;
|
||||
break;
|
||||
@@ -1193,7 +1216,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int ret;
|
||||
|
||||
ret = nand_reset_data_interface(chip);
|
||||
ret = nand_reset_data_interface(chip, chipnr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1206,7 +1229,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
|
||||
chip->select_chip(mtd, -1);
|
||||
|
||||
chip->select_chip(mtd, chipnr);
|
||||
ret = nand_setup_data_interface(chip);
|
||||
ret = nand_setup_data_interface(chip, chipnr);
|
||||
chip->select_chip(mtd, -1);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1421,7 +1444,10 @@ static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
|
||||
|
||||
for (; len >= sizeof(long);
|
||||
len -= sizeof(long), bitmap += sizeof(long)) {
|
||||
weight = hweight_long(*((unsigned long *)bitmap));
|
||||
unsigned long d = *((unsigned long *)bitmap);
|
||||
if (d == ~0UL)
|
||||
continue;
|
||||
weight = hweight_long(d);
|
||||
bitflips += BITS_PER_LONG - weight;
|
||||
if (unlikely(bitflips > bitflips_threshold))
|
||||
return -EBADMSG;
|
||||
@@ -1524,14 +1550,15 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
||||
*/
|
||||
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
chip->read_buf(mtd, buf, mtd->writesize);
|
||||
if (oob_required)
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_read_page_raw);
|
||||
|
||||
/**
|
||||
* nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
|
||||
@@ -2469,8 +2496,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
||||
*/
|
||||
static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page)
|
||||
int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
chip->write_buf(mtd, buf, mtd->writesize);
|
||||
if (oob_required)
|
||||
@@ -2478,6 +2505,7 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_write_page_raw);
|
||||
|
||||
/**
|
||||
* nand_write_page_raw_syndrome - [INTERN] raw page write function
|
||||
@@ -2715,7 +2743,7 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
||||
*/
|
||||
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw)
|
||||
int oob_required, int page, int raw)
|
||||
{
|
||||
int status, subpage;
|
||||
|
||||
@@ -2741,30 +2769,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Cached progamming disabled for now. Not sure if it's worth the
|
||||
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).
|
||||
*/
|
||||
cached = 0;
|
||||
if (nand_standard_page_accessors(&chip->ecc)) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
|
||||
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
|
||||
|
||||
if (nand_standard_page_accessors(&chip->ecc))
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
/*
|
||||
* See if operation failed and additional status checks are
|
||||
* available.
|
||||
*/
|
||||
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
|
||||
status = chip->errstat(mtd, chip, FL_WRITING, status,
|
||||
page);
|
||||
|
||||
if (status & NAND_STATUS_FAIL)
|
||||
return -EIO;
|
||||
} else {
|
||||
chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2872,7 +2882,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
|
||||
while (1) {
|
||||
int bytes = mtd->writesize;
|
||||
int cached = writelen > bytes && page != blockmask;
|
||||
uint8_t *wbuf = buf;
|
||||
int use_bufpoi;
|
||||
int part_pagewr = (column || writelen < mtd->writesize);
|
||||
@@ -2890,7 +2899,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
if (use_bufpoi) {
|
||||
pr_debug("%s: using write bounce buffer for buf@%p\n",
|
||||
__func__, buf);
|
||||
cached = 0;
|
||||
if (part_pagewr)
|
||||
bytes = min_t(int, bytes - column, writelen);
|
||||
chip->pagebuf = -1;
|
||||
@@ -2909,7 +2917,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||
}
|
||||
|
||||
ret = nand_write_page(mtd, chip, column, bytes, wbuf,
|
||||
oob_required, page, cached,
|
||||
oob_required, page,
|
||||
(ops->mode == MTD_OPS_RAW));
|
||||
if (ret)
|
||||
break;
|
||||
@@ -3225,14 +3233,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||
|
||||
status = chip->erase(mtd, page & chip->pagemask);
|
||||
|
||||
/*
|
||||
* See if operation failed and additional status checks are
|
||||
* available
|
||||
*/
|
||||
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
|
||||
status = chip->errstat(mtd, chip, FL_ERASING,
|
||||
status, page);
|
||||
|
||||
/* See if block erase succeeded */
|
||||
if (status & NAND_STATUS_FAIL) {
|
||||
pr_debug("%s: failed erase, page 0x%08x\n",
|
||||
@@ -3418,6 +3418,25 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_onfi_get_set_features_notsupp - set/get features stub returning
|
||||
* -ENOTSUPP
|
||||
* @mtd: MTD device structure
|
||||
* @chip: nand chip info structure
|
||||
* @addr: feature address.
|
||||
* @subfeature_param: the subfeature parameters, a four bytes array.
|
||||
*
|
||||
* Should be used by NAND controller drivers that do not support the SET/GET
|
||||
* FEATURES operations.
|
||||
*/
|
||||
int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, int addr,
|
||||
u8 *subfeature_param)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp);
|
||||
|
||||
/**
|
||||
* nand_suspend - [MTD Interface] Suspend the NAND flash
|
||||
* @mtd: MTD device structure
|
||||
@@ -4177,6 +4196,7 @@ static const char * const nand_ecc_modes[] = {
|
||||
[NAND_ECC_HW] = "hw",
|
||||
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
|
||||
[NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
|
||||
[NAND_ECC_ON_DIE] = "on-die",
|
||||
};
|
||||
|
||||
static int of_get_nand_ecc_mode(struct device_node *np)
|
||||
@@ -4371,7 +4391,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
|
||||
* For the other dies, nand_reset() will automatically switch to the
|
||||
* best mode for us.
|
||||
*/
|
||||
ret = nand_setup_data_interface(chip);
|
||||
ret = nand_setup_data_interface(chip, 0);
|
||||
if (ret)
|
||||
goto err_nand_init;
|
||||
|
||||
@@ -4509,6 +4529,226 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_check_ecc_caps - check the sanity of preset ECC settings
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* When ECC step size and strength are already set, check if they are supported
|
||||
* by the controller and the calculated ECC bytes fit within the chip's OOB.
|
||||
* On success, the calculated ECC bytes is set.
|
||||
*/
|
||||
int nand_check_ecc_caps(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int preset_step = chip->ecc.size;
|
||||
int preset_strength = chip->ecc.strength;
|
||||
int nsteps, ecc_bytes;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (!preset_step || !preset_strength)
|
||||
return -ENODATA;
|
||||
|
||||
nsteps = mtd->writesize / preset_step;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
|
||||
if (stepinfo->stepsize != preset_step)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
if (stepinfo->strengths[j] != preset_strength)
|
||||
continue;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(preset_step,
|
||||
preset_strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
return ecc_bytes;
|
||||
|
||||
if (ecc_bytes * nsteps > oobavail) {
|
||||
pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
|
||||
preset_step, preset_strength);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
chip->ecc.bytes = ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
|
||||
preset_step, preset_strength);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
|
||||
|
||||
/**
|
||||
* nand_match_ecc_req - meet the chip's requirement with least ECC bytes
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC engine caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* If a chip's ECC requirement is provided, try to meet it with the least
|
||||
* number of ECC bytes (i.e. with the largest number of OOB-free bytes).
|
||||
* On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_match_ecc_req(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int req_step = chip->ecc_step_ds;
|
||||
int req_strength = chip->ecc_strength_ds;
|
||||
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
|
||||
int best_step, best_strength, best_ecc_bytes;
|
||||
int best_ecc_bytes_total = INT_MAX;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* No information provided by the NAND chip */
|
||||
if (!req_step || !req_strength)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* number of correctable bits the chip requires in a page */
|
||||
req_corr = mtd->writesize / req_step * req_strength;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
step_size = stepinfo->stepsize;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
strength = stepinfo->strengths[j];
|
||||
|
||||
/*
|
||||
* If both step size and strength are smaller than the
|
||||
* chip's requirement, it is not easy to compare the
|
||||
* resulted reliability.
|
||||
*/
|
||||
if (step_size < req_step && strength < req_strength)
|
||||
continue;
|
||||
|
||||
if (mtd->writesize % step_size)
|
||||
continue;
|
||||
|
||||
nsteps = mtd->writesize / step_size;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
continue;
|
||||
ecc_bytes_total = ecc_bytes * nsteps;
|
||||
|
||||
if (ecc_bytes_total > oobavail ||
|
||||
strength * nsteps < req_corr)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We assume the best is to meet the chip's requrement
|
||||
* with the least number of ECC bytes.
|
||||
*/
|
||||
if (ecc_bytes_total < best_ecc_bytes_total) {
|
||||
best_ecc_bytes_total = ecc_bytes_total;
|
||||
best_step = step_size;
|
||||
best_strength = strength;
|
||||
best_ecc_bytes = ecc_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_ecc_bytes_total == INT_MAX)
|
||||
return -ENOTSUPP;
|
||||
|
||||
chip->ecc.size = best_step;
|
||||
chip->ecc.strength = best_strength;
|
||||
chip->ecc.bytes = best_ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_match_ecc_req);
|
||||
|
||||
/**
|
||||
* nand_maximize_ecc - choose the max ECC strength available
|
||||
* @chip: nand chip info structure
|
||||
* @caps: ECC engine caps info structure
|
||||
* @oobavail: OOB size that the ECC engine can use
|
||||
*
|
||||
* Choose the max ECC strength that is supported on the controller, and can fit
|
||||
* within the chip's OOB. On success, the chosen ECC settings are set.
|
||||
*/
|
||||
int nand_maximize_ecc(struct nand_chip *chip,
|
||||
const struct nand_ecc_caps *caps, int oobavail)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
const struct nand_ecc_step_info *stepinfo;
|
||||
int step_size, strength, nsteps, ecc_bytes, corr;
|
||||
int best_corr = 0;
|
||||
int best_step = 0;
|
||||
int best_strength, best_ecc_bytes;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON(oobavail < 0))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < caps->nstepinfos; i++) {
|
||||
stepinfo = &caps->stepinfos[i];
|
||||
step_size = stepinfo->stepsize;
|
||||
|
||||
/* If chip->ecc.size is already set, respect it */
|
||||
if (chip->ecc.size && step_size != chip->ecc.size)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < stepinfo->nstrengths; j++) {
|
||||
strength = stepinfo->strengths[j];
|
||||
|
||||
if (mtd->writesize % step_size)
|
||||
continue;
|
||||
|
||||
nsteps = mtd->writesize / step_size;
|
||||
|
||||
ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
|
||||
if (WARN_ON_ONCE(ecc_bytes < 0))
|
||||
continue;
|
||||
|
||||
if (ecc_bytes * nsteps > oobavail)
|
||||
continue;
|
||||
|
||||
corr = strength * nsteps;
|
||||
|
||||
/*
|
||||
* If the number of correctable bits is the same,
|
||||
* bigger step_size has more reliability.
|
||||
*/
|
||||
if (corr > best_corr ||
|
||||
(corr == best_corr && step_size > best_step)) {
|
||||
best_corr = corr;
|
||||
best_step = step_size;
|
||||
best_strength = strength;
|
||||
best_ecc_bytes = ecc_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!best_corr)
|
||||
return -ENOTSUPP;
|
||||
|
||||
chip->ecc.size = best_step;
|
||||
chip->ecc.strength = best_strength;
|
||||
chip->ecc.bytes = best_ecc_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_maximize_ecc);
|
||||
|
||||
/*
|
||||
* Check if the chip configuration meet the datasheet requirements.
|
||||
|
||||
@@ -4730,6 +4970,18 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
}
|
||||
break;
|
||||
|
||||
case NAND_ECC_ON_DIE:
|
||||
if (!ecc->read_page || !ecc->write_page) {
|
||||
WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
if (!ecc->read_oob)
|
||||
ecc->read_oob = nand_read_oob_std;
|
||||
if (!ecc->write_oob)
|
||||
ecc->write_oob = nand_write_oob_std;
|
||||
break;
|
||||
|
||||
case NAND_ECC_NONE:
|
||||
pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
|
||||
ecc->read_page = nand_read_page_raw;
|
||||
@@ -4770,6 +5022,11 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
goto err_free;
|
||||
}
|
||||
ecc->total = ecc->steps * ecc->bytes;
|
||||
if (ecc->total > mtd->oobsize) {
|
||||
WARN(1, "Total number of ECC bytes exceeded oobsize\n");
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* The number of bytes available for a client to place data into
|
||||
|
Reference in New Issue
Block a user