aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c78
1 files changed, 73 insertions, 5 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 798ed5961e8f..6e461560c6a8 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -1064,14 +1064,87 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
1064 /* Loop over status bytes, accumulating ECC status. */ 1064 /* Loop over status bytes, accumulating ECC status. */
1065 status = auxiliary_virt + nfc_geo->auxiliary_status_offset; 1065 status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
1066 1066
1067 read_page_swap_end(this, buf, nfc_geo->payload_size,
1068 this->payload_virt, this->payload_phys,
1069 nfc_geo->payload_size,
1070 payload_virt, payload_phys);
1071
1067 for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { 1072 for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
1068 if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) 1073 if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
1069 continue; 1074 continue;
1070 1075
1071 if (*status == STATUS_UNCORRECTABLE) { 1076 if (*status == STATUS_UNCORRECTABLE) {
1077 int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
1078 u8 *eccbuf = this->raw_buffer;
1079 int offset, bitoffset;
1080 int eccbytes;
1081 int flips;
1082
1083 /* Read ECC bytes into our internal raw_buffer */
1084 offset = nfc_geo->metadata_size * 8;
1085 offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
1086 offset -= eccbits;
1087 bitoffset = offset % 8;
1088 eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
1089 offset /= 8;
1090 eccbytes -= offset;
1091 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
1092 chip->read_buf(mtd, eccbuf, eccbytes);
1093
1094 /*
1095 * ECC data are not byte aligned and we may have
1096 * in-band data in the first and last byte of
1097 * eccbuf. Set non-eccbits to one so that
1098 * nand_check_erased_ecc_chunk() does not count them
1099 * as bitflips.
1100 */
1101 if (bitoffset)
1102 eccbuf[0] |= GENMASK(bitoffset - 1, 0);
1103
1104 bitoffset = (bitoffset + eccbits) % 8;
1105 if (bitoffset)
1106 eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
1107
1108 /*
1109 * The ECC hardware has an uncorrectable ECC status
1110 * code in case we have bitflips in an erased page. As
1111 * nothing was written into this subpage the ECC is
1112 * obviously wrong and we can not trust it. We assume
1113 * at this point that we are reading an erased page and
1114 * try to correct the bitflips in buffer up to
1115 * ecc_strength bitflips. If this is a page with random
1116 * data, we exceed this number of bitflips and have a
1117 * ECC failure. Otherwise we use the corrected buffer.
1118 */
1119 if (i == 0) {
1120 /* The first block includes metadata */
1121 flips = nand_check_erased_ecc_chunk(
1122 buf + i * nfc_geo->ecc_chunk_size,
1123 nfc_geo->ecc_chunk_size,
1124 eccbuf, eccbytes,
1125 auxiliary_virt,
1126 nfc_geo->metadata_size,
1127 nfc_geo->ecc_strength);
1128 } else {
1129 flips = nand_check_erased_ecc_chunk(
1130 buf + i * nfc_geo->ecc_chunk_size,
1131 nfc_geo->ecc_chunk_size,
1132 eccbuf, eccbytes,
1133 NULL, 0,
1134 nfc_geo->ecc_strength);
1135 }
1136
1137 if (flips > 0) {
1138 max_bitflips = max_t(unsigned int, max_bitflips,
1139 flips);
1140 mtd->ecc_stats.corrected += flips;
1141 continue;
1142 }
1143
1072 mtd->ecc_stats.failed++; 1144 mtd->ecc_stats.failed++;
1073 continue; 1145 continue;
1074 } 1146 }
1147
1075 mtd->ecc_stats.corrected += *status; 1148 mtd->ecc_stats.corrected += *status;
1076 max_bitflips = max_t(unsigned int, max_bitflips, *status); 1149 max_bitflips = max_t(unsigned int, max_bitflips, *status);
1077 } 1150 }
@@ -1091,11 +1164,6 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
1091 chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; 1164 chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
1092 } 1165 }
1093 1166
1094 read_page_swap_end(this, buf, nfc_geo->payload_size,
1095 this->payload_virt, this->payload_phys,
1096 nfc_geo->payload_size,
1097 payload_virt, payload_phys);
1098
1099 return max_bitflips; 1167 return max_bitflips;
1100} 1168}
1101 1169