+static void omap_rotate_ecc_bch(struct mtd_info *mtd, uint8_t *calc_ecc,
+ uint8_t *syndrome)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *bch = chip->priv;
+ uint8_t n_bytes = 0;
+ int8_t i, j;
+
+ switch (bch->type) {
+ case ECC_BCH4:
+ n_bytes = 8;
+ break;
+
+ case ECC_BCH16:
+ n_bytes = 28;
+ break;
+
+ case ECC_BCH8:
+ default:
+ n_bytes = 13;
+ break;
+ }
+
+ for (i = 0, j = (n_bytes-1); i < n_bytes; i++, j--)
+ syndrome[i] = calc_ecc[j];
+}
+
+/*
+ * omap_calculate_ecc_bch - Read BCH ECC result
+ *
+ * @mtd: MTD structure
+ * @dat: unused
+ * @ecc_code: ecc_code buffer
+ */
+static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *bch = chip->priv;
+ uint8_t big_endian = 1;
+ int8_t ret = 0;
+
+ if (bch->type == ECC_BCH8)
+ omap_read_bch8_result(mtd, big_endian, ecc_code);
+ else /* BCH4 and BCH16 currently not supported */
+ ret = -1;
+
+ /*
+ * Stop reading anymore ECC vals and clear old results
+ * enable will be called if more reads are required
+ */
+ omap_ecc_disable(mtd);
+
+ return ret;
+}
+
+/*
+ * omap_fix_errors_bch - Correct bch error in the data
+ *
+ * @mtd: MTD device structure
+ * @data: Data read from flash
+ * @error_count:Number of errors in data
+ * @error_loc: Locations of errors in the data
+ *
+ */
+static void omap_fix_errors_bch(struct mtd_info *mtd, uint8_t *data,
+ uint32_t error_count, uint32_t *error_loc)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *bch = chip->priv;
+ uint8_t count = 0;
+ uint32_t error_byte_pos;
+ uint32_t error_bit_mask;
+ uint32_t last_bit = (bch->nibbles * 4) - 1;
+
+ /* Flip all bits as specified by the error location array. */
+ /* FOR( each found error location flip the bit ) */
+ for (count = 0; count < error_count; count++) {
+ if (error_loc[count] > last_bit) {
+ /* Remove the ECC spare bits from correction. */
+ error_loc[count] -= (last_bit + 1);
+ /* Offset bit in data region */
+ error_byte_pos = ((512 * 8) -
+ (error_loc[count]) - 1) / 8;
+ /* Error Bit mask */
+ error_bit_mask = 0x1 << (error_loc[count] % 8);
+ /* Toggle the error bit to make the correction. */
+ data[error_byte_pos] ^= error_bit_mask;
+ }
+ }
+}
+
+/*
+ * omap_correct_data_bch - Compares the ecc read from nand spare area
+ * with ECC registers values and corrects one bit error if it has occured
+ *
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash (ignored)
+ * @calc_ecc: ecc read from ECC registers
+ *
+ * @return 0 if data is OK or corrected, else returns -1
+ */
+static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *bch = chip->priv;
+ uint8_t syndrome[28];
+ uint32_t error_count = 0;
+ uint32_t error_loc[8];
+ uint32_t i, ecc_flag;
+
+ ecc_flag = 0;
+ for (i = 0; i < chip->ecc.bytes; i++)
+ if (read_ecc[i] != 0xff)
+ ecc_flag = 1;
+
+ if (!ecc_flag)
+ return 0;
+
+ elm_reset();
+ elm_config((enum bch_level)(bch->type));
+
+ /*
+ * while reading ECC result we read it in big endian.
+ * Hence while loading to ELM we have rotate to get the right endian.
+ */
+ omap_rotate_ecc_bch(mtd, calc_ecc, syndrome);
+
+ /* use elm module to check for errors */
+ if (elm_check_error(syndrome, bch->nibbles, &error_count,
+ error_loc) != 0) {
+ printf("ECC: uncorrectable.\n");
+ return -1;
+ }
+
+ /* correct bch error */
+ if (error_count > 0)
+ omap_fix_errors_bch(mtd, dat, error_count, error_loc);
+
+ return 0;
+}
+
+/**
+ * omap_read_page_bch - hardware ecc based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ */
+static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *oob = chip->oob_poi;
+ uint32_t data_pos;
+ uint32_t oob_pos;
+
+ data_pos = 0;
+ /* oob area start */
+ oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
+ oob += chip->ecc.layout->eccpos[0];
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
+ oob += eccbytes) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ /* read data */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page);
+ chip->read_buf(mtd, p, eccsize);
+
+ /* read respective ecc from oob area */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page);
+ chip->read_buf(mtd, oob, eccbytes);
+ /* read syndrome */
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ data_pos += eccsize;
+ oob_pos += eccbytes;
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+#endif /* CONFIG_NAND_OMAP_ELM */
+
+/*
+ * OMAP3 BCH8 support (with BCH library)
+ */
+#ifdef CONFIG_BCH
+/*
+ * omap_calculate_ecc_bch_sw - Read BCH ECC result
+ *
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed (unused here)
+ * @ecc: The ECC output buffer
+ */
+static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc)
+{
+ int ret = 0;
+ size_t i;
+ unsigned long nsectors, val1, val2, val3, val4;
+
+ nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1;
+
+ for (i = 0; i < nsectors; i++) {
+ /* Read hw-computed remainder */
+ val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]);
+ val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]);
+ val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]);
+ val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]);
+
+ /*
+ * Add constant polynomial to remainder, in order to get an ecc
+ * sequence of 0xFFs for a buffer filled with 0xFFs.
+ */
+ *ecc++ = 0xef ^ (val4 & 0xFF);
+ *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
+ *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
+ *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
+ *ecc++ = 0xed ^ (val3 & 0xFF);
+ *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
+ *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
+ *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
+ *ecc++ = 0x97 ^ (val2 & 0xFF);
+ *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
+ *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
+ *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
+ *ecc++ = 0xb5 ^ (val1 & 0xFF);
+ }
+
+ /*
+ * Stop reading anymore ECC vals and clear old results
+ * enable will be called if more reads are required
+ */
+ omap_ecc_disable(mtd);
+
+ return ret;
+}
+
+/**
+ * omap_correct_data_bch_sw - Decode received data and correct errors
+ * @mtd: MTD device structure
+ * @data: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ */
+static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ int i, count;
+ /* cannot correct more than 8 errors */
+ unsigned int errloc[8];
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *chip_priv = chip->priv;
+ struct bch_control *bch = chip_priv->control;
+
+ count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc);
+ if (count > 0) {
+ /* correct errors */
+ for (i = 0; i < count; i++) {
+ /* correct data only, not ecc bytes */
+ if (errloc[i] < 8*512)
+ data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
+ printf("corrected bitflip %u\n", errloc[i]);
+#ifdef DEBUG
+ puts("read_ecc: ");
+ /*
+ * BCH8 have 13 bytes of ECC; BCH4 needs adoption
+ * here!
+ */
+ for (i = 0; i < 13; i++)
+ printf("%02x ", read_ecc[i]);
+ puts("\n");
+ puts("calc_ecc: ");
+ for (i = 0; i < 13; i++)
+ printf("%02x ", calc_ecc[i]);
+ puts("\n");
+#endif
+ }
+ } else if (count < 0) {
+ puts("ecc unrecoverable error\n");
+ }
+ return count;
+}
+
+/**
+ * omap_free_bch - Release BCH ecc resources
+ * @mtd: MTD device structure
+ */
+static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *chip_priv = chip->priv;
+ struct bch_control *bch = NULL;
+
+ if (chip_priv)
+ bch = chip_priv->control;
+
+ if (bch) {
+ free_bch(bch);
+ chip_priv->control = NULL;
+ }
+}
+#endif /* CONFIG_BCH */
+
+/**
+ * omap_select_ecc_scheme - configures driver for particular ecc-scheme
+ * @nand: NAND chip device structure
+ * @ecc_scheme: ecc scheme to configure
+ * @pagesize: number of main-area bytes per page of NAND device
+ * @oobsize: number of OOB/spare bytes per page of NAND device
+ */
+static int omap_select_ecc_scheme(struct nand_chip *nand,
+ enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) {
+ struct nand_bch_priv *bch = nand->priv;
+ struct nand_ecclayout *ecclayout = &omap_ecclayout;
+ int eccsteps = pagesize / SECTOR_BYTES;
+ int i;
+
+ switch (ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n");
+ /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are
+ * initialized in nand_scan_tail(), so just set ecc.mode */
+ bch_priv.control = NULL;
+ bch_priv.type = 0;
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.layout = NULL;
+ nand->ecc.size = 0;
+ bch->ecc_scheme = OMAP_ECC_HAM1_CODE_SW;
+ break;
+
+ case OMAP_ECC_HAM1_CODE_HW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (3 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ bch_priv.control = NULL;
+ bch_priv.type = 0;
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 1;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 3;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data;
+ nand->ecc.calculate = omap_calculate_ecc;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ for (i = 0; i < ecclayout->eccbytes; i++) {
+ if (nand->options & NAND_BUSWIDTH_16)
+ ecclayout->eccpos[i] = i + 2;
+ else
+ ecclayout->eccpos[i] = i + 1;
+ }
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ bch->ecc_scheme = OMAP_ECC_HAM1_CODE_HW;
+ break;
+
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#ifdef CONFIG_BCH
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (13 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ /* check if BCH S/W library can be used for error detection */
+ bch_priv.control = init_bch(13, 8, 0x201b);
+ if (!bch_priv.control) {
+ printf("nand: error: could not init_bch()\n");
+ return -ENODEV;
+ }
+ bch_priv.type = ECC_BCH8;
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 13;
+ nand->ecc.hwctl = omap_enable_ecc_bch;
+ nand->ecc.correct = omap_correct_data_bch_sw;
+ nand->ecc.calculate = omap_calculate_ecc_bch_sw;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH;
+ for (i = 1; i < ecclayout->eccbytes; i++) {
+ if (i % nand->ecc.bytes)
+ ecclayout->eccpos[i] =
+ ecclayout->eccpos[i - 1] + 1;
+ else
+ ecclayout->eccpos[i] =
+ ecclayout->eccpos[i - 1] + 2;
+ }
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ omap_hwecc_init_bch(nand, NAND_ECC_READ);
+ bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ break;
+#else
+ printf("nand: error: CONFIG_BCH required for ECC\n");
+ return -EINVAL;
+#endif
+
+ case OMAP_ECC_BCH8_CODE_HW:
+#ifdef CONFIG_NAND_OMAP_ELM
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (14 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ /* intialize ELM for ECC error detection */
+ elm_init();
+ bch_priv.type = ECC_BCH8;
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 14;
+ nand->ecc.hwctl = omap_enable_ecc_bch;
+ nand->ecc.correct = omap_correct_data_bch;
+ nand->ecc.calculate = omap_calculate_ecc_bch;
+ nand->ecc.read_page = omap_read_page_bch;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW;
+ break;
+#else
+ printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
+ return -EINVAL;
+#endif
+
+ default:
+ debug("nand: error: ecc scheme not enabled or supported\n");
+ return -EINVAL;
+ }
+
+ /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */
+ if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW)
+ nand->ecc.layout = ecclayout;
+
+ return 0;
+}
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ * omap_nand_switch_ecc - switch the ECC operation between different engines
+ * (h/w and s/w) and different algorithms (hamming and BCHx)
+ *
+ * @hardware - true if one of the HW engines should be used
+ * @eccstrength - the number of bits that could be corrected
+ * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)
+ */
+int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)