mtd: nand: ecc: Add an I/O request tweaking mechanism
Currently, BCH and Hamming engine are sharing the same tweaking/restoring I/O mechanism: they need the I/O request to fully cover the main/OOB area. Let's make this code generic as sharing the code between two drivers is already a win. Maybe other ECC engine drivers will need it too. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-2-miquel.raynal@bootlin.com
This commit is contained in:
parent
b650545978
commit
51e7bf4534
@ -95,6 +95,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* nand_ecc_init_ctx - Init the ECC engine context
|
||||
@ -479,6 +480,111 @@ bool nand_ecc_is_strong_enough(struct nand_device *nand)
|
||||
}
|
||||
EXPORT_SYMBOL(nand_ecc_is_strong_enough);
|
||||
|
||||
/* ECC engine driver internal helpers */
|
||||
int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_device *nand)
|
||||
{
|
||||
unsigned int total_buffer_size;
|
||||
|
||||
ctx->nand = nand;
|
||||
|
||||
/* Let the user decide the exact length of each buffer */
|
||||
if (!ctx->page_buffer_size)
|
||||
ctx->page_buffer_size = nanddev_page_size(nand);
|
||||
if (!ctx->oob_buffer_size)
|
||||
ctx->oob_buffer_size = nanddev_per_page_oobsize(nand);
|
||||
|
||||
total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size;
|
||||
|
||||
ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL);
|
||||
if (!ctx->spare_databuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking);
|
||||
|
||||
void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx)
|
||||
{
|
||||
kfree(ctx->spare_databuf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking);
|
||||
|
||||
/*
|
||||
* Ensure data and OOB area is fully read/written otherwise the correction might
|
||||
* not work as expected.
|
||||
*/
|
||||
void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = ctx->nand;
|
||||
struct nand_page_io_req *orig, *tweak;
|
||||
|
||||
/* Save the original request */
|
||||
ctx->orig_req = *req;
|
||||
ctx->bounce_data = false;
|
||||
ctx->bounce_oob = false;
|
||||
orig = &ctx->orig_req;
|
||||
tweak = req;
|
||||
|
||||
/* Ensure the request covers the entire page */
|
||||
if (orig->datalen < nanddev_page_size(nand)) {
|
||||
ctx->bounce_data = true;
|
||||
tweak->dataoffs = 0;
|
||||
tweak->datalen = nanddev_page_size(nand);
|
||||
tweak->databuf.in = ctx->spare_databuf;
|
||||
memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size);
|
||||
}
|
||||
|
||||
if (orig->ooblen < nanddev_per_page_oobsize(nand)) {
|
||||
ctx->bounce_oob = true;
|
||||
tweak->ooboffs = 0;
|
||||
tweak->ooblen = nanddev_per_page_oobsize(nand);
|
||||
tweak->oobbuf.in = ctx->spare_oobbuf;
|
||||
memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size);
|
||||
}
|
||||
|
||||
/* Copy the data that must be writen in the bounce buffers, if needed */
|
||||
if (orig->type == NAND_PAGE_WRITE) {
|
||||
if (ctx->bounce_data)
|
||||
memcpy((void *)tweak->databuf.out + orig->dataoffs,
|
||||
orig->databuf.out, orig->datalen);
|
||||
|
||||
if (ctx->bounce_oob)
|
||||
memcpy((void *)tweak->oobbuf.out + orig->ooboffs,
|
||||
orig->oobbuf.out, orig->ooblen);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_tweak_req);
|
||||
|
||||
void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_page_io_req *orig, *tweak;
|
||||
|
||||
orig = &ctx->orig_req;
|
||||
tweak = req;
|
||||
|
||||
/* Restore the data read from the bounce buffers, if needed */
|
||||
if (orig->type == NAND_PAGE_READ) {
|
||||
if (ctx->bounce_data)
|
||||
memcpy(orig->databuf.in,
|
||||
tweak->databuf.in + orig->dataoffs,
|
||||
orig->datalen);
|
||||
|
||||
if (ctx->bounce_oob)
|
||||
memcpy(orig->oobbuf.in,
|
||||
tweak->oobbuf.in + orig->ooboffs,
|
||||
orig->ooblen);
|
||||
}
|
||||
|
||||
/* Ensure the original request is restored */
|
||||
*req = *orig;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_ecc_restore_req);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Generic ECC engine");
|
||||
|
@ -278,6 +278,38 @@ int nand_ecc_finish_io_req(struct nand_device *nand,
|
||||
struct nand_page_io_req *req);
|
||||
bool nand_ecc_is_strong_enough(struct nand_device *nand);
|
||||
|
||||
/**
|
||||
* struct nand_ecc_req_tweak_ctx - Help for automatically tweaking requests
|
||||
* @orig_req: Pointer to the original IO request
|
||||
* @nand: Related NAND device, to have access to its memory organization
|
||||
* @page_buffer_size: Real size of the page buffer to use (can be set by the
|
||||
* user before the tweaking mechanism initialization)
|
||||
* @oob_buffer_size: Real size of the OOB buffer to use (can be set by the
|
||||
* user before the tweaking mechanism initialization)
|
||||
* @spare_databuf: Data bounce buffer
|
||||
* @spare_oobbuf: OOB bounce buffer
|
||||
* @bounce_data: Flag indicating a data bounce buffer is used
|
||||
* @bounce_oob: Flag indicating an OOB bounce buffer is used
|
||||
*/
|
||||
struct nand_ecc_req_tweak_ctx {
|
||||
struct nand_page_io_req orig_req;
|
||||
struct nand_device *nand;
|
||||
unsigned int page_buffer_size;
|
||||
unsigned int oob_buffer_size;
|
||||
void *spare_databuf;
|
||||
void *spare_oobbuf;
|
||||
bool bounce_data;
|
||||
bool bounce_oob;
|
||||
};
|
||||
|
||||
int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_device *nand);
|
||||
void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx);
|
||||
void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req);
|
||||
void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx,
|
||||
struct nand_page_io_req *req);
|
||||
|
||||
/**
|
||||
* struct nand_ecc - Information relative to the ECC
|
||||
* @defaults: Default values, depend on the underlying subsystem
|
||||
|
Loading…
Reference in New Issue
Block a user