Skip to content

Commit 2febcdf

Browse files
Huang ShijieDavid Woodhouse
authored andcommitted
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 <[email protected]> Signed-off-by: Brian Norris <[email protected]> Signed-off-by: David Woodhouse <[email protected]>
1 parent d1048aa commit 2febcdf

File tree

1 file changed

+131
-1
lines changed

1 file changed

+131
-1
lines changed

drivers/mtd/nand/gpmi-nand/gpmi-nand.c

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,132 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
111111
return true;
112112
}
113113

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)
115240
{
116241
struct bch_geometry *geo = &this->bch_geometry;
117242
struct mtd_info *mtd = &this->mtd;
@@ -223,6 +348,11 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
223348
return 0;
224349
}
225350

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+
226356
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
227357
{
228358
int chipnr = this->current_chip;

0 commit comments

Comments
 (0)