diff options
Diffstat (limited to 'target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch')
-rw-r--r-- | target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch | 893 |
1 files changed, 893 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch b/target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch new file mode 100644 index 0000000000..5d37a38b52 --- /dev/null +++ b/target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch @@ -0,0 +1,893 @@ +From ef4bc8ab68979e5c1c30f061c5af1a7d6ec8eb52 Mon Sep 17 00:00:00 2001 +From: Boris Brezillon <boris.brezillon@free-electrons.com> +Date: Tue, 21 Oct 2014 14:40:42 +0200 +Subject: [PATCH] mtd: nand: sunxi: Add HW randomizer support + +Add support for the HW randomizer available on the sunxi nand controller. + +Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +--- + drivers/mtd/nand/sunxi_nand.c | 603 ++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 585 insertions(+), 18 deletions(-) + +diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c +index c3e0473..2f6ab39 100644 +--- a/drivers/mtd/nand/sunxi_nand.c ++++ b/drivers/mtd/nand/sunxi_nand.c +@@ -206,10 +206,12 @@ struct sunxi_nand_hw_ecc { + * + * @part: base paritition structure + * @ecc: per-partition ECC info ++ * @rnd: per-partition randomizer info + */ + struct sunxi_nand_part { + struct nand_part part; + struct nand_ecc_ctrl ecc; ++ struct nand_rnd_ctrl rnd; + }; + + static inline struct sunxi_nand_part * +@@ -219,6 +221,29 @@ to_sunxi_nand_part(struct nand_part *part) + } + + /* ++ * sunxi NAND randomizer structure: stores NAND randomizer information ++ * ++ * @page: current page ++ * @column: current column ++ * @nseeds: seed table size ++ * @seeds: seed table ++ * @subseeds: pre computed sub seeds ++ * @step: step function ++ * @left: number of remaining bytes in the page ++ * @state: current randomizer state ++ */ ++struct sunxi_nand_hw_rnd { ++ int page; ++ int column; ++ int nseeds; ++ u16 *seeds; ++ u16 *subseeds; ++ u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left); ++ int left; ++ u16 state; ++}; ++ ++/* + * NAND chip structure: stores NAND chip device related information + * + * @node: used to store NAND chips into a list +@@ -233,6 +258,7 @@ struct sunxi_nand_chip { + struct list_head node; + struct nand_chip nand; + struct mtd_info mtd; ++ void *buffer; + unsigned long clk_rate; + int selected; + int nsels; +@@ -489,6 +515,185 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + } + } + ++static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count) ++{ ++ state &= 0x7fff; ++ count *= 8; ++ while (count--) ++ state = ((state >> 1) | ++ ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; ++ ++ return state; ++} ++ ++static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count) ++{ ++ state &= 0x7fff; ++ while (count--) ++ state = ((state >> 1) | ++ ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; ++ ++ return state; ++} ++ ++static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column, ++ enum nand_rnd_action action) ++{ ++ struct nand_chip *nand = mtd->priv; ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); ++ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; ++ u16 state; ++ ++ if (page < 0 && column < 0) { ++ rnd->page = -1; ++ rnd->column = -1; ++ return 0; ++ } ++ ++ if (column < 0) ++ column = 0; ++ if (page < 0) ++ page = rnd->page; ++ ++ if (page < 0) ++ return -EINVAL; ++ ++ if (page != rnd->page && action == NAND_RND_READ) { ++ int status; ++ ++ status = nand_page_get_status(mtd, page); ++ if (status == NAND_PAGE_STATUS_UNKNOWN) { ++ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); ++ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, ++ mtd->writesize + mtd->oobsize); ++ ++ if (nand_page_is_empty(mtd, sunxi_nand->buffer, ++ sunxi_nand->buffer + ++ mtd->writesize)) ++ status = NAND_PAGE_EMPTY; ++ else ++ status = NAND_PAGE_FILLED; ++ ++ nand_page_set_status(mtd, page, status); ++ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1); ++ } ++ } ++ ++ state = rnd->seeds[page % rnd->nseeds]; ++ rnd->page = page; ++ rnd->column = column; ++ ++ if (rnd->step) { ++ rnd->state = rnd->step(mtd, state, column, &rnd->left); ++ } else { ++ rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096); ++ rnd->left = mtd->oobsize + mtd->writesize - column; ++ } ++ ++ return 0; ++} ++ ++static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *nand = mtd->priv; ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; ++ u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ int cnt; ++ int offs = 0; ++ int rndactiv; ++ ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); ++ ++ if (rnd->page < 0) { ++ sunxi_nfc_write_buf(mtd, buf, len); ++ return; ++ } ++ ++ while (len > offs) { ++ cnt = len - offs; ++ if (cnt > 1024) ++ cnt = 1024; ++ ++ rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column, ++ &cnt); ++ if (rndactiv > 0) { ++ writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), ++ nfc->regs + NFC_REG_ECC_CTL); ++ if (rnd->left < cnt) ++ cnt = rnd->left; ++ } ++ ++ sunxi_nfc_write_buf(mtd, buf + offs, cnt); ++ ++ if (rndactiv > 0) ++ writel(tmp & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++ ++ offs += cnt; ++ if (len <= offs) ++ break; ++ ++ sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE); ++ } ++} ++ ++static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf, ++ int len) ++{ ++ struct nand_chip *nand = mtd->priv; ++ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); ++ struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv; ++ u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ int cnt; ++ int offs = 0; ++ int rndactiv; ++ ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN); ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); ++ ++ if (rnd->page < 0) { ++ sunxi_nfc_read_buf(mtd, buf, len); ++ return; ++ } ++ ++ while (len > offs) { ++ cnt = len - offs; ++ if (cnt > 1024) ++ cnt = 1024; ++ ++ if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY && ++ nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0) ++ rndactiv = 1; ++ else ++ rndactiv = 0; ++ ++ if (rndactiv > 0) { ++ writel(tmp | NFC_RANDOM_EN | (rnd->state << 16), ++ nfc->regs + NFC_REG_ECC_CTL); ++ if (rnd->left < cnt) ++ cnt = rnd->left; ++ } ++ ++ if (buf) ++ sunxi_nfc_read_buf(mtd, buf + offs, cnt); ++ else ++ sunxi_nfc_read_buf(mtd, NULL, cnt); ++ ++ if (rndactiv > 0) ++ writel(tmp & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++ ++ offs += cnt; ++ if (len <= offs) ++ break; ++ ++ sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ); ++ } ++} ++ + static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) + { + uint8_t ret; +@@ -538,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + int oob_required, int page) + { + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct nand_ecclayout *layout = ecc->layout; + struct sunxi_nand_hw_ecc *data = ecc->priv; + unsigned int max_bitflips = 0; ++ int status; + int offset; + int ret; + u32 tmp; + int i; + int cnt; + ++ status = nand_page_get_status(mtd, page); ++ if (status == NAND_PAGE_STATUS_UNKNOWN) { ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); ++ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, ++ mtd->writesize + mtd->oobsize); ++ ++ if (nand_page_is_empty(mtd, sunxi_nand->buffer, ++ sunxi_nand->buffer + ++ mtd->writesize)) { ++ status = NAND_PAGE_EMPTY; ++ } else { ++ status = NAND_PAGE_FILLED; ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); ++ } ++ ++ nand_page_set_status(mtd, page, status); ++ } ++ ++ if (status == NAND_PAGE_EMPTY) { ++ memset(buf, 0xff, mtd->writesize); ++ if (oob_required) ++ memset(chip->oob_poi, 0xff, mtd->oobsize); ++ return 0; ++ } ++ + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); + tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | +@@ -556,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + for (i = 0; i < ecc->steps; i++) { ++ bool rndactiv = false; ++ + if (i) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); + + offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; + +- chip->read_buf(mtd, NULL, ecc->size); ++ nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ); ++ nand_rnd_read_buf(mtd, NULL, ecc->size); + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + +@@ -569,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + if (ret) + return ret; + ++ if (i) { ++ cnt = ecc->bytes + 4; ++ if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && ++ cnt == ecc->bytes + 4) ++ rndactiv = true; ++ } else { ++ cnt = ecc->bytes + 2; ++ if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 && ++ cnt == ecc->bytes + 2) ++ rndactiv = true; ++ } ++ ++ if (rndactiv) { ++ tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); ++ tmp |= NFC_RANDOM_EN; ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); ++ } ++ + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); + writel(tmp, nfc->regs + NFC_REG_CMD); + +@@ -579,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + memcpy_fromio(buf + (i * ecc->size), + nfc->regs + NFC_RAM0_BASE, ecc->size); + ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++ + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { + mtd->ecc_stats.failed++; + } else { +@@ -594,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + if (ret) + return ret; + ++ nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + offset -= mtd->writesize; +- chip->read_buf(mtd, chip->oob_poi + offset, +- ecc->bytes + 4); ++ nand_rnd_read_buf(mtd, chip->oob_poi + offset, ++ ecc->bytes + 4); + } + } + +@@ -606,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + offset = mtd->writesize + + ecc->layout->oobfree[ecc->steps].offset; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); ++ nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + offset -= mtd->writesize; +- chip->read_buf(mtd, chip->oob_poi + offset, cnt); ++ nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt); + } + } + ++ nand_rnd_config(mtd, -1, -1, NAND_RND_READ); ++ + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~NFC_ECC_EN; + +@@ -627,6 +888,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct nand_ecclayout *layout = ecc->layout; + struct sunxi_nand_hw_ecc *data = ecc->priv; ++ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + int offset; + int ret; + u32 tmp; +@@ -641,22 +903,56 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + for (i = 0; i < ecc->steps; i++) { ++ bool rndactiv = false; ++ u8 oob_buf[4]; ++ + if (i) + chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); + +- chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); ++ nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE); ++ nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); + + offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; + + /* Fill OOB data in */ +- if (oob_required) { +- tmp = 0xffffffff; +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, +- 4); ++ if (!oob_required) ++ memset(oob_buf, 0xff, 4); ++ else ++ memcpy(oob_buf, ++ chip->oob_poi + layout->oobfree[i].offset, ++ 4); ++ ++ ++ memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); ++ ++ if (i) { ++ cnt = ecc->bytes + 4; ++ if (rnd && ++ nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 && ++ cnt == ecc->bytes + 4) ++ rndactiv = true; + } else { +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, +- chip->oob_poi + offset - mtd->writesize, +- 4); ++ cnt = ecc->bytes + 2; ++ if (rnd && ++ nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 && ++ cnt == ecc->bytes + 2) ++ rndactiv = true; ++ } ++ ++ if (rndactiv) { ++ /* pre randomize to generate FF patterns on the NAND */ ++ if (!i) { ++ u16 state = rnd->subseeds[rnd->page % rnd->nseeds]; ++ state = sunxi_nfc_hwrnd_single_step(state, 15); ++ oob_buf[0] ^= state; ++ state = sunxi_nfc_hwrnd_step(rnd, state, 1); ++ oob_buf[1] ^= state; ++ memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4); ++ } ++ tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); ++ tmp |= NFC_RANDOM_EN; ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + } + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); +@@ -671,6 +967,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ret) + return ret; ++ ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); + } + + if (oob_required) { +@@ -679,11 +978,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + offset = mtd->writesize + + ecc->layout->oobfree[i].offset; + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); ++ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + offset -= mtd->writesize; +- chip->write_buf(mtd, chip->oob_poi + offset, cnt); ++ nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt); + } + } + ++ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); ++ + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~NFC_ECC_EN; + +@@ -692,22 +994,76 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + return 0; + } + ++static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state, ++ int column, int *left) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct nand_ecc_ctrl *ecc = chip->cur_ecc; ++ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; ++ int nblks = mtd->writesize / ecc->size; ++ int modsize = ecc->size; ++ int steps; ++ ++ if (column < mtd->writesize) { ++ steps = column % modsize; ++ *left = modsize - steps; ++ } else if (column < mtd->writesize + ++ (nblks * (ecc->bytes + 4))) { ++ column -= mtd->writesize; ++ steps = column % (ecc->bytes + 4); ++ *left = ecc->bytes + 4 - steps; ++ state = rnd->subseeds[rnd->page % rnd->nseeds]; ++ } else { ++ steps = column % 4096; ++ *left = mtd->writesize + mtd->oobsize - column; ++ } ++ ++ return sunxi_nfc_hwrnd_step(rnd, state, steps); ++} ++ + static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) + { + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); ++ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_ecc *data = ecc->priv; + unsigned int max_bitflips = 0; + uint8_t *oob = chip->oob_poi; + int offset = 0; + int ret; ++ int status; + int cnt; + u32 tmp; + int i; + ++ status = nand_page_get_status(mtd, page); ++ if (status == NAND_PAGE_STATUS_UNKNOWN) { ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); ++ sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, ++ mtd->writesize + mtd->oobsize); ++ ++ if (nand_page_is_empty(mtd, sunxi_nand->buffer, ++ sunxi_nand->buffer + ++ mtd->writesize)) { ++ status = NAND_PAGE_EMPTY; ++ } else { ++ status = NAND_PAGE_FILLED; ++ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); ++ } ++ ++ nand_page_set_status(mtd, page, status); ++ } ++ ++ if (status == NAND_PAGE_EMPTY) { ++ memset(buf, 0xff, mtd->writesize); ++ if (oob_required) ++ memset(chip->oob_poi, 0xff, mtd->oobsize); ++ return 0; ++ } ++ + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE); + tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) | +@@ -716,7 +1072,17 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + for (i = 0; i < ecc->steps; i++) { +- chip->read_buf(mtd, NULL, ecc->size); ++ nand_rnd_config(mtd, page, offset, NAND_RND_READ); ++ nand_rnd_read_buf(mtd, NULL, ecc->size); ++ ++ cnt = ecc->bytes + 4; ++ if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && ++ cnt == ecc->bytes + 4) { ++ tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); ++ tmp |= NFC_RANDOM_EN; ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); ++ } + + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30); + writel(tmp, nfc->regs + NFC_REG_CMD); +@@ -729,6 +1095,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + buf += ecc->size; + offset += ecc->size; + ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++ + if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) { + mtd->ecc_stats.failed++; + } else { +@@ -739,7 +1108,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + + if (oob_required) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); +- chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad); ++ nand_rnd_config(mtd, -1, offset, NAND_RND_READ); ++ nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad); + oob += ecc->bytes + ecc->prepad; + } + +@@ -750,10 +1120,13 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + cnt = mtd->oobsize - (oob - chip->oob_poi); + if (cnt > 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); +- chip->read_buf(mtd, oob, cnt); ++ nand_rnd_config(mtd, page, offset, NAND_RND_READ); ++ nand_rnd_read_buf(mtd, oob, cnt); + } + } + ++ nand_rnd_config(mtd, -1, -1, NAND_RND_READ); ++ + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + nfc->regs + NFC_REG_ECC_CTL); + +@@ -768,6 +1141,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct nand_ecc_ctrl *ecc = chip->cur_ecc; + struct sunxi_nand_hw_ecc *data = ecc->priv; ++ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; + uint8_t *oob = chip->oob_poi; + int offset = 0; + int ret; +@@ -783,7 +1157,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + writel(tmp, nfc->regs + NFC_REG_ECC_CTL); + + for (i = 0; i < ecc->steps; i++) { +- chip->write_buf(mtd, buf + (i * ecc->size), ecc->size); ++ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); ++ nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); + offset += ecc->size; + + /* Fill OOB data in */ +@@ -796,6 +1171,16 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + 4); + } + ++ cnt = ecc->bytes + 4; ++ if (rnd && ++ nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 && ++ cnt == ecc->bytes + 4) { ++ tmp = readl(nfc->regs + NFC_REG_ECC_CTL); ++ tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION); ++ tmp |= NFC_RANDOM_EN; ++ writel(tmp, nfc->regs + NFC_REG_ECC_CTL); ++ } ++ + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | + (1 << 30); + writel(tmp, nfc->regs + NFC_REG_CMD); +@@ -804,6 +1189,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + if (ret) + return ret; + ++ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, ++ nfc->regs + NFC_REG_ECC_CTL); ++ + offset += ecc->bytes + ecc->prepad; + oob += ecc->bytes + ecc->prepad; + } +@@ -812,9 +1200,11 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + cnt = mtd->oobsize - (oob - chip->oob_poi); + if (cnt > 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); +- chip->write_buf(mtd, oob, cnt); ++ nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); ++ nand_rnd_write_buf(mtd, oob, cnt); + } + } ++ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + + tmp = readl(nfc->regs + NFC_REG_ECC_CTL); + tmp &= ~NFC_ECC_EN; +@@ -824,6 +1214,128 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + return 0; + } + ++static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state, ++ int column, int *left) ++{ ++ struct nand_chip *chip = mtd->priv; ++ struct nand_ecc_ctrl *ecc = chip->cur_ecc; ++ struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv; ++ int eccsteps = mtd->writesize / ecc->size; ++ int modsize = ecc->size + ecc->prepad + ecc->bytes; ++ int steps; ++ ++ if (column < (eccsteps * modsize)) { ++ steps = column % modsize; ++ *left = modsize - steps; ++ if (steps >= ecc->size) { ++ steps -= ecc->size; ++ state = rnd->subseeds[rnd->page % rnd->nseeds]; ++ } ++ } else { ++ steps = column % 4096; ++ *left = mtd->writesize + mtd->oobsize - column; ++ } ++ ++ return sunxi_nfc_hwrnd_step(rnd, state, steps); ++} ++ ++static u16 default_seeds[] = {0x4a80}; ++ ++static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd) ++{ ++ struct sunxi_nand_hw_rnd *hwrnd = rnd->priv; ++ ++ if (hwrnd->seeds != default_seeds) ++ kfree(hwrnd->seeds); ++ kfree(hwrnd->subseeds); ++ kfree(rnd->layout); ++ kfree(hwrnd); ++} ++ ++static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd, ++ struct nand_rnd_ctrl *rnd, ++ struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ struct sunxi_nand_hw_rnd *hwrnd; ++ struct nand_rnd_layout *layout = NULL; ++ int ret; ++ ++ hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL); ++ if (!hwrnd) ++ return -ENOMEM; ++ ++ hwrnd->seeds = default_seeds; ++ hwrnd->nseeds = ARRAY_SIZE(default_seeds); ++ ++ if (of_get_property(np, "nand-randomizer-seeds", &ret)) { ++ hwrnd->nseeds = ret / sizeof(*hwrnd->seeds); ++ hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds), ++ GFP_KERNEL); ++ if (!hwrnd->seeds) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = of_property_read_u16_array(np, "nand-randomizer-seeds", ++ hwrnd->seeds, hwrnd->nseeds); ++ if (ret) ++ goto err; ++ } ++ ++ switch (ecc->mode) { ++ case NAND_ECC_HW_SYNDROME: ++ hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps; ++ break; ++ ++ case NAND_ECC_HW: ++ hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps; ++ ++ default: ++ layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree), ++ GFP_KERNEL); ++ if (!layout) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ layout->nranges = 1; ++ layout->ranges[0].offset = mtd->writesize; ++ layout->ranges[0].length = 2; ++ rnd->layout = layout; ++ break; ++ } ++ ++ if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) { ++ int i; ++ ++ hwrnd->subseeds = kzalloc(hwrnd->nseeds * ++ sizeof(*hwrnd->subseeds), ++ GFP_KERNEL); ++ if (!hwrnd->subseeds) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ for (i = 0; i < hwrnd->nseeds; i++) ++ hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd, ++ hwrnd->seeds[i], ++ ecc->size); ++ } ++ ++ rnd->config = sunxi_nfc_hwrnd_config; ++ rnd->read_buf = sunxi_nfc_hwrnd_read_buf; ++ rnd->write_buf = sunxi_nfc_hwrnd_write_buf; ++ rnd->priv = hwrnd; ++ ++ return 0; ++ ++err: ++ kfree(hwrnd); ++ kfree(layout); ++ ++ return ret; ++} ++ + static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, + const struct nand_sdr_timings *timings) + { +@@ -1084,6 +1596,40 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, + return 0; + } + ++static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd) ++{ ++ switch (rnd->mode) { ++ case NAND_RND_HW: ++ sunxi_nand_rnd_ctrl_cleanup(rnd); ++ break; ++ default: ++ break; ++ } ++} ++ ++static int sunxi_nand_rnd_init(struct mtd_info *mtd, ++ struct nand_rnd_ctrl *rnd, ++ struct nand_ecc_ctrl *ecc, ++ struct device_node *np) ++{ ++ int ret; ++ ++ rnd->mode = NAND_RND_NONE; ++ ++ ret = of_get_nand_rnd_mode(np); ++ if (ret >= 0) ++ rnd->mode = ret; ++ ++ switch (rnd->mode) { ++ case NAND_RND_HW: ++ return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np); ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) + { + switch (ecc->mode) { +@@ -1175,7 +1721,14 @@ struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master, + if (ret) + goto err; + ++ ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp); ++ if (ret) { ++ sunxi_nand_ecc_cleanup(&part->ecc); ++ goto err; ++ } ++ + part->part.ecc = &part->ecc; ++ part->part.rnd = &part->rnd; + + return &part->part; + +@@ -1300,18 +1853,30 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, + if (ret) + return ret; + ++ chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); ++ if (!chip->buffer) ++ return -ENOMEM; ++ + ret = sunxi_nand_chip_init_timings(chip, np); + if (ret) { + dev_err(dev, "could not configure chip timings: %d\n", ret); + return ret; + } + ++ ret = nand_pst_create(mtd); ++ if (ret) ++ return ret; ++ + ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); + if (ret) { + dev_err(dev, "ECC init failed: %d\n", ret); + return ret; + } + ++ ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np); ++ if (ret) ++ return ret; ++ + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); +@@ -1367,6 +1932,8 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) + node); + nand_release(&chip->mtd); + sunxi_nand_ecc_cleanup(&chip->nand.ecc); ++ sunxi_nand_rnd_cleanup(&chip->nand.rnd); ++ kfree(chip->buffer); + } + } + |