* [PATCH v3 23/40] mtd: nand: ecc: Turn the software Hamming implementation generic
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Add helpers in the raw NAND core to call the generic functions that
will be re-used by the SPI-NAND layer.
While at it, do some cleanup in the file and its header.
There are two drivers (not even raw NAND controller drivers) using the
bare helpers ecc_sw_hamming_calculate/correct():
mtd_nandecctest.c and sm_ftl.c. It would be nice to find another way
to call these functions and finish to clean the driver.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/ecc-sw-hamming.c | 121 +++++++++++-------------
drivers/mtd/nand/raw/cs553x_nand.c | 3 +-
drivers/mtd/nand/raw/fsmc_nand.c | 2 +-
drivers/mtd/nand/raw/lpc32xx_slc.c | 2 +-
drivers/mtd/nand/raw/nand_base.c | 88 ++++++++++++++++-
drivers/mtd/nand/raw/ndfc.c | 3 +-
drivers/mtd/nand/raw/sharpsl.c | 2 +-
drivers/mtd/nand/raw/tmio_nand.c | 6 +-
drivers/mtd/nand/raw/txx9ndfmc.c | 4 +-
drivers/mtd/sm_ftl.c | 28 +++---
drivers/mtd/tests/mtd_nandecctest.c | 29 +++---
include/linux/mtd/nand-ecc-sw-hamming.h | 50 +++++-----
include/linux/mtd/rawnand.h | 9 ++
13 files changed, 211 insertions(+), 136 deletions(-)
diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c
index 68812b62aa6c..8494e68814bc 100644
--- a/drivers/mtd/nand/ecc-sw-hamming.c
+++ b/drivers/mtd/nand/ecc-sw-hamming.c
@@ -17,8 +17,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <asm/byteorder.h>
@@ -75,7 +73,7 @@ static const char bitsperbyte[256] = {
* addressbits is a lookup table to filter out the bits from the xor-ed
* ECC data that identify the faulty location.
* this is only used for repairing parity
- * see the comments in nand_correct_data for more details
+ * see the comments in nand_ecc_sw_hamming_correct for more details
*/
static const char addressbits[256] = {
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01,
@@ -112,30 +110,23 @@ static const char addressbits[256] = {
0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f
};
-/**
- * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
- * block
- * @buf: input buffer with raw data
- * @eccsize: data bytes per ECC step (256 or 512)
- * @code: output buffer with ECC
- * @sm_order: Smart Media byte ordering
- */
-void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
- unsigned char *code, bool sm_order)
+int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
+ unsigned char *code, bool sm_order)
{
- int i;
- const uint32_t *bp = (uint32_t *)buf;
- /* 256 or 512 bytes/ecc */
- const uint32_t eccsize_mult = eccsize >> 8;
- uint32_t cur; /* current value in buffer */
+ const u32 *bp = (uint32_t *)buf;
+ const u32 eccsize_mult = step_size >> 8;
+ /* current value in buffer */
+ u32 cur;
/* rp0..rp15..rp17 are the various accumulated parities (per byte) */
- uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
- uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
- uint32_t uninitialized_var(rp17); /* to make compiler happy */
- uint32_t par; /* the cumulative parity for all data */
- uint32_t tmppar; /* the cumulative parity for this iteration;
- for rp12, rp14 and rp16 at the end of the
- loop */
+ u32 rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
+ u32 rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16;
+ /* Make the compiler happy */
+ u32 uninitialized_var(rp17);
+ /* Cumulative parity for all data */
+ u32 par;
+ /* Cumulative parity at the end of the loop (rp12, rp14, rp16) */
+ u32 tmppar;
+ int i;
par = 0;
rp4 = 0;
@@ -356,45 +347,36 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
(invparity[par & 0x55] << 2) |
(invparity[rp17] << 1) |
(invparity[rp16] << 0);
-}
-EXPORT_SYMBOL(__nand_calculate_ecc);
-
-/**
- * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
- * block
- * @chip: NAND chip object
- * @buf: input buffer with raw data
- * @code: output buffer with ECC
- */
-int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
- unsigned char *code)
-{
- bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
-
- __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order);
return 0;
}
-EXPORT_SYMBOL(nand_calculate_ecc);
+EXPORT_SYMBOL(ecc_sw_hamming_calculate);
/**
- * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
- * @eccsize: data bytes per ECC step (256 or 512)
- * @sm_order: Smart Media byte order
+ * nand_ecc_sw_hamming_calculate - Calculate 3-byte ECC for 256/512-byte block
*
- * Detect and correct a 1 bit error for eccsize byte block
+ * @nand: NAND device
+ * @buf: Input buffer with raw data
+ * @code: Output buffer with ECC
*/
-int __nand_correct_data(unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc,
- unsigned int eccsize, bool sm_order)
+int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
+ const unsigned char *buf, unsigned char *code)
{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int step_size = nand->ecc.ctx.conf.step_size;
+
+ return ecc_sw_hamming_calculate(buf, step_size, code,
+ engine_conf->sm_order);
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate);
+
+int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc,
+ unsigned char *calc_ecc, unsigned int step_size,
+ bool sm_order)
+{
+ const u32 eccsize_mult = step_size >> 8;
unsigned char b0, b1, b2, bit_addr;
unsigned int byte_addr;
- /* 256 or 512 bytes/ecc */
- const uint32_t eccsize_mult = eccsize >> 8;
/*
* b0 to b2 indicate which bit is faulty (if any)
@@ -458,27 +440,30 @@ int __nand_correct_data(unsigned char *buf,
pr_err("%s: uncorrectable ECC error\n", __func__);
return -EBADMSG;
}
-EXPORT_SYMBOL(__nand_correct_data);
+EXPORT_SYMBOL(ecc_sw_hamming_correct);
/**
- * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @chip: NAND chip object
- * @buf: raw data read from the chip
- * @read_ecc: ECC from the chip
- * @calc_ecc: the ECC calculated from raw data
+ * nand_ecc_sw_hamming_correct - Detect and correct bit error(s)
*
- * Detect and correct a 1 bit error for 256/512 byte block
+ * @nand: NAND device
+ * @buf: Raw data read from the chip
+ * @read_ecc: ECC bytes read from the chip
+ * @calc_ecc: ECC calculated from the raw data
+ *
+ * Detect and correct up to 1 bit error per 256/512-byte block.
*/
-int nand_correct_data(struct nand_chip *chip, unsigned char *buf,
- unsigned char *read_ecc, unsigned char *calc_ecc)
+int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc)
{
- bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int step_size = nand->ecc.ctx.conf.step_size;
- return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size,
- sm_order);
+ return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size,
+ engine_conf->sm_order);
}
-EXPORT_SYMBOL(nand_correct_data);
+EXPORT_SYMBOL(nand_ecc_sw_hamming_correct);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
-MODULE_DESCRIPTION("Generic NAND ECC support");
+MODULE_DESCRIPTION("NAND software Hamming ECC support");
diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c
index a2524a78ff59..06f960ab6fd4 100644
--- a/drivers/mtd/nand/raw/cs553x_nand.c
+++ b/drivers/mtd/nand/raw/cs553x_nand.c
@@ -19,7 +19,6 @@
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <asm/msr.h>
@@ -215,7 +214,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
this->ecc.bytes = 3;
this->ecc.hwctl = cs_enable_hwecc;
this->ecc.calculate = cs_calculate_ecc;
- this->ecc.correct = nand_correct_data;
+ this->ecc.correct = rawnand_sw_hamming_correct;
this->ecc.strength = 1;
/* Enable the following for a flash based bad block table */
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index 61e87e410a62..fe25b1123819 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -900,7 +900,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
case NAND_HW_ECC_ENGINE:
dev_info(host->dev, "Using 1-bit HW ECC scheme\n");
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
- nand->ecc.correct = nand_correct_data;
+ nand->ecc.correct = rawnand_sw_hamming_correct;
nand->ecc.bytes = 3;
nand->ecc.strength = 1;
nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index 1c6f0be727f6..e1c7600d1dbb 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -893,7 +893,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome;
chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome;
chip->ecc.calculate = lpc32xx_nand_ecc_calculate;
- chip->ecc.correct = nand_correct_data;
+ chip->ecc.correct = rawnand_sw_hamming_correct;
chip->ecc.strength = 1;
chip->ecc.hwctl = lpc32xx_nand_ecc_enable;
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index ee62c2f774ba..7734f18ddc59 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -4832,6 +4832,75 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
kfree(chip->parameters.onfi);
}
+int rawnand_sw_hamming_init(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct nand_ecc_sw_hamming_conf *engine_conf;
+ struct nand_device *base = &chip->base;
+
+ base->ecc.user_conf.provider = NAND_SOFT_ECC_ENGINE;
+ base->ecc.user_conf.algo = NAND_ECC_HAMMING;
+ base->ecc.user_conf.strength = chip->ecc.strength;
+ base->ecc.user_conf.step_size = chip->ecc.size;
+
+ if (base->ecc.user_conf.strength != 1 ||
+ (base->ecc.user_conf.step_size != 256 &&
+ base->ecc.user_conf.step_size != 512)) {
+ pr_err("%s: unsupported strength or step size\n", __func__);
+ return -EINVAL;
+ }
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ engine_conf->code_size = 3;
+ engine_conf->nsteps = mtd->writesize / base->ecc.user_conf.step_size;
+
+ if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER)
+ engine_conf->sm_order = true;
+
+ base->ecc.ctx.priv = engine_conf;
+
+ chip->ecc.size = base->ecc.ctx.conf.step_size;
+ chip->ecc.strength = base->ecc.ctx.conf.strength;
+ chip->ecc.total = base->ecc.ctx.total;
+ chip->ecc.steps = engine_conf->nsteps;
+ chip->ecc.bytes = engine_conf->code_size;
+
+ return 0;
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_init);
+
+int rawnand_sw_hamming_calculate(struct nand_chip *chip,
+ const unsigned char *buf,
+ unsigned char *code)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_hamming_calculate(base, buf, code);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_calculate);
+
+int rawnand_sw_hamming_correct(struct nand_chip *chip,
+ unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc)
+{
+ struct nand_device *base = &chip->base;
+
+ return nand_ecc_sw_hamming_correct(base, buf, read_ecc, calc_ecc);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_correct);
+
+void rawnand_sw_hamming_cleanup(struct nand_chip *chip)
+{
+ struct nand_device *base = &chip->base;
+
+ kfree(base->ecc.ctx.priv);
+}
+EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
+
int rawnand_sw_bch_init(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
@@ -4905,8 +4974,8 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
switch (ecc->algo) {
case NAND_ECC_HAMMING:
- ecc->calculate = nand_calculate_ecc;
- ecc->correct = nand_correct_data;
+ ecc->calculate = rawnand_sw_hamming_calculate;
+ ecc->correct = rawnand_sw_hamming_correct;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
@@ -4922,6 +4991,12 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC))
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
+ ret = rawnand_sw_hamming_init(chip);
+ if (ret) {
+ WARN(1, "Hamming ECC initialization failed!\n");
+ return ret;
+ }
+
return 0;
case NAND_ECC_BCH:
if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
@@ -5697,9 +5772,12 @@ EXPORT_SYMBOL(nand_scan_with_ids);
*/
void nand_cleanup(struct nand_chip *chip)
{
- if (chip->ecc.mode == NAND_SOFT_ECC_ENGINE &&
- chip->ecc.algo == NAND_ECC_BCH)
- rawnand_sw_bch_cleanup(chip);
+ if (chip->ecc.mode == NAND_SOFT_ECC_ENGINE) {
+ if (chip->ecc.algo == NAND_ECC_HAMMING)
+ rawnand_sw_hamming_cleanup(chip);
+ else if (chip->ecc.algo == NAND_ECC_BCH)
+ rawnand_sw_bch_cleanup(chip);
+ }
/* Free bad block table memory */
kfree(chip->bbt);
diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c
index e4076e6c9ea6..3d076b22d57b 100644
--- a/drivers/mtd/nand/raw/ndfc.c
+++ b/drivers/mtd/nand/raw/ndfc.c
@@ -18,7 +18,6 @@
*/
#include <linux/module.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/ndfc.h>
#include <linux/slab.h>
@@ -146,7 +145,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
chip->controller = &ndfc->ndfc_control;
chip->legacy.read_buf = ndfc_read_buf;
chip->legacy.write_buf = ndfc_write_buf;
- chip->ecc.correct = nand_correct_data;
+ chip->ecc.correct = ecc_sw_hamming_correct;
chip->ecc.hwctl = ndfc_enable_hwecc;
chip->ecc.calculate = ndfc_calculate_ecc;
chip->ecc.mode = NAND_HW_ECC_ENGINE;
diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
index 8a894bd3ceda..8222239c21f3 100644
--- a/drivers/mtd/nand/raw/sharpsl.c
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -164,7 +164,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
this->badblock_pattern = data->badblock_pattern;
this->ecc.hwctl = sharpsl_nand_enable_hwecc;
this->ecc.calculate = sharpsl_nand_calculate_ecc;
- this->ecc.correct = nand_correct_data;
+ this->ecc.correct = rawnand_sw_hamming_correct;
/* Scan to find existence of the device */
err = nand_scan(this, 1);
diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c
index 2a5cbc3c3ffa..f0bacbad1533 100644
--- a/drivers/mtd/nand/raw/tmio_nand.c
+++ b/drivers/mtd/nand/raw/tmio_nand.c
@@ -292,11 +292,11 @@ static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf,
int r0, r1;
/* assume ecc.size = 512 and ecc.bytes = 6 */
- r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false);
+ r0 = rawnand_sw_hamming_correct(chip, buf, read_ecc, calc_ecc);
if (r0 < 0)
return r0;
- r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256,
- false);
+ r1 = rawnand_sw_hamming_correct(chip, buf + 256, read_ecc + 3,
+ calc_ecc + 3);
if (r1 < 0)
return r1;
return r0 + r1;
diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c
index 98be4949080d..1466791ecb5d 100644
--- a/drivers/mtd/nand/raw/txx9ndfmc.c
+++ b/drivers/mtd/nand/raw/txx9ndfmc.c
@@ -194,8 +194,8 @@ static int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf,
int stat;
for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {
- stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256,
- false);
+ stat = rawnand_sw_hamming_correct(chip, buf, read_ecc,
+ calc_ecc);
if (stat < 0)
return stat;
corrected += stat;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 72cc031ea3bb..d5e6be9559d8 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -216,20 +216,19 @@ static void sm_break_offset(struct sm_ftl *ftl, loff_t loffset,
static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
uint8_t ecc[3];
- __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
- if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0)
+ ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order);
+ if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc1, SM_SMALL_PAGE,
+ sm_order) < 0)
return -EIO;
buffer += SM_SMALL_PAGE;
- __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
- if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0)
+ ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order);
+ if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc2, SM_SMALL_PAGE,
+ sm_order) < 0)
return -EIO;
return 0;
}
@@ -368,6 +367,7 @@ static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf,
int zone, int block, int lba,
unsigned long invalid_bitmap)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
struct sm_oob oob;
int boffset;
int retry = 0;
@@ -394,13 +394,13 @@ static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf,
}
if (ftl->smallpagenand) {
- __nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE,
- oob.ecc1,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
+ ecc_sw_hamming_calculate(buf + boffset,
+ SM_SMALL_PAGE, oob.ecc1,
+ sm_order);
- __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE,
- SM_SMALL_PAGE, oob.ecc2,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
+ ecc_sw_hamming_calculate(buf + boffset + SM_SMALL_PAGE,
+ SM_SMALL_PAGE, oob.ecc2,
+ sm_order);
}
if (!sm_write_sector(ftl, zone, block, boffset,
buf + boffset, &oob))
diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c
index e92e3fb287b6..c4f271314f52 100644
--- a/drivers/mtd/tests/mtd_nandecctest.c
+++ b/drivers/mtd/tests/mtd_nandecctest.c
@@ -119,13 +119,13 @@ static void no_bit_error(void *error_data, void *error_ecc,
static int no_bit_error_verify(void *error_data, void *error_ecc,
void *correct_data, const size_t size)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
unsigned char calc_ecc[3];
int ret;
- __nand_calculate_ecc(error_data, size, calc_ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
- ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
+ ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
+ ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
+ sm_order);
if (ret == 0 && !memcmp(correct_data, error_data, size))
return 0;
@@ -149,13 +149,13 @@ static void single_bit_error_in_ecc(void *error_data, void *error_ecc,
static int single_bit_error_correct(void *error_data, void *error_ecc,
void *correct_data, const size_t size)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
unsigned char calc_ecc[3];
int ret;
- __nand_calculate_ecc(error_data, size, calc_ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
- ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
+ ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
+ ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
+ sm_order);
if (ret == 1 && !memcmp(correct_data, error_data, size))
return 0;
@@ -186,13 +186,13 @@ static void double_bit_error_in_ecc(void *error_data, void *error_ecc,
static int double_bit_error_detect(void *error_data, void *error_ecc,
void *correct_data, const size_t size)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
unsigned char calc_ecc[3];
int ret;
- __nand_calculate_ecc(error_data, size, calc_ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
- ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
+ ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order);
+ ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size,
+ sm_order);
return (ret == -EBADMSG) ? 0 : -EINVAL;
}
@@ -248,6 +248,7 @@ static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data,
static int nand_ecc_test_run(const size_t size)
{
+ bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC);
int i;
int err = 0;
void *error_data;
@@ -266,9 +267,7 @@ static int nand_ecc_test_run(const size_t size)
}
prandom_bytes(correct_data, size);
- __nand_calculate_ecc(correct_data, size, correct_ecc,
- IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC));
-
+ ecc_sw_hamming_calculate(correct_data, size, correct_ecc, sm_order);
for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) {
nand_ecc_test[i].prepare(error_data, error_ecc,
correct_data, correct_ecc, size);
diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h
index 85e9a929b5f9..aed46e43d2f6 100644
--- a/include/linux/mtd/nand-ecc-sw-hamming.h
+++ b/include/linux/mtd/nand-ecc-sw-hamming.h
@@ -10,30 +10,36 @@
#ifndef __MTD_NAND_ECC_SW_HAMMING_H__
#define __MTD_NAND_ECC_SW_HAMMING_H__
-struct nand_chip;
+#include <linux/mtd/nand.h>
-/*
- * Calculate 3 byte ECC code for eccsize byte block
+/**
+ * struct nand_ecc_sw_hamming_conf - private software Hamming ECC engine structure
+ * @reqooblen: Save the actual user OOB length requested before overwriting it
+ * @code_size: Number of bytes needed to store a code (one code per step)
+ * @nsteps: Number of steps
+ * @calc_buf: Buffer to use when calculating ECC bytes
+ * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip
+ * @sm_order: Smart Media special ordering
*/
-void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize,
- u_char *ecc_code, bool sm_order);
+struct nand_ecc_sw_hamming_conf {
+ unsigned int reqooblen;
+ unsigned int code_size;
+ unsigned int nsteps;
+ u8 *calc_buf;
+ u8 *code_buf;
+ unsigned int sm_order;
+};
-/*
- * Calculate 3 byte ECC code for 256/512 byte block
- */
-int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
- u_char *ecc_code);
-
-/*
- * Detect and correct a 1 bit error for eccsize byte block
- */
-int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc,
- unsigned int eccsize, bool sm_order);
-
-/*
- * Detect and correct a 1 bit error for 256/512 byte block
- */
-int nand_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc,
- u_char *calc_ecc);
+int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
+ unsigned char *code, bool sm_order);
+int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
+ const unsigned char *buf,
+ unsigned char *code);
+int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc,
+ unsigned char *calc_ecc, unsigned int step_size,
+ bool sm_order);
+int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc);
#endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index b15015b7be3c..c343cf2321b2 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -1272,6 +1272,15 @@ static inline int nand_opcode_8bits(unsigned int command)
return 0;
}
+int rawnand_sw_hamming_init(struct nand_chip *chip);
+int rawnand_sw_hamming_calculate(struct nand_chip *chip,
+ const unsigned char *buf,
+ unsigned char *code);
+int rawnand_sw_hamming_correct(struct nand_chip *chip,
+ unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc);
+void rawnand_sw_hamming_cleanup(struct nand_chip *chip);
int rawnand_sw_bch_init(struct nand_chip *chip);
int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc);
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 24/40] mtd: nand: Remove useless include about software Hamming ECC
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Most of the includes are simply useless, drop them.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
arch/arm/mach-s3c24xx/common-smdk.c | 1 -
arch/arm/mach-s3c24xx/mach-anubis.c | 1 -
arch/arm/mach-s3c24xx/mach-at2440evb.c | 1 -
arch/arm/mach-s3c24xx/mach-bast.c | 1 -
arch/arm/mach-s3c24xx/mach-gta02.c | 1 -
arch/arm/mach-s3c24xx/mach-jive.c | 1 -
arch/arm/mach-s3c24xx/mach-mini2440.c | 1 -
arch/arm/mach-s3c24xx/mach-osiris.c | 1 -
arch/arm/mach-s3c24xx/mach-qt2410.c | 1 -
arch/arm/mach-s3c24xx/mach-rx3715.c | 1 -
arch/arm/mach-s3c24xx/mach-vstms.c | 1 -
drivers/mtd/nand/raw/fsl_elbc_nand.c | 1 -
drivers/mtd/nand/raw/fsl_ifc_nand.c | 1 -
drivers/mtd/nand/raw/fsl_upm.c | 1 -
drivers/mtd/nand/raw/fsmc_nand.c | 1 -
drivers/mtd/nand/raw/lpc32xx_mlc.c | 1 -
drivers/mtd/nand/raw/lpc32xx_slc.c | 1 -
drivers/mtd/nand/raw/pasemi_nand.c | 1 -
drivers/mtd/nand/raw/s3c2410.c | 1 -
drivers/mtd/nand/raw/sharpsl.c | 1 -
drivers/mtd/nand/raw/tmio_nand.c | 1 -
drivers/mtd/nand/raw/txx9ndfmc.c | 1 -
include/linux/mtd/sharpsl.h | 1 -
23 files changed, 23 deletions(-)
diff --git a/arch/arm/mach-s3c24xx/common-smdk.c b/arch/arm/mach-s3c24xx/common-smdk.c
index 6f6ed95df636..8db7f58691b2 100644
--- a/arch/arm/mach-s3c24xx/common-smdk.c
+++ b/arch/arm/mach-s3c24xx/common-smdk.c
@@ -19,7 +19,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
diff --git a/arch/arm/mach-s3c24xx/mach-anubis.c b/arch/arm/mach-s3c24xx/mach-anubis.c
index 9a70863fa678..a32c467d5ad3 100644
--- a/arch/arm/mach-s3c24xx/mach-anubis.c
+++ b/arch/arm/mach-s3c24xx/mach-anubis.c
@@ -36,7 +36,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <net/ax88796.h>
diff --git a/arch/arm/mach-s3c24xx/mach-at2440evb.c b/arch/arm/mach-s3c24xx/mach-at2440evb.c
index 6e45171e5371..1b5c432186b8 100644
--- a/arch/arm/mach-s3c24xx/mach-at2440evb.c
+++ b/arch/arm/mach-s3c24xx/mach-at2440evb.c
@@ -37,7 +37,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <plat/devs.h>
diff --git a/arch/arm/mach-s3c24xx/mach-bast.c b/arch/arm/mach-s3c24xx/mach-bast.c
index 295aebdf29e4..a7966683cf00 100644
--- a/arch/arm/mach-s3c24xx/mach-bast.c
+++ b/arch/arm/mach-s3c24xx/mach-bast.c
@@ -24,7 +24,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_data/asoc-s3c24xx_simtec.h>
diff --git a/arch/arm/mach-s3c24xx/mach-gta02.c b/arch/arm/mach-s3c24xx/mach-gta02.c
index d0f70236cd9c..b63e766850fa 100644
--- a/arch/arm/mach-s3c24xx/mach-gta02.c
+++ b/arch/arm/mach-s3c24xx/mach-gta02.c
@@ -36,7 +36,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
diff --git a/arch/arm/mach-s3c24xx/mach-jive.c b/arch/arm/mach-s3c24xx/mach-jive.c
index 8f63f36b664d..09933ecb48ee 100644
--- a/arch/arm/mach-s3c24xx/mach-jive.c
+++ b/arch/arm/mach-s3c24xx/mach-jive.c
@@ -40,7 +40,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <plat/gpio-cfg.h>
diff --git a/arch/arm/mach-s3c24xx/mach-mini2440.c b/arch/arm/mach-s3c24xx/mach-mini2440.c
index 809773221e62..dd23b62c9bf2 100644
--- a/arch/arm/mach-s3c24xx/mach-mini2440.c
+++ b/arch/arm/mach-s3c24xx/mach-mini2440.c
@@ -46,7 +46,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <plat/gpio-cfg.h>
diff --git a/arch/arm/mach-s3c24xx/mach-osiris.c b/arch/arm/mach-s3c24xx/mach-osiris.c
index 7ce4d789c8fe..deef889217b0 100644
--- a/arch/arm/mach-s3c24xx/mach-osiris.c
+++ b/arch/arm/mach-s3c24xx/mach-osiris.c
@@ -33,7 +33,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <plat/cpu.h>
diff --git a/arch/arm/mach-s3c24xx/mach-qt2410.c b/arch/arm/mach-s3c24xx/mach-qt2410.c
index 84a31ab930d9..12d92ab44937 100644
--- a/arch/arm/mach-s3c24xx/mach-qt2410.c
+++ b/arch/arm/mach-s3c24xx/mach-qt2410.c
@@ -21,7 +21,6 @@
#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <asm/mach/arch.h>
diff --git a/arch/arm/mach-s3c24xx/mach-rx3715.c b/arch/arm/mach-s3c24xx/mach-rx3715.c
index 6f083d1ce18c..e76ebae91461 100644
--- a/arch/arm/mach-s3c24xx/mach-rx3715.c
+++ b/arch/arm/mach-s3c24xx/mach-rx3715.c
@@ -22,7 +22,6 @@
#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <asm/mach/arch.h>
diff --git a/arch/arm/mach-s3c24xx/mach-vstms.c b/arch/arm/mach-s3c24xx/mach-vstms.c
index 2ae4d785ca5b..481727a79e43 100644
--- a/arch/arm/mach-s3c24xx/mach-vstms.c
+++ b/arch/arm/mach-s3c24xx/mach-vstms.c
@@ -16,7 +16,6 @@
#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/memblock.h>
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
index e55cf8f42ce6..dc0e3809d6cc 100644
--- a/drivers/mtd/nand/raw/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -22,7 +22,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 4c7788a9e405..3283f1c4c512 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -15,7 +15,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/fsl_ifc.h>
#include <linux/iopoll.h>
diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
index f2a8bebb3c5b..a9215df44659 100644
--- a/drivers/mtd/nand/raw/fsl_upm.c
+++ b/drivers/mtd/nand/raw/fsl_upm.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/mtd.h>
#include <linux/of_address.h>
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index fe25b1123819..5f4b38281625 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -26,7 +26,6 @@
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/mtd/partitions.h>
diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
index 58e67b4f830f..17c732874434 100644
--- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
@@ -31,7 +31,6 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#define DRV_NAME "lpc32xx_mlc"
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index e1c7600d1dbb..dedabb2ca603 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -23,7 +23,6 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c
index 41e3119b1f3a..8f2ee755fd0a 100644
--- a/drivers/mtd/nand/raw/pasemi_nand.c
+++ b/drivers/mtd/nand/raw/pasemi_nand.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c
index da1531f0721c..1c9946f5f67c 100644
--- a/drivers/mtd/nand/raw/s3c2410.c
+++ b/drivers/mtd/nand/raw/s3c2410.c
@@ -30,7 +30,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_data/mtd-nand-s3c2410.h>
diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c
index 8222239c21f3..860e247935f3 100644
--- a/drivers/mtd/nand/raw/sharpsl.c
+++ b/drivers/mtd/nand/raw/sharpsl.c
@@ -12,7 +12,6 @@
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/sharpsl.h>
#include <linux/interrupt.h>
diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c
index f0bacbad1533..2461face82e3 100644
--- a/drivers/mtd/nand/raw/tmio_nand.c
+++ b/drivers/mtd/nand/raw/tmio_nand.c
@@ -35,7 +35,6 @@
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c
index 1466791ecb5d..785559a6fd8f 100644
--- a/drivers/mtd/nand/raw/txx9ndfmc.c
+++ b/drivers/mtd/nand/raw/txx9ndfmc.c
@@ -14,7 +14,6 @@
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/platform_data/txx9/ndfmc.h>
diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h
index 8cabe41d0f5c..6ef75d7f543d 100644
--- a/include/linux/mtd/sharpsl.h
+++ b/include/linux/mtd/sharpsl.h
@@ -6,7 +6,6 @@
*/
#include <linux/mtd/rawnand.h>
-#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <linux/mtd/partitions.h>
struct sharpsl_nand_platform_data {
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 25/40] mtd: nand: ecc: Let the software Hamming ECC engine be unselected
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
There is no reason to always embed the software Hamming ECC engine
implementation. By default it is (with raw NAND), but we can let the
user decide.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/Kconfig | 11 +++++++-
drivers/mtd/nand/raw/Kconfig | 2 +-
include/linux/mtd/nand-ecc-sw-hamming.h | 36 +++++++++++++++++++++++++
3 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 04eaed7b6ab5..ebd2a4f7ec48 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -15,7 +15,16 @@ config MTD_NAND_ECC
bool
config MTD_NAND_ECC_SW_HAMMING
- bool
+ bool "Software Hamming ECC engine"
+ default y if MTD_RAW_NAND
+ select MTD_NAND_ECC
+ help
+ This enables support for software Hamming error
+ correction. This correction can correct up to 1 bit error
+ per chunk and detect up to 2 bit errors. While it used to be
+ widely used with old parts, newer NAND chips usually require
+ more strength correction and in this case BCH or RS will be
+ preferred.
config MTD_NAND_ECC_SW_HAMMING_SMC
bool "NAND ECC Smart Media byte order"
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index fd98860be20f..5c502e80d79e 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -3,7 +3,6 @@ menuconfig MTD_RAW_NAND
tristate "Raw/Parallel NAND Device Support"
select MTD_NAND_CORE
select MTD_NAND_ECC
- select MTD_NAND_ECC_SW_HAMMING
help
This enables support for accessing all type of raw/parallel
NAND flash devices. For further information see
@@ -72,6 +71,7 @@ config MTD_NAND_AU1550
config MTD_NAND_NDFC
tristate "IBM/MCC 4xx NAND controller"
depends on 4xx
+ select MTD_NAND_ECC_SW_HAMMING
select MTD_NAND_ECC_SW_HAMMING_SMC
help
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h
index aed46e43d2f6..9de80d324cfa 100644
--- a/include/linux/mtd/nand-ecc-sw-hamming.h
+++ b/include/linux/mtd/nand-ecc-sw-hamming.h
@@ -30,6 +30,8 @@ struct nand_ecc_sw_hamming_conf {
unsigned int sm_order;
};
+#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
+
int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
unsigned char *code, bool sm_order);
int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
@@ -42,4 +44,38 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc);
+#else /* !CONFIG_MTD_NAND_ECC_SW_HAMMING */
+
+static inline int ecc_sw_hamming_calculate(const unsigned char *buf,
+ unsigned int step_size,
+ unsigned char *code, bool sm_order)
+{
+ return -ENOTSUPP;
+}
+
+static inline int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
+ const unsigned char *buf,
+ unsigned char *code)
+{
+ return -ENOTSUPP;
+}
+
+static inline int ecc_sw_hamming_correct(unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc,
+ unsigned int step_size, bool sm_order)
+{
+ return -ENOTSUPP;
+}
+
+static inline int nand_ecc_sw_hamming_correct(struct nand_device *nand,
+ unsigned char *buf,
+ unsigned char *read_ecc,
+ unsigned char *calc_ecc)
+{
+ return -ENOTSUPP;
+}
+
+#endif /* CONFIG_MTD_NAND_ECC_SW_HAMMING */
+
#endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 26/40] mtd: nand: ecc: Create the software BCH engine instance
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Let's continue introducing the generic ECC engine abstraction in the
NAND subsystem by instantiating a first ECC engine: the software
BCH one.
While at it, make a very tidy ecc_sw_bch_init() function and move all
the sanity checks and user input management in
nand_ecc_sw_bch_init_ctx(). This second helper will be called from the
raw RAND core.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/ecc-sw-bch.c | 340 +++++++++++++++++++++++-----
drivers/mtd/nand/raw/nand_base.c | 62 +----
include/linux/mtd/nand-ecc-sw-bch.h | 14 +-
3 files changed, 302 insertions(+), 114 deletions(-)
diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c
index 95d0635d10aa..e4f90dd47ca0 100644
--- a/drivers/mtd/nand/ecc-sw-bch.c
+++ b/drivers/mtd/nand/ecc-sw-bch.c
@@ -81,7 +81,7 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_correct);
* nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources
* @nand: NAND device
*/
-void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
+static void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
{
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
@@ -89,7 +89,6 @@ void nand_ecc_sw_bch_cleanup(struct nand_device *nand)
kfree(engine_conf->errloc);
kfree(engine_conf->eccmask);
}
-EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup);
/**
* nand_ecc_sw_bch_init - Initialize software BCH ECC engine
@@ -106,71 +105,36 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup);
* step_size = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
* bytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
*/
-int nand_ecc_sw_bch_init(struct nand_device *nand)
+static int nand_ecc_sw_bch_init(struct nand_device *nand)
{
- struct mtd_info *mtd = nanddev_to_mtd(nand);
- unsigned int m, t, eccsteps, i;
struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
- unsigned char *erased_page;
unsigned int eccsize = nand->ecc.ctx.conf.step_size;
unsigned int eccbytes = engine_conf->code_size;
- unsigned int eccstrength = nand->ecc.ctx.conf.strength;
+ unsigned int m, t, i;
+ unsigned char *erased_page;
+ int ret;
- if (!eccbytes && eccstrength) {
- eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
- engine_conf->code_size = eccbytes;
- }
-
- if (!eccsize || !eccbytes) {
- pr_warn("ecc parameters not supplied\n");
- return -EINVAL;
- }
-
- m = fls(1+8*eccsize);
- t = (eccbytes*8)/m;
+ m = fls(1 + (8 * eccsize));
+ t = (eccbytes * 8) / m;
engine_conf->bch = init_bch(m, t, 0);
if (!engine_conf->bch)
return -EINVAL;
- /* verify that eccbytes has the expected value */
- if (engine_conf->bch->ecc_bytes != eccbytes) {
- pr_warn("invalid eccbytes %u, should be %u\n",
- eccbytes, engine_conf->bch->ecc_bytes);
- goto fail;
- }
-
- eccsteps = mtd->writesize/eccsize;
-
- /* Check that we have an oob layout description. */
- if (!mtd->ooblayout) {
- pr_warn("missing oob scheme");
- goto fail;
- }
-
- /* sanity checks */
- if (8*(eccsize+eccbytes) >= (1 << m)) {
- pr_warn("eccsize %u is too large\n", eccsize);
- goto fail;
- }
-
- if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
- pr_warn("invalid ecc layout\n");
- goto fail;
- }
-
engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL);
engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc),
GFP_KERNEL);
- if (!engine_conf->eccmask || !engine_conf->errloc)
- goto fail;
+ if (!engine_conf->eccmask || !engine_conf->errloc) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
- /*
- * compute and store the inverted ecc of an erased ecc block
- */
+ /* Compute and store the inverted ECC of an erased step */
erased_page = kmalloc(eccsize, GFP_KERNEL);
- if (!erased_page)
- goto fail;
+ if (!erased_page) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
memset(erased_page, 0xff, eccsize);
encode_bch(engine_conf->bch, erased_page, eccsize,
@@ -180,17 +144,279 @@ int nand_ecc_sw_bch_init(struct nand_device *nand)
for (i = 0; i < eccbytes; i++)
engine_conf->eccmask[i] ^= 0xff;
- if (!eccstrength)
- nand->ecc.ctx.conf.strength = (eccbytes * 8) / fls(8 * eccsize);
+ /* Verify that the number of code bytes has the expected value */
+ if (engine_conf->bch->ecc_bytes != eccbytes) {
+ pr_err("Invalid number of ECC bytes: %u, expected: %u\n",
+ eccbytes, engine_conf->bch->ecc_bytes);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ /* Sanity checks */
+ if (8 * (eccsize + eccbytes) >= (1 << m)) {
+ pr_err("ECC step size is too large (%u)\n", eccsize);
+ ret = -EINVAL;
+ goto cleanup;
+ }
return 0;
-fail:
+cleanup:
nand_ecc_sw_bch_cleanup(nand);
- return -EINVAL;
+ return ret;
}
-EXPORT_SYMBOL(nand_ecc_sw_bch_init);
+
+int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_ecc_sw_bch_conf *engine_conf;
+ unsigned int code_size = 0, nsteps;
+ int ret;
+
+ /* Only large page NAND chips may use BCH */
+ if (mtd->oobsize < 64) {
+ pr_err("BCH cannot be used with small page NAND chips\n");
+ return -EINVAL;
+ }
+
+ if (!mtd->ooblayout)
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
+ conf->provider = NAND_SOFT_ECC_ENGINE;
+ conf->algo = NAND_ECC_BCH;
+ conf->step_size = nand->ecc.user_conf.step_size;
+ conf->strength = nand->ecc.user_conf.strength;
+
+ /*
+ * Board driver should supply ECC size and ECC strength
+ * values to select how many bits are correctable.
+ * Otherwise, default to 512 bytes for large page devices and 256 for
+ * small page devices.
+ */
+ if (!conf->step_size) {
+ if (mtd->oobsize >= 64)
+ conf->step_size = 512;
+ else
+ conf->step_size = 256;
+
+ conf->strength = 4;
+ }
+
+ nsteps = mtd->writesize / conf->step_size;
+
+ /* Maximize */
+ if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE) {
+ conf->step_size = 1024;
+ nsteps = mtd->writesize / conf->step_size;
+ /* Reserve 2 bytes for the BBM */
+ code_size = (mtd->oobsize - 2) / nsteps;
+ conf->strength = code_size * 8 / fls(8 * conf->step_size);
+ }
+
+ if (!code_size)
+ code_size = DIV_ROUND_UP(conf->strength *
+ fls(8 * conf->step_size), 8);
+
+ if (!conf->strength)
+ conf->strength = (code_size * 8) / fls(8 * conf->step_size);
+
+ if (!code_size && !conf->strength) {
+ pr_err("Missing ECC parameters\n");
+ return -EINVAL;
+ }
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ engine_conf->code_size = code_size;
+ engine_conf->nsteps = nsteps;
+ engine_conf->calc_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL);
+ engine_conf->code_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL);
+ if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+ kfree(engine_conf);
+ return -ENOMEM;
+ }
+
+ nand->ecc.ctx.priv = engine_conf;
+ nand->ecc.ctx.total = nsteps * code_size;
+
+ ret = nand_ecc_sw_bch_init(nand);
+ if (ret) {
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+ kfree(engine_conf);
+ return -ENOMEM;
+ }
+
+ /* Verify the layout validity */
+ if (mtd_ooblayout_count_eccbytes(mtd) !=
+ engine_conf->nsteps * engine_conf->code_size) {
+ pr_err("Invalid ECC layout\n");
+ nand_ecc_sw_bch_cleanup(nand);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_init_ctx);
+
+void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+
+ if (engine_conf) {
+ nand_ecc_sw_bch_cleanup(nand);
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+ kfree(engine_conf);
+ }
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup_ctx);
+
+static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = engine_conf->nsteps;
+ int total = nand->ecc.ctx.total;
+ u8 *ecccalc = engine_conf->calc_buf;
+ const u8 *data = req->databuf.out;
+ int i, ret;
+
+ /* Ensure the OOB buffer is empty before using it */
+ if (req->oobbuf.in)
+ memset(req->oobbuf.in, 0xff, nanddev_per_page_oobsize(nand));
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /*
+ * Ensure OOB area is fully read/written otherwise the software
+ * correction cannot apply.
+ */
+ engine_conf->reqooblen = req->ooblen;
+ req->ooblen = nanddev_per_page_oobsize(nand);
+
+ /* No more preparation for page read */
+ if (req->type == NAND_PAGE_READ)
+ return 0;
+
+ /* Preparation for page write: derive the ECC bytes and place them */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecccalc, oobbuf, 0, total);
+
+ /* Also place user data OOB bytes in the free area, if any */
+ if (engine_conf->reqooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+ oobbuf,
+ req->ooboffs,
+ req->ooblen);
+ else
+ memcpy(oobbuf + req->ooboffs, req->oobbuf.out,
+ req->ooblen);
+ }
+
+ return ret;
+}
+
+static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int total = nand->ecc.ctx.total;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = engine_conf->nsteps;
+ u8 *ecccalc = engine_conf->calc_buf;
+ u8 *ecccode = engine_conf->code_buf;
+ unsigned int max_bitflips = 0;
+ u8 *data = req->databuf.in;
+ int i, ret;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /* Don't mess up with the upper layer: restore the request OOB length */
+ req->ooblen = engine_conf->reqooblen;
+
+ /* Nothing more to do for page write */
+ if (req->type == NAND_PAGE_WRITE)
+ return 0;
+
+ /* Finish a page read: retrieve the (raw) ECC bytes*/
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, oobbuf, 0, total);
+ if (ret)
+ return ret;
+
+ /* Calculate the ECC bytes */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
+
+ /* Finish a page read: compare and correct */
+ for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize) {
+ int stat = nand_ecc_sw_bch_correct(nand, data,
+ &ecccode[i],
+ &ecccalc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Format the OOB buffer that will be returned to the user */
+ if (req->ooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_get_databytes(mtd, oobbuf,
+ req->oobbuf.in,
+ req->ooboffs, req->ooblen);
+ else
+ memcpy(req->oobbuf.in, oobbuf + req->ooboffs,
+ req->ooblen);
+ }
+
+ return max_bitflips;
+}
+
+static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = {
+ .init_ctx = nand_ecc_sw_bch_init_ctx,
+ .cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx,
+ .prepare_io_req = nand_ecc_sw_bch_prepare_io_req,
+ .finish_io_req = nand_ecc_sw_bch_finish_io_req,
+};
+
+static struct nand_ecc_engine nand_ecc_sw_bch_engine = {
+ .ops = &nand_ecc_sw_bch_engine_ops,
+};
+
+struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void)
+{
+ return &nand_ecc_sw_bch_engine;
+}
+EXPORT_SYMBOL(nand_ecc_sw_bch_get_engine);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 7734f18ddc59..22416334357a 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -4912,17 +4912,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
base->ecc.user_conf.step_size = chip->ecc.size;
base->ecc.user_conf.strength = chip->ecc.strength;
- engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
- if (!engine_conf)
- return -ENOMEM;
-
- engine_conf->code_size = chip->ecc.bytes;
-
- base->ecc.ctx.priv = engine_conf;
-
- ret = nand_ecc_sw_bch_init(base);
+ ret = nand_ecc_sw_bch_init_ctx(base);
if (ret)
- kfree(base->ecc.ctx.priv);
+ return ret;
+
+ engine_conf = base->ecc.ctx.priv;
chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength;
@@ -4930,7 +4924,7 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
chip->ecc.steps = engine_conf->nsteps;
chip->ecc.bytes = engine_conf->code_size;
- return ret;
+ return 0;
}
EXPORT_SYMBOL(rawnand_sw_bch_init);
@@ -4956,9 +4950,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
- nand_ecc_sw_bch_cleanup(base);
-
- kfree(base->ecc.ctx.priv);
+ nand_ecc_sw_bch_cleanup_ctx(base);
}
EXPORT_SYMBOL(rawnand_sw_bch_cleanup);
@@ -5013,51 +5005,15 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_oob_std;
- /*
- * Board driver should supply ecc.size and ecc.strength
- * values to select how many bits are correctable.
- * Otherwise, default to 4 bits for large page devices.
- */
- if (!ecc->size && (mtd->oobsize >= 64)) {
- ecc->size = 512;
- ecc->strength = 4;
- }
-
- /*
- * if no ecc placement scheme was provided pickup the default
- * large page one.
- */
- if (!mtd->ooblayout) {
- /* handle large page devices only */
- if (mtd->oobsize < 64) {
- WARN(1, "OOB layout is required when using software BCH on small pages\n");
- return -EINVAL;
- }
-
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-
- }
-
/*
* We can only maximize ECC config when the default layout is
* used, otherwise we don't know how many bytes can really be
* used.
*/
- if (mtd->ooblayout == &nand_ooblayout_lp_ops &&
- nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE) {
- int steps, bytes;
+ if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE &&
+ mtd->ooblayout != &nand_ooblayout_lp_ops)
+ nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE;
- /* Always prefer 1k blocks over 512bytes ones */
- ecc->size = 1024;
- steps = mtd->writesize / ecc->size;
-
- /* Reserve 2 bytes for the BBM */
- bytes = (mtd->oobsize - 2) / steps;
- ecc->strength = bytes * 8 / fls(8 * ecc->size);
- }
-
- /* See ecc_sw_bch_init() for details. */
- ecc->bytes = 0;
ret = rawnand_sw_bch_init(chip);
if (ret) {
WARN(1, "BCH ECC initialization failed!\n");
diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h
index 61a5b44b94ef..eec5373a2423 100644
--- a/include/linux/mtd/nand-ecc-sw-bch.h
+++ b/include/linux/mtd/nand-ecc-sw-bch.h
@@ -39,8 +39,9 @@ int nand_ecc_sw_bch_calculate(struct nand_device *nand,
const unsigned char *buf, unsigned char *code);
int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc);
-int nand_ecc_sw_bch_init(struct nand_device *nand);
-void nand_ecc_sw_bch_cleanup(struct nand_device *nand);
+int nand_ecc_sw_bch_init_ctx(struct nand_device *nand);
+void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand);
+struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void);
#else /* !CONFIG_MTD_NAND_ECC_SW_BCH */
@@ -59,12 +60,17 @@ static inline int nand_ecc_sw_bch_correct(struct nand_device *nand,
return -ENOTSUPP;
}
-static inline int nand_ecc_sw_bch_init(struct nand_device *nand)
+static inline int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
{
return -EINVAL;
}
-static inline void nand_ecc_sw_bch_cleanup(struct nand_device *nand) {}
+static inline void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) {}
+
+static inline struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void)
+{
+ return NULL;
+}
#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 27/40] mtd: nand: ecc: Create the software Hamming engine instance
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Let's continue introducing the generic ECC engine abstraction in the
NAND subsystem by instantiating a second ECC engine: software
Hamming.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/ecc-sw-hamming.c | 208 ++++++++++++++++++++++++
drivers/mtd/nand/raw/nand_base.c | 25 +--
include/linux/mtd/nand-ecc-sw-hamming.h | 15 ++
3 files changed, 231 insertions(+), 17 deletions(-)
diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c
index 8494e68814bc..a524747458a8 100644
--- a/drivers/mtd/nand/ecc-sw-hamming.c
+++ b/drivers/mtd/nand/ecc-sw-hamming.c
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mtd/nand.h>
#include <linux/mtd/nand-ecc-sw-hamming.h>
#include <asm/byteorder.h>
@@ -464,6 +465,213 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
}
EXPORT_SYMBOL(nand_ecc_sw_hamming_correct);
+int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct nand_ecc_sw_hamming_conf *engine_conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+
+ if (!mtd->ooblayout) {
+ switch (mtd->oobsize) {
+ case 8:
+ case 16:
+ mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
+ break;
+ case 64:
+ case 128:
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ conf->provider = NAND_SOFT_ECC_ENGINE;
+ conf->algo = NAND_ECC_HAMMING;
+ conf->step_size = nand->ecc.user_conf.step_size;
+ conf->strength = 1;
+
+ /* Use the strongest configuration by default */
+ if (conf->step_size != 256 && conf->step_size != 512)
+ conf->step_size = 256;
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ engine_conf->code_size = 3;
+ engine_conf->nsteps = mtd->writesize / conf->step_size;
+ engine_conf->calc_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL);
+ engine_conf->code_buf = kzalloc(sizeof(mtd->oobsize), GFP_KERNEL);
+ if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+ kfree(engine_conf);
+ return -ENOMEM;
+ }
+
+ nand->ecc.ctx.priv = engine_conf;
+ nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size;
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx);
+
+void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+
+ if (engine_conf) {
+ kfree(engine_conf->calc_buf);
+ kfree(engine_conf->code_buf);
+ }
+
+ kfree(engine_conf);
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx);
+
+static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = engine_conf->nsteps;
+ int total = nand->ecc.ctx.total;
+ u8 *ecccalc = engine_conf->calc_buf;
+ const u8 *data = req->databuf.out;
+ int i, ret;
+
+ /* Ensure the OOB buffer is empty before using it */
+ if (req->oobbuf.in)
+ memset(req->oobbuf.in, 0xff, nanddev_per_page_oobsize(nand));
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /*
+ * Ensure OOB area is fully read/written otherwise the software
+ * correction cannot apply.
+ */
+ engine_conf->reqooblen = req->ooblen;
+ req->ooblen = nanddev_per_page_oobsize(nand);
+
+ /* No preparation for page read */
+ if (req->type == NAND_PAGE_READ)
+ return 0;
+
+ /* Preparation for page write: derive the ECC bytes and place them */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
+
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecccalc, oobbuf, 0, total);
+
+ /* Also place user data OOB bytes in the free area, if any */
+ if (engine_conf->reqooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+ oobbuf,
+ req->ooboffs,
+ req->ooblen);
+ else
+ memcpy(oobbuf + req->ooboffs, req->oobbuf.out,
+ req->ooblen);
+ }
+
+ return ret;
+}
+
+static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ int eccsize = nand->ecc.ctx.conf.step_size;
+ int total = nand->ecc.ctx.total;
+ int eccbytes = engine_conf->code_size;
+ int eccsteps = engine_conf->nsteps;
+ u8 *ecccalc = engine_conf->calc_buf;
+ u8 *ecccode = engine_conf->code_buf;
+ unsigned int max_bitflips = 0;
+ u8 *data = req->databuf.in;
+ int i, ret;
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* This engine does not provide BBM/free OOB bytes protection */
+ if (!req->datalen)
+ return 0;
+
+ /* Don't mess up with the upper layer: restore the request OOB length */
+ req->ooblen = engine_conf->reqooblen;
+
+ /* Nothing more to do for page write */
+ if (req->type == NAND_PAGE_WRITE)
+ return 0;
+
+ /* Finish a page read: retrieve the (raw) ECC bytes*/
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, oobbuf, 0, total);
+ if (ret)
+ return ret;
+
+ /* Calculate the ECC bytes */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize)
+ nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
+
+ eccsteps = engine_conf->nsteps;
+
+ /* Finish a page read: compare and correct */
+ for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
+ eccsteps;
+ eccsteps--, i += eccbytes, data += eccsize) {
+ int stat = nand_ecc_sw_hamming_correct(nand, data,
+ &ecccode[i],
+ &ecccalc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Format the OOB buffer that will be returned to the user */
+ if (req->ooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_get_databytes(mtd, oobbuf,
+ req->oobbuf.in,
+ req->ooboffs, req->ooblen);
+ else
+ memcpy(req->oobbuf.in, oobbuf + req->ooboffs,
+ req->ooblen);
+ }
+
+ return max_bitflips;
+}
+
+static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = {
+ .init_ctx = nand_ecc_sw_hamming_init_ctx,
+ .cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx,
+ .prepare_io_req = nand_ecc_sw_hamming_prepare_io_req,
+ .finish_io_req = nand_ecc_sw_hamming_finish_io_req,
+};
+
+static struct nand_ecc_engine nand_ecc_sw_hamming_engine = {
+ .ops = &nand_ecc_sw_hamming_engine_ops,
+};
+
+struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void)
+{
+ return &nand_ecc_sw_hamming_engine;
+}
+EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>");
MODULE_DESCRIPTION("NAND software Hamming ECC support");
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 22416334357a..a0f23051cc21 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -4834,34 +4834,24 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
int rawnand_sw_hamming_init(struct nand_chip *chip)
{
- struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_sw_hamming_conf *engine_conf;
struct nand_device *base = &chip->base;
+ int ret;
base->ecc.user_conf.provider = NAND_SOFT_ECC_ENGINE;
base->ecc.user_conf.algo = NAND_ECC_HAMMING;
base->ecc.user_conf.strength = chip->ecc.strength;
base->ecc.user_conf.step_size = chip->ecc.size;
- if (base->ecc.user_conf.strength != 1 ||
- (base->ecc.user_conf.step_size != 256 &&
- base->ecc.user_conf.step_size != 512)) {
- pr_err("%s: unsupported strength or step size\n", __func__);
- return -EINVAL;
- }
+ ret = nand_ecc_sw_hamming_init_ctx(base);
+ if (ret)
+ return ret;
- engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
- if (!engine_conf)
- return -ENOMEM;
-
- engine_conf->code_size = 3;
- engine_conf->nsteps = mtd->writesize / base->ecc.user_conf.step_size;
+ engine_conf = base->ecc.ctx.priv;
if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER)
engine_conf->sm_order = true;
- base->ecc.ctx.priv = engine_conf;
-
chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength;
chip->ecc.total = base->ecc.ctx.total;
@@ -4897,7 +4887,7 @@ void rawnand_sw_hamming_cleanup(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
- kfree(base->ecc.ctx.priv);
+ nand_ecc_sw_hamming_cleanup_ctx(base);
}
EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
@@ -5361,7 +5351,8 @@ static int nand_scan_tail(struct nand_chip *chip)
* If no default placement scheme is given, select an appropriate one.
*/
if (!mtd->ooblayout &&
- !(ecc->mode == NAND_SOFT_ECC_ENGINE && ecc->algo == NAND_ECC_BCH)) {
+ !(ecc->mode == NAND_SOFT_ECC_ENGINE && ecc->algo == NAND_ECC_BCH) &&
+ !(ecc->mode == NAND_SOFT_ECC_ENGINE && ecc->algo == NAND_ECC_HAMMING)) {
switch (mtd->oobsize) {
case 8:
case 16:
diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h
index 9de80d324cfa..3ae51bd2e2ab 100644
--- a/include/linux/mtd/nand-ecc-sw-hamming.h
+++ b/include/linux/mtd/nand-ecc-sw-hamming.h
@@ -32,6 +32,8 @@ struct nand_ecc_sw_hamming_conf {
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
+int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand);
+void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand);
int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size,
unsigned char *code, bool sm_order);
int nand_ecc_sw_hamming_calculate(struct nand_device *nand,
@@ -43,9 +45,17 @@ int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc,
int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc);
+struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void);
#else /* !CONFIG_MTD_NAND_ECC_SW_HAMMING */
+static inline int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
+{
+ return -ENOTSUPP;
+}
+
+static inline void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) {}
+
static inline int ecc_sw_hamming_calculate(const unsigned char *buf,
unsigned int step_size,
unsigned char *code, bool sm_order)
@@ -76,6 +86,11 @@ static inline int nand_ecc_sw_hamming_correct(struct nand_device *nand,
return -ENOTSUPP;
}
+static inline struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void)
+{
+ return NULL;
+}
+
#endif /* CONFIG_MTD_NAND_ECC_SW_HAMMING */
#endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 28/40] mtd: nand: Let software ECC engines be retrieved from the NAND core
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Before making use of the ECC engines, we must retrieve them. Add the
boilerplate for the ones already available: software engines (Hamming
and BCH).
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/ecc.c | 20 ++++++++++++++++++++
include/linux/mtd/nand-ecc-sw-bch.h | 3 +++
include/linux/mtd/nand-ecc-sw-hamming.h | 3 +++
include/linux/mtd/nand.h | 3 +++
4 files changed, 29 insertions(+)
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
index 8e7859355a10..da3aa2379b94 100644
--- a/drivers/mtd/nand/ecc.c
+++ b/drivers/mtd/nand/ecc.c
@@ -480,6 +480,26 @@ bool nand_ecc_correction_is_enough(struct nand_device *nand)
}
EXPORT_SYMBOL(nand_ecc_correction_is_enough);
+struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
+{
+ unsigned int algo = nand->ecc.user_conf.algo;
+
+ if (algo == NAND_ECC_UNKNOWN)
+ algo = nand->ecc.defaults.algo;
+
+ switch (algo) {
+ case NAND_ECC_HAMMING:
+ return nand_ecc_sw_hamming_get_engine();
+ case NAND_ECC_BCH:
+ return nand_ecc_sw_bch_get_engine();
+ default:
+ break;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(nand_ecc_get_sw_engine);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Generic ECC engine");
diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h
index eec5373a2423..3c0cf42b7a1d 100644
--- a/include/linux/mtd/nand-ecc-sw-bch.h
+++ b/include/linux/mtd/nand-ecc-sw-bch.h
@@ -11,6 +11,9 @@
#include <linux/mtd/nand.h>
#include <linux/bch.h>
+/* Needed for cross inclusion with nand.h */
+struct nand_device;
+
/**
* struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure
* @reqooblen: Save the actual user OOB length requested before overwriting it
diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h
index 3ae51bd2e2ab..d79a72393ef4 100644
--- a/include/linux/mtd/nand-ecc-sw-hamming.h
+++ b/include/linux/mtd/nand-ecc-sw-hamming.h
@@ -12,6 +12,9 @@
#include <linux/mtd/nand.h>
+/* Needed for cross inclusion with nand.h */
+struct nand_device;
+
/**
* struct nand_ecc_sw_hamming_conf - private software Hamming ECC engine structure
* @reqooblen: Save the actual user OOB length requested before overwriting it
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9d7b62933cc1..b410ef778e52 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -11,6 +11,8 @@
#define __LINUX_MTD_NAND_H
#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand-ecc-sw-hamming.h>
+#include <linux/mtd/nand-ecc-sw-bch.h>
struct nand_device;
@@ -270,6 +272,7 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
int nand_ecc_finish_io_req(struct nand_device *nand,
struct nand_page_io_req *req, void *oobbuf);
bool nand_ecc_correction_is_enough(struct nand_device *nand);
+struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
/**
* struct nand_ecc - High-level ECC object
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 29/40] mtd: spinand: Fix typo in comment
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
One comment in the SPI-NAND core is not very clear, fix it to ease the
understanding of what the block does.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
---
drivers/mtd/nand/spi/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index c2a653707886..7f6b0adbba26 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1031,7 +1031,7 @@ static int spinand_init(struct spinand_device *spinand)
/*
* Right now, we don't support ECC, so let the whole oob
- * area is available for user.
+ * area available for the user.
*/
mtd->_read_oob = spinand_mtd_read;
mtd->_write_oob = spinand_mtd_write;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 30/40] mtd: spinand: Move ECC related definitions earlier in the driver
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Prepare the creation of a SPI-NAND on-die ECC engine by gathering the
ECC-related code earlier enough in the core to avoid the need for
forward declarations.
The next step is to actually create that engine by implementing the
generic ECC interface.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 106 +++++++++++------------
drivers/mtd/nand/spi/on-die-ecc-engine.c | 0
2 files changed, 53 insertions(+), 53 deletions(-)
create mode 100644 drivers/mtd/nand/spi/on-die-ecc-engine.c
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 7f6b0adbba26..49b252bc1352 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -192,6 +192,59 @@ static int spinand_ecc_enable(struct spinand_device *spinand,
enable ? CFG_ECC_ENABLE : 0);
}
+static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+
+ if (spinand->eccinfo.get_status)
+ return spinand->eccinfo.get_status(spinand, status);
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /*
+ * We have no way to know exactly how many bitflips have been
+ * fixed, so let's return the maximum possible value so that
+ * wear-leveling layers move the data immediately.
+ */
+ return nand->ecc.ctx.conf.strength;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ return -ERANGE;
+}
+
+static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ /* Reserve 2 bytes for the BBM. */
+ region->offset = 2;
+ region->length = 62;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
+ .ecc = spinand_noecc_ooblayout_ecc,
+ .free = spinand_noecc_ooblayout_free,
+};
+
static int spinand_write_enable_op(struct spinand_device *spinand)
{
struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
@@ -400,35 +453,6 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
}
-static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
-{
- struct nand_device *nand = spinand_to_nand(spinand);
-
- if (spinand->eccinfo.get_status)
- return spinand->eccinfo.get_status(spinand, status);
-
- switch (status & STATUS_ECC_MASK) {
- case STATUS_ECC_NO_BITFLIPS:
- return 0;
-
- case STATUS_ECC_HAS_BITFLIPS:
- /*
- * We have no way to know exactly how many bitflips have been
- * fixed, so let's return the maximum possible value so that
- * wear-leveling layers move the data immediately.
- */
- return nand->ecc.ctx.conf.strength;
-
- case STATUS_ECC_UNCOR_ERROR:
- return -EBADMSG;
-
- default:
- break;
- }
-
- return -EINVAL;
-}
-
static int spinand_read_page(struct spinand_device *spinand,
const struct nand_page_io_req *req,
bool ecc_enabled)
@@ -928,30 +952,6 @@ static int spinand_detect(struct spinand_device *spinand)
return 0;
}
-static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *region)
-{
- return -ERANGE;
-}
-
-static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *region)
-{
- if (section)
- return -ERANGE;
-
- /* Reserve 2 bytes for the BBM. */
- region->offset = 2;
- region->length = 62;
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
- .ecc = spinand_noecc_ooblayout_ecc,
- .free = spinand_noecc_ooblayout_free,
-};
-
static int spinand_init(struct spinand_device *spinand)
{
struct device *dev = &spinand->spimem->spi->dev;
diff --git a/drivers/mtd/nand/spi/on-die-ecc-engine.c b/drivers/mtd/nand/spi/on-die-ecc-engine.c
new file mode 100644
index 000000000000..e69de29bb2d1
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 31/40] mtd: spinand: Instantiate a SPI-NAND on-die ECC engine
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Make use of the existing functions taken from the SPI-NAND core to
instantiate an on-die ECC engine specific to the SPI-NAND core. The
next step will be to tweak the core to use this object instead of
calling the helpers directly.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 69 +++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 9 +++++
2 files changed, 78 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 49b252bc1352..5a935b7343eb 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -245,6 +245,75 @@ static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
.free = spinand_noecc_ooblayout_free,
};
+static int spinand_ondie_ecc_init_ctx(struct nand_device *nand)
+{
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct spinand_ondie_ecc_conf *engine_conf;
+
+ nand->ecc.ctx.conf.provider = NAND_ON_DIE_ECC_ENGINE;
+ nand->ecc.ctx.conf.step_size = nand->ecc.requirements.step_size;
+ nand->ecc.ctx.conf.strength = nand->ecc.requirements.strength;
+
+ engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL);
+ if (!engine_conf)
+ return -ENOMEM;
+
+ nand->ecc.ctx.priv = engine_conf;
+
+ if (spinand->eccinfo.ooblayout)
+ mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
+ else
+ mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
+
+ return 0;
+}
+
+static void spinand_ondie_ecc_cleanup_ctx(struct nand_device *nand)
+{
+ kfree(nand->ecc.ctx.priv);
+}
+
+static int spinand_ondie_ecc_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ bool enable = (req->mode != MTD_OPS_RAW);
+
+ /* Only enable or disable the engine */
+ return spinand_ecc_enable(spinand, enable);
+}
+
+static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req,
+ void *oobbuf)
+{
+ struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
+ struct spinand_device *spinand = nand_to_spinand(nand);
+
+ if (req->mode == MTD_OPS_RAW)
+ return 0;
+
+ /* Nothing to do when finishing a page write */
+ if (req->type == NAND_PAGE_WRITE)
+ return 0;
+
+ /* Finish a page write: check the status, report errors/bitflips */
+ return spinand_check_ecc_status(spinand, engine_conf->status);
+}
+
+static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = {
+ .init_ctx = spinand_ondie_ecc_init_ctx,
+ .cleanup_ctx = spinand_ondie_ecc_cleanup_ctx,
+ .prepare_io_req = spinand_ondie_ecc_prepare_io_req,
+ .finish_io_req = spinand_ondie_ecc_finish_io_req,
+};
+
+static __maybe_unused struct nand_ecc_engine spinand_ondie_ecc_engine = {
+ .ops = &spinand_ondie_ecc_engine_ops,
+};
+
static int spinand_write_enable_op(struct spinand_device *spinand)
{
struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 87d774b7500c..7526e1edb47b 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -271,6 +271,15 @@ struct spinand_ecc_info {
#define SPINAND_HAS_QE_BIT BIT(0)
+/**
+ * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
+ * @status: status of the last wait operation that will be used in case
+ * ->get_status() is not populated by the spinand device.
+ */
+struct spinand_ondie_ecc_conf {
+ u8 status;
+};
+
/**
* struct spinand_info - Structure used to describe SPI NAND chips
* @model: model name
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 32/40] mtd: nand: Let on-die ECC engines be retrieved from the NAND core
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Before making use of the ECC engines, we must retrieve them. Add the
necessary boilerplate.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/ecc.c | 6 ++++++
include/linux/mtd/nand.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
index da3aa2379b94..4f869d33213d 100644
--- a/drivers/mtd/nand/ecc.c
+++ b/drivers/mtd/nand/ecc.c
@@ -500,6 +500,12 @@ struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
}
EXPORT_SYMBOL(nand_ecc_get_sw_engine);
+struct nand_ecc_engine *nand_ecc_get_ondie_engine(struct nand_device *nand)
+{
+ return nand->ecc.ondie_engine;
+}
+EXPORT_SYMBOL(nand_ecc_get_ondie_engine);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Generic ECC engine");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index b410ef778e52..b25d17ef9714 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -273,6 +273,7 @@ int nand_ecc_finish_io_req(struct nand_device *nand,
struct nand_page_io_req *req, void *oobbuf);
bool nand_ecc_correction_is_enough(struct nand_device *nand);
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
+struct nand_ecc_engine *nand_ecc_get_ondie_engine(struct nand_device *nand);
/**
* struct nand_ecc - High-level ECC object
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 33/40] mtd: rawnand: Fill a default ECC provider/algorithm
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
The raw NAND layer may want to use one or another ECC provider if the
user did not explicitly broadcasted his choice through the DT. Raw
NAND controllers can also want to force these values. In any case, the
NAND core must be able to know their preference and this works by
letting subsystems/drivers filling/changing the "defaults" ECC
structure.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/raw/nand_base.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index a0f23051cc21..d2433d912aee 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5586,6 +5586,13 @@ static int nand_scan_tail(struct nand_chip *chip)
break;
}
+ /*
+ * If there is no specific user request for the ECC engine provider, use
+ * the one chosen by the driver being instantiated.
+ */
+ chip->base.ecc.defaults.provider = ecc->mode;
+ chip->base.ecc.defaults.algo = ecc->algo;
+
ret = nanddev_init(&chip->base, &rawnand_ops, mtd->owner);
if (ret)
goto err_nand_manuf_cleanup;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 34/40] mtd: spinand: Fill a default ECC provider/algorithm
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
The SPI-NAND layer default is on-die ECC because until now it was the
only one supported. New SPI-NAND chip flavors might use something else
as ECC engine provider but this will always be the default if the user
does not choose explicitly something else.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 5a935b7343eb..c20d57f9c11b 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -310,7 +310,7 @@ static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = {
.finish_io_req = spinand_ondie_ecc_finish_io_req,
};
-static __maybe_unused struct nand_ecc_engine spinand_ondie_ecc_engine = {
+static struct nand_ecc_engine spinand_ondie_ecc_engine = {
.ops = &spinand_ondie_ecc_engine_ops,
};
@@ -1098,6 +1098,10 @@ static int spinand_init(struct spinand_device *spinand)
if (ret)
goto err_manuf_cleanup;
+ /* SPI-NAND default ECC engine is on-die */
+ nand->ecc.defaults.provider = NAND_ON_DIE_ECC_ENGINE;
+ nand->ecc.ondie_engine = &spinand_ondie_ecc_engine;
+
/*
* Right now, we don't support ECC, so let the whole oob
* area available for the user.
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 35/40] mtd: nand: Add helpers to manage ECC engines and configurations
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Add the logic in the NAND core to find the right ECC engine depending
on the NAND chip requirements and the user desires. Right now, the
choice may be made between (more will come):
* software Hamming
* software BCH
* on-die (SPI-NAND devices only)
Once the ECC engine has been found, the ECC engine must be
configured.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/core.c | 124 +++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 4 ++
2 files changed, 128 insertions(+)
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
index 0a2be5e6d669..062a65e131db 100644
--- a/drivers/mtd/nand/core.c
+++ b/drivers/mtd/nand/core.c
@@ -207,6 +207,130 @@ int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
}
EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
+/**
+ * nanddev_get_ecc_engine() - Find and get a suitable ECC engine
+ * @nand: NAND device
+ */
+static int nanddev_get_ecc_engine(struct nand_device *nand)
+{
+ int provider;
+
+ /* Read the user desires in terms of ECC engine/configuration */
+ nand_ecc_read_user_conf(nand);
+
+ provider = nand->ecc.user_conf.provider;
+ if (provider == NAND_INVALID_ECC_ENGINE)
+ provider = nand->ecc.defaults.provider;
+
+ switch (provider) {
+ case NAND_NO_ECC_ENGINE:
+ return 0;
+ case NAND_SOFT_ECC_ENGINE:
+ nand->ecc.engine = nand_ecc_get_sw_engine(nand);
+ break;
+ case NAND_ON_DIE_ECC_ENGINE:
+ nand->ecc.engine = nand_ecc_get_ondie_engine(nand);
+ break;
+ case NAND_HW_ECC_ENGINE:
+ pr_err("Hardware ECC engines not supported yet\n");
+ break;
+ default:
+ pr_err("Missing ECC engine provider\n");
+ }
+
+ if (!nand->ecc.engine)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine
+ * @nand: NAND device
+ */
+static int nanddev_put_ecc_engine(struct nand_device *nand)
+{
+ switch (nand->ecc.ctx.conf.provider) {
+ case NAND_HW_ECC_ENGINE:
+ pr_err("Hardware ECC engines not supported yet\n");
+ break;
+ case NAND_NO_ECC_ENGINE:
+ case NAND_SOFT_ECC_ENGINE:
+ case NAND_ON_DIE_ECC_ENGINE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * nanddev_find_ecc_configuration() - Find a suitable ECC configuration
+ * @nand: NAND device
+ */
+static int nanddev_find_ecc_configuration(struct nand_device *nand)
+{
+ int ret;
+
+ if (!nand->ecc.engine)
+ return -ENOTSUPP;
+
+ ret = nand_ecc_init_ctx(nand);
+ if (ret)
+ return ret;
+
+ if (!nand_ecc_correction_is_enough(nand))
+ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+ nand->mtd.name);
+
+ return 0;
+}
+
+/**
+ * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip
+ * @nand: NAND device
+ */
+int nanddev_ecc_engine_init(struct nand_device *nand)
+{
+ int ret;
+
+ /* Look for the ECC engine to use */
+ ret = nanddev_get_ecc_engine(nand);
+ if (ret) {
+ pr_err("No ECC engine found\n");
+ return ret;
+ }
+
+ /* No ECC engine requested */
+ if (!nand->ecc.engine)
+ return 0;
+
+ /* Configure the engine: balance user input and chip requirements */
+ ret = nanddev_find_ecc_configuration(nand);
+ if (ret) {
+ pr_err("No suitable ECC configuration\n");
+ nanddev_put_ecc_engine(nand);
+
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init);
+
+/**
+ * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations
+ * @nand: NAND device
+ */
+void nanddev_ecc_engine_cleanup(struct nand_device *nand)
+{
+ if (nand->ecc.engine)
+ nand_ecc_cleanup_ctx(nand);
+
+ nanddev_put_ecc_engine(nand);
+}
+EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup);
+
/**
* nanddev_init() - Initialize a NAND device
* @nand: NAND device
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index b25d17ef9714..5a745f74eb35 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -858,6 +858,10 @@ bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);
+/* ECC related functions */
+int nanddev_ecc_engine_init(struct nand_device *nand);
+void nanddev_ecc_engine_cleanup(struct nand_device *nand);
+
/* BBT related functions */
enum nand_bbt_block_status {
NAND_BBT_BLOCK_STATUS_UNKNOWN,
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 36/40] mtd: spinand: Use the external ECC engine logic
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Now that all the logic is available in the NAND core, let's use it
from the SPI-NAND core. Right now there is no functional change as the
default ECC engine for SPI-NANDs is set to 'on-die', but user can now
use software correction if they want to by just setting the right
properties in the DT.
Also note that the OOB layout handling is removed from the SPI-NAND
core as each ECC engine is supposed to handle it by it's own; users
should not be aware of that.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/Kconfig | 1 +
drivers/mtd/nand/spi/core.c | 101 ++++++++++++++++++++---------------
2 files changed, 58 insertions(+), 44 deletions(-)
diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
index da89b250df7c..3d7649a2dd72 100644
--- a/drivers/mtd/nand/spi/Kconfig
+++ b/drivers/mtd/nand/spi/Kconfig
@@ -2,6 +2,7 @@
menuconfig MTD_SPI_NAND
tristate "SPI NAND device Support"
select MTD_NAND_CORE
+ select MTD_NAND_ECC
depends on SPI_MASTER
select SPI_MEM
help
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index c20d57f9c11b..a521eeb0d351 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -314,6 +314,15 @@ static struct nand_ecc_engine spinand_ondie_ecc_engine = {
.ops = &spinand_ondie_ecc_engine_ops,
};
+static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
+{
+ struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
+
+ if (nand->ecc.ctx.conf.provider == NAND_ON_DIE_ECC_ENGINE &&
+ engine_conf)
+ engine_conf->status = status;
+}
+
static int spinand_write_enable_op(struct spinand_device *spinand)
{
struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
@@ -335,7 +344,6 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
const struct nand_page_io_req *req)
{
struct nand_device *nand = spinand_to_nand(spinand);
- struct mtd_info *mtd = nanddev_to_mtd(nand);
struct spi_mem_dirmap_desc *rdesc;
unsigned int nbytes = 0;
void *buf = NULL;
@@ -375,17 +383,6 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
req->datalen);
- if (req->ooblen) {
- if (req->mode == MTD_OPS_AUTO_OOB)
- mtd_ooblayout_get_databytes(mtd, req->oobbuf.in,
- spinand->oobbuf,
- req->ooboffs,
- req->ooblen);
- else
- memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
- req->ooblen);
- }
-
return 0;
}
@@ -393,7 +390,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
const struct nand_page_io_req *req)
{
struct nand_device *nand = spinand_to_nand(spinand);
- struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct mtd_info *mtd = spinand_to_mtd(spinand);
struct spi_mem_dirmap_desc *wdesc;
unsigned int nbytes, column = 0;
void *buf = spinand->databuf;
@@ -405,9 +402,12 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
* must fill the page cache entirely even if we only want to program
* the data portion of the page, otherwise we might corrupt the BBM or
* user data previously programmed in OOB area.
+ *
+ * Only reset the data buffer manually, the OOB buffer is prepared by
+ * ECC engines ->prepare_io_req() callback.
*/
nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- memset(spinand->databuf, 0xff, nbytes);
+ memset(spinand->databuf, 0xff, nanddev_page_size(nand));
if (req->datalen)
memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
@@ -523,12 +523,17 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
}
static int spinand_read_page(struct spinand_device *spinand,
- const struct nand_page_io_req *req,
- bool ecc_enabled)
+ const struct nand_page_io_req *req)
{
+ struct nand_device *nand = spinand_to_nand(spinand);
u8 status;
int ret;
+ ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req,
+ spinand->oobbuf);
+ if (ret)
+ return ret;
+
ret = spinand_load_page_op(spinand, req);
if (ret)
return ret;
@@ -537,22 +542,28 @@ static int spinand_read_page(struct spinand_device *spinand,
if (ret < 0)
return ret;
+ spinand_ondie_ecc_save_status(nand, status);
+
ret = spinand_read_from_cache_op(spinand, req);
if (ret)
return ret;
- if (!ecc_enabled)
- return 0;
-
- return spinand_check_ecc_status(spinand, status);
+ return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req,
+ spinand->oobbuf);
}
static int spinand_write_page(struct spinand_device *spinand,
const struct nand_page_io_req *req)
{
+ struct nand_device *nand = spinand_to_nand(spinand);
u8 status;
int ret;
+ ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req,
+ spinand->oobbuf);
+ if (ret)
+ return ret;
+
ret = spinand_write_enable_op(spinand);
if (ret)
return ret;
@@ -567,9 +578,10 @@ static int spinand_write_page(struct spinand_device *spinand,
ret = spinand_wait(spinand, &status);
if (!ret && (status & STATUS_PROG_FAILED))
- ret = -EIO;
+ return -EIO;
- return ret;
+ return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req,
+ spinand->oobbuf);
}
static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
@@ -579,25 +591,24 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
struct nand_device *nand = mtd_to_nanddev(mtd);
unsigned int max_bitflips = 0;
struct nand_io_iter iter;
- bool enable_ecc = false;
+ bool disable_ecc = false;
bool ecc_failed = false;
int ret = 0;
- if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout)
- enable_ecc = true;
+ if (ops->mode == MTD_OPS_RAW || !spinand->eccinfo.ooblayout)
+ disable_ecc = true;
mutex_lock(&spinand->lock);
nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
+ if (disable_ecc)
+ iter.req.mode = MTD_OPS_RAW;
+
ret = spinand_select_target(spinand, iter.req.pos.target);
if (ret)
break;
- ret = spinand_ecc_enable(spinand, enable_ecc);
- if (ret)
- break;
-
- ret = spinand_read_page(spinand, &iter.req, enable_ecc);
+ ret = spinand_read_page(spinand, &iter.req);
if (ret < 0 && ret != -EBADMSG)
break;
@@ -628,20 +639,19 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to,
struct spinand_device *spinand = mtd_to_spinand(mtd);
struct nand_device *nand = mtd_to_nanddev(mtd);
struct nand_io_iter iter;
- bool enable_ecc = false;
+ bool disable_ecc = false;
int ret = 0;
- if (ops->mode != MTD_OPS_RAW && mtd->ooblayout)
- enable_ecc = true;
+ if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout)
+ disable_ecc = true;
mutex_lock(&spinand->lock);
nanddev_io_for_each_page(nand, NAND_PAGE_WRITE, to, ops, &iter) {
- ret = spinand_select_target(spinand, iter.req.pos.target);
- if (ret)
- break;
+ if (disable_ecc)
+ iter.req.mode = MTD_OPS_RAW;
- ret = spinand_ecc_enable(spinand, enable_ecc);
+ ret = spinand_select_target(spinand, iter.req.pos.target);
if (ret)
break;
@@ -671,7 +681,7 @@ static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos)
memset(spinand->oobbuf, 0, 2);
spinand_select_target(spinand, pos->target);
- spinand_read_page(spinand, &req, false);
+ spinand_read_page(spinand, &req);
if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff)
printk("would be bad, but fuck it\n");
// return true;
@@ -1102,6 +1112,11 @@ static int spinand_init(struct spinand_device *spinand)
nand->ecc.defaults.provider = NAND_ON_DIE_ECC_ENGINE;
nand->ecc.ondie_engine = &spinand_ondie_ecc_engine;
+ spinand_ecc_enable(spinand, false);
+ ret = nanddev_ecc_engine_init(nand);
+ if (ret)
+ goto err_cleanup_nanddev;
+
/*
* Right now, we don't support ECC, so let the whole oob
* area available for the user.
@@ -1114,19 +1129,17 @@ static int spinand_init(struct spinand_device *spinand)
mtd->_erase = spinand_mtd_erase;
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
- if (spinand->eccinfo.ooblayout)
- mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
- else
- mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
-
ret = mtd_ooblayout_count_freebytes(mtd);
if (ret < 0)
- goto err_cleanup_nanddev;
+ goto err_cleanup_ecc_engine;
mtd->oobavail = ret;
return 0;
+err_cleanup_ecc_engine:
+ nanddev_ecc_engine_cleanup(nand);
+
err_cleanup_nanddev:
nanddev_cleanup(nand);
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 37/40] mtd: spinand: Propagate ECC information to the MTD structure
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
This is done by default in the raw NAND core (nand_base.c) but was
missing in the SPI-NAND core. Without these two lines the ecc_strength
and ecc_step_size values are not exported to the user through sysfs.
This fix depends on recent changes and should not be backported as-is.
Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs")
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index a521eeb0d351..36b99f68da81 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1135,6 +1135,10 @@ static int spinand_init(struct spinand_device *spinand)
mtd->oobavail = ret;
+ /* Propagate ECC information to mtd_info */
+ mtd->ecc_strength = nand->ecc.ctx.conf.strength;
+ mtd->ecc_step_size = nand->ecc.ctx.conf.step_size;
+
return 0;
err_cleanup_ecc_engine:
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 38/40] mtd: spinand: Allow the case where there is no ECC engine
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Even if this is not supposed to happen, there is no reason to fail the
probe if it was explicitly requested to use no ECC engine at all (for
instance, during development). This condition is met by just
commenting out the error on the OOB free bytes count after the
assignation of an ECC engine if none was provided (any other situation
would error out much earlier anyway).
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 36b99f68da81..314cbdad1462 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1129,9 +1129,11 @@ static int spinand_init(struct spinand_device *spinand)
mtd->_erase = spinand_mtd_erase;
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
- ret = mtd_ooblayout_count_freebytes(mtd);
- if (ret < 0)
- goto err_cleanup_ecc_engine;
+ if (nand->ecc.engine) {
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ goto err_cleanup_ecc_engine;
+ }
mtd->oobavail = ret;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 39/40] mtd: spinand: Fix OOB read
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, stable, Thomas Petazzoni,
Miquel Raynal, Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
So far OOB have never been used in SPI-NAND, add the missing memcpy to
make it work properly.
Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs")
Cc: stable@vger.kernel.org
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/spi/core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 314cbdad1462..f5d2fcae5e59 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -383,6 +383,10 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
req->datalen);
+ if (req->ooblen)
+ memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
+ req->ooblen);
+
return 0;
}
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 40/40] mtd: nand: ecc: Add infrastructure to support hardware engines
From: Miquel Raynal @ 2019-09-19 19:31 UTC (permalink / raw)
To: Richard Weinberger, David Woodhouse, Brian Norris, Marek Vasut,
Tudor Ambarus, Vignesh Raghavendra
Cc: Tudor Ambarus, Julien Su, Schrempf Frieder, Paul Cercueil,
Boris Brezillon, linux-mtd, Thomas Petazzoni, Miquel Raynal,
Mason Yang, linux-arm-kernel
In-Reply-To: <20190919193141.7865-1-miquel.raynal@bootlin.com>
Add the necessary helpers to register/unregister hardware ECC engines
that will be called from ECC engine drivers.
Also add helpers to get the right engine from the user
perspective. Keep a reference on the engine in use to prevent modules
to be unloaded. Put the reference if the engine is retired.
A static list of hardware (only) ECC engines is setup to keep track of
the registered engines.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/nand/core.c | 10 ++--
drivers/mtd/nand/ecc.c | 103 +++++++++++++++++++++++++++++++++++++++
include/linux/mtd/nand.h | 12 +++++
3 files changed, 122 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c
index 062a65e131db..55dc1920deae 100644
--- a/drivers/mtd/nand/core.c
+++ b/drivers/mtd/nand/core.c
@@ -232,7 +232,9 @@ static int nanddev_get_ecc_engine(struct nand_device *nand)
nand->ecc.engine = nand_ecc_get_ondie_engine(nand);
break;
case NAND_HW_ECC_ENGINE:
- pr_err("Hardware ECC engines not supported yet\n");
+ nand->ecc.engine = nand_ecc_get_hw_engine(nand);
+ if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
break;
default:
pr_err("Missing ECC engine provider\n");
@@ -252,7 +254,7 @@ static int nanddev_put_ecc_engine(struct nand_device *nand)
{
switch (nand->ecc.ctx.conf.provider) {
case NAND_HW_ECC_ENGINE:
- pr_err("Hardware ECC engines not supported yet\n");
+ nand_ecc_put_hw_engine(nand);
break;
case NAND_NO_ECC_ENGINE:
case NAND_SOFT_ECC_ENGINE:
@@ -297,7 +299,9 @@ int nanddev_ecc_engine_init(struct nand_device *nand)
/* Look for the ECC engine to use */
ret = nanddev_get_ecc_engine(nand);
if (ret) {
- pr_err("No ECC engine found\n");
+ if (ret != -EPROBE_DEFER)
+ pr_err("No ECC engine found\n");
+
return ret;
}
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
index 4f869d33213d..eabf936c5a50 100644
--- a/drivers/mtd/nand/ecc.c
+++ b/drivers/mtd/nand/ecc.c
@@ -96,6 +96,10 @@
#include <linux/module.h>
#include <linux/mtd/nand.h>
+#include <linux/of_platform.h>
+
+static LIST_HEAD(hw_engines);
+static DEFINE_MUTEX(hw_engines_mutex);
int nand_ecc_init_ctx(struct nand_device *nand)
{
@@ -480,6 +484,39 @@ bool nand_ecc_correction_is_enough(struct nand_device *nand)
}
EXPORT_SYMBOL(nand_ecc_correction_is_enough);
+int nand_ecc_register_hw_engine(struct nand_ecc_engine *engine)
+{
+ struct nand_ecc_engine *item;
+
+ if (!engine)
+ return -ENOTSUPP;
+
+ /* Prevent multiple registrations of one engine */
+ list_for_each_entry(item, &hw_engines, node)
+ if (item == engine)
+ return 0;
+
+ mutex_lock(&hw_engines_mutex);
+ list_add_tail(&engine->node, &hw_engines);
+ mutex_unlock(&hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_register_hw_engine);
+
+int nand_ecc_unregister_hw_engine(struct nand_ecc_engine *engine)
+{
+ if (!engine)
+ return -ENOTSUPP;
+
+ mutex_lock(&hw_engines_mutex);
+ list_del(&engine->node);
+ mutex_unlock(&hw_engines_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_ecc_unregister_hw_engine);
+
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand)
{
unsigned int algo = nand->ecc.user_conf.algo;
@@ -506,6 +543,72 @@ struct nand_ecc_engine *nand_ecc_get_ondie_engine(struct nand_device *nand)
}
EXPORT_SYMBOL(nand_ecc_get_ondie_engine);
+struct nand_ecc_engine *nand_ecc_match_hw_engine(struct device *dev)
+{
+ struct nand_ecc_engine *item;
+
+ list_for_each_entry(item, &hw_engines, node)
+ if (item->dev == dev)
+ return item;
+
+ return NULL;
+}
+EXPORT_SYMBOL(nand_ecc_match_hw_engine);
+
+struct nand_ecc_engine *nand_ecc_get_hw_engine(struct nand_device *nand)
+{
+ struct nand_ecc_engine *engine = NULL;
+ struct device *dev = &nand->mtd.dev;
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ if (list_empty(&hw_engines))
+ return NULL;
+
+ /* Check for an explicit ecc-engine property in the parent */
+ np = of_parse_phandle(dev->of_node->parent, "ecc-engine", 0);
+ if (np) {
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ engine = nand_ecc_match_hw_engine(&pdev->dev);
+ of_dev_put(pdev);
+ of_node_put(np);
+ }
+
+ /* Support DTs without ecc-engine property: check the parent node */
+ if (!engine) {
+ pdev = of_find_device_by_node(dev->of_node->parent);
+ if (pdev) {
+ engine = nand_ecc_match_hw_engine(&pdev->dev);
+ of_dev_put(pdev);
+ }
+ }
+
+ /* Support no DT or very old DTs: check the node itself */
+ if (!engine) {
+ pdev = of_find_device_by_node(dev->of_node);
+ if (pdev) {
+ engine = nand_ecc_match_hw_engine(&pdev->dev);
+ of_dev_put(pdev);
+ }
+ }
+
+ if (engine)
+ get_device(engine->dev);
+
+ return engine;
+}
+EXPORT_SYMBOL(nand_ecc_get_hw_engine);
+
+void nand_ecc_put_hw_engine(struct nand_device *nand)
+{
+ put_device(nand->ecc.engine->dev);
+}
+EXPORT_SYMBOL(nand_ecc_put_hw_engine);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
MODULE_DESCRIPTION("Generic ECC engine");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 5a745f74eb35..673d6c893f01 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -258,10 +258,16 @@ struct nand_ecc_engine_ops {
/**
* struct nand_ecc_engine - Generic ECC engine abstraction for NAND devices
+ * @dev: Host device
+ * @node: Private field for registration time
* @ops: ECC engine operations
+ * @priv: Private data
*/
struct nand_ecc_engine {
+ struct device *dev;
+ struct list_head node;
struct nand_ecc_engine_ops *ops;
+ void *priv;
};
void nand_ecc_read_user_conf(struct nand_device *nand);
@@ -272,8 +278,14 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
int nand_ecc_finish_io_req(struct nand_device *nand,
struct nand_page_io_req *req, void *oobbuf);
bool nand_ecc_correction_is_enough(struct nand_device *nand);
+int nand_ecc_register_hw_engine(struct nand_ecc_engine *engine);
+int nand_ecc_unregister_hw_engine(struct nand_ecc_engine *engine);
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
struct nand_ecc_engine *nand_ecc_get_ondie_engine(struct nand_device *nand);
+struct nand_ecc_engine *nand_ecc_get_hw_engine(struct nand_device *nand);
+struct nand_ecc_engine *nand_ecc_match_hw_engine(struct device *dev);
+void nand_ecc_put_hw_engine(struct nand_device *nand);
+
/**
* struct nand_ecc - High-level ECC object
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH 10/13] KVM: Provide common implementation for generic dirty log functions
From: Sean Christopherson @ 2019-09-19 19:39 UTC (permalink / raw)
To: Paul Mackerras
Cc: Julien Thierry, Cornelia Huck, Wanpeng Li, Janosch Frank, kvm,
Radim Krčmář, Marc Zyngier, James Hogan,
Joerg Roedel, David Hildenbrand, linux-mips, kvm-ppc,
linux-kernel, Christian Borntraeger, James Morse,
linux-arm-kernel, Paolo Bonzini, Vitaly Kuznetsov, kvmarm,
Suzuki K Pouloze, Jim Mattson
In-Reply-To: <20190919002242.GA19503@blackberry>
On Thu, Sep 19, 2019 at 10:22:42AM +1000, Paul Mackerras wrote:
> On Wed, Sep 11, 2019 at 11:50:35AM -0700, Sean Christopherson wrote:
> > Move the implementations of KVM_GET_DIRTY_LOG and KVM_CLEAR_DIRTY_LOG
> > for CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT into common KVM code.
> > The arch specific implemenations are extremely similar, differing
> > only in whether the dirty log needs to be sync'd from hardware (x86)
> > and how the TLBs are flushed. Add new arch hooks to handle sync
> > and TLB flush; the sync will also be used for non-generic dirty log
> > support in a future patch (s390).
> >
> > The ulterior motive for providing a common implementation is to
> > eliminate the dependency between arch and common code with respect to
> > the memslot referenced by the dirty log, i.e. to make it obvious in the
> > code that the validity of the memslot is guaranteed, as a future patch
> > will rework memslot handling such that id_to_memslot() can return NULL.
>
> I notice you add empty definitions of kvm_arch_sync_dirty_log() for
> PPC, both Book E and Book 3S. Given that PPC doesn't select
> CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT, why is this necessary?
s390 has a non-empty kvm_arch_sync_dirty_log() but doesn't select
CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT. Patch 11/13 moves s390's call
of kvm_arch_sync_dirty_log() from s390's kvm_vm_ioctl_get_dirty_log() into
the common (but not "generic") kvm_get_dirty_log() so that it's obvious
that kvm_vm_ioctl_get_dirty_log() and kvm_get_dirty_log() are operating on
the same memslot, i.e. aren't independently querying id_to_memslot().
I originally made kvm_arch_sync_dirty_log() opt-in with a __KVM_HAVE_ARCH
macro, but the resulting #ifdeffery felt uglier than having PPC and ARM
provide empty functions.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCHv6 01/10] dt-bindings: omap: add new binding for PRM instances
From: Rob Herring @ 2019-09-19 19:54 UTC (permalink / raw)
To: Tero Kristo
Cc: devicetree, Tony Lindgren, Philipp Zabel, Santosh Shilimkar,
linux-omap,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20190919123001.23081-1-t-kristo@ti.com>
On Thu, Sep 19, 2019 at 7:30 AM Tero Kristo <t-kristo@ti.com> wrote:
>
> Add new binding for OMAP PRM (Power and Reset Manager) instances. Each
> of these will act as a power domain controller and potentially as a reset
> provider.
>
> Signed-off-by: Tero Kristo <t-kristo@ti.com>
> ---
> v6: added common compatible as per request from Tony Lindgren. This is
> to simplify the support code in patch #10 of the series slightly
>
> .../devicetree/bindings/arm/omap/prm-inst.txt | 30 +++++++++++++++++++
> 1 file changed, 30 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/arm/omap/prm-inst.txt
Reviewed-by: Rob Herring <robh@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 7/8] PM / devfreq: Use dev_pm_qos for sysfs min/max_freq
From: Matthias Kaehlcke @ 2019-09-19 19:59 UTC (permalink / raw)
To: Leonard Crestez
Cc: Artur Świgoń, Abel Vesa, Saravana Kannan, linux-pm,
Viresh Kumar, Krzysztof Kozlowski, Chanwoo Choi, Kyungmin Park,
MyungJoo Ham, Alexandre Bailon, Georgi Djakov, linux-arm-kernel,
Jacky Bai
In-Reply-To: <a80dae0a1aec9932689aaadff68bcabc94a816be.1568764439.git.leonard.crestez@nxp.com>
On Wed, Sep 18, 2019 at 03:18:26AM +0300, Leonard Crestez wrote:
> Switch the handling of min_freq and max_freq from sysfs to use the
> dev_pm_qos interface.
nit: PM QoS?
if you agree please change all instances in comments.
> Since dev_pm_qos handles frequencies as kHz this change reduces the
> precision of min_freq and max_freq. This shouldn't introduce problems
> because frequencies which are not an integer number of kHz are likely
> not an integer number of Hz either.
>
> Try to ensure compatibilitity by rounding min values down and rounding
> max values up.
>
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> ---
> drivers/devfreq/devfreq.c | 51 +++++++++++++++++++++++++--------------
> include/linux/devfreq.h | 9 ++++---
> 2 files changed, 38 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index d8d57318b12c..7977bad93949 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -132,14 +132,10 @@ static void devfreq_get_freq_range(struct devfreq *devfreq,
> *min_freq = max(*min_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value(
> devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY));
> *max_freq = min(*max_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value(
> devfreq->dev.parent, DEV_PM_QOS_MAX_FREQUENCY));
>
> - /* constraints from sysfs: */
> - *min_freq = max(*min_freq, devfreq->min_freq);
> - *max_freq = min(*max_freq, devfreq->max_freq);
> -
> /* constraints from opp interface: */
> *min_freq = max(*min_freq, devfreq->scaling_min_freq);
> /* scaling_max_freq can be zero on error */
> if (devfreq->scaling_max_freq)
> *max_freq = min(*max_freq, devfreq->scaling_max_freq);
> @@ -675,10 +671,12 @@ static void devfreq_dev_release(struct device *dev)
> DEV_PM_QOS_MIN_FREQUENCY);
>
> if (devfreq->profile->exit)
> devfreq->profile->exit(devfreq->dev.parent);
>
> + dev_pm_qos_remove_request(&devfreq->max_freq_req);
> + dev_pm_qos_remove_request(&devfreq->min_freq_req);
mega-nit: keep common mix/max order since it doesn't really matter here?
> mutex_destroy(&devfreq->lock);
> kfree(devfreq->time_in_state);
> kfree(devfreq->trans_table);
> kfree(devfreq);
> }
> @@ -743,18 +741,28 @@ struct devfreq *devfreq_add_device(struct device *dev,
> devfreq->scaling_min_freq = find_available_min_freq(devfreq);
> if (!devfreq->scaling_min_freq) {
> err = -EINVAL;
> goto err_dev;
> }
> - devfreq->min_freq = devfreq->scaling_min_freq;
>
> devfreq->scaling_max_freq = find_available_max_freq(devfreq);
> if (!devfreq->scaling_max_freq) {
> err = -EINVAL;
> goto err_dev;
> }
> - devfreq->max_freq = devfreq->scaling_max_freq;
> +
> + /* dev_pm_qos requests for min/max freq from sysfs */
> + err = dev_pm_qos_add_request(dev, &devfreq->min_freq_req,
> + DEV_PM_QOS_MIN_FREQUENCY, 0);
> + if (err < 0) {
> + goto err_dev;
> + }
no curly braces needed for single line.
> + err = dev_pm_qos_add_request(dev, &devfreq->max_freq_req,
> + DEV_PM_QOS_MAX_FREQUENCY, S32_MAX);
> + if (err < 0) {
> + goto err_dev;
> + }
ditto
> devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
> atomic_set(&devfreq->suspend_count, 0);
>
> devfreq->trans_table = kzalloc(
> @@ -833,10 +841,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
> mutex_unlock(&devfreq_list_lock);
> err_devfreq:
> devfreq_remove_device(devfreq);
> return ERR_PTR(err);
> err_dev:
> + if (dev_pm_qos_request_active(&devfreq->max_freq_req))
> + dev_pm_qos_remove_request(&devfreq->max_freq_req);
> + if (dev_pm_qos_request_active(&devfreq->min_freq_req))
> + dev_pm_qos_remove_request(&devfreq->min_freq_req);
> kfree(devfreq->time_in_state);
> kfree(devfreq->trans_table);
> kfree(devfreq);
> err_out:
> return ERR_PTR(err);
> @@ -1397,14 +1409,17 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
>
> ret = sscanf(buf, "%lu", &value);
> if (ret != 1)
> return -EINVAL;
>
> - mutex_lock(&df->lock);
> - df->min_freq = value;
> - update_devfreq(df);
> - mutex_unlock(&df->lock);
> + /* round down to kHz for dev_pm_qos */
> + if (value)
> + value = value / HZ_PER_KHZ;
> +
> + ret = dev_pm_qos_update_request(&df->min_freq_req, value);
> + if (ret < 0)
> + return ret;
>
> return count;
> }
>
> static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
> @@ -1429,19 +1444,19 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
>
> ret = sscanf(buf, "%lu", &value);
> if (ret != 1)
> return -EINVAL;
>
> - mutex_lock(&df->lock);
> -
> - /* Interpret zero as "don't care" */
> - if (!value)
> - value = ULONG_MAX;
> + /* round up to kHz for dev_pm_qos and interpret zero as "don't care" */
> + if (value)
> + value = DIV_ROUND_UP(value, HZ_PER_KHZ);
> + else
> + value = S32_MAX;
>
> - df->max_freq = value;
> - update_devfreq(df);
> - mutex_unlock(&df->lock);
> + ret = dev_pm_qos_update_request(&df->max_freq_req, value);
> + if (ret < 0)
> + return ret;
>
> return count;
> }
> static DEVICE_ATTR_RW(min_freq);
>
> diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
> index dac0dffeabb4..4b5cc80abbe3 100644
> --- a/include/linux/devfreq.h
> +++ b/include/linux/devfreq.h
> @@ -11,10 +11,11 @@
> #define __LINUX_DEVFREQ_H__
>
> #include <linux/device.h>
> #include <linux/notifier.h>
> #include <linux/pm_opp.h>
> +#include <linux/pm_qos.h>
>
> #define DEVFREQ_NAME_LEN 16
>
> /* DEVFREQ governor name */
> #define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand"
> @@ -121,12 +122,12 @@ struct devfreq_dev_profile {
> * devfreq.nb to the corresponding register notifier call chain.
> * @work: delayed work for load monitoring.
> * @previous_freq: previously configured frequency value.
> * @data: Private data of the governor. The devfreq framework does not
> * touch this.
> - * @min_freq: Limit minimum frequency requested by user (0: none)
> - * @max_freq: Limit maximum frequency requested by user (0: none)
> + * @min_freq_req: Limit minimum frequency requested by user (0: none)
'(0: none)' is not correct anymore.
Maybe also say that it's a PM QoS request?
Since you are already changing the variable name it could be a good
opportunity to make it more specific, i.e. make clear that it's the
userspace constraint.
e.g.
min_freq_req_user
user_min_freq_req
min_freq_user_req
or
struct {
struct {
min;
max;
} user;
struct {
min;
max;
} scaling; // not a great name, but that's what it is currently ...
} freq_constraints;
> + * @max_freq_req: Limit maximum frequency requested by user (0: none)
> * @scaling_min_freq: Limit minimum frequency requested by OPP interface
> * @scaling_max_freq: Limit maximum frequency requested by OPP interface
> * @stop_polling: devfreq polling status of a device.
> * @suspend_freq: frequency of a device set during suspend phase.
> * @resume_freq: frequency of a device set in resume phase.
> @@ -161,12 +162,12 @@ struct devfreq {
> unsigned long previous_freq;
> struct devfreq_dev_status last_status;
>
> void *data; /* private data for governors */
>
> - unsigned long min_freq;
> - unsigned long max_freq;
> + struct dev_pm_qos_request min_freq_req;
> + struct dev_pm_qos_request max_freq_req;
> unsigned long scaling_min_freq;
> unsigned long scaling_max_freq;
> bool stop_polling;
>
> unsigned long suspend_freq;
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 2/3] soc: amlogic: Add support for Secure power domains controller
From: Martin Blumenstingl @ 2019-09-19 20:03 UTC (permalink / raw)
To: Jianxin Pan
Cc: devicetree, Hanjie Lin, Victor Wan, Neil Armstrong, Kevin Hilman,
linux-pm, linux-kernel, Zhiqiang Liang, Rob Herring, Jian Hu,
Xingyu Chen, linux-amlogic, linux-arm-kernel, Jerome Brunet
In-Reply-To: <1568895064-4116-3-git-send-email-jianxin.pan@amlogic.com>
Hi Jianxin,
I added three comments below from a quick glance at this driver (I
didn't have time for a complete review)
On Thu, Sep 19, 2019 at 2:11 PM Jianxin Pan <jianxin.pan@amlogic.com> wrote:
[...]
> + pm_genpd_init(&dom->base, NULL,
> + (match->domains[i].get_power ?
> + match->domains[i].get_power(dom) : true));
.get_power is never NULL in this driver so the ": true" part is
effectively a no-op
[...]
> +static const struct of_device_id meson_secure_pwrc_match_table[] = {
> + {
> + .compatible = "amlogic,meson-a1-pwrc",
> + .data = &meson_secure_a1_pwrc_data,
> + },
> + { }
many drivers use a /* sentinel */ comment inside { }
[...]
> +arch_initcall_sync(meson_secure_pwrc_init);
why arch_initcall_sync instead of builtin_platform_driver?
$ grep -R arch_initcall_sync drivers/soc/
$
Martin
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 1/3] dt-bindings: power: add Amlogic secure power domains bindings
From: Martin Blumenstingl @ 2019-09-19 20:06 UTC (permalink / raw)
To: Jianxin Pan
Cc: devicetree, Hanjie Lin, Victor Wan, Neil Armstrong, Kevin Hilman,
linux-pm, linux-kernel, Zhiqiang Liang, Rob Herring, Jian Hu,
Xingyu Chen, linux-amlogic, linux-arm-kernel, Jerome Brunet
In-Reply-To: <1568895064-4116-2-git-send-email-jianxin.pan@amlogic.com>
Hi Jianxin,
On Thu, Sep 19, 2019 at 2:11 PM Jianxin Pan <jianxin.pan@amlogic.com> wrote:
>
> Add the bindings for the Amlogic Secure power domains, controlling the
> secure power domains.
>
> The bindings targets the Amlogic A1 and C1 compatible SoCs, in which the
> power domain registers are in secure world.
>
> Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
> Signed-off-by: Zhiqiang Liang <zhiqiang.liang@amlogic.com>
> ---
> .../bindings/power/amlogic,meson-sec-pwrc.yaml | 32 ++++++++++++++++++++++
> include/dt-bindings/power/meson-a1-power.h | 32 ++++++++++++++++++++++
> 2 files changed, 64 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml
> create mode 100644 include/dt-bindings/power/meson-a1-power.h
>
> diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml
> new file mode 100644
> index 00000000..327e0d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml
> @@ -0,0 +1,32 @@
> +# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +# Copyright (c) 2019 Amlogic, Inc
> +# Author: Jianxin Pan <jianxin.pan@amlogic.com>
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/power/amlogic,meson-sec-pwrc.yaml#"
> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: Amlogic Meson Secure Power Domains
> +
> +maintainers:
> + - Jianxin Pan <jianxin.pan@amlogic.com>
> +
> +description: |+
> + A1/C1 series The Secure Power Domains node should be the child of a syscon
> + node with the required property.
> +
> +properties:
> + compatible:
> + enum:
> + - amlogic,meson-a1-pwrc
> +
> +required:
> + - compatible
> +
> +examples:
> + - |
> + pwrc: power-controller {
> + compatible = "amlogic,meson-a1-pwrc";
> + };
not a comment about this binding but about the secure monitor in general:
there's a recent discussion about the secure monitor in the nvmem bindings: [0]
Martin
[0] https://www.spinics.net/lists/arm-kernel/msg750010.html
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* SPE capable Arm CPUs in the fields
From: Itaru Kitayama @ 2019-09-19 20:23 UTC (permalink / raw)
To: linux-arm-kernel
Will,
Though SPE is just an optional feature of the Armv8.2-A extension, I am wondering whether CPUs are already out in the fields or some vendors are planning to make one of two in the near future.
Itaru.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v4 3/5] locking/qspinlock: Introduce CNA into the slow path of qspinlock
From: Waiman Long @ 2019-09-19 20:54 UTC (permalink / raw)
To: Alex Kogan
Cc: linux-arch, guohanjun, arnd, peterz, dave.dice, jglauber, x86,
will.deacon, linux, linux-kernel, rahul.x.yadav, mingo, bp, hpa,
steven.sistare, tglx, daniel.m.jordan, linux-arm-kernel
In-Reply-To: <87B87982-670F-4F12-9EE0-DC89A059FAEC@oracle.com>
On 9/19/19 11:55 AM, Alex Kogan wrote:
>>> +/*
>>> + * cna_try_find_next - scan the main waiting queue looking for the first
>>> + * thread running on the same NUMA node as the lock holder. If found (call it
>>> + * thread T), move all threads in the main queue between the lock holder and
>>> + * T to the end of the secondary queue and return T; otherwise, return NULL.
>>> + *
>>> + * Schematically, this may look like the following (nn stands for numa_node and
>>> + * et stands for encoded_tail).
>>> + *
>>> + * when cna_try_find_next() is called (the secondary queue is empty):
>>> + *
>>> + * A+------------+ B+--------+ C+--------+ T+--------+
>>> + * |mcs:next | -> |mcs:next| -> |mcs:next| -> |mcs:next| -> NULL
>>> + * |mcs:locked=1| |cna:nn=0| |cna:nn=2| |cna:nn=1|
>>> + * |cna:nn=1 | +--------+ +--------+ +--------+
>>> + * +----------- +
>>> + *
>>> + * when cna_try_find_next() returns (the secondary queue contains B and C):
>>> + *
>>> + * A+----------------+ T+--------+
>>> + * |mcs:next | -> |mcs:next| -> NULL
>>> + * |mcs:locked=B.et | -+ |cna:nn=1|
>>> + * |cna:nn=1 | | +--------+
>>> + * +--------------- + |
>>> + * |
>>> + * +-> B+--------+ C+--------+
>>> + * |mcs:next| -> |mcs:next|
>>> + * |cna:nn=0| |cna:nn=2|
>>> + * |cna:tail| -> +--------+
>>> + * +--------+
>>> + *
>>> + * The worst case complexity of the scan is O(n), where n is the number
>>> + * of current waiters. However, the fast path, which is expected to be the
>>> + * common case, is O(1).
>>> + */
>>> +static struct mcs_spinlock *cna_try_find_next(struct mcs_spinlock *node,
>>> + struct mcs_spinlock *next)
>>> +{
>>> + struct cna_node *cn = (struct cna_node *)node;
>>> + struct cna_node *cni = (struct cna_node *)next;
>>> + struct cna_node *first, *last = NULL;
>>> + int my_numa_node = cn->numa_node;
>>> +
>>> + /* fast path: immediate successor is on the same NUMA node */
>>> + if (cni->numa_node == my_numa_node)
>>> + return next;
>>> +
>>> + /* find any next waiter on 'our' NUMA node */
>>> + for (first = cni;
>>> + cni && cni->numa_node != my_numa_node;
>>> + last = cni, cni = (struct cna_node *)READ_ONCE(cni->mcs.next))
>>> + ;
>>> +
>>> + /* if found, splice any skipped waiters onto the secondary queue */
>>> + if (cni && last)
>>> + cna_splice_tail(cn, first, last);
>>> +
>>> + return (struct mcs_spinlock *)cni;
>>> +}
>> At the Linux Plumbers Conference last week, Will has raised the concern
>> about the latency of the O(1) cna_try_find_next() operation that will
>> add to the lock hold time.
> While the worst case complexity of the scan is O(n), I _think it can be proven
> that the amortized complexity is O(1). For intuition, consider a two-node
> system with N threads total. In the worst case scenario, the scan will go
> over N/2 threads running on a different node. If the scan ultimately “fails”
> (no thread from the lock holder’s node is found), the lock will be passed
> to the first thread from a different node and then between all those N/2 threads,
> with a scan of just one node for the next N/2 - 1 passes. Otherwise, those
> N/2 threads will be moved to the secondary queue. On the next lock handover,
> we pass the lock either to the next thread in the main queue (as it has to be
> from our node) or to the first node in the secondary queue. In both cases, we
> scan just one node, and in the latter case, we have again N/2 - 1 passes with
> a scan of just one node each.
I agree that it should not be a problem for a 2-socket. For larger SMP
systems with 8, 16 or even 32 sockets, it can be an issue as those
systems are also more likely to have more lock contention and hence
longer wait queues.
>> One way to hide some of the latency is to do
>> a pre-scan before acquiring the lock. The CNA code could override the
>> pv_wait_head_or_lock() function to call cna_try_find_next() as a
>> pre-scan and return 0. What do you think?
> This is certainly possible, but I do not think it would completely eliminate
> the worst case scenario. It will probably make it even less likely, but at
> the same time, we will reduce the chance of actually finding a thread from the
> same node (that may enter the main queue while we wait for the owner & pending
> to go away).
When I said prescan, I mean to move the front queue entries that are
from non-local nodes to the secondary queue before acquiring the lock.
After acquiring the lock, you can repeat the scan in case the prescan
didn't find any local node queue entry. Yes, we will need to do the
similar operation twice.
Yes, it does not eliminate the worst case scenario, but it should help
in reducing the average lock hold time.
Of course, the probabilistic (or deterministic) check to go to the next
local node entry or to the secondary queue should be done before
pre-scan so that we won't waste the effort.
Cheers,
Longman
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox