mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-09 15:29:16 +00:00
mtd: nand: sunxi: move some ECC related operations to their own functions
In order to support DMA operations in a clean way we need to extract some of the logic coded in sunxi_nfc_hw_ecc_read/write_page() into their own function. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
This commit is contained in:
parent
c0c9dfa8ad
commit
cc6822fb75
@ -775,6 +775,94 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
|
|||||||
buf[3] = user_data >> 24;
|
buf[3] = user_data >> 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
|
||||||
|
{
|
||||||
|
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
|
||||||
|
int step, bool bbm, int page)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
|
||||||
|
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
|
||||||
|
oob);
|
||||||
|
|
||||||
|
/* De-randomize the Bad Block Marker. */
|
||||||
|
if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
|
||||||
|
sunxi_nfc_randomize_bbm(mtd, page, oob);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
|
||||||
|
const u8 *oob, int step,
|
||||||
|
bool bbm, int page)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
u8 user_data[4];
|
||||||
|
|
||||||
|
/* Randomize the Bad Block Marker. */
|
||||||
|
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
|
||||||
|
memcpy(user_data, oob, sizeof(user_data));
|
||||||
|
sunxi_nfc_randomize_bbm(mtd, page, user_data);
|
||||||
|
oob = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(sunxi_nfc_buf_to_user_data(oob),
|
||||||
|
nfc->regs + NFC_REG_USER_DATA(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
|
||||||
|
unsigned int *max_bitflips, int ret)
|
||||||
|
{
|
||||||
|
if (ret < 0) {
|
||||||
|
mtd->ecc_stats.failed++;
|
||||||
|
} else {
|
||||||
|
mtd->ecc_stats.corrected += ret;
|
||||||
|
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
|
||||||
|
int step, bool *erased)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||||
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
|
u32 status, tmp;
|
||||||
|
|
||||||
|
*erased = false;
|
||||||
|
|
||||||
|
status = readl(nfc->regs + NFC_REG_ECC_ST);
|
||||||
|
|
||||||
|
if (status & NFC_ECC_ERR(step))
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
if (status & NFC_ECC_PAT_FOUND(step)) {
|
||||||
|
u8 pattern;
|
||||||
|
|
||||||
|
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
|
||||||
|
pattern = 0x0;
|
||||||
|
} else {
|
||||||
|
pattern = 0xff;
|
||||||
|
*erased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
memset(data, pattern, ecc->size);
|
||||||
|
|
||||||
|
if (oob)
|
||||||
|
memset(oob, pattern, ecc->bytes + 4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
|
||||||
|
|
||||||
|
return NFC_ECC_ERR_CNT(step, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
|
static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
|
||||||
u8 *data, int data_off,
|
u8 *data, int data_off,
|
||||||
u8 *oob, int oob_off,
|
u8 *oob, int oob_off,
|
||||||
@ -786,7 +874,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
|
|||||||
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
|
||||||
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
struct nand_ecc_ctrl *ecc = &nand->ecc;
|
||||||
int raw_mode = 0;
|
int raw_mode = 0;
|
||||||
u32 status;
|
bool erased;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (*cur_off != data_off)
|
if (*cur_off != data_off)
|
||||||
@ -812,27 +900,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
|
|||||||
|
|
||||||
*cur_off = oob_off + ecc->bytes + 4;
|
*cur_off = oob_off + ecc->bytes + 4;
|
||||||
|
|
||||||
status = readl(nfc->regs + NFC_REG_ECC_ST);
|
ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob, 0, &erased);
|
||||||
if (status & NFC_ECC_PAT_FOUND(0)) {
|
if (erased)
|
||||||
u8 pattern = 0xff;
|
|
||||||
|
|
||||||
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
|
|
||||||
pattern = 0x0;
|
|
||||||
|
|
||||||
memset(data, pattern, ecc->size);
|
|
||||||
memset(oob, pattern, ecc->bytes + 4);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
|
if (ret < 0) {
|
||||||
|
|
||||||
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
|
|
||||||
|
|
||||||
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
|
|
||||||
sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
|
|
||||||
|
|
||||||
if (status & NFC_ECC_ERR(0)) {
|
|
||||||
/*
|
/*
|
||||||
* Re-read the data with the randomizer disabled to identify
|
* Re-read the data with the randomizer disabled to identify
|
||||||
* bitflips in erased pages.
|
* bitflips in erased pages.
|
||||||
@ -840,34 +912,31 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
|
|||||||
if (nand->options & NAND_NEED_SCRAMBLING) {
|
if (nand->options & NAND_NEED_SCRAMBLING) {
|
||||||
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
|
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
|
||||||
nand->read_buf(mtd, data, ecc->size);
|
nand->read_buf(mtd, data, ecc->size);
|
||||||
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
|
} else {
|
||||||
nand->read_buf(mtd, oob, ecc->bytes + 4);
|
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
|
||||||
|
ecc->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
|
||||||
|
nand->read_buf(mtd, oob, ecc->bytes + 4);
|
||||||
|
|
||||||
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
ret = nand_check_erased_ecc_chunk(data, ecc->size,
|
||||||
oob, ecc->bytes + 4,
|
oob, ecc->bytes + 4,
|
||||||
NULL, 0, ecc->strength);
|
NULL, 0, ecc->strength);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
raw_mode = 1;
|
raw_mode = 1;
|
||||||
} else {
|
} else {
|
||||||
/*
|
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
|
||||||
* The engine protects 4 bytes of OOB data per chunk.
|
|
||||||
* Retrieve the corrected OOB bytes.
|
|
||||||
*/
|
|
||||||
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
|
|
||||||
oob);
|
|
||||||
|
|
||||||
/* De-randomize the Bad Block Marker. */
|
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
|
||||||
if (bbm && nand->options & NAND_NEED_SCRAMBLING)
|
sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
|
||||||
sunxi_nfc_randomize_bbm(mtd, page, oob);
|
true, page);
|
||||||
|
|
||||||
|
sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
|
||||||
|
bbm, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);
|
||||||
mtd->ecc_stats.failed++;
|
|
||||||
} else {
|
|
||||||
mtd->ecc_stats.corrected += ret;
|
|
||||||
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return raw_mode;
|
return raw_mode;
|
||||||
}
|
}
|
||||||
@ -897,11 +966,6 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
|
|||||||
*cur_off = mtd->oobsize + mtd->writesize;
|
*cur_off = mtd->oobsize + mtd->writesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
|
|
||||||
{
|
|
||||||
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
|
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
|
||||||
const u8 *data, int data_off,
|
const u8 *data, int data_off,
|
||||||
const u8 *oob, int oob_off,
|
const u8 *oob, int oob_off,
|
||||||
@ -918,19 +982,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
|
|||||||
|
|
||||||
sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
|
sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
|
||||||
|
|
||||||
/* Fill OOB data in */
|
|
||||||
if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
|
|
||||||
u8 user_data[4];
|
|
||||||
|
|
||||||
memcpy(user_data, oob, 4);
|
|
||||||
sunxi_nfc_randomize_bbm(mtd, page, user_data);
|
|
||||||
writel(sunxi_nfc_buf_to_user_data(user_data),
|
|
||||||
nfc->regs + NFC_REG_USER_DATA(0));
|
|
||||||
} else {
|
|
||||||
writel(sunxi_nfc_buf_to_user_data(oob),
|
|
||||||
nfc->regs + NFC_REG_USER_DATA(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_off + ecc->size != oob_off)
|
if (data_off + ecc->size != oob_off)
|
||||||
nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
|
nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
|
||||||
|
|
||||||
@ -939,6 +990,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
sunxi_nfc_randomizer_enable(mtd);
|
sunxi_nfc_randomizer_enable(mtd);
|
||||||
|
sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);
|
||||||
|
|
||||||
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
|
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
|
||||||
NFC_ACCESS_DIR | NFC_ECC_OP,
|
NFC_ACCESS_DIR | NFC_ECC_OP,
|
||||||
nfc->regs + NFC_REG_CMD);
|
nfc->regs + NFC_REG_CMD);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user