Lines Matching +full:nand +full:- +full:ecc +full:- +full:engine

1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * This file provides ECC correction for more than 1 bit per block of data,
14 #include <linux/mtd/nand.h>
15 #include <linux/mtd/nand-ecc-sw-bch.h>
18 * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block
19 * @nand: NAND device
21 * @code: Output buffer with ECC
23 int nand_ecc_sw_bch_calculate(struct nand_device *nand, in nand_ecc_sw_bch_calculate() argument
26 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_calculate()
29 memset(code, 0, engine_conf->code_size); in nand_ecc_sw_bch_calculate()
30 bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code); in nand_ecc_sw_bch_calculate()
33 for (i = 0; i < engine_conf->code_size; i++) in nand_ecc_sw_bch_calculate()
34 code[i] ^= engine_conf->eccmask[i]; in nand_ecc_sw_bch_calculate()
41 * nand_ecc_sw_bch_correct - Detect, correct and report bit error(s)
42 * @nand: NAND device
44 * @read_ecc: ECC bytes from the chip
45 * @calc_ecc: ECC calculated from the raw data
49 int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, in nand_ecc_sw_bch_correct() argument
52 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_correct()
53 unsigned int step_size = nand->ecc.ctx.conf.step_size; in nand_ecc_sw_bch_correct()
54 unsigned int *errloc = engine_conf->errloc; in nand_ecc_sw_bch_correct()
57 count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc, in nand_ecc_sw_bch_correct()
65 /* Otherwise the error is in the ECC area: nothing to do */ in nand_ecc_sw_bch_correct()
70 pr_err("ECC unrecoverable error\n"); in nand_ecc_sw_bch_correct()
71 count = -EBADMSG; in nand_ecc_sw_bch_correct()
79 * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources
80 * @nand: NAND device
82 static void nand_ecc_sw_bch_cleanup(struct nand_device *nand) in nand_ecc_sw_bch_cleanup() argument
84 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_cleanup()
86 bch_free(engine_conf->bch); in nand_ecc_sw_bch_cleanup()
87 kfree(engine_conf->errloc); in nand_ecc_sw_bch_cleanup()
88 kfree(engine_conf->eccmask); in nand_ecc_sw_bch_cleanup()
92 * nand_ecc_sw_bch_init - Initialize software BCH ECC engine
93 * @nand: NAND device
95 * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure
97 * Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and
102 * bits, where m is such that 2^m - 1 > step_size * 8.
105 * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8)
108 static int nand_ecc_sw_bch_init(struct nand_device *nand) in nand_ecc_sw_bch_init() argument
110 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_init()
111 unsigned int eccsize = nand->ecc.ctx.conf.step_size; in nand_ecc_sw_bch_init()
112 unsigned int eccbytes = engine_conf->code_size; in nand_ecc_sw_bch_init()
120 engine_conf->bch = bch_init(m, t, 0, false); in nand_ecc_sw_bch_init()
121 if (!engine_conf->bch) in nand_ecc_sw_bch_init()
122 return -EINVAL; in nand_ecc_sw_bch_init()
124 engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL); in nand_ecc_sw_bch_init()
125 engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc), in nand_ecc_sw_bch_init()
127 if (!engine_conf->eccmask || !engine_conf->errloc) { in nand_ecc_sw_bch_init()
128 ret = -ENOMEM; in nand_ecc_sw_bch_init()
132 /* Compute and store the inverted ECC of an erased step */ in nand_ecc_sw_bch_init()
135 ret = -ENOMEM; in nand_ecc_sw_bch_init()
140 bch_encode(engine_conf->bch, erased_page, eccsize, in nand_ecc_sw_bch_init()
141 engine_conf->eccmask); in nand_ecc_sw_bch_init()
145 engine_conf->eccmask[i] ^= 0xff; in nand_ecc_sw_bch_init()
148 if (engine_conf->bch->ecc_bytes != eccbytes) { in nand_ecc_sw_bch_init()
149 pr_err("Invalid number of ECC bytes: %u, expected: %u\n", in nand_ecc_sw_bch_init()
150 eccbytes, engine_conf->bch->ecc_bytes); in nand_ecc_sw_bch_init()
151 ret = -EINVAL; in nand_ecc_sw_bch_init()
157 pr_err("ECC step size is too large (%u)\n", eccsize); in nand_ecc_sw_bch_init()
158 ret = -EINVAL; in nand_ecc_sw_bch_init()
165 nand_ecc_sw_bch_cleanup(nand); in nand_ecc_sw_bch_init()
170 int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) in nand_ecc_sw_bch_init_ctx() argument
172 struct nand_ecc_props *conf = &nand->ecc.ctx.conf; in nand_ecc_sw_bch_init_ctx()
173 struct mtd_info *mtd = nanddev_to_mtd(nand); in nand_ecc_sw_bch_init_ctx()
178 /* Only large page NAND chips may use BCH */ in nand_ecc_sw_bch_init_ctx()
179 if (mtd->oobsize < 64) { in nand_ecc_sw_bch_init_ctx()
180 pr_err("BCH cannot be used with small page NAND chips\n"); in nand_ecc_sw_bch_init_ctx()
181 return -EINVAL; in nand_ecc_sw_bch_init_ctx()
184 if (!mtd->ooblayout) in nand_ecc_sw_bch_init_ctx()
187 conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; in nand_ecc_sw_bch_init_ctx()
188 conf->algo = NAND_ECC_ALGO_BCH; in nand_ecc_sw_bch_init_ctx()
189 conf->step_size = nand->ecc.user_conf.step_size; in nand_ecc_sw_bch_init_ctx()
190 conf->strength = nand->ecc.user_conf.strength; in nand_ecc_sw_bch_init_ctx()
193 * Board driver should supply ECC size and ECC strength in nand_ecc_sw_bch_init_ctx()
198 if (!conf->step_size) { in nand_ecc_sw_bch_init_ctx()
199 if (mtd->oobsize >= 64) in nand_ecc_sw_bch_init_ctx()
200 conf->step_size = 512; in nand_ecc_sw_bch_init_ctx()
202 conf->step_size = 256; in nand_ecc_sw_bch_init_ctx()
204 conf->strength = 4; in nand_ecc_sw_bch_init_ctx()
207 nsteps = mtd->writesize / conf->step_size; in nand_ecc_sw_bch_init_ctx()
210 if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { in nand_ecc_sw_bch_init_ctx()
211 conf->step_size = 1024; in nand_ecc_sw_bch_init_ctx()
212 nsteps = mtd->writesize / conf->step_size; in nand_ecc_sw_bch_init_ctx()
214 code_size = (mtd->oobsize - 2) / nsteps; in nand_ecc_sw_bch_init_ctx()
215 conf->strength = code_size * 8 / fls(8 * conf->step_size); in nand_ecc_sw_bch_init_ctx()
219 code_size = DIV_ROUND_UP(conf->strength * in nand_ecc_sw_bch_init_ctx()
220 fls(8 * conf->step_size), 8); in nand_ecc_sw_bch_init_ctx()
222 if (!conf->strength) in nand_ecc_sw_bch_init_ctx()
223 conf->strength = (code_size * 8) / fls(8 * conf->step_size); in nand_ecc_sw_bch_init_ctx()
225 if (!code_size && !conf->strength) { in nand_ecc_sw_bch_init_ctx()
226 pr_err("Missing ECC parameters\n"); in nand_ecc_sw_bch_init_ctx()
227 return -EINVAL; in nand_ecc_sw_bch_init_ctx()
232 return -ENOMEM; in nand_ecc_sw_bch_init_ctx()
234 ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand); in nand_ecc_sw_bch_init_ctx()
238 engine_conf->code_size = code_size; in nand_ecc_sw_bch_init_ctx()
239 engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); in nand_ecc_sw_bch_init_ctx()
240 engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); in nand_ecc_sw_bch_init_ctx()
241 if (!engine_conf->calc_buf || !engine_conf->code_buf) { in nand_ecc_sw_bch_init_ctx()
242 ret = -ENOMEM; in nand_ecc_sw_bch_init_ctx()
246 nand->ecc.ctx.priv = engine_conf; in nand_ecc_sw_bch_init_ctx()
247 nand->ecc.ctx.nsteps = nsteps; in nand_ecc_sw_bch_init_ctx()
248 nand->ecc.ctx.total = nsteps * code_size; in nand_ecc_sw_bch_init_ctx()
250 ret = nand_ecc_sw_bch_init(nand); in nand_ecc_sw_bch_init_ctx()
256 nand->ecc.ctx.nsteps * engine_conf->code_size) { in nand_ecc_sw_bch_init_ctx()
257 pr_err("Invalid ECC layout\n"); in nand_ecc_sw_bch_init_ctx()
258 ret = -EINVAL; in nand_ecc_sw_bch_init_ctx()
265 nand_ecc_sw_bch_cleanup(nand); in nand_ecc_sw_bch_init_ctx()
267 nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); in nand_ecc_sw_bch_init_ctx()
268 kfree(engine_conf->calc_buf); in nand_ecc_sw_bch_init_ctx()
269 kfree(engine_conf->code_buf); in nand_ecc_sw_bch_init_ctx()
277 void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) in nand_ecc_sw_bch_cleanup_ctx() argument
279 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_cleanup_ctx()
282 nand_ecc_sw_bch_cleanup(nand); in nand_ecc_sw_bch_cleanup_ctx()
283 nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); in nand_ecc_sw_bch_cleanup_ctx()
284 kfree(engine_conf->calc_buf); in nand_ecc_sw_bch_cleanup_ctx()
285 kfree(engine_conf->code_buf); in nand_ecc_sw_bch_cleanup_ctx()
291 static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand, in nand_ecc_sw_bch_prepare_io_req() argument
294 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_prepare_io_req()
295 struct mtd_info *mtd = nanddev_to_mtd(nand); in nand_ecc_sw_bch_prepare_io_req()
296 int eccsize = nand->ecc.ctx.conf.step_size; in nand_ecc_sw_bch_prepare_io_req()
297 int eccbytes = engine_conf->code_size; in nand_ecc_sw_bch_prepare_io_req()
298 int eccsteps = nand->ecc.ctx.nsteps; in nand_ecc_sw_bch_prepare_io_req()
299 int total = nand->ecc.ctx.total; in nand_ecc_sw_bch_prepare_io_req()
300 u8 *ecccalc = engine_conf->calc_buf; in nand_ecc_sw_bch_prepare_io_req()
305 if (req->mode == MTD_OPS_RAW) in nand_ecc_sw_bch_prepare_io_req()
308 /* This engine does not provide BBM/free OOB bytes protection */ in nand_ecc_sw_bch_prepare_io_req()
309 if (!req->datalen) in nand_ecc_sw_bch_prepare_io_req()
312 nand_ecc_tweak_req(&engine_conf->req_ctx, req); in nand_ecc_sw_bch_prepare_io_req()
315 if (req->type == NAND_PAGE_READ) in nand_ecc_sw_bch_prepare_io_req()
318 /* Preparation for page write: derive the ECC bytes and place them */ in nand_ecc_sw_bch_prepare_io_req()
319 for (i = 0, data = req->databuf.out; in nand_ecc_sw_bch_prepare_io_req()
321 eccsteps--, i += eccbytes, data += eccsize) in nand_ecc_sw_bch_prepare_io_req()
322 nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); in nand_ecc_sw_bch_prepare_io_req()
324 return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out, in nand_ecc_sw_bch_prepare_io_req()
328 static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand, in nand_ecc_sw_bch_finish_io_req() argument
331 struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; in nand_ecc_sw_bch_finish_io_req()
332 struct mtd_info *mtd = nanddev_to_mtd(nand); in nand_ecc_sw_bch_finish_io_req()
333 int eccsize = nand->ecc.ctx.conf.step_size; in nand_ecc_sw_bch_finish_io_req()
334 int total = nand->ecc.ctx.total; in nand_ecc_sw_bch_finish_io_req()
335 int eccbytes = engine_conf->code_size; in nand_ecc_sw_bch_finish_io_req()
336 int eccsteps = nand->ecc.ctx.nsteps; in nand_ecc_sw_bch_finish_io_req()
337 u8 *ecccalc = engine_conf->calc_buf; in nand_ecc_sw_bch_finish_io_req()
338 u8 *ecccode = engine_conf->code_buf; in nand_ecc_sw_bch_finish_io_req()
340 u8 *data = req->databuf.in; in nand_ecc_sw_bch_finish_io_req()
344 if (req->mode == MTD_OPS_RAW) in nand_ecc_sw_bch_finish_io_req()
347 /* This engine does not provide BBM/free OOB bytes protection */ in nand_ecc_sw_bch_finish_io_req()
348 if (!req->datalen) in nand_ecc_sw_bch_finish_io_req()
352 if (req->type == NAND_PAGE_WRITE) { in nand_ecc_sw_bch_finish_io_req()
353 nand_ecc_restore_req(&engine_conf->req_ctx, req); in nand_ecc_sw_bch_finish_io_req()
357 /* Finish a page read: retrieve the (raw) ECC bytes*/ in nand_ecc_sw_bch_finish_io_req()
358 ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0, in nand_ecc_sw_bch_finish_io_req()
363 /* Calculate the ECC bytes */ in nand_ecc_sw_bch_finish_io_req()
364 for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) in nand_ecc_sw_bch_finish_io_req()
365 nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); in nand_ecc_sw_bch_finish_io_req()
368 for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in; in nand_ecc_sw_bch_finish_io_req()
370 eccsteps--, i += eccbytes, data += eccsize) { in nand_ecc_sw_bch_finish_io_req()
371 int stat = nand_ecc_sw_bch_correct(nand, data, in nand_ecc_sw_bch_finish_io_req()
375 mtd->ecc_stats.failed++; in nand_ecc_sw_bch_finish_io_req()
377 mtd->ecc_stats.corrected += stat; in nand_ecc_sw_bch_finish_io_req()
382 nand_ecc_restore_req(&engine_conf->req_ctx, req); in nand_ecc_sw_bch_finish_io_req()
406 MODULE_DESCRIPTION("NAND software BCH ECC support");