Commit cc6822fb authored by Boris Brezillon's avatar Boris Brezillon

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: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
parent c0c9dfa8
...@@ -775,6 +775,94 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) ...@@ -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, ...@@ -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, ...@@ -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)));
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)) { if (ret < 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,9 +912,13 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, ...@@ -840,9 +912,13 @@ 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);
} else {
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
ecc->size);
}
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4); 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,
...@@ -850,25 +926,18 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, ...@@ -850,25 +926,18 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
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);
}
if (ret < 0) { sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
mtd->ecc_stats.failed++; bbm, page);
} else {
mtd->ecc_stats.corrected += ret;
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
} }
sunxi_nfc_hw_ecc_update_stats(mtd, 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, ...@@ -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, ...@@ -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, ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment