diff options
author | Huang Shijie <b32955@freescale.com> | 2013-05-16 23:17:34 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:05:59 -0400 |
commit | 0d17442b52ffd0187e58bdbc27b8fee07401df01 (patch) | |
tree | 62b49439efdd52f0303942c0ad00450cd17af9ac /drivers | |
parent | ee3a2a3b69f5e4cca79ac437d12bba592ba66930 (diff) |
mtd: gpmi: set the BCH's geometry with the ecc info
If the nand chip provides us the ECC info, we can use it firstly.
The set_geometry_by_ecc_info() will use the ECC info, and
calculate the parameters we need.
Rename the old code to legacy_set_geometry() which will takes effect
when there is no ECC info from the nand chip or we fails in the ECC info case.
Signed-off-by: Huang Shijie <b32955@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 6213c53820e6..0c3e64793882 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c | |||
@@ -111,7 +111,132 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) | |||
111 | return true; | 111 | return true; |
112 | } | 112 | } |
113 | 113 | ||
114 | int common_nfc_set_geometry(struct gpmi_nand_data *this) | 114 | /* |
115 | * If we can get the ECC information from the nand chip, we do not | ||
116 | * need to calculate them ourselves. | ||
117 | * | ||
118 | * We may have available oob space in this case. | ||
119 | */ | ||
120 | static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) | ||
121 | { | ||
122 | struct bch_geometry *geo = &this->bch_geometry; | ||
123 | struct mtd_info *mtd = &this->mtd; | ||
124 | struct nand_chip *chip = mtd->priv; | ||
125 | struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree; | ||
126 | unsigned int block_mark_bit_offset; | ||
127 | |||
128 | if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) | ||
129 | return false; | ||
130 | |||
131 | switch (chip->ecc_step_ds) { | ||
132 | case SZ_512: | ||
133 | geo->gf_len = 13; | ||
134 | break; | ||
135 | case SZ_1K: | ||
136 | geo->gf_len = 14; | ||
137 | break; | ||
138 | default: | ||
139 | dev_err(this->dev, | ||
140 | "unsupported nand chip. ecc bits : %d, ecc size : %d\n", | ||
141 | chip->ecc_strength_ds, chip->ecc_step_ds); | ||
142 | return false; | ||
143 | } | ||
144 | geo->ecc_chunk_size = chip->ecc_step_ds; | ||
145 | geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); | ||
146 | if (!gpmi_check_ecc(this)) | ||
147 | return false; | ||
148 | |||
149 | /* Keep the C >= O */ | ||
150 | if (geo->ecc_chunk_size < mtd->oobsize) { | ||
151 | dev_err(this->dev, | ||
152 | "unsupported nand chip. ecc size: %d, oob size : %d\n", | ||
153 | chip->ecc_step_ds, mtd->oobsize); | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | /* The default value, see comment in the legacy_set_geometry(). */ | ||
158 | geo->metadata_size = 10; | ||
159 | |||
160 | geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; | ||
161 | |||
162 | /* | ||
163 | * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: | ||
164 | * | ||
165 | * | P | | ||
166 | * |<----------------------------------------------------->| | ||
167 | * | | | ||
168 | * | (Block Mark) | | ||
169 | * | P' | | | | | ||
170 | * |<-------------------------------------------->| D | | O' | | ||
171 | * | |<---->| |<--->| | ||
172 | * V V V V V | ||
173 | * +---+----------+-+----------+-+----------+-+----------+-+-----+ | ||
174 | * | M | data |E| data |E| data |E| data |E| | | ||
175 | * +---+----------+-+----------+-+----------+-+----------+-+-----+ | ||
176 | * ^ ^ | ||
177 | * | O | | ||
178 | * |<------------>| | ||
179 | * | | | ||
180 | * | ||
181 | * P : the page size for BCH module. | ||
182 | * E : The ECC strength. | ||
183 | * G : the length of Galois Field. | ||
184 | * N : The chunk count of per page. | ||
185 | * M : the metasize of per page. | ||
186 | * C : the ecc chunk size, aka the "data" above. | ||
187 | * P': the nand chip's page size. | ||
188 | * O : the nand chip's oob size. | ||
189 | * O': the free oob. | ||
190 | * | ||
191 | * The formula for P is : | ||
192 | * | ||
193 | * E * G * N | ||
194 | * P = ------------ + P' + M | ||
195 | * 8 | ||
196 | * | ||
197 | * The position of block mark moves forward in the ECC-based view | ||
198 | * of page, and the delta is: | ||
199 | * | ||
200 | * E * G * (N - 1) | ||
201 | * D = (---------------- + M) | ||
202 | * 8 | ||
203 | * | ||
204 | * Please see the comment in legacy_set_geometry(). | ||
205 | * With the condition C >= O , we still can get same result. | ||
206 | * So the bit position of the physical block mark within the ECC-based | ||
207 | * view of the page is : | ||
208 | * (P' - D) * 8 | ||
209 | */ | ||
210 | geo->page_size = mtd->writesize + geo->metadata_size + | ||
211 | (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; | ||
212 | |||
213 | /* The available oob size we have. */ | ||
214 | if (geo->page_size < mtd->writesize + mtd->oobsize) { | ||
215 | of->offset = geo->page_size - mtd->writesize; | ||
216 | of->length = mtd->oobsize - of->offset; | ||
217 | mtd->oobavail = gpmi_hw_ecclayout.oobavail = of->length; | ||
218 | } | ||
219 | |||
220 | geo->payload_size = mtd->writesize; | ||
221 | |||
222 | geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); | ||
223 | geo->auxiliary_size = ALIGN(geo->metadata_size, 4) | ||
224 | + ALIGN(geo->ecc_chunk_count, 4); | ||
225 | |||
226 | if (!this->swap_block_mark) | ||
227 | return true; | ||
228 | |||
229 | /* For bit swap. */ | ||
230 | block_mark_bit_offset = mtd->writesize * 8 - | ||
231 | (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) | ||
232 | + geo->metadata_size * 8); | ||
233 | |||
234 | geo->block_mark_byte_offset = block_mark_bit_offset / 8; | ||
235 | geo->block_mark_bit_offset = block_mark_bit_offset % 8; | ||
236 | return true; | ||
237 | } | ||
238 | |||
239 | static int legacy_set_geometry(struct gpmi_nand_data *this) | ||
115 | { | 240 | { |
116 | struct bch_geometry *geo = &this->bch_geometry; | 241 | struct bch_geometry *geo = &this->bch_geometry; |
117 | struct mtd_info *mtd = &this->mtd; | 242 | struct mtd_info *mtd = &this->mtd; |
@@ -223,6 +348,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) | |||
223 | return 0; | 348 | return 0; |
224 | } | 349 | } |
225 | 350 | ||
351 | int common_nfc_set_geometry(struct gpmi_nand_data *this) | ||
352 | { | ||
353 | return set_geometry_by_ecc_info(this) ? 0 : legacy_set_geometry(this); | ||
354 | } | ||
355 | |||
226 | struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) | 356 | struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) |
227 | { | 357 | { |
228 | int chipnr = this->current_chip; | 358 | int chipnr = this->current_chip; |