aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/raw/omap2.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 11:21:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 11:21:51 -0700
commitda85e7ed6993144a9ca43a1106c7f898626390a3 (patch)
treea5fdbea1cfe8deb13bcbef1b6e102ad1ca965e34 /drivers/mtd/nand/raw/omap2.c
parentMerge tag 'm68knommu-for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/gerg/m68knommu (diff)
parentMerge tag 'spi-nor/for-5.14' into mtd/next (diff)
downloadlinux-dev-da85e7ed6993144a9ca43a1106c7f898626390a3.tar.xz
linux-dev-da85e7ed6993144a9ca43a1106c7f898626390a3.zip
Merge tag 'mtd/for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull MTD updates from Richard Weinberger: "MTD core changes: - Convert list_for_each to entry variant - Use MTD_DEVICE_ATTR_RO/RW() helper macros - Remove unnecessary OOM messages - Potential NULL dereference in mtd_otp_size() - Fix freeing of otp_info buffer - Create partname and partid debug files for child MTDs - tests: - Remove redundant assignment to err - Fix error return code in mtd_oobtest_init() - Add OTP NVMEM provider support - Allow specifying of_node - Convert sysfs sprintf/snprintf family to sysfs_emit Bindings changes: - Convert ti,am654-hbmc.txt to YAML schema - spi-nor: add otp property - Add OTP bindings - add YAML schema for the generic MTD bindings - Add brcm,trx-magic MTD device drivers changes: - Add support for microchip 48l640 EERAM - Remove superfluous "break" - sm_ftl: - Fix alignment of block comment - nftl: - Return -ENOMEM when kmalloc failed - nftlcore: - Remove set but rewrite variables - phram: - Fix error return code in phram_setup() - plat-ram: - Remove redundant dev_err call in platram_probe() MTD parsers changes: - Qcom: - Fix leaking of partition name - Redboot: - Fix style issues - Seek fis-index-block in the right node - trx: - Allow to use TRX parser on Mediatek SoCs - Allow to specify brcm, trx-magic in DT Raw NAND core: - Allow SDR timings to be nacked - Bring support for NV-DDR timings which involved a number of small preparation changes to bring new helpers, properly introduce NV-DDR structures, fill them, differenciate them and pick the best timing set. - Add the necessary infrastructure to parse the new gpio-cs property which aims at enlarging the number of available CS when a hardware controller is too constrained. - Update dead URL - Silence static checker warning in nand_setup_interface() - BBT: - Fix corner case in bad block table handling - onfi: - Use more recent ONFI specification wording - Use the BIT() macro when possible Raw NAND controller drivers: - Atmel: - Ensure the data interface is supported. - Arasan: - Finer grain NV-DDR configuration - Rename the data interface register - Use the right DMA mask - Leverage additional GPIO CS - Ensure proper configuration for the asserted target - Add support for the NV-DDR interface - Fix a macro parameter - brcmnand: - Convert bindings to json-schema - OMAP: - Various fixes and style improvements - Add larger page NAND chips support - PL35X: - New driver - QCOM: - Avoid writing to obsolete register - Delete an unneeded bool conversion - Allow override of partition parser - Marvell: - Minor documentation correction - Add missing clk_disable_unprepare() on error in marvell_nfc_resume() - R852: - Use DEVICE_ATTR_RO() helper macro - MTK: - Remove redundant dev_err call in mtk_ecc_probe() - HISI504: - Remove redundant dev_err call in probe SPI-NAND core: - Light reorganisation for the introduction of a core resume handler - Fix double counting of ECC stats SPI-NAND manufacturer drivers: - Macronix: - Add support for serial NAND flash SPI NOR core changes: - Ability to dump SFDP tables via sysfs - Support for erasing OTP regions on Winbond and similar flashes - Few API doc updates and fixes - Locking support for MX25L12805D SPI NOR controller drivers changes: - Use SPI_MODE_X_MASK in nxp-spifi - Intel Alder Lake-M SPI serial flash support" * tag 'mtd/for-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (125 commits) mtd: spi-nor: remove redundant continue statement mtd: rawnand: omap: Add larger page NAND chips support mtd: rawnand: omap: Various style fixes mtd: rawnand: omap: Check return values mtd: rawnand: omap: Rename a macro mtd: rawnand: omap: Aggregate the HW configuration of the ELM mtd: rawnand: pl353: Add support for the ARM PL353 SMC NAND controller dt-bindings: mtd: pl353-nand: Describe this hardware controller MAINTAINERS: Add PL353 NAND controller entry mtd: rawnand: qcom: avoid writing to obsolete register mtd: rawnand: marvell: Minor documentation correction mtd: rawnand: r852: use DEVICE_ATTR_RO() helper macro mtd: spinand: add SPI-NAND MTD resume handler mtd: spinand: Add spinand_init_flash() helper mtd: spinand: add spinand_read_cfg() helper mtd: rawnand: marvell: add missing clk_disable_unprepare() on error in marvell_nfc_resume() mtd: rawnand: arasan: Finer grain NV-DDR configuration mtd: rawnand: arasan: Rename the data interface register mtd: rawnand: onfi: Fix endianness when reading NV-DDR values mtd: rawnand: arasan: Use the right DMA mask ...
Diffstat (limited to 'drivers/mtd/nand/raw/omap2.c')
-rw-r--r--drivers/mtd/nand/raw/omap2.c229
1 files changed, 141 insertions, 88 deletions
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index c75e7a0b101f..b1839eef5b65 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -131,7 +131,7 @@
#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
-#define BADBLOCK_MARKER_LENGTH 2
+#define BBM_LEN 2
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
@@ -171,6 +171,10 @@ struct omap_nand_info {
struct device *elm_dev;
/* NAND ready gpio */
struct gpio_desc *ready_gpiod;
+ unsigned int neccpg;
+ unsigned int nsteps_per_eccpg;
+ unsigned int eccpg_size;
+ unsigned int eccpg_bytes;
};
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -1355,7 +1359,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
{
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
- int eccsteps = info->nand.ecc.steps;
+ int eccsteps = info->nsteps_per_eccpg;
int i , j, stat = 0;
int eccflag, actual_eccbytes;
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
@@ -1525,24 +1529,37 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint8_t *ecc_calc = chip->ecc.calc_buf;
+ unsigned int eccpg;
+ int ret;
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- /* Write data */
- chip->legacy.write_buf(chip, buf, mtd->writesize);
+ /* Write data */
+ chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+ info->eccpg_size);
- /* Update ecc vector from GPMC result registers */
- omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
+ /* Update ecc vector from GPMC result registers */
+ ret = omap_calculate_ecc_bch_multi(mtd,
+ buf + (eccpg * info->eccpg_size),
+ ecc_calc);
+ if (ret)
+ return ret;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
+ chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
+ if (ret)
+ return ret;
+ }
/* Write ecc vector to OOB area */
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
@@ -1566,12 +1583,13 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
u8 *ecc_calc = chip->ecc.calc_buf;
int ecc_size = chip->ecc.size;
int ecc_bytes = chip->ecc.bytes;
- int ecc_steps = chip->ecc.steps;
u32 start_step = offset / ecc_size;
u32 end_step = (offset + data_len - 1) / ecc_size;
+ unsigned int eccpg;
int step, ret = 0;
/*
@@ -1580,36 +1598,48 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
* ECC is calculated for all subpages but we choose
* only what we want.
*/
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- /* Enable GPMC ECC engine */
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
-
- /* Write data */
- chip->legacy.write_buf(chip, buf, mtd->writesize);
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- for (step = 0; step < ecc_steps; step++) {
- /* mask ECC of un-touched subpages by padding 0xFF */
- if (step < start_step || step > end_step)
- memset(ecc_calc, 0xff, ecc_bytes);
- else
- ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ECC engine */
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+ /* Write data */
+ chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+ info->eccpg_size);
+
+ for (step = 0; step < info->nsteps_per_eccpg; step++) {
+ unsigned int base_step = eccpg * info->nsteps_per_eccpg;
+ const u8 *bufoffs = buf + (eccpg * info->eccpg_size);
+
+ /* Mask ECC of un-touched subpages with 0xFFs */
+ if ((step + base_step) < start_step ||
+ (step + base_step) > end_step)
+ memset(ecc_calc + (step * ecc_bytes), 0xff,
+ ecc_bytes);
+ else
+ ret = _omap_calculate_ecc_bch(mtd,
+ bufoffs + (step * ecc_size),
+ ecc_calc + (step * ecc_bytes),
+ step);
+
+ if (ret)
+ return ret;
+ }
+ /*
+ * Copy the calculated ECC for the whole page including the
+ * masked values (0xFF) corresponding to unwritten subpages.
+ */
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
if (ret)
return ret;
-
- buf += ecc_size;
- ecc_calc += ecc_bytes;
}
- /* copy calculated ECC for whole page to chip->buffer->oob */
- /* this include masked-value(0xFF) for unwritten subpages */
- ecc_calc = chip->ecc.calc_buf;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
/* write OOB buffer to NAND device */
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
@@ -1634,40 +1664,60 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint8_t *ecc_calc = chip->ecc.calc_buf;
uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0, eccpg;
int stat, ret;
- unsigned int max_bitflips = 0;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(chip, NAND_ECC_READ);
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- /* Read data */
- chip->legacy.read_buf(chip, buf, mtd->writesize);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
- /* Read oob bytes */
- nand_change_read_column_op(chip,
- mtd->writesize + BADBLOCK_MARKER_LENGTH,
- chip->oob_poi + BADBLOCK_MARKER_LENGTH,
- chip->ecc.total, false);
+ /* Read data */
+ ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size,
+ buf + (eccpg * info->eccpg_size),
+ info->eccpg_size, false);
+ if (ret)
+ return ret;
- /* Calculate ecc bytes */
- omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
+ /* Read oob bytes */
+ ret = nand_change_read_column_op(chip,
+ mtd->writesize + BBM_LEN +
+ (eccpg * info->eccpg_bytes),
+ chip->oob_poi + BBM_LEN +
+ (eccpg * info->eccpg_bytes),
+ info->eccpg_bytes, false);
+ if (ret)
+ return ret;
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
+ /* Calculate ecc bytes */
+ ret = omap_calculate_ecc_bch_multi(mtd,
+ buf + (eccpg * info->eccpg_size),
+ ecc_calc);
+ if (ret)
+ return ret;
- stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc);
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code,
+ chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
+ if (ret)
+ return ret;
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ stat = chip->ecc.correct(chip,
+ buf + (eccpg * info->eccpg_size),
+ ecc_code, ecc_calc);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
}
return max_bitflips;
@@ -1820,7 +1870,7 @@ static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
@@ -1840,7 +1890,7 @@ static int omap_ooblayout_free(struct mtd_info *mtd, int section,
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
@@ -1870,7 +1920,7 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct nand_device *nand = mtd_to_nanddev(mtd);
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (section >= nsteps)
return -ERANGE;
@@ -1891,7 +1941,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
struct nand_device *nand = mtd_to_nanddev(mtd);
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (section)
return -ERANGE;
@@ -1920,7 +1970,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
struct mtd_info *mtd = nand_to_mtd(chip);
struct omap_nand_info *info = mtd_to_omap(mtd);
struct device *dev = &info->pdev->dev;
- int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+ int min_oobbytes = BBM_LEN;
+ int elm_bch_strength = -1;
int oobbytes_per_step;
dma_cap_mask_t mask;
int err;
@@ -2074,12 +2125,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH4_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
+ elm_bch_strength = BCH4_ECC;
break;
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
@@ -2116,13 +2162,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH8_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
-
+ elm_bch_strength = BCH8_ECC;
break;
case OMAP_ECC_BCH16_CODE_HW:
@@ -2138,19 +2178,32 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH16_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
-
+ elm_bch_strength = BCH16_ECC;
break;
default:
dev_err(dev, "Invalid or unsupported ECC scheme\n");
return -EINVAL;
}
+ if (elm_bch_strength >= 0) {
+ chip->ecc.steps = mtd->writesize / chip->ecc.size;
+ info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
+ if (info->neccpg) {
+ info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
+ } else {
+ info->neccpg = 1;
+ info->nsteps_per_eccpg = chip->ecc.steps;
+ }
+ info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
+ info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
+
+ err = elm_config(info->elm_dev, elm_bch_strength,
+ info->nsteps_per_eccpg, chip->ecc.size,
+ chip->ecc.bytes);
+ if (err < 0)
+ return err;
+ }
+
/* Check if NAND device's OOB is enough to store ECC signatures */
min_oobbytes += (oobbytes_per_step *
(mtd->writesize / chip->ecc.size));