diff options
Diffstat (limited to 'drivers/mtd/nand/gpmi-nand/gpmi-nand.c')
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 63 |
1 files changed, 39 insertions, 24 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index e9b1c47e3cf9..717881a3d1b8 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c | |||
@@ -94,6 +94,25 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this) | |||
94 | return round_down(ecc_strength, 2); | 94 | return round_down(ecc_strength, 2); |
95 | } | 95 | } |
96 | 96 | ||
97 | static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) | ||
98 | { | ||
99 | struct bch_geometry *geo = &this->bch_geometry; | ||
100 | |||
101 | /* Do the sanity check. */ | ||
102 | if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { | ||
103 | /* The mx23/mx28 only support the GF13. */ | ||
104 | if (geo->gf_len == 14) | ||
105 | return false; | ||
106 | |||
107 | if (geo->ecc_strength > MXS_ECC_STRENGTH_MAX) | ||
108 | return false; | ||
109 | } else if (GPMI_IS_MX6Q(this)) { | ||
110 | if (geo->ecc_strength > MX6_ECC_STRENGTH_MAX) | ||
111 | return false; | ||
112 | } | ||
113 | return true; | ||
114 | } | ||
115 | |||
97 | int common_nfc_set_geometry(struct gpmi_nand_data *this) | 116 | int common_nfc_set_geometry(struct gpmi_nand_data *this) |
98 | { | 117 | { |
99 | struct bch_geometry *geo = &this->bch_geometry; | 118 | struct bch_geometry *geo = &this->bch_geometry; |
@@ -112,17 +131,24 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) | |||
112 | /* The default for the length of Galois Field. */ | 131 | /* The default for the length of Galois Field. */ |
113 | geo->gf_len = 13; | 132 | geo->gf_len = 13; |
114 | 133 | ||
115 | /* The default for chunk size. There is no oobsize greater then 512. */ | 134 | /* The default for chunk size. */ |
116 | geo->ecc_chunk_size = 512; | 135 | geo->ecc_chunk_size = 512; |
117 | while (geo->ecc_chunk_size < mtd->oobsize) | 136 | while (geo->ecc_chunk_size < mtd->oobsize) { |
118 | geo->ecc_chunk_size *= 2; /* keep C >= O */ | 137 | geo->ecc_chunk_size *= 2; /* keep C >= O */ |
138 | geo->gf_len = 14; | ||
139 | } | ||
119 | 140 | ||
120 | geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; | 141 | geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; |
121 | 142 | ||
122 | /* We use the same ECC strength for all chunks. */ | 143 | /* We use the same ECC strength for all chunks. */ |
123 | geo->ecc_strength = get_ecc_strength(this); | 144 | geo->ecc_strength = get_ecc_strength(this); |
124 | if (!geo->ecc_strength) { | 145 | if (!gpmi_check_ecc(this)) { |
125 | pr_err("wrong ECC strength.\n"); | 146 | dev_err(this->dev, |
147 | "We can not support this nand chip." | ||
148 | " Its required ecc strength(%d) is beyond our" | ||
149 | " capability(%d).\n", geo->ecc_strength, | ||
150 | (GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX | ||
151 | : MXS_ECC_STRENGTH_MAX)); | ||
126 | return -EINVAL; | 152 | return -EINVAL; |
127 | } | 153 | } |
128 | 154 | ||
@@ -920,8 +946,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
920 | dma_addr_t auxiliary_phys; | 946 | dma_addr_t auxiliary_phys; |
921 | unsigned int i; | 947 | unsigned int i; |
922 | unsigned char *status; | 948 | unsigned char *status; |
923 | unsigned int failed; | 949 | unsigned int max_bitflips = 0; |
924 | unsigned int corrected; | ||
925 | int ret; | 950 | int ret; |
926 | 951 | ||
927 | pr_debug("page number is : %d\n", page); | 952 | pr_debug("page number is : %d\n", page); |
@@ -945,35 +970,25 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
945 | payload_virt, payload_phys); | 970 | payload_virt, payload_phys); |
946 | if (ret) { | 971 | if (ret) { |
947 | pr_err("Error in ECC-based read: %d\n", ret); | 972 | pr_err("Error in ECC-based read: %d\n", ret); |
948 | goto exit_nfc; | 973 | return ret; |
949 | } | 974 | } |
950 | 975 | ||
951 | /* handle the block mark swapping */ | 976 | /* handle the block mark swapping */ |
952 | block_mark_swapping(this, payload_virt, auxiliary_virt); | 977 | block_mark_swapping(this, payload_virt, auxiliary_virt); |
953 | 978 | ||
954 | /* Loop over status bytes, accumulating ECC status. */ | 979 | /* Loop over status bytes, accumulating ECC status. */ |
955 | failed = 0; | 980 | status = auxiliary_virt + nfc_geo->auxiliary_status_offset; |
956 | corrected = 0; | ||
957 | status = auxiliary_virt + nfc_geo->auxiliary_status_offset; | ||
958 | 981 | ||
959 | for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { | 982 | for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { |
960 | if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) | 983 | if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) |
961 | continue; | 984 | continue; |
962 | 985 | ||
963 | if (*status == STATUS_UNCORRECTABLE) { | 986 | if (*status == STATUS_UNCORRECTABLE) { |
964 | failed++; | 987 | mtd->ecc_stats.failed++; |
965 | continue; | 988 | continue; |
966 | } | 989 | } |
967 | corrected += *status; | 990 | mtd->ecc_stats.corrected += *status; |
968 | } | 991 | max_bitflips = max_t(unsigned int, max_bitflips, *status); |
969 | |||
970 | /* | ||
971 | * Propagate ECC status to the owning MTD only when failed or | ||
972 | * corrected times nearly reaches our ECC correction threshold. | ||
973 | */ | ||
974 | if (failed || corrected >= (nfc_geo->ecc_strength - 1)) { | ||
975 | mtd->ecc_stats.failed += failed; | ||
976 | mtd->ecc_stats.corrected += corrected; | ||
977 | } | 992 | } |
978 | 993 | ||
979 | if (oob_required) { | 994 | if (oob_required) { |
@@ -995,8 +1010,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
995 | this->payload_virt, this->payload_phys, | 1010 | this->payload_virt, this->payload_phys, |
996 | nfc_geo->payload_size, | 1011 | nfc_geo->payload_size, |
997 | payload_virt, payload_phys); | 1012 | payload_virt, payload_phys); |
998 | exit_nfc: | 1013 | |
999 | return ret; | 1014 | return max_bitflips; |
1000 | } | 1015 | } |
1001 | 1016 | ||
1002 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 1017 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
@@ -1668,8 +1683,8 @@ exit_nfc_init: | |||
1668 | release_resources(this); | 1683 | release_resources(this); |
1669 | exit_acquire_resources: | 1684 | exit_acquire_resources: |
1670 | platform_set_drvdata(pdev, NULL); | 1685 | platform_set_drvdata(pdev, NULL); |
1671 | kfree(this); | ||
1672 | dev_err(this->dev, "driver registration failed: %d\n", ret); | 1686 | dev_err(this->dev, "driver registration failed: %d\n", ret); |
1687 | kfree(this); | ||
1673 | 1688 | ||
1674 | return ret; | 1689 | return ret; |
1675 | } | 1690 | } |