diff options
| -rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 78 |
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 | ||
