123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887 |
- 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(-)
- --- a/drivers/mtd/nand/sunxi_nand.c
- +++ b/drivers/mtd/nand/sunxi_nand.c
- @@ -210,10 +210,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 *
- @@ -223,6 +225,29 @@ to_sunxi_nand_part(struct nand_part *par
- }
-
- /*
- + * 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
- @@ -237,6 +262,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;
- @@ -493,6 +519,185 @@ static void sunxi_nfc_write_buf(struct m
- }
- }
-
- +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;
- @@ -542,16 +747,43 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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) |
- @@ -560,12 +792,15 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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);
-
- @@ -573,6 +808,25 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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);
-
- @@ -583,6 +837,9 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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 {
- @@ -598,9 +855,10 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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);
- }
- }
-
- @@ -610,11 +868,14 @@ static int sunxi_nfc_hw_ecc_read_page(st
- 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;
-
- @@ -631,6 +892,7 @@ static int sunxi_nfc_hw_ecc_write_page(s
- 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;
- @@ -645,17 +907,57 @@ static int sunxi_nfc_hw_ecc_write_page(s
- 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 */
- - writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
- - layout->oobfree[i].offset),
- - nfc->regs + NFC_REG_USER_DATA_BASE);
- + 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 {
- + 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);
-
- @@ -669,6 +971,9 @@ static int sunxi_nfc_hw_ecc_write_page(s
- 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) {
- @@ -677,11 +982,14 @@ static int sunxi_nfc_hw_ecc_write_page(s
- 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;
-
- @@ -690,22 +998,76 @@ static int sunxi_nfc_hw_ecc_write_page(s
- 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) |
- @@ -714,7 +1076,17 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
- 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);
- @@ -727,6 +1099,9 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
- 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 {
- @@ -737,7 +1112,8 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
-
- 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;
- }
-
- @@ -748,10 +1124,13 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
- 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);
-
- @@ -766,6 +1145,7 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
- 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;
- @@ -781,13 +1161,24 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
- 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 */
- writel(NFC_BUF_TO_USER_DATA(oob),
- nfc->regs + NFC_REG_USER_DATA_BASE);
-
- + 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);
- @@ -796,6 +1187,9 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
- 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;
- }
- @@ -804,9 +1198,11 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
- 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;
- @@ -816,6 +1212,128 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
- 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)
- {
- @@ -1076,6 +1594,40 @@ static int sunxi_nand_hw_syndrome_ecc_ct
- 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) {
- @@ -1167,7 +1719,14 @@ struct nand_part *sunxi_ofnandpart_parse
- 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;
-
- @@ -1292,18 +1851,30 @@ static int sunxi_nand_chip_init(struct d
- 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);
- @@ -1360,6 +1931,8 @@ static void sunxi_nand_chips_cleanup(str
- nand_release(&chip->mtd);
- sunxi_nand_ecc_cleanup(&chip->nand.ecc);
- list_del(&chip->node);
- + sunxi_nand_rnd_cleanup(&chip->nand.rnd);
- + kfree(chip->buffer);
- }
- }
-
|